Said ahead
Last time
Source code analysis
Return to the method org. apache. rocketmq. tools. admin. DefaultMQ AdminExtImpl start
@Override public void start() throws MQClientException { switch (this.serviceState) { case CREATE_JUST://Services are only started, not created this.serviceState = ServiceState.START_FAILED; this.defaultMQAdminExt.changeInstanceNameToPID(); // Create mqclient object = this.mqClientInstance = MQClientManager.getInstance().getAndCreateMQClientInstance(this.defaultMQAdminExt, rpcHook); // Register Management Service Processor = boolean registerOK = mqClientInstance.registerAdminExt(this.defaultMQAdminExt.getAdminExtGroup(), this); if (!registerOK) { this.serviceState = ServiceState.CREATE_JUST; throw new MQClientException("The adminExt group[" + this.defaultMQAdminExt.getAdminExtGroup() + "] has created already, specifed another name please." + FAQUrl.suggestTodo(FAQUrl.GROUP_NAME_DUPLICATE_URL), null); } // Start mqclient = mqClientInstance.start(); log.info("the adminExt [{}] start OK", this.defaultMQAdminExt.getAdminExtGroup()); this.serviceState = ServiceState.RUNNING; break; case RUNNING: case START_FAILED: case SHUTDOWN_ALREADY: throw new MQClientException("The AdminExt service state not OK, maybe started once, " + this.serviceState + FAQUrl.suggestTodo(FAQUrl.CLIENT_SERVICE_NOT_OK), null); default: break; } }
Enter the method org.apache.rocketmq.client.impl.MQClientManager#getAndCreateMQClientInstance(org.apache.rocketmq.client.ClientConfig, org.apache.rocketmq.remoting.RPCHook) to create mqclient objects
public MQClientInstance getAndCreateMQClientInstance(final ClientConfig clientConfig, RPCHook rpcHook) { String clientId = clientConfig.buildMQClientId(); // Getting client objects from local caches, simple concurrent HashMap is usually used as local caches with high performance. MQClientInstance instance = this.factoryTable.get(clientId); if (null == instance) { instance = new MQClientInstance(clientConfig.cloneClientConfig(), this.factoryIndexGenerator.getAndIncrement(), clientId, rpcHook); MQClientInstance prev = this.factoryTable.putIfAbsent(clientId, instance); if (prev != null) { instance = prev; log.warn("Returned Previous MQClientInstance for clientId:[{}]", clientId); } else { log.info("Created new MQClientInstance for clientId:[{}]", clientId); } } return instance; }
Enter the method org.apache.rocketmq.client.impl.factory.MQClientInstance#registerAdminExt registration management service
public boolean registerAdminExt(final String group, final MQAdminExtInner admin) { if (null == group || null == admin) { return false; } MQAdminExtInner prev = this.adminExtTable.putIfAbsent(group, admin); if (prev != null) { log.warn("the admin group[{}] exist already.", group); return false; } return true; }
Enter this method to start mqclient, org. apache. rocketmq. client. impl. factory. MQClientInstance start #
public void start() throws MQClientException { synchronized (this) { switch (this.serviceState) { case CREATE_JUST://Only Create Not Start this.serviceState = ServiceState.START_FAILED; // If not specified, look address from name server, if the command line does not specify the address of name server at startup, get it if (null == this.clientConfig.getNamesrvAddr()) { // Monitor connection availability this.mQClientAPIImpl.fetchNameServerAddr(); } // Start request-response channel starts channel = of request response this.mQClientAPIImpl.start(); // Start various schedule tasks to start scheduling tasks this.startScheduledTask(); // Start pull service this.pullMessageService.start(); // Start rebalance service starts load balancing service this.rebalanceService.start(); // Start push service starts push service this.defaultMQProducer.getDefaultMQProducerImpl().start(false); log.info("the client factory [{}] start OK", this.clientId); this.serviceState = ServiceState.RUNNING; break; case RUNNING: break; case SHUTDOWN_ALREADY: break; case START_FAILED: throw new MQClientException("The Factory object[" + this.getClientId() + "] has been created before, and failed.", null); default: break; } } }
Start mqclient and enter the method org. apache. rocketmq. remoting. netty. Netty RemotingClient start #
@Override public void start() { this.defaultEventExecutorGroup = new DefaultEventExecutorGroup( nettyClientConfig.getClientWorkerThreads(), new ThreadFactory() { private AtomicInteger threadIndex = new AtomicInteger(0); @Override public Thread newThread(Runnable r) { return new Thread(r, "NettyClientWorkerThread_" + this.threadIndex.incrementAndGet()); } }); Bootstrap handler = this.bootstrap.group(this.eventLoopGroupWorker).channel(NioSocketChannel.class) .option(ChannelOption.TCP_NODELAY, true) .option(ChannelOption.SO_KEEPALIVE, false) .option(ChannelOption.CONNECT_TIMEOUT_MILLIS, nettyClientConfig.getConnectTimeoutMillis()) // Setting the request and response message size by default is 65535 .option(ChannelOption.SO_SNDBUF, nettyClientConfig.getClientSocketSndBufSize()) .option(ChannelOption.SO_RCVBUF, nettyClientConfig.getClientSocketRcvBufSize()) .handler(new ChannelInitializer<SocketChannel>() { @Override public void initChannel(SocketChannel ch) throws Exception { ChannelPipeline pipeline = ch.pipeline(); if (nettyClientConfig.isUseTLS()) { if (null != sslContext) { pipeline.addFirst(defaultEventExecutorGroup, "sslHandler", sslContext.newHandler(ch.alloc())); log.info("Prepend SSL handler"); } else { log.warn("Connections are insecure as SSLContext is null!"); } } pipeline.addLast( // Adding Event Groups defaultEventExecutorGroup, // Registered netty encoder = " new NettyEncoder(), // Register netty decoder = new NettyDecoder(), new IdleStateHandler(0, 0, nettyClientConfig.getClientChannelMaxIdleTimeSeconds()), // netty connection management handler = new NettyConnectManageHandler(), // Register netty client handler = new NettyClientHandler()); } }); this.timer.scheduleAtFixedRate(new TimerTask() { @Override public void run() { try { // Scanning Abandoned Requests = NettyRemotingClient.this.scanResponseTable(); } catch (Throwable e) { log.error("scanResponseTable exception", e); } } }, 1000 * 3, 1000); if (this.channelEventListener != null) { this.nettyEventExecutor.start(); } }
netty encoder
public class NettyEncoder extends MessageToByteEncoder<RemotingCommand> { private static final InternalLogger log = InternalLoggerFactory.getLogger(RemotingHelper.ROCKETMQ_REMOTING); @Override public void encode(ChannelHandlerContext ctx, RemotingCommand remotingCommand, ByteBuf out) throws Exception { try { // The message header is encoded, and rocketmq only encodes the header. ByteBuffer header = remotingCommand.encodeHeader(); // Write headers to buf out.writeBytes(header); // Get message body byte[] body = remotingCommand.getBody(); if (body != null) { // Message body out.writeBytes(body); } } catch (Exception e) { log.error("encode exception, " + RemotingHelper.parseChannelRemoteAddr(ctx.channel()), e); if (remotingCommand != null) { log.error(remotingCommand.toString()); } // Abnormal closure of channel RemotingUtil.closeChannel(ctx.channel()); } } }
Code the message header into this method org.apache.rocketmq.remoting.protocol.RemotingCommand#encodeHeader(int).
public ByteBuffer encodeHeader(final int bodyLength) { // 1 > Header length size header length int length = 4; // 2> header data length byte[] headerData; // Message header data encoding headerData = this.headerEncode(); length += headerData.length; // 3> body data length length += bodyLength; // Allocation of buffers ByteBuffer result = ByteBuffer.allocate(4 + length - bodyLength); // length result.putInt(length); // header length result.put(markProtocolType(headerData.length, serializeTypeCurrentRPC)); // header data result.put(headerData); result.flip(); return result; }
Enter the method org. apache. rocketmq. remoting. protocol. RemotingCommand headerEncode
private byte[] headerEncode() { this.makeCustomHeaderToNet(); if (SerializeType.ROCKETMQ == serializeTypeCurrentRPC) { // mq proxy code return RocketMQSerializable.rocketMQProtocolEncode(this); } else { // json encoding return RemotingSerializable.encode(this); } }
Enter this method, the mq protocol encodes org.apache.rocketmq.remoting.protocol.RocketMQSerializable#rocketMQProtocolEncode.
public static byte[] rocketMQProtocolEncode(RemotingCommand cmd) { // String remark byte[] remarkBytes = null; int remarkLen = 0; if (cmd.getRemark() != null && cmd.getRemark().length() > 0) { remarkBytes = cmd.getRemark().getBytes(CHARSET_UTF8); remarkLen = remarkBytes.length; } // HashMap<String, String> extFields byte[] extFieldsBytes = null; int extLen = 0; if (cmd.getExtFields() != null && !cmd.getExtFields().isEmpty()) { // Parametric serialization in map form extFieldsBytes = mapSerialize(cmd.getExtFields()); extLen = extFieldsBytes.length; } // Calculate the total length int totalLen = calTotalLen(remarkLen, extLen); // Allocation of buffers ByteBuffer headerBuffer = ByteBuffer.allocate(totalLen); // int code(~32767) headerBuffer.putShort((short) cmd.getCode()); // LanguageCode language headerBuffer.put(cmd.getLanguage().getCode()); // int version(~32767) headerBuffer.putShort((short) cmd.getVersion()); // int opaque headerBuffer.putInt(cmd.getOpaque()); // int flag headerBuffer.putInt(cmd.getFlag()); // String remark if (remarkBytes != null) { headerBuffer.putInt(remarkBytes.length); headerBuffer.put(remarkBytes); } else { headerBuffer.putInt(0); } // HashMap<String, String> extFields; if (extFieldsBytes != null) { headerBuffer.putInt(extFieldsBytes.length); headerBuffer.put(extFieldsBytes); } else { headerBuffer.putInt(0); } return headerBuffer.array(); }
Create a netty decoder
public class NettyDecoder extends LengthFieldBasedFrameDecoder { private static final InternalLogger log = InternalLoggerFactory.getLogger(RemotingHelper.ROCKETMQ_REMOTING); private static final int FRAME_MAX_LENGTH = Integer.parseInt(System.getProperty("com.rocketmq.remoting.frameMaxLength", "16777216")); public NettyDecoder() { super(FRAME_MAX_LENGTH, 0, 4, 0, 4); } @Override public Object decode(ChannelHandlerContext ctx, ByteBuf in) throws Exception { ByteBuf frame = null; try { // The problem of sticking packages is solved by netty Length Field Based Frame Decoder. It is convenient to define some rules in message header and decode them according to certain rules, such as total message length, encoding and decoding method. frame = (ByteBuf) super.decode(ctx, in); if (null == frame) { return null; } ByteBuffer byteBuffer = frame.nioBuffer(); return RemotingCommand.decode(byteBuffer); } catch (Exception e) { log.error("decode exception, " + RemotingHelper.parseChannelRemoteAddr(ctx.channel()), e); RemotingUtil.closeChannel(ctx.channel()); } finally { if (null != frame) { frame.release(); } } return null; } }
Enter this method, message decoding org. apache. rocketmq. remoting. protocol. RemotingCommand decode (java. nio. ByteBuffer)
public static RemotingCommand decode(final ByteBuffer byteBuffer) { int length = byteBuffer.limit(); // Source header length int oriHeaderLen = byteBuffer.getInt(); // Header length int headerLength = getHeaderLength(oriHeaderLen); byte[] headerData = new byte[headerLength]; byteBuffer.get(headerData); // Decode= according to the serialized type passed in the header RemotingCommand cmd = headerDecode(headerData, getProtocolType(oriHeaderLen)); int bodyLength = length - 4 - headerLength; byte[] bodyData = null; if (bodyLength > 0) { bodyData = new byte[bodyLength]; byteBuffer.get(bodyData); } cmd.body = bodyData; return cmd; }
Message header decoding, enter the method org. apache. rocketmq. remoting. protocol. RemotingCommand headerDecode
private static RemotingCommand headerDecode(byte[] headerData, SerializeType type) { switch (type) { case JSON: //header json formal decoding RemotingCommand resultJson = RemotingSerializable.decode(headerData, RemotingCommand.class); resultJson.setSerializeTypeCurrentRPC(type); return resultJson; case ROCKETMQ: //mq proxy deserialization RemotingCommand resultRMQ = RocketMQSerializable.rocketMQProtocolDecode(headerData); resultRMQ.setSerializeTypeCurrentRPC(type); return resultRMQ; default: break; } return null; }
mq protocol decoding, enter the method org.apache.rocketmq.remoting.protocol.RocketMQSerializable#rocketMQProtocolDecode
public static RemotingCommand rocketMQProtocolDecode(final byte[] headerArray) { RemotingCommand cmd = new RemotingCommand(); // Wrap the header byte array into a buffer ByteBuffer headerBuffer = ByteBuffer.wrap(headerArray); // int code(~32767) cmd.setCode(headerBuffer.getShort()); // LanguageCode language cmd.setLanguage(LanguageCode.valueOf(headerBuffer.get())); // int version(~32767) cmd.setVersion(headerBuffer.getShort()); // int opaque cmd.setOpaque(headerBuffer.getInt()); // int flag cmd.setFlag(headerBuffer.getInt()); // String remark int remarkLength = headerBuffer.getInt(); if (remarkLength > 0) { byte[] remarkContent = new byte[remarkLength]; headerBuffer.get(remarkContent); cmd.setRemark(new String(remarkContent, CHARSET_UTF8)); } // HashMap<String, String> extFields int extFieldsLength = headerBuffer.getInt(); if (extFieldsLength > 0) { byte[] extFieldsBytes = new byte[extFieldsLength]; headerBuffer.get(extFieldsBytes); // Deserialization of map Formal Data cmd.setExtFields(mapDeserialize(extFieldsBytes)); } return cmd; }
Not finished yet.
Said at the end
This analysis only represents personal views, for reference only.
Technical Exchange Group