项目推荐 I 手写RPC框架(一)

2022-04-11 18:38:14 浏览数 (1)

在这篇推文中,小媛将为大家推荐一个Java项目——手写RPC框架。

前言

RPC框架代码量较多,将仅对核心过程进行梳理,完整代码见:https://github.com/wdw87/wRpc

在这篇推文中,将对项目本身以及技术栈进行进行简单介绍,并且给出框架测试Demo

在接下来的系统推文中,将对项目进行详细的介绍

主要将按照下面的内容进行分配:

手写RPC框架(一)

RPC简介、技术栈介绍、测试Demo

手写RPC框架(二)

远程通信实现

手写RPC框架(三)

制定协议与编解码器、动态代理

手写RPC框架(四)

注册中心

一、什么是RPC

RPC(Remote Procedure Call)即远程过程调用,是一种计算的通讯框架。该框架允许运行与一台计算机上的程序调用另一台计算机的程序,而程序员无需为这个交互过程做额外的编程。

RPC框架有很多,例如阿里的Dubbo,谷歌的gRPC等

一个简单的RPC框架主要涉及如下内容

  • 动态代理
  • 反射
  • 序列化和反序列化
  • 编码与解码
  • 网络通信
  • 服务注册与服务发现
  • ...

完整代码见:https://github.com/wdw87/wRpc

二、技术栈

  • Spring框架,项目的基础框架,方便打包,调试,并且基于Spring实现框架配置
  • Netty,使用基于Netty的异步IO,提高网络通信性能
  • zookeeper,基于zookeeper实现服务注册与服务发现
  • fastjson,实现序列化和反序列化
  • cglib,实现动态代理

三、Demo

框架介绍完毕,接下来测试一下框架:

在框架注册两个服务:

  • calculate:实现加法减法运算
  • multiply:实现乘法运算

完整代码见:https://github.com/wdw87/wRpc

1. 服务端

配置
代码语言:javascript复制
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:wrpc="https://www.wdw87.com/schema/wrpc"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd
       https://www.wdw87.com/schema/wrpc
       https://www.wdw87.com/schema/wrpc/wrpc.xsd
">
    <wrpc:application name="rpc_provider"/>
    <!--服务器提供服务的端口-->
    <wrpc:server port="8000" />
    <!--注册中心-->
    <wrpc:registry registry_address="123.57.175.207:2181" connection_timeout="20000" session_timeout="20000"/>
    <!--服务一-->
    <wrpc:service id="calculate" name="com.wdw.wrpc.service.CalculateInterFace" ref="CalculateInterFaceImpl"/>
    <bean name="CalculateInterFaceImpl" class="com.wdw.wrpc.service.impl.CalculateImpl"/>
    <!--服务二-->
    <wrpc:service id = "multiply" name="com.wdw.wrpc.service.MultiplyInterface" ref="MultiplyImpl"/>
    <bean name="MultiplyImpl" class="com.wdw.wrpc.service.impl.MultiplyImpl"/>
</beans>
服务类
代码语言:javascript复制
/**
* 服务一
*/
public interface CalculateInterFace {
    int add(int a, int b);
    int dec(int a, int b);
}
public class CalculateImpl implements CalculateInterFace {
    @Override
    public int add(int a, int b) {
        return a   b;
    }

    @Override
    public int dec(int a, int b) {
        return a - b;
    }
}
/**
* 服务二
*/
public interface MultiplyInterface {
    Double multiply(Double a, Double b);
}
public class MultiplyImpl implements MultiplyInterface {
    @Override
    public Double multiply(Double a, Double b) {
        return a * b;
    }
}
服务端编程
代码语言:javascript复制
public class Server {
    public static void main(String[] args) throws InterruptedException {
        //Spring框架读取配置文件,启动
        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("provider.xml");
        context.start();
    }
}

2. 客户端

