Java订阅/发布

介绍

一个简单的消息订阅/发布

git地址

https://gitee.com/ichiva/message-pub-sub.git

快速入门

  • 实例化一个异步的消息服务
MessageService messageService = new AsyncMessageService();
  • 订阅游戏部的消息
HashMap<String, Object> header = new HashMap<>();
header.put("department","game");
messageService.subscribe(header,System.out::println);
  • 向游戏部发送消息
Message message = new Message();
message.setContext("今晚吃鸡");
HashMap<String, Object> sendheader = new HashMap<>();
sendheader.put("department","game");
message.setHeader(sendheader);
messageService.publish(message);

实现细节

  • 定义消息体
@ToString
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Message {
    /**
     * 消息类型
     */
    private int type;
    /**
     * 消息头
     */
    private Map<String,Object> header = new HashMap<>();
    /**
     * 消息体
     */
    private String context;
}

  • 定义消息服务接口
public interface MessageService {
    /**
     * 发布消息
     * @param message 消息
     */
    void publish(Message message);

    /**
     * 订阅
     * @param header 头部
     * @param consumer 消费者
     * @return
     */
    String subscribe(Map<String,Object> header, Consumer<Message> consumer);

    /**
     * 取消订阅
     * @param subscribeId 订阅编号
     */
    void unsubscribe(String subscribeId);
}
  • 实现一个同步的消息服务
@Slf4j
public class SimpleMessageService implements MessageService {
    protected final ConcurrentHashMap<String,Handler> map = new ConcurrentHashMap<>();

    public SimpleMessageService(){
        log.info("Simple Message Service start.");
    }

    @Override
    public void publish(Message message) {
        map.values()
                .stream()
                .filter(e -> isContain(message.getHeader(), e.header))
                .map(e -> e.consumer)
                .forEach(e -> e.accept(message));
    }

    /**
     * 大map是否包含小map
     * @param bigMap
     * @param smallMap
     * @return
     */
    public boolean isContain(Map<String,Object> bigMap,Map<String,Object> smallMap){
        if(smallMap.size() == 0) return true;

        for (Map.Entry<String, Object> entry : smallMap.entrySet()) {
            if (!entry.getValue().equals(bigMap.get(entry.getKey()))) {
                return false;
            }
        }

        return true;
    }

    @Override
    public String subscribe(Map<String,Object> header, Consumer<Message> consumer) {
        String uuid = UUID.randomUUID().toString();
        map.put(uuid,new Handler(header,consumer));
        log.debug("subscribe channel size = {}",map.size());
        return uuid;
    }

    @Override
    public void unsubscribe(String id) {
        if(id != null){
            map.remove(id);
            log.debug("unsubscribe channel size = {}",map.size());
        }
    }

    public static class Handler{
        Map<String,Object> header;
        Consumer<Message> consumer;

        public Handler(Map<String, Object> header, Consumer<Message> consumer) {
            this.header = header;
            this.consumer = consumer;
        }
    }
}

  • 实现异步的消息服务
@Slf4j
public class AsyncMessageService extends SimpleMessageService {
    private final AsyncOptions opt;

    protected final ExecutorService executorService;
    protected final LinkedBlockingQueue<Message> queue = new LinkedBlockingQueue<>(100000);

    public AsyncMessageService(){
        this(new AsyncOptions());
    }

    public AsyncMessageService(AsyncOptions opt){
        this.opt = opt;
        executorService = Executors.newFixedThreadPool(opt.nThread,new AsyncThreadFactory());
        for (int i = 0; i < opt.nThread; i++) {
            executorService.submit(() -> head());
        }
    }

    private void head() {
        while (true){
            try {
                Message take = queue.take();
                super.publish(take);
            } catch (InterruptedException e) {
                break;
            } catch (Exception e){
                log.warn("异步发送消息失败",e);
            }
        }
    }

    @Override
    public void publish(Message message) {
        queue.add(message);
    }

    public AsyncOptions getOpt() {
        return opt;
    }

    private static class AsyncThreadFactory implements ThreadFactory{
        AtomicInteger index = new AtomicInteger();

        @Override
        public Thread newThread(Runnable r) {
            Thread thread = new Thread(r);
            thread.setName("AsyncMessageService-thread-" + index.incrementAndGet());
            return thread;
        }
    }
}
  • 扩展redis的消息服务
@Slf4j
public class RedisMessageService extends SimpleMessageService {

    private final RedisClient client;
    private final String channel;
    private final StatefulRedisPubSubConnection<String, String> listenerConnection;
    private final RedisPubSubAsyncCommands<String, String> listenerCmd;
    private final StatefulRedisPubSubConnection<String, String> publishConnection;
    private final RedisPubSubAsyncCommands<String, String> publishCmd;

    public RedisMessageService(RedisClient client, String channel) {
        this.client = client;
        this.channel = channel;
        this.listenerConnection = client.connectPubSub();
        this.publishConnection = client.connectPubSub();
        this.publishCmd = publishConnection.async();
        this.listenerCmd = listenerConnection.async();

        //添加监听通道
        listenerCmd.subscribe(this.channel);
        //添加消息监听器
        listenerConnection.addListener(new RedisPubSubAdapter<String,String>(){
            @Override
            public void message(String channel, String text) {
                Message message = JSON.parseObject(text, Message.class);
                RedisMessageService.super.publish(message);
            }
        });
    }

    @Override
    public void publish(Message message) {
        publishCmd.publish(this.channel,JSON.toJSONString(message));
    }
}

git地址

https://gitee.com/ichiva/message-pub-sub.git
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值