手写RPC基于Netty含源码
计划写一个RPC用于把学到的一些理论实践一下。下面记录每天干的事情和用到的知识点。
重要事情说三遍 RPC地址 请各位START。 非常感谢。
重要事情说三遍 RPC地址 请各位START。 非常感谢。
重要事情说三遍 RPC地址 请各位START。 非常感谢。
2020-06-18 项目初始化
在gitee上创建了一个项目,为什么不在github上呢?因为连接速度慢。为什么不提高一下速度呢?因为FQ还得花钱。为什么不花钱呢?因为工资低,为什么工资低呢?因为技术太差。怎么样才能技术好呢? 写一下RPC吧。
目前用到的知识点
- Java SPI。用它来实现面向接口开发。动态获取一些实现类。
- 在rpc中有stubs的概念,服务端和客户端 都用到了,包含一些网络地址等信息。用他来去发起请求,处理请求。
2020-06-23 序列化接口设计
主要功能是将对象序列化后存储到字节数组中,因此添加了计算序列化后的长度的方法,序列化的方法,反序列化的方法,获取序列化对象类型的方法,还有返回数据类型的标识的方法。
2020-06-24 序列化类注册,反序列化方法开发
主要是在序列化包装类加载时将序列化类注册到容器中。
2020-06-28 序列化类开发
- 添加了将类序列化成数组的方法
2010-06-29 启动关闭服务
- 启动关闭服务
2020-06-30
- 注册中心注册服务
2020-07-01
- 测试RPC项目搭建开发,已经开发到服务端
- SPI配置文件
- 响应类
- 传送中的请求
开发中复习了一下信号量的使用
2020-07-02
- 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);
        }
    }
先启动服务提供方,在启动服务调用方。
 最后结果
 

希望和你交个朋友

 
                    
                     
                    
                 
                    
                
 
                
            
         
         浙公网安备 33010602011771号
浙公网安备 33010602011771号