SpringBoot使⽤Netty实现远程调⽤的⽰例
前⾔
众所周知我们在进⾏⽹络连接的时候,建⽴套接字连接是⼀个⾮常消耗性能的事情,特别是在分布式的情况下,⽤线程池去保持多个客户端连接,是⼀种⾮常消耗线程的⾏为。那么我们该通过什么技术去解决上述的问题呢,那么就不得不提⼀个⽹络连接的利器——Netty.
正⽂ Netty
Netty是⼀个NIO客户端服务器框架:
它可快速轻松地开发⽹络应⽤程序,例如协议服务器和客户端。
它极⼤地简化和简化了⽹络编程,例如TCP和UDP套接字服务器。
NIO是⼀种⾮阻塞IO,它具有以下的特点
单线程可以连接多个客户端。
选择器可以实现但线程管理多个Channel,新建的通道都要向选择器注册。
⼀个SelectionKey键表⽰了⼀个特定的通道对象和⼀个特定的选择器对象之间的注册关系。
selector进⾏select()操作可能会产⽣阻塞,但是可以设置阻塞时间,并且可以⽤wakeup()唤醒selector,所以NIO是⾮阻塞IO。
Netty模型selector模式
它相对普通NIO的在性能上有了提升,采⽤了:
NIO采⽤多线程的⽅式可以同时使⽤多个selector
通过绑定多个端⼝的⽅式,使得⼀个selector可以同时注册多个ServerSocketServer
单个线程下只能有⼀个selector,⽤来实现Channel的匹配及复⽤
半包问题
TCP/IP在发送消息的时候,可能会拆包,这就导致接收端⽆法知道什么时候收到的数据是⼀个完整的数据。在传统的BIO中在读取不到数据时会发⽣阻塞,但是NIO不会。为了解决NIO的半包问
题,Netty在Selector模型的基础上,提出了reactor模式,从⽽解决客户端请求在服务端不完整的问题。
netty模型reactor模式
在selector的基础上解决了半包问题。
上图,简单地可以描述为"boss接活,让work⼲":manReactor⽤来接收请求(会与客户端进⾏握⼿验证),⽽subReactor⽤来处理请求(不与客户端直接连接)。SpringBoot使⽤Netty实现远程调⽤
maven依赖
<!--lombok-->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.2</version>
<optional>true</optional>
</dependency>
<!--netty-->
<dependency>
<groupId>ioty</groupId>
<artifactId>netty-all</artifactId>
<version>4.1.17.Final</version>
</dependency>
服务端部分
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("服务器启动开始监听端⼝: {}", Port());
future.channel().closeFuture().sync();
} catch (InterruptedException e) {
<("服务器开启失败", e);
} finally {
//关闭主线程组
bossGroup.shutdownGracefully();
/
/关闭⼯作线程组
workGroup.shutdownGracefully();
}
}
}
ServerChannelInitializer.java:netty服务初始化器
/**
* netty服务初始化器
**/
public class ServerChannelInitializer extends ChannelInitializer<SocketChannel> {
@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("");
}
/**
* 客户端发消息会触发
*/
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
log.info("服务器收到消息: {}", String());
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<SocketChannel>() {
@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 = t("127.0.0.1", 8082).sync();
log.info("客户端发送成功....");
//发送消息
future.channel().writeAndFlush(msg);
// 等待连接被关闭
future.channel().closeFuture().sync();
ResponseResult();
} catch (Exception e) {
<("客户端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("客户端收到消息: {}", String());
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
cause.printStackTrace();
ctx.close();
}
}
验证
测试接⼝
@RestController
reactor线程模型的特点@Slf4j
public class UserController {
@PostMapping("/helloNetty")
@MethodLogPrint
public ResponseResult helloNetty(@RequestParam String msg) {
return NettyClientUtil.helloNetty(msg);
}
}
访问测试接⼝
服务端打印信息
客户端打印信息
源码
项⽬源码可从的我的github中获取:
到此这篇关于SpringBoot使⽤Netty实现远程调⽤的⽰例的⽂章就介绍到这了,更多相关SpringBoot Netty远程调⽤内容请搜索以前的⽂章或继续浏览下⾯的相关⽂章希望⼤家以后多多⽀持!

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。