java架构师课程学习心得-第4篇-仿照Dubbo手写RPC框架

仿照Dubbo手写RPC框架


前言

Dubbo是一款开源的RPC框架,本文通过手写RPC框架的方式来加深对Dubbo的理解


一、RPC是什么?

RPC(Remote Procedure Call)远程过程调用,简单的理解是一个节点请求另一个节点提供的服务。它是一种通过网络从远程计算机程序上请求服务,而不需要了解底层网络技术的思想。

RPC 是一种技术思想而非一种规范或协议,常见 RPC 技术和框架有:

应用级的服务框架:阿里的 Dubbo/Dubbox、Google gRPC、Spring Boot/Spring Cloud。
远程通信协议:RMI、Socket、SOAP(HTTP XML)、REST(HTTP JSON)。
通信框架:MINA 和 Netty。

二、完整RPC框架的结构

在一个典型 RPC 的使用场景中,包含了服务发现、负载、容错、网络传输、序列化等组件,其中“RPC 协议”就指明了程序如何进行网络传输和序列化。
在这里插入图片描述

三、RPC框架的核心功能

RPC 的核心功能是指实现一个 RPC 最重要的功能模块,就是上图中的”RPC 协议”部分。
RPC的协议有很多,比如最早的CORBA,Java RMI,Web Service的RPC风格,Hessian,Thrift,甚至Rest API。网络传输方式有 HTTP、TCP、Websocket 等
在这里插入图片描述

一个 RPC 的核心功能主要有 5 个部分组成,分别是:客户端、客户端 Stub、网络传输模块、服务端 Stub、服务端等。
在这里插入图片描述
下面分别介绍核心 RPC 框架的重要组成:

客户端(Client):服务调用方。
客户端存根(Client Stub):存放服务端地址信息,将客户端的请求参数数据信息打包成网络消息,再通过网络传输发送给服务端。
服务端存根(Server Stub):接收客户端发送过来的请求消息并进行解包,然后再调用本地服务进行处理。
服务端(Server):服务的真正提供者。
Network Service:底层传输,可以是 TCP 或 HTTP。

RPC核心三大步骤

1、 寻址:客户端是如何知晓服务端具体(ip+port)地址的,比如输入域名会通过DNS服务查询到对应的ip
2、通讯方式:选择tcp/udp,以及具体的上层协议;比如http就是一种基于tcp之上的协议
3、数据序列化:客户端和服务端交互时,对数据使用的序列化方式,比如json,xml等

四、为什么要引入RPC

关于这个问题,首先我们来看看下面的场景:
假如我们的电商系统中需要一个发送短信的服务,调用的是A平台的接口,如果我们把短息服务和电商系统的其他模块都放在一个jar包中,那么当我们需要切换到B平台发送短信时,就需要修改代码,然后再重新部署。如果系统中还有其他类似的服务,那么对于代码管理和运维来说是个很繁重的工作,且这样的系统耦合度会很高。
使用RPC技术带来的好处:
1、使用RPC技术可以使系统降低耦合性,系统拆分成多模块后,各模块之间使用RPC通信,各模块可以独立部署,从而达到解耦的目的
2、RPC底层网络是长链接,不必每次通信都要像http一样去3次握手什么的,减少了网络开销
3、RPC框架一般都有注册中心,有丰富的监控管理、发布、下线接口、动态扩展等,对调用方来说是无感知、统一化的操作。
4、RPC协议的底层传输和序列化都可以自定义,在网络传输过程中是比较安全的。

五、手写RPC框架的流程和模块说明

1、使用的一些技术点

1、本文的RPC框架是用redis做注册中心,基于spring提供远程服务,使用spring注解完成远程服务的注入和调用
2、使用了spring中的bean的后置处理器,来做bean的扩展
3、使用了spi机制,加载配置类
4、使用了netty,完成底层的网络传输
5、使用redis的订阅发布机制,完成服务的注册发现,以及服务的上线和下线通知
6、客户端和服务端都采用了代理机制,来完成服务的调用
7、TrpcBootstrap类是服务调用者和服务提供者的统一入口,TrpcProtocol类是实现rpc协议的统一入口

2、一些模块的说明

类:OrderApplication
1.@EnableTRPC
2.context.star()
3.多线程调用OrderService
1.通过@EnableTRPC开启rpc功能
2.开启spring服务
3.子线程获取OrderServiceImpl对象,发起远程调用

