笔记5:Netty的自定义RPC(JSON序列化协议)

14 篇文章 0 订阅

目标:Netty的自定义RPC,序列化协议为JSON,使用fastjson作为JSON框架,并根据RpcRequest实体作为通信协议,服务端需根据客户端传递过来的RpcRequest对象通过反射,动态代理等技术,最终能够执行目标方法,返回字符串"success"。

  1. 结构
    分为三个模块:rpc-common 通用模块, rpc-consumer 客户端模块, rpc-my-provider客服端模块
    在这里插入图片描述

  2. rpc-common模块
    在这里插入图片描述
    1)接口

    public interface IUserService {
    
        public RpcResponse sayHello(String msg);
    
    }
    

    2)JSON序列化工具

    public interface Serializer {
        /**
         * java对象转换为二进制
         */
        byte[] serialize(Object object) throws IOException;
        
        /**
         * 二进制转换成java对象
         */
        <T> T deserialize(Class<T> clazz, byte[] bytes) throws IOException;
    }
    
    public class JSONSerializer implements Serializer {
    
        @Override
        public byte[] serialize(Object object) {
            return JSON.toJSONBytes(object);
        }
        
        @Override
        public <T> T deserialize(Class<T> clazz, byte[] bytes) {
            return JSON.parseObject(bytes, clazz);
        }
    }
    

    3)定义编码和解码

    public class RpcEncoder extends MessageToByteEncoder {
    
        private Class<?> clazz;
        private Serializer serializer;
        
        public RpcEncoder(Class<?> clazz, Serializer serializer) {
            this.clazz = clazz;
            this.serializer = serializer;
        }
    
        @Override
        protected void encode(ChannelHandlerContext channelHandlerContext, Object msg, ByteBuf byteBuf) throws Exception {
            if (clazz != null && clazz.isInstance(msg)) {
                byte[] bytes = serializer.serialize(msg);
                byteBuf.writeInt(bytes.length);
                byteBuf.writeBytes(bytes);
            }
        }
    }
    
    public class RpcDecoder extends ByteToMessageDecoder {
    
        private Class<?> clazz;
        private Serializer serializer;
        
        public RpcDecoder(Class<?> clazz, Serializer serializer) {
            this.clazz = clazz;
            this.serializer = serializer;
        }
    
        @Override
        protected void decode(ChannelHandlerContext channelHandlerContext, ByteBuf byteBuf, List<Object> list) throws Exception {
            // Check if there are at least 4 bytes readable
            if (byteBuf.readableBytes() >= 4) {
                int readInt = byteBuf.readInt();
                System.out.println("ByteToIntegerDecoder decode msg is " + readInt);
                byte[] bytes = new byte[readInt];
                byteBuf.readBytes(bytes);
                Object deserialize = serializer.deserialize(clazz, bytes);
                list.add(deserialize);
            }
        }
    }
    

    4)定义Request和Response实体组件

    public class RpcRequest {
    
        /**
         * 请求对象的ID
         */
        private String requestId;
    
        /**
         * 类名
         */
        private String className;
    
        /**
         * 方法名
         */
        private String methodName;
    
        /**
         * 参数类型
         */
        private Class<?>[] parameterTypes;
    
        /**
         * 入参
         */
        private Object[] parameters;
    
    
        public String getRequestId() {
            return requestId;
        }
    
        public void setRequestId(String requestId) {
            this.requestId = requestId;
        }
    
        public String getClassName() {
            return className;
        }
    
        public void setClassName(String className) {
            this.className = className;
        }
    
        public String getMethodName() {
            return methodName;
        }
    
        public void setMethodName(String methodName) {
            this.methodName = methodName;
        }
    
        public Class<?>[] getParameterTypes() {
            return parameterTypes;
        }
    
        public void setParameterTypes(Class<?>[] parameterTypes) {
            this.parameterTypes = parameterTypes;
        }
    
        public Object[] getParameters() {
            return parameters;
        }
    
        public void setParameters(Object[] parameters) {
            this.parameters = parameters;
        }
    }
    
    public class RpcResponse {
    
        private String responseId;
    
        private String resultMsg;
    
        private String result;
    
    
        public String getResponseId() {
            return responseId;
        }
    
        public void setResponseId(String responseId) {
            this.responseId = responseId;
        }
    
        public String getResultMsg() {
            return resultMsg;
        }
    
        public void setResultMsg(String resultMsg) {
            this.resultMsg = resultMsg;
        }
    
        public String getResult() {
            return result;
        }
    
        public void setResult(String result) {
            this.result = result;
        }
    
        @Override
        public String toString() {
            return "RpcResponse{" +
                    "responseId='" + responseId + '\'' +
                    ", resultMsg='" + resultMsg + '\'' +
                    ", result='" + result + '\'' +
                    '}';
        }
    }
    
  3. rpc-consumer 客服端编写
    在这里插入图片描述
    1)netty客户端的初始化及动态代理

    public class RpcConsumer {
    
        /**
         * 1.创建一个线程池对象  -- 它要处理我们自定义事件
         */
        private static ExecutorService executorService = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors());
    
        /**
         * 2.声明一个自定义事件处理器  UserClientHandler
         */
        private static UserClientHandler userClientHandler;
    
        /**
         * 3.编写方法,初始化客户端  ( 创建连接池  bootStrap  设置bootstrap  连接服务器)
         */
        public static void initClient() throws InterruptedException {
            //1) 初始化UserClientHandler
            userClientHandler = new UserClientHandler();
            //2)创建连接池对象
            NioEventLoopGroup group = new NioEventLoopGroup();
            //3)创建客户端的引导对象
            Bootstrap bootstrap = new Bootstrap();
            //4)配置启动引导对象
            bootstrap.group(group)
                    //设置通道为NIO
                    .channel(NioSocketChannel.class)
                    //设置请求协议为TCP
                    .option(ChannelOption.TCP_NODELAY, true)
                    //监听channel 并初始化
                    .handler(new ChannelInitializer<SocketChannel>() {
                        @Override
                        protected void initChannel(SocketChannel socketChannel) throws Exception {
                            //获取ChannelPipeline
                            ChannelPipeline pipeline = socketChannel.pipeline();
                            //设置编码
                            pipeline.addLast(new RpcEncoder(RpcRequest.class, new JSONSerializer()));
                            pipeline.addLast(new RpcDecoder(RpcResponse.class, new JSONSerializer()));
    
                            //添加自定义事件处理器
                            pipeline.addLast(userClientHandler);
                        }
                    });
            //5)连接服务端
            bootstrap.connect("127.0.0.1", 8999).sync();
        }
    
        /**
         * 4.编写一个方法,使用JDK的动态代理创建对象
         * serviceClass 接口类型,根据哪个接口生成子类代理对象;   providerParam :  "UserService#sayHello#"
         */
        public static Object createProxy(Class<?> serviceClass, final String providerParam) {
            return Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(), new Class[]{serviceClass}, new InvocationHandler() {
                @Override
                public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                    //1)初始化客户端client
                    if(userClientHandler == null) {
                        initClient();
                    }
    
                    //2)给UserClientHandler 设置param参数
                    RpcRequest rpcRequest = new RpcRequest();
                    rpcRequest.setRequestId(UUID.randomUUID().toString());
                    String[] classNameAndMethod = providerParam.split("#");
                    rpcRequest.setClassName(serviceClass.getName());
                    rpcRequest.setMethodName(classNameAndMethod[1]);
                    rpcRequest.setParameters(args);
                    rpcRequest.setParameterTypes(new Class[]{String.class});
                    userClientHandler.setParam(rpcRequest);
    
                    //3).使用线程池,开启一个线程处理处理call() 写操作,并返回结果
                    //4)return 结果
                    return executorService.submit(userClientHandler).get();
                }
            });
        }
    }
    

    2)自定义事件处理器

    public class UserClientHandler extends ChannelInboundHandlerAdapter implements Callable {
    
        /**
         * 1.定义成员变量
         * context: 事件处理器上下文对象 (存储handler信息,写操作)
         * result: 记录服务器返回的数据
         * param: 记录将要返送给服务器的数据
         */
        private ChannelHandlerContext context;
        private RpcRequest param;
        private RpcResponse result;
    
        /**
         * 2.实现channelActive  客户端和服务器连接时,该方法就自动执行
         */
        @Override
        public void channelActive(ChannelHandlerContext ctx) throws Exception {
            this.context = ctx;
        }
    
        /**
         * 3.实现channelRead 当我们读到服务器数据,该方法自动执行
         */
        @Override
        public synchronized void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
            result = (RpcResponse) msg;
            notify();
        }
    
        /**
         * 4.将客户端的数写到服务器
         */
        @Override
        public synchronized Object call() throws Exception {
            context.writeAndFlush(param);
            wait();
            return result;
        }
    
        /**
         * 5.设置参数的方法
         */
        public void setParam(RpcRequest param) {
            this.param = param;
        }
    }
    

    3)启动类, 向服务器发消息以及接收消息

    public class ConsumerBoot {
    
        /**
         * 参数定义
         */
        private static final String PROVIDER_NAME = "UserService#sayHello#";
    
        public static void main(String[] args) throws InterruptedException {
            //1.创建代理对象
            IUserService userService = (IUserService) RpcConsumer.createProxy(IUserService.class, PROVIDER_NAME);
    
            //2.循环给服务器写数据
            while (true) {
                System.out.println("客户端开始");
                RpcResponse result = userService.sayHello("Hi I am Tom, I want to play a game with u !");
                System.out.println("客服端返回:" + result.toString());
                System.out.println("客户端结束");
                //睡2s方便查看输出
                Thread.sleep(2000);
            }
        }
    }
    
  4. rpc-my-provider 服务端编写
    在这里插入图片描述
    1)启动类服务器

    @ComponentScan(value="com.lossdate")
    @SpringBootApplication
    public class ServerBoot {
    
        public static void main(String[] args) throws InterruptedException {
            SpringApplication.run(ServerBoot.class, args);
            // 启动服务器
            UserServiceImpl.startServer("127.0.0.1", 8999);
        }
    
    }
    

    2)netty客服端的初始化及返回客户端消息方法

    @Service
    public class UserServiceImpl implements IUserService {
    
        /**
         * 将来客户单要远程调用的方法
         */
        @Override
        public RpcResponse sayHello(String msg) {
            System.out.println("You ** **! -> " + msg);
            RpcResponse rpcResponse = new RpcResponse();
            rpcResponse.setResultMsg("服务器返回数据 : " + msg);
            rpcResponse.setResult("success");
            return rpcResponse;
        }
    
        /**
         * 创建一个方法启动服务器
         */
        public static void startServer(String ip, int port) throws InterruptedException {
            //1.创建两个线程池对象
            NioEventLoopGroup bossGroup = new NioEventLoopGroup();
            NioEventLoopGroup workGroup = new NioEventLoopGroup();
    
            //2.创建服务端的启动引导对象
            ServerBootstrap serverBootstrap = new ServerBootstrap();
    
            //3.配置启动引导对象
            serverBootstrap.group(bossGroup, workGroup)
                    //设置通道为NIO
                    .channel(NioServerSocketChannel.class)
                    //创建监听channel
                    .childHandler(new ChannelInitializer<NioSocketChannel>() {
                        @Override
                        protected void initChannel(NioSocketChannel nioSocketChannel) throws Exception {
                            //获取管道对象
                            ChannelPipeline pipeline = nioSocketChannel.pipeline();
                            //给管道对象pipeLine 设置编码
                            pipeline.addLast(new RpcEncoder(RpcResponse.class, new JSONSerializer()));
                            pipeline.addLast(new RpcDecoder(RpcRequest.class, new JSONSerializer()));
    
                            //把我们自定义的ChannelHander添加到通道中
                            pipeline.addLast(new UserServiceHandler());
                        }
                    });
    
            //4.绑定端口
            serverBootstrap.bind(ip, port).sync();
        }
    }
    

    3)获取客户端传来的消息,并通过反射调用sayHello方法,最后将消息传给客户端。这里需要借助实现ApplicationContextAware接口获取Bean

    public class UserServiceHandler extends ChannelInboundHandlerAdapter {
    
        /**
         * 当客户端读取数据时,该方法会被调用
         */
        @Override
        public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
            //注意:  客户端将来发送请求的时候会传递一个参数:  UserService#sayHello#Hi I am Tom, I want to play a game with u !
            RpcRequest rpcRequest = (RpcRequest) msg;
            System.out.println("服务端接收:" + rpcRequest.getRequestId());
            //通过反射获取class
            Class<?> aClass = Class.forName(rpcRequest.getClassName());
            //借助实现ApplicationContextAware接口获取Bean
            Object bean = SpringJobBeanFactory.getBean(aClass);
            //获取方法
            Method declaredMethod = aClass.getDeclaredMethod(rpcRequest.getMethodName(), rpcRequest.getParameterTypes());
            //invoke调用方法
            RpcResponse rpcResponse = (RpcResponse) declaredMethod.invoke(bean, rpcRequest.getParameters());
    
            //把调用实现类的方法获得的结果写到客户端
            rpcResponse.setResponseId(rpcRequest.getRequestId());
            ctx.writeAndFlush(rpcResponse);
        }
    }
    

    4)ApplicationContextAware获取Bean工具

    @Component
    public class SpringJobBeanFactory implements ApplicationContextAware {
    
        private static ApplicationContext applicationContext;
    
        @Override
        public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
            SpringJobBeanFactory.applicationContext = applicationContext;
        }
    
        public static ApplicationContext getApplicationContext() {
            return applicationContext;
        }
    
        @SuppressWarnings("unchecked")
        public static <T> T getBean(Class<T> name) throws BeansException {
            if (applicationContext == null){
                return null;
            }
            return (T)applicationContext.getBean(name);
        }
    }
    
  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值