探索设计模式的魅力:探索发布-订阅模式的深度奥秘-实现高效、解耦的系统通信

在这里插入图片描述
​🌈 个人主页:danci_
🔥 系列专栏:《设计模式》
💪🏻 制定明确可量化的目标,并坚持默默的做事。


探索发布-订阅模式的深度奥秘:实现高效、解耦的系统通信

文章目录

  • 一、案例场景🔍
    • 1.1 经典的运用场景
    • 1.2 一坨坨代码实现😻
    • 1.3 痛点
  • 二、解决方案🚀
    • 2.1 定义
    • 2.2 案例分析🧐
    • 2.3 模式结构图及说明
    • 2.4 使用模式重构示例
    • 2.5 重构后解决的问题👍
  • 三、模式讲解🎭
    • 3.1 认识发布订阅模式
    • 3.2 实现步骤
    • 3.3 思考中介者模式
  • 四、总结🌟
    • 4.1 优点💖
    • 4.2 缺点
    • 4.3 挑战和局限

一、案例场景🔍

在这里插入图片描述

1.1 经典的运用场景

    发布-订阅模式在软件开发中拥有广泛的应用,它适用于多种场景,帮助开发者构建灵活、可扩展和松耦合的系统。以下是一些经典的应用场景及其在实际项目中的应用价值:👇

  1. 实时消息系统:
    在聊天应用、社交媒体平台或实时数据流处理中,发布-订阅模式允许用户发送消息到一个中央通道,而其他用户可以根据兴趣订阅这些通道以接收实时更新。这降低了发送者和接收者之间的直接依赖,支持高并发和实时通信。
  2. 事件驱动架构:
    在微服务或分布式系统中,服务之间通过事件进行通信。一个服务发布事件(如订单创建、用户注册等),而其他服务订阅这些事件并作出响应。这种异步通信方式提高了系统的响应性和可扩展性。
  3. 状态更新通知:
    在用户界面或后台管理中,当某个状态发生变化时(如数据库记录更新),发布-订阅模式可以确保相关组件或用户得到通知。这有助于实现实时反馈和动态更新。
  4. 日志和监控:
    系统日志和监控工具经常采用发布-订阅模式来收集、聚合和分发日志事件。发布者将日志事件发送到中央收集点,而订阅者可以是各种分析工具或警报系统。
  5. 物联网(IoT):
    在物联网应用中,设备产生的大量数据需要被实时处理和分发。发布-订阅模式允许设备将数据发布到消息代理,而各种服务和应用程序可以订阅这些数据流以进行实时分析或响应。
  6. 股票交易和金融市场:
    在金融应用中,实时数据流(如股票价格变动)对交易者至关重要。发布-订阅模式确保交易者能够订阅他们感兴趣的金融工具,并在价格变动时立即收到通知。

    下面我们来实现实时消息系统场景 📄✏️。

1.2 一坨坨代码实现😻

在这里插入图片描述

    要使用Java实现第一个场景(实时消息系统)而不直接使用发布-订阅设计模式,我们可以使用基础的Java并发和集合类来模拟这个系统。下面是一个简单的示例,展示了如何实现一个简易的实时聊天系统。

import java.util.ArrayList;  
import java.util.HashMap;  
import java.util.List;  
import java.util.Map;  
import java.util.concurrent.CopyOnWriteArrayList;  public class SimpleChatSystem {  // 存储所有用户的聊天室  private static final Map<String, List<ChatListener>> chatRooms = new HashMap<>();  // 用于在控制台模拟用户发送消息  public static void main(String[] args) {  // 创建两个用户  ChatListener alice = new ChatListener("Alice");  ChatListener bob = new ChatListener("Bob");  // 将用户添加到同一个聊天室  String chatRoomName = "General";  addToChatRoom(chatRoomName, alice);  addToChatRoom(chatRoomName, bob);  // 模拟Alice发送消息  sendMessage(chatRoomName, "Alice: Hello, Bob!");  // 模拟Bob发送消息  sendMessage(chatRoomName, "Bob: Hi, Alice! How are you?");  }  // 将用户添加到聊天室  public static void addToChatRoom(String chatRoomName, ChatListener listener) {  chatRooms.computeIfAbsent(chatRoomName, k -> new CopyOnWriteArrayList<>()).add(listener);  }  // 从聊天室移除用户  public static void removeFromChatRoom(String chatRoomName, ChatListener listener) {  chatRooms.getOrDefault(chatRoomName, new ArrayList<>()).remove(listener);  }  // 发送消息到聊天室  public static void sendMessage(String chatRoomName, String message) {  List<ChatListener> listeners = chatRooms.get(chatRoomName);  if (listeners != null) {  for (ChatListener listener : listeners) {  listener.receiveMessage(message);  }  }  }  // 简单的聊天监听器,用于接收消息并打印到控制台  public static class ChatListener implements Runnable {  private final String name;  public ChatListener(String name) {  this.name = name;  }  public void receiveMessage(String message) {  System.out.println(name + " received: " + message);  }  @Override  public void run() {  // 这个run方法没有实际用途,只是为了让ChatListener实现Runnable接口  // 在实际应用中,ChatListener可能会是一个复杂的类,包含网络连接、消息处理等逻辑  }  }  
}

    在这个示例中,我们没有使用任何设计模式,而是直接使用了Java的基础类。我们创建了一个SimpleChatSystem类,它包含了一个静态的chatRooms映射,用于存储聊天室和其中的用户(ChatListener实例)。ChatListener是一个简单的接口,它有一个receiveMessage方法,用于接收并处理消息。
    在main方法中,我们创建了两个用户(Alice和Bob),并将它们添加到同一个聊天室。然后,我们模拟Alice和Bob发送消息到聊天室,所有在聊天室中的用户都会接收到这些消息,并将它们打印到控制台。

    注:上述实现只是个示例。在实际应用中,你可能需要考虑更多的因素,如网络编程、并发控制、错误处理、用户身份验证等。此外,如果你打算构建一个大规模的实时消息系统,使用专业的消息队列中间件(如RabbitMQKafka等)可能是一个更好的选择。

