1、安裝RocketMQ,忽略;
2、創建springboot項目,pom添加
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>io.netty</groupId>
<artifactId>netty-all</artifactId>
<version>4.1.53.Final</version>
</dependency>
<dependency>
<groupId>io.netty</groupId>
<artifactId>netty-all</artifactId>
<version>4.1.53.Final</version>
</dependency>
<dependency>
<groupId>org.apache.rocketmq</groupId>
<artifactId>rocketmq-client</artifactId>
<version>4.7.1</version>
</dependency>
<dependency>
<groupId>org.apache.rocketmq</groupId>
<artifactId>rocketmq-client</artifactId>
<version>4.7.1</version>
</dependency>
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>29.0-jre</version>
</dependency>
</dependencies>
3、MyChannelHandlerPool.java類,channelHandler設置。
/**
* @Author: Liu Yue
* @Descripition:
* @Date; Create in 2020/1/14 9:24
**/
public class MyChannelHandlerPool {
public MyChannelHandlerPool(){}
public static ChannelGroup channelGroup = new DefaultChannelGroup(GlobalEventExecutor.INSTANCE);
}
4、MyWebSocketHandler,socketHandler
/**
* @Author: Liu Yue
* @Descripition:
* @Date; Create in 2020/1/14 9:25
**/
@Configuration
@Component
@Slf4j
public class MyWebSocketHandler extends ChannelInboundHandlerAdapter {
@Autowired
private ProducerConfigure defaultMQProducer;
@Autowired
private ProducerConfig producerConfigure;
public MyWebSocketHandler(){}
public MyWebSocketHandler(ProducerConfigure defaultMQProducer,ProducerConfig producerConfigure){
this.defaultMQProducer = defaultMQProducer;
this.producerConfigure = producerConfigure;
}
@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
System.out.println("與客戶端建立連接,通道開啓!");
//添加到channelGroup通道組
//添加到channelGroup通道組
MyChannelHandlerPool.channelGroup.add(ctx.channel());
}
@Override
public void channelInactive(ChannelHandlerContext ctx) throws Exception {
System.out.println("與客戶端斷開連接,通道關閉!");
//添加到channelGroup 通道組
MyChannelHandlerPool.channelGroup.remove(ctx.channel());
}
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
log.info("server channelRead......" + " thread :" + ctx.getClass());
log.info("獲取值:" + msg);
Message message = new Message(producerConfigure.getTopic(), producerConfigure.getTags(), System.currentTimeMillis()+"", msg.toString().getBytes());
// 這裏用到了這個mq的異步處理,類似ajax,可以得到發送到mq的情況,並做相應的處理
//不過要注意的是這個是異步的
DefaultMQProducer producer = this.defaultMQProducer.defaultProducer();
producer.send(message, new SendCallback() {
@Override
public void onSuccess(SendResult sendResult) {
log.info("傳輸成功");
//log.info(GsonUtil.GSON.toJson(sendResult));
}
@Override
public void onException(Throwable e) {
log.error("傳輸失敗", e);
}
});
//將客戶端的信息直接返回寫入ctx
ctx.write(msg + "\n");
//刷新緩存區
ctx.flush();
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
cause.printStackTrace();
ctx.close();
}
public void sendAllMessage(String message,final String mark){
//收到信息後,羣發給所有channel
log.info("向服務器發送信息:{}",message);
//MyChannelHandlerPool.channelGroup.writeAndFlush( new TextWebSocketFrame(message));
byte[] bytes = MyStringUtil.hexString2Bytes(message);
ByteBuf bufff = Unpooled.buffer();//netty需要用ByteBuf傳輸
bufff.writeBytes(bytes);//對接需要16進制
//MyChannelHandlerPool.channelGroup.writeAndFlush(bufff);
MyChannelHandlerPool.channelGroup.writeAndFlush(bufff).addListener(new ChannelFutureListener() {
@Override
public void operationComplete(ChannelFuture future) throws Exception {
StringBuilder sb = new StringBuilder("");
if(!StringUtils.isEmpty(mark)){
sb.append("【").append(mark).append("】");
}
if (future.isSuccess()) {
System.out.println(sb.toString()+"回寫成功"+message);
log.info(sb.toString()+"回寫成功"+message);
} else {
System.out.println(sb.toString()+"回寫失敗"+message);
log.error(sb.toString()+"回寫失敗"+message);
}
}
});
}
5、NettyServer
/**
* @Author: Liu Yue
* @Descripition:
* @Date; Create in 2020/1/14 9:22
**/
@Configuration
@Slf4j
public class NettyServer {
@Autowired
private ProducerConfigure defaultMQProducer;
@Autowired
private ProducerConfig producerConfigure;
public void start(InetSocketAddress address) throws Exception {
EventLoopGroup bossGroup = new NioEventLoopGroup();
EventLoopGroup group = new NioEventLoopGroup();
try {
ServerBootstrap sb = new ServerBootstrap();
sb.option(ChannelOption.SO_BACKLOG, 1024);
sb.group(group, bossGroup) // 綁定線程池
.channel(NioServerSocketChannel.class) // 指定使用的channel
.localAddress(address)// 綁定監聽端口
.childHandler(new ChannelInitializer<SocketChannel>() { // 綁定客戶端連接時候觸發操作
@Override
protected void initChannel(SocketChannel ch) throws Exception {
log.info("收到新連接");
//ch.pipeline().addLast("decoder",new StringDecoder(CharsetUtil.UTF_8));
//ch.pipeline().addLast("encoder",new StringEncoder(CharsetUtil.UTF_8));
ChannelPipeline pipeline = ch.pipeline();
//IdleStateHandler心跳機制,如果超時觸發Handle中userEventTrigger()方法
pipeline.addLast("idleStateHandler",
new IdleStateHandler(5, 0, 0, TimeUnit.MINUTES));
//修改了這裏
pipeline.addLast("decoder", new ZMFADecoder());
pipeline.addLast(new LengthFieldBasedFrameDecoder(Integer.MAX_VALUE, 4, 4, -8, 0));
//pipeline.addLast("byteArrayEncoder", new ByteArrayEncoder());
//自定義Handler
pipeline.addLast(new MyWebSocketHandler(defaultMQProducer,producerConfigure));
}
})
/*.childHandler(new ServerChannelInitializer())*/
.option(ChannelOption.SO_BACKLOG, 128)
.childOption(ChannelOption.SO_KEEPALIVE, true);
ChannelFuture cf = sb.bind().sync(); // 服務器異步創建綁定
log.info(NettyServer.class + " 啓動正在監聽: " + cf.channel().localAddress());
cf.channel().closeFuture().sync(); // 關閉服務器通道
} finally {
group.shutdownGracefully().sync(); // 釋放線程池資源
bossGroup.shutdownGracefully().sync();
}
}
}
6、ZMFADecoder
/**
* @Author: Liu Yue
* @Descripition: 16進制解碼器
* @Date; Create in 2020/10/30 9:05
**/
@Component
@Slf4j
public class ZMFADecoder extends ByteToMessageDecoder {
@Override
protected void decode(ChannelHandlerContext ctx, ByteBuf buffer, List<Object> out) throws Exception {
//創建字節數組,buffer.readableBytes可讀字節長度
byte[] b = new byte[buffer.readableBytes()];
//複製內容到字節數組b
buffer.readBytes(b);
//字節數組轉字符串
out.add(bytesToHexString(b));
}
public String bytesToHexString(byte[] bArray) {
StringBuffer sb = new StringBuffer(bArray.length);
String sTemp;
for (int i = 0; i < bArray.length; i++) {
sTemp = Integer.toHexString(0xFF & bArray[i]);
if (sTemp.length() < 2)
sb.append(0);
sb.append(sTemp.toUpperCase());
}
return sb.toString();
}
public static String toHexString1(byte[] b) {
StringBuffer buffer = new StringBuffer();
for (int i = 0; i < b.length; ++i) {
buffer.append(toHexString1(b[i]));
}
return buffer.toString();
}
public static String toHexString1(byte b) {
String s = Integer.toHexString(b & 0xFF);
if (s.length() == 1) {
return "0" + s;
} else {
return s;
}
}
}
7、ProducerConfigure
/**
* @Author: Liu Yue
* @Descripition:
* @Date; Create in 2020/11/2 10:47
**/
@Configuration
@Slf4j
public class ProducerConfigure {
@Autowired
private ProducerConfig producerConfigure;
/**
* 創建普通消息發送者實例
*
* @return
* @throws MQClientException
*/
@Bean
public DefaultMQProducer defaultProducer() throws MQClientException {
log.info(producerConfigure.toString());
log.info("defaultProducer 正在創建---------------------------------------");
DefaultMQProducer producer = new DefaultMQProducer(producerConfigure.getGroupname());
producer.setNamesrvAddr(producerConfigure.getNamesrvaddr());
producer.setVipChannelEnabled(false);
producer.setRetryTimesWhenSendAsyncFailed(10);
producer.start();
log.info("rocketmq producer server開啓成功---------------------------------.");
return producer;
}
}
8、工具類MyStringUtil
/**
* @Author: Liu Yue
* @Descripition:
* @Date; Create in 2020/11/4 10:17
**/
@Component
public class MyStringUtil {
/**
* @Title:bytes2HexString
* @Description:字節數組轉16進制字符串
* @param b
* 字節數組
* @return 16進制字符串
* @throws
*/
public static String bytes2HexString(byte[] b) {
StringBuffer result = new StringBuffer();
String hex;
for (int i = 0; i < b.length; i++) {
hex = Integer.toHexString(b[i] & 0xFF);
if (hex.length() == 1) {
hex = '0' + hex;
}
result.append(hex.toUpperCase());
}
return result.toString();
}
/**
* @Title:hexString2Bytes
* @Description:16進制字符串轉字節數組
* @param src 16進制字符串
* @return 字節數組
*/
public static byte[] hexString2Bytes(String src) {
src = src.replaceAll(" ", "");
byte[] bytes = new byte[src.length() / 2];
for (int i = 0; i < src.length() / 2; i++) {
String subStr = src.substring(i * 2, i * 2 + 2);
bytes[i] = (byte) Integer.parseInt(subStr, 16);
}
return bytes;
}
/**
* @Title:string2HexString
* @Description:字符串轉16進制字符串
* @param strPart 字符串
* @return 16進制字符串
*/
public static String string2HexString(String strPart) {
StringBuffer hexString = new StringBuffer();
for (int i = 0; i < strPart.length(); i++) {
int ch = (int) strPart.charAt(i);
String strHex = Integer.toHexString(ch);
hexString.append(strHex);
}
return hexString.toString();
}
/**
* @Title:hexString2String
* @Description:16進制字符串轉字符串
* @param src
* 16進制字符串
* @return 字節數組
* @throws
*/
public static String hexString2String(String src) {
String temp = "";
for (int i = 0; i < src.length() / 2; i++) {
//System.out.println(Integer.valueOf(src.substring(i * 2, i * 2 + 2),16).byteValue());
temp = temp+ (char)Integer.valueOf(src.substring(i * 2, i * 2 + 2),16).byteValue();
}
return temp;
}
/**
* @Title:char2Byte
* @Description:字符轉成字節數據char-->integer-->byte
* @param src
* @return
* @throws
*/
public static Byte char2Byte(Character src) {
return Integer.valueOf((int)src).byteValue();
}
/**
* @Title:intToHexString
* @Description:10進制數字轉成16進制
* @param a 轉化數據
* @param len 佔用字節數
* @return
* @throws
*/
public static String intToHexString(int a,int len){
len<<=1;
String hexString = Integer.toHexString(a);
int b = len -hexString.length();
if(b>0){
for(int i=0;i<b;i++) {
hexString = "0" + hexString;
}
}
return hexString;
}
/**
* 將16進制的2個字符串進行異或運算
* http://blog.csdn.net/acrambler/article/details/45743157
* @param strHex_X
* @param strHex_Y
* 注意:此方法是針對一個十六進制字符串一字節之間的異或運算,如對十五字節的十六進制字符串異或運算:1312f70f900168d900007df57b4884
先進行拆分:13 12 f7 0f 90 01 68 d9 00 00 7d f5 7b 48 84
13 xor 12-->1
1 xor f7-->f6
f6 xor 0f-->f9
....
62 xor 84-->e6
即,得到的一字節校驗碼爲:e6
* @return
*/
public static String xor(String strHex_X,String strHex_Y){
//將x、y轉成二進制形式
String anotherBinary=Integer.toBinaryString(Integer.valueOf(strHex_X,16));
String thisBinary=Integer.toBinaryString(Integer.valueOf(strHex_Y,16));
String result = "";
//判斷是否爲8位二進制,否則左補零
if(anotherBinary.length() != 8){
for (int i = anotherBinary.length(); i <8; i++) {
anotherBinary = "0"+anotherBinary;
}
}
if(thisBinary.length() != 8){
for (int i = thisBinary.length(); i <8; i++) {
thisBinary = "0"+thisBinary;
}
}
//異或運算
for(int i=0;i<anotherBinary.length();i++){
//如果相同位置數相同,則補0,否則補1
if(thisBinary.charAt(i)==anotherBinary.charAt(i))
result+="0";
else{
result+="1";
}
}
return Integer.toHexString(Integer.parseInt(result, 2));
}
/**
* Convert byte[] to hex string.這裏我們可以將byte轉換成int
* @param src byte[] data
* @return hex string
*/
public static String bytes2Str(byte[] src){
StringBuilder stringBuilder = new StringBuilder("");
if (src == null || src.length <= 0) {
return null;
}
for (int i = 0; i < src.length; i++) {
int v = src[i] & 0xFF;
String hv = Integer.toHexString(v);
if (hv.length() < 2) {
stringBuilder.append(0);
}
stringBuilder.append(hv);
}
return stringBuilder.toString();
}
/**
* @param by
* @return 接收字節數據並轉爲16進制字符串
*/
public static String receiveHexToString(byte[] by) {
try {
/*io.netty.buffer.WrappedByteBuf buf = (WrappedByteBuf)msg;
ByteBufInputStream is = new ByteBufInputStream(buf);
byte[] by = input2byte(is);*/
String str = bytes2Str(by);
str = str.toLowerCase();
return str;
} catch (Exception ex) {
ex.printStackTrace();
System.out.println("接收字節數據並轉爲16進制字符串異常");
}
return null;
}
/**
* "7dd",4,'0'==>"07dd"
* @param input 需要補位的字符串
* @param size 補位後的最終長度
* @param symbol 按symol補充 如'0'
* @return
* N_TimeCheck中用到了
*/
public static String fill(String input, int size, char symbol) {
while (input.length() < size) {
input = symbol + input;
}
return input;
}
}
9、配置文件ProducerConfig
/**
* @Author: Liu Yue
* @Descripition:
* @Date; Create in 2020/11/2 10:27
**/
@Data
@Slf4j
@Configuration
@Component
@ConfigurationProperties(prefix = "rocketmq.producer")
@ToString
public class ProducerConfig {
private String namesrvaddr;
private String groupname;
private String topic;
private String tags;
}
yml配置
rocketmq:
producer:
namesrvaddr: 192.168.2.0:9876
groupname: base_group_syncMsg
topic: base_topic
tags: base_tags
10、MessageController
/**
* @Author: Liu Yue
* @Descripition:修改服務器的閾值
* @Date; Create in 2020/11/4 10:18
**/
@RestController
@RequestMapping("/fegin/server")
public class MessageController {
@Resource
MyWebSocketHandler myWebSocketHandler;
@GetMapping("/send/{id}/{thresholdval}")
public String send(@PathVariable("id") String id, @PathVariable("thresholdval")String thresholdval){
String msg = "FD FE "+id+" 00 00 "+ thresholdval;
myWebSocketHandler.sendAllMessage(msg,"SUCCESS");
return "SUCCESS";
}
}
11、設置
http://localhost:9005/rocketmq-producer/fegin/server/send/01/FB
三個功能,1、netty監聽服務;2、rocketMQ 生產者;3、設置閾值MessageController。下一題,使用fegin客戶端。