热门标签 | HotTags
当前位置:  开发笔记 > 前端 > 正文

SpringBoot使用Netty实现远程调用的示例

这篇文章主要介绍了SpringBoot使用Netty实现远程调用的示例,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧

前言

众所周知我们在进行网络连接的时候,建立套接字连接是一个非常消耗性能的事情,特别是在分布式的情况下,用线程池去保持多个客户端连接,是一种非常消耗线程的行为。那么我们该通过什么技术去解决上述的问题呢,那么就不得不提一个网络连接的利器——Netty.

正文 Netty

Netty是一个NIO客户端服务器框架:

  • 它可快速轻松地开发网络应用程序,例如协议服务器和客户端。
  • 它极大地简化和简化了网络编程,例如TCPUDP套接字服务器。

NIO是一种非阻塞IO ,它具有以下的特点

  • 单线程可以连接多个客户端。
  • 选择器可以实现但线程管理多个Channel,新建的通道都要向选择器注册。
  • 一个SelectionKey键表示了一个特定的通道对象和一个特定的选择器对象之间的注册关系。
  • selector进行select()操作可能会产生阻塞,但是可以设置阻塞时间,并且可以用wakeup()唤醒selector,所以NIO是非阻塞IO

Netty模型selector模式

它相对普通NIO的在性能上有了提升,采用了:

  • NIO采用多线程的方式可以同时使用多个selector
  • 通过绑定多个端口的方式,使得一个selector可以同时注册多个ServerSocketServer
  • 单个线程下只能有一个selector,用来实现Channel的匹配及复用

在这里插入图片描述

半包问题

TCP/IP在发送消息的时候,可能会拆包,这就导致接收端无法知道什么时候收到的数据是一个完整的数据。在传统的BIO中在读取不到数据时会发生阻塞,但是NIO不会。为了解决NIO的半包问题,NettySelector模型的基础上,提出了reactor模式,从而解决客户端请求在服务端不完整的问题。

netty模型reactor模式

selector的基础上解决了半包问题。

在这里插入图片描述

上图,简单地可以描述为"boss接活,让work干":manReactor用来接收请求(会与客户端进行握手验证),而subReactor用来处理请求(不与客户端直接连接)。

SpringBoot使用Netty实现远程调用

maven依赖



 org.projectlombok
 lombok
 1.18.2
 true




 io.netty
 netty-all
 4.1.17.Final

服务端部分

NettyServer.java:服务启动监听器

@Slf4j
public class NettyServer {
  public void start() {
    InetSocketAddress socketAddress = new InetSocketAddress("127.0.0.1", 8082);
    //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) {
      log.error("服务器开启失败", e);
    } finally {
      //关闭主线程组
      bossGroup.shutdownGracefully();
      //关闭工作线程组
      workGroup.shutdownGracefully();
    }
  }
}

ServerChannelInitializer.java:netty服务初始化器

/**
* netty服务初始化器
**/
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(new NettyServerHandler());
  }
}

NettyServerHandler.java:netty服务端处理器

/**
* netty服务端处理器
**/
@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 {
    log.info("服务器收到消息: {}", msg.toString());
    ctx.write("你也好哦");
    ctx.flush();
  }


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

RpcServerApp.java:SpringBoot启动类

/**
* 启动类
*
*/
@Slf4j
@SpringBootApplication(exclude = {DataSourceAutoConfiguration.class})
public class RpcServerApp extends SpringBootServletInitializer {
  @Override
  protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
    return application.sources(RpcServerApp.class);
  }

  /**
   * 项目的启动方法
   *
   * @param args
   */
  public static void main(String[] args) {
    SpringApplication.run(RpcServerApp.class, args);
    //开启Netty服务
    NettyServer nettyServer =new NettyServer ();
    nettyServer.start();
    log.info("======服务已经启动========");
  }
}

客户端部分

NettyClientUtil.java:NettyClient工具类

/**
* Netty客户端
**/
@Slf4j
public class NettyClientUtil {

  public static ResponseResult helloNetty(String msg) {
    NettyClientHandler nettyClientHandler = new NettyClientHandler();
    EventLoopGroup group = new NioEventLoopGroup();
    Bootstrap bootstrap = new Bootstrap()
        .group(group)
        //该参数的作用就是禁止使用Nagle算法,使用于小数据即时传输
        .option(ChannelOption.TCP_NODELAY, true)
        .channel(NioSocketChannel.class)
        .handler(new ChannelInitializer() {
          @Override
          protected void initChannel(SocketChannel socketChannel) throws Exception {
            socketChannel.pipeline().addLast("decoder", new StringDecoder());
            socketChannel.pipeline().addLast("encoder", new StringEncoder());
            socketChannel.pipeline().addLast(nettyClientHandler);
          }
        });
    try {
      ChannelFuture future = bootstrap.connect("127.0.0.1", 8082).sync();
      log.info("客户端发送成功....");
      //发送消息
      future.channel().writeAndFlush(msg);
      // 等待连接被关闭
      future.channel().closeFuture().sync();
      return nettyClientHandler.getResponseResult();
    } catch (Exception e) {
      log.error("客户端Netty失败", e);
      throw new BusinessException(CouponTypeEnum.OPERATE_ERROR);
    } finally {
      //以一种优雅的方式进行线程退出
      group.shutdownGracefully();
    }
  }
}

NettyClientHandler.java:客户端处理器

/**
* 客户端处理器
**/
@Slf4j
@Setter
@Getter
public class NettyClientHandler extends ChannelInboundHandlerAdapter {

  private ResponseResult responseResult;

  @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());
    this.respOnseResult= ResponseResult.success(msg.toString(), CouponTypeEnum.OPERATE_SUCCESS.getCouponTypeDesc());
    ctx.close();
  }

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

