Rails 微服务架构
时间:2015-12-05 20:46 来源:oschina.net 作者:zicode
Rails 应用有各种类型,规模也各有不同。有的是一个独立的庞大的应用,全部应用都在同一个位置(包括管理界面、API、前端部分以及所有需要的模块)。另一些应用则是划分成一系列的微服务,服务之间互相通信,这样可以把整个应用切分成更易管理的部分。
这种微服务的架构被称为面向服务的架构( SOA )。虽然我见到过的 Rails 应用通常都倾向于成为独立的程序,不过开发者也完全可以选择让多个 Rails 程序,以及与其他语言或者框架编写的服务一起工作来完成任务。
Rails应用通常是独立的程序,但是没有理由阻止你尝试微服务的架构。
独立的程序不意味着一定写的不好,但是写的差的独立程序被拆成微服务后大多也是很糟糕的。有多种方式可以让你写出清晰的(更容易测试的)代码,同时在需要拆分应用的时候也更轻松。
使用微服务架构的 Rails 应用的用例
本文会讨论如何实现一个 CMS 的网站。可以假设是一家大的报纸或者博客,有很多作者负责投稿,用户可以按主题订阅内容。
Martin Fowler 有一篇很不错的文章,介绍了为什么编辑和发布应该分成两个不同的系统。我们的用例与此类似,另外我们还要添加两个模块:通知和订阅。
我们的 CMS 现在有四个主要的模块:
-
CMS 编辑器:作者和编辑用来创建、编辑和发布文章。
-
公共的网站:对外提供服务,浏览已发布的文章。
-
通知: 通知订阅者有新发布的文章。
-
订阅: 管理用户账号和订阅。

