软件体系结构期末复习(三)
此文章仅用作期末复习
一、Event-driven Architecture Event-driven Architecture是一种软件设计模式,其中系统组件通过事件和消息相互通信,以响应事件和完成任务。它是一种异步编程模型,用于解决传统同步编程模型中出现的并发问题和性能瓶颈。
其作用和意义在于:
实现系统组件的松耦合:组件之间的通信是通过事件和消息进行,因此可以实现组件的松耦合,从而更容易实现复杂系统的开发和维护。
提高系统的可扩展性:事件驱动的系统具有高可扩展性,因为不同的组件可以独立地运行,而不会对其他组件产生影响。
实现高并发、高性能的系统:事件驱动的系统可以同时处理多个事件和任务,从而实现高并发、高性能的系统。
下面是一个简单的Python代码示例,展示了如何使用事件驱动的方式实现一个简单的聊天室:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 import socketimport selectclass ChatRoom : def __init__ (self ): self.clients = [] self.server = socket.socket(socket.AF_INET, socket.SOCK_STREAM) self.server.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1 ) self.server.bind(('localhost' , 8000 )) self.server.listen(5 ) def run (self ): self.clients.append(self.server) while True : readable, _, _ = select.select(self.clients, [], []) for sock in readable: if sock == self.server: client, _ = sock.accept() self.clients.append(client) print ('New client joined' ) else : data = sock.recv(1024 ) if data: print (f'Received message: {data.decode()} ' ) for client in self.clients: if client != self.server and client != sock: client.send(data) else : sock.close() self.clients.remove(sock) print ('Client left' )if __name__ == '__main__' : room = ChatRoom() room.run()
在这个例子中,ChatRoom类表示了一个聊天室。在聊天室中,每个客户端可以发送消息,其他客户端会收到这些消息。聊天室使用了select模块来实现事件驱动的方式。每当有新客户端加入或有客户端发送消息时,聊天室就会响应相应事件,并执行对应的处理逻辑。
二、Redis pub/sub(发布/订阅) Redis pub/sub(发布/订阅)是一种消息通信模式,用于解耦消息的发送者和接收者。它包括两个主要组件:发布者和订阅者。发布者可以将消息发布到一个或多个特定频道,而订阅者则可以订阅一个或多个频道以接收该频道的消息。
Redis pub/sub的主要作用和意义是在分布式系统中传递消息。它可以帮助不同的服务之间进行通信,使得系统更加灵活和可扩展,同时也可以减少服务之间的依赖,降低耦合度。
在Redis中,channel是指一个消息的频道,每个频道可以有多个订阅者,同时也可以有一个或多个发布者。当一个消息被发布到某个频道时,所有订阅该频道的订阅者都将会收到该消息。
下面是一个使用Python实现Redis pub/sub的例子:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 import redis r = redis.StrictRedis(host='localhost' , port=6379 , db=0 ) p = r.pubsub() p.publish('news' , 'Hello, World!' ) s = r.pubsub() s.subscribe('news' )for message in s.listen(): print (message)
在上面的例子中,我们首先创建了一个Redis连接,并使用发布者发布了一条消息到’news’频道。接着,我们创建了一个订阅者并订阅了’news’频道。最后,我们使用listen()函数读取订阅到的消息,并打印出来。
三、Message Processor Message Processor是指一个程序或服务,用于处理消息或事件,通常是从消息队列或事件总线中获取消息或事件,然后对其进行转换、处理或分发。其作用和意义是帮助系统进行异步处理,提高系统的可扩展性和可靠性。
举个例子,假设有一个在线购物网站,用户下单后需要发送一封确认邮件和将订单信息保存到数据库中。这些操作需要时间,如果在用户下单时即时处理,会让用户等待很久,影响用户体验。因此,可以将这些操作作为消息发送到消息队列中,然后由Message Processor进行处理,这样就可以让用户快速完成下单操作,而不需要等待。
以下是用Python实现一个简单的Message Processor的代码示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 import timeimport randomimport threadingfrom queue import Queueclass MessageProcessor : def __init__ (self ): self.queue = Queue() self.is_running = True def start (self ): while self.is_running: if not self.queue.empty(): message = self.queue.get() self.process_message(message) time.sleep(0.1 ) def stop (self ): self.is_running = False def put_message (self, message ): self.queue.put(message) def process_message (self, message ): print (f"processing message: {message} " ) time.sleep(random.randint(1 , 5 )) print (f"message processed: {message} " )if __name__ == "__main__" : processor = MessageProcessor() def add_message (): while True : message = random.randint(1 , 10 ) processor.put_message(message) time.sleep(1 ) threading.Thread(target=add_message, daemon=True ).start() processor.start()
该示例中,Message Processor使用队列来存储消息,使用一个线程来处理消息,可以不断地向队列中添加消息,而Message Processor会不断地获取并处理消息。其中,process_message方法可以根据实际需求实现具体的消息处理逻辑。
四、依赖反转原则(DIP) 依赖反转原则(DIP)是面向对象设计中的一项原则,它强调高层模块不应该依赖于低层模块,两者都应该依赖于抽象。抽象不应该依赖于具体实现,具体实现应该依赖于抽象。这样做的好处是可以提高系统的灵活性和可扩展性。
DIP的意义在于,通过将依赖关系的方向反转,可以使得代码更加灵活和易于修改。具体而言,它使得系统更容易实现替换和扩展具体实现,从而增强了系统的可维护性和可测试性。
以下是一个使用DIP原则的Python代码示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 class DataSource (): def __init__ (self, data ): self._data = data def read_data (self ): pass class FileDataSource (DataSource ): def read_data (self ): with open (self._data) as f: data = f.read() return dataclass DatabaseDataSource (DataSource ): def read_data (self ): connection = pymysql.connect(host='localhost' , user='user' , password='password' , db='database' ) with connection.cursor() as cursor: sql = "SELECT * FROM data" cursor.execute(sql) data = cursor.fetchone() return datadef process_data (data_source ): data = data_source.read_data() pass file_data_source = FileDataSource('data.txt' ) process_data(file_data_source) database_data_source = DatabaseDataSource() process_data(database_data_source)
在这个示例中,我们定义了一个DataSource
类作为抽象基类,其中包含了read_data
方法,但并不实现该方法。我们再定义了两个子类FileDataSource
和DatabaseDataSource
,它们分别从文件和数据库中读取数据,并实现了read_data
方法。process_data
函数接受一个DataSource
对象,并调用其read_data
方法来读取数据并进行后续处理。
这个示例中,我们使用了DIP原则,通过定义抽象基类DataSource
,将依赖关系从高层模块转移到了抽象基类中。这样做的好处是,如果我们需要以后增加其他数据源,只需要定义一个新的子类,并实现抽象基类中的read_data
方法即可。这个过程是非常容易的,因为我们定义了一个抽象基类来规范子类的行为,并将依赖关系的方向反转,使得高层模块只依赖于抽象,而不依赖于具体实现。
五、Thin Web Controller Principle Thin Web Controller Principle是一种设计原则,指的是将web应用程序中的控制器逻辑保持尽可能的简洁和薄。这种原则的作用是避免控制器逻辑过于复杂和混乱,同时使得控制器逻辑的维护更加容易和清晰。
Thin Web Controller Principle通常包括以下几个方面的内容:
控制器应该只负责数据的验证和处理,不应该直接参与业务逻辑的处理。
控制器应该尽可能的保持短小精悍,不要过多的包含业务逻辑。
控制器应该尽可能的避免处理一些复杂的业务场景,应该将这些业务场景转交给其他的模块来处理。
Python代码示例:
在一个Flask框架的项目中,实现一个简单的登录接口,代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 from flask import Flask, request, jsonify app = Flask(__name__)@app.route('/login' , methods=['POST' ] ) def login (): username = request.form.get('username' ) password = request.form.get('password' ) if not username or not password: return jsonify({'code' : 400 , 'msg' : '用户名或密码不能为空' }) if username == 'admin' and password == '123456' : return jsonify({'code' : 200 , 'msg' : '登录成功' }) else : return jsonify({'code' : 400 , 'msg' : '用户名或密码错误' })if __name__ == '__main__' : app.run(debug=True )
在上面的代码中,我们只处理了用户的输入和验证,没有涉及到具体的业务逻辑。这样可以避免控制器逻辑的复杂性,使得代码更加简洁易懂。具体的业务逻辑可以放在其他的模块中处理,这样可以更好地实现代码的模块化和可维护性。
六、Dependency Injection (DI)依赖注入 Dependency Injection(DI)是一种设计模式,它的主要目的是通过将对象的依赖关系交给外部管理,从而实现更松散的耦合和更高的可重用性。简而言之,DI就是让对象对它所需要的依赖项进行解耦。
在DI中,对象不再自己创建它所需要的依赖项,而是由外部容器或框架来注入它们。这意味着对象只需要专注于它自己的责任,而不用担心如何满足它的依赖项。
下面是一个简单的Python示例,演示了如何使用DI实现依赖项注入:
1 2 3 4 5 6 7 8 9 10 11 class EmailService : def send_email (self, message ): print (f"Sending email: {message} " ) class UserService : def __init__ (self, email_service ): self.email_service = email_service def register (self, name, email ): print (f"Registering user: {name} ({email} )" ) self.email_service.send_email(f"Welcome {name} !" )
在上面的示例中,我们定义了两个类,一个是EmailService
,用于发送电子邮件,另一个是UserService
,用于注册用户。UserService
类有一个依赖项email_service
,它的类型是EmailService
。在__init__
构造函数中,我们将EmailService
实例作为参数传入,并将它保存到self.email_service
中。
现在,我们需要创建UserService
实例并调用它的register
方法。当我们创建UserService
实例时,我们需要将一个EmailService
实例注入到它的构造函数中。下面是一个示例:
1 2 3 email_service = EmailService() user_service = UserService(email_service) user_service.register("John" , "john@example.com" )
在上面的示例中,我们首先创建一个EmailService
实例,然后将它注入到UserService
的构造函数中,最后我们调用UserService
的register
方法,该方法会使用EmailService
实例来发送欢迎邮件给新用户。这就是依赖注入的基本流程。
当我们使用DI时,我们需要一个容器或框架来管理依赖项的注入。这些容器或框架会自动创建和管理对象的实例,并将它们的依赖项注入到它们的构造函数中。下面是一个简单的Python示例,演示了如何使用injector
模块来进行DI:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 from injector import Injector, injectclass EmailService : def send_email (self, message ): print (f"Sending email: {message} " ) class UserService : @inject def __init__ (self, email_service: EmailService ): self.email_service = email_service def register (self, name, email ): print (f"Registering user: {name} ({email} )" ) self.email_service.send_email(f"Welcome {name} !" ) injector = Injector() user_service = injector.get(UserService) user_service.register("John" , "john@example.com" )
在上面的示例中,我们使用了injector
模块来创建一个Injector
实例,并使用它来获取一个UserService
实例。UserService
的构造函数中声明了一个名为email_service
的依赖项,并使用@inject
装饰器将它标记为需要注入的依赖项。当我们调用injector.get(UserService)
时,injector
会自动创建一个EmailService
实例,并将它注入到UserService
实例的构造函数中。
总之,使用DI可以让我们的代码更加模块化和可重用,使得不同模块之间的耦合更加松散。在Python中,我们可以使用injector
模块等工具来实现依赖项注入。