如何设计RPC框架

  RPC框架的核心目标是解决分布式系统中服务之间的调用问题。

RPC框架三个核心角色

  1)服务提供者(Server)对外提供后台服务,将自己的服务信息,注册到注册中心

  2)注册中心(Registry)用于服务端注册远程服务以及客户端发现服务。

  3)服务消费者(Client) 从注册中心获取远程服务的注册信息,然后进行远程过程调用。

  

RPC远程调用过程

  1)服务调用方(client)调用以本地调用方式调用服务;
  2)client stub接收到调用后负责将方法、参数等组装成能够进行网络传输的消息体;在Java里就是序列化的过程;
  3)client stub找到服务地址,并将消息通过网络发送到服务端;
  4)server stub收到消息后进行解码,在Java里就是反序列化的过程;
  5)server stub根据解码结果调用本地的服务;
  6)本地服务执行处理逻辑;
  7)本地服务将结果返回给server stub;
  8)server stub将返回结果打包成消息,Java里的序列化;
  9)server stub将打包后的消息通过网络并发送至消费方;
  10)client stub接收到消息,并进行解码, Java里的反序列化;
  11)服务调用方(client)得到最终结果。

  RPC框架的目标就是要2~10这些步骤都封装起来。

  

RPC框架涉及技术

a.建立通信
  首先,要解决通讯的问题,主要是通过在客户端和服务器之间建立TCP连接,远程过程调用的所有交换的数据都在这个连接里传输。
  当前很多RPC框架都直接基于netty这一IO通信框架,推荐使用Netty 作为底层通信框架。

b.网络传输
  数据传输采用什么协议(二进制数据格式组织)?
  数据该如何序列化和反序列化?(kryo/protobuf/protostuff/hessian/fastjson/…)

c.服务寻址
  1)服务注册
  服务提供者启动后主动把服务注册到服务中心,注册中心存储了该服务的IP、端口、调用方式(协议、序列化方式)等信息。

  2)服务发现
  服务消费者第一次调用服务时,会通过注册中心找到相应的服务提供方地址列表,并缓存到本地,以供后续使用。当消费者再次调用服务时,不会再去请求注册中心,而是直接通过负载均衡算法从IP列表中取一个服务提供者的服务器调用服务。

d.服务调用
  服务消费者进行本地调用(通过代理Proxy)之后得到了返回值。实际上是在Proxy中封装了一系列的过程,包括序列化、请求服务提供者、反序列化等等。
  创建Proxy的方式?(jdk proxy/javassist/cglib/asm/bytebuddy)
  Proxy还能做什么?软负载均衡(加权随机、加权轮询、最小负载、一致性Hash…) 、集群容错(Fail-fast、Failover、Fail-safe、Fail-back)、同步/异步调用、流控、熔断、降级、限流、隔离和超时…(服务治理)

  

实现高可用RPC框架需要考虑到的问题

  要实现一个RPC不算难,难的是实现一个高性能高可靠的RPC框架。

  • 既然系统采用分布式架构,那一个服务势必会有多个实例,要解决如何获取实例的问题?
  • 如何选择实例呢?就要考虑负载均衡
  • 如果每次都去注册中心查询列表,效率很低,那么就要加缓存
  • 客户端总不能每次调用完都等着服务端返回数据,所以就要支持异步调用
  • 服务端的接口修改了,老的接口还有人在用,这就需要版本控制;
  • 服务端总不能每次接到请求都马上启动一个线程去处理,于是就需要线程池;

  
Failover - 失败自动切换,当出现失败,重试其它服务器。通常用于读操作,但重试会带来更长延迟。可通过 retries=“2” 来设置重试次数(不含第一次)。

Failfast - 快速失败,只发起一次调用,失败立即报错。通常用于非幂等性的写操作,比如新增记录。

Failsafe - 失败安全,出现异常时,直接忽略。通常用于写入审计日志等操作。

Failback - 失败自动恢复,后台记录失败请求,定时重发。通常用于消息通知操作。

Forking - 并行调用多个服务器,只要一个成功即返回。通常用于实时性要求较高的读操作,但需要浪费更多服务资源。可通过 forks=“2” 来设置最大并行数。

Broadcast - 播调用所有提供者,逐个调用,任意一台报错则报错。通常用于通知所有提供者更新缓存或日志等本地资源信息

你可能感兴趣的:(分布式服务)