手写RPC框架(六)整合Netty

Netty简介:

Netty是一个基于NIO的,提供异步,事件驱动的网络应用工具,具有高性能高可靠性等特点。

使用传统的Socket来进行网络通信,服务端每一个连接都要新建一个线程,清楚处理完成后通过输出流返回给客户端。而Netty通过NIO的方式,服务端实现为一个请求一个线程,客户端发送的连接请求会注册到多路复用器上,多路复用器轮询到连接有I/O请求时才会启动一个线程进行处理。

这次我们通过Netty来实现网络通信,替代Socket,提高框架性能。

  1. 引入Netty

    <!-- https://mvnrepository.com/artifact/io.netty/netty-all -->
    <dependency>
    <groupId>io.netty</groupId>
    <artifactId>netty-all</artifactId>
    <version>4.1.73.Final</version>
    </dependency>
  2. netty服务端

    public class NettyServer{
    private static final Logger logger = LoggerFactory.getLogger(NettyServer.class);
    private String serverAddress; //启动地址
    private int serverPort; //启动端口 private EventLoopGroup boss = null;
    private EventLoopGroup worker = null; public NettyServer(String serverAddress, int serverPort) {
    this.serverAddress = serverAddress;
    this.serverPort = serverPort;
    } public void startNettyServer() throws Exception {
    //netty调度模块,负责接收请求
    NioEventLoopGroup bossGroup = new NioEventLoopGroup();
    //netty调度模块,负责处理请求
    NioEventLoopGroup workGroup = new NioEventLoopGroup();
    //启动类
    ServerBootstrap bootstrap = new ServerBootstrap();
    bootstrap.group(bossGroup,workGroup);
    bootstrap.channel(NioServerSocketChannel.class);
    bootstrap.childHandler(new ChannelInitializer<SocketChannel>() {
    @Override
    protected void initChannel(SocketChannel ch) throws Exception {
    //传输数据的channel
    ChannelPipeline pipeline = ch.pipeline();
    //解码器
    pipeline.addLast(new StringDecoder());
    //编码器
    pipeline.addLast(new StringEncoder());
    //业务逻辑
    pipeline.addLast(new RpcServerHandler());
    }
    }); try {
    //端口绑定
    ChannelFuture sync = bootstrap.bind(serverAddress, serverPort).sync();
    sync.channel().closeFuture().sync();
    } catch (InterruptedException e) {
    e.printStackTrace();
    }finally {
    bossGroup.shutdownGracefully();
    workGroup.shutdownGracefully();
    }
    } @PreDestroy
    public void destory() throws InterruptedException {
    boss.shutdownGracefully().sync();
    worker.shutdownGracefully().sync();
    logger.info("关闭Netty");
    }
    }

    在这里通过startServer方法会启动netty服务端,当有请求时,会进入RpcServerHandler()方法中进行处理。

    @ChannelHandler.Sharable
    public class RpcServerHandler extends SimpleChannelInboundHandler<String> { @Override
    protected void channelRead0(ChannelHandlerContext ctx, String msg) {
    //msg为接收到的请求信息
    RpcResponse response = new RpcResponse();
    //将请求信息解码
    RpcRegisterEntity rpcRegisterEntity=JSON.parseObject(msg,RpcRegisterEntity.class);
    //通过反射得到远程调用的类并执行该方法
    Object result = invoke(rpcRegisterEntity);
    try {
    //返回体
    response.setResult(result);
    } catch (Exception exception) {
    exception.printStackTrace();
    response.setException(exception);
    }
    //写入返回数据
    ctx.writeAndFlush(JSON.toJSONString(response));
    } private Object invoke(RpcRegisterEntity entity) {
    try {
    //接口名
    String interfaceName = entity.getServiceImplClassFullName();
    // String implClassName = RegisterCenter.getProviderData(interfaceName);
    //类名
    String implClassName = entity.getServiceImplClassFullName();
    Class<?> clazz = Class.forName(implClassName);
    String methodName = entity.getMethodName();
    Class<?>[] parameterTypes = entity.getParameterTypes();
    Object[] parameters = entity.getParameters();
    Method method = clazz.getMethod(methodName, parameterTypes);
    //通过反射得到结果
    return method.invoke(clazz.newInstance(), parameters);
    } catch (ClassNotFoundException | NoSuchMethodException | InvocationTargetException | IllegalAccessException | InstantiationException e) {
    e.printStackTrace();
    return e;
    }
    }
    //当Channel处理于活动状态时被调用
    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
    // System.out.println(ctx.channel().remoteAddress().toString());
    super.channelActive(ctx);
    } @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
    // System.out.println(JSON.toJSONString(cause));
    super.exceptionCaught(ctx, cause);
    }
    }
  3. netty消费端

    public class RpcClient{
    private static final Logger logger = LoggerFactory.getLogger(RpcClient.class);
    private EventLoopGroup group; private Channel channel; private String ip; private int port; private RpcConsumerHandler rpcConsumerHandler=new RpcConsumerHandler(); private ExecutorService executorService = Executors.newCachedThreadPool(); public RpcClient(String ip, int port) {
    this.ip = ip;
    this.port = port;
    initClient();
    } public void initClient() {
    try {
    //1.创建线程组
    group = new NioEventLoopGroup();
    //2.创建启动助手
    Bootstrap bootstrap = new Bootstrap();
    //3.设置参数
    bootstrap.group(group)
    //传输数据用的channel
    .channel(NioSocketChannel.class)
    .option(ChannelOption.SO_KEEPALIVE, Boolean.TRUE)
    .option(ChannelOption.CONNECT_TIMEOUT_MILLIS, 3000)
    .handler(new ChannelInitializer<SocketChannel>() {
    @Override
    protected void initChannel(SocketChannel channel) throws Exception {
    ChannelPipeline pipeline = channel.pipeline();
    pipeline.addLast(new StringEncoder());
    pipeline.addLast(new StringDecoder());
    //添加客户端处理类
    pipeline.addLast(rpcConsumerHandler);
    }
    });
    //4.连接Netty服务端
    connect(bootstrap, ip, port, 5);
    } catch (Exception exception) {
    exception.printStackTrace();
    if (channel != null) {
    channel.close();
    }
    if (group != null) {
    group.shutdownGracefully();
    }
    }
    } private void connect(Bootstrap bootstrap, String host, int port, int retry) {
    ChannelFuture channelFuture = bootstrap.connect(host, port).addListener(future -> {
    if (future.isSuccess()) {
    logger.info("连接服务端成功");
    } else if (retry == 0) {
    logger.error("重试次数已用完,放弃连接");
    } else {
    //第几次重连:
    int order = (5 - retry) + 1;
    //本次重连的间隔
    int delay = 1 << order;
    logger.error("{} : 连接失败,第 {} 重连....", new Date(), order);
    bootstrap.config().group().schedule(() -> connect(bootstrap, host, port, retry - 1), delay, TimeUnit.SECONDS);
    }
    });
    channel = channelFuture.channel();
    }
    /**
    * 提供给调用者主动关闭资源的方法
    */
    public void close() {
    if (channel != null) {
    channel.close();
    }
    if (group != null) {
    group.shutdownGracefully();
    }
    } /**
    * 提供消息发送的方法
    */
    public Object send(String msg) throws ExecutionException, InterruptedException {
    rpcConsumerHandler.setRequestMsg(msg);
    Future submit = executorService.submit(rpcConsumerHandler);
    return submit.get();
    } public void destroy() throws Exception {
    if (channel != null) {
    channel.close();
    }
    if (group != null) {
    group.shutdownGracefully();
    }
    } public String getIp() {
    return ip;
    } public void setIp(String ip) {
    this.ip = ip;
    } public int getPort() {
    return port;
    } public void setPort(int port) {
    this.port = port;
    }
    }

    当创建好客户端时,发送请求时数据会交由rpcConsumerHandler处理,

    public class RpcConsumerHandler extends SimpleChannelInboundHandler<String> implements Callable {
    ChannelHandlerContext context;
    //发送的消息
    String requestMsg; //服务端返回的消息
    String responseMsg; public void setRequestMsg(String requestMsg) {
    this.requestMsg = requestMsg;
    }
    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
    context = ctx;
    }
    //接收由服务端返回的数据
    @Override
    protected synchronized void channelRead0(ChannelHandlerContext channelHandlerContext, String msg) throws Exception {
    System.out.println("客户端结果:"+msg);
    responseMsg = msg;
    //唤醒等待的线程
    notify();
    }
    //发送数据
    @Override
    public synchronized Object call() throws Exception {
    //消息发送
    context.writeAndFlush(requestMsg);
    //线程等待
    wait();
    return responseMsg;
    }
    }
  4. 调用

    //新建连接
    RpcClient rpcClient = getClient(rpcRegisterEntity.getHost(), rpcRegisterEntity.getPort());
    //发送数据
    Object responseMsg = rpcClient.send(JSON.toJSONString(rpcRegisterEntity));
    //解析返回的数据
    RpcResponse rpcResponse = JSON.parseObject(responseMsg.toString(), RpcResponse.class);