注解:EnableTRPC
@Import()
1.TRPCPostProcessor.class
2.TRPCConfiguration.class
1.通过@import注解,当服务启动时调用TRPCPostProcessor.class
TRPCConfiguration.class,完成一些类的注册
类:TRPCConfiguration
1.BeanDefinitionBuilder
2.environment.getProperty
3.BeanDefinitionRegistry
1.获取配置相关类的构建器
2.将配置文件中的属性,注入到配置类对应的字段中
3.完成配置类对象bean的注册

类:TRPCPostProcessor
1.ServiceConfig
2.ReferenceConfig
3.TrpcBootstrap
1.服务提供者,通过TRpcService注解找到服务的实现类,构建serviceConfig配置,最后由TrpcBootstrap.export()暴露服务
2.服务调用者,通过TRpcReference注解找到执行远程调用的对象变量,通过TrpcBootstrap.getReferenceBean(referenceConfig)获取服务代理对象,并注入到变量中

类:TrpcBootstrap
1.export()
2.getReferenceBean()
1.服务提供者
1.1 ProxyFactory.getInvoker()获取服务实现类的代理对象
1.2 serviceConfig.getProtocolConfigs()获取具体协议,构建协议的URI
1.3 protocol.export(exportUri, invoker)暴露协议
1.4 RegistryService通过spi加载,然后注册协议到redis

2.服务调用者
2.1 new ClusterInvoker()获取服务提供者实例集群
2.2 ProxyFactory.getProxy(clusterInvoker, new Class[]{referenceConfig.getService()})获取实例集群的代理
2.3代理调用invoke方法时,会采用负载均衡策略选择实例聚群中的一个实例进行远程调用

类:ClusterInvoker
1.构造函数
2.invoke()
2.1 loadbalance
1.在构造函数中获取ReferenceConfig中的注册中心uri
2.通过RegistryService和uri,初始化注册中心,并订阅相关服务,最后通过notify回调,更新本地服务列表
3.当notiy回调通知有新的服务uri时,会通过protocol.refer(uri)创建一个新的invoker服务代理,并添加到本地服务列表中

类:ProxyFactory
1 getProxy()
1.1 new InvokerInvocationHandler(invoker)
2.getInvoker()
2.1 new Invoker()
1.服务提供者
1.1 getInvoker(Object proxy, Class type), 返回new Invoker()匿名代理类
2.1 参数proxy是具体提供服务的实现类,匿名代理类中的invoke(RpcInvocation rpcInvocation)方法就会通过反射调用服务实现类的方法

2.服务调用者
2.1 getProxy(Invoker invoker), 方法返回InvokerInvocationHandler代理对象
2.2 该代理对象执行本地方法,有需要远程调用的方法时,就构建RpcInvocation对象
2.3 最后InvokerInvocationHandler(invoker)对象会调用真正服务实现类的invoker来执行服务,既这里多了一层代理

类:TrpcProtocol
1.export()
2.refer()
2.1 transporter.connect()
2.2 TrpcClientInvoker
1.服务提供者
1.1 getInvoker(Object proxy, Class type),获取序列化对象,编解码器对象,服务端Handler对象
1.2 通过spi加载网络协议的实现类transporter,通过transporter.export()导出服务

2.服务调用者
2.1 refer(URI consumerUri),获取序列化对象,编解码器对象,客户端Handler对象
2.2 通过spi加载网络协议的实现类transporter,通过transporter.connect()获取客户端连接
2.3 new TrpcClientInvoker(connect, serialization)返回客户端连接代理,既ClusterInvoker中的服务代理

类:TrpcClientInvoker
1.implements Invoker
2.invoke(RpcInvocation rpcInvocation)
2.1 client.getChannel().send(requestBody)
2.2 future=TrpcClientHandler.waitResult()
2.3 return (Response) future.get()
1.实现了Invoker接口
2.invoke(RpcInvocation rpcInvocation)
2.1方法中通过client中的trpcChannel来完成远程服务调用,既把相关数据发送到netty服务端
2.2 通过TrpcClientHandler异步获取服务端返回的响应,并通过id唯一标识
2.3 将获取到的响应返回

类:RedisRegistry
1.构造函数
2.void register(URI uri)
3.void subscribe(String service, NotifyListener notifyListener)
1.init()
1.1 通过心跳机制,定时刷新redis中服务uri的过期时间
1.2 开启子线程,new JedisPubSub()定义收到订阅消息时的回调处理方式,并通过NotifyListener完成回调通知
1.3 通过__keyspace@0__:trpc-*"和psubscribe进行模式订阅
2.register(URI uri),在redis中set 服务的uri
3. subscribe(),通过 keys()命令,模糊匹配的方式获取到想要调用的远程服务的uri列表

