再谈Dubbo

最近工作中由于涉及到微服务这块,所以把Dubbo又重新回顾了一遍,现在做个总结。

为什么使用RPC框架?

一个老生常谈的问题,为什么使用RPC框架?如果你的系统是单机系统,完全可以不用考虑这个问题,但是这些年微服务盛行,SOAP,企业级总线这些过于重量级,大厂都不一定能玩得转的玩意,所以何况是中小企业。所以退而求其次选择RESTful或者轻量级RPC框架,Dubbo之所以能在国内脱颖而出,肯定是有它的原因,在这里我们就不详细说了。

Dubbo能为我们做什么?

其实应该说作为一款企业级RPC框架应该具备哪些基本的功能:

1.基本的远程调用
2.微服务管理
3.监控功能
4.负载均衡
5.网关限流
6.高并发,高可用

基本的远程调用

这个是我们今天要谈的重点,Dubbo是怎么使用远程调用的?

说到远程调用,我们可以采用HTTP,TCP/IP等协议,我们在大学的网络课程里面学过,这些协议承载了太多内容,很多东西都是我们不需要的,我们调用选择一个方法其实只是传几个参数而已,没有必要用HTTP,TCP/IP传输太多我们不需要的内容吧,所以Dubbo选择自己实现了自己的协议,那就是Dubbo协议。

现在协议有了,那么协议的内容通过什么方式传输呢?既然HTTP,TCP/IP不能用,那么我们只能选择socket,但是socket太低端,需要自己考虑的东西太多,所以当然是选择业内作为成熟的框架Netty。

那么Netty是如何实现网络通信的?我们先看服务端实现代码,这个段代码来自Dubbo的Netty Server 类doOpen方法


        NettyHelper.setNettyLoggerFactory();
        ExecutorService boss = Executors.newCachedThreadPool(new NamedThreadFactory("NettyServerBoss", true));
        ExecutorService worker = Executors.newCachedThreadPool(new NamedThreadFactory("NettyServerWorker", true));
        ChannelFactory channelFactory = new NioServerSocketChannelFactory(boss, worker, this.getUrl().getPositiveParameter("iothreads", Constants.DEFAULT_IO_THREADS));
        this.bootstrap = new ServerBootstrap(channelFactory);
        final NettyHandler nettyHandler = new NettyHandler(this.getUrl(), this);
        this.channels = nettyHandler.getChannels();
        this.bootstrap.setPipelineFactory(new ChannelPipelineFactory() {
            public ChannelPipeline getPipeline() {
                NettyCodecAdapter adapter = new NettyCodecAdapter(NettyServer.this.getCodec(), NettyServer.this.getUrl(), NettyServer.this);
                ChannelPipeline pipeline = Channels.pipeline();
                pipeline.addLast("decoder", adapter.getDecoder());
                pipeline.addLast("encoder", adapter.getEncoder());
                pipeline.addLast("handler", nettyHandler);
                return pipeline;
            }
        });

        this.channel = this.bootstrap.bind(this.getBindAddress());

如果你对netty不熟悉的话可能看不懂上面的代码,我来解释一下:

ServerBootstrap为netty的启动类,传入参数是ChannelFactory,这个类有两个构造函数,netty使用reactor模式进行网络通信,我们可以看到传了两个线程池,一个是boss,一个worker,顾名思义boss是老板,worker是打工仔,boss负责请求的接待,然后将工作分配给worker进行处理,最后一个参数是需要分配的io线程数的个数,这个是从URL里面获取的,这个相当于Dubbo的一个全局上下文,这个参数是netty 的workers的数量,所有workers共享worker线程池,说白就是这些workers轮流去干活,干玩活把任务丢给worker线程池进行处理,有兴趣的同学可以去看下Netty源代码。

接着我们看到setPipelineFactory方法,这个是Netty内部实现的管道机制,简单来说就是服务端如果有数据处理就把数据丢到这些管道依次执行,nettyHandler是Dubbo实现的一个管道处理方法。这些细节其实我们不用关注太多,其实说来就是Netty帮我们创建了一个 socket链接,如果有请求连接进来,或者有数据进来,我们只需要关注具体的实现业务逻辑,其它方面Netty已经帮我们处理好了。

说到了服务端的实现,那么客户端是如何做的呢?

聪明的你可能已经想到,既然有NettyServer,那么肯定有NettyClient,没错!下面我们来看NettyClient的doOpen方法实现:


        NettyHelper.setNettyLoggerFactory();
        this.bootstrap = new ClientBootstrap(channelFactory);
        this.bootstrap.setOption("keepAlive", true);
        this.bootstrap.setOption("tcpNoDelay", true);
        this.bootstrap.setOption("connectTimeoutMillis", this.getTimeout());
        final NettyHandler nettyHandler = new NettyHandler(this.getUrl(), this);
        this.bootstrap.setPipelineFactory(new ChannelPipelineFactory() {
            public ChannelPipeline getPipeline() {
                NettyCodecAdapter adapter = new NettyCodecAdapter(NettyClient.this.getCodec(), NettyClient.this.getUrl(), NettyClient.this);
                ChannelPipeline pipeline = Channels.pipeline();
                pipeline.addLast("decoder", adapter.getDecoder());
                pipeline.addLast("encoder", adapter.getEncoder());
                pipeline.addLast("handler", nettyHandler);
                return pipeline;
            }
        });

注意以上代码是基于2.8.4,对于最新的Dubbo可能不是这样,但是原理是一样的,如果你有理解上面Server端的初始化过程,那么这段代码应该不难看懂。

所以RPC不过如此吧,但是这只是Dubbo的冰山一脚,本篇文章只是为了说明原理,列举了底层核心代码而已,在实现方法Dubbo使用了CompleteFuture来实现异步调度。

其它方面

Dubbo作为一款企业级别的框架,当然不会只能上面两段代码解决所有问题,为了考虑扩展性,Dubbo提供了注册中心,以及自己一套URL的上下文传输机制,简单来说URL就是DubboInvoker,DubboInvoker就是Dubbo的核心,说白了,DubboInvoker就是封装上面两端代码,使RPC使用对我们开发人员透明,我们不用去关注底层的网络传输,序列化,就是代理调用机制。为了实现扩展Dubbo参考TCP/IP协议,实现了Exchanger和Transporter两层,刚才列举上面6点中的第5点是在这两层实现的。

技术方面,Dubbo实现了自己的IOC机制SPI扩展,为了实现高并发,Dubbo当然会实现自己的线程调度机制以及高效的时间轮算法,具体可以参考我之前写的《Dubbo的线程模型》。

至于负载均衡,集群,以及配置中心的实现也是其可圈可点的地方,有机会我下次再分享吧,今天主要是介绍Dubbo的底层原理。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,544评论 6 501
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,430评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,764评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,193评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,216评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,182评论 1 299
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,063评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,917评论 0 274
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,329评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,543评论 2 332
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,722评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,425评论 5 343
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,019评论 3 326
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,671评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,825评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,729评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,614评论 2 353

推荐阅读更多精彩内容