基于Netty与RabbitMQ的消息服务
Netty作为一个高性能的异步网络开发框架,可以作为各种服务的开发框架。
前段时间的一个项目涉及到硬件设备实时数据的采集,采用Netty作为采集服务的实现框架,同时使用RabbitMQ作为采集服务和各个其他模块的通信消息队列,整个服务框架图如下:

将业务代码和实际协议解析部分的代码抽离,得到以上一个简单的设计图,代码开源在GitHub上,简单介绍下NettyMQServer采集服务涉及到的几个关键技术点:
1、设备TCP消息解析:
NettyMQServer和采集设备Device之间采用TCP通信,TCP消息的解析可以使用LengthFieldBasedFrameDecoder(消息头和消息体),可以有效的解决TCP消息“粘包”问题。
消息包解析图如下:bytes length field at offset 0, do not strip header, the length field represents the length of the whole message
lengthFieldOffset = 0
lengthFieldLength = 2
lengthAdjustment = -2 (= the length of the Length field)
initialBytesToStrip = 0 BEFORE DECODE (14 bytes) AFTER DECODE (14 bytes)
+--------+----------------+ +--------+----------------+
| Length | Actual Content |----->| Length | Actual Content |
| 0x000E | "HELLO, WORLD" | | 0x000E | "HELLO, WORLD" |
+--------+----------------+ +--------+----------------+
代码中消息长度的存储采用了4个字节,采用LengthFieldBasedFrameDecoder(65535,0,4,-4,0)解码,Netty会从接收的数据中头4个字节中得到消息的长度,进而得到一个TCP消息包。
2、给设备发消息:
首先在连接创建时,要保留TCP的连接:
static final ChannelGroup channels = new DefaultChannelGroup(
GlobalEventExecutor.INSTANCE); @Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
// A closed channel will be removed from ChannelGroup automatically
channels.add(ctx.channel());
}
在每次一个Channel Active(连接创建)的时候用ChannelGroup保存这个Channel连接,当需要给某个设备发消息的时候,可以遍历该ChannelGroup,找到对应的Channel,给该Channel发送消息:
for (io.netty.channel.Channel c : EchoServerHandler.channels) {
ByteBuf msg = Unpooled.copiedBuffer(message.getBytes());
c.writeAndFlush(msg);
}
这里是给所有的连接的设备都发。当连接断开的时候,ChannelGroup会自动remove掉这个连接,不需要我们手动管理。
3、心跳检测
当某个设备Device由于断电或是其他原因导致设备不正常无法采集数据,Netty服务端需要知道该设备是否在正常工作,可以使用Netty的IdleStateHandler,示例代码如下:
// 3 minutes for read idle
ch.pipeline().addLast(new IdleStateHandler(3*60,0,0));
ch.pipeline().addLast(new HeartBeatHandler()); /**
* Handler implementation for heart beating.
*/
public class HeartBeatHandler extends ChannelInboundHandlerAdapter{ @Override
public void userEventTriggered(ChannelHandlerContext ctx, Object evt)
throws Exception {
if (evt instanceof IdleStateEvent) {
IdleStateEvent event = (IdleStateEvent) evt;
if (event.state() == IdleState.READER_IDLE) {
// Read timeout
System.out.println("READER_IDLE: read timeout from "+ctx.channel().remoteAddress());
//ctx.disconnect(); //Channel disconnect
}
}
}
}
上面设置3分钟没有读到数据,则触发一个READER_IDLE事件。
4、RabbitMQ消息接收与发送
NettyMQServer消息发送采用了Spring AMQP,只需要在配置文件中简单配置一下,就可以方便使用。
NettyMQServer消息接收同样可以采用Spring AMQP,但由于对Spring相关的配置不是很熟悉,为了更灵活的使用MQ,这里使用了RabbitMQ Client Java API来实现:
Connection connection = connnectionFactory.newConnection();
Channel channel = connection.createChannel();
channel.exchangeDeclare(exchangeName, "direct", true, false, null);
channel.queueDeclare(queueName, true, false, false, null);
channel.queueBind(queueName, exchangeName, routeKey); // process the message one by one
channel.basicQos(1); QueueingConsumer queueingConsumer = new QueueingConsumer(channel);
// auto-ack is false
channel.basicConsume(queueName, false, queueingConsumer);
while (true) {
QueueingConsumer.Delivery delivery = queueingConsumer.nextDelivery();
String message = new String(delivery.getBody()); log.debug("Mq Receiver get message");
// Send the message to all connected clients
// If you want to send to a specified client, just add
// your own logic and ack manually
// Be aware that ChannelGroup is thread safe
log.info(String.format("Conneted client number: %d",EchoServerHandler.channels.size()));
for (io.netty.channel.Channel c : EchoServerHandler.channels) {
ByteBuf msg = Unpooled.copiedBuffer(message.getBytes());
c.writeAndFlush(msg);
}
// manually ack to MQ server the message is consumed.
channel.basicAck(delivery.getEnvelope().getDeliveryTag(), false);
}
以上代码从一个Queue中读取数据,为了有效处理数据,防止异常数据丢失,使用了手动Ack。
RabbitMQ的使用方式:http://www.cnblogs.com/luxiaoxun/p/3918054.html
代码托管在GitHub上:https://github.com/luxiaoxun/Code4Java
参考:
http://netty.io/
http://netty.io/4.0/api/io/netty/handler/codec/LengthFieldBasedFrameDecoder.html
http://netty.io/4.0/api/io/netty/handler/timeout/IdleStateHandler.html
基于Netty与RabbitMQ的消息服务的更多相关文章
- .net平台 基于 XMPP协议的即时消息服务端简单实现
.net平台 基于 XMPP协议的即时消息服务端简单实现 昨天抽空学习了一下XMPP,在网上找了好久,中文的资料太少了所以做这个简单的例子,今天才完成.公司也正在准备开发基于XMPP协议的即时通讯工具 ...
- 基于PHP使用rabbitmq实现消息队列
1.从github上面获取AMQP基于php的实现扩展 2.创建生产者 send.php 3.创建消费者 receive.php 4.在cli模式下 分别执行 send.php receive. ...
- MQTT的学习研究(八)基于HTTP DELETE MQTT 订阅消息服务端使用
HTTP DELETE 订阅主题请求协议和响应协议http://publib.boulder.ibm.com/infocenter/wmqv7/v7r0/topic/com.ibm.mq.csqzau ...
- webcat——基于netty的http和websocket框架
代码地址如下:http://www.demodashi.com/demo/12687.html Webcat是一个基于netty的简单.高性能服务端框架,目前提供http和websocket两种协议的 ...
- 【5】JMicro免费在线消息服务
JMicro是一个用Java语言实现的开源微服务全家桶, 源码地址:https://github.com/mynewworldyyl/jmicro, Demo地址:http://jmicro.cn/. ...
- 微服务实战(三):落地微服务架构到直销系统(构建基于RabbitMq的消息总线)
从前面文章可以看出,消息总线是EDA(事件驱动架构)与微服务架构的核心部件,没有消息总线,就无法很好的实现微服务之间的解耦与通讯.通常我们可以利用现有成熟的消息代理产品或云平台提供的消息服务来构建自己 ...
- 适合新手:从零开发一个IM服务端(基于Netty,有完整源码)
本文由“yuanrw”分享,博客:juejin.im/user/5cefab8451882510eb758606,收录时内容有改动和修订. 0.引言 站长提示:本文适合IM新手阅读,但最好有一定的网络 ...
- 基于netty轻量的高性能分布式RPC服务框架forest<上篇>
工作几年,用过不不少RPC框架,也算是读过一些RPC源码.之前也撸过几次RPC框架,但是不断的被自己否定,最近终于又撸了一个,希望能够不断迭代出自己喜欢的样子. 顺便也记录一下撸RPC的过程,一来作为 ...
- Android 基于Netty的消息推送方案之对象的传递(四)
在上一篇文章中<Android 基于Netty的消息推送方案之字符串的接收和发送(三)>我们介绍了Netty的字符串传递,我们知道了Netty的消息传递都是基于流,通过ChannelBuf ...
随机推荐
- springmvc 格式化使用Jackjson格式化报Failed to write HTTP message
Failed to write HTTP message: org.springframework.http.converter.HttpMessageNotWritableException: Co ...
- mybatis-generator运行命令
java -jar mybatis-generator-core-x.x.x.jar -configfile generatorConfig.xml -overwrite
- selenium 定位元素
一.单个元素的定位方式: By.className(className))By.cssSelector(selector)By.id(id)By.linkText(linkText)By.name(n ...
- UNIX下的LD_PRELOAD环境变量
UNIX下的LD_PRELOAD环境变量 也许这个话题并不新鲜,因为LD_PRELOAD所产生的问题由来已久.不过,在这里,我还是想讨论一下这个环境变量.因为这个环境变量所带来的安全问题非常严重,值得 ...
- mysql myisam简单分表设计
一般来说,当我们的数据库的数据超过了100w记录的时候就应该考虑分表或者分区了,这次我来详细说说分表的一些方法.目前我所知道的方法都是MYISAM的,INNODB如何做分表并且保留事务和外键,我还不是 ...
- Mybatis学习总结(一)——入门基础
一.Mybatis介绍 1.MyBatis是什么? MyBatis 本是apache的一个开源项目iBatis, 2010年这个项目由apache software foundation 迁移到了go ...
- Android 项目结构图
src:存放Java源代码 gen:存放系统自动生成的配置文件 Android 4.4.2:包含Android.jar文件,包含构建应用程序所需的所有Android SDK库 asssets:存放资源 ...
- WebClient 实现多文件/文本同时上传
public class CreateBytes { Encoding encoding = Encoding.UTF8; /**/ /// <summary> /// 拼接所有的二进制数 ...
- python raise a string exception is deprecated
python不允许raise 一个内建的string 对象.所以就崩溃,可以先将其转换成其他string,比如赋值.
- ContextMenuStrip 类
表示快捷菜单 命名空间: System.Windows.Forms程序集: System.Windows.Forms(位于 System.Windows.Forms.dll) 继承层次结构 Sy ...