问题描述:
用java实现第一个场景,要求不使用设计模式、编码逻辑思路清晰

问题描述:
上述实现是否有优点?

    上述实现确实有一些优点,但也存在一些局限性。以下是对其优点的分析:👇

  1. 简单性:
    ✨ 这个实现非常直观和简单,易于理解和实现。它使用基础的Java类和并发控制来模拟实时消息系统,不需要掌握复杂的设计模式或框架。
  2. 演示目的:
    ✨ 作为一个示例,它很好地演示了如何使用Java的集合和并发特性来构建一个简单的实时通信系统。这对于初学者理解基本概念很有帮助。
  3. 静态模拟:
    ✨ 虽然它只在控制台中模拟用户之间的通信,但它可以作为一个起点,用于构建更复杂的系统。例如,可以将 ChatListener 类扩展为包含网络连接、GUI界面或与其他服务集成的功能。
  4. 无额外依赖:
    ✨ 该实现不依赖于任何外部库或框架,这意味着它可以在任何标准的Java环境中运行,无需担心依赖管理或版本兼容性问题。

    这个简单实现也有一些局限性,比如缺乏真正的异步通信、可扩展性限制、没有错误处理等。在实际项目中,通常会使用更复杂、更健壮的解决方案来处理这些问题。

1.3 痛点

在这里插入图片描述

    然而,没有复杂的设计下上述实现确实存在一些缺点,尤其是在面对更复杂、大规模或生产级别的实时消息系统需求时。以下是对其缺点的分析:👇

  1. 缺乏真正的异步通信:
    ✨ 虽然示例中的 ChatListener 可以接收消息,但整个系统并不是真正异步的。在真实世界的实时消息系统中,消息通常是通过异步方式发送和接收的,以提高响应性和吞吐量。

  2. 可扩展性限制:
    ✨ 由于使用了静态的映射来存储聊天室和用户,这个实现在处理大量用户或聊天室时可能会遇到性能瓶颈。此外,它也没有提供动态添加或删除聊天室的功能。

  3. 没有错误处理:
    ✨ 示例代码中没有包含任何错误处理逻辑。在实际应用中,网络中断、用户断开连接、消息格式错误等情况都需要妥善处理。

  4. 用户界面和交互性有限:
    ✨ 该实现仅在控制台打印接收到的消息,缺乏一个真正的用户界面或与其他服务的集成。这限制了其在现实世界应用中的实用性。

  5. 线程安全性问题:
    ✨ 虽然使用了 CopyOnWriteArrayList 来提供一定程度的线程安全,但在更复杂的多线程环境中,这可能不足以保证系统的稳定性和数据的一致性。例如,当同时添加和删除用户时,可能会出现竞态条件。

  6. 单一的消息传递方式:
    ✨ 示例中只有一种简单的消息传递方式,即广播到整个聊天室。在实际应用中,可能需要更复杂的消息路由和传递策略,如点对点消息、组播、主题订阅等。

  7. 缺乏持久化:
    ✨ 消息在发送后被立即消费,没有持久化机制来存储历史消息或支持离线消息传递。

  8. 缺乏认证和授权:
    ✨ 示例中没有实现用户认证和授权机制,这在任何需要安全性的应用中都是必需的。

    违反的设计原则(问题)下面逐一分析:👇

  1. 单一职责原则(SRP):
    SimpleChatSystem 类负责了多个职责,包括管理聊天室、添加/移除用户以及发送消息。这可能导致类变得难以维护和扩展。更好的做法是将这些职责拆分到不同的类中,例如,可以有一个专门的 ChatRoomManagerd 类来管理聊天室和用户,以及一个 MessageSender 类来负责发送消息。

  2. 开闭原则(OCP):
    ✨ 当前的实现不够灵活,无法在不修改现有代码的情况下添加新的功能或行为。例如,如果想要添加新的聊天室类型或消息传递方式,可能需要修改 SimpleChatSystem 类的代码。这违背了开闭原则,即软件实体应该对扩展开放,对修改关闭。

  3. 里氏替换原则(LSP):
    ✨ 虽然在这个简单的示例中没有明显的违反里氏替换原则的情况,但如果 ChatListener 接口被扩展或修改,可能会导致子类无法正确替换父类或接口的问题。这通常发生在当子类没有遵守父类的约定或行为时。

  4. 接口隔离原则(ISP):
    ChatListener 接口可能违反了接口隔离原则,因为它可能包含了不需要的方法(在这个简单示例中只有一个方法,但在更复杂的情况下可能会有更多)。如果接口过于庞大或包含不相关的方法,那么实现这个接口的类可能会被迫实现它们不需要的方法。

  5. 依赖倒置原则(DIP):
    ✨ 当前的实现中,高层模块(如SimpleChatSystem)直接依赖于低层模块(如ChatListener),这可能导致代码的耦合度过高。更好的做法是使用抽象(如接口或抽象类)来定义依赖关系,这样高层模块就可以依赖于抽象而不是具体的实现。

  6. 迪米特法则(LoD)或最少知识原则:
    ✨ 在这个实现中,SimpleChatSystem 类直接访问和操作了ChatListener 实例的内部状态(通过调用 receiveMessage 方法)。这违反了迪米特法则,即一个对象应该对其他对象保持最少的了解。更好的做法是通过定义清晰的接口和委托关系来减少类之间的直接依赖。

    注:设计原则是为了指导软件设计而提出的,旨在提高代码的可维护性、可扩展性和可重用性。在实际项目中,根据具体的需求和上下文,可能需要权衡这些原则的应用。不过,在构建更大规模或更复杂的系统时,遵循这些原则通常是有益的。

