Netty——四

文章介绍了RPC的基本概念,包括其作为远程过程调用的作用以及常见的RPC框架。接着详细描述了RPC的调用流程,从服务消费方发起请求到服务提供方处理并返回结果。文章的重点在于,通过Netty构建了一个简单的RPC框架,模拟了Dubbo的RPC调用,包括定义公共接口、实现服务提供者、消费者以及利用Netty进行网络通信的各个步骤。
摘要由CSDN通过智能技术生成

十一、用Netty自己实现dubbo RPC

1、RPC基本介绍

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

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

    image-20230301221855994

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

    image-20230301221927036

2、RPC调用流程

RPC调用流程图

image-20230301221955196

术语说明:在RPC 中, Client 叫服务消费者,Server 叫服 务提供者

PRC调用流程说明
  1. 服务消费方(client)以本地调用方式调用服务

  2. client stub 接收到调用后负责将方法、参数等封装成能够进行网络传输的消息体

  3. client stub 将消息进行编码并发送到服务端

  4. server stub 收到消息后进行解码

  5. server stub 根据解码结果调用本地的服务

  6. 本地服务执行并将结果返回给 server stub

  7. server stub 将返回导入结果进行编码并发送至消费方

  8. client stub 接收到消息并进行解码

  9. 服务消费方(client)得到结果

小结:RPC 的目标就是将 2-8 这些步骤都封装起来,用户无需关心这些细节,可以像调 用本地方法一样即可完成远程服务调用。

3、自己实现 dubbo RPC(基于Netty)

需求说明
  1. dubbo 底层使用了 Netty 作为网络通讯框架,要求用 Netty 实现一个简单的 RPC 框 架

  2. 模仿 dubbo,消费者和提供者约定接口和协议,消费者远程调用提供者的服务,提供 者返回一个字符串,消费者打印提供者返回的数据。底层网络通信使用 Netty 4.1.20

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

dubbo RPC 思路分析图:

image-20230305224019821

代码实现

公共接口:

package com.lxg.netty.dubborpc.publicinterface;


//这个是接口,是服务提供方和服务消费方都需要
public interface HelloService {

    String hello(String mes);

}

实现类:

package com.lxg.netty.dubborpc.provider;

import com.lxg.netty.dubborpc.publicinterface.HelloService;

public class HelloServiceImpl implements HelloService {

    private static int count = 0;

    //当有消费方调用该方法时,就返回一个结果
    @Override
    public String hello(String mes) {
        System.out.println("收到客户端消息=" + mes);
        //根据mes返回不同的结果
        if (mes!= null) {
            return "你好客户端,我已经收到你的消息 [" + mes + "] 第"+ (++count) +"次";
        } else {
            return "你好客户端,我已经收到你的消息";
        }
    }
}

服务器端:

package com.lxg.netty.dubborpc.netty;


import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.EventLoopGroup;
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;

public class NettyServer {

    public static void startServer(String hostname, int port){
        startServer0(hostname, port);
    }


    //编写一个方法,完成对NettyServer的初始化和启动
    private static void startServer0(String hostname, int port){

        EventLoopGroup bossGroup = new NioEventLoopGroup(1);
        EventLoopGroup workerGroup = new NioEventLoopGroup();
        try{
            ServerBootstrap bootstrap = new ServerBootstrap();
            bootstrap.group(bossGroup, workerGroup)
                    .channel(NioServerSocketChannel.class)
                    .childHandler(new ChannelInitializer<SocketChannel>() {
                        @Override
                        protected void initChannel(SocketChannel ch) throws Exception {
                            ChannelPipeline pipeline = ch.pipeline();
                            //给pipeline设置处理器
                            pipeline.addLast(new StringDecoder()); //解码器
                            pipeline.addLast(new StringEncoder()); //编码器
                            pipeline.addLast(new NettyServerHandler()); //自定义的处理器
                        }
                    });
            ChannelFuture channelFuture = bootstrap.bind(hostname, port).sync();
            System.out.println("服务提供方开始提供服务~~");
            channelFuture.channel().closeFuture().sync();

        }catch (Exception e){
            e.printStackTrace();
        } finally{
            bossGroup.shutdownGracefully();
            workerGroup.shutdownGracefully();
        }

    }
}

服务器端handler:

package com.lxg.netty.dubborpc.netty;

import com.lxg.netty.dubborpc.customer.ClientBootstrap;
import com.lxg.netty.dubborpc.provider.HelloServiceImpl;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;

