RocketMQ高手之路系列之十:RocketMQ网络通信原理分析(一)

public interface RemotingServer extends RemotingService {

//注册处理请求的处理器, 根据requestCode, 获取处理器,处理请求

void registerProcessor(final int requestCode, final NettyRequestProcessor processor,

final ExecutorService executor);

void registerDefaultProcessor(final NettyRequestProcessor processor, final ExecutorService executor);

int localListenPort();

Pair<NettyRequestProcessor, ExecutorService> getProcessorPair(final int requestCode);

RemotingCommand invokeSync(final Channel channel, final RemotingCommand request,

final long timeoutMillis) throws InterruptedException, RemotingSendRequestException,

RemotingTimeoutException;

void invokeAsync(final Channel channel, final RemotingCommand request, final long timeoutMillis,

final InvokeCallback invokeCallback) throws InterruptedException,

RemotingTooMuchRequestException, RemotingTimeoutException, RemotingSendRequestException;

//单向发送消息,只管发送消息,不管消息发送的结果

void invokeOneway(final Channel channel, final RemotingCommand request, final long timeoutMillis)

throws InterruptedException, RemotingTooMuchRequestException, RemotingTimeoutException,

RemotingSendRequestException;

}

二、消息编解码


(1)通信协议设计

在这里插入图片描述

(图片来自于网络)

(2)编码

remoting模块对于消息进行了自定义协议,将发送的消息以及收到的消息封装为RemotingCommand对象。

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.put(markProtocolType(headerData.length, serializeTypeCurrentRPC));

// header data

result.put(headerData);

// body data;

if (this.body != null) {

result.put(this.body);

}

result.flip();

return result;

}

(3)解码

public static RemotingCommand decode(final ByteBuffer byteBuffer) {

// 获取byteBuffer的总长度

int length = byteBuffer.limit();

int oriHeaderLen = byteBuffer.getInt();

int headerLength = getHeaderLength(oriHeaderLen);

// 保存header data

byte[] headerData = new byte[headerLength];

byteBuffer.get(headerData);

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;

}

三、以NameServer启动为例


在了解remoting模块的核心接口之后,我们接下来看下具体的实现过程。其实在如NameServer启动过程中,它本身就会作为一个Netty的服务端进行启动。我们这里先忽略掉NameServer启动过程中的其他的配置操作,着重对Netty作为服务端启动的流程。大致的启动流程如下所示:

在这里插入图片描述

NameServer实际作为Netty服务端启动底层网络连接的,我们都知道它的作用是作为服务端提供给Broker进行注册以及客户端向其拉取路由信息。

NameServer启动过程中实际是创建了NettyRemotingServer,而NettyRemotingServer是RocketMQ自己开发的网络连接组件,当然它的底层实际是基于Netty的接口实现的ServerBootstrap。下列是start的方法,同样我们只关注Netty服务器的启动。

public static NamesrvController start(final NamesrvController controller) throws Exception {

if (null == controller) {

throw new IllegalArgumentException(“NamesrvController is null”);

}

//初始化

boolean initResult = controller.initialize();

if (!initResult) {

controller.shutdown();

System.exit(-3);

}

//通过Runtime类注册了一个JVM关闭时的shutdown的钩子

Runtime.getRuntime().addShutdownHook(new ShutdownHookThread(log, new Callable() {

@Override

public Void call() throws Exception {

controller.shutdown();

return null;

}

}));

controller.start();

return controller;

}

其中初始化的方法如下所示:

