后面考虑通过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服务端,获得响应后返回页面(同步响应)的更多相关文章

  1. Netty学习4—NIO服务端报错:远程主机强迫关闭了一个现有的连接

    1 发现问题 NIO编程中服务端会出现报错 Exception in thread "main" java.io.IOException: 远程主机强迫关闭了一个现有的连接. at ...

  2. 基于NIO的同步非阻塞编程完整案例,客户端发送请求,服务端获取数据并返回给客户端数据,客户端获取返回数据

    这块还是挺复杂的,挺难理解,但是多练几遍,多看看研究研究其实也就那样,就是一个Selector轮询的过程,这里想要双向通信,客户端和服务端都需要一个Selector,并一直轮询, 直接贴代码: Ser ...

  3. Netty源码解析---服务端启动

    Netty源码解析---服务端启动 一个简单的服务端代码: public class SimpleServer { public static void main(String[] args) { N ...

  4. NIO服务端主要创建过程

    NIO服务端主要创建过程:   步骤一:打开ServerSocketChannel,用于监听客户端的连接,它是所有客户端连接的副管道,示例代码如下:      ServerSocketChannel ...

  5. 如何通过JavaScript构建Asp.net服务端控件

    摘要 虽然ASP.NET的服务器控件一直被大家所诟病,但是用户控件(ACSX)在某些场景下还是非常有用的. 在一些极特珠的情况下,我们会使用JavaScript动态的构建页面中的控件,但假设遇到了我要 ...

  6. 一次http请求,谁会先断开TCP连接?什么情况下客户端先断,什么情况下服务端先断?

    我们有2台内部http服务(nginx): 201:这台服务器部署的服务是account.api.91160.com,这个服务是供前端页面调用: 202:这台服务器部署的服务是hdbs.api.911 ...

  7. Query通过Ajax向PHP服务端发送请求并返回JSON数据

    Query通过Ajax向PHP服务端发送请求并返回JSON数据 服务端PHP读取MYSQL数据,并转换成JSON数据,传递给前端Javascript,并操作JSON数据.本文将通过实例演示了jQuer ...

  8. Netty(6)源码-服务端与客户端创建

    原生的NIO类图使用有诸多不便,Netty向用户屏蔽了细节,在与用户交界处做了封装. 一.服务端创建时序图 步骤一:创建ServerBootstrap实例 ServerBootstrap是Netty服 ...

  9. NIO服务端和客户端通信demo

    代码转自 https://www.jianshu.com/p/a9d030fec081 服务端: package nio; import java.io.IOException; import jav ...

随机推荐

  1. JSP读取Oracle数据库里的图片Blob字段并显示在页面上

    1.java代码: /** * 打印模板获取电子签名 * @param request * @param resp * @param id * @return * @throws Exception ...

  2. Python Day22

    Django之Form组件 Django的Form主要具有一下几大功能: 生成HTML标签 验证用户数据(显示错误信息) HTML Form提交保留上次提交数据 初始化页面显示内容 1.创建Form类 ...

  3. 树状数组 洛谷P3616 富金森林公园

    P3616 富金森林公园 题目描述 博艾的富金森林公园里有一个长长的富金山脉,山脉是由一块块巨石并列构成的,编号从1到N.每一个巨石有一个海拔高度.而这个山脉又在一个盆地中,盆地里可能会积水,积水也有 ...

  4. Python小世界:项目虚拟环境配置的N种方法

    前言 和其他大多数现代编程语言一样,Python对包和 模块的下载.存储以及管理有其自己的一套方法.但是当我们同时开发多个项目工程的时候,不同的项目会将第三方的包存放在相同的路径下.这就意味着,如果有 ...

  5. 实现网上大神的asp.net mvc + ef +easyui

    大神开源博客: http://www.cnblogs.com/ymnets/p/3424309.html 系统更换UI:本人喜欢基于bootstrap的adminlteUI,所以后面会将UI更换为ad ...

  6. web性能优化--缓存

     什么是缓存? 缓存(Web缓存)是指代理服务器和客户端本地磁盘保存的资源副本.当 web 缓存发现请求的资源已经被存储,它会拦截请求,返回该资源的拷贝,而不会去源服务器重新下载. 缓存大致可以分为私 ...

  7. 为什么要问Servlet的初始化时间

    Servlet的init方法到底是在什么时候调用的? j2ee specification和java doc中有以下说明 如果load-on-startup设置为>=0, 部署的时候就会调用.  ...

  8. excel单元格内容合并

    这几天在整理数据,有时候数据都在表格的不同单元格中,想把两格内容合并为一格,于是验证了两种方法 方法一: (1)在B1输入公式=A1&B1 (2)做完第一步后,选中B1后,鼠标移到单元格右下出 ...

  9. 转 xshell 图像化展示

    http://www.cnblogs.com/kellyseeme/p/7965830.html 限制: 无法显示通过堡垒机登录的机器的图形接界面. 只能显示直接登录的服务到期的图像化界面 Xshel ...

  10. quartz任务调度初次使用记录

    近期公司开发的数据交换系统嵌入了quartz任务调度功能,大概了解了任务调度的整个流程,项目中需要用到它来进行定时任务操作,对数据定时检查以及及时交换. Quartz是OpenSymphony开源组织 ...