文章插图
RPC,即 Remote Procedure Call(远程过程调用),说得通俗一点就是:调用远程计算机上的服务,就像调用本地服务一样 。
RPC 可基于 HTTP 或 TCP 协议,Web Service 就是基于 HTTP 协议的 RPC,它具有良好的跨平台性,但其性能却不如基于 TCP 协议的 RPC 。会两方面会直接影响 RPC 的性能,一是传输方式,二是序列化 。
众所周知,TCP 是传输层协议,HTTP 是应用层协议,而传输层较应用层更加底层,在数据传输方面,越底层越快,因此,在一般情况下,TCP 一定比 HTTP 快 。就序列化而言,JAVA 提供了默认的序列化方式,但在高并发的情况下,这种方式将会带来一些性能上的瓶颈,于是市面上出现了一系列优秀的序列化框架,比如:Protobuf、Kryo、Hessian、Jackson 等,它们可以取代 Java 默认的序列化,从而提供更高效的性能 。
为了支持高并发,传统的阻塞式 IO 显然不太合适,因此我们需要异步的 IO,即 NIO 。Java 提供了 NIO 的解决方案,Java 7 也提供了更优秀的 NIO.2 支持,用 Java 实现 NIO 并不是遥不可及的事情,只是需要我们熟悉 NIO 的技术细节 。
我们需要将服务部署在分布式环境下的不同节点上,通过服务注册的方式,让客户端来自动发现当前可用的服务,并调用这些服务 。这需要一种服务注册表(Service Registry)的组件,让它来注册分布式环境下所有的服务地址(包括:主机名与端口号) 。
应用、服务、服务注册表之间的关系见下图:
文章插图
每台 Server 上可发布多个 Service,这些 Service 共用一个 host 与 port,在分布式环境下会提供 Server 共同对外提供 Service 。此外,为防止 Service Registry 出现单点故障,因此需要将其搭建为集群环境 。
本文将为您揭晓开发轻量级分布式 RPC 框架的具体过程,该框架基于 TCP 协议,提供了 NIO 特性,提供高效的序列化方式,同时也具备服务注册与发现的能力 。
根据以上技术需求,我们可使用如下技术选型:
- Spring:它是最强大的依赖注入框架,也是业界的权威标准 。
- Netty:它使 NIO 编程更加容易,屏蔽了 Java 底层的 NIO 细节 。
- Protostuff:它基于 Protobuf 序列化框架,面向 POJO,无需编写 .proto 文件 。
- ZooKeeper:提供服务注册与发现功能,开发分布式系统的必备选择,同时它也具备天生的集群能力 。
第一步:编写服务接口public interface HelloService {
String hello(String name);
}
将该接口放在独立的客户端 jar 包中,以供应用使用 。
第二步:编写服务接口的实现类@RpcService(HelloService.class) // 指定远程接口
public class HelloServiceImpl implements HelloService {
@Override
public String hello(String name) {
return "Hello! " + name;
}
}
使用RpcService注解定义在服务接口的实现类上,需要对该实现类指定远程接口,因为实现类可能会实现多个接口,一定要告诉框架哪个才是远程接口 。
RpcService代码如下:
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Component // 表明可被 Spring 扫描
public @interface RpcService {
Class<?> value();
}
该注解具备 Spring 的Component注解的特性,可被 Spring 扫描 。
该实现类放在服务端 jar 包中,该 jar 包还提供了一些服务端的配置文件与启动服务的引导程序 。
第三步:配置服务端服务端 Spring 配置文件名为spring.xml,内容如下:
<beans ...>
<context:component-scan base-package="com.xxx.rpc.sample.server"/>
<context:property-placeholder location="classpath:config.properties"/>
<!-- 配置服务注册组件 -->
<bean id="serviceRegistry" class="com.xxx.rpc.registry.ServiceRegistry">
<constructor-arg name="registryAddress" value=https://www.isolves.com/it/cxkf/kj/2019-10-24/"${registry.address}"/>
</bean>
<!-- 配置 RPC 服务器 -->
<bean id="rpcServer" class="com.xxx.rpc.server.RpcServer">
<constructor-arg name="serverAddress" value=https://www.isolves.com/it/cxkf/kj/2019-10-24/"${server.address}"/>
<constructor-arg name="serviceRegistry" ref="serviceRegistry"/>
</bean>
</beans>
具体的配置参数在config.properties文件中,内容如下:
推荐阅读
- 分布式原理:一致性哈希算法简介
- 分布式基础之CAP
- 基于分布式服务器搭建NFS服务共享视频、文档目录
- 华为实验 ENSP模拟VXLAN EVPN分布式网关
- 蚂蚁金服推出分布式的图神经知识表示框架,性能和可扩展性俱佳
- gRPC 1.24.2 发布,谷歌开源的高性能 RPC 框架
- 什么是分布式系统,如何学习分布式系统
- 一文看懂MySQL中基于XA实现的分布式事务
- Java架构-还不了解Flink底层RPC使用的框架和原理?那就认真看完
- 图解各路分布式ID生成算法