手写rpc框架(2):利用netty进行底层连接

3 篇文章 0 订阅

上一篇我们通过socket对两个系统进行了通信,现在我们通过netty来对上一段demo的socket部分进行更换。

netty封装java socket noi,更好用。

可以通过这个栗子简单的学习一下netty的基本使用。
netty示例,亲测可用

我们的需求是将 comsumer和provider 之间建立起netty通讯。

在pom里添加netty依赖

        <dependency>
            <groupId>io.netty</groupId>
            <artifactId>netty-all</artifactId>
            <version>4.1.42.Final</version>
        </dependency>

        <dependency>
            <groupId>io.netty</groupId>
            <artifactId>netty-common</artifactId>
            <version>4.1.31.Final</version>
        </dependency>

项目框架如下:

在这里插入图片描述

新建 NettyClient类

@Slf4j
public class NettyClient {

    public void startAdd(CalculateRpcRequest calculateRpcRequest) {
        EventLoopGroup group = new NioEventLoopGroup();
        Bootstrap bootstrap = new Bootstrap()
                .group(group)
                //该参数的作用就是禁止使用Nagle算法,使用于小数据即时传输
                .option(ChannelOption.TCP_NODELAY, true)
                .channel(NioSocketChannel.class)
                .handler(new NettyClientInitializer());

        try {
            ChannelFuture future = bootstrap.connect("127.0.0.1", 8090).sync();
            log.info("客户端成功....");
            //发送 数据体
            future.channel().writeAndFlush(calculateRpcRequest);
            // 等待连接被关闭
            future.channel().closeFuture().sync();
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            group.shutdownGracefully();
        }
    }
}

新建NettyClientHandler

@Slf4j
public class NettyClientHandler extends ChannelInboundHandlerAdapter {
    @Autowired
    ResultFactory resultFactory;

    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
        log.info("客户端Active .....");
    }

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

        log.info("客户端收到消息: {}", msg.toString());
        if (msg instanceof AddRequest) {
            AddRequest addRequest = (AddRequest) msg;
            ResultFactory.put(addRequest.getTimeId().toString(), addRequest.getResult());
        }


    }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        cause.printStackTrace();
        ctx.close();
    }
}

新建NettyClientInitializer

public class NettyClientInitializer extends ChannelInitializer<SocketChannel> {
    @Override
    protected void initChannel(SocketChannel socketChannel) throws Exception {
        socketChannel.pipeline().addLast("decoder",
                new ObjectDecoder(ClassResolvers.cacheDisabled(this.getClass().getClassLoader())));
        socketChannel.pipeline().addLast("encoder", new ObjectEncoder());
        socketChannel.pipeline().addLast(new NettyClientHandler());

    }
}

在 CalculatorRemoteImpl中添加方法

    //启动netty客户端
    public void getMethodAdd(int a, int b) {
        NettyClient nettyClient = new NettyClient();
        CalculateRpcRequest calculateRpcRequest = generateRequest(a, b);
        nettyClient.startAdd(calculateRpcRequest);
    }

