手写MQ消息中间件(基于netty实现)


通讯协议,什么是协议?

如:生产者投递消息到mq服务器之间的通讯如何定义格式就叫协议,(mq协议是amqp协议,是mq作者自己起的通讯协议名称)。

流程图

实现流程:

1.生产者把消息投递到netty服务,消费者自己取。

2.生产者把消息投递到netty服务,自动发送给消费者。


目录结构

目录

流程图

目录结构

MarshallingCodeCFactory

NettyMQServer

NettyMQProducer

NettyMQConsumer

NettyMQServerHandler

NettyMQProducerHandler

NettyMQConsumerHandler

DeliveryInfoEntity


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;
    }
}

posted @   DiligentCoder  阅读(600)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 25岁的心里话
· 闲置电脑爆改个人服务器(超详细) #公网映射 #Vmware虚拟网络编辑器
· 零经验选手,Compose 一天开发一款小游戏!
· 通过 API 将Deepseek响应流式内容输出到前端
· AI Agent开发,如何调用三方的API Function,是通过提示词来发起调用的吗
历史上的今天:
2020-12-28 数据库无限层级分类设计
点击右上角即可分享
微信分享提示