• 这次我设计了一款TPS百万级别的分布式、高性能、可扩展的RPC框架


    次准备下手的是RPC框架项目。为什么要对RPC框架项目下手呢,因为在如今分布式、微服务乃至云原生不断发展的过程中,RPC作为底层必不可少通信组件,被广泛应用在分布式、微服务和云原生项目中。

    为啥要开发RPC框架

    事情是这样的,在开发这个RPC框架之前,我花费了不少时间算是对Dubbo框架彻底研究透彻了。

    冰河在撸透了Dubbo2.x和Dubbo3.x的源码之后,本来想给大家写一个Dubbo源码解析的专栏。为此,我其实私下准备了一个多月:画流程图、分析源码、写测试Demo,自己在看Dubbo源码时,也为Dubbo源码添加了非常详细的注释。这里,就包含Dubbo2.x和Dubbo3.x的源码。

    当我就这么熬夜肝文一个多月后,突然发现一个问题:Dubbo经过多年不断的迭代开发,它的源码已经非常多了,如果以文章的形式将Dubbo的源码面面俱到的分析到位,那还不知道要写到何年何月去了。当我写文章分析Dubbo的最新版本3.x时,可能写到专栏的中后期Dubbo已经更新到4.x、5.x,设置有可能是6.x、7.x了。

    与其这么费劲吧咧的分析源码,还不如从零开始带着大家一起手撸一个能够在实际生产环境使用的、分布式、高性能、可扩展的RPC框架。这样,大家也能够直观的感受到一个能够在实际场景使用的RPC框架是如何一步步开发出来的。

    相信大家在学完《RPC手撸专栏》后,自己再去看Dubbo源码的话,就相对来说简单多了。你说是不是这样的呢?

    你能学到什么?

    既然是整个专栏的开篇嘛,肯定是要告诉你在这个专栏中能够学习到哪些实用的技术的。这里,我就画一张图来直观的告诉你在《RPC手撸专栏》能够学到哪些技术吧。

    相信小伙伴们看到《RPC手撸专栏》涉及到的知识点,应该能够了解到咱们这个从零开始的《RPC手撸专栏》还是比较硬核的吧?

    另外,咱这RPC项目支持同步调用、异步调用、回调和单向调用。

    • 同步调用:

    • 异步调用:

    • 回调:

    • 单向调用

    对,没错,咱们《RPC手撸专栏》最终实现的RPC框架的定位就是尽量可以在实际环境使用。通过这个专栏的学习,让大家深入了解到能够在实际场景使用的RPC框架是如何一步步开发出来的。

    代码结构

    我将这个bhrpc项目定位为可在实际场景使用的、分布式、高性能、可扩展的RPC框架,目前总体上已经开发并完善的功能达到60+个子项目,大家看图吧(可以点开看大图)。

    项目大量使用了对标Dubbo的自定义SPI技术实现高度可扩展性,各位小伙伴可以根据自己的需要,按照SPI的设计要求添加自己实现的自定义插件。

    显示效果

    说了那么多,咱们一起来看看这个RPC框架的使用效果吧,因为咱们这个RPC框架支持的调用方式有:原生RPC调用、整合Spring(XML/注解)、整合SpringBoot、整合SpringCloud、整合SpringCloud Alibaba,整合Docker和整合K8S七种使用方式。

    这里,咱们就以 整合Spring注解的方式 来给大家演示下这个RPC框架。

    RPC核心注解说明

    为了让大家更好的了解这个RPC框架,我先给大家看下RPC框架的两个核心注解,一个是RPC的服务提供者注解@RpcService,一个是RPC的服务调用者注解@RpcReference

    (1)服务提供者注解@RpcService的核心源码如下所示。

    1. /**
    2.  * @author binghe
    3.  * @version 1.0.0
    4.  * @description bhrpc服务提供者注解
    5.  */
    6. @Target({ElementType.TYPE})
    7. @Retention(RetentionPolicy.RUNTIME)
    8. @Component
    9. public @interface RpcService {
    10.     /**
    11.      * 接口的Class
    12.      */
    13.     Class<?> interfaceClass() default void.class;
    14.     /**
    15.      * 接口的ClassName
    16.      */
    17.     String interfaceClassName() default "";
    18.     /**
    19.      * 版本号
    20.      */
    21.     String version() default "1.0.0";
    22.     /**
    23.      * 服务分组,默认为空
    24.      */
    25.     String group() default "";
    26.     /**
    27.      * 延迟发布,预留
    28.      */
    29.     int delay() default 0;
    30.     /**
    31.      * 是否导出rpc服务,预留
    32.      */
    33.     boolean export() default true;
    34. }

    (2)服务调用者注解@RpcReference的核心源码如下所示。

    1. /**
    2.  * @author binghe
    3.  * @version 1.0.0
    4.  * @description bhrpc服务消费者
    5.  */
    6. @Retention(RetentionPolicy.RUNTIME)
    7. @Target(ElementType.FIELD)
    8. @Autowired
    9. public @interface RpcReference {
    10.     /**
    11.      * 版本号
    12.      */
    13.     String version() default "1.0.0";
    14.     /**
    15.      * 注册中心类型, 目前的类型包含:zookeeper、nacos、etcd、consul
    16.      */
    17.     String registryType() default "zookeeper";
    18.     /**
    19.      * 注册地址
    20.      */
    21.     String registryAddress() default "127.0.0.1:2181";
    22.     /**
    23.      * 负载均衡类型,默认基于ZK的一致性Hash
    24.      */
    25.     String loadBalanceType() default "zkconsistenthash";
    26.     /**
    27.      * 序列化类型,目前的类型包含:protostuff、kryo、json、jdk、hessian2、fst
    28.      */
    29.     String serializationType() default "protostuff";
    30.     /**
    31.      * 超时时间,默认5s
    32.      */
    33.     long timeout() default 5000;
    34.     /**
    35.      * 是否异步执行
    36.      */
    37.     boolean async() default false;
    38.     /**
    39.      * 是否单向调用
    40.      */
    41.     boolean oneway() default false;
    42.     /**
    43.      * 代理的类型,jdk:jdk代理, javassist: javassist代理, cglib: cglib代理
    44.      */
    45.     String proxy() default "jdk";
    46.     /**
    47.      * 服务分组,默认为空
    48.      */
    49.     String group() default "";
    50. }

    这里,我只列出了服务提供者注解@RpcService和服务调用者注解@RpcReference的部分源码,后续在RPC框架不断完善的过程中,大家就可以慢慢看到源码的全貌和其每个注解实现的功能。这里,我就不详细介绍了。

    当然啦,在这个RPC框架实现的原生调用方式中,可以不用这些注解就能够实现远程调用。

    效果演示

    接口定义

    定义两个接口,分别为HelloService和HelloPersonService,源码如下所示。

    • HelloService接口源码

    1. public interface HelloService {
    2.     String hello(String name);
    3.     String hello(Person person);
    4. }
    • HelloPersonService接口源码

    1. public interface HelloPersonService {
    2.     List getTestPerson(String name,int num);
    3. }

    实现服务提供者demo

    (1)创建HelloService接口和HelloPersonService接口的实现类HelloServiceImpl和HelloPersonServiceImpl,如下所示。

    • HelloServiceImpl类源码

    1. @RpcService(interfaceClass = HelloService.class, version = "1.0.0")
    2. public class HelloServiceImpl implements HelloService {
    3.     @Override
    4.     public String hello(String name) {
    5.         return "Hello! " + name;
    6.     }
    7.     @Override
    8.     public String hello(Person person) {
    9.         return "Hello! " + person.getFirstName() + " " + person.getLastName();
    10.     }
    11. }

    可以看到,在HelloServiceImpl类上添加了RPC服务提供者注解@RpcService,表示将其发布为一个RPC服务。

    • HelloPersonServiceImpl类源码

    1. @RpcService(interfaceClass = HelloPersonService.class, version = "1.0.0")
    2. public class HelloPersonServiceImpl implements HelloPersonService {
    3.     @Override
    4.     public List<Person> getTestPerson(String name, int num) {
    5.         List<Person> persons = new ArrayList<>(num);
    6.         for (int i = 0; i < num; ++i) {
    7.             persons.add(new Person(Integer.toString(i), name));
    8.         }
    9.         return persons;
    10.     }
    11. }

    可以看到,在HelloPersonServiceImpl类上添加了RPC服务提供者注解@RpcService,表示将其发布为一个RPC服务。

    (2)创建服务提供者demo的配置类ServerConfig,在ServerConfig类中注入RegistryService注册中心接口的实现类,以及RPC服务提供者的核心类RpcServer,如下所示。

    1. /**
    2.  * @author binghe
    3.  * @version 1.0.0
    4.  * @description 基于注解的配置类
    5.  */
    6. @Configuration
    7. @ComponentScan(value = {"io.binghe.rpc.demo"})
    8. @PropertySource(value = {"classpath:rpc.properties"})
    9. public class SpringAnnotationProviderConfig {
    10.     @Value("${registry.address}")
    11.     private String registryAddress;
    12.     @Value("${registry.type}")
    13.     private String registryType;
    14.     @Value("${registry.loadbalance.type}")
    15.     private String registryLoadbalanceType;
    16.     @Value("${server.address}")
    17.     private String serverAddress;
    18.     @Value("${reflect.type}")
    19.     private String reflectType;
    20.     @Bean
    21.     public RpcSpringServer rpcSpringServer(){
    22.         return new RpcSpringServer(serverAddress, registryAddress, registryType, registryLoadbalanceType, reflectType);
    23.     }
    24. }

    (3)创建服务提供者demo的启动类ServerTest,如下所示。

    1. /**
    2.  * @author binghe
    3.  * @version 1.0.0
    4.  * @description RPC整合Spring注解,服务提供者demo启动类
    5.  */
    6. public class ServerTest {
    7.     public static void main(String[] args){
    8.         new AnnotationConfigApplicationContext(SpringAnnotationProviderConfig.class);
    9.     }
    10. }

    实现服务调用者demo

    (1)创建测试服务调用者的TestService接口,如下所示。

    1. public interface TestService {
    2.     void printResult();
    3. }

    (2)创建TestService接口的实现类TestServiceImpl,在TestServiceImpl类上标注Spring的@Service注解,并在TestServiceImpl类中通过@RpcReference注解注入HelloService接口的实现类和HelloPersonService接口的实现类,并实现TestService接口的printResult()方法,源码如下所示。

    1. /**
    2.  * @author binghe
    3.  * @version 1.0.0
    4.  * @description 测试RPC服务调用者
    5.  */
    6. @Service
    7. public class TestServiceImpl implements TestService {
    8.     @RpcReference(version = "1.0.0", timeout = 3000, proxy = "javassist", isAsync = true)
    9.     private HelloService helloService;
    10.     
    11.     @RpcReference(proxy = "cglib")
    12.     private HelloPersonService helloPersonService;
    13.     @Override
    14.     public void printResult() {
    15.         String result = helloService.hello("binghe");
    16.         System.out.println(result);
    17.         result = helloService.hello(new Person("binghe001""binghe002"));
    18.         System.out.println(result);
    19.         System.out.println("=================================");
    20.         List<Person> personList = helloPersonService.getTestPerson("binghe"2);
    21.         personList.stream().forEach(System.out::println);
    22.     }
    23. }

    通过TestServiceImpl类的源码我们可以看到,远程调用HelloService接口的方法时使用的是javassist动态代理,远程调用HelloPersonService接口时,使用的是cglib动态代理。

    (3)创建服务调用者demo的配置类ClientConfig,如下所示。

    1. @Configuration
    2. @ComponentScan(value = {"io.binghe.rpc.*"})
    3. @PropertySource(value = {"classpath:rpc.properties"})
    4. public class ClientConfig {
    5. }

    (4)创建服务调用者demo的启动类ClientTest,如下所示。

    1. public class ClientTest {
    2.     public static void main(String[] args){
    3.         AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(ClientConfig.class);
    4.         TestService testService = context.getBean(TestService.class);
    5.         testService.printResult();
    6.         context.close();
    7.     }
    8. }

    启动服务测试

    (1)启动Zookeeper,这里,为了演示简单,就直接在我本机启动单机Zookeeper好了,启动后的效果如下图所示。

    (2)启动服务提供者ServerTest类,启动后输出的日志信息如下所示。

    1. 13:43:36,876  INFO ConnectionStateManager:228 - State change: CONNECTED
    2. 13:43:36,905  INFO RpcClient:79 - use cglib dynamic proxy...
    3. 13:43:36,942  INFO CuratorFrameworkImpl:235 - Starting
    4. 13:43:36,943  INFO ZooKeeper:868 - Initiating client connection, connectString=127.0.0.1:2181 

    可以看到,服务提供者已经将发布的服务注册到了Zookeeper中。

    (3)登录Zookeeper客户端查看Zookeeper中注册的服务,如下所示。

    • 查看HelloService接口发布的服务信息

    1. [zk: localhost:2181(CONNECTED) 5get /binghe_rpc/io.binghe.rpc.test.client.HelloService#1.0.0/65eb0d7f-4bf7-4a0a-bafc-1b7e0e030353
    2. {"name":"io.binghe.rpc.test.client.HelloService#1.0.0","id":"65eb0d7f-4bf7-4a0a-bafc-1b7e0e030353","address":"127.0.0.1","port":18866,"sslPort":null,"payload":{"@class":"io.binghe.rpc.center.meta.ServiceMeta","serviceName":"io.binghe.rpc.test.client.HelloService","serviceVersion":"1.0.0","serviceAddr":"127.0.0.1","servicePort":18866},"registrationTimeUTC":1656135817627,"serviceType":"DYNAMIC","uriSpec":null,"enabled":true}
    • 查看HelloPersonService接口发布的服务信息

    1. [zk: localhost:2181(CONNECTED) 7get /binghe_rpc/io.binghe.rpc.test.client.HelloPersonService#1.0.0/882a5cdb-f581-4a83-8d56-800a8f14e831
    2. {"name":"io.binghe.rpc.test.client.HelloPersonService#1.0.0","id":"882a5cdb-f581-4a83-8d56-800a8f14e831","address":"127.0.0.1","port":18866,"sslPort":null,"payload":{"@class":"io.binghe.rpc.center.meta.ServiceMeta","serviceName":"io.binghe.rpc.test.client.HelloPersonService","serviceVersion":"1.0.0","serviceAddr":"127.0.0.1","servicePort":18866},"registrationTimeUTC":1656135817274,"serviceType":"DYNAMIC","uriSpec":null,"enabled":true}

    通过Zookeeper客户端可以看出,HelloService接口和HelloPersonService接口发布的服务都已经被注册到Zookeeper了。

    (4)启动服务提供者ClientTest类,实现RPC调用,输出的日志信息如下所示。

    1. 13:56:47,391  INFO ConnectionStateManager:228 - State change: CONNECTED
    2. 13:56:47,488  INFO RpcClient:76 - use javassist dynamic proxy...
    3. 13:56:47,518  INFO ConnectionStateManager:228 - State change: CONNECTED
    4. 13:56:47,545  INFO RpcClient:79 - use cglib dynamic proxy...
    5. 13:56:48,253  INFO RpcConsumer:85 - connect rpc server 127.0.0.1 on port 18866 success.
    6. Hello! binghe
    7. Hello! binghe001 binghe002
    8. =================================
    9. 0 binghe
    10. 1 binghe

    可以看到,在ClientTest类的命令行输出了远程调用的结果信息。并输出了调用HelloService接口的远程方法使用的是javassist动态代理。调用HelloPersonService接口的远程方法使用的是cglib动态代理。

    咱们一起手撸的RPC框架其实还有很多非常强大的功能,这里,就不一一演示了,后面咱们都会一起手撸来实现它。


    如果本文对你有帮助,别忘记给我个3连 ,点赞,转发,评论,

    咱们下期见!答案获取方式:已赞 已评 已关~

    学习更多知识与技巧,关注与私信博主(03)

     

     

  • 相关阅读:
    数据结构—前缀树Trie的实现原理以及Java代码的实现
    力扣 2370. 最长理想子序列
    CentOS 升级 OpenSSL 至最新版教程
    英集芯推出4串锂电池100W移动电源升降压方案SoC芯片IP5389
    GitLab CI/CD关键词(九):作业依赖needs,GitLab Pages pages,制品依赖 dependencies
    Java多线程基础
    JavaScript数据类型BigInt实践之id数值太大,导致前后端交互异常
    电梯SIP-IP五方对讲管理系统
    在Android studio上开发APP之后,找不到应用图标,但是手机管家显示已经安装
    天才基本法中预测犯罪发生地点的数学建模真的可以为所欲为【全国大学生数学建模竞赛】
  • 原文地址:https://blog.csdn.net/weixin_70730532/article/details/126503410