学习Dubbo-带你通过源码看看dubbo对netty的使用

前言前段时间,从头开始将netty源码了解了个大概,但都是原理上理解。刚好博主对dubbo框架了解过一些,这次就以dubbo框架为例,详细看看dubbo这种出色的开源框架是如何使用netty的,又是如何与框架本身逻辑进行融合的。本文分成两大部分,一部分是dubbo服务端对netty的封装,一部分是dubbo客户端对netty的封装,而每部分都分netty初始化和调用两个阶段,下面进入正题。前言前段时间,从头开始将netty源码了解了个大概,但都是原理上理解。刚好博主对dubbo框架了解过一些,这次就
摘要由CSDN通过智能技术生成

前言

前段时间,从头开始将netty源码了解了个大概,但都是原理上理解。刚好博主对dubbo框架了解过一些,这次就以dubbo框架为例,详细看看dubbo这种出色的开源框架是如何使用netty的,又是如何与框架本身逻辑进行融合的。

本文分成两大部分,一部分是dubbo服务端对netty的封装,一部分是dubbo客户端对netty的封装,而每部分都分netty初始化和调用两个阶段,下面进入正题。

前言

前段时间,从头开始将netty源码了解了个大概,但都是原理上理解。刚好博主对dubbo框架了解过一些,这次就以dubbo框架为例,详细看看dubbo这种出色的开源框架是如何使用netty的,又是如何与框架本身逻辑进行融合的。

本文分成两大部分,一部分是dubbo服务端对netty的封装,一部分是dubbo客户端对netty的封装,而每部分都分netty初始化和调用两个阶段,下面进入正题。

[图片上传失败…(image-eb3834-1614247162101)]

一、Dubbo服务端

Dubbo服务端对netty的调用始于服务导出,在服务导出的最后,会调用DubboProtocol#openServer方法,就是在此方法中完成的netty服务端的初始化(本文均以配置了netty通信为前提),下面就以该处作为起点探寻。

1、服务端初始化

openServer方法源码如下,主体逻辑是先获取了address作为key—ip:port格式的字符串,然后做了一个双重检查,server不存在则调createServer创建一个放入serverMap中。到这里我们可以知道,dubbo服务提供者中一个ip+端口对应一个nettyServer,所有的nettyServer统一放在一个ConcurrentHashMap中维护了起来。但其实通常情况下,一个服务提供者的服务器,只会暴露一个端口给dubbo用,故虽然用Map存起来,但一般只会有一个nettyServer。此处还要注意,dubbo中是暴露一个服务提供者执行一次export方法,即一个服务提供者接口触发一次openServer方法、对应一个nettyServer,下面跟进server的创建过程。

 1 private void openServer(URL url) {
 2         // find server.
 3         String key = url.getAddress();
 4         //client can export a service which's only for server to invoke
 5         boolean isServer = url.getParameter(IS_SERVER_KEY, true);
 6         if (isServer) {
 7             ProtocolServer server = serverMap.get(key);
 8             if (server == null) {
 9                 synchronized (this) {
10                     server = serverMap.get(key);
11                     if (server == null) {
12                         serverMap.put(key, createServer(url));
13                     }
14                 }
15             } else {
16                 // server supports reset, use together with override
17                 server.reset(url);
18             }
19         }
20     }

openServer调用的方法栈如下所示:

学习Dubbo-带你通过源码看看dubbo对netty的使用

进入NettyTransporter的bind方法,NettyTransporter一共有两个方法-bind和connect,前者初始化服务端时调用,后者初始化客户端时触发,源码如下:

 1 public class NettyTransporter implements Transporter {
 2     public static final String NAME = "netty";
 3     @Override
 4     public RemotingServer bind(URL url, ChannelHandler listener) throws RemotingException {
 5         return new NettyServer(url, listener);
 6     }
 7     @Override
 8     public Client connect(URL url, ChannelHandler listener) throws RemotingException {
 9         return new NettyClient(url, listener);
10     }
11 }

下面看NettyServer如何与netty关联起来的。先看下NettyServer的类图:

学习Dubbo-带你通过源码看看dubbo对netty的使用

