Springboot2整合RocketMQ、Netty

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客戶端。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章