验证

测试接口

@RestController
@Slf4j
public class UserController {

  @PostMapping("/helloNetty")
  @MethodLogPrint
  public ResponseResult helloNetty(@RequestParam String msg) {
    return NettyClientUtil.helloNetty(msg);
  }
}

访问测试接口

在这里插入图片描述

服务端打印信息

在这里插入图片描述

客户端打印信息

在这里插入图片描述

源码

项目源码可从的我的github中获取:github源码地址

到此这篇关于SpringBoot使用Netty实现远程调用的示例的文章就介绍到这了,更多相关SpringBoot Netty远程调用内容请搜索以前的文章或继续浏览下面的相关文章希望大家以后多多支持!


推荐阅读
  • 本文介绍了解决Netty拆包粘包问题的一种方法——使用特殊结束符。在通讯过程中,客户端和服务器协商定义一个特殊的分隔符号,只要没有发送分隔符号,就代表一条数据没有结束。文章还提供了服务端的示例代码。 ... [详细]
  • [译]技术公司十年经验的职场生涯回顾
    本文是一位在技术公司工作十年的职场人士对自己职业生涯的总结回顾。她的职业规划与众不同,令人深思又有趣。其中涉及到的内容有机器学习、创新创业以及引用了女性主义者在TED演讲中的部分讲义。文章表达了对职业生涯的愿望和希望,认为人类有能力不断改善自己。 ... [详细]
  • 关于我们EMQ是一家全球领先的开源物联网基础设施软件供应商,服务新产业周期的IoT&5G、边缘计算与云计算市场,交付全球领先的开源物联网消息服务器和流处理数据 ... [详细]
  • 解决Cydia数据库错误:could not open file /var/lib/dpkg/status 的方法
    本文介绍了解决iOS系统中Cydia数据库错误的方法。通过使用苹果电脑上的Impactor工具和NewTerm软件,以及ifunbox工具和终端命令,可以解决该问题。具体步骤包括下载所需工具、连接手机到电脑、安装NewTerm、下载ifunbox并注册Dropbox账号、下载并解压lib.zip文件、将lib文件夹拖入Books文件夹中,并将lib文件夹拷贝到/var/目录下。以上方法适用于已经越狱且出现Cydia数据库错误的iPhone手机。 ... [详细]
  • 本文介绍了高校天文共享平台的开发过程中的思考和规划。该平台旨在为高校学生提供天象预报、科普知识、观测活动、图片分享等功能。文章分析了项目的技术栈选择、网站前端布局、业务流程、数据库结构等方面,并总结了项目存在的问题,如前后端未分离、代码混乱等。作者表示希望通过记录和规划,能够理清思路,进一步完善该平台。 ... [详细]
  • SpringBoot整合SpringSecurity+JWT实现单点登录
    SpringBoot整合SpringSecurity+JWT实现单点登录,Go语言社区,Golang程序员人脉社 ... [详细]
  • Netty源代码分析服务器端启动ServerBootstrap初始化
    本文主要分析了Netty源代码中服务器端启动的过程,包括ServerBootstrap的初始化和相关参数的设置。通过分析NioEventLoopGroup、NioServerSocketChannel、ChannelOption.SO_BACKLOG等关键组件和选项的作用,深入理解Netty服务器端的启动过程。同时,还介绍了LoggingHandler的作用和使用方法,帮助读者更好地理解Netty源代码。 ... [详细]
  • 本文总结了初学者在使用dubbo设计架构过程中遇到的问题,并提供了相应的解决方法。问题包括传输字节流限制、分布式事务、序列化、多点部署、zk端口冲突、服务失败请求3次机制以及启动时检查。通过解决这些问题,初学者能够更好地理解和应用dubbo设计架构。 ... [详细]
  • 云原生应用最佳开发实践之十二原则(12factor)
    目录简介一、基准代码二、依赖三、配置四、后端配置五、构建、发布、运行六、进程七、端口绑定八、并发九、易处理十、开发与线上环境等价十一、日志十二、进程管理当 ... [详细]
  • 像跟踪分布式服务调用那样跟踪Go函数调用链 | Gopher Daily (2020.12.07) ʕ◔ϖ◔ʔ
    每日一谚:“Acacheisjustamemoryleakyouhaven’tmetyet.”—Mr.RogersGo技术专栏“改善Go语⾔编程质量的50个有效实践” ... [详细]
  • [翻译]微服务设计模式5. 服务发现服务端服务发现
    服务之间需要互相调用,在单体架构中,服务之间的互相调用直接通过编程语言层面的方法调用就搞定了。在传统的分布式应用的部署中,服务地 ... [详细]
  • UDP千兆以太网FPGA_verilog实现(四、代码前期准备UDP和IP协议构建)
    UDP:userDatagramprotocol用户数据报协议无连接的传输层协议,提供面向事务的简单不可靠信息传送服务,IETFRFC76 ... [详细]
  • 媒介本文的前身是源自github上的项目awesome-github-vue,但由于该项目上次更新时候为2017年6月12日,许多内容早已逾期或是许多近期优异组件未被收录,所以小肆 ... [详细]
  • 一面自我介绍对象相等的判断,equals方法实现。可以简单描述挫折,并说明自己如何克服,最终有哪些收获。职业规划表明自己决心,首先自己不准备继续求学了,必须招工作了。希望去哪 ... [详细]
  • 域名解析系统DNS
    文章目录前言一、域名系统概述二、因特网的域名结构三、域名服务器1.根域名服务器2.顶级域名服务器(TLD,top-leveldomain)3.权威(Authoritative)域名 ... [详细]
author-avatar
射手座的双子55
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有