手写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;
}
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 25岁的心里话
· 闲置电脑爆改个人服务器(超详细) #公网映射 #Vmware虚拟网络编辑器
· 零经验选手,Compose 一天开发一款小游戏!
· 通过 API 将Deepseek响应流式内容输出到前端
· AI Agent开发,如何调用三方的API Function,是通过提示词来发起调用的吗
2020-12-28 数据库无限层级分类设计