粗汉手撕RPC核心原理-Netty版

粗汉手撕RPC核心原理-Netty版


源码地址: https://gitee.com/kylin1991_admin/mini-tomcat/tree/master/nio-tomcat

环境准备

1、maven

    <!-- https://mvnrepository.com/artifact/org.projectlombok/lombok -->
    <dependency>
      <groupId>org.projectlombok</groupId>
      <artifactId>lombok</artifactId>
      <version>1.18.10</version>
      <scope>provided</scope>
    </dependency>
    <!-- https://mvnrepository.com/artifact/io.netty/netty-all -->
      <!-- 5 版本已经被丢弃了-->
      <dependency>
          <groupId>io.netty</groupId>
          <artifactId>netty-all</artifactId>
          <version>4.1.48.Final</version>
      </dependency>

2、插件:Lombok

1、protocol协议包
  • 直接用一个协议类就好了,用于数据传输过程的包装类
@Data
public class InvokeProtocol implements Serializable {
    private String className;
    private String methodName;
    private Class<?>[] params;
    private Object[] values;
}

2、registry注册中心
a、RpcRegistry注册中心类
  • 主要就是服务开启。用netty来开启服务
public class RpcRegistry {
    private static final int PORT = 8080;

    public static void main(String[] args) {
        new RpcRegistry().start();
    }

    private void start() {
        EventLoopGroup bossGroup = new NioEventLoopGroup();
        EventLoopGroup workerGroup = new NioEventLoopGroup();

        ServerBootstrap registry = new ServerBootstrap()
                .group(bossGroup, workerGroup)
                .channel(NioServerSocketChannel.class)
                .option(ChannelOption.SO_BACKLOG, 1024)
                .childOption(ChannelOption.SO_KEEPALIVE, true)
                .childHandler(new ChannelInitializer<SocketChannel>() {
                    @Override
                    protected void initChannel(SocketChannel serverSocketChannel) throws Exception {
                        ChannelPipeline pipeline = serverSocketChannel.pipeline();
                        pipeline.addLast(new LengthFieldBasedFrameDecoder(Integer.MAX_VALUE, 0, 4, 0, 4));
                        pipeline.addLast(new LengthFieldPrepender(4));

                        pipeline.addLast("decoder", new ObjectDecoder(Integer.MAX_VALUE, ClassResolvers.cacheDisabled(null)));
                        pipeline.addLast("encoder", new ObjectEncoder());

                        pipeline.addLast(new RegistryHandler());
                    }
                });

        try {
            ChannelFuture f = registry.bind(this.PORT).sync();
            System.out.println("start rpc registry success , prot : " + this.PORT);
            f.channel().closeFuture().sync();
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            bossGroup.shutdownGracefully();
            workerGroup.shutdownGracefully();
        }

    }
}
b、RegistryHandler初始化逻辑类
  • 逻辑处理的,主要是初始化容器,然后用于回调。其实这里也可以用Proxy来远程调用。
  • 所以真正的情况就是consumer远程调用。provider远程调用。简化简化哈哈
public class RegistryHandler extends ChannelInboundHandlerAdapter {
    private static final List<String> classNames = new ArrayList<>();
    private static final Map<String, Object> serverMap = new HashMap<>();
    // 指定扫描的实现路径,正常的情况是去读取配置文件,为了方便
    private static final String PACKAGE_NAME = "org.example.provider";

    public RegistryHandler() {
        scannerClassName(PACKAGE_NAME);
        doRegistry();
    }

    private void doRegistry() {
        if (classNames == null) return;
        for (String className : classNames) {
            try {
                Class<?> clazz = Class.forName(className);
                Class<?> server = clazz.getInterfaces()[0];
                serverMap.put(server.getName(), clazz.newInstance());

            } catch (ClassNotFoundException | InstantiationException | IllegalAccessException e) {
                e.printStackTrace();
            }
        }
    }

