前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Netty在Dubbo中使用了哪些Handler

Netty在Dubbo中使用了哪些Handler

作者头像
书唐瑞
发布2022-06-02 14:08:04
3890
发布2022-06-02 14:08:04
举报
文章被收录于专栏:Netty历险记

本篇以Dubbo作为服务端为例.

当配置如下信息时

代码语言:javascript
复制
<dubbo:application name="infuq-dubbo-provider" />
<dubbo:registry protocol="zookeeper" address="127.0.0.1:2181" check="false" />
<dubbo:protocol name="dubbo" port="20880" threads="200"/>
<dubbo:service ref="queryUserInfoFacade" interface="com.infuq.facade.QueryUserInfoFacade" version="1.0.0" />
<bean id="queryUserInfoFacade" class="com.infuq.facade.impl.QueryUserInfoFacadeImpl" />

Spring在启动的过程中,通过DubboNamespaceHandler解析上面的标签.

将每个标签与之对应的BeanDefinition注册到BeanFactory中.

Spring再根据BeanDefinition生成对应的Bean实例.

上面的<dubbo:service />标签最终会生成对应的ServiceBean实例.

代码语言:javascript
复制
// 源码位置: com.alibaba.dubbo.config.spring.ServiceBean
public class ServiceBean<T> extends ServiceConfig<T> implements InitializingBean, DisposableBean,
        ApplicationContextAware, ApplicationListener<ContextRefreshedEvent>, BeanNameAware,
        ApplicationEventPublisherAware {
          
}

ServiceBean实现了ApplicationListener<ContextRefreshedEvent>接口.

在Spring创建完所有的Bean之后,最后会发布一个ContextRefreshedEvent事件.

因此ServiceBean的onApplicationEvent()方法会被执行.

代码语言:javascript
复制
public void onApplicationEvent(ContextRefreshedEvent event) {
    if (isDelay() && !isExported() && !isUnexported()) {
        if (logger.isInfoEnabled()) {
            logger.info("The service ready on spring started. service: " + getInterface());
        }
        // 暴露服务
        export();
    }
}

接下来就进入到了服务暴露的过程.

本篇文章不会详细讲述暴露流程

服务暴露会完成两件事情. 第一件事情是通过Netty开启服务,监听端口.

第二件事情是将服务注册到注册中心.

跟进export()方法, 最后会来到DubboProtocol类.

大体看下它是如何开启服务,监听端口? 留意下,有个属性requestHandler.

代码语言:javascript
复制
public class DubboProtocol extends AbstractProtocol {
  private ExchangeHandler requestHandler = new ExchangeHandlerAdapter() { ... };

  private ExchangeServer createServer(URL url) {
    // 绑定
    server = Exchangers.bind(url, requestHandler);
  }

}

跟进bind()方法,最后会来到NettyServer的doOpen()方法.

留意下,有个属性nettyServerHandler.

代码语言:javascript
复制
// 源码位置: com.alibaba.dubbo.remoting.transport.netty4.NettyServer
protected void doOpen() throws Throwable {
    bootstrap = new ServerBootstrap();

    bossGroup = new NioEventLoopGroup(1, new DefaultThreadFactory("NettyServerBoss", true));
    workerGroup = new NioEventLoopGroup(getUrl().getPositiveParameter(Constants.IO_THREADS_KEY, Constants.DEFAULT_IO_THREADS), new DefaultThreadFactory("NettyServerWorker", true));

    // 重要的Handler
    final NettyServerHandler nettyServerHandler = new NettyServerHandler(getUrl(), this);
    channels = nettyServerHandler.getChannels();

    bootstrap.group(bossGroup, workerGroup)
            .channel(NioServerSocketChannel.class)
            .childOption(ChannelOption.TCP_NODELAY, Boolean.TRUE)
            .childOption(ChannelOption.SO_REUSEADDR, Boolean.TRUE)
            .childOption(ChannelOption.ALLOCATOR, PooledByteBufAllocator.DEFAULT)
            .childHandler(new ChannelInitializer<NioSocketChannel>() {
                @Override
                protected void initChannel(NioSocketChannel ch) throws Exception {
                    NettyCodecAdapter adapter = new NettyCodecAdapter(getCodec(), getUrl(), NettyServer.this);
                    ch.pipeline()
                            .addLast("decoder", adapter.getDecoder())
                            .addLast("encoder", adapter.getEncoder())
                            .addLast("handler", nettyServerHandler);// 处理请求和响应的Handler
                }
            });
    // bind
    ChannelFuture channelFuture = bootstrap.bind(getBindAddress());
    channelFuture.syncUninterruptibly();
    channel = channelFuture.channel();

}

从DubboProtocol类的requestHandler属性到NettyServer的nettyServerHandler属性.

这一路会经历很多Handler,经过层层封装,最后才封装成NettyServerHandler.

它会经历如下Handler

NettyServerHandler -> NettyServer -> MultiMessageHandler -> HeartbeatHandler -> AllChannelHandler -> DecodeHandler -> HeaderExchangeHandler -> ExchangeHandler

当客户端连接服务端,或者发送数据到服务端的时候,

首先会由NettyServerHandler处理请求,然后依次将请求传递下去,最后到ExchangeHandler.

那么这些Handler是否都由同一个线程执行的吗? 并不是

如上图, 在AllChannelHandler中有个executor属性,它是一个线程池.

NettyServerHandler -> NettyServer -> MultiMessageHandler -> HeartbeatHandler -> AllChannelHandler

以上这几个Handler是由同一个线程执行的, 是由Netty的IO线程执行的, 名称类似NettyServerWorker-5-7

-> DecodeHandler -> HeaderExchangeHandler -> ExchangeHandler

以上这几个Handler是由另一类线程执行的, 是由AllChannelHandler中的线程池执行的, 名称类似DubboServerHandler-2.0.1.15:20880-thread-57

也就是说, Netty的IO线程在接收到请求后, 先依次执行

NettyServerHandler -> NettyServer ->

MultiMessageHandler -> HeartbeatHandler

-> AllChannelHandler 这五个Handler.

之后会由AllChannelHandler中的线程池执行后面的DecodeHandler -> HeaderExchangeHandler -> ExchangeHandler 这三个Handler.

代码语言:javascript
复制
<dubbo:protocol name="dubbo" port="20880" threads="200" threadpool="fixed"  />

使用threads=200, threadpool=fixed 就是在配置图中红色区域的线程池. 线程池也是调优的一个地方.

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2021-01-24,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 Netty历险记 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
相关产品与服务
微服务引擎 TSE
微服务引擎(Tencent Cloud Service Engine)提供开箱即用的云上全场景微服务解决方案。支持开源增强的云原生注册配置中心(Zookeeper、Nacos 和 Apollo),北极星网格(腾讯自研并开源的 PolarisMesh)、云原生 API 网关(Kong)以及微服务应用托管的弹性微服务平台。微服务引擎完全兼容开源版本的使用方式,在功能、可用性和可运维性等多个方面进行增强。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档