二、解决方案🚀

2.1 定义

发布订阅模式:一种消息传递模型,发送者不直接将消息发送给接收者,而是发送到中间层。

    接收者可以订阅这些消息,并在消息发布时接收到通知。这种模式的核心目的是实现发布者与订阅者之间的解耦,允许它们独立地扩展和修改,同时提供一种灵活的消息通信机制。通过发布订阅模式,系统可以更加灵活、可扩展和可维护。

2.2 案例分析🧐

在这里插入图片描述

2.3 模式结构图及说明

在这里插入图片描述

  • 发布者(Publisher):
    它是消息或者说主题的生产者,负责产生并发布消息到发布订阅管理器。发布者不需要知道或关心消息会被哪些订阅者接收,它只需要关注消息的发布。
  • 订阅者(Subscriber)
    它是订阅消息或主题的消费者,负责从发布订阅管理器接收并处理感兴趣的消息。订阅者需要提前向发布订阅管理器订阅自己感兴趣的主题,当发布者发布相关主题的消息时,发布订阅管理器会将消息推送给所有订阅了该主题的订阅者。
  • 发布订阅管理器:
    它是整个发布订阅模式的核心,负责接收并存储发布者发布的消息,同时管理订阅者的订阅信息。当发布者发布消息时,发布订阅管理器会根据订阅信息将消息推送给相应的订阅者。发布订阅管理器实现了发布者与订阅者之间的解耦,使得它们可以独立地扩展和修改。

    发布订阅模式的功能是实现消息的发布与订阅,提供了一种灵活的消息通信机制,使得消息的发送者和接收者可以解耦,提高了系统的灵活性和可扩展性。同时,发布订阅模式还支持一对多、多对多的消息通信,可以满足复杂场景下的消息通信需求。

2.4 使用模式重构示例

    为了解决上述实现中的缺点,并采用发布订阅模式重构实时消息系统,我们需要对原有的代码结构进行调整。以下是一个简化的重构示例:👇

  1. 首先,定义消息发布的接口和消息订阅的接口:
// 定义消息类型  
public class Message {  private String sender;  private String content;  // 构造方法、getters和setters略...  
}  // 消息发布接口  
public interface MessagePublisher {  void publish(Message message);  
}  // 消息订阅接口  
public interface MessageSubscriber {  void onMessage(Message message);  
}  // 消息订阅管理器,管理订阅者和消息的分发  
public class MessageSubscriptionManager implements MessagePublisher {  private List<MessageSubscriber> subscribers = new CopyOnWriteArrayList<>();  @Override  public void publish(Message message) {  for (MessageSubscriber subscriber : subscribers) {  subscriber.onMessage(message);  }  }  public void subscribe(MessageSubscriber subscriber) {  subscribers.add(subscriber);  }  public void unsubscribe(MessageSubscriber subscriber) {  subscribers.remove(subscriber);  }  
}
  1. 接下来,我们定义ChatRoom类,它将包含 MessageSubscriptionManager 实例来管理订阅和消息发布:
public class ChatRoom {  private String name;  private MessageSubscriptionManager subscriptionManager = new MessageSubscriptionManager();  public ChatRoom(String name) {  this.name = name;  }  public void sendMessage(String sender, String content) {  Message message = new Message(sender, content);  subscriptionManager.publish(message);  }  public void subscribe(MessageSubscriber subscriber) {  subscriptionManager.subscribe(subscriber);  }  public void unsubscribe(MessageSubscriber subscriber) {  subscriptionManager.unsubscribe(subscriber);  }  
}
  1. 最后,实现一个简单的 MessageSubscriber 示例,它可以接收并处理来自 ChatRoom 的消息:
public class ChatUser implements MessageSubscriber {  private String username;  public ChatUser(String username) {  this.username = username;  }  @Override  public void onMessage(Message message) {  System.out.println(username + " received message from " + message.getSender() + ": " + message.getContent());  }  // getters和setters略...  
}
  1. 现在,创建 ChatRoomChatUser 的实例,并将用户订阅到聊天室中,以接收实时消息:
public class RealTimeMessagingSystem {  public static void main(String[] args) {  ChatRoom chatRoom = new ChatRoom("General");  ChatUser user1 = new ChatUser("Alice");  ChatUser user2 = new ChatUser("Bob");  chatRoom.subscribe(user1);  chatRoom.subscribe(user2);  // 发送消息到聊天室  chatRoom.sendMessage("Server", "Hello, everyone!");  // ... 可以添加更多逻辑,比如用户之间的交互,或用户发送消息等。  // 当某个用户需要离开时,取消订阅  chatRoom.unsubscribe(user1);  }  
}

    <这个重构示例遵循了发布订阅模式,允许用户订阅聊天室的消息,并通过聊天室发送消息给所有订阅者。此外,通过 MessageSubscriptionManager 来管理订阅者和消息的分发,使得系统更加解耦、灵活,并支持多个用户之间的实时交互。这种模式使得系统易于扩展,能够适应不同数量和类型的订阅者和消息类型。

2.5 重构后解决的问题👍

在这里插入图片描述

    优点
    上述使用发布订阅模式的实现解决了以下已知缺点:👇

