在《服务治理篇-应用架构的演变》中提到在分布式服务架构中,用于提高业务复用及整合的分布式服务框架(RPC)是关键。Dubbo是其中的典型代表。今天咱们就来手撕Dubbo源码,来达到彻底了解其本质的目的。 Dubbo怎样实现远程过程通信 手撕代码之前咱们来做一个宏观上的认知。 上面是Dubbo的部署架构。注册中心、配置中心、元数据中心这三大中心化组件的各自的职责、工作方式如下:
以上三个中心并不是运行 Dubbo 的必要条件,用户完全可以根据自身业务情况决定只启用其中一个或多个,以达到简化部署的目的。通常情况下,所有用户都会以独立的注册中心 开始 Dubbo 服务开发,而配置中心、元数据中心则会在微服务演进的过程中逐步地按需被引入进来。 下面是Dubbo早期的架构,这个架构核心组件只包含注册中心,基本就是运行Dubbo的最简架构。而注册中心也是异步弱依赖,唯一的强依赖是4. invoke这一步。也就是RPC调用发起请求到下游的部分。 Dubbo源码-场景设定 下面来手撕Dubbo的核心源码。为了好理解,这里讲Dubbo默认的dubbo协议使用http协议做说明。 下面的代码想达到的效果如上图,先来说说思路:
先来设计一个场景,要暴露的服务端如下: 服务的接口 实现类 以上咱们用服务提供者端核心5行代码、服务消费者端核心5行代码来实现。 Dubbo源码-服务提供者 五行代码 简单来说就是注册并暴露服务。按照这个思路咱们不难得到下面的提供端代码框架。这里面共5行有效代码,咱们一行一行来解释。 第一行 回到主线代码第一行有效代码,封装了一个url对象,这个是自己写的: 就是一个主机名端口的存储简单对象。 第二行 第二行有效代码作用是将url注册到远程注册中心上,咱们脑补一下注册中心的存储大概如下所示: 这里咱用一个map来模拟注册中心,不难得到下面的代码。 save是写文件来模拟的,这块不是重点,为了完整性简单提一下。 第三行 第三行有效代码作用是指明接口对应的实现类,这个实现时也使用map数据结构。本质上就是一个存取。 第四行 第四行有效代码是获取协议。作为一个框架来说需要具有多协议的支持,这里做了一个简单的实现。 协议的接口规定了两个动作,一个是启动时做的事情,一个是发送时做的事情。 第五行 先不着急看实现,先回到主线代码第五行。第五行就是把url传入后调用协议的start。这时候咱们来看启动方法的实现: 就是启动了一个httpServer。咱们来看httpServer.start的具体实现。这里面就是启动了一个tomcat。关键点是加了一个DispatcherServlet,并对所有的请求进行拦截处理。重点我用红框标出来了 这里本质上说明了web容器和servlet的核心作用。web容器主要是负责网络通信,servlet是java应用内部路由分发。咱们来看看路由分发是怎么做的: 咱们来分析一下HttpServerHandler.handler方法是怎么实现。分三步: 第一步,解析请求输入流。 第二步,解析出要调用的接口,从本地注册缓存中获取实现类。 第三步,利用java反射机制将解释出的请求参数传入实现类发起真正调用。 以上就完成了服务暴露的整个过程。 Dubbo源码-服务消费者 五行代码 客户端调用的整个过程比较简单,分成两步:
关键逻辑就是代理如何实现: 第一行 第一行有效代码:封装Invacation对象,将接口名、方法名、方法参数传入。 第二行 第二行有效代码:从注册中心获取url列表 第三行 第三行代码,因为获取到的是url列表,怎么选择发往哪个呢?这里采用的是随机算法决定发往的地址,这也是dubbo默认的地址选择策略。 第四行 第四行是获取协议,在服务提供端介绍过了,直接往下。 第五行 第五行是通过协议将invacation对象发送到url上。 看看httpProtocal内部是怎么实现的。 内部很简单,就是调用httpClient把请求发出去。虽然这个httpClient是自己写的,但是实际上功能和开源的那个差不多。咱们简单看一下就好: 总结 在《mybatis的本质和原理》中,我手撕了一个简易却包含mybatis核心的代码,来探究mybatis的本质原理。这一篇呢,我手撕了一个Dubbo的源码,是不是也没有那么难。 再来回顾一下今天讲述的代码完整链路: 服务提供者端将将接口注册到注册中心,并指明对应的实现类。通过tomcat、netty等实现网络通信,将服务暴露出去。内部使用servlet等实现路由在收到消费端请求时找到对应的实现类。 服务消费者使用从注册中心获取url列表,使用随机数等算法找到一个url,将参数、方法名当做http等协议的请求请求参数发起调用。 现在大家闭上眼睛想一想,Dubbo框架的核心原理是不是了然于胸了~ |
|