客户端(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 ...
随机推荐
- ios一个自定义的下拉多选菜单
前段时间项目刚好要做到条件筛选菜单,正好找到一些别人写的,结合自己实际需求进行优化修改,一个实用的多条件筛选菜单,根据其他的下拉进行一些改进. 点击后返回点击文字显示 github地址:https:/ ...
- vue使用webpack压缩后体积过大要怎么优化
vue使用webPack压缩后存储过大,怎么优化 在生产环境去除developtool选项 在webpack.config.js中设置的developtool选项,仅适用于开发环境,这样会造成打包成的 ...
- 基于rabbitMQ 消息延时队列方案 模拟电商超时未支付订单处理场景
前言 传统处理超时订单 采取定时任务轮训数据库订单,并且批量处理.其弊端也是显而易见的:对服务器.数据库性会有很大的要求,并且当处理大量订单起来会很力不从心,而且实时性也不是特别好 当然传统的手法还可 ...
- poj1082 Calendar Game (博弈)
题意是:Adam和Eve两人做游戏,开始给出一个日期,截止日期是2011.11.4,游戏规则如下: 每个人只能将天数增加一天或者将月份增加一天.如果下个月没有这一天,那么只能增加天数. 游戏胜利定义为 ...
- 3 - Selenium元素定位和操作
3.1定位 <button id="gbqfba" aria-label="Google Search" name="btnK" cl ...
- Comparing Two High-Performance I/O Design Patterns--reference
by Alexander Libman with Vladimir GilbourdNovember 25, 2005 Summary This article investigates and co ...
- 搭建Node.js Redis开发环境
创建项目 初始化为node项目 $npm init 安装redis 安装@types/node, @types/redis, typescript 初始化TypeScript 配置ts ...
- 上机练习2 生成计算机ID
using System;using System.Collections.Generic;using System.Linq;using System.Text;using System.Threa ...
- 从零开始的全栈工程师——js篇2.16
js操作css样式 div.style.width=“200px” 在div标签内我们添加了一个style属性 并设定了width值 这种写法会给标签带来了大量的style属性 跟实际项目是不符的 我 ...
- cf600E. Lomsat gelral(dsu on tree)
题意 题目链接 给出一个树,求出每个节点的子树中出现次数最多的颜色的编号和 Sol dsu on tree的裸题. 一会儿好好总结总结qwq #include<bits/stdc++.h> ...