基于Netty与RabbitMQ的消息服务

来源:互联网 发布:p2p理财跑路汇总数据 编辑:程序博客网 时间:2024/06/05 03:15

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 idlech.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/NettyMqServer

 

参考:

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


来源 :http://www.cnblogs.com/luxiaoxun/archive/2015/01/28/4257105.html

 

原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 房门前乱挂光纤线影响住户怎么办 挂断低于限高的光缆怎么办 开大车挂住光缆怎么办 风把树枝挂断压到车该怎么办 货车柴油冻住了怎么办 尖头鞋老是折尖怎么办 打 氟氯西林疼怎么办 多余的十字绣线怎么办 硅胶类的东西沾到蓝药水怎么办? ph计斜率不到90怎么办 ph计斜率低于90怎么办 顾客说衣服起球怎么办 买的手机壳太滑怎么办 硅胶手机壳太滑怎么办 磨砂手机壳太滑怎么办 被热胶棒烫了怎么办 车钢垫子次了怎么办 【图】机组主轴密封漏水怎么办? 孕妇吃了好多杏怎么办 怀孕6个月吃了好多杏怎么办 白色纯棉衣服染色了怎么办 红色硅胶壳黑了怎么办 小米6gps信号弱怎么办 网线头卡子断了怎么办 入户网线太短了怎么办 孩子弹钢琴大拇指出琴键怎么办 手指肿胀疼痛变粗怎么办 iphone系统占用内存大怎么办 手机系统占用内存大怎么办 头盔固定配件掉了怎么办 移动4g网络不好怎么办 wifi登录密码忘记了怎么办 电脑登录密码忘记了怎么办 笔记本电脑登录密码忘记了怎么办 信用卡登录密码忘记了怎么办 华为p9后置摄像头模糊怎么办 手机图片文件夹删了怎么办 发票系统导出的xml怎么办 微信支付被限制怎么办 跨行三天不到账怎么办 测速正常但实际很慢怎么办