将add方法里的内容注解,调用上面的方法

 @Override
    public int add(int a, int b) {
        this.getMethodAdd(a, b);
        return 0;
``}`



接下来是nettyserver部分

新建 NettyServer,这是服务端启动时的代码

@Slf4j
public class NettyServer {

@Autowired
Calculator calculator;

public void start(InetSocketAddress socketAddress) {
    //new 一个主线程组
    EventLoopGroup bossGroup = new NioEventLoopGroup(1);
    //new 一个工作线程组
    EventLoopGroup workGroup = new NioEventLoopGroup(200);
    ServerBootstrap bootstrap = new ServerBootstrap()
            .group(bossGroup, workGroup)
            .channel(NioServerSocketChannel.class)
            .childHandler(new ServerChannelInitializer())
            .localAddress(socketAddress)
            //设置队列大小
            .option(ChannelOption.SO_BACKLOG, 1024)
            // 两小时内没有数据的通信时,TCP会自动发送一个活动探测数据报文
            .childOption(ChannelOption.SO_KEEPALIVE, true);
    //绑定端口,开始接收进来的连接
    try {
        ChannelFuture future = bootstrap.bind(socketAddress).sync();
        log.info("服务器启动开始监听端口: {}", socketAddress.getPort());
        // 等待连接被关闭
        future.channel().closeFuture().sync();
    } catch (InterruptedException e) {
        e.printStackTrace();
    } finally {
        //关闭主线程组
        bossGroup.shutdownGracefully();
        //关闭工作线程组
        workGroup.shutdownGracefully();
    }
}

}


新建NettyServerHandler


@Slf4j
public class NettyServerHandler extends ChannelInboundHandlerAdapter {

/**
 * 客户端连接会触发
 */
@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
    log.info("Channel active......");
}

/**
 * 客户端发消息会触发
 */
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
    System.out.println("服务端收到消息:" + msg.toString());
    AddRequest addRequest = this.getMethodAdd(msg);

    ctx.write(addRequest);
    ctx.flush();
}

/**
 * 发生异常触发
 */
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
    cause.printStackTrace();
    ctx.close();
}


//用netty接收的数据来实现add
public AddRequest getMethodAdd(Object object) {
    // 调用服务
    int result = 0;
    if (object instanceof CalculateRpcRequest) {
        CalculateRpcRequest calculateRpcRequest = (CalculateRpcRequest) object;
        if ("add".equals(calculateRpcRequest.getMethod())) {
            // 返回结果
            result = new CalculatorImpl().add(calculateRpcRequest.getA(), calculateRpcRequest.getB());
            AddRequest addRequest = new AddRequest();
            addRequest.setResult(result);
            addRequest.setTimeId(calculateRpcRequest.getTimeId());
            return addRequest;
        } else {
            throw new UnsupportedOperationException();
        }
    }
    return null;
}

}



新建ServerChannelInitializer

public class ServerChannelInitializer extends ChannelInitializer {

@Override
protected void initChannel(SocketChannel socketChannel) throws Exception {
    //添加编解码

// socketChannel.pipeline().addLast(“decoder”, new StringDecoder(CharsetUtil.UTF_8));
// socketChannel.pipeline().addLast(“encoder”, new StringEncoder(CharsetUtil.UTF_8));
socketChannel.pipeline().addLast(“decoder”,
new ObjectDecoder(ClassResolvers.cacheDisabled(this.getClass().getClassLoader())));
socketChannel.pipeline().addLast(“encoder”, new ObjectEncoder());
socketChannel.pipeline().addLast(new NettyServerHandler());
}
}

在ProviderApp更改如下代码

    public static void main(String[] args) throws IOException {
//        new ProviderApp().run();
        new ProviderApp().runNetty();
    }
    //通过netty通信
    private void runNetty() throws IOException {
        //启动服务端
        NettyServer nettyServer = new NettyServer();
        //监听9090端口
        nettyServer.start(new InetSocketAddress("127.0.0.1", 8090));
    }

以上,启动时先运行ProviderApp,在运行ComsumerApp
可以看到结果:
在这里插入图片描述
传输成功

其中一些值得注意的地方:
在这里插入图片描述
进入ServerChannelInitializer方法内部,这里定义了如何处理netty通信的数据.
我们使用的是类处理数据的方式。

在这里插入图片描述

ComsumerApp 启动后,将calculateRpcRequest类发送给ProviderApp

在这里插入图片描述

provider会接收到该数据,在NettyServerHandler的channelRead()中处理并返回

在这里插入图片描述

最后comsumer的NettyClientHandler中会处理返回的数据

在这里插入图片描述
这里其实有一个小问题,细心的朋友其实应该能发现。
channelRead() 方法没有返回值,这就是说我们从服务端接收的结果没有办法直接使用。

netty是异步框架,客户端发出数据后,可以做其他的事情,而不必一直等待服务端的返回数据。
所以我们要把每个发起的请求都要设定id,服务端处理好后,将结果和id一并返回。
我们通过id,就可以知道返回的结果是哪一个请求的。

up处理的方式是将其存入一个客户端的map中,
在这里插入图片描述
客户端要使用时,可以直接调用。

git地址

https://github.com/bobly2/rpc-0.2
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值