前面介绍了Broker在网络传输过程中使用的数据结构,同时也介绍了MetaQ使用了Gecko框架作为网络传输框架。

有人会问,Gecko什么调用MetaEncodeCommand的encode()方法,让命令变成可见的明文在网络传输,Gecko又在什么时候将网络传输的数据包装成一个个Command对象?

或许有人已经注意到了笔者在介绍Broker启动类MetaMorphosisBroker的时候估计漏掉了一个方法newRemotingServer()方法,即创建Gecko Server。



Java代码 

 
    
   
  
1. private static RemotingServer newRemotingServer(final MetaConfig metaConfig) { 
2.         final ServerConfig serverConfig = new ServerConfig(); 
3.         serverConfig.setWireFormatType(new MetamorphosisWireFormatType()); //注册了MetamorphosisWireFormatType实例,该实例负责编码和解码Command 
4.         serverConfig.setPort(metaConfig.getServerPort()); 
5.         final RemotingServer server = RemotingFactory.newRemotingServer(serverConfig); 
6.         return server; 
7. }



在该方法内注册了一个MetamorphosisWireFormatType实例,该实例负责Command 的编码解码工作,MetamorphosisWireFormatType实现接口WireFormatType。



Java代码 

 
    
   
  
1. public class MetamorphosisWireFormatType extends WireFormatType { 
2.     public static final String SCHEME = "meta"; 
3.  
4.     public String getScheme() { 
5.         return SCHEME; 
6.     } 
7.  
8.     public String name() { 
9.         return "metamorphosis"; 
10.     } 
11.  
12.     public CodecFactory newCodecFactory() { 
13.         return new MetaCodecFactory(); 
14.     } 
15.  
16.     public CommandFactory newCommandFactory() { 
17.         return new MetaCommandFactory(); 
18.     }





MetamorphosisWireFormatType本身并没有进行编码解码,而是交给了类MetaCodecFactory去实现,另外我们也看到newCommandFactory()方法,该方法主要是用于连接的心跳检测。下面让我们分别来看看这两个类: MetaCommandFactory和MetaCodecFactory,MetaCommandFactory和MetaCodecFactory均是MetamorphosisWireFormatType的内部类

用于心跳检测的类MetaCommandFactory,该类主要有两个方法,创建心跳请求的createHeartBeatCommand()方法和响应心跳请求的createBooleanAckCommand()方法:



Java代码 

 
    
   
  
1. static class MetaCommandFactory implements CommandFactory { 
2.  
3.         public BooleanAckCommand createBooleanAckCommand(final CommandHeader request, final ResponseStatus responseStatus, final String errorMsg) { 
4. //响应心跳请求 
5.             int httpCode = -1; 
6.             switch (responseStatus) { 
7.                 case NO_ERROR: 
8.                     httpCode = HttpStatus.Success; 
9.                     break; 
10.                 case THREADPOOL_BUSY: 
11.                 case NO_PROCESSOR: 
12.                     httpCode = HttpStatus.ServiceUnavilable; 
13.                     break; 
14.                 case TIMEOUT: 
15.                     httpCode = HttpStatus.GatewayTimeout; 
16.                     break; 
17.                 default: 
18.                     httpCode = HttpStatus.InternalServerError; 
19.                     break; 
20.             } 
21.             return new BooleanCommand(httpCode, errorMsg, request.getOpaque()); 
22.         } 
23.  
24.         public HeartBeatRequestCommand createHeartBeatCommand() { 
25. //前面介绍过VersionCommand用于心跳检测,就是用于此处 
26.             return new VersionCommand(OpaqueGenerator.getNextOpaque()); 
27.         } 
28.     }




MetaCodecFactory是MetaQ(包括Broker和Client,因为编码解码Broker和Client都需要)网络传输最重要的一个类,负责命令的编码解码,MetaCodecFactory要实现Gecko框架定义的接口CodecFactory,MetaCodecFactory实例才能被Gecko框架使用,接口CodecFactory就定义了两个方法,返回编码器和解码器(由于Client和Broker均需要使用到MetamorphosisWireFormatType,所以MetamorphosisWireFormatType放在common工程中):



Java代码 

 
    
   
  
1. static class MetaCodecFactory implements CodecFactory { 
2.     //返回解码器 
3.         @Override 
4.         public Decoder getDecoder() { 
5.  
6.             return new Decoder() { 
7.                 //Gecko框架会在适当的时候调用该方法,并将数据放到参数buff中, 
8.                 //用户可以根据buff的内容进行解析,包装成对应的Command类型 
9.                 public Object decode(final IoBuffer buff, final Session session) { 
10.                     if (buff == null || !buff.hasRemaining()) { 
11.                         return null; 
12.                     } 
13.                     buff.mark(); 
14.                      //匹配第一个{‘\r’, ‘\n’},也就是找到命令的内容(不包括数据),目前只有PutCommand和SynCommand有数据部分,其他的命令都只有命令的内容 
15.                     final int index = LINE_MATCHER.matchFirst(buff); 
16.                     if (index >= 0) { 
17.                           //获取命令内容 
18.                         final byte[] bytes = new byte[index - buff.position()]; 
19.                         buff.get(bytes); 
20.                         //跳过\r\n 
21.                         buff.position(buff.position() + 2); 
22.                           //将命令字节数组转换成字符串 
23.                         final String line = ByteUtils.getString(bytes); 
24.                         if (log.isDebugEnabled()) { 
25.                             log.debug("Receive command:" + line); 
26.                         } 
27.                           //以空格为单位分离内容 
28.                         final String[] sa = SPLITER.split(line); 
29.                         if (sa == null || sa.length == 0) { 
30.                             throw new MetaCodecException("Blank command line."); 
31.                         } 
32.                           //判断内容的第一个字母 
33.                         final byte op = (byte) sa[0].charAt(0); 
34.                         switch (op) { 
35.                             case 'p': 
36.                                     //如果是p的话,认为是put命令,具体见MetaEncodeCommand定义的命令的内容并解析put命令,具体格式在每个命令的实现类里的注释都有,下面的各个方法的注释也有部分 
37.                                 return this.decodePut(buff, sa); 
38.                             case 'g': 
39.                                    //如果是g的话,认为是get命令 
40.                                 return this.decodeGet(sa); 
41.                             case 't': 
42.                                    //如果是g的话,认为是事务命令 
43.                                 return this.decodeTransaction(sa); 
44.                             case 'r': 
45.                                    //如果是g的话,认为是结果响应 
46.                                 return this.decodeBoolean(buff, sa); 
47.                             case 'v': 
48.                                      //如果是v的话,则可能是心跳请求或者数据响应,所以得使用更详细的信息进行判断 
49.                                 if (sa[0].equals("value")) { 
50.                                     return this.decodeData(buff, sa); 
51.                                 } else { 
52.                                     return this.decodeVersion(sa); 
53.                                 } 
54.                             case 's': 
55.                                //如果是s的话,则可能是统计请求或者同步,所以得使用更详细的信息进行判断 
56. if (sa[0].equals("stats")) { 
57.                                     return this.decodeStats(sa); 
58.                                 } else { 
59.                                     return this.decodeSync(buff, sa); 
60.                                 } 
61.                             case 'o': 
62.                                   //如果是o的话,查询最近可用位置请求 
63.                                 return this.decodeOffset(sa); 
64.                             case 'q': 
65.                                    //如果是q的话,退出连接请求 
66.                                 return this.decodeQuit(); 
67.                             default: 
68.                                 throw new MetaCodecException("Unknow command:" + line); 
69.                         } 
70.                     } else { 
71.                         return null; 
72.                     } 
73.                 } 
74.  
75.                 private Object decodeQuit() { 
76.                     return new QuitCommand(); 
77.                 } 
78.  
79.                 private Object decodeVersion(final String[] sa) { 
80.                     if (sa.length >= 2) { 
81.                         return new VersionCommand(Integer.parseInt(sa[1])); 
82.                     } else { 
83.                         return new VersionCommand(Integer.MAX_VALUE); 
84.                     } 
85.                 } 
86.  
87.                 // offset topic group partition offset opaque\r\n 
88.                 private Object decodeOffset(final String[] sa) { 
89.                     this.assertCommand(sa[0], "offset"); 
90.                     return new OffsetCommand(sa[1], sa[2], Integer.parseInt(sa[3]), Long.parseLong(sa[4]), Integer.parseInt(sa[5])); 
91.                 } 
92.  
93.                 // stats item opaque\r\n 
94.                 // opaque可以为空 
95.                 private Object decodeStats(final String[] sa) { 
96.                     this.assertCommand(sa[0], "stats"); 
97.                     int opaque = Integer.MAX_VALUE; 
98.                     if (sa.length >= 3) { 
99.                         opaque = Integer.parseInt(sa[2]); 
100.                     } 
101.                     String item = null; 
102.                     if (sa.length >= 2) { 
103.                         item = sa[1]; 
104.                     } 
105.                     return new StatsCommand(opaque, item); 
106.                 } 
107.  
108.                 // value totalLen opaque\r\n data 
109.                 private Object decodeData(final IoBuffer buff, final String[] sa) { 
110.                     this.assertCommand(sa[0], "value"); 
111.                     final int valueLen = Integer.parseInt(sa[1]); 
112.                     if (buff.remaining() < valueLen) { 
113.                         buff.reset(); 
114.                         return null; 
115.                     } else { 
116.                         final byte[] data = new byte[valueLen]; 
117.                         buff.get(data); 
118.                         return new DataCommand(data, Integer.parseInt(sa[2])); 
119.                     } 
120.                 } 
121.  
122.                 /**
123.                  * result code length opaque\r\n message
124.                  * 
125.                  * @param buff
126.                  * @param sa
127.                  * @return
128.                  */ 
129.                 private Object decodeBoolean(final IoBuffer buff, final String[] sa) { 
130.                     this.assertCommand(sa[0], "result"); 
131.                     final int valueLen = Integer.parseInt(sa[2]); 
132.                     if (valueLen == 0) { 
133.                         return new BooleanCommand(Integer.parseInt(sa[1]), null, Integer.parseInt(sa[3])); 
134.                     } else { 
135.                         if (buff.remaining() < valueLen) { 
136.                             buff.reset(); 
137.                             return null; 
138.                         } else { 
139.                             final byte[] data = new byte[valueLen]; 
140.                             buff.get(data); 
141.                             return new BooleanCommand(Integer.parseInt(sa[1]), ByteUtils.getString(data), Integer.parseInt(sa[3])); 
142.                         } 
143.                     } 
144.                 } 
145.  
146.                 // get topic group partition offset maxSize opaque\r\n 
147.                 private Object decodeGet(final String[] sa) { 
148.                     this.assertCommand(sa[0], "get"); 
149.                     return new GetCommand(sa[1], sa[2], Integer.parseInt(sa[3]), Long.parseLong(sa[4]), Integer.parseInt(sa[5]), Integer.parseInt(sa[6])); 
150.                 } 
151.  
152.                 // transaction key sessionId type [timeout] [unique qualifier] 
153.                 // opaque\r\n 
154.                 private Object decodeTransaction(final String[] sa) { 
155.                     this.assertCommand(sa[0], "transaction"); 
156.                     final TransactionId transactionId = this.getTransactionId(sa[1]); 
157.                     final TransactionType type = TransactionType.valueOf(sa[3]); 
158.                     switch (sa.length) { 
159.                         case 7: 
160.                             // Both include timeout and unique qualifier. 
161.                             int timeout = Integer.valueOf(sa[4]); 
162.                             String uniqueQualifier = sa[5]; 
163.                             TransactionInfo info = new TransactionInfo(transactionId, sa[2], type, uniqueQualifier, timeout); 
164.                             return new TransactionCommand(info, Integer.parseInt(sa[6])); 
165.                         case 6: 
166.                             // Maybe timeout or unique qualifier 
167.                             if (StringUtils.isNumeric(sa[4])) { 
168.                                 timeout = Integer.valueOf(sa[4]); 
169.                                 info = new TransactionInfo(transactionId, sa[2], type, null, timeout); 
170.                                 return new TransactionCommand(info, Integer.parseInt(sa[5])); 
171.                             } else { 
172.                                 uniqueQualifier = sa[4]; 
173.                                 info = new TransactionInfo(transactionId, sa[2], type, uniqueQualifier, 0); 
174.                                 return new TransactionCommand(info, Integer.parseInt(sa[5])); 
175.                             } 
176.                         case 5: 
177.                             // Without timeout and unique qualifier. 
178.                             info = new TransactionInfo(transactionId, sa[2], type, null); 
179.                             return new TransactionCommand(info, Integer.parseInt(sa[4])); 
180.                         default: 
181.                             throw new MetaCodecException("Invalid transaction command:" + StringUtils.join(sa)); 
182.                     } 
183.                 } 
184.  
185.                 private TransactionId getTransactionId(final String s) { 
186.                     return TransactionId.valueOf(s); 
187.                 } 
188.  
189.                 // sync topic partition value-length flag msgId 
190.                 // opaque\r\n 
191.                 private Object decodeSync(final IoBuffer buff, final String[] sa) { 
192.                     this.assertCommand(sa[0], "sync"); 
193.                     final int valueLen = Integer.parseInt(sa[3]); 
194.                     if (buff.remaining() < valueLen) { 
195.                         buff.reset(); 
196.                         return null; 
197.                     } else { 
198.                         final byte[] data = new byte[valueLen]; 
199.                         buff.get(data); 
200.                         switch (sa.length) { 
201.                             case 7: 
202.                                 // old master before 1.4.4 
203.                                 return new SyncCommand(sa[1], Integer.parseInt(sa[2]), data, Integer.parseInt(sa[4]), Long.valueOf(sa[5]), -1, Integer.parseInt(sa[6])); 
204.                             case 8: 
205.                                 // new master since 1.4.4 
206.                                 return new SyncCommand(sa[1], Integer.parseInt(sa[2]), data, Integer.parseInt(sa[4]), Long.valueOf(sa[5]), Integer.parseInt(sa[6]), Integer.parseInt(sa[7])); 
207.                             default: 
208.                                 throw new MetaCodecException("Invalid Sync command:" + StringUtils.join(sa)); 
209.                         } 
210.                     } 
211.                 } 
212.  
213.                 // put topic partition value-length flag checksum 
214.                 // [transactionKey] 
215.                 // opaque\r\n 
216.                 private Object decodePut(final IoBuffer buff, final String[] sa) { 
217.                     this.assertCommand(sa[0], "put"); 
218.                     final int valueLen = Integer.parseInt(sa[3]); 
219.                     if (buff.remaining() < valueLen) { 
220.                         buff.reset(); 
221.                         return null; 
222.                     } else { 
223.                         final byte[] data = new byte[valueLen]; 
224.                         buff.get(data); 
225.                         switch (sa.length) { 
226.                             case 6: 
227.                                 // old clients before 1.4.4 
228.                                 return new PutCommand(sa[1], Integer.parseInt(sa[2]), data, null, Integer.parseInt(sa[4]), Integer.parseInt(sa[5])); 
229.                             case 7: 
230.                                 // either transaction command or new clients since 
231.                                 // 1.4.4 
232.                                 String slot = sa[5]; 
233.                                 char firstChar = slot.charAt(0); 
234.                                 if (Character.isDigit(firstChar) || '-' == firstChar) { 
235.                                     // slot is checksum. 
236.                                     int checkSum = Integer.parseInt(slot); 
237.                                     return new PutCommand(sa[1], Integer.parseInt(sa[2]), data, Integer.parseInt(sa[4]), checkSum, null, Integer.parseInt(sa[6])); 
238.                                 } else { 
239.                                     // slot is transaction id. 
240.                                     return new PutCommand(sa[1], Integer.parseInt(sa[2]), data, this.getTransactionId(slot), Integer.parseInt(sa[4]), Integer.parseInt(sa[6])); 
241.                                 } 
242.                             case 8: 
243.                                 // New clients since 1.4.4 
244.                                 // A transaction command 
245.                                 return new PutCommand(sa[1], Integer.parseInt(sa[2]), data, Integer.parseInt(sa[4]), Integer.parseInt(sa[5]), this.getTransactionId(sa[6]), Integer.parseInt(sa[7])); 
246.                             default: 
247.                                 throw new MetaCodecException("Invalid put command:" + StringUtils.join(sa)); 
248.                         } 
249.                     } 
250.                 } 
251.  
252.                 private void assertCommand(final String cmd, final String expect) { 
253.                     if (!expect.equals(cmd)) { 
254.                         throw new MetaCodecException("Expect " + expect + ",but was " + cmd); 
255.                     } 
256.                 } 
257.             }; 
258.         } 
259.  
260.         @Override 
261.         public Encoder getEncoder() { 
262.             //返回编码器 
263. return new Encoder() { 
264.                 @Override 
265.                 public IoBuffer encode(final Object message, final Session session) { 
266.                       //框架会在适当的时候调用编码器的encode()方法,前面说过如果响应的命令是DataCommand的时候假设不是zeroCopy的话,会出现问题。原因就在这里,因为如果不使用zeroCopy的话,返回给Gecko框架的是一个DataCommand的实例,这时候会调用到此方法,而此方法并没有按照DataCommand的格式进行编码,解码器会识别不了,所以容易出问题 
267.                     return ((MetaEncodeCommand) message).encode(); 
268.                 } 
269.             }; 
270.         }



前面还介绍到过MetaMorphosisBroker在启动时会注册请求类型与Processor的映射,见代码:



Java代码 

 
    
   
  
1. private void registerProcessors() { 
2.         this.remotingServer.registerProcessor(GetCommand.class, new GetProcessor(this.brokerProcessor, this.executorsManager.getGetExecutor())); 
3.         this.remotingServer.registerProcessor(PutCommand.class, new PutProcessor(this.brokerProcessor, this.executorsManager.getUnOrderedPutExecutor())); 
4.         this.remotingServer.registerProcessor(OffsetCommand.class, new OffsetProcessor(this.brokerProcessor, this.executorsManager.getGetExecutor())); 
5.         this.remotingServer.registerProcessor(HeartBeatRequestCommand.class, new VersionProcessor(this.brokerProcessor)); 
6.         this.remotingServer.registerProcessor(QuitCommand.class, new QuitProcessor(this.brokerProcessor)); 
7.         this.remotingServer.registerProcessor(StatsCommand.class, new StatsProcessor(this.brokerProcessor)); 
8.         this.remotingServer.registerProcessor(TransactionCommand.class, new TransactionProcessor(this.brokerProcessor, this.executorsManager.getUnOrderedPutExecutor())); 
9. }




依据注册的类型,Gecko框架将会根据解析出来的命令实例调用处理器的不同方法,并返回不同请求的响应。下面让我们来看看不同的处理到底做了些什么事情?因为是Broker针对请求的处理,所以所有的Processor都在server工程中,先上类图:

MetaQ技术内幕——源码分析(七)_网络传输

所有的处理器均实现了RequestProcessor接口,该接口由Gecko框架定义,RequestProcessor类中只定义了两个方法:



Java代码 

 
    
   
  
1. public interface RequestProcessor<T extends RequestCommand> { 
2.     /**
3.      * 处理请求
4.      * 
5.      * @param request请求命令
6.      * @param conn 请求来源的连接
7.      */ 
8.     public void handleRequest(T request, Connection conn); 
9.  
10.  
11.     /**
12.      * 用户自定义的线程池,如果提供,那么请求的处理都将在该线程池内执行
13.      * 
14.      * @return
15.      */ 
16.     public ThreadPoolExecutor getExecutor(); 
17. }




所以,加上上一篇文章,我们可以得出MetaQ的大致网络处理流程图解如下:

MetaQ技术内幕——源码分析(七)_MetaQ_02






 

http://soledede.com/

 



或者关注soledede的微信公众号:soledede


微信公众号:



MetaQ技术内幕——源码分析(七)_Boo_03