springboot+netty+rocketMq

netty相关工具类:
netty工具类
netty接收大量的数据消息,可能会造成消息堆积,采用mq的形式来消费消息
相关文件位置:
在这里插入图片描述

1、添加netty和mq依赖

<dependency>
    <groupId>io.netty</groupId>
    <artifactId>netty-all</artifactId>
    <version>5.0.0.Alpha2</version>
</dependency>
<!-- 支持rocketMq-->
<dependency>
    <groupId>org.apache.rocketmq</groupId>
    <artifactId>rocketmq-spring-boot-starter</artifactId>
    <version>2.1.0</version>
</dependency>

2、集成mq
创建生产者

package com.disruptor.rocketmq;

import lombok.extern.slf4j.Slf4j;
import org.apache.rocketmq.client.exception.MQClientException;
import org.apache.rocketmq.client.producer.DefaultMQProducer;
import org.springframework.stereotype.Component;

@Slf4j
@Component
public class Producer {
    private String producerGroup = "producer";
    private DefaultMQProducer producer;

    public Producer() throws MQClientException {
        //示例生产者
        producer = new DefaultMQProducer(producerGroup);
        //不开启vip通道 开通口端口会减2
        producer.setVipChannelEnabled(false);
        //绑定name server
        producer.setNamesrvAddr("ip:9876");
        start();
    }
    /**
     * 对象在使用之前必须要调用一次,只能初始化一次
     */
    public void start(){
        try {
            this.producer.start();
            log.warn("mq启动成功!");
        } catch (MQClientException e) {
            e.printStackTrace();
        }
    }

    public DefaultMQProducer getProducer(){
        return this.producer;
    }
    /**
     * 一般在应用上下文,使用上下文监听器,进行关闭
     */
    public void shutdown(){
        this.producer.shutdown();
    }
}

创建消费者监听

package com.disruptor.rocketmq;

import lombok.extern.slf4j.Slf4j;
import org.apache.rocketmq.client.consumer.DefaultMQPushConsumer;
import org.apache.rocketmq.client.consumer.listener.ConsumeConcurrentlyContext;
import org.apache.rocketmq.client.consumer.listener.ConsumeConcurrentlyStatus;
import org.apache.rocketmq.client.consumer.listener.MessageListenerConcurrently;
import org.apache.rocketmq.client.exception.MQClientException;
import org.apache.rocketmq.common.message.MessageExt;
import org.apache.rocketmq.common.protocol.heartbeat.MessageModel;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.stereotype.Component;

import java.util.List;

/**
 * @author bluekingzmm
 * @desc
 * @date 2022年01月17日
 */
@Slf4j
@Component
public class RocketMqConsumerListener {


//    @Autowired
//    private ParseMsgBodyHandlerFactory parseMsgBodyHandlerFactory;

    @Bean
    public DefaultMQPushConsumer getRocketMQConsumer() throws MQClientException {
        //1.创建一个接收消息的对象Consumer
        DefaultMQPushConsumer consumer = new DefaultMQPushConsumer("newyctech-smp-equip");
        // ★★★组名
        //2.设定接收的命名服务器地址
        consumer.setNamesrvAddr("36.137.215.234:9876");
        //3.设置接收消息对应的topic,对应的sub标签为任意
        consumer.subscribe("producerTopic", "*");
        //设置当前消费者的消费模式(默认模式:负载均衡)
        consumer.setMessageModel(MessageModel.CLUSTERING);
        consumer.setConsumeThreadMax(10);
        consumer.setConsumeThreadMin(6);
        consumer.setConsumeMessageBatchMaxSize(10);
        //3.开启监听,用于接收消息
        consumer.registerMessageListener(new MessageListenerConcurrently() {
            @Override
            public ConsumeConcurrentlyStatus consumeMessage(List<MessageExt> list, ConsumeConcurrentlyContext consumeConcurrentlyContext) {
                //遍历消息
                for (MessageExt msg : list) {
//                    log.warn("收到消息:" + msg);
//                    log.warn("消息是:" + new String(msg.getBody()));
                    //业务代码
//                    parseMsgBodyHandlerFactory.parseMsg(new String(msg.getBody()));
                }
                return ConsumeConcurrentlyStatus.CONSUME_SUCCESS;
            }
        });
        //4.启动接收消息的服务
        consumer.start();
        log.warn("接受消息服务已经开启!");
        //5 不要关闭消费者!
        return consumer;
    }

}