  1. 解耦:
    ✨ 在原始的实现中,发送者和接收者之间可能存在直接的依赖关系,这限制了系统的灵活性和可维护性。通过使用发布订阅模式,我们引入了中间层(MessageSubscriptionManager),使得发送者(发布者)和接收者(订阅者)之间解耦。这意味着发送者不需要知道或关心消息被哪些接收者处理,同样,接收者也不需要知道消息来自哪个发送者。

  2. 可扩展性:
    ✨ 发布订阅模式允许系统更容易地扩展。无论是增加新的订阅者还是处理更多的消息类型,都不需要对现有代码进行大量修改。在重构后的代码中,添加新的订阅者只需要调用 subscribe 方法,而移除订阅者则调用 unsubscribe 方法。

  3. 实时性与异步处理:
    ✨ 发布订阅模式天然支持异步消息传递。在重构后的系统中,消息的发送是异步的,发送者不需要等待接收者的响应。这提高了系统的响应性和吞吐量,使得它能够更好地处理实时消息。

  4. 容错性与可靠性:
    ✨ 虽然上述示例代码没有直接展示容错性机制,但发布订阅模式为实现容错性和可靠性提供了基础。例如,中间层可以设计为支持消息的持久化存储,以便在订阅者暂时不可用时保留消息。此外,通过监控和重试机制,可以确保消息最终被传递给订阅者。

  5. 灵活性:
    ✨ 发布订阅模式使得系统更加灵活,能够适应不同的业务需求。例如,订阅者可以选择性地订阅感兴趣的消息类型,或者根据消息的内容执行不同的操作。这种灵活性使得系统更容易适应变化,降低了维护成本。

    遵循的设计原则
    上述使用发布-订阅模式重构示例后的代码遵循了以下设计原则:👇

  1. 单一职责原则(Single Responsibility Principle, SRP):
    ✈️ 每个类(如 Message , MessagePublisher , MessageSubscriber , MessageSubscriptionManager , ChatRoom , ChatUser )都只负责一项功能或职责。例如,Message 类只负责封装消息内容,而 MessageSubscriptionManager 只负责管理订阅和消息的分发。

  2. 开闭原则(Open-Closed Principle, OCP):
    ✈️ 系统应该对扩展开放,对修改关闭。在上述实现中,如果需要添加新的消息类型或订阅者,不需要修改现有的代码,只需扩展相应的接口或类即可。例如,可以通过实现MessageSubscriber接口来创建新的订阅者。

  3. 里氏替换原则(Liskov Substitution Principle, LSP):
    ✈️ 子类必须能够替换其父类出现在任何地方,并且不会引入任何错误或异常。虽然上述示例中没有直接展示继承关系,但如果有子类继承自 MessageSubscriber ,那么它们应该能够无缝地替换父类实例,而不需要修改 MessageSubscriptionManagerChatRoom 的代码。

  4. 接口隔离原则(Interface Segregation Principle, ISP):
    ✈️ 客户端不应该依赖于它不使用的接口。在上述实现中, MessagePublisherMessageSubscriber 接口都是小而专注的,只定义了与发布和订阅消息相关的操作,没有包含任何不相关的方法。

  5. 依赖倒置原则(Dependency Inversion Principle, DIP):
    ✈️ 高层模块不应该依赖于低层模块,它们都应该依赖于抽象。在上述实现中, ChatRoom 类依赖于抽象的 MessagePublisher 接口(通过 MessageSubscriptionManager 实现),而不是具体的实现类。这意味着如果需要更换消息发布的管理方式,只需要提供一个新的 MessagePublisher 实现,而不需要修改ChatRoom的代码。

  6. 迪米特法则(Law of Demeter, LoD)或最少知识原则(Least Knowledge Principle, LKP)::
    ✈️ 一个对象应该对其他对象保持最少的了解。在上述实现中, ChatRoom 只与 MessageSubscriptionManager 交互,而不直接与订阅者交互。同样,订阅者(如 ChatUser )也只与接收到的消息交互,而不需要知道消息是如何发布或管理的。

    缺点
    上述实现(假设是指使用发布订阅模式的实现)虽然解决了许多已知的问题,并遵循了良好的设计原则,但仍然可能存在一些潜在的缺点或考虑因素:👇

  1. 内存消耗:
    💡 如果订阅者数量众多或者消息产生频率非常高,系统可能会面临内存压力。因为每个订阅者都可能保留一份消息副本,或者中间层(如 MessageSubscriptionManager )需要维护大量的订阅关系和待处理消息。

  2. 消息处理顺序:
    💡 发布订阅模式通常不保证消息的处理顺序。如果有多个订阅者同时处理同一条消息,或者订阅者在不同线程/进程中运行,那么消息的处理顺序可能会变得不可预测。

  3. 消息丢失或重复:
    💡 在分布式系统或存在网络延迟/故障的环境中,发布订阅模式可能会面临消息丢失或重复的问题。需要额外的机制来确保消息的可靠传输和幂等性处理。

  4. 复杂性增加:
    💡 引入发布订阅模式可能会增加系统的复杂性。需要管理额外的组件(如消息代理、订阅管理器等),并处理它们之间的交互和依赖关系。此外,调试和维护一个复杂的消息传递系统也可能更加困难。

  5. 性能瓶颈:
    💡 如果所有消息都通过一个集中的 MessageSubscriptionManager 进行处理和分发,那么它可能会成为系统的性能瓶颈。特别是在高并发或大数据量的场景下,单点压力可能会很大。

  6. 安全性考虑:
    💡 发布订阅模式可能暴露更多的攻击面。例如,未经授权的订阅者可能会接收到敏感信息,或者恶意订阅者可能会发送伪造的消息来干扰系统的正常运行。

三、模式讲解🎭

在这里插入图片描述