配置
代码语言:javascript复制
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:wrpc="https://www.wdw87.com/schema/wrpc"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd
       https://www.wdw87.com/schema/wrpc
       https://www.wdw87.com/schema/wrpc/wrpc.xsd
">
    <wrpc:application name="rpc_consumer"/>
    <!--负载均衡策略和动态代理方式-->
    <wrpc:client load_balance="random" proxy="cglib"/>
    <!--注册中心-->
    <wrpc:registry registry_address="123.57.175.207:2181" connection_timeout="20000" session_timeout="20000"/>
    <!--引用服务一-->
    <wrpc:reference id="calculate" name="com.wdw.wrpc.service.CalculateInterFace"/>
    <!--引用服务一-->
    <wrpc:reference id="multiply" name="com.wdw.wrpc.service.MultiplyInterface"/>
</beans>
服务接口

接口与服务端相互统一

代码语言:javascript复制
/**
* 服务一
*/
public interface CalculateInterFace {
    int add(int a, int b);
    int dec(int a, int b);
}
/**
* 服务二
*/
public interface MultiplyInterface {
    Double multiply(Double a, Double b);
}
客户端编程
代码语言:javascript复制
public class Client {
    public static void main(String[] args) {
        ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("consumer.xml");
        applicationContext.start();
        //调用服务一
        CalculateInterFace bean = (CalculateInterFace)SpringUtil.getApplicationContext().getBean("calculate");
        System.out.println(bean.add(1,2));
        System.out.println(bean.dec(10,2));
        //调用服务二
        MultiplyInterface bean1 = (MultiplyInterface)SpringUtil.getApplicationContext().getBean("multiply");
        System.out.println(bean1.multiply(3.5,5.5));
    }
}

3. 启动测试

服务端

服务端启动后,首先启动了Netty服务器,然后连接了Zookeeper,并将两个服务注册到Zookeeper,日志如下:

代码语言:javascript复制
...
[2020-03-29 17:55:48 下午]:INFO com.wdw.wrpc.server.netty.NettyServer.start(NettyServer.java:46)服务端启动中...
[2020-03-29 17:55:48 下午]:INFO com.wdw.wrpc.server.netty.NettyServer$2.operationComplete(NettyServer.java:70)server boot successful, bind port [8000]
[2020-03-29 17:55:49 下午]:INFO com.wdw.wrpc.server.registry.ServiceRegistry.connectServer(ServiceRegistry.java:51)zookeeper 已连接
[2020-03-29 17:55:50 下午]:INFO com.wdw.wrpc.server.registry.ServiceRegistry.register(ServiceRegistry.java:73)注册了服务:/wrpc/com.wdw.wrpc.service.CalculateInterFace/providers/192.168.1.46:8000
[2020-03-29 17:55:50 下午]:INFO com.wdw.wrpc.server.registry.ServiceRegistry.register(ServiceRegistry.java:73)注册了服务:/wrpc/com.wdw.wrpc.service.MultiplyInterface/providers/192.168.1.46:8000
    ...
[2020-03-29 17:55:56 下午]:INFO com.wdw.wrpc.server.netty.ServiceRequestHandler.channelActive(ServiceRequestHandler.java:30)客户端建立了连接
[2020-03-29 17:55:56 下午]:INFO com.wdw.wrpc.server.netty.ServiceRequestHandler.channelRead0(ServiceRequestHandler.java:64)Service invoked : ServiceRequestPacket{id='0082455e56de40e69dce96a43ded890d', className='com.wdw.wrpc.service.CalculateInterFace', methodName='add', parameterTypes=[int, int], args=[1, 2]}
[2020-03-29 17:55:56 下午]:INFO com.wdw.wrpc.server.netty.ServiceRequestHandler.channelRead0(ServiceRequestHandler.java:64)Service invoked : ServiceRequestPacket{id='da862a30b3bf4e7f93f1ee2776cb5444', className='com.wdw.wrpc.service.CalculateInterFace', methodName='dec', parameterTypes=[int, int], args=[10, 2]}
[2020-03-29 17:55:56 下午]:INFO com.wdw.wrpc.server.netty.ServiceRequestHandler.channelActive(ServiceRequestHandler.java:30)客户端建立了连接
[2020-03-29 17:55:56 下午]:INFO com.wdw.wrpc.server.netty.ServiceRequestHandler.channelRead0(ServiceRequestHandler.java:64)Service invoked : ServiceRequestPacket{id='5b0eac41b43a430f9df91846f2e696a5', className='com.wdw.wrpc.service.MultiplyInterface', methodName='multiply', parameterTypes=[class java.lang.Double, class java.lang.Double], args=[3.5, 5.5]}
...

