分布式消息中间件_2 基于Netty手写MQ分布式消息中间件

                                        分布式消息中间件                             

                                 基于Netty手写MQ分布式消息中间件

                                                                                                                                                                                                田超凡

                                                                                                                                                                                           20191212

转载请注明原作者

1 实现思路

(1) 定义Netty序列化编解码器工具类-MarshallingCoderFactory,基于工厂方法模式创建编码解码器

(2) 定义常量类,区分Netty客户端是生产者还是消费者、MQ服务器地址和端口

(3) 实现MQ服务器端-MQBroker,基于Netty搭建服务器,绑定编码解码器和事件驱动处理器

(4) 实现MQ服务器端事件驱动处理器,使用全局容器存放队列名称和队列的映射关系,队列和订阅的消费者之间的映射关系,

在接收到客户端发送的请求数据之后,根据客户端类型不同分开处理:

如果是生产者角色,那么就根据队列名称获取队列(不存在就创建对应的队列),然后需要把发布的消息放入队列,同时基于观察者模式通知所有订阅该消息队列的消费者进行消费,消费成功则做出队列操作

如果是消费者角色,那么根据队列名称获取MQ服务器缓存的队列消息,同时在消费者和订阅的消息队列之间建立映射关系并存入MQ服务器,最后把获取到的队列消息返回响应给消费者

(5) 实现生产者Netty客户端,发送队列名称、队列消息、客户端类型参数到MQ消息中间件服务器

(6) 实现消费者Netty客户端,发送队列名称、客户端类型参数到MQ消息中间件容器,绑定事件驱动处理器

(7) 实现消费者事件驱动处理器,在监听到MQ消息中间件服务器返回的响应消息之后,对获取到的消息进行进一步业务处理。

2 手写MQ消息中间件代码实现

package com.tcf.mq.source.util;

/***
 * TODO TCF 常量类
 * @author Hasee
 *
 */
public class Const {

    //TODO TCF MQ消息中间件-服务器运行主机和端口号
    public static class MQBrokerInfo
    {
        //TODO TCF 主机ip
        public static final String HOST="127.0.0.1";
        
        //TODO TCF 主机端口号
        public static final Integer PORT=8080;
    }
    
    //TODO TCF 生产者和消费者类型
    public static class NettyClientType
    {
        //TODO TCF 生产者
        public static final String PRODUCER="1";
        
        //TODO TCF 消费者
        public static final String CONSUMER="2";
    }
    
    //TODO TCF MQ队列名称
    public static class MQQueueNames
    {
        //TODO TCF 队列1
        public static final String QUEUE_FIRST="queue_first";
    }
}

 

package com.tcf.mq.source.util;

import org.jboss.marshalling.MarshallerFactory;
import org.jboss.marshalling.Marshalling;
import org.jboss.marshalling.MarshallingConfiguration;

import io.netty.handler.codec.marshalling.DefaultMarshallerProvider;
import io.netty.handler.codec.marshalling.DefaultUnmarshallerProvider;
import io.netty.handler.codec.marshalling.MarshallerProvider;
import io.netty.handler.codec.marshalling.MarshallingDecoder;
import io.netty.handler.codec.marshalling.MarshallingEncoder;
import io.netty.handler.codec.marshalling.UnmarshallerProvider;

/***
 * TODO TCF Netty内置序列化编码解码器
 * 
 * @author Hasee
 *
 */
public class MarshallingCoderFactory {

    /**
     * 创建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;
    }

}
 

package com.tcf.mq.source.entity;

import java.io.Serializable;

/***
 * TODO TCF 发送消息信息类
 * @author Hasee
 *
 */
public class DevilyEntity implements Serializable{

    /**
     * 
     */
    private static final long serialVersionUID = -4668828944906705282L;

    //TODO TCF 队列名称
    private String queueName;
    
    //TODO TCF 发送的消息
    private String message;
    
    //TODO TCF 中间件类型:1 生产者 2 消费者
    private String connectionType;
    
    //TODO TCF 构造柱入
    public DevilyEntity(String queueName,String message,String connectionType)
    {
        this.queueName=queueName;
        this.message=message;
        this.connectionType=connectionType;
    }
    
    //TODO TCF 默认无参构造
    public DevilyEntity()
    {
        
    }
    
