在 RocketMQ 的架构体系中,NettyRemotingAbstract 类占据着举足轻重的地位,它是 RocketMQ 基于 Netty 实现网络通信的抽象基础类,为上层的 RemotingServer 和 RemotingClient 提供了核心的通信处理逻辑。深入理解 NettyRemotingAbstract 类,有助于我们把握 RocketMQ 消息收发、连接管理以及线程模型等关键机制。
一、NettyRemotingAbstract 类的作用
NettyRemotingAbstract 类主要负责封装与 Netty 相关的网络通信操作,将复杂的网络编程细节抽象化,使得 RocketMQ 的 Remoting 模块能够专注于消息协议处理和业务逻辑实现。它管理着 Netty 的 Channel、EventLoopGroup 等核心组件,提供了统一的消息发送、接收和处理接口,同时处理网络连接的建立、断开以及异常情况。
二、核心组件与初始化
(一)主要核心属性
/**
* Semaphore to limit maximum number of on-going one-way requests, which protects system memory footprint.
* 限制 one-way请求的最大数量
*/
protected final Semaphore semaphoreOneway;
/**
* Semaphore to limit maximum number of on-going asynchronous requests, which protects system memory footprint.
* 限制异步请求的最大数量
*/
protected final Semaphore semaphoreAsync;
/**
* This map caches all on-going requests.
* 通过remotingServer发送出去的所有请求都会缓存到这个map中 来等待请求的响应
*/
protected final ConcurrentMap<Integer /* opaque */, ResponseFuture> responseTable =
new ConcurrentHashMap<Integer, ResponseFuture>(256);
/**
* This container holds all processors per request code, aka, for each incoming request, we may look up the
* responding processor in this map to handle the request.
* key为请求id value值为一个pair pair里有对应的请求处理组件 以及请求处理组件对应的线程池
*/
protected final HashMap<Integer/* request code */, Pair<NettyRequestProcessor, ExecutorService>> processorTable =
new HashMap<Integer, Pair<NettyRequestProcessor, ExecutorService>>(64);
/**
* Executor to feed netty events to user defined {@link ChannelEventListener}.
* netty网络事件处理的组件
*
*/
protected final NettyEventExecutor nettyEventExecutor = new NettyEventExecutor();
/**
* The default request processor to use in case there is no exact match in {@link #processorTable} per request code.
* 默认的请求处理组件
*/
protected Pair<NettyRequestProcessor, ExecutorService> defaultRequestProcessor;
/**
* SSL context via which to create {@link SslHandler}.
* 用于进行安全网络加密通信 是属于netty的一个组件
*/
protected volatile SslContext sslContext;
/**
* custom rpc hooks
* 自定义的rpc 回调的钩子
*/
protected List<RPCHook> rpcHooks = new ArrayList<RPCHook>();
(二)消息编解码与处理器
- 编解码器
NettyRemotingAbstract 类使用自定义的编解码器将 RocketMQ 的消息协议(如 RemotingCommand)与字节流进行相互转换。编解码器的实现基于 Netty 的 ByteToMessageDecoder 和 MessageToByteEncoder,确保消息在网络传输过程中的正确格式。
a.编码
/**
* 针对内容进行编码
* @return
*/
public ByteBuffer encode() {
// 1> header length size
int length = 4;
// 2> header data length
byte[] headerData = this.headerEncode();
length += headerData.length;
// 3> body data length
if (this.body != null) {
length += body.length;
}
ByteBuffer result = ByteBuffer.allocate(4 + length);
// length
result.putInt(length);
// header length
result.putInt(markProtocolType(headerData.length, serializeTypeCurrentRPC));
// header data
result.put(headerData);
// body data;
if (this.body != null) {
result.put(this.body);
}
result.flip();
return result;
}
/**
* 进行头编码的操作
* @return
*/
private byte[] headerEncode() {
// 自定义的headers 放入到extfiled的Map中
this.makeCustomHeaderToNet();
//判断序列化的类型是哪种 采用对应的序列化方式
if (SerializeType.ROCKETMQ == serializeTypeCurrentRPC) {
return RocketMQSerializable.rocketMQProtocolEncode(this);
} else {
return RemotingSerializable.encode(this);
}
}
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()) {
extFieldsBytes = mapSerialize(cmd.getExtFields());
extLen = extFieldsBytes.length;
}
//计算总长度
int totalLen = calTotalLen(remarkLen, extLen);
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 {