如何在Ruby中编写微服务

Python013

如何在Ruby中编写微服务,第1张

所以,我想出一份力。让我们先来看看如何在Ruby中编写和部署微服务。

想象一下这个场景:我们需要编写一个微服务,其职责是发邮件。它收到的信息如下:

{

'provider': 'mandrill',

'template': 'invoice',

'from': '[email protected]',

'to': '[email protected]',

'replacements': {

'salutation': 'Jack',

'year': '2016'

Zeebe架构主要包含4大组件:client, gateway, brokers 以及 exporters。

客户端向Zeebe发送指令:

发布工作流(deploy workflows)

执行业务逻辑(carry out business logic)

创建工作流实例(start workflow instances)发布消息(publish messages)激活任务(activate jobs)完成任务(complete jobs)失败任务(fail jobs)

处理运维问题(handle operational issues)

更新实例流程变量(update workflow instance variables)解决异常(resolve incidents)

1.客户端程序可以完全独立于Zeebe扩缩容 - Zeebe brokers不执行任何业务逻辑。

2.客户端是嵌入到应用程序(执行业务逻辑的微服务)的库,用于跟Zeebe集群连接通信。

3.客户端通过基于HTTP/2协议的gRPC与Zeebe gateway连接。

4.Zeebe官方提供了Java和Go客户端。社区提供了C#,Ruby,Java客户端实现。

5.Client中,执行单独任务的单元叫JobWorker。

Gateway作为Zeebe集群的入口,转发请求到brokers。Gateway是无状态(stateless)无会话(sessionless)的,可以按需增加节点,以负载均衡及高可用。

Broker是分布式的流程引擎,维护运行中流程实例的状态。Brokers可以分区以实现横向扩容、副本以实现容错。通常情况下,Zeebe集群都不止一个节点。

需要重点强调的是,broker不包含任何业务逻辑,它只负责:

处理客户端发送的指令

存储和管理运行中流程实例的状态

分配任务给job workers

Brokes形成一个对等网络(peer-to-peer),这样集群不会有单点故障。集群中所有节点都承担相同的职责,所以一个节点不可用后,节点的任务会被透明的重新分配到网络中其他节点。

exporter系统提供Zeebe内状态变化的事件流。这些事件流数据有很多潜在用处,包括但不限于:

监控当前运行流程实例的状态

分析历史的工作流数据以做审计或BI

跟踪Zeebe抛出的异常(incident)

exporter提供了简洁的API,可以流式导出数据到任何存储系统。Zeebe官方提供开箱即用的Elasticsearch exporter,社区也提供了其他exporters。

Zeebe能做到高吞吐、高可用的微服务编排,得益于三个关键实现:

1.Zeebe设计之初就考虑了分布式部署,可以在不依赖外部组件的情况下,搭建一个zeebe broker集群,集群中节点组成一个对等的网络(peer-to-peer network)。在网络中,所有的节点都有相同的职责,保整集群不会有单点故障。

2.Zeebe内部抽象了一个只追加写的队列(可以类比理解成kafka的topic),来处理和存储数据。当集群有多个broker节点时,会将队列划分成多个分区(partitions,或者分片shards),分布到各个节点上。每个分区有多个副本(replicas)。在所有的副本中,会根据raft协议选出一个leader,leader负责接收请求和执行所有处理逻辑。其他broker上的副本就是被动的跟随者(passive followers)。当leader不可用时,followers会透明地选出新的leader。

Zeebe消息驱动架构,体现在两个方面:

1.Zeebe Broker内部使用队列(即LogStream,只追加写),异步处理请求

2.Zeebe JobWorker和Broker使用发布订阅的模式交互,当工作流任务状态发生变化,Broker会发布相应事件。JobWorker通过轮询的方式,订阅处理自己相关的事件。

2.2.1 Broker内部流处理模型

Zeebe内部实现,其实就是一系列作用在记录流(record streams)上的流处理器(stream processors)。流处理模型作为一个统一的实现方式,提供:

指令协议(command protocol,即请求响应)

记录导出(record export / streaming)

工作流演算(evaluation, 异步后台任务)

当Zeebe处理任务、工作流或者内部维护时,会产生有序的记录流:

————————————————————————————————————

原文链接: https://www.sohu.com/a/456966231_100093134

Docker技术在Ruby社区是有影响力的,我所知道的一些创业团队很早就在运用它来解决环境管理、持续集成以及部署的问题了。但是,也有一些同学尚未注意到这个技术,或者了解过后认为它不是很重要,所以我想讨论一下Docker对Ruby系技术的帮助。

有的人可能对Docker技术不太了解,不妨参考论坛里的这篇文章(https://ruby-china.org/topics/22004 )以及肖德时写的系列文章(http://www.infoq.com/cn/articles/docker-core-technology-preview )。 Docker 与 Vagrant

我一直很喜欢Vagrant这个工具,两三年前就用它来进行自己项目的环境维护,那时候主要是做测试,由于Vagrant将操作系统环境进行了标准化,我很容易就能让自己的应用系统以及相关的测试结果保持稳定。

Vagrant还有一个好处,Ruby社区比较偏爱Mac,但是线上的系统基本都是Linux,所以开发环境所做的测试是有疑问的,特别是遇到一些有so依赖的gem,这时一个和线上完全一样的环境就特别重要。

其实上面的表述不太准确,Vagrant也有各种provider,我所说的场景,基本上都是virtualbox的provider,所以这些地方正确的说法是 vagrant/virtualbox。

和Docker相比,vagrant/virtualbox组合的成本还是很高的,无论是setup一个环境还是reset一个环境,都需要一段时间的等待,Vagrant只是把virtualbox的操作DSL了而已,底层的做法没有变化。而Docker由于本质上就是一个进程,因此天生就是轻量级的。对于运行时间在分钟级别的自动化测试工作,Docker显然有很大的优势。

当然,也有人会认为Docker不能模拟完整的操作系统,不过这恐怕是一个优点而不是缺点。我在以前的文章中已经说过了,这里概述一下主要观点——

Docker简化了操作系统这个基础设施,让应用精简为其最核心的形态——携带有限资源的进程,在此基础上更有利于架构上的最佳实践。

而对Ruby工程师而言,这个“最佳实践”中肯定少不了的一条就是——微服务。

微服务

Ruby工程师中有很多就是Rails工程师,而Rails实际上更倾向于单体架构,因此后来社区的工程师们才需要在实际工作中总结1 to 30这样的实践。

其实微服务本身不是个教条,即使没有人教,我们也常常自发的去进行服务化改造,但是这个工作并不容易,主要是会受到一些问题的掣肘,比如运维复杂度和系统测试成本会大幅度上升等等。

处理这些困难,首先当然是看是否必要,一些简单场景我们也可以用单体架构直接搞定,但是我们很容易会注意到,这两年大家越来越多的提到了微服务或者服务化,这背后其实是有趋势的——各种业务形态都在朝着互联网级的用户规模推进,同时大家都在努力从每一个用户的各种维度上挖掘价值(这导致了大数据的需求),这些场景变得越来越常见,单体架构是难以支持的。

既然微服务或者服务化不可避免,那么就要有相应的对策,虽然Ruby社区也有很多人在不同问题点上针对微服务进行改进(比如完善异步化框架,以及对服务协议的探索等),但是在基础设施层面,Docker是最重要的武器,没有之一!

对Ruby工程师来说,Docker能做两件事:约束边界和建立通用基础服务。

约束服务边界

Ruby项目Docker化,并不是简单换个虚拟机那么简单,我们会面对拆分的压力,相信很多人尝试用Dockerfile来描述自己的项目的时候都会觉得束手束脚,但这些地方其实是促使我们想清楚——这个应用到底要做什么?它和外界是什么关系?对于外界的变化它如何响应?失败后怎样恢复?

这类的问题对系统架构非常重要。比如应用到底要做什么,这是让工程师去思考系统的目标,无论是提供web服务,管理调度后台任务,还是提供实时分析,它们都应该有一个尽可能单一的目标,在这个基础之上,我们建立的服务才有可能是易测试、易扩展和易维护的。

其它问题也类似,这些地方以前如果没有留意,很可能不是没问题,而是没意识到,使用Docker有助于我们意识到这些问题。

另外补充一点,由于Ruby项目不能完全脱离动态库依赖(java大都可以),本身的打包机制又没有自包含结构(gem+bundle不包括动态库,相比之下,Golang是静态联编的),在分布式环境中的交付和软件包分发其实是有着先天不足的,Docker的Image恰好补上了这一块,简直是睡觉时候有人送枕头了。 建立通用基础服务

当我们将应用系统分裂为各种服务并明确其边界以后,就出现了“分久必合”的问题,这很自然,服务化改造并不是各行其是,应用之间还是要协作,而对应用的运维——服务发现、水平扩展、容错等等——都需要基础设施的支持。

以前,对于这种运维基础设施,各公司甚至同一个公司的各个团队的做法都千差万别,但是借助Docker以及周边的生态圈,我们可以很容易的得到通用的服务发现框架,享受自动的部署和弹性扩展。

更好的消息是,这些基础服务是通用的——不但不关心是rails还是sinatra,甚至根本不关心是不是Ruby。

这也很好理解,Docker是对进程这个操作系统工作单元进行了简化约束,而进程的概念本来就是与语言和框架无关的。

这使得Ruby工程师以及Ruby项目可以更为自由的选择合适的技术去扩展公司的产品线。

延伸技术框架

Ruby 刚出来的时候,有很多来自 Java 社区的工程师加入其中(我也算是其中之一吧),很多人最大的感受是——视野被打开了。曾经象口号一样的“all in java”变成了落后的标志,大家意识到,一把钥匙开一把锁,用最合适的技术针对性的解决问题才是聪明的做法,单纯排斥某种技术或者语言框架并不明智。

这个道理在Ruby/RoR应用开发中也不例外,但是不少人在使用了几年Ruby以后都会遇到一个问题——“Ruby确实很适合开发Web,但是现在有些问题需要使用XX技术,而我们的系统严重依赖Ruby环境,这该怎么办呢?”

我认为问题就出在“系统严重依赖Ruby环境”上,研发的基础设施,比如配管、自动化测试、打包、部署,不应该仅满足一种技术或是语言,它一开始就要考虑到通用性,否则我们就只能“手里拿着锤子,看谁都像钉子”。

Docker本身和语言无关,它唯一的约束大概就是要运行在Linux上,这个对互联网服务端系统来说也算是标准了,问题不大。所以,我们应该以Docker为核心打造研发的基础设施,这将是未来的一笔重要投资。

当然,为未来画饼是危险的,不过还好,Docker领域的创业很活跃,有很多团队和公司已经做了相当多的基础工作,对于Ruby工程师和Ruby创业团队,去用现成的基础设施其实更方便。