    public String getQueueName() {
        return queueName;
    }
    public void setQueueName(String queueName) {
        this.queueName = queueName;
    }
    
    public String getMessage() {
        return message;
    }
    public void setMessage(String message) {
        this.message = message;
    }
    
    public String getConnectionType() {
        return connectionType;
    }
    public void setConnectionType(String connectionType) {
        this.connectionType = connectionType;
    }
    
}
 

package com.tcf.mq.source.broker;

import java.net.InetSocketAddress;

import com.tcf.mq.source.handle.MQBrokerEventHandle;
import com.tcf.mq.source.util.Const;
import com.tcf.mq.source.util.MarshallingCoderFactory;

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;

/***
 * TODO TCF 消息中间件服务器
 * @author Hasee
 *
 */
public class MQBroker {
    
    public static void main(String[] args)
    {
        //TODO TCF Netty初始化服务器
        NioEventLoopGroup bossGroup=new NioEventLoopGroup();
        NioEventLoopGroup workGroup=new NioEventLoopGroup();
        
        //TODO TCF 初始化Netty服务器
        ServerBootstrap serverBootstrap=new ServerBootstrap();
        serverBootstrap.group(bossGroup,workGroup)
                       .channel(NioServerSocketChannel.class)
                       .childHandler(new ChannelInitializer<SocketChannel>() {
                        
                           @Override
                           protected void initChannel(SocketChannel serverSocketChannel) throws Exception
                           {
                               //TODO TCF 绑定事件驱动处理器和编码解码器
                               serverSocketChannel.pipeline().addLast(MarshallingCoderFactory.buildMarshallingEncoder());
                               serverSocketChannel.pipeline().addLast(MarshallingCoderFactory.buildMarshallingDecoder());
                               
                               //TODO TCF 绑定MQ消息中间件服务器事件驱动处理器
                               serverSocketChannel.pipeline().addLast(new MQBrokerEventHandle());
                           }
                       });
        
        //TODO TCF 初始化通道,运行MQ中间件服务器
        try
        {
            ChannelFuture channelFuture=serverBootstrap.bind(new InetSocketAddress(Const.MQBrokerInfo.HOST,Const.MQBrokerInfo.PORT));
            
            System.out.println("====MQ消息中间件服务器启动成功====");
            
            channelFuture.channel().closeFuture().sync();
        }
        catch(Exception e)
        {
            e.printStackTrace();
        }
        finally
        {
            //TODO TCF 关闭Netty线程组,释放线程资源
            bossGroup.shutdownGracefully();
            workGroup.shutdownGracefully();
        }
    }
}
 

package com.tcf.mq.source.handle;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Queue;
import java.util.concurrent.LinkedBlockingQueue;
import org.apache.commons.lang3.StringUtils;
import com.tcf.mq.source.entity.DevilyEntity;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;

/***
 * TODO TCF MQ服务器事件驱动处理器
 * @author Hasee
 *
 */
public class MQBrokerEventHandle extends SimpleChannelInboundHandler<DevilyEntity> {

    //TODO TCF 生产者发布的消息
    private static Map<String,Queue<DevilyEntity>> queueMap=new HashMap<String,Queue<DevilyEntity>>();
    
    //TODO TCF 消息队列和订阅的消费者
    private static Map<String,List<ChannelHandlerContext>> consumerMap=new HashMap<String,List<ChannelHandlerContext>>();
    