  核心思想

通过引入一个中间层来实现发布者和订阅者之间的解耦通信,支持事件驱动、基于兴趣的注册、异步通信以及高度的可扩展性和灵活性。

3.1 认识发布订阅模式

    发布订阅模式是一种消息传递模型,它定义了如何在发布者和订阅者之间传递消息。在这种模式中,发布者(也称为发送者或生产者)生成并发送消息到一个中间层,而不需要知道或关心哪些订阅者(也称为接收者或消费者)会接收这些消息。订阅者则向中间层注册自己的兴趣,以便在相关消息发布时接收它们。

    发布订阅模式具有以下功能:👇

  1. 消息发布:
    🌈 发布者能够生成消息并将其发送到中间层。这些消息可以是事件通知、状态更新、数据变更等。发布者不需要知道订阅者的具体信息,只需将消息发送到中间层即可。

  2. 消息订阅:
    🌈 订阅者能够向中间层注册自己的兴趣,表示它们希望接收特定类型的消息。订阅者可以指定自己感兴趣的消息类型、主题或标签,以便在相关消息发布时得到通知。

  3. 消息分发:
    🌈 中间层负责接收发布者发送的消息,并根据订阅者的兴趣将消息分发给相应的订阅者。这个过程是自动的,订阅者不需要主动轮询或请求消息,而是被动地接收中间层推送的消息。

  4. 解耦:
    🌈 发布订阅模式实现了发布者和订阅者之间的解耦。它们不需要直接相互依赖或了解对方的存在。这种解耦使得系统更加灵活,易于扩展和维护。

  5. 异步通信:
    🌈 发布订阅模式支持异步通信。消息的发送和接收不需要同步进行,发布者可以在任何时间发送消息,而订阅者则可以在稍后的时间异步地接收和处理这些消息。这种异步通信机制有助于提高系统的响应性和吞吐量。

  6. 可扩展性:
    🌈 由于发布者和订阅者之间的解耦以及中间层的存在,发布订阅模式提供了很高的可扩展性。新的发布者或订阅者可以很容易地添加到系统中,而不需要修改现有的代码或配置。此外,系统可以支持多种不同类型的事件和消息,以满足不同的业务需求。

  7. 容错性和可靠性:
    🌈 发布订阅模式可以支持消息的持久化存储和重试机制,以确保消息在传输过程中的可靠性和容错性。即使订阅者暂时不可用或网络出现故障,消息也不会丢失,而是会在适当的时候重新发送给订阅者。

3.2 实现步骤

    发布订阅模式是一种在软件工程中广泛使用的设计模式,它定义了一种一对多的依赖关系,让多个订阅者对象同时监听某一个主题(或事件)。当该主题(或事件)在发布者对象上触发时,所有订阅了该主题的订阅者对象都会自动接收到通知。

    以下是实现发布订阅模式的基本步骤:👇

  1. 确定发布者和订阅者:
    ⌛ 首先,需要明确系统中的哪些对象将充当发布者,哪些对象将充当订阅者。发布者负责产生并发送事件或消息,而订阅者则负责接收并处理这些事件或消息。

  2. 创建事件通道或消息代理:
    ⌛ 这是发布者和订阅者之间的中间层,用于管理事件或消息的传递。发布者将事件发送到这个中间层,而订阅者则从这个中间层接收事件。这个中间层可以是一个事件总线、消息队列、或者任何能够实现发布者和订阅者之间解耦的机制。

  3. 注册订阅者:
    ⌛ 订阅者需要向事件通道或消息代理注册自己的兴趣,即它们希望接收哪些类型的事件或消息。这通常通过调用事件通道或消息代理的注册方法来实现,注册时需要提供订阅者的标识符和它们感兴趣的事件类型。

  4. 发布事件:
    ⌛ 当发布者产生一个事件时,它会将该事件发送到事件通道或消息代理。发布者不需要知道有哪些订阅者正在监听这个事件,也不需要直接与订阅者进行通信。事件的发布通常通过调用事件通道或消息代理的发布方法来实现,发布时需要提供事件的类型和相关的数据。

  5. 分发事件:
    ⌛ 事件通道或消息代理接收到事件后,会根据注册信息将事件分发给所有对该类型事件感兴趣的订阅者。这个过程是自动的,订阅者不需要主动请求或轮询事件。事件通道或消息代理负责确保事件能够正确地分发给所有相关的订阅者。

  6. 处理事件:
    ⌛ 订阅者接收到事件后,会根据自己的业务逻辑对事件进行处理。处理的方式取决于具体的业务需求和订阅者的实现。处理完事件后,订阅者可以选择是否继续监听该类型的事件,或者注销自己的订阅。

    发布订阅模式允许发布者和订阅者之间实现解耦的通信,提高了系统的灵活性和可扩展性。同时,由于事件的分发是自动的,因此也提高了系统的响应速度和吞吐量。

3.3 思考中介者模式

 
    相似的设计模式

 1. 观察者模式(Observer Pattern)

    相似点

    一对多依赖:两者都处理一对多的依赖关系,即一个对象(发布者/主题)改变其状态时,多个对象(订阅者/观察者)会收到通知。
    解耦:两者都实现了对象之间的解耦,允许它们独立地改变和演化。

    易混淆点

    通知机制:发布订阅模式通常依赖于一个中间层(如事件总线)来传递消息,而观察者模式则直接由主题对象通知其观察者。
    异步性:发布订阅模式通常支持异步通信,而观察者模式通常是同步的。
 
 2. 中介者模式(Mediator Pattern)

