【重复造轮子系列】手撸RPC(二)

一、前言

在上一篇文章中我们模仿着Dubbo初步搭建了一个RPC框架,虽然目前什么都没有实现,但是万丈高楼平地起,接下来我们会一步步完善我们的框架。

二、一次RPC的流程是什么样的

在正式编写代码之前我们需要了解的是一次完整的RPC调用是怎么样的。这里我们以ZK为注册中心,UserService作为服务提供方,OrderService做为消费方为例来聊一聊一次完整的RPC调用。

  1. 首先在服务启动的初期服务提供方(UserService)需要将提供的接口注册到ZK上(假设该接口为getUserInfo(Long userId))
  2. 消费方(OrderService)想要获取用户信息所以需要调用UserService。那么就至少需要知道UserSerivce的地址信息(IP+端口),所以OrderService会到ZK中查找对应的信息。
  3. 拿到信息后就要调用UserService对应的方法,在UserService没有提供Http接口的前提下我们怎么调用呢?显然就要用到RPC调用。OrderService通过网络发送对应的请求,UserService收到请求后返回对应的响应,这就是一个简单的RPC调用过程。接下来我们要讨论的是如这个网络请求是如何发送的。

三、在RPC调用中怎么发送网络请求

Java天生是支持网络编程的,想要发送一个网络请求也是比较容易的,下面就是几个方案。
1、最容易想到的就是采用Socket实现BIO模型的网络通信,每次调用之前服务端和消费端都要建立一个Socket,然后通信结束后释放链接,这么做的优缺点都非常明显,优点:模型简单,代码易懂。缺点:效率很低,浪费资源。一个线程处理一个链接。
2、NIO模型,Jdk1.4之后提供了NIO相关API(这里不细谈NIO)。这个方案弥补了方案1缺点,但是NIO的代码相对晦涩难懂。
3、基于Netty实现网络通信,Netty是一个基于事件驱动的NIO框架,提供了相对简单的API屏蔽了Jdk自带NIO晦涩难懂的概念。
综上我们采用Netty作为我们RPC框架的网络模块框架。既然选中了Netty作为网络框架,那么整个网络发送的流程是怎么样的?

  1. 首先我们要知道,作为网络通信一定是有一定的规则的,这个规则我们称之为“通信协议”。所谓的协议,可以认为是一种约定,即服务端和客户端定义好数据是如何解析的。由于在网络传输的过程都是比特流(01010101),所以双方需要约定好如何解读这些数据。
  2. 我们发送的请求在网络中传输的过程中是比特流,所以我们需要将参数进行序列化操作。

这两个是必须的,当然我们也可以在此基础上做一些额外的操作,例如记录日志,压缩等等。下面我们以图的形式来展示。 image.png

四、来一次简单的网络通信

1、服务端代码

public class ServerTest {

    public static void main(String[] args) throws InterruptedException {
        ServerBootstrap serverBootstrap = new ServerBootstrap();
        //boss 只负责接收请求
        final NioEventLoopGroup bossGroup = new NioEventLoopGroup(1);
        //worker 负责具体业务
        final NioEventLoopGroup workerGroup = new NioEventLoopGroup();
        final ChannelFuture channelFuture = serverBootstrap.group(bossGroup, workerGroup)
            .channel(NioServerSocketChannel.class)
            .childHandler(new ChannelInitializer<SocketChannel>() {
                @Override
                protected void initChannel(final SocketChannel channel) throws Exception {
                    //服务器初始化
                    channel.writeAndFlush(Unpooled.copiedBuffer("Hello I am Server".getBytes(StandardCharsets.UTF_8)));
                }
            }).bind(8888);
        channelFuture.sync();
        channelFuture.channel().closeFuture().sync();
    }
}

2、客户端代码

public class ConsumerTest {

    public static void main(String[] args) throws InterruptedException {
        Bootstrap bootstrap = new Bootstrap();
        EventLoopGroup group = new NioEventLoopGroup();
        bootstrap.group(group).remoteAddress("127.0.0.1",8888).channel(NioSocketChannel.class)
            .handler(new ChannelInitializer<SocketChannel>() {
                @Override
                protected void initChannel(final SocketChannel socketChannel) throws Exception {
                    System.out.println("客户端初始化handler");
                    socketChannel.pipeline().addLast(new SimpleChannelInboundHandler<ByteBuf>() {
                        @Override
                        protected void channelRead0(final ChannelHandlerContext channelHandlerContext,
                            final ByteBuf byteBuf)
                            throws Exception {
                            System.out.println("收到服务端的消息:"+byteBuf.toString(Charset.defaultCharset()));
                        }
                    });
                }
            });
        final ChannelFuture connect = bootstrap.connect();
        final ChannelFuture channelFuture = connect.sync();
        channelFuture.channel().writeAndFlush(Unpooled.copiedBuffer("hello world".getBytes(StandardCharsets.UTF_8)));
        //阻塞,等待接收关闭消息
        channelFuture.channel().closeFuture().sync();
    }
}

3、结果

image.png
至此我们已经完成了一次最简单的客户端和服务端的通信,那么接下来要考虑的是如何完成我们的RPC请求呢?这一部分我们放到下一篇中完成

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值