Netty- Netty实现RPC

Netty实现RPC

RPC基本介绍
  • RPC(Remote Procedure Call)–远程过程调用,是一个计算机通信协议。该协议允许运行一台计算机的程序调用另一台计算机的子程序,而程序员无需额外地为这个交互作用编程

  • 两个或多个应用程序都分布在不同的服务器上,它们之间的调用像是本地方法调用一样

  • 在这里插入图片描述

  • 常见的RPC框架有:比较知名的有阿里的Dubbo、google的gRPC,Go语言的rpcx、Apache的thrift,Spring旗下的Spring Cloud

RPC调用流程说明
  • 在这里插入图片描述

  • 流程说明

    • 服务消费放(client)以本地调用方式调用服务
    • client stub接收调用后负责将方法、参数等封装成能够进行网络传输的消息体
    • client stub将消息进行编码并发送到服务端
    • server stub收到消息后进行解码
    • server stub根据解码结果调用本地的服务
    • 本地服务执行并将结果返回给server stub
    • server stub将返回导入结果进行编码并发送至消费放
    • client stub接收到消息并进行解码
    • 服务消费放(client)得到结果
  • 小结:RPC的目标就是将2-8这些步骤都封装起来,用户无需关系这些细节,可以像调用本地方法一样即可完成远程服务调用

