首页
RPC
概念
RPC(Remote Procedure Call) 即远程过程调用,是为了让调用远程方法就像调用本地方法一样简单。
组成:
- 客户端:即服务消费者;
- 客户端Stub(桩):即消费者代理类,用于将调用信息发送到服务端Stub;
- 网络传输:用于网络通信,如Netty;
- 服务端 Stub(桩):非代理类,用于接收客户端Stub发送的调用信息,然后执行对应的方法并返回结果。
- 服务端(服务提供端):即服务提供者。
Dubbo
采用微内核(Microkernel)+插件(Plugin)模式,微内核只负责组装插件。
- Invoker:实体域,是对远程调用的抽象,代表一个可执行体,分为服务提供Invoker和服务消费Invoker。
- Invocation:会话域,表示每次操作的瞬时状态,操作前创建,操作后销毁,其实就是调用信息,存放了调用的类名、方法名、参数等信息。
- Protocol:服务域,负责实体域和会话域的生命周期管理。
支持的功能
- 负载均衡;
- 服务链路生成;
- 服务统计,资源调度治理;
- 服务降级;
调用过程
调用代码 -> Proxy -> Invoker -> TCP/IP -> Exporter -> Invoker -> 服务实现
架构
- serivce:接口层,服务提供者和消费者自行实现;
- config:配置层,Dubbo相关的配置,以 ServiceConfig、ReferenceConfig为中心,通过ServiceConfig.export实例化服务提供者,ReferenceConfig.get实例化服务消费者对象;
- proxy:服务代理层,真实调用过程依赖代理类,代理类之间网络通信,以 ServiceProxy为中心;
- registry:注册中心层,封装服务地址的注册与发现;
- cluster:路由层,封装多个提供者的路由及负载均衡为Invoker,并桥接注册中心;
- monitor:监控层,以 Statistics 为中心;
- protocol:远程调用层,封装RPC调用,以Invocation、Result为中心。
- exchange:信息交换层,封装请求响应模式,同步转异步,以Request、Response为中心;
- transport:网络传输层,抽象网络传输框架(默认Netty)为统一接口,以Message为中心;
- serialize:数据序列化层,对需要在网络传输的数据进行序列化。
通信协议
- dubbo:基于TCP,使用单一长连接和NIO,hessian作为序列化协议,适用于高并发小数据,消费者远大于提供者场景。
- rmi:使用标准的RMI协议实现,多个短链接,JDK标准序列化方式,一般较少用。
- hessian:底层采用Http通讯,使用Servlet暴露服务,默认使用Jetty作为服务器,使用hessian序列化协议和多个短连接,适用于提供者数量比消费者数量还多的情况,适用于文件的传输,一般较少用。
- http:基于http表单提交,使用Spring的HttpInvoke实现,多个短链接,传输协议http,同步,适合跨语言调用。
负载均衡策略
- RandomLoadBalance:默认策略,生成权重分布坐标,使用随机数确认区间选择对应服务器;
- LeastActiveLoadBalance:最小活跃数,活跃数表示当前正处理的请求数量,实现上还会考虑加权,如果相同则使用RandomLoadBalance;
- ConsistentHashLoadBalance:使用一致性哈希算法,为了避免数据倾斜会添加虚拟节点;
- RoundRobinLoadBalance:平滑加权轮询。
Cluster容错策略
- Failover:失败自动切换,默认方式,找到可用的地址并重试指定的次数,适合读操作;
- Failfast:失败自动恢复,失败直接抛出异常,适合于非幂等的写操作,因为服务提供者可能已经完成了操作,所以不应该重试;
- Failsafe:失败安全,出错只打印异常并返回空结果,适用于不重要的场景,如记录日志;
- fallback:如果失败先返回一个空结果,然后不断重试直到成功,适合发送消息等。
- Forking:并行调用,同时发起多个调用只要一个成功即返回结果,适用于对实时性要求高的操作,且要考虑幂等。
- Broadcast:广播模式,会逐个调用所有提供者,一个失败则视作失败,适合集群通知操作,如全局更新缓存等。
服务暴露
- Spring容器刷新完成后,首先完成初始化装配ServiceConfig对象;
- 检查及更新ServiceConfig对象,如果没有设置延迟暴露则开始暴露服务;
- 向注册中心注册服务,同时也会注册到本地ServiceRepository;
- 解析需要暴露的服务和协议然后构建URL,默认情况下会同时暴露到本地和远程;
- 暴露服务到本地,使用injvm协议,创建InjvmExporter,不监听本地端口,不走网络传输,但是会走Filter和Listener;
- 暴露服务到远程,通过proxyFactory.getInvoker()方法,默认使用javassist字节码技术,生成invoker对象;
- 根据协议将invoker对象封装为export对象,封装到exportedMap中,供远程调用查找;
- 将提供者信息注册到注册中心;
- 最后订阅Configurations节点。
延迟暴露
为了解决平滑发布的问题,即在生产者启动后延时一段时间才暴露服务。serviceBean是Spring容器初始化完成后在执行export方法,如果设置了延迟,则通过定时线程池去延迟执行export方法。
服务引用
支持饿汉式和懒汉式,默认懒汉式,即只有当服务被引用到其他类中才开启服务引用,首先构建要引用服务的URL对象,然后向注册中心注册消费者,并订阅提供者、配置、路由等节点,使用指定的集群容错策略包装invoker,然后通过proxyFactory.getInvoker()获取代理类。
服务检查
如果出现了循环引用或者由于服务的启动顺序,会出现引用的服务不存在而抛出异常的情况,需要在消费者端关闭服务检查。
服务请求流程
- 提供者启动时,往注册中心注册所有接口,并订阅动态配置节点;
- 消费者启动时,会在注册中心订阅需要引用的提供者、配置、路由节点;
- 信息变更时,注册中心基于长连接推送变更给消费者;
- 消费者从提供者地址列表中,根据负载均衡策略选择一个提供者调用,如果失败则使用集群容错策略;
- 服务提供者和消费者自身统计调用信息,并每分钟发送一次到监控中心。
角色
- Container:服务运行容器,负责加载、运行服务提供者。
- Provider:暴露服务的服务提供方,会向注册中心注册自己提供的服务。
- Consumer:调用远程服务的服务消费方,会向注册中心订阅自己所需的服务。
- Registry:注册中心,用于服务注册与发现,注册中心会返回服务提供者地址列表给消费者。
- 非必须,可以通过直连,而且故障也不会影响,因为会缓存提供者地址。
- Monitor:服务统计和监控中心,服务消费者和提供者会定时发送统计数据到监控中心,非必须。
优雅上下线
- Kill-9或容器stop命令会通知到进程;
- 使用JDK的shutdown hook实现关闭清理动作,同时Spring也注册了钩子方法,可以直接监听ApplicationEvent;
- 提供者会把自身标记为不接受请求,新请求直接报错,剩余请求及任务处理完成;
- 消费方,不再发起请求,未返回的响应等待完成;
- 同时提供者下线后注册中心会通知到所有消费者。
Open Feign
是RPC框架,是基于HTTP协议实现。