Netty学习笔记(五):Netty实现简易版Dubbo RPC

第 9 章 实现简易版 dubbo RPC

一、RPC 基本介绍

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

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

Netty学习笔记(五):Netty实现简易版Dubbo RPC_第1张图片

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

二、RPC 调用流程说明

调用流程:

  • 服务消费方(client)以本地调用方式调用服务
  • clientstub 接收到调用后负责将方法、参数等封装成能够进行网络传输的消息体
  • clientstub 将消息进行编码并发送到服务端
  • serverstub 收到消息后进行解码
  • serverstub 根据解码结果调用本地的服务
  • 本地服务执行并将结果返回给 serverstub
  • serverstub 将返回导入结果进行编码并发送至消费方
  • clientstub 接收到消息并进行解码
  • 服务消费方(client)得到结果

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

Netty学习笔记(五):Netty实现简易版Dubbo RPC_第2张图片

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

三、实现

1、要求

要求用 Netty 实现一个简单的 RPC 框架 。模仿 dubbo,消费者和提供者约定接口和协议,消费者远程调用提供者的服务,提供者返回一个字符串,消费 者打印提供者返回的数据。底层网络通信使用 Netty4.1.20

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

2、实现

实现代码的目录结构如下:

  • publicinterface包中包含公共的接口

  • customer和provider分别代表服务消费端和服务提供端,它们都包含一个启动器,其中服务提供端还有接口的具体的实现。

  • netty包下面是rpc的实现
    Netty学习笔记(五):Netty实现简易版Dubbo RPC_第3张图片
    下面是具体的代码:

publicinterface包:

public interface HelloService {

  String hello(String ping);

}

netty包:

/**
 * 服务端
 */
public class NettyServer {

    /**
     * 启动客户端
     */
    public static void startServer(String hostName, int port) {
        startServer0(hostName, port);
    }

    private static void startServer0(String hostName, int port) {
        try {
            ServerBootstrap bootstrap = new ServerBootstrap();
            NioEventLoopGroup eventLoopGroup = new NioEventLoopGroup();
            bootstrap.group(eventLoopGroup)
                    .channel(NioServerSocketChannel.class)
                    .childHandler(new ChannelInitializer<SocketChannel>() {
                        @Override
                        protected void initChannel(SocketChannel ch) throws Exception {
                            ChannelPipeline p = ch.pipeline();
                            p.addLast(new StringDecoder());
                            p.addLast(new StringEncoder());
                            p.addLast(new ServerHandler());
                        }
                    });

            bootstrap.bind(hostName, port).sync();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}
/**
 * 用于处理请求数据
 */
public class ServerHandler extends ChannelInboundHandlerAdapter {

  @Override
  public void channelRead(ChannelHandlerContext ctx, Object msg) {

    // 如何符合约定,则调用本地方法,返回数据
    if (msg.toString().startsWith(ClientBootstrap.providerName)) {
      String result = new HelloServiceImpl()
          .hello(msg.toString().substring(msg.toString().lastIndexOf("#") + 1));
      ctx.writeAndFlush(result);
    }

  }
}
public class NettyClient {

    private static ExecutorService executor = Executors
            .newFixedThreadPool(Runtime.getRuntime().availableProcessors());

    private static ClientHandler client;

    /**
     * 创建一个代理对象
     */
    public Object getBean(final Class<?> serviceClass,
                          final String providerName) {
        return Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(),
                new Class<?>[]{serviceClass}, (proxy, method, args) -> {
                    if (client == null) {
                        initClient();
                    }
                    // 设置参数
                    client.setPara(providerName + args[0]);
                    return executor.submit(client).get();
                });
    }

    /**
     * 初始化客户端
     */
    private static void initClient() {
        client = new ClientHandler();
        EventLoopGroup group = new NioEventLoopGroup();
        Bootstrap b = new Bootstrap();
        b.group(group)
                .channel(NioSocketChannel.class)
                .option(ChannelOption.TCP_NODELAY, true)
                .handler(new ChannelInitializer<SocketChannel>() {
                    @Override
                    public void initChannel(SocketChannel ch) throws Exception {
                        ChannelPipeline p = ch.pipeline();
                        p.addLast(new StringDecoder());
                        p.addLast(new StringEncoder());
                        p.addLast(client);
                    }
                });
        try {
            b.connect("127.0.0.1", 7002).sync();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}
public class ClientHandler extends ChannelInboundHandlerAdapter implements Callable {

    private ChannelHandlerContext context;

    private String result;

    private String para;

    /**
     * 与服务器的连接已经及建立之后将被调用
     *
     * @param ctx
     */
    @Override
    public void channelActive(ChannelHandlerContext ctx) {
        context = ctx;
    }

    /**
     * 收到服务端数据,唤醒等待线程
     */
    @Override
    public synchronized void channelRead(ChannelHandlerContext ctx, Object msg) {
        result = msg.toString();
        notify();
    }

    /**
     * 写出数据,开始等待唤醒
     */
    @Override
    public synchronized Object call() throws InterruptedException {
        context.writeAndFlush(para);
        wait();
        return result;
    }

    void setPara(String para) {
        this.para = para;
    }
}

provider包:

/**
 * 实现类
 */
public class HelloServiceImpl implements HelloService {

  @Override
  public String hello(String msg) {
    System.out.println("收到客户端消息: " + msg);
    return msg != null ? msg + " -----> hello client." : "hello client.";
  }
}
public class ServerBootstrap {

  public static void main(String[] args) {
    NettyServer.startServer("127.0.0.1", 7002);

  }
}

customer包:

public class ClientBootstrap {

  public static final String providerName = "HelloService#hello#";

  public static void main(String[] args) throws InterruptedException {

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

    for (; ; ) {
      Thread.sleep(3000);
      System.out.println(service.hello("你好 dubbo ~ "));
    }
  }
}

运行结果:

服务提供端:

收到客户端消息: 你好 dubbo ~ 
收到客户端消息: 你好 dubbo ~ 
收到客户端消息: 你好 dubbo ~ 

服务调用端:

你好 dubbo ~  -----> hello client.
你好 dubbo ~  -----> hello client.
你好 dubbo ~  -----> hello client.

ntln(service.hello("你好 dubbo ~ "));
}
}
}


运行结果:

服务提供端:

```java
收到客户端消息: 你好 dubbo ~ 
收到客户端消息: 你好 dubbo ~ 
收到客户端消息: 你好 dubbo ~ 

服务调用端:

你好 dubbo ~  -----> hello client.
你好 dubbo ~  -----> hello client.
你好 dubbo ~  -----> hello client.

你可能感兴趣的:(Netty,后端)