客户端发送消息并同步获取结果,其实是违背Netty的设计原则的,但是有时候不得不这么做的话,那么建议进行如下的设计:

比如我们的具体用法如下:

  1. NettyRequest request = new NettyRequest();
  2. request.setRequestId(UUID.randomUUID().toString());
  3. request.setClassName(method.getDeclaringClass().getName());
  4. request.setMethodName(method.getName());
  5. request.setParameterTypes(method.getParameterTypes());
  6. request.setParameterValues(args);
  7.  
  8. NettyMessage nettyMessage = new NettyMessage();
  9. nettyMessage.setType(MessageType.SERVICE_REQ.value());
  10. nettyMessage.setBody(request);
  11.  
  12. if (serviceDiscovery != null) {
  13. serverAddress = serviceDiscovery.discover();
  14. }
  15. String[] array = serverAddress.split(":");
  16. String host = array[0];
  17. int port = Integer.parseInt(array[1]);
  18.  
  19. NettyClient client = new NettyClient(host, port);
  20. NettyMessage nettyResponse = client.send(nettyMessage);
  21. if (nettyResponse != null) {
  22. return JSON.toJSONString(nettyResponse.getBody());
  23. } else {
  24. return null;
  25. }

.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, "Courier New", courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }

先来看看NettyClient的写法 和 send方法的写法:

  1. public class NettyClient {
  2.  
  3. /**
  4. * 日志记录
  5. */
  6. private static final Logger logger = LoggerFactory.getLogger(NettyClient.class);
  7.  
  8. /**
  9. * 客户端业务处理handler
  10. */
  11. private ClientHandler clientHandler = new ClientHandler();
  12.  
  13. /**
  14. * 事件池
  15. */
  16. private EventLoopGroup group = new NioEventLoopGroup();
  17.  
  18. /**
  19. * 启动器
  20. */
  21. private Bootstrap bootstrap = new Bootstrap();
  22.  
  23. /**
  24. * 客户端通道
  25. */
  26. private Channel clientChannel;
  27.  
  28. /**
  29. * 客户端连接
  30. * @param host
  31. * @param port
  32. * @throws InterruptedException
  33. */
  34. public NettyClient(String host, int port) throws InterruptedException {
  35. bootstrap.group(group)
  36. .channel(NioSocketChannel.class)
  37. .option(ChannelOption.TCP_NODELAY, true)
  38. .handler(new ChannelInitializer<SocketChannel>() {
  39. @Override
  40. protected void initChannel(SocketChannel channel) throws Exception {
  41. channel.pipeline().addLast("idleStateHandler", new IdleStateHandler(5, 5, 12));
  42. channel.pipeline().addLast("nettyMessageDecoder", new NettyMessageDecoder(1024 * 1024, 4, 4));
  43. channel.pipeline().addLast("nettyMessageEncoder", new NettyMessageEncoder());
  44. channel.pipeline().addLast("heartBeatHandler", new HeartBeatRequestHandler());
  45. channel.pipeline().addLast("clientHandler", clientHandler);
  46. channel.pipeline().addLast("loginAuthHandler", new LoginAuthRequestHandler());
  47. }
  48. });
  49.  
  50. //发起同步连接操作
  51. ChannelFuture channelFuture = bootstrap.connect(host, port);
  52.  
  53. //注册连接事件
  54. channelFuture.addListener((ChannelFutureListener)future -> {
  55. //如果连接成功
  56. if (future.isSuccess()) {
  57. logger.info("客户端[" + channelFuture.channel().localAddress().toString() + "]已连接...");
  58. clientChannel = channelFuture.channel();
  59. }
  60. //如果连接失败,尝试重新连接
  61. else{
  62. logger.info("客户端[" + channelFuture.channel().localAddress().toString() + "]连接失败,重新连接中...");
  63. future.channel().close();
  64. bootstrap.connect(host, port);
  65. }
  66. });
  67.  
  68. //注册关闭事件
  69. channelFuture.channel().closeFuture().addListener(cfl -> {
  70. close();
  71. logger.info("客户端[" + channelFuture.channel().localAddress().toString() + "]已断开...");
  72. });
  73. }
  74.  
  75. /**
  76. * 客户端关闭
  77. */
  78. private void close() {
  79. //关闭客户端套接字
  80. if(clientChannel!=null){
  81. clientChannel.close();
  82. }
  83. //关闭客户端线程组
  84. if (group != null) {
  85. group.shutdownGracefully();
  86. }
  87. }
  88.  
  89. /**
  90. * 客户端发送消息
  91. * @param message
  92. * @return
  93. * @throws InterruptedException
  94. * @throws ExecutionException
  95. */
  96. public NettyMessage send(NettyMessage message) throws InterruptedException, ExecutionException {
  97. ChannelPromise promise = clientHandler.sendMessage(message);
  98. promise.await(3, TimeUnit.SECONDS);
  99. return clientHandler.getResponse();
  100. }
  101. }

.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, "Courier New", courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }

可以看出,我们使用了clientHandler来进行消息发送行为,通过promise阻塞来同步获取返回结果,接下来看看sendMessage的写法:

  1. public class ClientHandler extends ChannelInboundHandlerAdapter {
  2.  
  3. private static final Logger logger = LoggerFactory.getLogger(ClientHandler.class);
  4.  
  5. private ChannelHandlerContext ctx;
  6. private ChannelPromise promise;
  7. private NettyMessage response;
  8.  
  9. @Override
  10. public void channelActive(ChannelHandlerContext ctx) throws Exception {
  11. super.channelActive(ctx);
  12. this.ctx = ctx;
  13. }
  14.  
  15. @Override
  16. public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
  17. NettyMessage message = (NettyMessage) msg;
  18. if (message != null && message.getType() == MessageType.SERVICE_RESP.value()) {
  19. response = message;
  20. promise.setSuccess();
  21. } else {
  22. ctx.fireChannelRead(msg);
  23. }
  24. }
  25.  
  26. public synchronized ChannelPromise sendMessage(Object message) {
  27. while (ctx == null) {
  28. try {
  29. TimeUnit.MILLISECONDS.sleep(1);
  30. //logger.error("等待ChannelHandlerContext实例化");
  31. } catch (InterruptedException e) {
  32. logger.error("等待ChannelHandlerContext实例化过程中出错",e);
  33. }
  34. }
  35. promise = ctx.newPromise();
  36. ctx.writeAndFlush(message);
  37. return promise;
  38. }
  39.  
  40. public NettyMessage getResponse(){
  41. return response;
  42. }
  43.  
  44. }

.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, "Courier New", courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }

可以看到,在利用ChannelHanderContext进行发送消息前,我们先创建了一个promise并返回给send方法,那么send方法此时就会阻塞等待;当我们收到服务端消息后,promise.setSuccess就会解除send方法的等待行为,这样我们就能获取结果了。

此法针对真正需要同步等待获取结果的场景,如非必要,还是建议利用future来改造。

benchmark测试表明,此种同步获取结果的行为,表现挺稳定的,但是ops 在 150 左右, 真是性能太差了。高性能场合禁用此法。

Netty客户端发送消息并同步获取结果的更多相关文章

  1. 使用Java客户端发送消息和消费的应用

    体验链接:https://developer.aliyun.com/adc/scenario/fb1b72ee956a4068a95228066c3a40d6 实验简介 本教程将Demo演示使用jav ...

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

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

  3. mina客户端发送消息延迟问题分析

    原文:http://www.cnblogs.com/haiq/archive/2011/08/01/2124292.html (写的蛮好,保存下来) 由于项目需要,用到了 mina 框架进行 tcp ...

  4. Spring Boot+Socket实现与html页面的长连接,客户端给服务器端发消息,服务器给客户端轮询发送消息,附案例源码

    功能介绍 客户端给所有在线用户发送消息 客户端给指定在线用户发送消息 服务器给客户端发送消息(轮询方式) 项目搭建 项目结构图 pom.xml <?xml version="1.0&q ...

  5. java socket 一个服务器对应多个客户端,可以互相发送消息

    直接上代码,这是网上找的demo,然后自己根据需求做了一定的修改.代码可以直接运行 服务器端: package socket; import java.io.BufferedReader; impor ...

  6. ActiveMQ producer同步/异步发送消息

    http://activemq.apache.org/async-sends.html producer发送消息有同步和异步两种模式,可以通过代码配置: ((ActiveMQConnection)co ...

  7. Socket通讯-C#客户端与Java服务端通讯(发送消息和文件)

    设计思路 使用websocket通信,客户端采用C#开发界面,服务端使用Java开发,最终实现Java服务端向C#客户端发送消息和文件,C#客户端实现语音广播的功能. Java服务端设计 packag ...

  8. 2.技巧: 用 JAXM 发送和接收 SOAP 消息—Java API 使许多手工生成和发送消息方面必需的步骤自动化

    转自:https://www.cnblogs.com/chenying99/archive/2013/05/23/3094128.html 技巧: 用 JAXM 发送和接收 SOAP 消息—Java ...

  9. server-sent-event使用流信息向客户端发送数据

    <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...

随机推荐

  1. vue配置手机通过IP访问电脑开发环境

    vue配置手机通过IP访问电脑开发环境config/index.js// Various Dev Server settings host: '0.0.0.0', // can be overwrit ...

  2. C语言输出

    转自:https://blog.csdn.net/u014647208/article/details/53337315 int PrintVal = 9; /*按整型输出,默认右对齐*/ print ...

  3. python-支付宝支付示例

      项目演示: 1.输入金额 2.扫码支付: 3.支付完成: 4.跳转回商户 一.注册账号 https://openhome.alipay.com/platform/appDaily.htm?tab= ...

  4. vue中路由跳转的底层原理

    前端路由是直接找到与地址匹配的一个组件或对象并将其渲染出来.改变浏览器地址而不向服务器发出请求有两种方式: 1. 在地址中加入#以欺骗浏览器,地址的改变是由于正在进行页内导航 2. 使用H5的wind ...

  5. oracle数据库创建用户

    --4.1 创建表空间 CREATE TABLESPACE mdm_data DATAFILE 'D:\soft\Oracle\oracl\oradata\mdm_data01.dbf' SIZE 3 ...

  6. Java运行环境

    Java 开发环境配置 在本章节中我们将为大家介绍如何搭建Java开发环境. Windows 上安装开发环境 Linux 上安装开发环境 安装 Eclipse 运行 Java Cloud Studio ...

  7. 处理smartgit 过期脚本

    @echo off @title SmartGit License Tool color 1f cls set "version=18.1" set "fpath=%AP ...

  8. rsync如何不指定密码文件

    平时用rsync做数据同步时,都是通过--password-file指定一个密码文件 这个密码文件权限要求比较高,一般是600,属主属组都是rsync命令执行者 如果是在脚本中执行rsync,比如定时 ...

  9. mfc动态控件生成

    1.变量.函数声明 std::vector<CButton*>pBtn; afx void OnBtnClik(UINT uID);//回调函数 2.分配空间 pBtn.resize(50 ...

  10. Missing artifact com.oracle:ojdbc14:jar:10.2.0.3.0

    1.Missing artifact com.oracle:ojdbc14:jar:10.2.0.3.0操作如下: 2.下载链接:链接:https://pan.baidu.com/s/1Ziyg2jl ...