
如果不使用分布式系统,现代世界简直是不可想象的。 即使是最简单的移动应用程序也具有API,通过它可以连接到云存储。 但是,分布式系统的设计仍然是一门艺术,而不是一门精确的科学。 早就该建立一个坚实的基础了,如果您想对分布式系统的创建,支持和操作充满信心,请从本书开始!
云计算技术和Kubernetes方面的著名专家Brendan Burns在这项小型工作中提出了正确设计分布式系统所需的绝对最低要求。 本书描述了设计分布式系统的永恒模式。 它不仅可以帮助您从头创建此类系统,还可以有效地转换现有系统。
摘录。 装饰图案。 转换请求或响应
当您需要简单的功能来处理输入数据然后将其传输到其他服务时,FaaS是理想的选择。 这种模式可用于扩展或修饰另一服务发送或接收的HTTP请求。 该图案在图1中示意性地示出。 8.1。
顺便说一下,在编程语言中,有几种类似于该模式的类比。 特别是,Python具有功能装饰器,其功能类似于请求或响应装饰器。 由于修饰转换不存储状态,并且通常随服务的发展而事后添加,因此它们非常适合作为FaaS实施。 另外,FaaS的轻便性意味着您可以尝试使用不同的装饰器,直到找到与服务更紧密集成的装饰器为止。
将默认值添加到HTTP RESTful API请求的输入参数中可证明Decorator模式的好处。 如果调用者未指定,许多API请求的字段都需要填写合理的值。 例如,您希望该字段默认为true。 传统的JSON很难做到这一点,因为默认的空字段为null,通常将其解释为false。 要解决此问题,您可以在API服务器之前或在应用程序代码中添加替换默认值的逻辑(例如,如果(field == null)field = true。 但是,这两种方法都不是最佳方法,因为默认替换机制在概念上与请求处理无关。 相反,我们可以使用FaaS装饰器模式,该模式可以在用户和服务实现之间转换请求。
考虑到前面有关单节点模式的部分中所说的内容,您可能想知道为什么我们没有设计适配器容器形式的默认替换服务。 这种方法很有意义,但也意味着默认查找服务的缩放和API服务本身的缩放变得相互依赖。 替换默认值是一项易于计算的操作,为此,很可能不需要很多服务实例。
在本章的示例中,我们将使用无kube FaaS框架(https://github.com/kubeless/kubeless)。 Kubeless部署在Kubernetes容器协调器服务之上。 如果您已经准备好Kubernetes集群,请继续安装Kubeless,可以从相应的站点(https://github.com/kubeless/kubeless/releases)下载。 一旦有了kubeless可执行文件,就可以使用kubeless install命令将其安装在集群中。
Kubeless是作为第三方Kubernetes API附加组件安装的。 这意味着在安装后,它可以用作kubectl命令行工具的一部分。 例如,可以通过运行kubectl get functions命令来查看集群中部署的功能。 当前群集中未部署任何功能。
工作坊 在请求处理之前替换默认值
您可以使用在RESTful调用中将默认值替换为用户未设置值的参数的示例,来演示FaaS中Decorator模式的有用性。 使用FaaS,这非常简单。 默认的查询功能是用Python编写的:
# -, # def handler(context): # obj = context.json # "name" , # if obj.get("name", None) is None: obj["name"] = random_name() # 'color', # 'blue' if obj.get("color", None) is None: obj["color"] = "blue" # API- # # return call_my_api(obj)
将此函数保存到名为defaults.py的文件中。 请记住将call_my_api调用替换为所需的API。 可以使用以下命令将此默认替换函数注册为无库函数:
kubeless function deploy add-defaults \ --runtime python27 \ --handler defaults.handler \ --from-file defaults.py \ --trigger-http
要对其进行测试,可以使用kubeless工具:
kubeless function call add-defaults --data '{"name": "foo"}'
Decorator模式显示了使用诸如验证或替换默认值等附加功能来适应和扩展现有API的过程是多么容易。
事件处理
大多数系统都是面向查询的-它们处理用户和API请求的连续流。 尽管如此,还是有很多面向事件的系统。 在我看来,请求和事件之间的区别在于会话的概念。 请求是较大的交互过程(会话)的一部分。 通常,每个用户请求都是与Web应用程序或整个API交互的过程的一部分。 我将事件视为本质上是异步的“一次性”事件。 事件很重要,应该相应地进行处理,但是它们是从交互的主要上下文中拉出来的,它们的答案只有在一段时间之后才会出现。 事件的一个示例是用户对某种服务的订阅,这将导致发送一封问候信。 将文件上传到共享文件夹,这将导致向该文件夹的所有用户发送通知; 甚至为重新启动计算机做准备,这将通知操作员或自动化系统需要采取适当的措施。
由于这些事件在很大程度上是独立的并且没有内部状态,并且它们的频率非常可变,因此它们非常适合在面向事件的FaaS架构中工作。 它们通常部署在“战斗”应用服务器旁边,以提供附加功能或响应新兴事件对数据进行后台处理。 另外,由于将新类型的已处理事件不断添加到服务中,因此功能部署的简单性使其适合于实现事件处理程序。 而且,由于每个事件在概念上都是彼此独立的,因此在基于功能构建的系统中,关系的被迫削弱使得我们可以降低其概念复杂性,从而使开发人员可以专注于仅处理一种特定类型事件的必要步骤。
将面向事件的组件集成到现有服务中的一个特定示例是两因素身份验证的实现。 在这种情况下,事件将是用户对系统的登录。 服务可以为此操作生成一个事件,并将其传递给处理程序函数。 根据所发送的代码和用户的联系方式,处理器将以文本消息的形式向他发送身份验证代码。
工作坊 实施两因素身份验证
两因素身份验证表明,对于用户进入系统而言,他需要他知道的东西(例如密码)和他所拥有的东西(例如电话号码)。 双重身份验证比仅使用密码要好得多,因为攻击者必须窃取您的密码和电话号码才能获得访问权限。
在计划实施双重身份验证时,您需要处理用于生成随机代码的请求,将其注册到登录服务并将消息发送给用户。 您可以将实现此功能的代码直接添加到登录服务本身。 这使系统复杂化,使其更加单一。 发送消息应与生成登录网页的代码同时进行,这可能会导致一定的延迟。 此延迟会降低用户与系统交互的质量。
最好创建一个FaaS服务,该服务将异步生成一个随机数,然后将其注册到登录服务中并将其发送到用户的电话。 因此,登录服务器可以简单地执行对FaaS服务的异步请求,该请求将并行执行相对慢的注册和发送代码的任务。
要查看其工作原理,请考虑以下代码:
def two_factor(context): # code = random.randint(1 00000, 9 99999) # user = context.json["user"] register_code_with_login_service(user, code) # Twillio account = "my-account-sid" token = "my-token" client = twilio.rest.Client(account, token) user_number = context.json["phoneNumber"] msg = ", {}, : {}.".format(user, code) message = client.api.account.messages.create(to=user_number, from_="+1 20652 51212", body=msg) return {"status": "ok"}
然后在kubeless中注册FaaS:
kubeless function deploy add-two-factor \ --runtime python27 \ --handler two_factor.two_factor \ --from-file two_factor.py \ --trigger-http
用户输入正确的密码后,可以从客户端JavaScript代码异步生成此函数的实例。 Web界面可以立即显示用于输入代码的页面,用户一旦收到代码,便可以通知他有关已在其中注册该代码的登录服务。
因此,FaaS方法极大地促进了简单,异步,面向事件的服务的开发,该服务在用户登录系统时启动。
活动传送带
实际上,有许多应用程序更容易视为松散耦合事件的管道。 事件管道通常类似于良好的旧流程图。 它们可以表示为相关事件同步的有向图。 在事件管道模式的框架内,节点对应于功能,连接它们的弧线对应于HTTP请求或其他类型的网络调用。
通常,在容器的各个元素之间没有共同的状态,但是可以有共同的上下文或其他参考点,以此为基础在存储中进行搜索。
这样的管道和微服务架构之间有什么区别? 有两个重要区别。 服务功能和持续运行的服务之间的第一个也是最重要的区别是事件管道本质上是事件驱动的。 相反,微服务体系结构意味着一组持续工作的服务。 此外,事件管道可以是异步的,并且可以绑定各种事件。 很难想象如何将Jira的应用程序批准集成到微服务应用程序中。 同时,很容易想象它如何集成到事件管道中。
例如,考虑一个管道,其中源事件是将代码加载到版本控制系统中。 此事件将导致代码重建。 组装可能要花费几分钟,之后会生成一个事件,该事件触发已组装应用程序的测试功能。 根据组装成功与否,测试功能会采取不同的措施。 如果组装成功,则将创建一个应用程序,该应用程序必须得到本人的批准,以使新版本的应用程序可以运行。 关闭应用程序是使新版本投入运行的信号。 如果组装失败,Jira会请求检测到的错误,然后管道退出。
工作坊 实施用于注册新用户的管道
考虑实现用于注册新用户的一系列操作的任务。 创建新帐户时,总是执行一系列操作,例如,发送欢迎电子邮件。 还有一些可能并非每次都执行的操作,例如,订阅有关产品新版本(也称为垃圾邮件)的电子邮件通讯。
一种方法涉及创建用于创建新帐户的整体服务。 通过这种方法,一个开发团队将负责整个服务,该服务也将作为一个整体进行部署。 这使得难以进行实验并难以更改用户与应用程序的交互过程。
将用户登录的实现视为几种FaaS服务的事件管道。 通过这种分离,用户创建功能不知道用户登录期间会发生什么。 她有两个列表:
- 必要操作的列表(例如,发送欢迎电子邮件);
- 可选操作的列表(例如,时事通讯订阅)。
这些操作中的每一个也都实现为FaaS,并且操作列表仅是HTTP回调函数的列表。 因此,用户创建功能具有以下形式:
def create_user(context): # for key, value in required.items(): call_function(value.webhook, context.json) # # for key, value in optional.items(): if context.json.get(key, None) is not None: call_function(value.webhook, context.json)
现在也可以根据FaaS原理来实现每个处理程序:
def email_user(context): # user = context.json['username'] msg = ', {}, , !".format(user) send_email(msg, contex.json['email]) def subscribe_user(context): # email = context.json['email'] subscribe_user(email)
以这种方式分解后,FaaS服务变得更加简单,包含更少的代码行,并着重于一种特定功能的实现。 微服务方法简化了代码编写,但可能导致部署和管理三种不同微服务的困难。 FaaS方法在所有方面都证明了自己,因为使用它的结果是,管理一小段代码变得非常简单。 以事件管道的形式可视化创建用户过程的过程,还使我们能够从总体上理解用户登录时究竟发生了什么,只需跟踪管道中函数之间的上下文变化即可。
»这本书的更多信息可以
在出版商的网站上找到»
目录»
摘录设计师优惠券20%的折扣-
设计模式支付纸质版本的书后,将通过电子邮件发送该书的电子版本。