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

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

 

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

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 @ 2020-10-29 15:46  和平鸽  阅读(158)  评论(0编辑  收藏  举报