从零开始实现自己的消息框架

1. 分为消息生产者和消息消费者,生产者接收消息请求,保存到文件中,消费者接收消费消息的请求,获取消息。没有做消息的删除,消息可用重复消费(以后完善吧)。

 

2. 使用了NETTY 框架作为消息接收的框架,

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
package com.lz.nb.mq.producer;
 
import org.apache.log4j.Logger;
 
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
 
public class ProducerNettyServer {
     
    private static Logger logger = Logger.getLogger(ProducerNettyServer.class);
     
    
     
      
 
     public void bind(int port) throws Exception {
           
            EventLoopGroup bossGroup = new NioEventLoopGroup(); //bossGroup就是parentGroup,是负责处理TCP/IP连接的
            EventLoopGroup workerGroup = new NioEventLoopGroup(); //workerGroup就是childGroup,是负责处理Channel(通道)的I/O事件
      
            ServerBootstrap sb = new ServerBootstrap();
            sb.group(bossGroup, workerGroup)
                .channel(NioServerSocketChannel.class)
                .option(ChannelOption.SO_BACKLOG, 128) //初始化服务端可连接队列,指定了队列的大小128
                .childOption(ChannelOption.SO_KEEPALIVE, true) //保持长连接
                .childHandler(new ChannelInitializer<SocketChannel>() {  // 绑定客户端连接时候触发操作
                    @Override
                    protected void initChannel(SocketChannel sh) throws Exception {
                        sh.pipeline()
                         .addLast(new ProducerDecoder())  //解码
                        .addLast(new ProducerHandler())  // 业务处理
                        .addLast(new ProducerEncoder()) ; // 编码
                    }
                });
            //绑定监听端口,调用sync同步阻塞方法等待绑定操作完
            ChannelFuture future = sb.bind(port).sync();
      
            if (future.isSuccess()) {
                System.out.println("服务端启动成功");
            } else {
                System.out.println("服务端启动失败");
                future.cause().printStackTrace();
                bossGroup.shutdownGracefully(); //关闭线程组
                workerGroup.shutdownGracefully();
            }
      
            //成功绑定到端口之后,给channel增加一个 管道关闭的监听器并同步阻塞,直到channel关闭,线程才会往下执行,结束进程。
            future.channel().closeFuture().sync();
      
        }
     
}

  3. 定义了消息的协议

复制代码
package com.lz.nb.mq.model;

import java.nio.ByteOrder;
import com.lz.nb.mq.util.ByteToIntUtil;

public class MessageProtocol {

    public final byte HEAD = 0x12;
    
    private byte  REQTYPE;
    
    private byte[] TOPICID = new byte[4];
    
    private byte[] LEN = new byte[4];
    
    private byte[] BODY;
    
    public MessageProtocol(byte reqType,Integer topicId,int length,byte[]body){
        this.REQTYPE = reqType;
        this.TOPICID = ByteToIntUtil.int2Bytes(topicId,ByteOrder.BIG_ENDIAN);
        this.LEN = ByteToIntUtil.int2Bytes(length,ByteOrder.BIG_ENDIAN);
        this.BODY = body;
    }
    
    public String toHexString() {
        byte[] dest = new byte[6+ LEN.length + BODY.length];
        dest[0] =HEAD;
        dest[1] =REQTYPE;
        for(int i=0;i<4;i++) {
            dest[i+2] =TOPICID[i];
        }
        System.arraycopy(LEN, 0, dest, 6, LEN.length);
        System.arraycopy(BODY, 0, dest,10, BODY.length);
        StringBuilder sb = new StringBuilder();
        for(byte b: dest) {
            if(Math.abs(b&0xff)<16) {
                sb.append("0");
            }
            sb.append(Integer.toHexString(b&0xff));
        }
        return sb.toString().toUpperCase();
    }
   
    
}
复制代码

4. 使用LengthFieldBasedFrameDecoder 作为解码器,进行消息的解码。

复制代码
package com.lz.nb.mq.producer;

import org.apache.log4j.Logger;

import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.LengthFieldBasedFrameDecoder;

/**
 */

public class ProducerDecoder extends LengthFieldBasedFrameDecoder {
    
    private static Logger log = Logger.getLogger(ProducerDecoder.class);
    
    
    public static java.nio.ByteOrder ByteOrder = java.nio.ByteOrder.LITTLE_ENDIAN;
    
    public static int maxFrameLength = Integer.MAX_VALUE;

    public static int lengthFieldOffset = 6;
    
    public static int lengthFieldLength = 4;
    