    相似点

    集中控制:两者都通过一个中间层(发布订阅模式中的事件通道或消息代理,中介者模式中的中介者对象)来集中控制对象间的交互。
    减少依赖:两者都减少了对象之间的直接依赖,使得系统更加灵活和可维护。

    易混淆点

    职责:中介者模式通常负责协调多个对象之间的复杂交互,而发布订阅模式更专注于事件的发布和订阅。
    通信类型:中介者模式通常处理请求/响应类型的通信,而发布订阅模式则更侧重于事件的通知和分发。

 
    容易混淆的设计模式

 1. 消息队列模式(Message Queue Pattern)

    易混淆点

    异步通信:发布订阅模式和消息队列模式都支持异步通信,使得发送者和接收者可以独立地工作。
    中间件:两者都依赖于一个中间件(发布订阅模式中的事件通道或消息代理,消息队列模式中的消息队列)来进行消息的传递。

    区分点

    持久性:消息队列通常支持消息的持久化存储,即使在系统崩溃或重启后也能保证消息的可靠传递。而发布订阅模式则可能只是临时传递消息。
    用途:消息队列通常用于构建分布式系统或微服务架构中的通信机制,而发布订阅模式则更广泛地应用于各种需要解耦通信的场景。
 
 2. 责任链模式(Chain of Responsibility Pattern)

    易混淆点

    多个处理者:在责任链模式和发布订阅模式中,都可能涉及多个对象或组件来处理请求或事件。

    区分点

    处理顺序:责任链模式按照预设的顺序依次将请求传递给链中的对象,直到某个对象处理它或链结束。而发布订阅模式则是同时通知所有订阅者。
    处理结果:在责任链模式中,每个对象都可以决定是否继续传递请求,而在发布订阅模式中,发布者不关心订阅者如何处理事件。

    这些设计模式在某些方面有相似之处,但也存在明显的差异。理解这些相似点和易混淆点有助于更准确地选择和应用设计模式,以满足特定的软件设计需求。
 

四、总结🌟

在这里插入图片描述

4.1 优点💖

    发布订阅模式在松耦合、关注点分离、高伸缩性、高可靠性和异步通信等方面具有显著优点。这使得它在许多复杂和分布式系统中得到广泛应用,成为实现高效、可靠和可扩展通信的重要手段之一。然而,也需要注意到该模式可能引入一些额外的复杂性,如中间件的管理和维护等,因此在应用时需要权衡其优缺点并根据具体场景进行选择。以下是对发布订阅模式优点的详细描述:👇

  1. 松耦合与独立性:
    ❤️ 发布订阅模式的核心优点之一在于它能够将众多需要通信的子系统或组件解耦。这意味着发送者和接收者之间不需要直接了解彼此的存在或实现细节。每个子系统或组件都可以独立地管理自己的逻辑和状态,而不需要关心其他部分的变化。这种松耦合的特性使得系统更加灵活,可以更容易地添加、删除或修改子系统,而不会对整个系统的结构造成大的影响。

  2. 关注点分离:
    ❤️ 在发布订阅模式中,发送者(发布者)的主要职责是发布消息或事件,而接收者(订阅者)则关注于处理这些消息或事件。这样,每个组件都可以专注于其核心功能,而不必担心与其他组件的交互细节。这种关注点分离使得代码更加清晰、易于理解和维护。

  3. 高伸缩性:
    ❤️ 发布订阅模式具有出色的伸缩性。由于发送者和接收者之间是解耦的,因此系统可以轻松地处理大量的并发请求或事件。此外,通过增加更多的订阅者或优化消息传递机制,系统可以进一步扩展其处理能力。这种伸缩性使得发布订阅模式非常适合处理大规模、高并发的应用场景。

  4. 高可靠性:
    ❤️ 发布订阅模式通过异步通信和消息队列等方式,提高了系统的可靠性。即使部分子系统或组件出现故障或下线,也不会影响整个系统的消息传递和事件处理。此外,通过消息持久化、重试机制等技术手段,可以确保消息在传输过程中的可靠性和完整性。这种高可靠性使得系统更加稳定、可依赖。

  5. 异步通信:
    ❤️ 发布订阅模式支持异步通信,这意味着发送者不需要等待接收者处理完消息后再继续执行其他任务。这种异步性使得系统可以更加高效地利用资源,提高响应速度和处理能力。同时,异步通信也减少了系统之间的耦合度,使得系统更加灵活和可扩展。

4.2 缺点

    发布订阅模式虽然具有许多优点,但也存在一些缺点和挑战。在应用该模式时,需要权衡其优缺点,并根据具体场景和需求进行选择。同时,需要采取适当的设计和实现策略来缓解这些缺点带来的问题。例如,可以通过引入消息顺序保证机制、优化消息代理的性能、加强系统的安全性和可靠性等措施来改进发布订阅模式的实现。以下是对发布订阅模式缺点的详细描述:👇

  1. 消息传递的顺序问题:
    💔 在发布订阅模式中,消息的顺序不能得到保证。如果订阅者之间有依赖关系,或者某些操作需要按照特定顺序执行,那么这可能会成为一个问题。因为发布者只是简单地将消息发送给所有订阅者,而不关心它们如何处理或消费这些消息。

  2. 消息代理的性能开销:
    💔 发布订阅模式中通常需要引入一个消息代理或中间件来管理消息的发布和订阅。这可能会带来额外的性能开销,包括内存占用、处理延迟等。特别是在处理大量消息或高并发场景时,这种性能开销可能会更加明显。