3、集成netty
创建netty相关属性类

package com.disruptor.prop;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Service;

@Service
@Component
public class PropertiesConfig {
    @Value("${newyctech.smp.netty.debug}")
    private Integer nettyDebug;

    @Value("${newyctech.smp.netty.url}")
    private String nettyUrl;

    @Value("${newyctech.smp.netty.port}")
    private Integer nettyPort;

    public Integer getNettyDebug() {
        return nettyDebug;
    }

    public void setNettyDebug(Integer nettyDebug) {
        this.nettyDebug = nettyDebug;
    }

    public String getNettyUrl() {
        return nettyUrl;
    }

    public void setNettyUrl(String nettyUrl) {
        this.nettyUrl = nettyUrl;
    }

    public Integer getNettyPort() {
        return nettyPort;
    }

    public void setNettyPort(Integer nettyPort) {
        this.nettyPort = nettyPort;
    }
}

创建服务端启动

package com.disruptor.netty.core;

import com.disruptor.rocketmq.Producer;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelOption;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.rocketmq.spring.core.RocketMQTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

@Component
public class NettyServerListener {
    private static Log logger = LogFactory.getLog(NettyServerListener.class);

    @Value("${newyctech.smp.netty.port}")
    private int nettyPort;


    @Autowired
    private Producer producer;

    @Autowired
    private RocketMQTemplate rocketMQTemplate;

    public void start() throws Exception {
        logger.info("..........netty服务器开始启动.......");

        //服务端要建立两个group,一个负责接收客户端的连接,一个负责处理数据传输
        //连接处理group
        EventLoopGroup bossGroup = new NioEventLoopGroup();
        //事件处理group
        EventLoopGroup workGroup = new NioEventLoopGroup();

        try {

            ServerBootstrap b = new ServerBootstrap();
            // 绑定处理group
            b.group(bossGroup, workGroup);
            b.channel(NioServerSocketChannel.class);
            //
            b.option(ChannelOption.SO_BACKLOG, 256);
            //保持连接数
            b.option(ChannelOption.SO_BACKLOG, 1024);
            b.option(ChannelOption.SO_SNDBUF, 32 * 2014);   //设置发送缓冲区大小
            b.option(ChannelOption.SO_RCVBUF, 32 * 2014);   //设置接受缓冲区大小
            //保持连接
            //b.childOption(ChannelOption.SO_KEEPALIVE, true);
            //处理新连接
            b.childHandler(new ChildChannelHandler(producer, rocketMQTemplate));
            // 绑定端口
            ChannelFuture f = b.bind(nettyPort).sync();

            // 等待服务端监听端口关闭
            f.channel().closeFuture().sync();

        } finally {
            // 优雅的退出
            bossGroup.shutdownGracefully();
            workGroup.shutdownGracefully();
        }
    }
}

创建客户端监听

package com.disruptor.netty.core;


import com.disruptor.netty.server.ReadMessageChannelHandler;
import com.disruptor.rocketmq.Producer;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.socket.SocketChannel;
import io.netty.handler.codec.DelimiterBasedFrameDecoder;
import io.netty.handler.codec.string.StringDecoder;
import io.netty.handler.codec.string.StringEncoder;
import io.netty.util.CharsetUtil;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.rocketmq.spring.core.RocketMQTemplate;
import org.springframework.stereotype.Component;

@Component
public class ChildChannelHandler extends ChannelInitializer<SocketChannel> {

    private static Log logger = LogFactory.getLog(ChildChannelHandler.class);
    private Producer producer;
    private RocketMQTemplate rocketMQTemplate;

    public ChildChannelHandler(Producer producer, RocketMQTemplate rocketMQTemplate
    ) {
        this.producer = producer;
        this.rocketMQTemplate = rocketMQTemplate;
    }

