使用事件流扩展微服务

这是一篇来自thoughtworks微服务实战经验总结,总结了传统的点对点集成模型的问题,也就是业务流程orchestration风格的问题,展示了基于事件流的choreography风格在微服务架构中高聚合与松耦合的好处。

来自传统的orchestration业务流程
2014年参与的一个微服务项目因为代码改变使得项目变得很难维护或拓展,根本原因是在服务之间形成了异步的点对点调用模型,一个服务A调用另外一个服务B,服务A就依赖服务B,完成一个业务功能需要十多个微服务依赖,如下图,下面一个节点代码一个服务,每条线代表从一个服务到另外一个服务的直接HTTP调用。

这种方式在实现包括几个业务服务的业务事务时会引入了大量复杂性,因为业务事务跨几个服务,处理失败变得非常小心,这个服务失败会对整个业务流程事务有影响吗?如果有,那么事务就得退出,返回一个有意义的错误给调用者,另外一方面,当系统从失败中恢复时,我们需要让这个业务事务继续成功处理完成。

测试策略因为需要依赖服务变得很有戏剧性。对于更复杂的服务,需要依赖基于浏览器的客户端测试来验证集成效果,因为高度的服务耦合阻止了我们单独发布这些微服务,最终只能以传统的整体monolithic发布,所有的服务都同时部署,同时部署客户端应用程序。

使用事件流的服务Choreography
这种点对点的集成能够通过解耦服务避免,并限制业务事务的边际,choreography编舞风格能够限制业务事务的边界,上游服务发送事件,而下游服务订阅这些事件,这就提供足够的隔离,如下图通过这种事件流方式实现微服务集成:

这种与orchestration不同在于:以客户注册过程来举例,在orchestration业务流程方式下,当一个新的客户注册,客户服务会实现整个客户的创建流程:保存新客户的细节资料,调用下游其他服务,这样客户会收到一个欢迎的电子邮件,获得他的第一个成员积分点。

而在基于事件的choreography风格下:新客户只是发出创建事件customer_created 给下游服务,如下:


app.post('/', function(req, res) {
  customers.push(req.body);
  var event = { 'type':'customer_created', 'data':req.body };
  publish(event);
  res.sendStatus(200);
});

客户创建的事务就局限在了客户服务中,只是简单地发布新的客户事件并让全世界都知道,任何下游服务能够订阅这个事件流,当这种事件一旦被发布,订阅者会异步收到通知,比如email服务:


http.listen(3001, function() {
  eventStore.subscribe('customer_created',
  function(customer) {
    sendWelcomeEmail(customer);
  });
  console.log('email service listening on *:3001');
});

Email服务订阅了客户创建的事件。这种choreography模型实际是一种发布/订阅publish/subscriber模型。


在点对点模型中处理端点错误需要非常小心,因为使用消息模型实现,这样围绕一个组件的服务编排choreography需要对这些错误失败非常小心。

如果一个服务在短时间内当机,事件存储会在订阅者重新运行时重新发送丢失事件,如果一个服务当机很长时间,服务必须重新加载过去事件的历史记录,检查曾经错过丢失事件,如下:


http.listen(3001, function() {
eventStore.history('customer_created',
function(customer) {
if(!hasWelcomeEmail(customer)) {
sendWelcomeEmail(customer);
}
});
console.log('email service listening on *:3001');
});

为了能跟踪错误以便处理错误,一个通用模式是使用事件ID作为聚合ID,横跨这个事件经过的所有流程,这样当事件被发布,它由上游服务使用唯一ID作为日志记录,比如,客户服务会记录customer_created,其event_id是2987,那么在下游服务将使用指向原始事件的方式记录自己事件日志,比如loyalty服务会使用transaction_id =2987.0987来记录add_status_point 这个事件。

如果下游服务记录它的事务和上游一个被触发的事件有关,通过聚合所有微服务的日志,就可以画出下游对于这个事件的一个所有下游事务流程,确保它们能成功执行。

在一个数据库为中心( database-centric)应用中,专用视图(专门用于显示的数据表)用于实现不同展示目标而进行定制,而在事件驱动世界中,它只是创建一个特定服务用来订阅相关事件,以便汇聚和暴露汇总结果给消费者或调用者。

服务之间通过事件协调会更加具有可伸缩性,降低复杂性,提高处理和分析数据的能力。

原文:
Scaling Microservices with an Event Stream | Thoug

完整基于事件流的微服务源码:Github

参考:为什么微服务应该是事件驱动?

全新角度总结Twitter Facebook和LinkedIn业务模型与架构

[该贴被banq于2016-06-02 16:42修改过]

前提是事件发布/订阅服务足够稳定和可靠。如果消息中间件都跨了。那么你的所有基于时间的业务单元都失灵了。
[该贴被netroby于2016-06-02 17:24修改过]

2016-06-02 17:23 "@netroby"的内容
如果消息中间件都跨了 ...

这需要topic类型的pub/sub的消息中间件,需要没有单点风险和失败容错,使用apache kafka或Twitter的分布式日志DistributedLog。

kafka拿来做交易的话,我们简单压测了下,性能似乎不怎么样