手写RPC基于Netty含源码

本文记录了作者从零开始构建一个Java RPC框架的过程,包括项目初始化、序列化接口设计、序列化类开发、Netty消息传输、请求编码器、响应结果处理、服务注册与发现、动态桩生产工厂及Netty服务监听器的实现。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

计划写一个RPC用于把学到的一些理论实践一下。下面记录每天干的事情和用到的知识点。

重要事情说三遍 RPC地址 请各位START。 非常感谢。

重要事情说三遍 RPC地址 请各位START。 非常感谢。

重要事情说三遍 RPC地址 请各位START。 非常感谢。

2020-06-18 项目初始化

在gitee上创建了一个项目,为什么不在github上呢?因为连接速度慢。为什么不提高一下速度呢?因为翻墙还得花钱。为什么不花钱呢?因为工资低,为什么工资低呢?因为技术太差。怎么样才能技术好呢? 写一下RPC吧。

目前用到的知识点

  1. Java SPI。用它来实现面向接口开发。动态获取一些实现类。
  2. 在rpc中有stubs的概念,服务端和客户端 都用到了,包含一些网络地址等信息。用他来去发起请求,处理请求。

2020-06-23 序列化接口设计

主要功能是将对象序列化后存储到字节数组中,因此添加了计算序列化后的长度的方法,序列化的方法,反序列化的方法,获取序列化对象类型的方法,还有返回数据类型的标识的方法。

2020-06-24 序列化类注册,反序列化方法开发

主要是在序列化包装类加载时将序列化类注册到容器中。

2020-06-28 序列化类开发

  1. 添加了将类序列化成数组的方法

2010-06-29 启动关闭服务

  1. 启动关闭服务

2020-06-30

  1. 注册中心注册服务

2020-07-01

  1. 测试RPC项目搭建开发,已经开发到服务端
  2. SPI配置文件
  3. 响应类
  4. 传送中的请求

开发中复习了一下信号量的使用

2020-07-02

  1. Netty实现传输消息客户端

还真得复习复习netty了, 好多东西都得现查。

2020-07-03

这几天的开发主要集中在netty上面,使用netty 来完成进程 之间的消息传递。使用到了自定义协议。

2020-07-06 请求编码器开发

此处要特别注意请求协议的设计,具体体现在Netty中Bytebuf的操作上。

2020-07-07 响应结果添加到future中

在netty的响应pipline中添加处理器,将响应结果添加到响应集合中。
在这里插入图片描述

关于CompletableFuture中学习文章请参考:https://www.liaoxuefeng.com/wiki/1252599548343744/1306581182447650
鸟窝,大牛博客大牛博客!!!!

相当推荐第二个博客,真是大牛大牛大牛。

2020-07-08 Netty 实现消息的传递

这一块的功能主要是在PIPELINE中处理, 重要是要弄懂channel.writeAndFLush()方法。

闪电侠的解析 比较推荐。

2020-07-09 消息处理器和服务服务实现开发

2020-07-12 请求处理器

请求处理器,是服务端接收到请求,执行方法使用的。

Header header = requestCommand.getHeader();
        // 从payload中反序列化RpcRequest
        RpcRequest rpcRequest = SerializeSupport.parse(requestCommand.getPayload());
        try {
            // 查找所有已注册的服务提供方,寻找rpcRequest中需要的服务
            Object serviceProvider = serviceProviders.get(rpcRequest.getInterfaceName());
            if (serviceProvider != null) {
                //找到服务提供者,利用java反射机制调用服务的对应方法
                String arg = SerializeSupport.parse(rpcRequest.getSerializedArguments());
                Method method = serviceProvider.getClass().getMethod(rpcRequest.getMethodName(), String.class);
                String result = (String) method.invoke(serviceProvider, arg);
                //把结果封装成响应命令并返回
                return new Command(new ResponseHeader(type(), header.getVersion(), header.getRequestId()), SerializeSupport.serialize(result));
            }

//如果没找到,返回NO_PROVIDER错误响应
            logger.warn("No service Provider of {}#{}(String)!", rpcRequest.getInterfaceName(), rpcRequest.getMethodName());
            return new Command(new ResponseHeader(type(), header.getVersion(), header.getRequestId(), Code.NO_PROVIDER.getCode(), "No provider!"), new byte[0]);
        } catch (Throwable t) {
            // 发生异常,返回UNKNOWN_ERROR错误响应。
            logger.warn("Exception: ", t);
            return new Command(new ResponseHeader(type(), header.getVersion(), header.getRequestId(), Code.UNKNOWN_ERROR.getCode(), t.getMessage()), new byte[0]);
        }