    @Override
    protected void initChannel(SocketChannel socketChannel) throws Exception {
        logger.info("报告:有一客户端链接到本服务端,ip:" + socketChannel.localAddress().getHostName() + "port" + socketChannel.localAddress().getPort() + "报告完毕");
        ChannelPipeline pipeline = socketChannel.pipeline();
        // 解码器
        // 基于换行符号
        ByteBuf delimiter = Unpooled.copiedBuffer("$_&".getBytes());
        //设置字符串形式的编码
        pipeline.addLast(new StringEncoder(CharsetUtil.ISO_8859_1));
        //设置字符串解码
        pipeline.addLast(new StringDecoder(CharsetUtil.ISO_8859_1));
        pipeline.addLast(new DelimiterBasedFrameDecoder(1024, delimiter));
        pipeline.addLast(new ReadMessageChannelHandler(producer, rocketMQTemplate));
    }
}

创建netty接收消息并处理,获取消息客户端存储,发送消息时需要拿到客户端才能发
mq延迟发送1分钟(数量量太大,生产多,消费少,因此延迟发送)

package com.disruptor.netty.server;


import com.disruptor.rocketmq.Producer;
import com.disruptor.util.ChannelhandlerUtils;
import com.disruptor.util.NettyUtil;
import com.disruptor.util.StringUtil;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.util.CharsetUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.rocketmq.common.message.Message;
import org.apache.rocketmq.spring.core.RocketMQTemplate;
import org.springframework.scheduling.annotation.Async;
import org.springframework.util.StringUtils;

import java.util.concurrent.ConcurrentHashMap;

@Slf4j
public class ReadMessageChannelHandler extends ChannelInboundHandlerAdapter {

    private static Log logger = LogFactory.getLog(ReadMessageChannelHandler.class);
    private Producer producer;
    private RocketMQTemplate rocketMQTemplate;

    public ReadMessageChannelHandler(Producer producer, RocketMQTemplate rocketMQTemplate) {
        this.producer = producer;
        this.rocketMQTemplate = rocketMQTemplate;
    }

    @Override
    public void channelInactive(ChannelHandlerContext ctx) throws Exception {

    }


    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {

    }


    @Override
    @Async
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
        String body = (String) msg;
//        logger.error("body:"+body);
        String message = StringUtil.bytesToHexString(body.getBytes(CharsetUtil.ISO_8859_1));
//        logger.error("message:"+message);
        String start = message.substring(0, 2);
        if (message.length() < 30 || !"7e".equals(start)) {
            return;
        }
        String end = message.substring(message.length() - 2, message.length());
        if ("ff".equals(end)) {
            message = message.substring(0, message.length() - 2);
        }
//        logger.warn("***************  移除的receive msg is " + JSONUtils.toJSONString(message));
        String replyMessage = NettyUtil.replyVerification(message);
        if (StringUtils.hasLength(replyMessage)) {
            String equipCode = replyMessage.substring(6, 18);
            ChannelhandlerUtils.set(equipCode, ctx);
//            logger.error("拿到数据,响应发送应答标识:" + replyMessage);
            byte[] bytes = StringUtil.hexStringToBytes(replyMessage);
            ByteBuf byteBuf = Unpooled.copiedBuffer(bytes);
            ctx.write(byteBuf);
            ctx.flush();
        }
//        logger.warn("发送mq,message:" + message);
        Thread.sleep(600);
        rocketMQTemplate.convertAndSend("producerTopic",message);
        //延迟消息发送
        Message msgg = new Message("producerTopic", message.getBytes());
        //设置延迟level为5, 1分钟
        msgg.setDelayTimeLevel(5);
        producer.getProducer().send(msgg);
    }

    @Override
    public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
        ctx.flush();
    }


    public static void main(String[] args) {
//        System.out.println(String.valueOf(Unpooled.buffer().writeBytes("7e500101202107029061d6d1c300010000c47e".getBytes())) + "");
        String a = "7e58fe012021070197621477cf0001007d010000000c0000000d000000020000347d026285f7c201000201347d0202ffff000000000000000000000000000000000000000000000000000000000000434d494f540000000000000000000000393";
        String substring = a.substring(0, 2);
        System.out.println(substring);
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值