设计模式【发布订阅这模式】,再也不担心时间轮问题了

144 阅读3分钟

前言

  • 你有使用过rabbitmq等消息中间件吗?他们的特性是啥呢?有没有被他们的解耦特性惊艳到。
  • 在消息中间件中我们省下了定时器定时的耗时。今天我门查实解读下发布订阅这模式

ArrayBlockingQueue

  • 一个有数组结构构成的有界阻塞队列。了解该队列的都清楚他的内部依赖于ReentrantLock完成线程安全功能。

image-20211019104323487.png

  • 通过他的构造器能够了解到可以设置容器大小和锁特征。
  • 该队列提供好几种添加元素的方法,在开始发布订阅模式之前我们先来了解下他的添加方法有哪些

add(e)

  • 队列未满时,添加成功并返回true
  • 队列已满时,抛出IllegalStateException异常

offer(e)

  • 队列未满时,添加成功并返回true
  • 队列已满时,返回false。非阻塞立即返回。

offer(e,unit)

  • 队列未满时,添加成功并返回true
  • 队列已满时,定等待的时间,如果在指定时间内还不能往队列中插入数据则返回false

put(e)

  • 队列未满时,添加成功无返回
  • 队列已满时,队列满时会阻塞等待,一直等到队列未满时再插入。

发布者

  • 我们定义一个IPublisher用于发布消息,而真正发布消息的需要借助上面提到的ArrayBlockingQueue , 在发布者之前我们需要定义一个类用于管理ArrayBlockingQueue队列。
@Data
public class SubscribePublish<T> {
    /**
     * 订阅器名称
     */
    private String name;
    /**
     * 订阅器队列容量
     */
    private int QUEUE_CAPACITY = 20;
​
    /**
     * 订阅器存储队列
     */
    private BlockingQueue<Msg> queue = new ArrayBlockingQueue<Msg>(QUEUE_CAPACITY);
    /**
     * 订阅者
     */
    private List<ISubscribe> subcribers = new ArrayList<ISubscribe>();
​
    public SubscribePublish(String name) {
        this.name = name;
    }
​
    public void publish(String publisher, T message, boolean isInstantMsg) {
        if (isInstantMsg) {
            update(publisher, message);
            return;
        }
        Msg<T> m = new Msg<T>(publisher, message);
        if (!queue.offer(m)) {
            update();
        }
    }
​
    public void subcribe(ISubscribe subcriber) {
        subcribers.add(subcriber);
    }
​
    public void unSubcribe(ISubscribe subcriber) {
        subcribers.remove(subcriber);
    }
​
    public void update() {
        Msg m = null;
        while ((m = queue.poll()) != null) {
            this.update(m.getPublisher(), (T) m.getMsg());
        }
    }
​
    public void update(String publisher, T Msg) {
        for (ISubscribe subcriber : subcribers) {
            subcriber.update(publisher, Msg);
        }
    }
}
  • 在每次发布消息的时候根据isInstanceMsg进行判定是否立马通知监听者,如果不立马通知则会存储在ArrayBlockingQueue中,直到该队列满时才会下发到监听者,这个可以实现批量集中处理功能。
public interface IPublisher<T> {
    public void publish(SubscribePublish subscribePublish, T message, boolean isInstantMsg);
}

具体发布者

  • 上面已经提到了实际上是SubscribePublish在真正进行消息下发,并且根据isInstanceMsg来进行消息批量下发的控制
public class PublisherImpOne<T> implements IPublisher<T> {
    private String name;
​
    public PublisherImpOne(String name) {
        super();
        this.name = name;
    }
​
    public void publish(SubscribePublish subscribePublish, T message, boolean isInstantMsg) {
        subscribePublish.publish(this.name, message, isInstantMsg);
    }
}

订阅

  • 订阅这就涉及到监听和解绑的关系,所以需要subcribe和unSuncribe两个方法。除了这两个方法用于处理和发布者之间的关联关系还有一个重要的就是接受数据处理逻辑,这里定义为update
public interface ISubscribe<T> {
​
    public void subcribe(SubscribePublish subscribePublish);
​
    public void unSubcribe(SubscribePublish subscribePublish);
​
    public void update(String publisher, T message);
}

订阅实现

public class SubcriberImpOne<T> implements ISubscribe<T> {
    public String name;
​
    public SubcriberImpOne(String name) {
        super();
        this.name = name;
    }
​
    public void subcribe(SubscribePublish subscribePublish) {
        subscribePublish.subcribe(this);
    }
​
    public void unSubcribe(SubscribePublish subscribePublish) {
        subscribePublish.unSubcribe(this);
    }
​
    public void update(String publisher, T message) {
        System.out.println(this.name + "收到" + publisher + "发来的消息:" + message.toString());
    }
}

测试

  • 最后我们只需要维护好发布者和订阅者,我们就可以在发布者中发布消息实现订阅者接受消息了。如何做到真正解耦我们可以参考下mq或者直接多线程实现就可以了。
public class SubPubTest {
    public static void main(String[] args) {
        SubscribePublish<String> subscribePublish = new SubscribePublish<String>("订阅器");
        IPublisher<String> publisher1 = new PublisherImpOne<String>("发布者1");
        ISubscribe<String> subcriber1 = new SubcriberImpOne<String>("订阅者1");
        ISubscribe<String> subcriber2 = new SubcriberImpOne<String>("订阅者2");
        subcriber1.subcribe(subscribePublish);
        subcriber2.subcribe(subscribePublish);
        publisher1.publish(subscribePublish, "welcome", true);
        publisher1.publish(subscribePublish, "to", true);
        for (int i = 0; i < 21; i++) {
            publisher1.publish(subscribePublish, "yy"+i, false);
        }
    }
}