手写RPC框架(六)整合Netty的更多相关文章

  1. 手写RPC框架指北另送贴心注释代码一套

    Angular8正式发布了,Java13再过几个月也要发布了,技术迭代这么快,框架的复杂度越来越大,但是原理是基本不变的.所以沉下心看清代码本质很重要,这次给大家带来的是手写RPC框架. 完整代码以及 ...

  2. 看了这篇你就会手写RPC框架了

    一.学习本文你能学到什么? RPC的概念及运作流程 RPC协议及RPC框架的概念 Netty的基本使用 Java序列化及反序列化技术 Zookeeper的基本使用(注册中心) 自定义注解实现特殊业务逻 ...

  3. 基于netty手写RPC框架

    代码目录结构 rpc-common存放公共类 rpc-interface为rpc调用方需要调用的接口 rpc-register提供服务的注册与发现 rpc-client为rpc调用方底层实现 rpc- ...

  4. 手写RPC框架(netty+zookeeper)

    RPC是什么?远程过程调用,过程就是业务处理.计算任务,像调用本地方法一样调用远程的过程. RMI和RPC的区别是什么?RMI是远程方法调用,是oop领域中RPC的一种实现,我们熟悉的restfull ...

  5. 手写RPC框架

    https://www.bilibili.com/video/av23508597?from=search&seid=6870947260580707913 https://github.co ...

  6. 手写MQ框架(四)-使用netty改造梳理

    一.背景 书接上文手写MQ框架(三)-客户端实现,前面通过web的形式实现了mq的服务端和客户端,现在计划使用netty来改造一下.前段时间学习了一下netty的使用(https://www.w3cs ...

  7. java 从零开始手写 RPC (04) -序列化

    序列化 java 从零开始手写 RPC (01) 基于 socket 实现 java 从零开始手写 RPC (02)-netty4 实现客户端和服务端 java 从零开始手写 RPC (03) 如何实 ...

  8. java 从零开始手写 RPC (05) reflect 反射实现通用调用之服务端

    通用调用 java 从零开始手写 RPC (01) 基于 socket 实现 java 从零开始手写 RPC (02)-netty4 实现客户端和服务端 java 从零开始手写 RPC (03) 如何 ...

  9. 手写MQ框架(一)-准备启程

    一.背景 很久以前写了DAO框架和MVC框架,前段时间又重写了DAO框架-GDAO(手写DAO框架(一)-从“1”开始,源码:https://github.com/shuimutong/gdao.gi ...

随机推荐

  1. SYCOJ798Biorhythms

    https://oj.shiyancang.cn/Problem/798.html #include<bits/stdc++.h> using namespace std; typedef ...

  2. Easticsearch概述(API使用)二

    Rest简介 一种软件架构风格,而不是标准,只是提供了一组设计原则和约束条件.它主要用于客户端和服务端互类的软件.基于这个风格设计的软件可以更简洁,更有层次,更易于实现缓存等机制 Rest的操作分为以 ...

  3. 【Java】final

    final final可以用来修饰的结构:类.方法.变量 final 用来修饰一个类:此类不能被其他类所继承. 比如:String类.System类.StringBuffer类 final 用来修饰方 ...

  4. 18个示例详解 Spring 事务传播机制(附测试源码)

    什么是事务传播机制 事务的传播机制,顾名思义就是多个事务方法之间调用,事务如何在这些方法之间传播. 举个例子,方法 A 是一个事务的方法,方法 A 执行的时候调用了方法 B,此时方法 B 有无事务以及 ...

  5. rocketmq实现延迟队列精确到秒级实现(总结编)

    前言篇: 为了节约成本,决定通过自研来改造rocketmq,添加任意时间延迟的延时队列,开源版本的rocketmq只有支持18个等级的延迟时间, 其实对于大部分的功能是够用了的,但是以前的项目,全部都 ...

  6. Java Selenide 介绍&使用

    目录 Selenide 介绍 官方快速入门 元素定位 元素操作 浏览器操作 断言 常用配置 Selenide 和 Webdriver 对比 Selenide 介绍 Selenide github Se ...

  7. linux正则表达式(全面解析)

    目录 一:linux正则表达式介绍 二:普通正则表达式 三:扩展正则 一:linux正则表达式介绍 1.正则表达式的分类(grep) 1.普通正则表达式 2.扩展正则表达式 二:普通正则表达式 ^ : ...

  8. 『无为则无心』Python函数 — 40、Python自定义异常

    目录 1.使用 raise 语句来抛出异常 (1)抛出异常类 (2)抛出异常类的实例 2.自定义异常类 (1)简单实现 (2)完整实现 在Python中,抛出自定义异常的语法为 raise 异常类对象 ...

  9. Docker常用命令速查

    docker pull ${CONTAINER NAME} #拉取镜像 docker images #查看本地所有镜像 docker ps #查看所有正在运行的容器,加-q返回id docker ps ...

  10. dfs时间复杂度分析

    前言 之前一直想不明白dfs的时间复杂度是怎么算的,前几天想了下大概想明白了,现在记录一下. 存图方式都是链式前向星或邻接矩阵.主要通过几道经典题目来阐述dfs时间复杂度的计算方法. $n$是图中结点 ...