  3. 复杂性增加:
    💔 发布订阅模式可能增加系统的复杂性。由于引入了额外的组件和通信机制,系统的架构和设计可能变得更加复杂。此外,多个发布者和订阅者之间的交互也可能导致难以追踪和调试的问题。

  4. 可能的内存泄漏:
    💔 如果订阅者忘记取消订阅,或者由于某些原因无法处理接收到的消息,那么这可能会导致内存泄漏。因为消息代理可能会持续地向这些订阅者发送消息,占用越来越多的内存资源。

  5. 安全性问题:
    💔 发布订阅模式可能面临安全性挑战。由于消息是在发布者和订阅者之间传递的,因此可能存在未经授权的访问、数据泄露或篡改等风险。需要采取适当的安全措施来保护消息的安全性和完整性。

4.3 挑战和局限

    发布订阅模式作为一种流行的软件设计模式,确实为构建松耦合、可扩展和异步通信的系统提供了强大的支持。然而,它并非没有局限和挑战。以下是对发布订阅模式面临的主要挑战和局限的详细描述:👇

    挑战

  1. 消息顺序保证:
    💖 在并发环境中,确保消息按照预期的顺序被处理是一个挑战。发布订阅模式本身不提供消息排序的机制,这可能导致订阅者接收到乱序的消息。

  2. 消息过滤和路由:
    🤩 当系统中有大量的发布者和订阅者时,有效地过滤和路由消息变得复杂。需要设计高效的策略来确保消息能够准确地传递给感兴趣的订阅者,同时避免不必要的消息传递。

  3. 系统监控和调试:
    🌈 由于发布订阅模式的异步和分布式特性,监控和调试变得困难。跟踪消息的流动、识别和处理故障点需要额外的工具和日志记录策略。

  4. 性能优化:
    🚀 在高负载下,消息代理或事件总线可能成为性能瓶颈。需要优化这些组件以处理大量的消息流,并确保系统的响应时间和吞吐量满足需求。

  5. 安全性保障:
    🎭 确保消息在传输和存储过程中的安全性是一个重要挑战。需要实施加密、身份验证和访问控制等安全措施来保护敏感数据免受未经授权的访问和篡改。

    局限

  1. 不适合实时性要求极高的场景:
    😂 由于消息传递的异步性和可能的延迟,发布订阅模式可能不适合那些对实时性要求极高的应用,如金融交易系统或实时控制系统。

  2. 额外的管理和维护开销:
    😢 引入消息代理或事件总线增加了系统的复杂性,需要额外的管理和维护工作。这包括配置、部署、监控和升级中间件等任务。

  3. 可能的消息丢失:
    🧐 在分布式环境中,网络故障、进程崩溃或存储故障可能导致消息丢失。虽然可以通过持久化、重试和确认机制来减少这种风险,但完全避免消息丢失是困难的。

  4. 数据一致性挑战:
    🤔 在分布式系统中使用发布订阅模式时,确保数据的一致性和完整性是一个挑战。特别是在处理跨多个订阅者的状态时,需要仔细设计更新策略和同步机制。

