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