    private void scannerClassName(String packageName) {
        URL url = this.getClass().getResource("/" + packageName.replaceAll("\\.", "/"));
        File files = new File(url.getFile());
        for (File file : files.listFiles()) {
            if (file.isDirectory()) {
                scannerClassName(file.getName());
            } else {
                classNames.add(packageName + "." + file.getName().replace(".class", ""));
            }
        }
    }

    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
        Object result = null;
        if (msg instanceof InvokeProtocol) {
            InvokeProtocol request = (InvokeProtocol) msg;
            if (serverMap.containsKey(request.getClassName())) {
                Object service = serverMap.get(request.getClassName());
                Method method = service.getClass().getMethod(request.getMethodName(), request.getParams());
                result = method.invoke(service, request.getValues());
            }
        }
        ctx.writeAndFlush(result);
        ctx.close();

    }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        System.out.println("registry exception ");
        throw new RuntimeException(cause.getMessage());
    }
}
3、api
  • 用于提供接口使用的jar包
public interface IHelloService {
    String sayHello(String content);
}
4、provider服务提供方
public class HelloServiceImpl implements IHelloService {
    @Override
    public String sayHello(String content) {
        System.out.println("request in :" + content);
        return "SUCCESS";
    }
}
5、Consumer客户调用方

还是要用AOP代理的思想

a、Consumer调用类
public class Consumer {
    public static void main(String[] args) {
        IHelloService helloService = ProxyClient.create(IHelloService.class);
        System.out.println(helloService.sayHello("七楼"));
    }
}
b、RpcProxyClient代理类
  • 利用的jdk的动态代理,然后代理时采用netty来做客户端请求
public class RpcProxyClient {
    public static <T> T create(Class<?> interfacesCls) {
        return (T) Proxy.newProxyInstance(interfacesCls.getClassLoader(), new Class<?>[]{interfacesCls}, new ProxyHandler());
    }

    private static class ProxyHandler implements InvocationHandler {
        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            if (Object.class.equals(method.getDeclaringClass())) {
                return method.invoke(this, args);
            }

            InvokeProtocol request = new InvokeProtocol();
            request.setClassName(method.getDeclaringClass().getName());
            request.setMethodName(method.getName());
            request.setParams(method.getParameterTypes());
            request.setValues(args);

            RemoteHandler remoteHandler = new RemoteHandler();
            EventLoopGroup workerGroup = new NioEventLoopGroup();

            Bootstrap client = new Bootstrap()
                    .group(workerGroup)
                    .channel(NioSocketChannel.class)
                    .option(ChannelOption.SO_KEEPALIVE, true)
                    .handler(new ChannelInitializer<SocketChannel>() {
                        @Override
                        protected void initChannel(SocketChannel socketChannel) throws Exception {
                            ChannelPipeline pipeline = socketChannel.pipeline();

                            pipeline.addLast(new LengthFieldBasedFrameDecoder(Integer.MAX_VALUE, 0, 4, 0, 4));
                            pipeline.addLast(new LengthFieldPrepender(4));

                            pipeline.addLast("decoder", new ObjectDecoder(Integer.MAX_VALUE, ClassResolvers.cacheDisabled(null)));
                            pipeline.addLast("encoder", new ObjectEncoder());

                            pipeline.addLast(remoteHandler);

                        }
                    });


            ChannelFuture f = client.connect("localhost", 8080).sync();
            f.channel().writeAndFlush(request).sync();
            f.channel().closeFuture().sync();
            workerGroup.shutdownGracefully();
            return remoteHandler.getResult();
        }
    }
}
c、RemoteHandler远程调用逻辑类
  • 接受返回结果
public class RemoteHandler extends ChannelInboundHandlerAdapter {
    private Object result;


    public Object getResult() {
        return result;
    }

    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
        result = msg;
        super.channelRead(ctx, msg);
    }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        System.out.println("rpc registry exception");
        throw new RuntimeException(cause.getMessage());
    }
}
6、测试服务类
  • 启动RpcRegistry

  • 直接consumer上操作就好了

好了。至此。直接启动Netty版的RPC就完成了就可以访问了。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值