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

本文详细解释了RemotingServer接口在RocketMQ中的作用,涉及消息编解码、自定义通信协议设计,以及以NameServer为例展示Netty服务器启动流程,包括Dubbo、Redis和Springcloud等相关技术的应用。
摘要由CSDN通过智能技术生成

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开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Java开发知识点,真正体系化!

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

如果你觉得这些内容对你有帮助,可以扫码获取!!(备注Java获取)

img

最后

小编精心为大家准备了一手资料

以上Java高级架构资料、源码、笔记、视频。Dubbo、Redis、设计模式、Netty、zookeeper、Spring cloud、分布式、高并发等架构技术

【附】架构书籍

  1. BAT面试的20道高频数据库问题解析
  2. Java面试宝典
  3. Netty实战
  4. 算法

BATJ面试要点及Java架构师进阶资料

《互联网大厂面试真题解析、进阶开发核心学习笔记、全套讲解视频、实战项目源码讲义》点击传送门即可获取!
alt=“img” style=“zoom: 33%;” />

最后

小编精心为大家准备了一手资料

[外链图片转存中…(img-BPY2tZT8-1713283585155)]

[外链图片转存中…(img-TEBD4Fdd-1713283585156)]

以上Java高级架构资料、源码、笔记、视频。Dubbo、Redis、设计模式、Netty、zookeeper、Spring cloud、分布式、高并发等架构技术

【附】架构书籍

  1. BAT面试的20道高频数据库问题解析
  2. Java面试宝典
  3. Netty实战
  4. 算法

[外链图片转存中…(img-XdKqxvx9-1713283585156)]

BATJ面试要点及Java架构师进阶资料

[外链图片转存中…(img-RVJWfp69-1713283585156)]

《互联网大厂面试真题解析、进阶开发核心学习笔记、全套讲解视频、实战项目源码讲义》点击传送门即可获取!

  • 30
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值