从零开始我的rpc第三篇之通信层Provider端

上一篇讲了consumer端,这次说下provider端.不过老是在client/consumer,server/provider名词间胡乱转换,挺烦的.个人觉得站在通信层更适合讲client/server,不过站在整个rpc上来讲,还是consumer/provider比较合适.好了,回到这个provider端来.
首先我们梳理下server主要的功能有哪些.
当然server肯定是用来接受client发来的消息的.那么client想得到的是她的方法调用的结果.所以,server端要做的就是根据消息来找到对应的方法获得调用结果.
这里我们同样从netty server开始讲起:

boot.group(boss, worker)
                    .channel(NioServerSocketChannel.class)
                    .handler(new LoggingHandler(LogLevel.DEBUG))
                    .option(ChannelOption.SO_BACKLOG, 128)
                    .childOption(ChannelOption.TCP_NODELAY, true)
                    .childHandler(new ChannelInitializer<SocketChannel>() {
        @Override
        protected void initChannel(SocketChannel ch) throws Exception {
            final ChannelPipeline pipe = ch.pipeline();
            pipe
                    .addLast("idleStateHandler",
                            new IdleStateHandler(BeaconConstants.IDLE_READ_TIMEOUT, 0, 0, TimeUnit.SECONDS))
                    .addLast("lengthDecoder",
                            new LengthFieldBasedFrameDecoder(BeaconConstants.MAX_LEN,
                                    BeaconConstants.LEN_OFFSET, BeaconConstants.INT_LEN))
                    .addLast("beaconDecoder", new NettyDecoder())
                    .addLast("beaconEncoder", new NettyEncoder())
                    .addLast("beaconServerHandler", serverHandler);
        }
    };);
            f = boot.bind(port).syncUninterruptibly();

netty的server和client格式差不多,但是server端是由俩个NioEventLoopGroup组成:boss和worker,这样命名可以理解为boss只是接收client发来的消息,而具体的消息怎么处理交给worker去做.好处就是降低了每个事件处理任务的复杂度,通过分发任务增加了并发量.然后具体的handler和client端差不多,和client端共享一套编解码机制,idleStateHandler是用来处理心跳的,也是netty原生提供的方法.而我们自定义的serverHandler用来处理我们的具体业务.


     @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
        BeaconHandler handler = NettyChannel.getChannel(ctx.channel(), From.SERVER);
        try {
            handler.receive(msg);
        } finally {
            if (!ctx.channel().isActive()) {
                NettyChannel.removeChannel(ctx.channel());
            }
        }
    }

我们这里来看接受消息的方法 channelRead,和client端几乎一样.这里的BeaconHandler是我们上一篇讲到的我们rpc用来处理一些逻辑的,我们接收到消息后,直接交给handler来处理这些消息.其中NettyChannel是我对BeaconHandler和netty的channel的一个封装,用来对channel的复用管理.

/**
     * 每一个channel都会生成对应的BaseChannel,用于对channel的存储
     */
    public static final ConcurrentMap<Channel, BeaconHandler> CHANNEL_MAP = new ConcurrentHashMap<>(16);

    private Channel channel;

    public NettyChannel(Channel channel) {
        this.channel = channel;

    }

    public static BeaconHandler getChannel(Channel ch, From client) throws Exception {
        BeaconHandler beacon = CHANNEL_MAP.get(ch);
        if (beacon == null) {
            NettyChannel nc = new NettyChannel(ch);
            if (From.CLIENT == client) {
                BeaconHandler b = new BeaconClientHandler(new BeaconClientChannel(nc));
                CHANNEL_MAP.putIfAbsent(ch, (beacon = b));
            } else {
                BeaconHandler b = new BeaconServerHandler(new BeaconServerChannel(nc));
                CHANNEL_MAP.putIfAbsent(ch, (beacon = b));
            }
        }
        return beacon;
    }

上一篇讲到我们baseChannel的核心就是netty的channel,所以我们看到所有的封装的核心都是围绕channel展开.
我们回到handler上面来.server接受到消息交给handler处理.当然这里和consumer一样,我定义的beaconServerHandler依旧木有做啥子事情.最终还是让我们的BeaconServerChannel来处理消息.

 @Override
    protected void doReceive(Object message) throws Exception {
        this.addTask(new Runnable() {
            @Override
            public void run() {
                RpcResponse resp = new RpcResponse();
                Object result = null;
                try {
                    try {
                        // 处理收到client信息
                        Class<?> target = Class.forName(req.getInterfaceImpl());
                        // 根据信息,找到实现类
                        for (Method d : target.getDeclaredMethods()) {
                            if (d.getName().equals(req.getMethodName())) {
                                result = d.invoke(target.newInstance(), req.getParams());
                                break;
                            }
                        }
                    } catch (Exception bize) {
                        // 非rpc异常
                        throw new BizException(bize);
                    }
                } catch (Exception e) {
                    LOG.error("error->" + e);
                    resp.setException(e);
                }
                // 调用发送给client发送结果
                baseChannel.send(resp.setResult(result));
            }
        });

    }

这里同样是通过线程池来处理消息,可以借用netty server的思想,我们的netty channel只负责接收消息,具体的逻辑处理交给rpc的channel来做,这样就可以增加netty channel的处理能力,所以我们采用线程池来做.然后这里逻辑也不难,通过获取消息里面的信息,找到实现类,经过反射获取结果,然后直接调用baseChannel发送给client端.
ok,server端差不多就这样了,主要是说了核心的代码思想,也可以看到我们借用了很多netty的写代码思路.希望我也可以设计出好的代码来.
具体的实现参照我的github:https://github.com/dressrosa/beacon

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值