客户端(springmvc)调用netty构建的nio服务端,获得响应后返回页面(同步响应)
后面考虑通过netty做一个真正意义的简约版RPC框架,今天先尝试通过正常调用逻辑调用netty构建的nio服务端并同步获得返回信息。为后面做铺垫
服务端实现
我们先完成服务端的逻辑,逻辑很简单,把客户端请求的内容加上服务器时间戳一并返回
public void run() throws InterruptedException {
EventLoopGroup bossGroup = new NioEventLoopGroup(1);
EventLoopGroup workGroup = new NioEventLoopGroup();
try{
ServerBootstrap serverBootstrap = new ServerBootstrap();
serverBootstrap.group(bossGroup,workGroup)
.channel(NioServerSocketChannel.class)
.option(ChannelOption.SO_BACKLOG,4096)
.childHandler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel ch) throws Exception {
ch.pipeline().addLast(new LineBasedFrameDecoder(Integer.MAX_VALUE));
ch.pipeline().addLast(new StringDecoder());
ch.pipeline().addLast(new Handler());
}
});
System.out.println("服务启动"+port);
ChannelFuture channelFuture = serverBootstrap.bind(port).sync();
channelFuture.channel().closeFuture().sync();
}finally {
workGroup.shutdownGracefully();
bossGroup.shutdownGracefully();
}
}
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
String data = (String) msg;
System.out.println("服务端接收数据:" + data);
data = data + " now:"+System.currentTimeMillis();
ctx.writeAndFlush(Unpooled.copiedBuffer(data, CharsetUtil.UTF_8));
}
服务端用了LineBasedFrameDecoder,以防止半包读写问题,客户端需要进行配合
客户端实现
这个案例客户端实现有两个难点:
1、客户端方法如何能请求到SimpleChannelInboundHandler,即把需要发送的消息传到handler中
2、如何能等待服务端响应同步返回
第一个问题其实是如何把客户端输入的参数传入handler,所以我们需要把参数以构造函数传参的方式以此传入ChannelInitializer、SimpleChannelInboundHandler,这样handler就可以拿到客户端输入的数据了
下面的Controller里需要提前把channel准备好,如果RPC框架需要考虑通道与服务的关系
@RestController
public class Controller {
static String ip = "127.0.0.1";
static int port = 9876;
static Bootstrap bootstrap;
static NioEventLoopGroup worker;
static {
bootstrap = new Bootstrap();
worker = new NioEventLoopGroup();
bootstrap.group(worker);
bootstrap.channel(NioSocketChannel .class)
.option(ChannelOption.TCP_NODELAY, true)
.remoteAddress(new InetSocketAddress(ip, port));
} @GetMapping("/nio/netty/server")
public String test(String param){
CustomerChannelInitializer customerChannelInitializer = new CustomerChannelInitializer(param);
bootstrap.handler(customerChannelInitializer);
ChannelFuture channelFuture= null;
String msg = "";
try {
channelFuture = bootstrap.connect().sync();
} catch (InterruptedException e) {
e.printStackTrace();
}
return customerChannelInitializer.getResponse();
}
}
public class CustomerChannelInitializer extends ChannelInitializer<SocketChannel> {
private String response;
private CountDownLatch countDownLatch;
private String param;
private ClientChannelHandlerAdapter clientChannelHandlerAdapter;
public CustomerChannelInitializer(String param) {
this.param = param;
}
@Override
protected void initChannel(SocketChannel ch) throws Exception {
countDownLatch = new CountDownLatch(1);
clientChannelHandlerAdapter = new ClientChannelHandlerAdapter(param, this);
ChannelPipeline pipeline = ch.pipeline();
pipeline.addLast(clientChannelHandlerAdapter);
}
public String getResponse() {
try {
countDownLatch.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
return response;
}
public void setResponse(String response) {
this.response = response;
countDownLatch.countDown();
}
}
public class ClientChannelHandlerAdapter extends SimpleChannelInboundHandler<ByteBuf> {
private String param;
private CustomerChannelInitializer customerChannelInitializer;
public ClientChannelHandlerAdapter(String param, CustomerChannelInitializer customerChannelInitializer) {
this.param = param;
this.customerChannelInitializer = customerChannelInitializer;
}
@Override
protected void channelRead0(ChannelHandlerContext ctx, ByteBuf msg) throws Exception {
System.out.println("客户端收到返回数据:" + msg.toString(CharsetUtil.UTF_8));
customerChannelInitializer.setResponse(msg.toString(CharsetUtil.UTF_8));
}
@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
System.out.println("客户端准备发送数据");
ctx.writeAndFlush(Unpooled.copiedBuffer(param + System.getProperty("line.separator"), CharsetUtil.UTF_8));
System.out.println("客户端发送数据完成");
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
System.out.println("发生异常");
cause.printStackTrace();
ctx.close();
}
}
我们来看第二个问题,由于netty是异步的,所以无法等待到服务端响应后调用客户端的channelRead0方法,controller就已经返回了,导致了网页显示的返回结果一直是空
主线程通过CountDownLatch来锁住没有返回结果的线程,直到工作线程获得结果并解锁
@Override
protected void initChannel(SocketChannel ch) throws Exception {
countDownLatch = new CountDownLatch(1);
…… public String getResponse() {
try {
countDownLatch.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
return response;
} public void setResponse(String response) {
this.response = response;
countDownLatch.countDown();
}
客户端(springmvc)调用netty构建的nio服务端,获得响应后返回页面(同步响应)的更多相关文章
- Netty学习4—NIO服务端报错:远程主机强迫关闭了一个现有的连接
1 发现问题 NIO编程中服务端会出现报错 Exception in thread "main" java.io.IOException: 远程主机强迫关闭了一个现有的连接. at ...
- 基于NIO的同步非阻塞编程完整案例,客户端发送请求,服务端获取数据并返回给客户端数据,客户端获取返回数据
这块还是挺复杂的,挺难理解,但是多练几遍,多看看研究研究其实也就那样,就是一个Selector轮询的过程,这里想要双向通信,客户端和服务端都需要一个Selector,并一直轮询, 直接贴代码: Ser ...
- Netty源码解析---服务端启动
Netty源码解析---服务端启动 一个简单的服务端代码: public class SimpleServer { public static void main(String[] args) { N ...
- NIO服务端主要创建过程
NIO服务端主要创建过程: 步骤一:打开ServerSocketChannel,用于监听客户端的连接,它是所有客户端连接的副管道,示例代码如下: ServerSocketChannel ...
- 如何通过JavaScript构建Asp.net服务端控件
摘要 虽然ASP.NET的服务器控件一直被大家所诟病,但是用户控件(ACSX)在某些场景下还是非常有用的. 在一些极特珠的情况下,我们会使用JavaScript动态的构建页面中的控件,但假设遇到了我要 ...
- 一次http请求,谁会先断开TCP连接?什么情况下客户端先断,什么情况下服务端先断?
我们有2台内部http服务(nginx): 201:这台服务器部署的服务是account.api.91160.com,这个服务是供前端页面调用: 202:这台服务器部署的服务是hdbs.api.911 ...
- Query通过Ajax向PHP服务端发送请求并返回JSON数据
Query通过Ajax向PHP服务端发送请求并返回JSON数据 服务端PHP读取MYSQL数据,并转换成JSON数据,传递给前端Javascript,并操作JSON数据.本文将通过实例演示了jQuer ...
- Netty(6)源码-服务端与客户端创建
原生的NIO类图使用有诸多不便,Netty向用户屏蔽了细节,在与用户交界处做了封装. 一.服务端创建时序图 步骤一:创建ServerBootstrap实例 ServerBootstrap是Netty服 ...
- NIO服务端和客户端通信demo
代码转自 https://www.jianshu.com/p/a9d030fec081 服务端: package nio; import java.io.IOException; import jav ...
随机推荐
- bzoj1412: [ZJOI2009]狼和羊的故事(最小割)
传送门 首先,考虑只有狼和羊怎么办.我们把源点向所有羊连边,容$inf$,所有狼向汇点连边,容$inf$,然后羊向周围所有的狼连边,容$1$.那么,只要求一个割就能把狼和羊给分开,求一个最小割就是答案 ...
- ShardingJDBC(一)-转载
Sharding-JDBC:垂直拆分怎么做? 原创: 尹吉欢 猿天地 今天 经过读写分离的优化后,小王可算是轻松了一段时间,读写分离具体的方案请查看这篇文章:Sharding-JDBC:查询量大如何优 ...
- webpack坑系列--安装webpack-cli
综下所述: 还是装3.11.0版本吧 cnpm i webpack@3.11.0 -D 最近在学习webpack过程中,根据官方文档推荐局部安装webpack,执行webpack -h命令出现以下提示 ...
- Ubuntu下rsyslog审计用户bash操作命令、收集、写入MySQL
服务端 2台服务端:10.25.109.64.10.45.18.133 1.rsyslog最新版本安装 sudo add-apt-repository ppa:adiscon/v8-stable su ...
- Luogu P3265 [JLOI2015]装备购买
好吧刚开始不知道自己在写什么,,,后来写了线性方程组,又过了一天一上午终于明白了... 当然题意很显然:求代价最小的极大线性无关组. 那就高斯消元(好吧刚开始我不会用它来解这道题qwq) 第一个循环是 ...
- Luogu P3391 文艺平衡树(Splay or FHQ Treap)
这道题要求区间反转...好东西.. 对于Splay:把l-1旋到根,把r+1旋到根的右儿子,这样r+1的左儿子就是整个区间了,然后对这个区间打个tg 注意要插-Inf和Inf到树里面,防止越界,坐标要 ...
- groovy使用小记
下载groovy的sdk, 解压后设置GROOVY_HOME和PATH变量 http://groovy-lang.org/download.html 使用IDEA创建java项目勾选Groovy组件 ...
- Unity 碰撞的条件
- Python 添加模块
一.方法1: 单文件模块直接把文件拷贝到 $python_dir/Lib 二.方法2: 多文件模块,带setup.py 下载模块包,进行解压,进入模块文件夹,执行:python setup.py in ...
- jquery字符串数组转json字符串 C#json字符串转字符串list
一.jquery字符串数组转json字符串 var str=['1','2','3']; var jsonText= JSON.stringify(str);//把一个对象转换成json字符串 str ...