    /***
     * TODO TCF 接收到客户端发送的消息
     * TODO TCF 生产者则读取消息存入缓冲区,通知消费者消费
     * TODO TCF 消费者则直接从缓冲区读取数据
     */
    @Override
    protected void channelRead0(ChannelHandlerContext ctx,DevilyEntity devilyEntity) throws Exception
    {
        //TODO TCF 消息队列名称
        String queueName=devilyEntity.getQueueName();
        
        if(StringUtils.isNotEmpty(queueName))
        {
            //TODO TCF 生产者则直接把投递的消息放入队列并通知消费者消费,如果是消费者则关联MQ中间件服务器上下文并直接消费
            String connectionType=devilyEntity.getConnectionType();
            
            if(StringUtils.isNotEmpty(connectionType))
            {
                if(connectionType.equals("1"))
                {
                    //TODO TCF 生产者
                    //TODO TCF 根据队列名称获取队列,如果队列为空则创建队列放入集合,否则直接投递消息
                    Queue<DevilyEntity> queue=queueMap.get(queueName);
                    
                    if(queue==null)
                    {
                        queue=new LinkedBlockingQueue<DevilyEntity>();
                        queueMap.put(queueName,queue);
                    }
                    
                    //TODO TCF 消息投递到MQ队列,入队列
                    queue.offer(devilyEntity);
                    
                    System.out.println("===>生产者投递的消息:"+devilyEntity.getMessage());
                    
                    //TODO TCF 通知消费者消费,获取消费者绑定的MQ中间件上下文
                    List<ChannelHandlerContext> list=consumerMap.get(queueName);
                    
                    if(list!=null && list.size()>0)
                    {
                        //TODO TCF 投递消息的时候消费者已存在,通知消费者消费,直接出队列
                        DevilyEntity data=queue.poll();
                        
                        System.out.println("发现"+list.size()+"个订阅了当前队列的消费者,通知消费者消费");
                        for(ChannelHandlerContext context:list)
                        {
                            if(context!=null)
                            {
                                context.writeAndFlush(data);
                            }
                        }
                    }
                }
                else
                {
                    //TODO TCF 消费者
                    //TODO TCF 根据队列名称获取需要消费的队列
                    Queue<DevilyEntity> queue=queueMap.get(queueName);
                    
                    //TODO TCF 建立消费者和消费队列之间的关联关系
                    //TODO TCF 消费者订阅消息队列
                    if(consumerMap.containsKey(queueName))
                    {
                        consumerMap.get(queueName).add(ctx);
                    }
                    else
                    {
                        List<ChannelHandlerContext> list=new ArrayList<ChannelHandlerContext>();
                        list.add(ctx);
                        consumerMap.put(queueName,list);
                    }
                    
                    if(queue!=null && queue.size()>0)
                    {
                        //TODO TCF 直接消费,出队列,返回获取到的消息队列到消费者,消费成功
                        devilyEntity=queue.poll();
                        
                        if(devilyEntity!=null)
                        {
                            System.out.println("消费者获取到缓存中的消息===>"+devilyEntity.getMessage()+",队列剩余消息数量:"+queue.size());
                            
                            //TODO TCF 把获取的到的队列消息返回给消费者
                            ctx.writeAndFlush(devilyEntity);
                        }
                    }
                    else
                    {
                        System.out.println("没有获取到队列名称为:"+queueName+"的消息,请先使用生产者发布消息");
                    }
                }
            }
        }
    }
    
}
 

package com.tcf.mq.source.producer;

import java.net.InetSocketAddress;

import com.tcf.mq.source.entity.DevilyEntity;
import com.tcf.mq.source.util.Const;
import com.tcf.mq.source.util.MarshallingCoderFactory;

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;

/***
 * TODO TCF MQ消息中间件-生产者客户端,投递消息到MQ
 * @author Hasee
 *
 */
public class MQProducer {

    public static void main(String[] args) 
    {
        //TODO TCF 初始化Netty客户端
        NioEventLoopGroup workGroup=new NioEventLoopGroup();
        
        Bootstrap bootstrap=new Bootstrap();
        bootstrap.group(workGroup)
                 .channel(NioSocketChannel.class)
                 .remoteAddress(new InetSocketAddress(Const.MQBrokerInfo.HOST,Const.MQBrokerInfo.PORT))
                 .handler(new ChannelInitializer<SocketChannel>() {
                     
                     @Override
                     protected void initChannel(SocketChannel socketChannel) throws Exception
                     {
                         //TODO TCF 绑定编码解码器
                         socketChannel.pipeline().addLast(MarshallingCoderFactory.buildMarshallingEncoder());
                         socketChannel.pipeline().addLast(MarshallingCoderFactory.buildMarshallingDecoder());
                     }
                     
                });
        
        //TODO TCF 启动Netty客户端,投递消息到MQ
        try
        {
            ChannelFuture channelFuture=bootstrap.connect().sync();
            
            System.out.println("生产者启动成功,已投递消息");
            
            //TODO TCF 生产者需要投递的消息
            String message="***测试消息***";
            
            DevilyEntity devilyEntity=new DevilyEntity(Const.MQQueueNames.QUEUE_FIRST,message,Const.NettyClientType.PRODUCER);
            
            //TODO TCF 生产者投递消息
            channelFuture.channel().writeAndFlush(devilyEntity);
            channelFuture.channel().closeFuture().sync();
        }
        catch(Exception e)
        {
            e.printStackTrace();
        }
        finally
        {
            //TODO TCF 关闭Netty线程组,释放占用的线程资源
            workGroup.shutdownGracefully();
        }
    }
}
 

package com.tcf.mq.source.consumer;

import java.net.InetSocketAddress;

import com.tcf.mq.source.entity.DevilyEntity;
import com.tcf.mq.source.handle.MQConsumerEventHandle;
import com.tcf.mq.source.util.Const;
import com.tcf.mq.source.util.MarshallingCoderFactory;

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;

/***
 * TODO TCF MQ消费者,连接到MQ消息中间件,根据队列名称获取队列消息
 * @author Hasee
 *
 */
public class MQConsumer {