有经验的园友看到类图估计就能猜到,此处是源码框架中常用的分层抽象,AbstractServer作为一个模板的抽象,继承它之后可以扩展出其他类型的通信,比如MinaServer、GrizzlyServer。下面回到本文的主角NettyServer,看看其构造器:

1 public NettyServer(URL url, ChannelHandler handler) throws RemotingException {
2         super(ExecutorUtil.setThreadName(url, SERVER_THREAD_POOL_NAME), ChannelHandlers.wrap(handler, url));
3     }

设置了一下url中的线程名参数,将handler和url进行了封装,然后调用了父类AbstractServer的构造器。

到这里,需要确定好入参的handler类型和传给父类构造器的handler类型。NettyServer构造器入参ChannelHandler是在HeaderExchanger#bind中封装的,方式如下:

1 public ExchangeServer bind(URL url, ExchangeHandler handler) throws RemotingException {
2         return new HeaderExchangeServer(Transporters.bind(url, new DecodeHandler(new HeaderExchangeHandler(handler))));
3     }

再进一步,bind方法入参ExchangeHandler的实现类要追溯到DubboProtocol,是其成员变量requestHandler如下:

private ExchangeHandler requestHandler = new ExchangeHandlerAdapter() {
// 省略若干个重写的方法逻辑
}

至此,NettyServer构造器入参ChannelHandler的类型已经确认了,其内部最终实现是DubboProtocol中的ExchangeHandlerAdapter,外部封装了一层HeaderExchangeHandler,又封装了一层DecodeHandler。简图如下:

学习Dubbo-带你通过源码看看dubbo对netty的使用

搞清楚NettyServer构造器入参的ChannelHandler之后,下面跟进ChannelHandlers.wrap方法,最终封装方法如下:

1 protected ChannelHandler wrapInternal(ChannelHandler handler, URL url) {
2         return new MultiMessageHandler(new HeartbeatHandler(ExtensionLoader.getExtensionLoader(Dispatcher.class)
3                 .getAdaptiveExtension().dispatch(handler, url)));
4     }

而Dispatcher默认是AllDispatcher,其dispatch方法如下:

1 public ChannelHandler dispatch(ChannelHandler handler, URL url) {
2         return new AllChannelHandler(handler, url);
3     }

至此,ChannelHandlers.wrap方法执行完后得到的ChannelHandler结构如下,采用的是装饰器模式,层层装饰。

[图片上传失败…(image-8e480e-1614247162100)]

了解清楚了wrap方法,下面回到主线,进入AbstractServer的构造器:

 1 public AbstractServer(URL url, ChannelHandler handler) throws RemotingException {
 2         // 1、调用父类构造器将这两个变量存起来,最终是存在了AbstractPeer中
 3         super(url, handler);
 4         // 2、设置两个address
 5         localAddress = getUrl().toInetSocketAddress();
 6         String bindIp = getUrl().getParameter(Constants.BIND_IP_KEY, getUrl().getHost());
 7         int bindPort = getUrl().getParameter(Constants.BIND_PORT_KEY, getUrl().getPort());
 8         if (url.getParameter(ANYHOST_KEY, false) || NetUtils.isInvalidLocalHost(bindIp)) {
 9             bindIp = ANYHOST_VALUE;
10         }
11         bindAddress = new InetSocketAddress(bindIp, bindPort);
12         this.accepts = url.getParameter(ACCEPTS_KEY, DEFAULT_ACCEPTS);
13         this.idleTimeout = url.getParameter(IDLE_TIMEOUT_KEY, DEFAULT_IDLE_TIMEOUT);
14         try {
15             // 3、完成netty源码的调用-开启netty服务端
16             doOpen();
17         } catch (Throwable t) {
18             // 省略异常处理
19         }
20         // 4、获取/创建线程池
21         executor = executorRepository.createExecutorIfAbsent(url);
22     }

1/2的逻辑较简单,3和4才是重点,下面进入3处的doOpen方法,doOpen方法在AbstractServer中是抽象方法,所以要到其子类NettyServer中看:

 1 protected void doOpen() throws Throwable {
 2         // 这里可以看到熟悉的netty代码了
 3         bootstrap = new ServerBootstrap();
 4         // bossGroup一个线程
 5         bossGroup = new NioEventLoopGroup(1, new DefaultThreadFactory("NettyServerBoss", tru
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值