基于 Netty 纯手写 RPC 框架

最近为了复习 netty,纯手写了一套 RPC 框架,项目地址 qingcha-rpc

项目介绍

本项目是一个基于 netty 的 rpc 框架,可以使用纯 java 编程或者跟 SpringBoot 集成。

本项目纯属练手项目,未经测试和生产使用,谨慎使用!欢迎交流。

使用介绍

java 方式

Server 端

  1. 引入依赖

    <dependency>
      <groupId>com.qingcha.rpc</groupId>
      <artifactId>qingcha-rpc-server</artifactId>
      <version>1.0-SNAPSHOT</version>
    </dependency>
    <dependency>
      <groupId>com.alibaba</groupId>
      <artifactId>fastjson</artifactId>
      <version>1.2.68</version>
    </dependency>
    
  2. 编写需要暴露的服务,在类上或者方法上加上@RpcInvoke注解表示将服务暴露出去。

    @RpcInvoke
    public class HelloServiceImpl implements HelloService{
      @Override
      public String hello(String name) {
        return "hello qingcha";
      }
    }
    public class BookServiceImpl implements BookService {
      @Override
      @RpcInvoke
      public List<Book> findByAuthor(String author) {
        return new ArrayList<Book>() {{
          add(new Book("天龙八部"));
          add(new Book("射雕英雄转"));
          add(new Book("倚天屠龙记"));
        }};
      }
    }
    
  3. 编写启动类。

    public class RpcServerExample {
      public static void main(String[] args) {
        // 配置需要扫描的服务实现类所在包
        RpcServerConfiguration.configuration().setPackagePath("com.qingcha.rpc.examples.quickstart.service");
        RpcServer rpcServer = new RpcServer(9900);
        rpcServer.start();
      }
    }
    

Client 端

  1. 引入依赖。

    <dependency>
      <groupId>com.qingcha.rpc</groupId>
      <artifactId>qingcha-rpc-client</artifactId>
      <version>1.0-SNAPSHOT</version>
    </dependency>
    <dependency>
      <groupId>com.alibaba</groupId>
      <artifactId>fastjson</artifactId>
      <version>1.2.68</version>
    </dependency>
    
  2. 编写启动类。

    public class RpcClientConcurrencyExample {
      public static void main(String[] args) {
        RpcClientConfiguration.configuration().setHost("localhost");
        RpcClientConfiguration.configuration().setPort(9900);
        RpcClientConfiguration.configuration().setPackagePath("com.qingcha.rpc.examples.quickstart.service");
        ProxyProcessor proxyProcessor = ProxyProcessor.instance();
        HelloService helloService = proxyProcessor.getProxy(HelloService.class);
        for (int i = 0; i < 10; i++) {
          System.out.println(helloService.hello("JonKee" + i));
        }
        ExecutorService executorService = Executors.newFixedThreadPool(4);
        BookService bookService = proxyProcessor.getProxy(BookService.class);
        for (int i = 0; i < 10; i++) {
          executorService.execute(new Runnable() {
            @Override
            public void run() {
              for (int j = 0; j < 10; j++) {
                List<Book> books = bookService.findByAuthor("金庸");
                System.out.println(JSON.toJSONString(books));
              }
            }
          });
        }
      }
    }
    

SpringBoot 集成

Server

  1. 引入依赖。

    <dependency>
      <groupId>com.qingcha.rpc</groupId>
      <artifactId>qingcha-rpc-spring-boot-starter</artifactId>
      <version>1.0-SNAPSHOT</version>
    </dependency>
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter</artifactId>
      <version>2.3.2.RELEASE</version>
    </dependency>
    <dependency>
      <groupId>com.alibaba</groupId>
      <artifactId>fastjson</artifactId>
      <version>1.2.68</version>
    </dependency>
    
  2. 新增配置文件。

    qingcha:
      rpc:
         port: 9900
    
  3. 编写启动类。

    @SpringBootApplication
    @EnableRpcServer
    public class SpringServerApplication {
      public static void main(String[] args) {
        SpringApplication.run(SpringServerApplication.class, args);
      }
    }
    