使用Netty实现dubbo RPC(基于Netty)
  • 需求说明

    • dubbo底层使用了Netty作为网络通讯框架,要求用Netty实现一个简单的RPC框架
    • 模仿dubbo,消费者和提供者约定接口和协议,消费者远程调用提供者,提供者返回一个字符串,消费者打印提供者返回数据。底层网络通信使用Netty 4.x
  • 设计说明

    • 创建一个接口,定义抽象方法。用于消费者和提供者之间的约定。
    • 创建一个提供者,该类需要监听消费者的请求,并按照约定返回数据。
    • 创建一个消费者,该类需要透明的调用自己不存在的方法,内部需要使用Netty请求提供者返回数据
  • 代码

    • 创建一个接口存根接口提供给客户端服务端使用,HelloService

      • package com.jl.java.web.dubborpc.stub;
        
        /**
         * 接口
         * 服务提供方和消费者方都需要的
         * @author jiangl
         * @version 1.0
         * @date 2021/5/30 10:57
         */
        public interface HelloService {
            String hello(String msg);
        }
        
    • 服务端HelloServiceImpl实现HelloService接口,供消费者调用

      • package com.jl.java.web.dubborpc.provider;
        
        import com.jl.java.web.dubborpc.stub.HelloService;
        
        /**
         * @author jiangl
         * @version 1.0
         * @date 2021/5/30 10:58
         */
        public class HelloServiceImpl implements HelloService {
            /**
             * 当有消费方调用该方法时,就返回一个结果
             * @param msg
             * @return
             */
            @Override
            public String hello(String msg) {
                System.out.println("收到客户端消息="+msg);
                if(msg != null){
                    return "你好客户端,已经收到消息,["+msg+"]";
                }else{
                    return "你好客户端,已经收到消息";
                }
            }
        }
        
    • ServerBootstrap

      • package com.jl.java.web.dubborpc.provider;
        
        import com.jl.java.web.dubborpc.netty.NettyServer;
        
        /**
         * 会启动一个服务的提供者就是Netty Server
         * @author jiangl
         * @version 1.0
         * @date 2021/5/30 11:00
         */
        public class ServerBootstrap {
            public static void main(String[] args) {
                NettyServer.startServer("127.0.0.1",7000);
            }
        }
        
    • NettyServer

      • package com.jl.java.web.dubborpc.netty;
        
        import io.netty.bootstrap.ServerBootstrap;
        import io.netty.channel.ChannelFuture;
        import io.netty.channel.ChannelInitializer;
        import io.netty.channel.ChannelOption;
        import io.netty.channel.ChannelPipeline;
        import io.netty.channel.nio.NioEventLoopGroup;
        import io.netty.channel.socket.SocketChannel;
        import io.netty.channel.socket.nio.NioServerSocketChannel;
        import io.netty.handler.codec.string.StringDecoder;
        import io.netty.handler.codec.string.StringEncoder;
        
        /**
         * @author jiangl
         * @version 1.0
         * @date 2021/5/30 11:01
         */
        public class NettyServer {
        
            /**
             * 编写一个方法,完成对NettyServer的初始化和启动
             * @param hostname
             * @param port
             */
            private static void startServer0(String hostname,int port){
                NioEventLoopGroup bossGroup = new NioEventLoopGroup(1);
                NioEventLoopGroup workerGroup = new NioEventLoopGroup();
                try{
                    ServerBootstrap serverBootstrap = new ServerBootstrap().group(bossGroup,workerGroup)
                            .channel(NioServerSocketChannel.class)
                            .childHandler(new ChannelInitializer<SocketChannel>() {
                                @Override
                                protected void initChannel(SocketChannel ch) throws Exception {
                                    ChannelPipeline pipeline = ch.pipeline();
                                    pipeline.addLast(new StringDecoder());
                                    pipeline.addLast(new StringEncoder());
                                    pipeline.addLast(new NettyServerHandler());
                                }
                            });
                    ChannelFuture channelFuture = serverBootstrap.bind(hostname, port).sync();
                    System.out.println("服务提供方启动...开始提供符");
                    channelFuture.channel().closeFuture().sync();
                }catch (Exception e){
        
                }finally {
                    bossGroup.shutdownGracefully();
                    workerGroup.shutdownGracefully();
                }
            }
        
            public static void startServer(String hostname,int port){
                startServer0(hostname,port);
            }
        }
        
    • NettyServerHandler

      • package com.jl.java.web.dubborpc.netty;
        
        import com.jl.java.web.dubborpc.provider.HelloServiceImpl;
        import io.netty.channel.ChannelHandlerContext;
        import io.netty.channel.ChannelInboundHandlerAdapter;
        import io.netty.channel.SimpleChannelInboundHandler;
        
        /**
         * @author jiangl
         * @version 1.0
         * @date 2021/5/30 11:22
         */
        public class NettyServerHandler extends ChannelInboundHandlerAdapter {
            @Override
            public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
                //客户端发送的消息,并调用服务
                System.out.println("msg="+msg);
                //客户端在调用服务器的api时,需要定义一个协议
                //比如,要求 每次发消息是都是必须以某个字符串开头"HelloService#hello#"
                if(msg.toString().startsWith("HelloService#hello#")){
                    String result = new HelloServiceImpl().hello(msg.toString().substring(msg.toString().lastIndexOf("#") + 1));
                    ctx.writeAndFlush(result);
                }
            }
        
            @Override
            public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
                cause.printStackTrace();
                ctx.close();
            }
        }
        
    • ClientBootstrap

      • package com.jl.java.web.dubborpc.customer;
        
        import com.jl.java.web.dubborpc.netty.NettyClient;
        import com.jl.java.web.dubborpc.stub.HelloService;
        
        /**
         * @author jiangl
         * @version 1.0
         * @date 2021/5/30 12:36
         */
        public class ClientBootstrap {
        
            //这里定义协议头
            private static final String providerName = "HelloService#hello#";
        
            public static void main(String[] args) {
                //创建一个消费者
                NettyClient nettyClient = new NettyClient();
                HelloService helloService = (HelloService) nettyClient.getBean(HelloService.class, providerName);
                String result = helloService.hello("你好");
                System.out.println(result);
            }
        }
        
    • NettyClient

      • package com.jl.java.web.dubborpc.netty;
        
        import io.netty.bootstrap.Bootstrap;
        import io.netty.channel.ChannelFuture;
        import io.netty.channel.ChannelInitializer;
        import io.netty.channel.ChannelOption;
        import io.netty.channel.ChannelPipeline;
        import io.netty.channel.nio.NioEventLoopGroup;
        import io.netty.channel.socket.SocketChannel;
        import io.netty.channel.socket.nio.NioSocketChannel;
        import io.netty.handler.codec.string.StringDecoder;
        import io.netty.handler.codec.string.StringEncoder;
        
        import java.lang.reflect.InvocationHandler;
        import java.lang.reflect.Method;
        import java.lang.reflect.Proxy;
        import java.util.concurrent.Executor;
        import java.util.concurrent.ExecutorService;
        import java.util.concurrent.Executors;
        
        /**
         * @author jiangl
         * @version 1.0
         * @date 2021/5/30 12:16
         */
        public class NettyClient {
        
            //创建线程池
            private static ExecutorService executor = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors());
        
            private static NettyClientHandler client;
        
            //编写方法使用代理模式,获取一个代理对象
            public Object getBean(final Class<?> serviceClass,final String providerName){
                return Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(), new Class<?>[]{serviceClass}, new InvocationHandler() {
                    @Override
                    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                        //单例
                        if(client == null){
                            initClient();
                        }
                        //设置要发送给服务器端的信息
                        //providerName 协议头 args[0] 就是客户端调用api hello(???) ,参数
                        client.setPara(providerName+args[0]);
                        return executor.submit(client).get();
                    }
                });
            }
        
            //初始化客户端
            private static void initClient(){
                client = new NettyClientHandler();
                //创建EventLoopGroup
                NioEventLoopGroup group = new NioEventLoopGroup();
        
                try {
                    Bootstrap bootstrap = new Bootstrap().group(group)
                            .channel(NioSocketChannel.class)
        //                    .option(ChannelOption.TCP_NODELAY,true)
                            .handler(new ChannelInitializer<SocketChannel>() {
                                @Override
                                protected void initChannel(SocketChannel ch) throws Exception {
                                    ChannelPipeline pipeline = ch.pipeline();
                                    pipeline.addLast(new StringDecoder());
                                    pipeline.addLast(new StringEncoder());
                                    pipeline.addLast(client);
                                }
                            });
                    ChannelFuture channelFuture = bootstrap.connect("127.0.0.1", 7000).sync();
        //            channelFuture.channel().closeFuture().sync();
                } catch (Exception e) {
                    e.printStackTrace();
                }finally {
        //            group.shutdownGracefully();
                }
            }
        }
        
    • NettyClientHandler

      • package com.jl.java.web.dubborpc.netty;
        
        import io.netty.channel.ChannelHandlerContext;
        import io.netty.channel.ChannelInboundHandlerAdapter;
        
        import java.util.concurrent.Callable;
        
        /**
         * @author jiangl
         * @version 1.0
         * @date 2021/5/30 11:46
         */
        public class NettyClientHandler extends ChannelInboundHandlerAdapter implements Callable {
        
            private ChannelHandlerContext context;//上下文
            private String result;//返回结果
            private String para;//客户端调用方法时,传入参数
        
            /**
             * 被代理对象调用,发送数据给服务器->wait,等待被唤醒-> 返回结果
             * @return
             * @throws Exception
             */
            @Override
            public synchronized Object call() throws Exception {
                context.writeAndFlush(para);
                //进行wait
                wait();//等待channelRead 方法获取到服务器的结果后,唤醒
                return result;
            }
        
            @Override
            public void channelActive(ChannelHandlerContext ctx) throws Exception {
                //需要在其他方法会使用到ctx
                context = ctx;
            }
        
            @Override
            public synchronized void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
                result = msg.toString();
                notify();
            }
        
            @Override
            public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
                ctx.close();
            }
        
            public String getPara() {
                return para;
            }
        
            public void setPara(String para) {
                this.para = para;
            }
        }
        

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值