2020-07-13 RPC桩抽象类开发

    protected byte[] invokeRemote(RpcRequest request){
        Header header = new Header(ServiceTypes.TYPE_RPC_REQUEST,1, RequestIdSupport.next());
        byte[] payload = SerializeSupport.serialize(request);
        Command requestCommand = new Command(header,payload);
        try {
            Command responseCommand = transport.send(requestCommand).get();
            ResponseHeader responseHeader = (ResponseHeader) responseCommand.getHeader();
            if(responseHeader.getCode() == Code.SUCCESS.getCode()) {
                return responseCommand.getPayload();
            } else {
                throw new Exception(responseHeader.getError());
            }
        }catch (ExecutionException e) {
            throw new RuntimeException(e.getCause());
        } catch (Throwable e) {
            throw new RuntimeException(e);
        }
    }

2020-07-14 动态桩生产工厂

  try {
            String stubSimpleName = serviceClass.getSimpleName() + "Stub";
            String classFullName = serviceClass.getName();
            String stubFullName = "com.github.liyue2008.rpc.client.stubs." + stubSimpleName;
            String methodName = serviceClass.getMethods()[0].getName();
            String source = String.format(STUB_SOURCE_TEMPLATE, stubSimpleName, classFullName, methodName, classFullName, methodName);
            // 编译源代码
            JavaStringCompiler compiler = new JavaStringCompiler();
            Map<String, byte[]> results = compiler.compile(stubSimpleName + ".java", source);
//加载编译好的类
            Class<?> clazz = compiler.loadClass(stubFullName, results);

//            把Transport赋值给桩
            ServiceStub stubInstance = (ServiceStub) clazz.newInstance();
            stubInstance.setTransport(transport);
            return (T) stubInstance;
        } catch (Throwable t) {
            throw new RuntimeException(t);
        }

2020-07-15 netty服务启动器

    @Override
    public void start(RequestHandlerRegistry requestHandlerRegistry, int port) throws Exception {
        this.port = port;
        this.requestHandlerRegistry = requestHandlerRegistry;
        // 接收连接的处理器
        EventLoopGroup acceptEventGroup = newEventLoopGroup();
//      处理连接中消息的处理器
        EventLoopGroup ioEventGroup = newEventLoopGroup();

//        NETTY ChannelPipeline
        //TODO 启动器还没有开发完成 ING 1
    }

直接在Netty启动类中替换为在Linux系统下的epoll组件

private EventLoopGroup newEventLoopGroup() {
        if (Epoll.isAvailable()) {
            return new EpollEventLoopGroup();
        } else {
            return new NioEventLoopGroup();
        }
    }

2020-07-16 Netty 服务监听器开发

Netty服务开发

public class NettyServer implements TransportServer {

    public static final Logger logger = LoggerFactory.getLogger(NettyServer.class);
    private int port;
    private EventLoopGroup acceptEventGroup;
    private EventLoopGroup ioEventGroup;
    private Channel channel;
    private RequestHandlerRegistry requestHandlerRegistry;


