spring项目实时同步到服务器,客户端(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() {

@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 {

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 {

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();

}

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 ...

随机推荐

利用UICollectionViewFlowLayout的隐式动画实现UICollectionView的layout的动画调整(外加放大指定cell效果)

前几天在gitHub看到个不错的效果,就是DaiExpandCollectionView,效果如图:   所以赶紧下下来源码看看他怎么实现的,打开源码看了半天,发现他没写什么关于动画的代码啊... 经 ...

C++中的一些小知识

判断字符是否为数字 在C/C++中有isdigit()来判断一个字符是否为数字 原型:int isdigit(char c); 用法:#include (C语言):#i ...

完全自制的五子棋人机对战游戏(VC++实现)

五子棋工作文档 1说明: 这个程序在创建初期的时候是有一个写的比较乱的文档的,但是很可惜回学校的时候没有带回来……所以现在赶紧整理一下,不然再过一段时间就忘干净了. 最初这个程序是受老同学所托做的,一 ...

Hbase常用操作

下面我们看看HBase Shell的一些基本操作命令,我列出了几个常用的HBase Shell命令,如 名称 命令表达式 创建表 create '表名称', '列名称1','列名称2','列名称N' ...

OpenShift中的持续交付

上一文中讲述了如何在AWS下搭建OpenShift集群.这篇文章将目光转向如何在OpenShift中实现CI/CD以及产品环境的部署. 持续交付 如果要打造一个持续交付的流水线,首先要考虑多环境的问题 ...

dulicate symbol for architecture i386 或者其他什么CPU架构 比如i386

昨天群里有个哥们遇到和么一个问题 , 错误的大概意思呢,就是 重复定义了  一个名字. 解决办法,只能修改名字啊. 而且,错误信息 也很明确的 支出了 重复定义的类文件名字PassGuardViewC ...

2186 Popular Cows

Popular Cows Time Limit: 2000MS   Memory Limit: 65536K Total Submissions: 41771   Accepted: 16955 De ...

delphi压缩与解压_不需要特别的控件

unit unzip; interface uses Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms ...

Vue音乐项目笔记(三)

1. 音乐播放前进后退的实现   https://blog.csdn.net/weixin_40814356/article/details/80379606 2. 音乐进度条实现(单独一个组件) h ...

redis list 查询、下标查询、删除、裁剪、压入弹出、队列实现

查询  lrange list 0 1 // 注意0和1之间是空格:这个命令和pop命令不一样,不会删除里面的值lrange list 0 -1 // 所有的 下标查询 lpush person zs ...

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值