本文基于dubbo 2.7.5版本代码
本文分析一下dubbo协议如何使用netty完成了报文的发送和接收,分为服务端和消费端解析源代码。
文章目录
- 一、服务端
- 二、消费端
一、服务端
下面的代码来自类NettyServer(基于netty4)的doOpen方法,该方法完成netty服务端的启动,执行完该方法,netty就可以接收请求了。
protected void doOpen() throws Throwable {
//ServerBootstrap是服务端启动辅助类,通过他可以方便的创建一个Netty服务端
bootstrap = new ServerBootstrap();
//创建处理客户端连接的线程池,线程只有一个,而且都是后台线程
bossGroup = new NioEventLoopGroup(1, new DefaultThreadFactory("NettyServerBoss", true));
创建处理客户端IO操作的线程池,线程数默认是处理器个数+1,可以通过参数iothreads配置
workerGroup = new NioEventLoopGroup(getUrl().getPositiveParameter(IO_THREADS_KEY, Constants.DEFAULT_IO_THREADS),
new DefaultThreadFactory("NettyServerWorker", true));
//创建服务端处理器
final NettyServerHandler nettyServerHandler = new NettyServerHandler(getUrl(), this);
//channels代表了所有客户端的连接
channels = nettyServerHandler.getChannels();
//配置ServerBootstrap
bootstrap.group(bossGroup, workerGroup)
.channel(NioServerSocketChannel.class)
//低延迟发送数据
.childOption(ChannelOption.TCP_NODELAY, Boolean.TRUE)
//SO_REUSEADDR=true表示允许重复使用本地地址和端口
.childOption(ChannelOption.SO_REUSEADDR, Boolean.TRUE)
//ALLOCATOR设置内存分配器,Channel收到的数据保存在该分配器分配的内存中
//默认使用的是池化直接内存
.childOption(ChannelOption.ALLOCATOR, PooledByteBufAllocator.DEFAULT)
.childHandler(new ChannelInitializer<NioSocketChannel>() {
@Override
//初始化连接channel
//当客户端连接进来后,便使用下面的方法对其Channel初始化
protected void initChannel(NioSocketChannel ch) throws Exception {
//获得心跳超时时间,可以通过heartbeat.timeout配置
//默认超时时间是3分钟
int idleTimeout = UrlUtils.getIdleTimeout(getUrl());
NettyCodecAdapter adapter = new NettyCodecAdapter(getCodec(), getUrl(), NettyServer.this);
//是否开启SSL
if (getUrl().getParameter(SSL_ENABLED_KEY, false)) {
ch.pipeline().addLast("negotiation",
SslHandlerInitializer.sslServerHandler(getUrl(), nettyServerHandler));
}
ch.pipeline()
.addLast("decoder", adapter.getDecoder())//设置解码器
.addLast("encoder", adapter.getEncoder())//设置编码器
.addLast("server-idle-handler", new IdleStateHandler(0, 0, idleTimeout, MILLISECONDS))//设置心跳检测处理器
.addLast("handler", nettyServerHandler);//设置自定义处理器,该处理器最后通过异步线程调用到真正提供服务的对象上
}
});
// 绑定端口
ChannelFuture channelFuture = bootstrap.bind(getBindAddress());
//执行下面的代码后,表示服务端可以接收外部请求了
channelFuture.syncUninterruptibly();
//获得代表服务端的channel对象
channel = channelFuture.channel();
}
二、消费端
消费端代码来自类NettyClient(基于netty4),消费端netty启动分为两步,第一个步是启动netty(doOpen方法),第二步是建立与服务端的连接(doConnect方法)。
消费端启动:
protected void doOpen() throws Throwable {
final NettyClientHandler nettyClientHandler = new NettyClientHandler(getUrl(), this);
//Bootstrap是消费端启动辅助类,通过他可以方便的创建一个Netty消费端
bootstrap = new Bootstrap();
//nioEventLoopGroup表示连接池中的线程数,是处理器个数+1
bootstrap.group(nioEventLoopGroup)
//下面三个参数设置含义参见服务端启动代码
.option(ChannelOption.SO_KEEPALIVE, true)
.option(ChannelOption.TCP_NODELAY, true)
.option(ChannelOption.ALLOCATOR, PooledByteBufAllocator.DEFAULT)
.channel(NioSocketChannel.class);
//设置调用超时毫秒数,默认值3秒,可以通过参数connect.timeout设置
bootstrap.option(ChannelOption.CONNECT_TIMEOUT_MILLIS, Math.max(3000, getConnectTimeout()));
bootstrap.handler(new ChannelInitializer() {
@Override
protected void initChannel(Channel ch) throws Exception {
//心跳间隔,默认是1分钟
int heartbeatInterval = UrlUtils.getHeartbeat(getUrl());
//检查是否开启SSL
if (getUrl().getParameter(SSL_ENABLED_KEY, false)) {
ch.pipeline().addLast("negotiation", SslHandlerInitializer.sslClientHandler(getUrl(), nettyClientHandler));
}
NettyCodecAdapter adapter = new NettyCodecAdapter(getCodec(), getUrl(), NettyClient.this);
ch.pipeline()
.addLast("decoder", adapter.getDecoder())//设置解码器
.addLast("encoder", adapter.getEncoder())//设置编码器
.addLast("client-idle-handler", new IdleStateHandler(heartbeatInterval, 0, 0, MILLISECONDS))设置心跳检测,默认每1分钟检测一次
.addLast("handler", nettyClientHandler);//设置自定义处理器
String socksProxyHost = ConfigUtils.getProperty(SOCKS_PROXY_HOST);
if(socksProxyHost != null) {
//使用socks5代理协议
int socksProxyPort = Integer.parseInt(ConfigUtils.getProperty(SOCKS_PROXY_PORT, DEFAULT_SOCKS_PROXY_PORT));
Socks5ProxyHandler socks5ProxyHandler = new Socks5ProxyHandler(new InetSocketAddress(socksProxyHost, socksProxyPort));
ch.pipeline().addFirst(socks5ProxyHandler);
}
}
});
}
建立与服务端的连接:
protected void doConnect() throws Throwable {
long start = System.currentTimeMillis();
//建立与服务端的连接
ChannelFuture future = bootstrap.connect(getConnectAddress());
try {
//用ret判断连接是否创建成功
boolean ret = future.awaitUninterruptibly(getConnectTimeout(), MILLISECONDS);
//下面的代码主要是判断是否连接成功,以及处理一些异常情况
if (ret && future.isSuccess()) {
//得到消费端Channel对象,在下面代码中保存该Channel对象
Channel newChannel = future.channel();
try {
//下面代码对旧Channel对象oldChannel关闭,如果在启动的时候,oldChannel是null
Channel oldChannel = NettyClient.this.channel;
if (oldChannel != null) {
try {
if (logger.isInfoEnabled()) {
logger.info("Close old netty channel " + oldChannel + " on create new netty channel " + newChannel);
}
oldChannel.close();
} finally {
NettyChannel.removeChannelIfDisconnected(oldChannel);
}
}
} finally {
if (NettyClient.this.isClosed()) {
try {
if (logger.isInfoEnabled()) {
logger.info("Close new netty channel " + newChannel + ", because the client closed.");
}
newChannel.close();
} finally {
NettyClient.this.channel = null;
NettyChannel.removeChannelIfDisconnected(newChannel);
}
} else {
NettyClient.this.channel = newChannel;
}
}
} else if (future.cause() != null) {
throw new RemotingException(this, "client(url: " + getUrl() + ") failed to connect to server "
+ getRemoteAddress() + ", error message is:" + future.cause().getMessage(), future.cause());
} else {
throw new RemotingException(this, "client(url: " + getUrl() + ") failed to connect to server "
+ getRemoteAddress() + " client-side timeout "
+ getConnectTimeout() + "ms (elapsed: " + (System.currentTimeMillis() - start) + "ms) from netty client "
+ NetUtils.getLocalHost() + " using dubbo version " + Version.getVersion());
}
} finally {
if (!isConnected()) {
}
}
}
dubbo协议对netty的使用给我们提供一个很好的编程参考。其中dubbo协议的实现就是在编解码处理器中完成的。
从这里我们可以看出,所谓RPC协议本质是通信双方的一个约定,协议是通过编码和解码实现的。