Rails 应用需要支持 SOA 吗?
是选择独立程序还是构建成微服务?这里没有对和错之分,不过下面的问题能帮你做出决定。
如何确定是选择独立程序还是微服务?
团队的组织结构是怎样的?
是否选择支持 SOA 通常与技术无关,而是在于开发团队的组织结构。
由四个团队分别负责一个主要的模块,比所有人在整个系统上一起工作要靠谱一些。如果你只有一个团队或者少数几个开发人员,一开始就决定采用微服务架构实际上会减慢开发的速度,这是因为需要为四个不同的组件直接的通信以及部署增加开发量。
不同的模块规模不一样?
对于本文的例子,有一个问题提现的很好,对外提供服务的公共网站肯定要比作者和编辑使用的 CMS 编辑器的访问压力要大很多。
如果这些模块都部署成分离的系统,我们就可以单独的控制它们的规模,为系统中不同的部分采用不同的缓存技术。你当然还是可以坚持采用单一的系统,但是那样的话你就只能为整个系统一次性确定其规模,而不是对不同的组件分开处理。
不同的模块使用不同的技术?
对于 CMS 编辑器,你也许想使用 Single Page Application (SPA),采用 React 或者 Angular 技术。而对外的网站,会使用更传统一些的服务端渲染的 Rails 应用(为了支持 SEO)。也许通知模块更适合 Elixir,因为这个语言对并发和并行处理支持不错。
模块的分离,使得你可以为每个模块选择最适合的编程语言。
边界定义
现在最重要的事情是定义好系统中模块之间的边界。
系统中的某个部分可能是某个外部 Server 的 Client。使用方法调用还是基于 HTTP 都不重要,它只需要知道它需要与系统中的其他部分进行通信。
为此我们需要定义清晰的边界。
当一篇文章发布时,会发生两件事:
-
首先会把文章的发布版本发送给对外的网站,它会返回一个发布后的 URL。
-
然后我们把刚创建的公开的 URL、话题、标题发送到通知模块,后者会通知到所有对话题感兴趣的订阅者。这一步可以是异步的,因为通常会耗费一些时间来通知到每一个用户,并且这个通知是不会有反馈的。
例如,下面的代码用来发布一篇文章。文章本身不会关心服务是通过方法调用还是 HTTP 来调用的。
?
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
class Publisher
attr_reader :article, :service
def initialize(article, service)
@article = article
@service = service
end
def publish
mark_as_published call_service
article
end
private
def call_service
service.new(
author: article.author,
title: article.title,
slug: article.slug,
category: article.category,
body: article.body
).call
end
def mark_as_published(published_url)
article.published_at = Time.zone.now
article.published_url = published_url
end
end
这种方式也可以让我们方便测试 Publisher 类的功能,我们可以使用 TestPublisherService 来做测试,它会返回预定义的应答。
?
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
require "rails_helper"
RSpec.describe Publisher, type: :model do
let(:article) {
OpenStruct.new({
author: 'Carlos Valderrama',
title: 'My Hair Secrets',
slug: 'my-hair-secrets',
category: 'Soccer',
body: "# My Hair Secrets\nHow hair was the secret to my soccer success."
})
}
class TestPublisherService < PublisherService
def call
"http://www.website.com/article/#{slug}"
end
end
describe 'publishes an article to public website' do
subject { Publisher.new(article, TestPublisherService) }
it 'sets published url' do
published_article = subject.publish
expect(published_article.published_url).to eq('http://www.website.com/article/my-hair-secrets')
end
it 'sets published at' do
published_article = subject.publish
expect(published_article.published_at).to be_a(Time)
end
end
end
实际上 PublisherService 的具体实现还没有完成,但是这不妨碍我们为客户端(此处是 Publisher)编写测试用例来保证其按预期工作。
?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class PublisherService
attr_reader :author, :title, :slug, :category, :body
def initialize(author:, title:, slug:, category:, body:)
@author = author
@title = title
@slug = slug
@category = category
@body = body
end
def call
# coming soon
end
end
服务间通信
服务之间需要能够互相通信。对此作为 Ruby 程序员应该是很熟悉了,即使之前没有做过微服务的程序。
调用某个对象的方法,只需要给它发送消息,例如调用 Time.send(:now) 就可以改变 Time.now。不管是通过方法调用还是基于 HTTP 进行通信,原理是一样的。我们要做的是给系统的其他部分发送消息,通常还需要有回应。
(责任编辑:IT)
Rails 应用有各种类型,规模也各有不同。有的是一个独立的庞大的应用,全部应用都在同一个位置(包括管理界面、API、前端部分以及所有需要的模块)。另一些应用则是划分成一系列的微服务,服务之间互相通信,这样可以把整个应用切分成更易管理的部分。 这种微服务的架构被称为面向服务的架构( SOA )。虽然我见到过的 Rails 应用通常都倾向于成为独立的程序,不过开发者也完全可以选择让多个 Rails 程序,以及与其他语言或者框架编写的服务一起工作来完成任务。
独立的程序不意味着一定写的不好,但是写的差的独立程序被拆成微服务后大多也是很糟糕的。有多种方式可以让你写出清晰的(更容易测试的)代码,同时在需要拆分应用的时候也更轻松。 使用微服务架构的 Rails 应用的用例本文会讨论如何实现一个 CMS 的网站。可以假设是一家大的报纸或者博客,有很多作者负责投稿,用户可以按主题订阅内容。 Martin Fowler 有一篇很不错的文章,介绍了为什么编辑和发布应该分成两个不同的系统。我们的用例与此类似,另外我们还要添加两个模块:通知和订阅。 我们的 CMS 现在有四个主要的模块:
Rails 应用需要支持 SOA 吗?是选择独立程序还是构建成微服务?这里没有对和错之分,不过下面的问题能帮你做出决定。
团队的组织结构是怎样的?是否选择支持 SOA 通常与技术无关,而是在于开发团队的组织结构。
由四个团队分别负责一个主要的模块,比所有人在整个系统上一起工作要靠谱一些。如果你只有一个团队或者少数几个开发人员,一开始就决定采用微服务架构实际上会减慢开发的速度,这是因为需要为四个不同的组件直接的通信以及部署增加开发量。 不同的模块规模不一样?对于本文的例子,有一个问题提现的很好,对外提供服务的公共网站肯定要比作者和编辑使用的 CMS 编辑器的访问压力要大很多。 如果这些模块都部署成分离的系统,我们就可以单独的控制它们的规模,为系统中不同的部分采用不同的缓存技术。你当然还是可以坚持采用单一的系统,但是那样的话你就只能为整个系统一次性确定其规模,而不是对不同的组件分开处理。 不同的模块使用不同的技术?对于 CMS 编辑器,你也许想使用 Single Page Application (SPA),采用 React 或者 Angular 技术。而对外的网站,会使用更传统一些的服务端渲染的 Rails 应用(为了支持 SEO)。也许通知模块更适合 Elixir,因为这个语言对并发和并行处理支持不错。
模块的分离,使得你可以为每个模块选择最适合的编程语言。 边界定义现在最重要的事情是定义好系统中模块之间的边界。 系统中的某个部分可能是某个外部 Server 的 Client。使用方法调用还是基于 HTTP 都不重要,它只需要知道它需要与系统中的其他部分进行通信。 为此我们需要定义清晰的边界。 当一篇文章发布时,会发生两件事:
|