从零开始实现自己的消息框架
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")); } }
7. 调试示例
8.https://gitee.com/cailun-hx/mq