  5. 订阅者依赖管理:
    😅 当订阅者之间存在依赖关系时,管理这些依赖关系可能变得复杂。需要确保订阅者以正确的顺序接收到消息,并处理可能的依赖冲突。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/750281.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Python深度学习之路:TensorFlow与PyTorch对比【第140篇—Python实现】

Python深度学习之路&#xff1a;TensorFlow与PyTorch对比 在深度学习领域&#xff0c;TensorFlow和PyTorch是两个备受青睐的框架&#xff0c;它们为开发人员提供了强大的工具来构建和训练神经网络模型。本文将对这两个框架进行对比&#xff0c;探讨它们的优势和劣势&#xff0…

自适应日落动态卡通动画404页面模板

源码介绍 自适应日落动态卡通动画404页面模板&#xff0c;源码由HTMLCSSJS组成&#xff0c;记事本打开源码文件可以进行内容文字之类的修改&#xff0c;双击html文件可以本地运行效果&#xff0c;也可以上传到服务器里面 下载地址 自适应日落动态卡通动画404页面模板

Java高级互联网架构师之路:排查当前JVM错误的步骤

程序 这个程序是有问题的,我们通过一些命令来分析这个程序究竟是哪里出了问题。首先把当前的程序通过SSH工具传输到centos系统中,之后我们就可以在linux环境下编译和执行。 注意一点:上面类的名字是Z,但是在linux环境下,我们将其改为了AA,并且文件名改为了AA,所以文章下…

计算机网络----计算机网络的基础

目录 一.计算机网络的相关概念 二.计算机网络的功能 三.计算机网络的发展 四.计算机网络的组成 五.计算机网络的分类 六.计算机的性能指标 1.速率 2.带宽 3.吞吐量 4.时延 5.时延带宽积 6.往返时延RTT 7.利用率 七.计算机的分层结构 八.ISO/OSI参考模型 九.OSI…

软考80-上午题-【面向对象技术3-设计模式】-结构型设计模式03

一、外观模式 1-1、意图 为子系统中的一组接口提供一个一致的界面。 Facade 模式定义了一个高层接口&#xff0c;这个接口使得这一子系统更加容易使用。 1-2、结构 Facade 知道哪些子系统类负责处理请求&#xff1a;将客户的请求代理给适当的子系统对象。Subsvstem classes …

Ansible playbook格式 语法 Ansible playbook格式入门演示 基础了解

目录 Ansible playbook格式简介实例运行结果 Ansible playbook格式简介 playbook由YMAL语言编写。YAML( /ˈjməl/ )参考了其他多种语言&#xff0c;包括&#xff1a;XML、C语言、Python、Perl以及电子邮件格式RFC2822&#xff0c;Clark Evans在2001年5月在首次发表了这种语言…

macOS 安装 NetLogo 6.4.0

netlogo 下载地址 NetLogo-6.4.0.dmg参考 netlogo 官网

C#,图论与图算法,图(Graph)的数据结构设计与源代码

因为后面即将发布的大量有关“图”的算法与源代码都需要用到下面的这些基础数据&#xff0c;为避免大家去下载&#xff0c;特意先发布于此。 一、图&#xff08;Graph&#xff09;的基础知识 图&#xff08;Graph&#xff09;是一组对象的图示&#xff0c;其中一些对象对通过链…

STM32(TIM定时器中断)

理论知识 定时器定时中断 接线图 定时器工作配置步骤 定时中断和内外时钟源选择 定时器中需要使用的函数 程序实现效果&#xff1a; void TIM_DeInit(TIM_TypeDef* TIMx); **// 恢复定时器的缺省配置**void TIM_TimeBaseInit(TIM_TypeDef* TIMx, TIM_TimeBaseInitTypeDef*TIM…

数据可视化-ECharts Html项目实战(2)

在之前的文章中&#xff0c;我们学习了如何创建简单的折线图&#xff0c;条形图&#xff0c;柱形图并实现动态触发&#xff0c;最大最小平均值。想了解的朋友可以查看这篇文章。同时&#xff0c;希望我的文章能帮助到你&#xff0c;如果觉得我的文章写的不错&#xff0c;请留下…

波奇学Linux:线程安全和自选锁和读写锁

STL不是线程安全的 单例模式的线程安全 自选锁&#xff1a;当线程申请锁失败时&#xff0c;不是挂起&#xff0c;而是一直申请 挂起等待锁 &#xff1a;当线程申请锁失败时&#xff0c;把锁挂起 一般临界区时间短的适合自选锁&#xff0c;长的适合挂起等待锁

Unity的AssetBundle资源运行内存管理的再次深入思考

大家好&#xff0c;我是阿赵。   这篇文章我想写了很久&#xff0c;是关于Unity项目使用AssetBundle加载资源时的内存管理的。这篇文章不会分享代码&#xff0c;只是分享思路&#xff0c;思路不一定正确&#xff0c;欢迎讨论。   对于Unity引擎的资源内存管理&#xff0c;我…

Visual Studio项目模板的创建与使用

Visual Studio项目模板的创建、使用、删除 创建模板项目模板的使用模板的删除 创建模板 点击项目&#xff0c;点击导出模板 选择你要创建哪个项目的项目模板&#xff0c;点击下一步 输入你的模板名称并添加模板说明&#xff0c;方便记忆 项目模板的使用 点击创建新项目 输入刚刚…

[长城杯 2021 院校组]funny_js

[长城杯 2021 院校组]funny_js 审题 根据题名提示为js&#xff0c;再在ida中查看&#xff0c;基本可以确定为quickjs题 QuickJS 是一个快速、灵活且易于嵌入的 JavaScript 引擎&#xff0c;适用于需要在资源受限环境下运行 JavaScript 代码的场景。 工具准备 来到Linux&…

Qt QTableWidget 实现行选中及行悬浮高亮

表格整行的 selected、hover 高亮需求很常见&#xff0c;但使用 Qt 提供的开箱即用的方法根本无法实现这个需求&#xff08;至少在当前的时间节点是不行的&#xff09;&#xff1b;想要实现这个效果必须要费一点点力气&#xff0c;我们尽量选择较为简单的方法。 话不多说&…

高可用系统有哪些设计原则

1.降级 主动降级&#xff1a;开关推送 被动降级&#xff1a;超时降级 异常降级 失败率 熔断保护 多级降级2.限流 nginx的limit模块 gateway redisLua 业务层限流 本地限流 gua 分布式限流 sentinel 3.弹性计算 弹性伸缩—K8Sdocker 主链路压力过大的时候可以将非主链路的机器给…

Task-balanced distillation for object detection用于

Task-balanced distillation for object detection用于目标检测的任务平衡蒸馏 摘要 主流的目标检测器通常由分类和回归两个子任务组成&#xff0c;由两个并行头部实现。这种经典的设计范式不可避免的导致分类得分和定位质量&#xff08;IOU&#xff09;之间的空间分布不一致…

【CTF web1】

CTF web 一、CTF web -PHP弱类型1、是否相等&#xff1f;2、转换规则: 二、CTF web -md5绕过1、若类型比较绕过2、null绕过3、碰撞绕过 三、习题 一、CTF web -PHP弱类型 1、是否相等&#xff1f; &#xff1a;在进行比较的时候&#xff0c;会先判断两种字符串的类型是否相等&…

切面条-蓝桥杯?-Lua 中文代码解题第1题

切面条-蓝桥杯&#xff1f;-Lua 中文代码解题第1题 一根高筋拉面&#xff0c;中间切一刀&#xff0c;可以得到2根面条。 如果先对折1次&#xff0c;中间切一刀&#xff0c;可以得到3根面条。 如果连续对折2次&#xff0c;中间切一刀&#xff0c;可以得到5根面条。 那么&#xf…

【ollama】(7):使用Nvidia Jetson Nano设备,成功运行ollama,运行qwen:0.5b-chat,速度还可以,可以做创新项目了

1&#xff0c;视频地址 https://www.bilibili.com/video/BV1Pj421o7W5/ 【ollama】&#xff08;7&#xff09;&#xff1a;使用Nvidia Jetson Nano设备&#xff0c;成功运行ollama&#xff0c;运行qwen:0.5b-chat&#xff0c;速度还可以&#xff0c;可以做创新项目了 2&#x…