手写MQ消息中间件(基于netty实现)
通讯协议,什么是协议?
如:生产者投递消息到mq服务器之间的通讯如何定义格式就叫协议,(mq协议是amqp协议,是mq作者自己起的通讯协议名称)。
流程图
实现流程:
1.生产者把消息投递到netty服务,消费者自己取。
2.生产者把消息投递到netty服务,自动发送给消费者。
目录结构
目录
MarshallingCodeCFactory
jboss解码器 二进制转对象、对象转二进制
- package com.mayikt.utils;
-
- import io.netty.handler.codec.marshalling.*;
- import org.jboss.marshalling.MarshallerFactory;
- import org.jboss.marshalling.Marshalling;
- import org.jboss.marshalling.MarshallingConfiguration;
-
- /**
- * jboss解码器
- * 二进制转对象、对象转二进制
- */
- public final class MarshallingCodeCFactory {
-
- /**
- * 创建Jboss Marshalling解码器MarshallingDecoder
- * @return MarshallingDecoder
- */
- public static MarshallingDecoder buildMarshallingDecoder() {
- //首先通过Marshalling工具类的精通方法获取Marshalling实例对象 参数serial标识创建的是java序列化工厂对象。
- final MarshallerFactory marshallerFactory = Marshalling.getProvidedMarshallerFactory("serial");
- //创建了MarshallingConfiguration对象,配置了版本号为5
- final MarshallingConfiguration configuration = new MarshallingConfiguration();
- configuration.setVersion(5);
- //根据marshallerFactory和configuration创建provider
- UnmarshallerProvider provider = new DefaultUnmarshallerProvider(marshallerFactory, configuration);
- //构建Netty的MarshallingDecoder对象,俩个参数分别为provider和单个消息序列化后的最大长度
- MarshallingDecoder decoder = new MarshallingDecoder(provider, 1024 * 1024 * 1);
- return decoder;
- }
-
- /**
- * 创建Jboss Marshalling编码器MarshallingEncoder
- * @return MarshallingEncoder
- */
- public static MarshallingEncoder buildMarshallingEncoder() {
- final MarshallerFactory marshallerFactory = Marshalling.getProvidedMarshallerFactory("serial");
- final MarshallingConfiguration configuration = new MarshallingConfiguration();
- configuration.setVersion(5);
- MarshallerProvider provider = new DefaultMarshallerProvider(marshallerFactory, configuration);
- //构建Netty的MarshallingEncoder对象,MarshallingEncoder用于实现序列化接口的POJO对象序列化为二进制数组
- MarshallingEncoder encoder = new MarshallingEncoder(provider);
- return encoder;
- }
-
- }
NettyMQServer
netty服务器 - MQ服务器
- package com.mayikt.mq;
-
- import com.mayikt.handler.NettyMQServerHandler;
- import com.mayikt.utils.MarshallingCodeCFactory;
- import io.netty.bootstrap.ServerBootstrap;
- import io.netty.channel.ChannelFuture;
- import io.netty.channel.ChannelInitializer;
- import io.netty.channel.nio.NioEventLoopGroup;
- import io.netty.channel.socket.SocketChannel;
- import io.netty.channel.socket.nio.NioServerSocketChannel;
-
- /**
- * netty服务器 - MQ服务器
- */
- public class NettyMQServer {
-
- public static void main(String[] args) {
- start(5872);
- }
-
- public static void start(int port) {
- /**
- * 客户端创建两个线程池组分别为 boss线程组和工作线程组
- */
- // 1.用于接受客户端连接的请求 (并没有处理请求)
- NioEventLoopGroup bossGroup = new NioEventLoopGroup();
- // 2.用于处理客户端连接的读写操作
- NioEventLoopGroup workGroup = new NioEventLoopGroup();
- // 3.用于创建我们的ServerBootstrap
- ServerBootstrap serverBootstrap = new ServerBootstrap();
- serverBootstrap.group(bossGroup, workGroup).channel(NioServerSocketChannel.class)
- .childHandler(new ChannelInitializer<SocketChannel>() {
- @Override
- protected void initChannel(SocketChannel ch) throws Exception {
- // 解决netty可以支持传输对象
- ch.pipeline().addLast(MarshallingCodeCFactory.buildMarshallingDecoder());
- ch.pipeline().addLast(MarshallingCodeCFactory.buildMarshallingEncoder());
- ch.pipeline().addLast(new NettyMQServerHandler());
-
- }
- });
- // 绑定我们的端口号码
- try {
- // 绑定端口号,同步等待成功
- ChannelFuture future = serverBootstrap.bind(port).sync();
- System.out.println("MQ服务器启动成功:" + port);
- // 等待服务器监听端口
- future.channel().closeFuture().sync();
- } catch (Exception e) {
- e.printStackTrace();
-
- } finally {
- // 优雅的关闭连接
- bossGroup.shutdownGracefully();
- workGroup.shutdownGracefully();
- }
- }
-
-
- }
NettyMQProducer
netty客户端 - 生产者投递消息
- package com.mayikt.mq;
- import com.mayikt.entity.DeliveryInfoEntity;
- import com.mayikt.handler.NettyMQProducerHandler;
- import com.mayikt.handler.NettyMQServerHandler;
- import com.mayikt.utils.MarshallingCodeCFactory;
- import io.netty.bootstrap.Bootstrap;
- import io.netty.bootstrap.ServerBootstrap;
- import io.netty.channel.ChannelFuture;
- import io.netty.channel.ChannelInitializer;
- import io.netty.channel.nio.NioEventLoopGroup;
- import io.netty.channel.socket.SocketChannel;
- import io.netty.channel.socket.nio.NioServerSocketChannel;
- import io.netty.channel.socket.nio.NioSocketChannel;
-
- import java.net.InetSocketAddress;
-
- /**
- * netty客户端 - 消费者
- */
- public class NettyMQProducer {
-
- // netty服务端ip、端口
- private static final String host = "127.0.0.1";
- private static final int port = 5872;
-
- public static void main(String[] args) {
- sendMsg("mayikt", "每特123");
- System.out.println("111");
- sendMsg("mayikt", "每特123");
- }
-
- public static void sendMsg(String queueName, String msg) {
- //创建nioEventLoopGroup
- NioEventLoopGroup group = new NioEventLoopGroup();
- Bootstrap bootstrap = new Bootstrap();
- bootstrap.group(group).channel(NioSocketChannel.class)
- .remoteAddress(new InetSocketAddress(host, port))
- .handler(new ChannelInitializer<SocketChannel>() {
- @Override
- protected void initChannel(SocketChannel ch) throws Exception {
- ch.pipeline().addLast(MarshallingCodeCFactory.buildMarshallingDecoder());
- ch.pipeline().addLast(MarshallingCodeCFactory.buildMarshallingEncoder());
- ch.pipeline().addLast(new NettyMQProducerHandler());
- }
- });
- try {
- // 发起同步连接
- ChannelFuture sync = bootstrap.connect().sync();
- DeliveryInfoEntity deliveryInfoEntity = new DeliveryInfoEntity(msg, queueName,
- true);
- // 发送数据给netty服务端
- sync.channel().writeAndFlush(deliveryInfoEntity);
- sync.channel().closeFuture().sync();
- } catch (Exception e) {
-
- } finally {
- group.shutdownGracefully();
- }
- }
-
- }
NettyMQConsumer
netty客户端 - 消费者接收指定队列消息
- package com.mayikt.mq;
-
- import com.mayikt.entity.DeliveryInfoEntity;
- import com.mayikt.handler.NettyMQConsumerHandler;
- import com.mayikt.utils.MarshallingCodeCFactory;
- import io.netty.bootstrap.Bootstrap;
- import io.netty.channel.ChannelFuture;
- import io.netty.channel.ChannelInitializer;
- import io.netty.channel.nio.NioEventLoopGroup;
- import io.netty.channel.socket.SocketChannel;
- import io.netty.channel.socket.nio.NioSocketChannel;
-
- import java.net.InetSocketAddress;
-
- /**
- * netty客户端 - 生产者投递消息
- */
- public class NettyMQConsumer {
-
- // netty服务端ip、端口
- private static final String host = "127.0.0.1";
- private static final int port = 5872;
-
- private static String queueName = "mayikt";
-
- public static void main(String[] args) {
- sendMsg("mayikt",null);
- }
-
- public static void sendMsg(String queueName,String msg) {
- //创建nioEventLoopGroup
- NioEventLoopGroup group = new NioEventLoopGroup();
- Bootstrap bootstrap = new Bootstrap();
- bootstrap.group(group).channel(NioSocketChannel.class)
- .remoteAddress(new InetSocketAddress(host, port))
- .handler(new ChannelInitializer<SocketChannel>() {
- @Override
- protected void initChannel(SocketChannel ch) throws Exception {
- ch.pipeline().addLast(MarshallingCodeCFactory.buildMarshallingDecoder());
- ch.pipeline().addLast(MarshallingCodeCFactory.buildMarshallingEncoder());
- ch.pipeline().addLast(new NettyMQConsumerHandler());
- }
- });
- try {
- // 发起同步连接
- ChannelFuture sync = bootstrap.connect().sync();
- DeliveryInfoEntity deliveryInfoEntity = new DeliveryInfoEntity(null, queueName,
- false);
- // 发送数据给netty服务端
- sync.channel().writeAndFlush(deliveryInfoEntity);
- sync.channel().closeFuture().sync();
- } catch (Exception e) {
-
- } finally {
- group.shutdownGracefully();
- }
- }
-
-
- }
NettyMQServerHandler
netty服务Handler mq服务Handler
- package com.mayikt.handler;
-
- import com.mayikt.entity.DeliveryInfoEntity;
- import io.netty.channel.ChannelHandlerContext;
- import io.netty.channel.SimpleChannelInboundHandler;
- import org.apache.commons.lang3.StringUtils;
-
- import java.util.*;
-
- /**
- * netty服务Handler
- */
- public class NettyMQServerHandler extends SimpleChannelInboundHandler<DeliveryInfoEntity> {
- /**
- * mq存放所有的队列 key:队列名称,value:队列数据内容
- */
- private static Map<String, Queue> queues = new HashMap<String, Queue>();
-
- /**
- * 存放我们消费者连接(点对点) key:队列名称,value:管道
- */
- private static Map<String, ChannelHandlerContext> ctxs = new HashMap<String, ChannelHandlerContext>();
-
- /**
- * Netty的服务器端接受 客户端消息 MQ服务器端
- *
- * @param ctx 管道
- * @param dInfo 客户端消息封装的Entity
- * @throws Exception
- */
- @Override
- protected void channelRead0(ChannelHandlerContext ctx, DeliveryInfoEntity dInfo) throws Exception {
- // 队列名称
- String queueName = dInfo.getQueueName();
- if (StringUtils.isEmpty(queueName)) {
- return;
- }
- Boolean connType = dInfo.getConnType();
- // 如果是为true的情况下 为生产者角色
- if (connType) {
- // 处理生产者角色(投递消息)
- procucterService(queueName, dInfo.getMsg(), ctx);
- return;
- }
- consumer(queueName, ctx);
-
- }
-
- private void procucterService(String queueName, String msg, ChannelHandlerContext channelHandlerContext) {
- Queue queue = queues.get(queueName);
- if (queue == null) {
- // 如果队列不存在的情况下,就创建
- queue = new LinkedList();
- queues.put(queueName, queue);
- }
- // 将消息缓存到队列中
- queue.offer(msg);
-
- // ack应答(消息是否投递成功)
- channelHandlerContext.writeAndFlush("“ack应答”消息投递成功");
-
- // 主动将消息推送给消费者
- ChannelHandlerContext ctx = ctxs.get(queueName);
- if (ctx != null) {
- ctx.writeAndFlush(queue.poll());
- }
-
- }
-
- /**
- * 消费和mq建立连接主动拉取消息
- */
- private void consumer(String queueName, ChannelHandlerContext ctx) {
- Queue queue = queues.get(queueName);
- if (queue == null) {
- return;
- }
- // 获取队列中消息
- Object poll = queue.poll();
- ctx.writeAndFlush(poll);
-
- // 将消费者连接存放到集合中
- ctxs.put(queueName, ctx);
- }
-
- }
NettyMQProducerHandler
生产者Handler
- package com.mayikt.handler;
-
- import io.netty.channel.ChannelHandlerContext;
- import io.netty.channel.SimpleChannelInboundHandler;
-
-
- public class NettyMQProducerHandler extends SimpleChannelInboundHandler {
-
- protected void channelRead0(ChannelHandlerContext channelHandlerContext, Object ack) throws Exception {
- System.out.println(ack);
- // 关闭连接
- channelHandlerContext.close();
- }
- }
NettyMQConsumerHandler
消费者Handler
- package com.mayikt.handler;
-
- import io.netty.channel.ChannelHandlerContext;
- import io.netty.channel.SimpleChannelInboundHandler;
-
- /**
- * 消费者Handler
- */
- public class NettyMQConsumerHandler extends SimpleChannelInboundHandler {
- @Override
- protected void channelRead0(ChannelHandlerContext ctx, Object msg) throws Exception {
- System.out.println("消费者获取生产者消息:" + msg);
- }
- }
DeliveryInfoEntity
传输协议的封装Entity - 传输协议如:amqp
- package com.mayikt.entity;
-
- import lombok.Data;
-
- import java.io.Serializable;
-
- /**
- * 传输协议封装Entity - 传输协议如:amqp
- */
- @Data
- public class DeliveryInfoEntity implements Serializable {
-
- /**
- * 发送消息内容
- */
- private String msg;
-
- /**
- * 队列名称
- */
- private String queueName;
-
- /**
- * true 生产者投递消息
- * false 消费者获取消息
- */
- private Boolean connType;
-
- public DeliveryInfoEntity(String msg, String queueName, Boolean connType) {
- this.msg = msg;
- this.queueName = queueName;
- this.connType = connType;
- }
- }