    public static void main(String[] args) 
    {
        //TODO TCF 初始化消费者Netty客户端
        NioEventLoopGroup workGroup=new NioEventLoopGroup();
        
        //TODO TCF 初始化Netty客户端信息,监听客户端初始化事件,绑定编解码器和事件驱动处理器
        Bootstrap bootstrap=new Bootstrap();
        bootstrap.group(workGroup)
                 .channel(NioSocketChannel.class)
                 .remoteAddress(new InetSocketAddress(Const.MQBrokerInfo.HOST,Const.MQBrokerInfo.PORT))
                 .handler(new ChannelInitializer<SocketChannel>() {
                     
                     //TODO TCF 初始化Netty客户端事件
                     @Override
                     protected void initChannel(SocketChannel socketChannel) throws Exception
                     {
                         //TODO TCF 绑定编解码器和事件驱动处理器
                         socketChannel.pipeline().addLast(MarshallingCoderFactory.buildMarshallingEncoder());
                         socketChannel.pipeline().addLast(MarshallingCoderFactory.buildMarshallingDecoder());
                         
                         //TODO TCF 绑定事件驱动处理器
                         socketChannel.pipeline().addLast(new MQConsumerEventHandle());
                     }
                });
        
        //TODO TCF 初始化Netty通道,连接到MQ消息中间件,指定需要消费的队列名称
        try
        {
            ChannelFuture channelFuture=bootstrap.connect().sync();
            
            System.out.println("消费者启动成功");
            
            //TODO TCF 需要传输的对象信息,指定需要获取的消息队列名称
            DevilyEntity devilyEntity=new DevilyEntity(Const.MQQueueNames.QUEUE_FIRST,null,Const.NettyClientType.CONSUMER);
            
            //TODO TCF 连接到MQ消息中间件,发送消息
            channelFuture.channel().writeAndFlush(devilyEntity);
            channelFuture.channel().closeFuture().sync();
        }
        catch(Exception e)
        {
            e.printStackTrace();
        }
        finally
        {
            //TODO TCF 关闭Netty线程组,释放资源
            workGroup.shutdownGracefully();
        }
    }
}
 

package com.tcf.mq.source.handle;

import com.tcf.mq.source.entity.DevilyEntity;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;

/***
 * TODO TCF MQ消费者-Netty客户端事件驱动处理器
 * @author Hasee
 *
 */
public class MQConsumerEventHandle extends SimpleChannelInboundHandler<DevilyEntity> {

    //TODO TCF 接收到MQ消息中间件返回的响应信息
    @Override
    protected void channelRead0(ChannelHandlerContext ctx,DevilyEntity devilyEntity) throws Exception
    {
        if(devilyEntity!=null)
        {
            //TODO TCF 获取到的消息中间件响应信息
            String message=devilyEntity.getMessage();
            
            System.out.println("消费者接收到MQ消息中间件中的消息:"+message);
        }
    }
    
}
 

转载请注明原作者

发布了100 篇原创文章 · 获赞 10 · 访问量 7万+

猜你喜欢

转载自blog.csdn.net/qq_30056341/article/details/103501790