从零开始实现自己的消息框架
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

浙公网安备 33010602011771号