Client

  1. 引入依赖,同服务端。

  2. 新增配置文件。

    qingcha:
      rpc:
        client:
          package-path: com.qingcha.rpc.examples.spring.service
          host: localhost
          port: 9900
    
  3. 编写启动类。

    @SpringBootApplication
    @EnableRpcClient
    public class SpringClientApplication {
      public static void main(String[] args) {
        ConfigurableApplicationContext context = SpringApplication.run(SpringClientApplication.class, args);
        BookService bookService = context.getBean(BookService.class);
        ExecutorService executorService = Executors.newFixedThreadPool(4);
        for (int i = 0; i < 10; i++) {
          executorService.execute(() -> {
            for (int j = 0; j < 10; j++) {
              List<Book> books = bookService.findByAuthor("金庸");
              System.out.println(JSON.toJSONString(books));
            }
          });
        }
      }
    }
    

扩展点

协议序列化工具扩展

实现com.qingcha.rpc.core.protocol.ProtocolSerialize接口。然后交由ProtocolSerializeManager管理,可以使用静态方法setProtocolSerialize设置或者使用 SPI 机制,默认使用FastJsonProtocolSerialize,如果使用默认,需要添加 fastjson 依赖。

public class ProtocolSerializeManager {
    private static ProtocolSerialize protocolSerialize;

    public static void setProtocolSerialize(ProtocolSerialize protocolSerialize) {
        ProtocolSerializeManager.protocolSerialize = protocolSerialize;
    }

    public static synchronized ProtocolSerialize getProtocolSerialize() {
        // 从 SPI 中获取
        if (protocolSerialize == null) {
            ServiceLoader<ProtocolSerialize> serviceLoader = ServiceLoader.load(ProtocolSerialize.class);
            for (ProtocolSerialize serialize : serviceLoader) {
                if (serialize != null) {
                    protocolSerialize = serialize;
                    break;
                }
            }
        }
        // 使用默认的序列化工具
        if (protocolSerialize == null) {
            protocolSerialize = new FastJsonProtocolSerialize();
        }
        return protocolSerialize;
    }
}

客户端代理池扩展

客户端代理池用于代理接口的管理,具体实现可参考以下两种组合。

  1. com.qingcha.rpc.client.proxy.DefaultProxyPoolFactorycom.qingcha.rpc.client.proxy.DefaultProxyPool
  2. com.qingcha.rpc.springboot.client.SpringRpcProxyPoolFactorycom.qingcha.rpc.springboot.client.SpringProxyPool
public class ProxyPoolManager {
  private static ProxyPool proxyPool;

  public static void setProxyPool(ProxyPool proxyPool) {
    ProxyPoolManager.proxyPool = proxyPool;
  }

  public static ProxyPool getProxyPool() {
    // 通过 SPI 机制获取 ProxyPoolFactory,从而获取代理池 proxyPool
    if (proxyPool == null) {
      ServiceLoader<ProxyPoolFactory> factories = ServiceLoader.load(ProxyPoolFactory.class);
      for (ProxyPoolFactory factory : factories) {
        if (factory != null) {
          proxyPool = factory.getProxyPool();
        }
      }
      if (proxyPool == null) {
        RpcClientConfiguration configuration = RpcClientConfiguration.configuration();
        proxyPool = new DefaultProxyPoolFactory(configuration.getPackagePath()).getProxyPool();
      }
    }
    return proxyPool;
  }
}

服务端方法池扩展

服务端方法池用于暴露服务的管理,具体可以参考一下两种组合。

  1. com.qingcha.rpc.server.invoke.DefaultMethodPoolFactorycom.qingcha.rpc.server.invoke.DefaultMethodPool
  2. com.qingcha.rpc.springboot.server.SpringRpcMethodPoolFactorycom.qingcha.rpc.springboot.server.SpringMethodPool
public class MethodPoolManager {
  private static MethodPool methodPool;

  public static void setMethodPool(MethodPool methodPool) {
    MethodPoolManager.methodPool = methodPool;
  }

  public static synchronized MethodPool getMethodPool() {
    if (methodPool == null) {
      // 从 spi 中获取 methodPool
      ServiceLoader<MethodPoolFactory> methodPoolFactories = ServiceLoader.load(MethodPoolFactory.class);
      for (MethodPoolFactory factory : methodPoolFactories) {
        if (factory != null) {
          methodPool = factory.getMethodPool();
          break;
        }
      }
      // 如果获取不到,则用默认的
      if (methodPool == null) {
        RpcServerConfiguration configuration = RpcServerConfiguration.configuration();
        methodPool = new DefaultMethodPoolFactory(configuration.getPackagePath()).getMethodPool();
      }
    }
    return methodPool;
  }
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值