然后,客户端发起调用请求时,服务端调用了相应服务的实现类,并且相应调用结果

客户端

客户端启动后,日志如下:

针对两个服务,经历了如下过程:

  • 连接Zookeeper
  • 发布客户端引用
  • 获取服务列表
  • 监听服务变化
代码语言:javascript复制
[2020-03-29 17:55:55 下午]:INFO com.wdw.wrpc.client.registry.ServiceDiscovery.connectServer(ServiceDiscovery.java:70)zookeeper 已连接

[2020-03-29 17:55:55 下午]:INFO com.wdw.wrpc.client.registry.ServiceDiscovery.registerReference(ServiceDiscovery.java:103)客户端引用发布成功:[/wrpc/com.wdw.wrpc.service.CalculateInterFace/consumers/192.168.1.46]
[2020-03-29 17:55:55 下午]:INFO com.wdw.wrpc.client.registry.ServiceDiscovery.getReference(ServiceDiscovery.java:113)正在获取服务引用[com.wdw.wrpc.service.CalculateInterFace]
[2020-03-29 17:55:56 下午]:INFO com.wdw.wrpc.client.registry.ServiceDiscovery.getReference(ServiceDiscovery.java:122)发现服务器列表[192.168.1.46:8000]
[2020-03-29 17:55:56 下午]:INFO com.wdw.wrpc.client.registry.ServiceDiscovery.getReference(ServiceDiscovery.java:124)引用服务获取完成[/wrpc/com.wdw.wrpc.service.CalculateInterFace/providers]:com.wdw.wrpc.service.CalculateInterFace


[2020-03-29 17:55:56 下午]:INFO com.wdw.wrpc.client.registry.ServiceDiscovery.registerReference(ServiceDiscovery.java:103)客户端引用发布成功:[/wrpc/com.wdw.wrpc.service.MultiplyInterface/consumers/192.168.1.46]
[2020-03-29 17:55:56 下午]:INFO com.wdw.wrpc.client.registry.ServiceDiscovery.getReference(ServiceDiscovery.java:113)正在获取服务引用[com.wdw.wrpc.service.MultiplyInterface]
[2020-03-29 17:55:56 下午]:INFO com.wdw.wrpc.client.registry.ServiceDiscovery.getReference(ServiceDiscovery.java:122)发现服务器列表[192.168.1.46:8000]
[2020-03-29 17:55:56 下午]:INFO com.wdw.wrpc.client.registry.ServiceDiscovery.getReference(ServiceDiscovery.java:124)引用服务获取完成[/wrpc/com.wdw.wrpc.service.MultiplyInterface/providers]:com.wdw.wrpc.service.MultiplyInterface


[2020-03-29 17:55:56 下午]:INFO com.wdw.wrpc.client.netty.ServiceResponseHandler.channelActive(ServiceResponseHandler.java:25)Client connected.
3
8

[2020-03-29 17:55:56 下午]:INFO com.wdw.wrpc.client.netty.ServiceResponseHandler.channelActive(ServiceResponseHandler.java:25)Client connected.
19.25

当客户端执行调用时,客户端连接服务器,得到调用结果。

完整代码见:https://github.com/wdw87/wRpc

作者:好吃懒做贪玩东

编辑:西瓜媛

0 人点赞