//服务器这边的handler比较简单
public class NettyServerHandler extends ChannelInboundHandlerAdapter {
    //读取数据实际(这里我们可以读取客户端发送的消息)
    /*
    1. ChannelHandlerContext ctx:上下文对象, 含有 管道pipeline , 通道channel, 地址
    2. Object msg: 就是客户端发送的数据 默认Object
    */
    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
        //获取客户端发送的消息,并调用服务
        System.out.println("msg=" + msg);
        //客户端在调用服务器的api时,我们需要定义一个协议
        //比如我们要求每次发消息都必须以某个字符串开头 "我来啦#你好呀#"
        if (msg.toString().startsWith(ClientBootstrap.providerName)) {
            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 {
        ctx.close();
    }
}

服务器端启动器:

package com.lxg.netty.dubborpc.provider;

import com.lxg.netty.dubborpc.netty.NettyServer;

//ServerBootstrap 启动一个服务提供者,就是NettyServer
public class ServerBootstrap {


    public static void main(String[] args) {
        //创建一个服务提供者,发布服务
        NettyServer.startServer("127.0.0.1",7005);
    }
}

客户端:

package com.lxg.netty.dubborpc.netty;

import io.netty.bootstrap.Bootstrap;
import io.netty.channel.*;
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.ExecutorService;
import java.util.concurrent.Executors;

public class NettyClient {

    //创建线程池
    private static ExecutorService executor = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors());

    private static NettyClientHandler client;

    private int count = 0;

    //编写方法使用代理模式,获取一个代理对象
    public Object getBean(final Class<?> serviceClass, final String providerName){
        return Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(),
                new Class<?>[]{serviceClass}, (proxy, method, args) -> {
                    System.out.println("(proxy, method, args) 进入....." + (++count) + " 次");
                    //客户端在调用服务器api 时,我们会去调用 invoke 方法
                    if(client == null){
                        initClient();
                    }
                    //设置要发给服务器端的信息
                    //providerName 协议头 args[0]就是客户端调用api hello(??)时传入的参数
                    client.setPara(providerName + args[0]);

                    //返回值
                    return executor.submit(client).get();
                });
    }

    //初始化客户端
    private static void initClient() throws Exception {
        client = new NettyClientHandler();
        //创建EventLoopGroup
        NioEventLoopGroup group = new NioEventLoopGroup();
        try {
            Bootstrap bootstrap = new Bootstrap();
            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); //加入自己的处理器
                        }
                    });
            bootstrap.connect("127.0.0.1", 7005).sync();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}


客户端handler:

package com.lxg.netty.dubborpc.netty;

import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;

import java.util.concurrent.Callable;

public class NettyClientHandler extends ChannelInboundHandlerAdapter implements Callable {

    private ChannelHandlerContext context; //上下文
    private String result; //返回的结果
    private String para; //客户端调用方法时,传入的参数


    //与服务器的连接创建后,就会被调用,这个方法是第一个被调用(1)
    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
        System.out.println(" channelActive 被调用  ");
        context=ctx; //因为我们在其它方法会使用到ctx

    }

    //收到服务器的数据后,调用方法(4)
    @Override
    public synchronized void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
        System.out.println(" channelRead 被调用  ");
        result=msg.toString();
        notify(); //唤醒等待的线程
    }

    //发生异常后,关闭通道(5)
    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        ctx.close();
    }

    //被代理对象调用,发送数据给服务器,-> wait -> 等待被唤醒(channelRead) -> 返回结果(3)
    @Override
    public synchronized Object call() throws Exception {
        System.out.println(" call1 被调用  ");
        context.writeAndFlush(para);
        //进行wait
        wait(); //等待channelRead 方法获取到服务器的结果后,唤醒
        System.out.println(" call2 被调用  ");
        return result; //服务方返回的结果
    }

    //设置参数(2)
    void setPara(String para){
        System.out.println(" setPara 被调用  ");
        this.para=para;
    }

}

客户端启动器:

package com.lxg.netty.dubborpc.customer;

import com.lxg.netty.dubborpc.netty.NettyClient;
import com.lxg.netty.dubborpc.publicinterface.HelloService;

public class ClientBootstrap {

    //这里定义协议头
    public static final String providerName = "我来啦#你好呀#";

    public static void main(String[] args) throws InterruptedException {
        //创建一个消费者
        NettyClient customer = new NettyClient();

        //创建一个代理对象
        HelloService service = (HelloService) customer
                .getBean(HelloService.class, providerName);

        for (;;){
            Thread.sleep(2*1000);
            //通过代理对象调用服务提供者的方法(服务)
            String res = service.hello("你好 dubbo~");
            System.out.println("调用的结果 res= " + res);
        }

    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值