    @Override
    public void start(RequestHandlerRegistry requestHandlerRegistry, int port) throws Exception {
        this.port = port;
        this.requestHandlerRegistry = requestHandlerRegistry;
        // 接收连接的处理器
        EventLoopGroup acceptEventGroup = newEventLoopGroup();
//      处理连接中消息的处理器
        EventLoopGroup ioEventGroup = newEventLoopGroup();
        ChannelHandler channelHandlerPipeline = newChannelHandlerPipeline();
        ServerBootstrap serverBootstrap = newBootStrap(channelHandlerPipeline, acceptEventGroup, ioEventGroup);
        Channel channel = doBind(serverBootstrap);

        this.acceptEventGroup = acceptEventGroup;
        this.ioEventGroup = ioEventGroup;
        this.channel = channel;

    }
}

服务提交方处理请求的Netty执行器开发

@ChannelHandler.Sharable
public class RequestInvocation extends SimpleChannelInboundHandler<Command> {

    private static final Logger logger = LoggerFactory.getLogger(ResponseInvocation.class);

    private final RequestHandlerRegistry requestHandlerRegistry;

    public RequestInvocation(RequestHandlerRegistry requestHandlerRegistry) {
        this.requestHandlerRegistry = requestHandlerRegistry;
    }

    @Override
    protected void channelRead0(ChannelHandlerContext ctx, Command request) throws Exception {
        RequestHandler requestHandler = requestHandlerRegistry.get(request.getHeader().getType());
        if (null != requestHandler) {
            Command response = requestHandler.handle(request);
            if (null != response) {
                ctx.writeAndFlush(response).addListener((ChannelFutureListener) channelFuture -> {
                    if (!channelFuture.isSuccess()){
                        logger.warn("Write response failed!", channelFuture.cause());
                        ctx.channel().close();
                    }
                });
            }else {
                logger.warn("Response is null!");
            }
        }
    }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        logger.warn("Exception: ", cause);
        super.exceptionCaught(ctx, cause);
        Channel channel = ctx.channel();
        if (channel.isActive()) ctx.close();
    }
}

RPC 测试

在服务提供方中编写启动服务Server.java

 public static void main(String[] args) throws Exception {
        String serviceName = HelloService.class.getCanonicalName();
        File tmpDirFile = new File(System.getProperty("java.io.tmpdir"));
        File file = new File(tmpDirFile, "simple_rpc_name_service.data");
        HelloService helloService = new HelloServiceImpl();

        logger.info("创建并启动RpcAccessPoint...");

        // TODO  编写SPI  主
        try (RpcAccessApi rpcAccessApi = ServiceSupport.load(RpcAccessApi.class);
             Closeable ignored = rpcAccessApi.startServer()
        ) {
            NameService nameService = rpcAccessApi.getNameService(file.toURI());
            assert nameService!=null;
            logger.info("向RpcAccessPoint注册{}服务...", serviceName);
            URI uri = rpcAccessApi.addServiceProvider(helloService, HelloService.class);
            logger.info("服务名: {}, 向NameService注册...", serviceName);
            nameService.registerService(serviceName, uri);
            logger.info("开始提供服务,按任何键退出.");

            System.in.read();
            logger.info("Bye!");
        }
    }

服务调用方 Client.java

 public static void main(String [] args) throws IOException {
        String serviceName = HelloService.class.getCanonicalName();
        File tmpDirFile = new File(System.getProperty("java.io.tmpdir"));
        File file = new File(tmpDirFile, "simple_rpc_name_service.data");
        String name = "Master MQ";
        try(RpcAccessApi rpcAccessPoint = ServiceSupport.load(RpcAccessApi.class)) {
            NameService nameService = rpcAccessPoint.getNameService(file.toURI());
            assert nameService != null;
            URI uri = nameService.lookupService(serviceName);
            assert uri != null;
            logger.info("找到服务{},提供者: {}.", serviceName, uri);
            HelloService helloService = rpcAccessPoint.getRemoteService(uri, HelloService.class);
            logger.info("请求服务, name: {}...", name);
            String response = helloService.hello(name);
            logger.info("收到响应: {}.", response);
        }
    }

先启动服务提供方,在启动服务调用方。
最后结果
在这里插入图片描述

在这里插入图片描述

希望和你交个朋友

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值