    public static int lengthAdjustment = 0;
    
    public static int initialBytesToStrip = 0;
    
    public static boolean failFast = false;

    
    public ProducerDecoder() {
        super(maxFrameLength, lengthFieldOffset, lengthFieldLength,lengthAdjustment,initialBytesToStrip,failFast);
    }

    @Override
    protected Object decode(ChannelHandlerContext ctx, ByteBuf in) throws Exception {
        log.info("进入解码器");
        return super.decode(ctx, in);
    }
 
    
    
}
复制代码

5.MessageToByteEncoder<String>  作为调用返回值的编码器。

复制代码
package com.lz.nb.mq.producer;

import org.apache.log4j.Logger;

import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.MessageToByteEncoder;

public class ProducerEncoder  extends  MessageToByteEncoder<String>   {
    
    private static Logger log = Logger.getLogger(ProducerEncoder.class);

    @Override
    protected void encode(ChannelHandlerContext ctx, String msg, ByteBuf out) throws Exception {
        log.info("进入编码器");
        log.info("String msg, ByteBuf out" + msg + new String(out.array()));
        
    }
 
    
}
复制代码

6.采用bson格式存储消息到文件中。

复制代码
package com.lz.nb.mq.util;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.HashMap;

import javax.json.JsonObject;
import javax.json.JsonString;
import javax.json.JsonValue;
import javax.json.JsonValue.ValueType;

import org.apache.log4j.Logger;
import org.yuanheng.cookjson.BsonFixLength;
import org.yuanheng.cookjson.BsonGenerator;
import org.yuanheng.cookjson.CookJsonParser;
import org.yuanheng.cookjson.CookJsonProvider;
import org.yuanheng.cookjson.value.CookJsonObject;

import com.alibaba.fastjson.JSONObject;
import com.lz.nb.mq.model.Request;

public class BsonFileUtil {

    
    private static Logger log = Logger.getLogger(BsonFileUtil.class);
     
    public static final String CHAR_SET = "UTF-8";
    
    public static final String KEY = "obj";
    
    
    public static void writeJson2File(File file,byte[] bs) throws IOException {
        BsonGenerator g = new BsonGenerator (new FileOutputStream (file));
        g.setUseDouble (true);
        g.writeStartObject();
        String str = new String(bs,CHAR_SET);
        g.write(KEY,str);
        g.writeEnd();
        g.close();
        BsonFixLength.fix (file);
    }
    
     
    public static String readFile2Json(File file) throws FileNotFoundException {
            CookJsonProvider provider = new CookJsonProvider ();
            HashMap<String, Object> bsonConfig = new HashMap<String, Object> ();
            bsonConfig.put (CookJsonProvider.FORMAT, CookJsonProvider.FORMAT_BSON);
            CookJsonParser p = (CookJsonParser) provider.createParserFactory (bsonConfig).createParser (new FileInputStream (file));
            p.next ();
            JsonValue value = p.getValue ();
            CookJsonObject obj = (CookJsonObject)value;
            String jsonValue = obj.getString(KEY);
            log.info("jsonValue:" + jsonValue);
            return jsonValue;
    }
    
    
    public static void main(String[] args) throws IOException {
           JSONObject obj = new JSONObject();
           obj.put("name","张三");
           obj.put("age", 18);
           HashMap<String, Object> hashMap = new HashMap<String,Object>();
           hashMap.put("爱好", "play");
           obj.put("map",hashMap );
           String json =   obj.toJSONString();
           writeJson2File(new File("E:/test2.bson"), json.getBytes(CHAR_SET));
           String readFile2Json = readFile2Json(new File("E:/test2.bson"));
         
           
    }
    
}
View Code
复制代码

7. 调试示例

 8.https://gitee.com/cailun-hx/mq

posted @   和平鸽  阅读(164)  评论(0编辑  收藏  举报
编辑推荐:
· .NET Core 托管堆内存泄露/CPU异常的常见思路
· PostgreSQL 和 SQL Server 在统计信息维护中的关键差异
· C++代码改造为UTF-8编码问题的总结
· DeepSeek 解答了困扰我五年的技术问题
· 为什么说在企业级应用开发中,后端往往是效率杀手?
阅读排行:
· 10亿数据,如何做迁移?
· 推荐几款开源且免费的 .NET MAUI 组件库
· 清华大学推出第四讲使用 DeepSeek + DeepResearch 让科研像聊天一样简单!
· c# 半导体/led行业 晶圆片WaferMap实现 map图实现入门篇
· 易语言 —— 开山篇
点击右上角即可分享
微信分享提示