类:Netty4Transporter
1.Client connect()
2.Server start()
1.通过netty服务端程序,开启网络服务
2.通过netty客户端程序,建立长连接

接口:LoadBalance()
1.Invoker select(Map<URI, Invoker> invokerMap)
1.两个实现类RandomLoadBalance和RoundRobin
2.通过select方法,采用对应的策略返回ClusterInvoker中的一个代理对象

类:TrpcClientHandler
1.CompletableFuture waitResult()
2.onReceive()
1.登记,创建返回一个future, 每个发起远程调用的线程有一个单独的future
2.接收远程服务执行结果,删除future
类:TrpcServerHandler
1.onReceive()
2.getInvoker()
1.获取远程服务实现类SmsServiceImpl的代理,并执行远程方法
2.获取远程方法执行结果,并包装成response返回给客户端

类:RpcInvocation
1.将netty发送数据的请求体进行包装
2.long incrementAndGet()
通过自旋方式获取自增id

类:RoundRobin
1.Invoker select()
2.int incrementAndGet()

类:SmsApplication
1.@EnableTRPC
2.context.star()

类:SmsServiceImpl
1.@TRpcService
2.Object send()服务实现类

类:OrderServiceImpl
1.@TRpcReference(loadbalance = “RoundRobin”)
2.smsService.send()远程调用

接口:SmsService
1.Object send(String phone, String content)

接口:OrderService
1.void create(String orderContent);

接口:Invoker
1.invoke(RpcInvocation rpcInvocation)

接口:Handler
1.onReceive()
2.onWrite()

3、整体流程图

在这里插入图片描述

总结

这个手写RPC框架,花了我两个星期的时间,真是不容易呀!欢迎各位技术大牛前来指点江山,交流评论。你的支持就是对我最大的鼓励!谢谢!

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
Spring Boot使用“习惯优于配置”的理念让我们的项目快速运行起来,我们可以不用或者只需要很少的配置就能创建一个独立运行、准生产级别的基于Spring框架的项目。我们不禁要问,这么一个优秀的框架,是不是在企业开发中就已经足够了,如果是,那么为什么像BAT这些大公司还要研发自己的交易框架,当然这里面除了核心技术之外,还有两个比较重要的原因:第一:像Spring ,Spring Boot这些开源框架固然很优秀,但却不满足这些大公司对框架的功能要求,如spring scheduler就没有分布式调度能力,阿里研发了自己的tbschedule,以及后来的schedulerx;第二:开源框架可以解决具体的领域问题,比如持久化框架Mybatis,RPC框架Dubbo,但是面对业务流程的开发却不是它的强项,以此就诞生了SSM,以及后来的Spring MVC。放眼整个java开源世界,不管是功能问题还是业务流程开发问题都有对应框架和组件能满足我们的需求,只要我们的视野足够开阔,能有效的去整合开源组件,足以应付日常的开发。当然我们很难出像Spring、Spring Boot、Mybatis这些优秀的框架,但是我们可以在这个基础之上,进行整合,甚至二次开发,形成公司自己的功能组件或者交易开发框架。不客气的说,开源框架的底层少不了spring的身影,那么可以肯定在Spring Boot推出以后,开源框架势必会以Spring Boot作为底层平台进行二次改造,这是趋势,也是必然。本课程顺应潮流,以Spring Boot作为基础平台,充分发挥其特性,抽象业务流程,整合开源组件,降低开发难度,打造出一个功能强大的交易开发框架,简洁,优雅,好用。本课程有如下技术特色:第一:充分使用Spring Boot的自动装配、条件注解,以及各种使用技巧;第二:使用注解@Transaction抽象业务流程,简化交易的定义和执行方式,比SpringMVC更符合业务流程的开发(当然SpringMVC很强大,无贬低之意)第三:为使交易具备RPC能力,使用泛化方式集成Dubbo,其好处是服务端不再需要提供接口给客户端使用,简单、高效;第四:使用nacos作为服务注册中心,也支持zookeeper;第五:为使交易具备Http能力,在Spring MVC的基础上提供HandlerMapping、HandlerAdapter。。。。一切尽在代码中

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值