public boolean initialize() {

//加载配置

this.kvConfigManager.load();

//构建Netty服务器

this.remotingServer = new NettyRemotingServer(this.nettyServerConfig, this.brokerHousekeepingService);

//Netty的分作线程池

this.remotingExecutor =

Executors.newFixedThreadPool(nettyServerConfig.getServerWorkerThreads(), new ThreadFactoryImpl(“RemotingExecutorThread_”));

//将工作线程池分配给Netty服务器

this.registerProcessor();

this.scheduledExecutorService.scheduleAtFixedRate(new Runnable() {

@Override

public void run() {

NamesrvController.this.routeInfoManager.scanNotActiveBroker();

}

}, 5, 10, TimeUnit.SECONDS);

this.scheduledExecutorService.scheduleAtFixedRate(new Runnable() {

@Override

public void run() {

NamesrvController.this.kvConfigManager.printAllPeriodically();

}

}, 1, 10, TimeUnit.MINUTES);

if (TlsSystemConfig.tlsMode != TlsMode.DISABLED) {

// Register a listener to reload SslContext

try {

fileWatchService = new FileWatchService(

new String[] {

TlsSystemConfig.tlsServerCertPath,

TlsSystemConfig.tlsServerKeyPath,

TlsSystemConfig.tlsServerTrustCertPath

},

new FileWatchService.Listener() {

boolean certChanged, keyChanged = false;

@Override

public void onChanged(String path) {

if (path.equals(TlsSystemConfig.tlsServerTrustCertPath)) {

log.info(“The trust certificate changed, reload the ssl context”);

reloadServerSslContext();

}

if (path.equals(TlsSystemConfig.tlsServerCertPath)) {

certChanged = true;

}

if (path.equals(TlsSystemConfig.tlsServerKeyPath)) {

keyChanged = true;

}

if (certChanged && keyChanged) {

log.info(“The certificate and private key changed, reload the ssl context”);

certChanged = keyChanged = false;

reloadServerSslContext();

}

}

private void reloadServerSslContext() {

((NettyRemotingServer) remotingServer).loadSslContext();

}

});

} catch (Exception e) {

log.warn(“FileWatchService created error, can’t load the certificate dynamically”);

}

}

return true;

}

初始化完成之后进行启动,我们可以看到实际启动的是NettyRemotingServer。

public void start() throws Exception {

this.remotingServer.start();

if (this.fileWatchService != null) {

this.fileWatchService.start();

}

}

NettyRemotingServer启动过程如下代码所示:

@Override

public void start() {

this.defaultEventExecutorGroup = new DefaultEventExecutorGroup(

nettyServerConfig.getServerWorkerThreads(),

new ThreadFactory() {

private AtomicInteger threadIndex = new AtomicInteger(0);

@Override

public Thread newThread(Runnable r) {

return new Thread(r, “NettyServerCodecThread_” + this.threadIndex.incrementAndGet());

}

});

//配置启动Netty服务器

ServerBootstrap childHandler =

//各种网络配置

this.serverBootstrap.group(this.eventLoopGroupBoss, this.eventLoopGroupSelector)

.channel(useEpoll() ? EpollServerSocketChannel.class : NioServerSocketChannel.class)

.option(ChannelOption.SO_BACKLOG, 1024)

.option(ChannelOption.SO_REUSEADDR, true)

最后

自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数Java工程师,想要提升技能,往往是自己摸索成长,自己不成体系的自学效果低效漫长且无助。

因此收集整理了一份《2024年Java开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Java开发知识点,不论你是刚入门Android开发的新手,还是希望在技术上不断提升的资深开发者,这些资料都将为你打开新的学习之门!

如果你觉得这些内容对你有帮助,需要这份全套学习资料的朋友可以戳我获取!!

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!
成长,自己不成体系的自学效果低效漫长且无助。**

因此收集整理了一份《2024年Java开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。

[外链图片转存中…(img-6GtBZ6k2-1715570894216)]

[外链图片转存中…(img-9TeKfAEI-1715570894217)]

[外链图片转存中…(img-5bd29NgK-1715570894218)]

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Java开发知识点,不论你是刚入门Android开发的新手,还是希望在技术上不断提升的资深开发者,这些资料都将为你打开新的学习之门!

如果你觉得这些内容对你有帮助,需要这份全套学习资料的朋友可以戳我获取!!

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值