关于 Spring For All

关于 Spring For All

Spring For All 的一切
最新动态

最新动态

Spring 5 会是咋样呢
Spring Boot

Spring Boot

快速构建并运行 Spring 应用程序
Spring Cloud

Spring Cloud

分布式系统的一套工具,可用于构建微服务
Spring Framework

Spring Framework

提供依赖注入、事务、Web应用、数据访问等模块
Spring Data

Spring Data

提供一致性数据访问模块
Spring Security

Spring Security

提供应用身份验证和授权支持
Spring Batch

Spring Batch

提供高容批处理操作模块
Spring AMQP

Spring AMQP

基于AMQP消息解决方案
Micro Service Arch.

Micro Service Arch.

微服务架构相关
开源项目及视频教程

开源项目及视频教程

做中国最好的 Spring 开源项目及视频教程
小马哥专栏

小马哥专栏

阿里技术布道者,专注 Spring Boot 及微服务

微服务之构建容错&自动降级的系统 (转载)

xiaobaxi 发表了文章 • 0 个评论 • 884 次浏览 • 2017-06-21 16:39 • 来自相关话题

本文转自https://github.com/JoeCao/JoeCao.github.io  由曹祖鹏编写发布,觉得写的非常不错,就转到社区来了

 微服务是什么

MicroService主要是针对以前的Monolithic(单体)服务来说的,类似优步打车(Uber)这样的应用,如果使用Monolithic来架构,可能是这样的






注:
STRIPE是支付服务
SENDGRID是邮件推送服务
TWILIO是语音、消息的云服务

大部分的项目都是由Monolithic起步的。刚开始最合适不过了,开发简单直接(直接在一个IDE里面就可以调试)、部署方便(搞个war包扔到tomcat里面就能跑)。
但是随着时间的增长,单体应用的缺陷暴露出来了。
首先是代码量大大增加
从几千行到几十万行,接手的程序员已经无法搞清楚里面模块交互逻辑和依赖关系了。大家都在小心翼翼的加代码,避免触碰以前的旧代码。至于单元测试,这种怪兽级别的应用,能不能拆分出来单元啊?
不可避免的超长启动时间
3分钟以内的都算是快的。启动后,程序员抽支烟再回来的不在少数。大量的时间在等待,使得程序员越来越不愿意测试自己的功能,写完了之后直接扔给测试就完事了。测试轮次一次又一次,时间不可避免的浪费。
部署扩展困难
各个模块对服务器的性能要求不同,有的占用CPU高,有的需要大量的IO,有的需要更多的内存。部署在一个服务器内相互影响,扩容的话,无法针对性的扩展。
单个的bug会损坏整个服务
可能一个模块的内存泄露,会使得整个服务都crash down。
对于一些新技术尝试有限制
比如想换个新框架试试,但是要换就全部得换,风险太大。

服务化的改造

解决方案就是服务化,进行下图的Y轴方向的功能拆解。






最终可能的情况类似下图






MicroService不是银弹
服务之间的通讯机制RPC开销大需要考虑分布式情况下的服务失效恢复等等问题数据库分布带来的分布式事务困扰,还有分布式数据结果Join的问题如何管理这么多服务之间的依赖关系,出了问题如何定位?部署服务变得复杂。包括配置、部署、扩容、监控,都是需要专门的工具链支持。

微服务和SOA

虽然微服务很火,但是仅仅把微服务和Monolithic服务做对比,然后搞个大新闻把Monolithic批判一番,我认为是很不公平的。
一般稍具规模企业都在前几年完成了SOA的演化,那么微服务和SOA的区别?SOA在十年前就提出了服务化的概念,那微服务到底是新的理论突破,还是新瓶装旧酒换了一个概念出来忽悠?
总结了微服务应该具备的特点:
小, 且专注于做一件事情独立的进程松耦合、独立部署轻量级的通信机制有边界:
           服务间边界清晰、灵活选择语言和技术(这个是有争议的)
松耦合:
           服务无状态、容错性、故障隔离、自动恢复、弹性伸缩、服务间独立部署,发布、升级、降级互不影响
面向服务架构(SOA) :
           服务化、服务分层

我个人理解SOA和MicroService理论是同源,但是实施方案上面有轻量和重量解决方案的区别,类似当年EJB和Spring之争。
基于ESB、SOAP、WSDL等重型解决方案,演化出来SOA。
利用Dubbo、zookeeper、http/rest、consul、Eureka这些框架的轻量解决方案,也就是现在流行的MicroService。

微服务之后,前端(Web、App)怎么开发?

以电商APP为例,需要购物车、物流服务、库存服务、推荐服务、订单服务、评价服务、商品类目服务....






如果我们让APP分别去调用这些服务会带来什么后果?
后端代码必须改造,因为APP调不了Dubbo协议(二进制的比较复杂),我们得给每个服务都架设一个HTTP代理,也无法利用MicroService的服务发现机制(暴露Zookeeper到公网,非常不安全),必须通过额外的nginx做负载均衡客户端的代码极其冗余,因为业务数据是需要融合的,很多页面需要发送多次请求才能返回给用户一个完成的View,再加上UI 的Callback hell,特别酸爽将内部的逻辑放到了客户端,一旦服务有拆分、融合之类的操作,APP也必须升级

适配移动化的BFF架构

在微服务和APP之间建立一个沟通的桥梁,一个网关(Gateway),我们称之为Backend for Fronter,一个专为前端准备的后端(绕口令中)






BFF职责是路由、聚合、协议转换。
每个请求BFF都可能会调用后面多个微服务的Dubbo接口,将结果聚合后返回给前台。BFF需要给前端更粗颗粒的接口,避免APP的多次调用降低效率。比如/products/{productId}这样的接口会将商品标题、规格、图片、详情页面、评价一股脑的返回给前台。

优缺点

优点:提供了一站式的接口,隐藏内部逻辑,对前端开发友好
缺点:对高可用的架构来说,这个单点的职责较重。

微服务后,中心应该如何开发?

以商品中心为例:一个大的商品中心服务可以被拆为类目、商品、价格、库存四个微服务。这样每个服务的职责专注,接口单一。每个服务可以有自己的存储,甚至可以用不同的存储,比如商品适合Elasticsearch的搜索引擎,库存就适合Redis这样的内存数据库。这样系统的性能提升。
问题又出现了,服务拆开,本来一个接口能返回的数据,必须通过分别调用不同服务的接口来完成。更糟糕的是,不同的服务使用不同的存储引擎,以前一个sql join能解决的事情,变得非常的复杂。
如何解决?
得益于现在技术的提升,我们可以在BFF侧使用zip、join这类的函数式写法替代原来的多层循环来更高效聚合数据。可以使用最新的思路在客户端缓存数据,相当于在app、js侧做了一个数据拷贝的子集(mongodb?),通过diff之类的算法和后端不断同步数据。(类似firebase,野狗的思路)使用类似物化视图的概念,在业务侧生成他需要的视图数据。加快查询速度。






以上方案视情况采用

Backend for Fronter和SOA配合的烦恼

一旦涉及到调用多个后台SOA的接口,SOA接口之间有一定关联关系,需要将结果组合,这组调用的危险性就很大。
以我们常见的商品列表的接口举例,该接口不仅仅是调用了商品中心商品信息接口,还调用了定价服务的用户级别价格接口,然后组合(zip)成为一个商品完整信息返回。
在某些情况下价格服务失效,或者是返回时长波动,那么整个商品列表就一直会阻塞,直到超时也不会返回结果,用户在前台看到的是一个***空白的转圈的页面***。 用户只会认为整个系统都挂了,他可不管你里面发生了什么。
微服务架构成规模后,任何一个我们认为的微不足道的服务的故障,都会导致整个业务的不可用。而在SOA模式下面数千个服务,可能有一定的几率某几个服务是失效的,这种minor的失效导致整个系统不可用的失败,是SOA的一大硬伤。






在后端服务失效这种情况下,我们希望API网关能做到
快速响应用户的请求给用户一些详尽的错误提示,安抚客户如果有可能,降级到某个缓存或者备用服务上快速的跟踪到错误位置防止失效扩散到整个集群如果服务正常了,前端API网关能够自动恢复






Hystrix 是什么?

Hystrix是Netflix开发的一个库,能够实现
调用外部API能够设置超时时间(默认1000ms)限制外部系统的并发调用(默认10个)Circuit Breaker可以在一定错误率后不再调用API(默认50%)Circuit Breaker启动后,每隔五秒可以再次尝试调用API可以监控实时的调用情况,Hystrix甚至提供了一个可视化的监控面板

Hystrix适用场景

Hystrix用在BFF侧,包装外部系统API的调用是最合适的。当然不仅仅是外部系统API调用,即使是内部进程内的调用,也可以用Hystrix的Semaphore模式进行并发限制和监控
Hystrix提供了贴心的Fallback(回落、降级,怎么称呼好呢?)功能,在我们设置的条件满足后,就会转到Fallback方法上,替代原来的接口的返回。
有两个场景,不适合Hystrix的Fallback:
如果接口是write操作
-- 这里的write包括append,update、delete等等操作,这些操作会改变外部系统状态,使用Hystrix是不合适的。因为Hystrix只是在客户端做了限制和监控,Hystrix判定的超时,和外部系统判定的标准并不同步,这就会导致数据不一致。
批量操作或者是离线操作
-- 如果是导出一个订单列表或者统计一个报表之类的操作,就不要Fallback了,让他异步执行吧,没有必要降级。

CircuitBreaker模式

Martin Flower 曾经总结了一个CircuitBreaker的模式,就是在服务故障的情况下,迅速做出反应,不让故障扩散






在图中看到,在可能出现超时的调用中,保护性的调用启动了两个线程,一个线程去实际调用,一个线程监控,如果超过设定的阈值,监控线程会返回一个默认值或者直接抛出异常给客户端,就不等那个真正的远调用线程的返回了
当然这种对超时的控制,一般的RPC都会有(比如Dubbo做了mock的功能)。但是CircuitBreaker的高效之处在于过了一段时间后,可以检测接口的状况,如果接口正常了,自动恢复。
在这里Martin Flower设计了一个“half open”半开的状态







从图上可以看到,在客户端调用次数到了一定的阈值的时候,CircuitBreaker进入了Open状态,在Open状态启动定时器,到了时间会转入Half Open,再次尝试一下接口调用,如果正常了,就恢复。如果还是错误,那就回到Open状态,继续等着吧。

如何集成到我们的SpringBoot的框架

很简单,就是几个Jar包的引入和annotation的使用
请参考例子
http://git.dev.qianmi.com/commons/micro-service-frontend-sample.git $(document).ready(function() {$('pre code').each(function(i, block) { hljs.highlightBlock( block); }); });@Service
public class PricingApi implements PricingProvider {
@Reference(timeout = 15000, check = false)
PricingProvider pricingProvider;

/**
* 默认在500毫秒内就需要返回结果,否则回落到default的值中
*
* @param itemids
* @return
*/
@Override
@HystrixCommand(fallbackMethod = "defaultPrice", commandProperties = {
@HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds", value = "500"),
@HystrixProperty(name = "circuitBreaker.requestVolumeThreshold", value = "10")})
public List<ItemPrice> getPrice(String... itemids) {
return pricingProvider.getPrice(itemids);
}

/**如果回落到此方法,使用-1代替价格返回**/
public List<ItemPrice> defaultPrice(String... itemids) {
List<ItemPrice> prices = new ArrayList<>();
for (String itemId : itemids) {
ItemPrice itemPrice = new ItemPrice(itemId, -1.0);
prices.add(itemPrice);
}
return prices;
}
}
只需要通过注解的方式将Dubbo的远程调用包住即可
如果没用Spring boot框架,可以参考这篇文章
https://ahus1.github.io/hystri ... .html

最佳实践
几个重要的参数说明
execution.isolation.thread.timeoutInMilliseconds
这个用于设置超时时间,默认是1000ms
execution.isolation.strategy
这里有两个选择,一个是thread 一个semaphore,thread会启动线程池,默认为10个,主要用于控制外部系统的API调用。semaphore是控制内部的API调用,本机调用(顺便说一下其实Hystrix不仅仅用于RPC场景)





circuitBreaker.requestVolumeThreshold
这个参数决定了,有规定时间内(一般是10s),有多少个错误请求,会触发circuit breaker open。默认20个
circuitBreaker.sleepWindowInMilliseconds
该参数决定了,在circuit break 进入到open状态后,多少时间会再次尝试进入

如何设置线程池的大小







打个比方,经过统计,一个远程接口的返回时间 Median: 40ms 99th: 200ms 99.5th: 300ms  
线程池的计算公式
requests per second at peak when healthy × 99th percentile latency in seconds + some breathing room
99%的请求都会在200ms内返回,一个高峰期并发30个每秒
那么thread pool size = 30 rps * 0.2 seconds = 6 + breathing room(冗余量) =10 threads
99%的请求都会在200ms之内返回,考虑到在失败时,设置一次retry,那么大概就是240ms左右,这样的话设置执行线程的超时时间为300ms。
如果设置得当,一般来说,线程池的Active Thread只会有1-2个,大部分的40ms内的请求会很快的处理掉。
Thread pool设置
@HystrixCommand(commandProperties = {
@HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds", value = "500")
},
threadPoolProperties = {
@HystrixProperty(name = "coreSize", value = "30"),
@HystrixProperty(name = "maxQueueSize", value = "101"),
@HystrixProperty(name = "keepAliveTimeMinutes", value = "2"),
@HystrixProperty(name = "queueSizeRejectionThreshold", value = "15"),
@HystrixProperty(name = "metrics.rollingStats.numBuckets", value = "12"),
@HystrixProperty(name = "metrics.rollingStats.timeInMilliseconds", value = "1440")
})
public User getUserById(String id) {
return userResource.getUserById(id);
}

版权说明

本文采用 CC BY 3.0 CN协议 进行许可。 可自由转载、引用,但需署名作者且注明文章出处 查看全部
本文转自https://github.com/JoeCao/JoeCao.github.io  由曹祖鹏编写发布,觉得写的非常不错,就转到社区来了

 微服务是什么

MicroService主要是针对以前的Monolithic(单体)服务来说的,类似优步打车(Uber)这样的应用,如果使用Monolithic来架构,可能是这样的

1.png


注:
STRIPE是支付服务
SENDGRID是邮件推送服务
TWILIO是语音、消息的云服务

大部分的项目都是由Monolithic起步的。刚开始最合适不过了,开发简单直接(直接在一个IDE里面就可以调试)、部署方便(搞个war包扔到tomcat里面就能跑)。
但是随着时间的增长,单体应用的缺陷暴露出来了。
  • 首先是代码量大大增加

从几千行到几十万行,接手的程序员已经无法搞清楚里面模块交互逻辑和依赖关系了。大家都在小心翼翼的加代码,避免触碰以前的旧代码。至于单元测试,这种怪兽级别的应用,能不能拆分出来单元啊?
  • 不可避免的超长启动时间

3分钟以内的都算是快的。启动后,程序员抽支烟再回来的不在少数。大量的时间在等待,使得程序员越来越不愿意测试自己的功能,写完了之后直接扔给测试就完事了。测试轮次一次又一次,时间不可避免的浪费。
  • 部署扩展困难

各个模块对服务器的性能要求不同,有的占用CPU高,有的需要大量的IO,有的需要更多的内存。部署在一个服务器内相互影响,扩容的话,无法针对性的扩展。
  • 单个的bug会损坏整个服务

可能一个模块的内存泄露,会使得整个服务都crash down。
  • 对于一些新技术尝试有限制

比如想换个新框架试试,但是要换就全部得换,风险太大。

服务化的改造

解决方案就是服务化,进行下图的Y轴方向的功能拆解。

2.png


最终可能的情况类似下图

3.png


MicroService不是银弹
  • 服务之间的通讯机制RPC开销大
  • 需要考虑分布式情况下的服务失效恢复等等问题
  • 数据库分布带来的分布式事务困扰,还有分布式数据结果Join的问题
  • 如何管理这么多服务之间的依赖关系,出了问题如何定位?
  • 部署服务变得复杂。包括配置、部署、扩容、监控,都是需要专门的工具链支持。


微服务和SOA

虽然微服务很火,但是仅仅把微服务和Monolithic服务做对比,然后搞个大新闻把Monolithic批判一番,我认为是很不公平的。
一般稍具规模企业都在前几年完成了SOA的演化,那么微服务和SOA的区别?SOA在十年前就提出了服务化的概念,那微服务到底是新的理论突破,还是新瓶装旧酒换了一个概念出来忽悠?
总结了微服务应该具备的特点:
  • 小, 且专注于做一件事情
  • 独立的进程
  • 松耦合、独立部署
  • 轻量级的通信机制
  • 有边界:

           服务间边界清晰、灵活选择语言和技术(这个是有争议的)
  • 松耦合:

           服务无状态、容错性、故障隔离、自动恢复、弹性伸缩、服务间独立部署,发布、升级、降级互不影响
  • 面向服务架构(SOA) :

           服务化、服务分层

我个人理解SOA和MicroService理论是同源,但是实施方案上面有轻量和重量解决方案的区别,类似当年EJB和Spring之争。
基于ESB、SOAP、WSDL等重型解决方案,演化出来SOA。
利用Dubbo、zookeeper、http/rest、consul、Eureka这些框架的轻量解决方案,也就是现在流行的MicroService。

微服务之后,前端(Web、App)怎么开发?

以电商APP为例,需要购物车、物流服务、库存服务、推荐服务、订单服务、评价服务、商品类目服务....

4.png


如果我们让APP分别去调用这些服务会带来什么后果?
  • 后端代码必须改造,因为APP调不了Dubbo协议(二进制的比较复杂),我们得给每个服务都架设一个HTTP代理,也无法利用MicroService的服务发现机制(暴露Zookeeper到公网,非常不安全),必须通过额外的nginx做负载均衡
  • 客户端的代码极其冗余,因为业务数据是需要融合的,很多页面需要发送多次请求才能返回给用户一个完成的View,再加上UI 的Callback hell,特别酸爽
  • 将内部的逻辑放到了客户端,一旦服务有拆分、融合之类的操作,APP也必须升级


适配移动化的BFF架构

在微服务和APP之间建立一个沟通的桥梁,一个网关(Gateway),我们称之为Backend for Fronter,一个专为前端准备的后端(绕口令中)

5.png


BFF职责是路由、聚合、协议转换。
每个请求BFF都可能会调用后面多个微服务的Dubbo接口,将结果聚合后返回给前台。BFF需要给前端更粗颗粒的接口,避免APP的多次调用降低效率。比如/products/{productId}这样的接口会将商品标题、规格、图片、详情页面、评价一股脑的返回给前台。

优缺点

优点:提供了一站式的接口,隐藏内部逻辑,对前端开发友好
缺点:对高可用的架构来说,这个单点的职责较重。

微服务后,中心应该如何开发?

以商品中心为例:一个大的商品中心服务可以被拆为类目、商品、价格、库存四个微服务。这样每个服务的职责专注,接口单一。每个服务可以有自己的存储,甚至可以用不同的存储,比如商品适合Elasticsearch的搜索引擎,库存就适合Redis这样的内存数据库。这样系统的性能提升。
问题又出现了,服务拆开,本来一个接口能返回的数据,必须通过分别调用不同服务的接口来完成。更糟糕的是,不同的服务使用不同的存储引擎,以前一个sql join能解决的事情,变得非常的复杂。
如何解决?
  • 得益于现在技术的提升,我们可以在BFF侧使用zip、join这类的函数式写法替代原来的多层循环来更高效聚合数据。
  • 可以使用最新的思路在客户端缓存数据,相当于在app、js侧做了一个数据拷贝的子集(mongodb?),通过diff之类的算法和后端不断同步数据。(类似firebase,野狗的思路)
  • 使用类似物化视图的概念,在业务侧生成他需要的视图数据。加快查询速度。


6.png


以上方案视情况采用

Backend for Fronter和SOA配合的烦恼

一旦涉及到调用多个后台SOA的接口,SOA接口之间有一定关联关系,需要将结果组合,这组调用的危险性就很大。
以我们常见的商品列表的接口举例,该接口不仅仅是调用了商品中心商品信息接口,还调用了定价服务的用户级别价格接口,然后组合(zip)成为一个商品完整信息返回。
在某些情况下价格服务失效,或者是返回时长波动,那么整个商品列表就一直会阻塞,直到超时也不会返回结果,用户在前台看到的是一个***空白的转圈的页面***。 用户只会认为整个系统都挂了,他可不管你里面发生了什么。
微服务架构成规模后,任何一个我们认为的微不足道的服务的故障,都会导致整个业务的不可用。而在SOA模式下面数千个服务,可能有一定的几率某几个服务是失效的,这种minor的失效导致整个系统不可用的失败,是SOA的一大硬伤。

7.png


在后端服务失效这种情况下,我们希望API网关能做到
  • 快速响应用户的请求
  • 给用户一些详尽的错误提示,安抚客户
  • 如果有可能,降级到某个缓存或者备用服务上
  • 快速的跟踪到错误位置
  • 防止失效扩散到整个集群
  • 如果服务正常了,前端API网关能够自动恢复


8.png


Hystrix 是什么?

Hystrix是Netflix开发的一个库,能够实现
  • 调用外部API能够设置超时时间(默认1000ms)
  • 限制外部系统的并发调用(默认10个)
  • Circuit Breaker可以在一定错误率后不再调用API(默认50%)
  • Circuit Breaker启动后,每隔五秒可以再次尝试调用API
  • 可以监控实时的调用情况,Hystrix甚至提供了一个可视化的监控面板


Hystrix适用场景

Hystrix用在BFF侧,包装外部系统API的调用是最合适的。当然不仅仅是外部系统API调用,即使是内部进程内的调用,也可以用Hystrix的Semaphore模式进行并发限制和监控
Hystrix提供了贴心的Fallback(回落、降级,怎么称呼好呢?)功能,在我们设置的条件满足后,就会转到Fallback方法上,替代原来的接口的返回。
有两个场景,不适合Hystrix的Fallback:
  • 如果接口是write操作

-- 这里的write包括append,update、delete等等操作,这些操作会改变外部系统状态,使用Hystrix是不合适的。因为Hystrix只是在客户端做了限制和监控,Hystrix判定的超时,和外部系统判定的标准并不同步,这就会导致数据不一致。
  • 批量操作或者是离线操作

-- 如果是导出一个订单列表或者统计一个报表之类的操作,就不要Fallback了,让他异步执行吧,没有必要降级。

CircuitBreaker模式

Martin Flower 曾经总结了一个CircuitBreaker的模式,就是在服务故障的情况下,迅速做出反应,不让故障扩散

9.png


在图中看到,在可能出现超时的调用中,保护性的调用启动了两个线程,一个线程去实际调用,一个线程监控,如果超过设定的阈值,监控线程会返回一个默认值或者直接抛出异常给客户端,就不等那个真正的远调用线程的返回了
当然这种对超时的控制,一般的RPC都会有(比如Dubbo做了mock的功能)。但是CircuitBreaker的高效之处在于过了一段时间后,可以检测接口的状况,如果接口正常了,自动恢复。
在这里Martin Flower设计了一个“half open”半开的状态

10.png



从图上可以看到,在客户端调用次数到了一定的阈值的时候,CircuitBreaker进入了Open状态,在Open状态启动定时器,到了时间会转入Half Open,再次尝试一下接口调用,如果正常了,就恢复。如果还是错误,那就回到Open状态,继续等着吧。

如何集成到我们的SpringBoot的框架

很简单,就是几个Jar包的引入和annotation的使用
请参考例子
http://git.dev.qianmi.com/commons/micro-service-frontend-sample.git 
@Service
public class PricingApi implements PricingProvider {
@Reference(timeout = 15000, check = false)
PricingProvider pricingProvider;

/**
* 默认在500毫秒内就需要返回结果,否则回落到default的值中
*
* @param itemids
* @return
*/
@Override
@HystrixCommand(fallbackMethod = "defaultPrice", commandProperties = {
@HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds", value = "500"),
@HystrixProperty(name = "circuitBreaker.requestVolumeThreshold", value = "10")})
public List<ItemPrice> getPrice(String... itemids) {
return pricingProvider.getPrice(itemids);
}

/**如果回落到此方法,使用-1代替价格返回**/
public List<ItemPrice> defaultPrice(String... itemids) {
List<ItemPrice> prices = new ArrayList<>();
for (String itemId : itemids) {
ItemPrice itemPrice = new ItemPrice(itemId, -1.0);
prices.add(itemPrice);
}
return prices;
}
}

只需要通过注解的方式将Dubbo的远程调用包住即可
如果没用Spring boot框架,可以参考这篇文章
https://ahus1.github.io/hystri ... .html

最佳实践
几个重要的参数说明
  • execution.isolation.thread.timeoutInMilliseconds

这个用于设置超时时间,默认是1000ms
  • execution.isolation.strategy

这里有两个选择,一个是thread 一个semaphore,thread会启动线程池,默认为10个,主要用于控制外部系统的API调用。semaphore是控制内部的API调用,本机调用(顺便说一下其实Hystrix不仅仅用于RPC场景)

11.png

  • circuitBreaker.requestVolumeThreshold

这个参数决定了,有规定时间内(一般是10s),有多少个错误请求,会触发circuit breaker open。默认20个
  • circuitBreaker.sleepWindowInMilliseconds

该参数决定了,在circuit break 进入到open状态后,多少时间会再次尝试进入

如何设置线程池的大小


12.png


打个比方,经过统计,一个远程接口的返回时间
 Median: 40ms 99th: 200ms 99.5th: 300ms 
 
  • 线程池的计算公式

requests per second at peak when healthy × 99th percentile latency in seconds + some breathing room
99%的请求都会在200ms内返回,一个高峰期并发30个每秒
那么thread pool size = 30 rps * 0.2 seconds = 6 + breathing room(冗余量) =10 threads
99%的请求都会在200ms之内返回,考虑到在失败时,设置一次retry,那么大概就是240ms左右,这样的话设置执行线程的超时时间为300ms。
如果设置得当,一般来说,线程池的Active Thread只会有1-2个,大部分的40ms内的请求会很快的处理掉。
  • Thread pool设置

@HystrixCommand(commandProperties = {
@HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds", value = "500")
},
threadPoolProperties = {
@HystrixProperty(name = "coreSize", value = "30"),
@HystrixProperty(name = "maxQueueSize", value = "101"),
@HystrixProperty(name = "keepAliveTimeMinutes", value = "2"),
@HystrixProperty(name = "queueSizeRejectionThreshold", value = "15"),
@HystrixProperty(name = "metrics.rollingStats.numBuckets", value = "12"),
@HystrixProperty(name = "metrics.rollingStats.timeInMilliseconds", value = "1440")
})
public User getUserById(String id) {
return userResource.getUserById(id);
}


版权说明

本文采用 CC BY 3.0 CN协议 进行许可。 可自由转载、引用,但需署名作者且注明文章出处

微服务(Microservices)【翻译】

程序猿DD 发表了文章 • 0 个评论 • 1058 次浏览 • 2017-06-21 13:13 • 来自相关话题

原文是 Martin Flower 于 2014 年 3 月 25 日写的[《Microservices》](http://martinfowler.com/articles/microservices.html)。


微服务

“微服务架构(*Microservice Architecture*)”一词在过去几年里广泛的传播,它用于描述一种设计应用程序的特别方式,作为一套独立可部署的服务。目前,这种架构方式还没有准确的定义,但是在围绕业务能力的组织、自动部署(automated deployment)、端智能(intelligence in the endpoints)、语言和数据的分散控制,却有着某种共同的特征。

“微服务(Microservices)”——只不过在满大街充斥的软件架构中的一新名词而已。尽管我们非常鄙视这样的东西,但是这玩意所描述的软件风格,越来越引起我们的注意。在过去几年里,我们发现越来越多的项目开始使用这种风格,以至于我们身边的同事在构建企业级应用时,把它理所当然的认为这是一种默认开发形式。然而,很不幸,微服务风格是什么,应该怎么开发,关于这样的理论描述却很难找到。

简而言之,微服务架构风格,就像是把一个单独的应用程序开发为一套小服务,每个小服务运行在自己的进程中,并使用轻量级机制通信,通常是 HTTP API。这些服务围绕业务能力来构建,并通过完全自动化部署机制来独立部署。这些服务使用不同的编程语言书写,以及不同数据存储技术,并保持最低限度的集中式管理。

在开始介绍微服务风格(microservice style)前,比较一下整体风格(monolithic style)是很有帮助的:一个完整应用程序(monolithic application)构建成一个单独的单元。企业级应用通常被构建成三个主要部分:客户端用户界面(由运行在客户机器上的浏览器的 HTML 页面、Javascript 组成)、数据库(由许多的表构成一个通用的、相互关联的数据管理系统)、服务端应用。服务端应用处理 HTTP 请求,执行领域逻辑(domain logic),检索并更新数据库中的数据,使用适当的 HTML 视图发送给浏览器。服务端应用是完整的 ,是一个单独的的逻辑执行。任何对系统的改变都涉及到重新构建和部署一个新版本的服务端应用程序。

这样的整体服务(monolithic server)是一种构建系统很自然的方式。虽然你可以利用开发语基础特性把应用程序划分成类、函数、命名空间,但所有你处理请求的逻辑都运行在一个单独的进程中。在某些场景中,开发者可以在的笔计本上开发、测试应用,然后利用部署通道来保证经过正常测试的变更,发布到产品中。你也可以使用横向扩展,通过负载均衡将多个应用部署到多台服务器上。

整体应用程序(Monolithic applications)相当成功,但是越来越多的人感觉到有点不妥,特别是在云中部署时。变更发布周期被绑定了——只是变更应用程序的一小部分,却要求整个重新构建和部署。随着时间的推移,很难再保持一个好的模块化结构,使得一个模块的变更很难不影响到其它模块。扩展就需要整个应用程序的扩展,而不能进行部分扩展。





图 1 整理架构与微服务架构

这导致了微服务架构风格(microservice architectural style)的出现:把应用程序构建为一套服务。事实是,服务可以独立部署和扩展,每个服务提供了一个坚实的模块边界,甚至不同的服务可以用不同的编程语言编写。它们可以被不同的团队管理。

**我们必须说,微服务风格不是什么新东西,它至少可以追溯到 Unix 的设计原则。**但是并没有太多人考虑微服务架构,如果他们用了,那么很多软件都会更好。

微服务风格的特性

微服务风格并没有一个正式的定义,但我们可以尝试描述一下微服务风格所具有的共同特点。并不是所有的微服务风格都要具有所有的特性,但我们期望常见的微服务都应该有这些特性。我们的意图是尝试描述我们工作中或者在其它我们了解的组件中所理解的微服务。特别是,我们不依赖于那些已经明确过的定义。

组件化(Componentization )与服务(Services)

自从我们开始软件行业以来,一直希望由组件构建系统,就像我们在物理世界所看到的一样。在过去的几十年里,我们已经看到了公共库的大量简编取得了相当的进步,这些库是大部分语言平台的一部分。

当我们谈论组件时,可能会陷入一个困境——什么是组件。我们的定义是,组件(component)是一个可独立替换和升级的软件单元。

微服务架构(Microservice architectures)会使用库(libraries),但组件化软件的主要方式是把它拆分成服务。我们把库(libraries)定义为组件,这些组件被链接到程序,并通过内存中函数调用(in-memory function calls)来调用,而服务(services )是进程外组件(out-of-process components),他们利用某个机制通信,比如 WebService 请求,或远程过程调用(remote procedure call)。组件和服务在很多面向对象编程中是不同的概念。

把服务当成组件(而不是组件库)的一个主要原因是,服务可以独立部署。如果你的应用程序是由一个单独进程中的很多库组成,那么对任何一个组件的改变都将导致必须重新部署整个应用程序。但是如果你把应用程序拆分成很多服务,那你只需要重新部署那个改变的服务。当然,这也不是绝对的,有些服务会改变导致协调的服务接口,但是一个好的微服务架构的目标就是通过在服务契约(service contracts)中解耦服务的边界和进化机制来避免这些。

另一个考虑是,把服务当组件将拥有更清晰的组件接口。大多数开发语言都没有一个良好的机制来定义一个[发布的接口](http://martinfowler.com/bliki/ ... e.html)(Published Interface)。发布的接口是指一个类向外公开的成员,比如 Java 中的声明为 Public 的成员,C# 中声明为非 Internal 的成员。通常只有在文档和规范中会说明,这是为了让避免客户端破坏组件的封装性,阻止组件间紧耦合。服务通过使用公开远程调用机制可以很容易避免这些。

像这样使用服务也有不足之处。远程调用比进制内调用更消耗资源,因此远程 API 需要粗粒度(coarser-grained),但这会比较难使用。如果你需要调整组件间的职责分配,当跨越进程边界时,这样做将会很难。

一个可能是,我们看到,服务可以映射到运行时进程(runtime processes)上,但也只是一个可能。服务可以由多个进程组成,它们会同时开发和部署,例如一个应用程序进程和一个只能由这个服务使用的数据库。

围绕业务功能的组织

当寻找把一个大的应用程序拆分成小的部分时,通常管理都会集中在技术层面,UI团队、服务端业务逻辑团队和数据库团队。当使用这种标准对团队进行划分时,甚至小小的更变都将导致跨团队项目协作,从而消耗时间和预算审批。一个高效的团队会针对这种情况进行改善,两权相害取其轻。业务逻辑无处不在。实践中,这就是 Conway's Law 的一个例子。设计一个系统的任何组织(广义上)都会产生这样一种设计,其结构是组织交流结构的复制。
——Melvyn Conway, 1967
Melvyn Conway 的意识是,像下图所展示的,设计一个系统时,将人员划分为 UI 团队,中间件团队,DBA 团队,那么相应地,软件系统也就会自然地被划分为 UI 界面,中间件系统,数据库。





图 2  实践中的 Conway's Law

微服务(microservice )的划分方法不同,它倾向围绕业务功能的组织来分割服务。这些服务实现商业领域的软件,包括用户界面,持久化存储,任何的外部协作。因此,团队是跨职能的(cross-functional),包含开发过程所要求的所有技能:用户体验(user-experience)、数据库(database)和项目管理(project management)。





图 3 通过团队边界强调服务边界

[www.comparethemarket.com](http://www.comparethemarket.com/)就是采用这种组织形式。跨职能的团队同时负责构建和运营每个产品,每个产品被分割成许多单个的服务,这些服务通过消息总线(Message Bus)通信。

大型的整体应用程序(monolithic applications)也可以按照业务功能进行模块化(modularized),尽管这样情况不常见。当然,我们可以敦促一个构建整体应用程序(monolithic application )的大型团队,按业务线来分割自己。我们已经看到的主要问题是,这种组件形式会导致很多的依赖。如果整体应用程序(monolithic applications)跨越很多模块边界(modular boundaries ),那么对于团队的每个成员短期内修复它们是很困难的。此外,我们发现,模块化需要大量的强制规范。服务组件所要求的必需的更明确的分离使得保持团队边界清晰更加容易。

产品不是项目

大部分的软件开发者都使用这样的项目模式:至力于提供一些被认为是完整的软件。交付一个他们认为完成的软件。软件移交给运维组织,然后,解散构建软件的团队。

微服务(Microservice )的支持者认为这种做法是不可取的,并提议团队应该负责产品的整个生命周期。Amazon 理念是“[你构建,你运维(you build, you run it)](https://queue.acm.org/detail.cfm?id=1142065)”,要求开发团队对软件产品的整个生命周期负责。这要求开发者每天都关注他们的软件运行如何,增加更用户的联系,同时承担一些售后支持。

产品的理念,跟业务能力联系起来。不是着眼于完成一套功能的软件,而是有一个持续的关系,是如何能够帮助软件及其用户提升业务能力。

为什么相同的方法不能用在整体应用程序(monolithic applications),但更小的服务粒度能够使创建服务的开发者与使用者之间的个人联系更容易。

强化终端及弱化通道

当构建不同的进程间通信机制的时候,我们发现有许多的产品和方法能够把更加有效方法强加入的通信机制中。比如企业服务总线(ESB),这样的产品提供更有效的方式改进通信过程中的路由、编码、传输、以及业务处理规则。

微服务倾向于做如下的选择:强化终端及弱化通道。微服务的应用致力松耦合和高内聚:采用单独的业务逻辑,表现的更像经典Unix意义上的过滤器一样,接受请求、处理业务逻辑、返回响应。它们更喜欢简单的REST风格,而不是复杂的协议,如WS或者BPEL或者集中式框架。

有两种协议最经常被使用到:包含资源API的HTTP的请求-响应和轻量级消息通信协议。最为重要的建议为:善于利用网络,而不是限制(Be of the web, not behind the web)。
——Ian Robinson
微服务团队采用这样的原则和规范:基于互联网(广义上,包含Unix系统)构建系统。这样经常使用的资源几乎不用什么的代价就可以被开发者或者运行商缓存。

第二种做法是通过轻量级消息总线来发布消息。这种的通信协议非常的单一(单一到只负责消息路由),像RabbitMQ或者ZeroMQ这样的简单的实现甚至像可靠的异步机制都没提供,以至于需要依赖产生或者消费消息的终端或者服务来处理这类问题。

在整体工风格中,组件在进程内执行,进程间的消息通信通常通过调用方法或者回调函数。从整体式风格到微服务框架最大的问题在于通信方式的变更。从内存内部原始的调用变成远程调用,产生的大量的不可靠通信。因此,你需要把粗粒度的方法成更加细粒度的通信。

分散治理

集中治理的一种好处是在单一平台上进行标准化。经验表明这种趋势的好处在缩小,因为并不是所有的问题都相同,而且解决方案并不是万能的。我们更加倾向于采用适当的工具解决适当的问题,整体式的应用在一定程度上比多语言环境更有优势,但也适合所有的情况。

把整体式框架中的组件,拆分成不同的服务,我们在构建它们时有更多的选择。你想用Node.js去开发报表页面吗?做吧。用C++来构建时时性要求高的组件?很好。你想以在不同类型的数据库中切换,来提高组件的读取性能?我们现在有技术手段来实现它了。

当然,你是可以做更多的选择,但也不意味的你就可以这样做,因为你的系统使用这种方式进行侵害意味着你已经有的决定。

采用微服务的团队更喜欢不同的标准。他们不会把这些标准写在纸上,而是喜欢这样的思想:开发有用的工具来解决开发者遇到的相似的问题。这些工具通常从实现中成长起来,并进行的广泛范围内分享,当然,它们有时,并不一定,会采用开源模式。现在开源的做法也变得越来越普遍,git或者github成为了它们事实上的版本控制系统。

Netfix就是这样的一个组织,它是非常好的一个例子。分享有用的、尤其是经过实践的代码库激励着其它的开发着也使用相似的方式来解决相似的问题,当然,也保留着根据需要使用不同的方法的权力。共享库更关注于数据存储、进程内通信以及我们接下来做讨论到的自动化等这些问题上。

微服务社区中,开销问题特别引人注意。这并不是说,社区不认为服务交互的价值。相反,正是因为发现到它的价值。这使得他们在寻找各种方法来解决它们。如[Tolearant Reader](http://martinfowler.com/bliki/TolerantReader.html)和[Consumer-Driven Contracts](http://martinfowler.com/articl ... s.html)这样的设计模式就经常被微服务使用。这些模式解决了独立服务在交互过程中的消耗问题。使用Consumer-Driven Contracts增加了你的信心,并实现了快速的反馈机制。事实上,我们知道澳大利亚的一个团队致力使用Consumer-Drvien Contracts开发新的服务。他们使用简单的工程,帮助他们定义服务的接口。使得在新服务的代码开始编写之前,这些接口就成为自动化构建的一个部分。构建出来的服务,只需要指出这些接口适用的范围,一个优雅的方法避免了新软件中的'YAGNI '困境。这些技术和工具在使用过程中完善,通过减少服务间的耦合,限制了集中式管理的需求。

也许分散治理普及于亚马逊“编译它,运维它”的理念。团队为他们开发的软件负全部责任,也包含7*24小时的运行。全责任的方式并不常见,但是我们确实发现越来越多的公司在他们的团队中所推广。Netfix是另外一个接受这种理念的组件。每天凌晨3点被闹钟吵醒,因为你非常的关注写的代码质量。这在传统的集中式治理中这是一样多么不思议的事情呀。

分散数据管理

对数据的分散管理有多种不同的表现形式。最为抽象层次,它意味着不同系统中的通用概念是不同的。这带来的觉问题是大型的跨系统整合时,用户使用不同的售后支持将得到不同的促销信息。这种情况叫做并没有给用户显示所有的促销手段。不同的语法确实存在相同的词义或者(更差)相同的词义。

应用之间这个问题很普遍,但应用内部这个问题也存在,特别是当应用拆分成不同的组件时。对待这个问题非常有用的方式为[Bounded Context](http://martinfowler.com/bliki/BoundedContext.html)的领域驱动设计。DDD把复杂的领域拆分成不同上下文边界以及它们之间的关系。这样的过程对于整体架构和微服务框架都很有用,但是服务间存在着明显的关系,帮助我们对上下文边界进行区分,同时也像我们在业务功能中谈到的,强行拆分。

当对概念模式下决心进行分散管理时,微服务也决定着分散数据管理。当整体式的应用使用单一逻辑数据库对数据持久化时,企业通常选择在应用的范围内使用一个数据库,这些决定也受厂商的商业权限模式驱动。微服务让每个服务管理自己的数据库:无论是相同数据库的不同实例,或者是不同的数据库系统。这种方法叫[Polyglot Persistence](http://martinfowler.com/bliki/ ... e.html)。你可以把这种方法用在整体架构中,但是它更常见于微服务架构中。





图 4 Polyglot Persistence

微服务音分散数据现任意味着管理数据更新。处理数据更新的常用方法是使用事务来保证不同的资源修改数据库的一致性。这种方法通常在整体架构中使用。

使用事务是因为它能够帮助处理一至性问题,但对时间的消耗是严重的,这给跨服务操作带来难题。分布式事务非常难以实施,因此微服务架构[强调服务间事务的协调](http://www.eaipatterns.com/ram ... s.html),并清楚的认识一致性只能是最终一致性以及通过补偿运算处理问题。

选择处理不一致问题对于开发团队来说是新的挑战,但是也是一个常见的业务实践模式。通常业务上允许一定的不一致以满足快速响应的需求,但同时也采用一些恢复的进程来处理这种错误。当业务上处理强一致性消耗比处理错误的消耗少时,这种付出是值的的。

基础设施自动化

基础设施自动化技术在过去几年中得到了长足的发展:云计算,特别是AWS的发展,减少了构建、发布、运维微服务的复杂性。

许多使用微服务架构的产品或者系统,它们的团队拥有丰富的[持集部署](http://martinfowler.com/bliki/ ... y.html)以及它的前任[持续集成](http://martinfowler.com/articl ... n.html)的经验。团队使用这种方式构建软件致使更广泛的依赖基础设施自动化技术。下图说明这种构建的流程:





图 5 基本的构建流程

尽管这不是介绍自动部署的文章,但我们也打算介绍一下它的主要特征。我们希望我们的软件应该这样方便的工作,因此我们需要更多的自动化测试。流程中工作的软件改进意味着我们能自动的部署到各种新的环境中。

整体风格的应用相当开心的在各种环境中构建、测试、发布。事实证明,一旦你打算投资一条整体架构应用自动化的的生产线,那么你会发现发布更多的应用似乎非不那么的可怕。记住,CD(持续部署)的一个目标在于让发布变得无趣,因此无论是一个还是三个应用,它都一样的无趣。

另一个方面,我们发现使用微服务的团队更加依赖于基础设施的自动化。相比之下,在整体架构也微服务架构中,尽管发布的场景不同,但发布工作的无趣并没有多大的区别。





图 6 模块化部署的区别

容错性设计

使用服务作为组件的一个结果在于应用需要有能容忍服务的故障的设计。任务服务可能因为供应商的不可靠而故障,客户端需要尽可能的优化这种场景的响应。跟整体构架相比,这是一个缺点,因为它带来的额外的复杂性。这将让微服务团队时刻的想到服务故障的情况下用户的体验。Netflix 的[Simian Army](https://github.com/Netflix/SimianArmy)可以为每个应用的服务及数据中心提供日常故障检测和恢复。

这种产品中的自动化测试可以让大部分的运维团队正常的上下班。这并不意味着整体构架的应用没有这么精巧的监控配置,只是在我们的经验中它并不常见。

由于服务可以随时故障,快速故障检测,乃至,自动恢复变更非常重要。微服务应用把实时的监控放在应用的各个阶段中,检测构架元素(每秒数据库的接收的请求数)和业务相关的指标(把分钟接收的定单数)。监控系统可以提供一种早期故障告警系统,让开发团队跟进并调查。

对于微服务框架来说,这相当重要,因为微服务相互的通信可能导致紧急意外行为。许多专家车称赞这种紧急事件的价值,但事实是这种紧急行为有时是灾难。监控是至关重要的,它能快速发现这种紧急不良行为,让我们迅速修复它。

整体架构,跟微服务一样,在构建时是通明的,实情上,它们就是这样子的。它们不同之处在于,你需要清楚的认识到不同进程间运行的服务是不相关的。库对于同一进程是透明的,也因此不那么重要了。

微服务团队期望清楚的监控和记录每个服务的配置,比如使用仪表盘显示上/下线状态、各种运维和业务相关的指标。对断路器(circuit breaker)状态、目前的吞吐量和时延细节,我们也会经常遇到。

设计改进

微服务实践者,通常有不断改进设计的背景,他们把服务分解成进一步的工具。这些工具可以让应用开发者在不改变速度情况下,控制都他们的应用的需求变更。变更控制不意味首减少变更,而是使用适当的方式和工具,让它更加频繁,至少,很好让它变得可控。

不论如何,当你试图软件系统拆分成组件时,你将面临着如何拆分的问题。那么我们的决定拆分我们应用的原则是什么呢?首要的因素,组件可以被独立替换和更新的,这意味着我们寻找的关键在于,我们要想象着重写一个组件而不影响它们之前的协作关系。事实上,许多的微服务小组给它进一步的预期:服务应该能够报废的,而不是要长久的发展的。

Guardian网站就是这方面的一个优秀的例子,它初期被设计和构建成一个整体架构,但它已经向微服务的发展了。整体构架仍然是它网站的核心,但是他们使用微服务来增加那些使用整体架构API的新特性。这种方法增加这些临时的特性非常方便,比如运动新闻的特稿。这样站点的一个部分可以使用快速的开发语言迅速整合起来,当它过时后可以一次性移除。我们发现一家金融机构用相似的方法增加新的市场营销活动,数周或者几个月后把它撤销。

可代替是模块化开发中的一个特例,它是用模块来应对需要变更的。你希望让变更是相同模块,相同周期中进行变化而已。系统的某些很小做变更部分,也应该放在不同的服务中,这样它们更容易让它们消亡。如果你发现两个服务一直重复的变更时,这就是一个要合并它们的信号了。

把组件改成服务,增加了细化发布计划的一个机会。整体构架的任务变更需要整个应用的完整的构建和发布。然而,使用微服务,你只需要发布你要修改的服务就可以了。这将简化和加速你的发布周期。缺点是你需要为一个变更服务发布可能中断用户的体验而担心。传统的集成方法是使用版本来处理这些问题,但是微服务[版本仅是最后的通告手段](http://martinfowler.com/articl ... ioning)。我们需要在设计服务时尽可能的容忍供应商的变更,以避免提供多个版本。

微服务是未来吗?

我们写这篇文章的主要目的在于解释微服务的主要思想和原则。但是发时间做这事的时候,我们清醒的认识到微服务构架风格是一个非常重要的想法:一个值得企业应用中认真考虑的东西。我们最近使用这种风格构建了几个系统,认识那些也使用和喜欢这种方法的爱好者。

我们认识的使用这种方式的先行者,包含亚马逊、Netflix、The Guardian、The UK Government Digital Service、realestate.com.au、Forward和comparethemarket.com。2013看的巡回会议充满了向正在想成为微服务一分子的公司,包含Travis CI。此外,大量的组件正在从事我们认为是微服务的事,只是没有使用微服务的名字而已。(通常,它们被打上SOA的标签,尽管,我们认为SOA有许多不同的地方。)

尽管有这些积极的经验,然后,我们也不急于确认微服务是未来软件架构方向。至今为止,我们的经验与整体风格的应该中相比出来的是有优势的,但是我们意识知这样的事实,我们并没有足够的时间来证明我们的论证。

你所使用的架构通常是你开发出来后,使用的几年的实际成果。我们看到这些工程是在一个优秀的团队,带着对模块化的强烈追求,使用在过去几年中已经衰退的整体架构构建出来的。许多人相信,这种衰退不太可能与微服务有关,因为服务边界是清晰的并且很难再完善的。然而,当我们还没看到足够多的系统运行足够长时间时,我们不能肯定微服务构架是成熟的。

当然,还有原因就是,有人期望微服务构架不够成熟。在组件化方面的任何努力,其成功都依赖于软件如何拆分成适合的组件。指出组件化的准确边界应该在那,这是非常困难的。改良设计要承认边界的权益困境和因此带来的易于重构的重要性。但是当你的组件是被远程通信的服务时,重构比进程内的库又要困难的多。服务边界上的代码迁移是困难的,任务接口的变更需要参与者的共同协作,向后兼容的层次需要被增加,测试也变更更加复杂。

另一个问题在于,如果组件并没有清晰的划分,你的工作的复杂性将从组件内部转向组件间的关系。做这事不仅要围绕着复杂,它也要面对着不清晰和更难控制的地方。很容易想到,当你在一个小的、简单的组件内找东西,总比在没有关系的混乱的服务间要容易。

最后,团队技能也是重要的因素。新的技术倾向于被掌握更多的技能的团队使用。但是掌握多技能的团队中使用的技巧在较少技能的团队中并不是必需的。我们发现大量的少技能的团队构建混乱的整合构架,但是它要发时间去证明使用微服务在这种情况下会发生什么。一个糟糕的团队通常开发糟糕的系统:很难说,微服务在这种情况下是否能帮助它们,还是破坏它们。

  一个理性的争议在于,我们听说,你不应该从微服务构架开始做。最好从整体构架开发,做模块化开发,然后当整体构架出现问题是再把模块化拆分成服务。(尽管这种建议不是好主意,因为一个好的进程内接口并不是一个好的服务接口。)

因此我们持这种谨慎的乐观。到目前为止,我们还没有足够认识,关于微构架能否被大范围的推广。我们不能肯定的说,我们要终结什么,但是软件开发的挑战在于你只能在不完整的信息中决定你目前要处理的问题。

其它

微服务系统多大?

尽管“微服务”一词在架构风格中越来越流行,它的名字很不辛让人关注它的服务大小,以及对“微”这个组成的争议。在我们与微服务实践者的谈话中,我们发现了服务的大小范围。被报道的最大团队遵循亚马逊Tow Pizaa团队理念(比如,一个团队吃两个比萨就可以了。),这意味着不超过20号(一打)人。我们发现最小配置是半打的团队支撑起一打的服务。

这也引发这样的考虑:规模为一个服务一打人到一个服务一个人的团队打上微服务的标签。此刻我们认为,它们是一样的,但是随着对这种风格的深入研究,也存在我们改变我们的想法的可能。

微服务与SOA

当前我们谈到微服务时,通常会问,这是不是我们20年前讨论的面向服务架构(SOA)。这是一个很好的观点,因为微服务风格也SOA所提倡的一些优势非常相似。尽管如此,问题在于SOA意味的[太多不同的东西](http://martinfowler.com/bliki/ ... y.html)了,因此通常时候我们谈的所谓“SOA”时,它与我们谈论的风格不一至,因为它通常是指在整体风格应用中的ESB。

此外,我们发现面向服务的风格是这么的拙劣:从试图使用ESB隐藏复杂性, 到使用多年才认识到发费数百美元却没产生任务价值这样的失败,到集中治理模式抑制变更。而且这些问题往往很难发现。

可以肯定的时,微服务社区中使用的许多的技术都开发者是从大型机构的整合服务经验中发展来的。[Tolerant Reader](http://martinfowler.com/bliki/TolerantReader.html)模式就是这样的一个例子。由于互联网的发展,利用简单的协议这种方法,让它从这些经验传达的出来。这是从已经很复杂的集中式标准中的一种反模式,坦白的说,真让人惊叹。(无论何时,当你需要用一个服务来管理你的所有的服务,你就知道这很麻烦。)

SOA的这种常见行为让微服务的提倡者拒绝打上SOA的标签,尽管有人认为微服务是从SOA中发展而来的,或许面向服务是对的。无论如何,事实上SOA表达这么多的含义,它给一个团队清醒的认识到这种构架风格就已经值的了。

多语言,多选择

JVM做为一个平台,它的增长就是一个平台中运行多语言的最大的例子。过去二十年中,它通常做为更高层次语言的壳,以达到更高层次的抽象。比如,研究它的内部结构,、使用低级的语言写更高效的代码。尽管如此,许多整体风格并不需要这种层次的性能优化或者在语法及高层次上的抽象,这很常见(让我们很失望)。此外整体构架通常意味着使用单一的语言,这也限制着使用技术的数量。

实践标准和强制标准

它有点尴尬,微服务团队倾向于避免这种通常由企业架构队伍定制的僵硬的强制标准,但是它们却非常乐于甚至推广这些开放的标准,如HTTP、ATOM、其它微规范。

关键的不同在这些标准是怎么开发出来的,以及它们是怎么被推广的。标准被一些组件管理,如IETF认证标准,仅当它们在互联网上有几个在用的实现,通常源自于开源工程的成功应用。

这些标准单独分离出来,与那种在企业中通常有没有什么编码经验的或者没有什么影响力的厂商标准进行区别。

让做对事更容易

一方面,我们发现在持续发布、部署越来越多的使用自动化,是很多有用的工具开发出来帮助开发者和运营商的努力结果。为打包、代码管理、支撑服务的工具,或者增加标准监控的记录的工具,现在都非常常见了。网络中最好的,可能就是[Netflix's的开源工具](http://netflix.github.io/),但是包含[Dropwizard](http://dropwizard.codahale.com/)在内的其它工具也被广泛的使用着。

断路器(circuit breaker)和产品中现有的代码

[断路器](http://martinfowler.com/bliki/CircuitBreaker.html)(circuit breaker)出现在《[Realease It!](http://www.amazon.com/gp/produ ... 32NXZO)》一书中,与Bulkhead和Timeout这样的模式放在一起。实施起来,这些模式用于构建通信应用时相当的重要。[Netflix的博客](http://techblog.netflix.com/20 ... e.html)在解释它们的应用时,做了大量的工作。

同步是有害的

任务时候,你在服务间的调用使用同步的方法,都会遇到宕机时间的乘积效应。简单的说,你的系统宕机时间是你系统的单独组件的宕机时间的乘积。你面临的选择使用异步或者管理宕机时间。在[www.guardian.co.uk](http://www.guardian.co.uk/)中,它们在新平台中使用一种简单的规则来实现它:在Netflix中每次用户请求的同步调用,他们重新设计的平台API都会把它构建成异步的API来执行。

本文作者:船长&CAP
原文链接:http://www.cnblogs.com/liuning8023/p/4493156.html
版权归作者所有,转载请注明出处 查看全部


原文是 Martin Flower 于 2014 年 3 月 25 日写的[《Microservices》](http://martinfowler.com/articles/microservices.html)。



微服务

“微服务架构(*Microservice Architecture*)”一词在过去几年里广泛的传播,它用于描述一种设计应用程序的特别方式,作为一套独立可部署的服务。目前,这种架构方式还没有准确的定义,但是在围绕业务能力的组织、自动部署(automated deployment)、端智能(intelligence in the endpoints)、语言和数据的分散控制,却有着某种共同的特征。

“微服务(Microservices)”——只不过在满大街充斥的软件架构中的一新名词而已。尽管我们非常鄙视这样的东西,但是这玩意所描述的软件风格,越来越引起我们的注意。在过去几年里,我们发现越来越多的项目开始使用这种风格,以至于我们身边的同事在构建企业级应用时,把它理所当然的认为这是一种默认开发形式。然而,很不幸,微服务风格是什么,应该怎么开发,关于这样的理论描述却很难找到。

简而言之,微服务架构风格,就像是把一个单独的应用程序开发为一套小服务,每个小服务运行在自己的进程中,并使用轻量级机制通信,通常是 HTTP API。这些服务围绕业务能力来构建,并通过完全自动化部署机制来独立部署。这些服务使用不同的编程语言书写,以及不同数据存储技术,并保持最低限度的集中式管理。

在开始介绍微服务风格(microservice style)前,比较一下整体风格(monolithic style)是很有帮助的:一个完整应用程序(monolithic application)构建成一个单独的单元。企业级应用通常被构建成三个主要部分:客户端用户界面(由运行在客户机器上的浏览器的 HTML 页面、Javascript 组成)、数据库(由许多的表构成一个通用的、相互关联的数据管理系统)、服务端应用。服务端应用处理 HTTP 请求,执行领域逻辑(domain logic),检索并更新数据库中的数据,使用适当的 HTML 视图发送给浏览器。服务端应用是完整的 ,是一个单独的的逻辑执行。任何对系统的改变都涉及到重新构建和部署一个新版本的服务端应用程序。

这样的整体服务(monolithic server)是一种构建系统很自然的方式。虽然你可以利用开发语基础特性把应用程序划分成类、函数、命名空间,但所有你处理请求的逻辑都运行在一个单独的进程中。在某些场景中,开发者可以在的笔计本上开发、测试应用,然后利用部署通道来保证经过正常测试的变更,发布到产品中。你也可以使用横向扩展,通过负载均衡将多个应用部署到多台服务器上。

整体应用程序(Monolithic applications)相当成功,但是越来越多的人感觉到有点不妥,特别是在云中部署时。变更发布周期被绑定了——只是变更应用程序的一小部分,却要求整个重新构建和部署。随着时间的推移,很难再保持一个好的模块化结构,使得一个模块的变更很难不影响到其它模块。扩展就需要整个应用程序的扩展,而不能进行部分扩展。

microservices-translate-1.png

图 1 整理架构与微服务架构

这导致了微服务架构风格(microservice architectural style)的出现:把应用程序构建为一套服务。事实是,服务可以独立部署和扩展,每个服务提供了一个坚实的模块边界,甚至不同的服务可以用不同的编程语言编写。它们可以被不同的团队管理。

**我们必须说,微服务风格不是什么新东西,它至少可以追溯到 Unix 的设计原则。**但是并没有太多人考虑微服务架构,如果他们用了,那么很多软件都会更好。

微服务风格的特性

微服务风格并没有一个正式的定义,但我们可以尝试描述一下微服务风格所具有的共同特点。并不是所有的微服务风格都要具有所有的特性,但我们期望常见的微服务都应该有这些特性。我们的意图是尝试描述我们工作中或者在其它我们了解的组件中所理解的微服务。特别是,我们不依赖于那些已经明确过的定义。

组件化(Componentization )与服务(Services)

自从我们开始软件行业以来,一直希望由组件构建系统,就像我们在物理世界所看到的一样。在过去的几十年里,我们已经看到了公共库的大量简编取得了相当的进步,这些库是大部分语言平台的一部分。

当我们谈论组件时,可能会陷入一个困境——什么是组件。我们的定义是,组件(component)是一个可独立替换和升级的软件单元。

微服务架构(Microservice architectures)会使用库(libraries),但组件化软件的主要方式是把它拆分成服务。我们把库(libraries)定义为组件,这些组件被链接到程序,并通过内存中函数调用(in-memory function calls)来调用,而服务(services )是进程外组件(out-of-process components),他们利用某个机制通信,比如 WebService 请求,或远程过程调用(remote procedure call)。组件和服务在很多面向对象编程中是不同的概念。

把服务当成组件(而不是组件库)的一个主要原因是,服务可以独立部署。如果你的应用程序是由一个单独进程中的很多库组成,那么对任何一个组件的改变都将导致必须重新部署整个应用程序。但是如果你把应用程序拆分成很多服务,那你只需要重新部署那个改变的服务。当然,这也不是绝对的,有些服务会改变导致协调的服务接口,但是一个好的微服务架构的目标就是通过在服务契约(service contracts)中解耦服务的边界和进化机制来避免这些。

另一个考虑是,把服务当组件将拥有更清晰的组件接口。大多数开发语言都没有一个良好的机制来定义一个[发布的接口](http://martinfowler.com/bliki/ ... e.html)(Published Interface)。发布的接口是指一个类向外公开的成员,比如 Java 中的声明为 Public 的成员,C# 中声明为非 Internal 的成员。通常只有在文档和规范中会说明,这是为了让避免客户端破坏组件的封装性,阻止组件间紧耦合。服务通过使用公开远程调用机制可以很容易避免这些。

像这样使用服务也有不足之处。远程调用比进制内调用更消耗资源,因此远程 API 需要粗粒度(coarser-grained),但这会比较难使用。如果你需要调整组件间的职责分配,当跨越进程边界时,这样做将会很难。

一个可能是,我们看到,服务可以映射到运行时进程(runtime processes)上,但也只是一个可能。服务可以由多个进程组成,它们会同时开发和部署,例如一个应用程序进程和一个只能由这个服务使用的数据库。

围绕业务功能的组织

当寻找把一个大的应用程序拆分成小的部分时,通常管理都会集中在技术层面,UI团队、服务端业务逻辑团队和数据库团队。当使用这种标准对团队进行划分时,甚至小小的更变都将导致跨团队项目协作,从而消耗时间和预算审批。一个高效的团队会针对这种情况进行改善,两权相害取其轻。业务逻辑无处不在。实践中,这就是 Conway's Law 的一个例子。
设计一个系统的任何组织(广义上)都会产生这样一种设计,其结构是组织交流结构的复制。
——Melvyn Conway, 1967

Melvyn Conway 的意识是,像下图所展示的,设计一个系统时,将人员划分为 UI 团队,中间件团队,DBA 团队,那么相应地,软件系统也就会自然地被划分为 UI 界面,中间件系统,数据库。

microservices-translate-2.png

图 2  实践中的 Conway's Law

微服务(microservice )的划分方法不同,它倾向围绕业务功能的组织来分割服务。这些服务实现商业领域的软件,包括用户界面,持久化存储,任何的外部协作。因此,团队是跨职能的(cross-functional),包含开发过程所要求的所有技能:用户体验(user-experience)、数据库(database)和项目管理(project management)。

microservices-translate-3.png

图 3 通过团队边界强调服务边界

[www.comparethemarket.com](http://www.comparethemarket.com/)就是采用这种组织形式。跨职能的团队同时负责构建和运营每个产品,每个产品被分割成许多单个的服务,这些服务通过消息总线(Message Bus)通信。

大型的整体应用程序(monolithic applications)也可以按照业务功能进行模块化(modularized),尽管这样情况不常见。当然,我们可以敦促一个构建整体应用程序(monolithic application )的大型团队,按业务线来分割自己。我们已经看到的主要问题是,这种组件形式会导致很多的依赖。如果整体应用程序(monolithic applications)跨越很多模块边界(modular boundaries ),那么对于团队的每个成员短期内修复它们是很困难的。此外,我们发现,模块化需要大量的强制规范。服务组件所要求的必需的更明确的分离使得保持团队边界清晰更加容易。

产品不是项目

大部分的软件开发者都使用这样的项目模式:至力于提供一些被认为是完整的软件。交付一个他们认为完成的软件。软件移交给运维组织,然后,解散构建软件的团队。

微服务(Microservice )的支持者认为这种做法是不可取的,并提议团队应该负责产品的整个生命周期。Amazon 理念是“[你构建,你运维(you build, you run it)](https://queue.acm.org/detail.cfm?id=1142065)”,要求开发团队对软件产品的整个生命周期负责。这要求开发者每天都关注他们的软件运行如何,增加更用户的联系,同时承担一些售后支持。

产品的理念,跟业务能力联系起来。不是着眼于完成一套功能的软件,而是有一个持续的关系,是如何能够帮助软件及其用户提升业务能力。

为什么相同的方法不能用在整体应用程序(monolithic applications),但更小的服务粒度能够使创建服务的开发者与使用者之间的个人联系更容易。

强化终端及弱化通道

当构建不同的进程间通信机制的时候,我们发现有许多的产品和方法能够把更加有效方法强加入的通信机制中。比如企业服务总线(ESB),这样的产品提供更有效的方式改进通信过程中的路由、编码、传输、以及业务处理规则。

微服务倾向于做如下的选择:强化终端及弱化通道。微服务的应用致力松耦合和高内聚:采用单独的业务逻辑,表现的更像经典Unix意义上的过滤器一样,接受请求、处理业务逻辑、返回响应。它们更喜欢简单的REST风格,而不是复杂的协议,如WS或者BPEL或者集中式框架。

有两种协议最经常被使用到:包含资源API的HTTP的请求-响应和轻量级消息通信协议。最为重要的建议为:
善于利用网络,而不是限制(Be of the web, not behind the web)。
——Ian Robinson

微服务团队采用这样的原则和规范:基于互联网(广义上,包含Unix系统)构建系统。这样经常使用的资源几乎不用什么的代价就可以被开发者或者运行商缓存。

第二种做法是通过轻量级消息总线来发布消息。这种的通信协议非常的单一(单一到只负责消息路由),像RabbitMQ或者ZeroMQ这样的简单的实现甚至像可靠的异步机制都没提供,以至于需要依赖产生或者消费消息的终端或者服务来处理这类问题。

在整体工风格中,组件在进程内执行,进程间的消息通信通常通过调用方法或者回调函数。从整体式风格到微服务框架最大的问题在于通信方式的变更。从内存内部原始的调用变成远程调用,产生的大量的不可靠通信。因此,你需要把粗粒度的方法成更加细粒度的通信。

分散治理

集中治理的一种好处是在单一平台上进行标准化。经验表明这种趋势的好处在缩小,因为并不是所有的问题都相同,而且解决方案并不是万能的。我们更加倾向于采用适当的工具解决适当的问题,整体式的应用在一定程度上比多语言环境更有优势,但也适合所有的情况。

把整体式框架中的组件,拆分成不同的服务,我们在构建它们时有更多的选择。你想用Node.js去开发报表页面吗?做吧。用C++来构建时时性要求高的组件?很好。你想以在不同类型的数据库中切换,来提高组件的读取性能?我们现在有技术手段来实现它了。

当然,你是可以做更多的选择,但也不意味的你就可以这样做,因为你的系统使用这种方式进行侵害意味着你已经有的决定。

采用微服务的团队更喜欢不同的标准。他们不会把这些标准写在纸上,而是喜欢这样的思想:开发有用的工具来解决开发者遇到的相似的问题。这些工具通常从实现中成长起来,并进行的广泛范围内分享,当然,它们有时,并不一定,会采用开源模式。现在开源的做法也变得越来越普遍,git或者github成为了它们事实上的版本控制系统。

Netfix就是这样的一个组织,它是非常好的一个例子。分享有用的、尤其是经过实践的代码库激励着其它的开发着也使用相似的方式来解决相似的问题,当然,也保留着根据需要使用不同的方法的权力。共享库更关注于数据存储、进程内通信以及我们接下来做讨论到的自动化等这些问题上。

微服务社区中,开销问题特别引人注意。这并不是说,社区不认为服务交互的价值。相反,正是因为发现到它的价值。这使得他们在寻找各种方法来解决它们。如[Tolearant Reader](http://martinfowler.com/bliki/TolerantReader.html)和[Consumer-Driven Contracts](http://martinfowler.com/articl ... s.html)这样的设计模式就经常被微服务使用。这些模式解决了独立服务在交互过程中的消耗问题。使用Consumer-Driven Contracts增加了你的信心,并实现了快速的反馈机制。事实上,我们知道澳大利亚的一个团队致力使用Consumer-Drvien Contracts开发新的服务。他们使用简单的工程,帮助他们定义服务的接口。使得在新服务的代码开始编写之前,这些接口就成为自动化构建的一个部分。构建出来的服务,只需要指出这些接口适用的范围,一个优雅的方法避免了新软件中的'YAGNI '困境。这些技术和工具在使用过程中完善,通过减少服务间的耦合,限制了集中式管理的需求。

也许分散治理普及于亚马逊“编译它,运维它”的理念。团队为他们开发的软件负全部责任,也包含7*24小时的运行。全责任的方式并不常见,但是我们确实发现越来越多的公司在他们的团队中所推广。Netfix是另外一个接受这种理念的组件。每天凌晨3点被闹钟吵醒,因为你非常的关注写的代码质量。这在传统的集中式治理中这是一样多么不思议的事情呀。

分散数据管理

对数据的分散管理有多种不同的表现形式。最为抽象层次,它意味着不同系统中的通用概念是不同的。这带来的觉问题是大型的跨系统整合时,用户使用不同的售后支持将得到不同的促销信息。这种情况叫做并没有给用户显示所有的促销手段。不同的语法确实存在相同的词义或者(更差)相同的词义。

应用之间这个问题很普遍,但应用内部这个问题也存在,特别是当应用拆分成不同的组件时。对待这个问题非常有用的方式为[Bounded Context](http://martinfowler.com/bliki/BoundedContext.html)的领域驱动设计。DDD把复杂的领域拆分成不同上下文边界以及它们之间的关系。这样的过程对于整体架构和微服务框架都很有用,但是服务间存在着明显的关系,帮助我们对上下文边界进行区分,同时也像我们在业务功能中谈到的,强行拆分。

当对概念模式下决心进行分散管理时,微服务也决定着分散数据管理。当整体式的应用使用单一逻辑数据库对数据持久化时,企业通常选择在应用的范围内使用一个数据库,这些决定也受厂商的商业权限模式驱动。微服务让每个服务管理自己的数据库:无论是相同数据库的不同实例,或者是不同的数据库系统。这种方法叫[Polyglot Persistence](http://martinfowler.com/bliki/ ... e.html)。你可以把这种方法用在整体架构中,但是它更常见于微服务架构中。

microservices-translate-4.png

图 4 Polyglot Persistence

微服务音分散数据现任意味着管理数据更新。处理数据更新的常用方法是使用事务来保证不同的资源修改数据库的一致性。这种方法通常在整体架构中使用。

使用事务是因为它能够帮助处理一至性问题,但对时间的消耗是严重的,这给跨服务操作带来难题。分布式事务非常难以实施,因此微服务架构[强调服务间事务的协调](http://www.eaipatterns.com/ram ... s.html),并清楚的认识一致性只能是最终一致性以及通过补偿运算处理问题。

选择处理不一致问题对于开发团队来说是新的挑战,但是也是一个常见的业务实践模式。通常业务上允许一定的不一致以满足快速响应的需求,但同时也采用一些恢复的进程来处理这种错误。当业务上处理强一致性消耗比处理错误的消耗少时,这种付出是值的的。

基础设施自动化

基础设施自动化技术在过去几年中得到了长足的发展:云计算,特别是AWS的发展,减少了构建、发布、运维微服务的复杂性。

许多使用微服务架构的产品或者系统,它们的团队拥有丰富的[持集部署](http://martinfowler.com/bliki/ ... y.html)以及它的前任[持续集成](http://martinfowler.com/articl ... n.html)的经验。团队使用这种方式构建软件致使更广泛的依赖基础设施自动化技术。下图说明这种构建的流程:

microservices-translate-5.png

图 5 基本的构建流程

尽管这不是介绍自动部署的文章,但我们也打算介绍一下它的主要特征。我们希望我们的软件应该这样方便的工作,因此我们需要更多的自动化测试。流程中工作的软件改进意味着我们能自动的部署到各种新的环境中。

整体风格的应用相当开心的在各种环境中构建、测试、发布。事实证明,一旦你打算投资一条整体架构应用自动化的的生产线,那么你会发现发布更多的应用似乎非不那么的可怕。记住,CD(持续部署)的一个目标在于让发布变得无趣,因此无论是一个还是三个应用,它都一样的无趣。

另一个方面,我们发现使用微服务的团队更加依赖于基础设施的自动化。相比之下,在整体架构也微服务架构中,尽管发布的场景不同,但发布工作的无趣并没有多大的区别。

microservices-translate-6.png

图 6 模块化部署的区别

容错性设计

使用服务作为组件的一个结果在于应用需要有能容忍服务的故障的设计。任务服务可能因为供应商的不可靠而故障,客户端需要尽可能的优化这种场景的响应。跟整体构架相比,这是一个缺点,因为它带来的额外的复杂性。这将让微服务团队时刻的想到服务故障的情况下用户的体验。Netflix 的[Simian Army](https://github.com/Netflix/SimianArmy)可以为每个应用的服务及数据中心提供日常故障检测和恢复。

这种产品中的自动化测试可以让大部分的运维团队正常的上下班。这并不意味着整体构架的应用没有这么精巧的监控配置,只是在我们的经验中它并不常见。

由于服务可以随时故障,快速故障检测,乃至,自动恢复变更非常重要。微服务应用把实时的监控放在应用的各个阶段中,检测构架元素(每秒数据库的接收的请求数)和业务相关的指标(把分钟接收的定单数)。监控系统可以提供一种早期故障告警系统,让开发团队跟进并调查。

对于微服务框架来说,这相当重要,因为微服务相互的通信可能导致紧急意外行为。许多专家车称赞这种紧急事件的价值,但事实是这种紧急行为有时是灾难。监控是至关重要的,它能快速发现这种紧急不良行为,让我们迅速修复它。

整体架构,跟微服务一样,在构建时是通明的,实情上,它们就是这样子的。它们不同之处在于,你需要清楚的认识到不同进程间运行的服务是不相关的。库对于同一进程是透明的,也因此不那么重要了。

微服务团队期望清楚的监控和记录每个服务的配置,比如使用仪表盘显示上/下线状态、各种运维和业务相关的指标。对断路器(circuit breaker)状态、目前的吞吐量和时延细节,我们也会经常遇到。

设计改进

微服务实践者,通常有不断改进设计的背景,他们把服务分解成进一步的工具。这些工具可以让应用开发者在不改变速度情况下,控制都他们的应用的需求变更。变更控制不意味首减少变更,而是使用适当的方式和工具,让它更加频繁,至少,很好让它变得可控。

不论如何,当你试图软件系统拆分成组件时,你将面临着如何拆分的问题。那么我们的决定拆分我们应用的原则是什么呢?首要的因素,组件可以被独立替换和更新的,这意味着我们寻找的关键在于,我们要想象着重写一个组件而不影响它们之前的协作关系。事实上,许多的微服务小组给它进一步的预期:服务应该能够报废的,而不是要长久的发展的。

Guardian网站就是这方面的一个优秀的例子,它初期被设计和构建成一个整体架构,但它已经向微服务的发展了。整体构架仍然是它网站的核心,但是他们使用微服务来增加那些使用整体架构API的新特性。这种方法增加这些临时的特性非常方便,比如运动新闻的特稿。这样站点的一个部分可以使用快速的开发语言迅速整合起来,当它过时后可以一次性移除。我们发现一家金融机构用相似的方法增加新的市场营销活动,数周或者几个月后把它撤销。

可代替是模块化开发中的一个特例,它是用模块来应对需要变更的。你希望让变更是相同模块,相同周期中进行变化而已。系统的某些很小做变更部分,也应该放在不同的服务中,这样它们更容易让它们消亡。如果你发现两个服务一直重复的变更时,这就是一个要合并它们的信号了。

把组件改成服务,增加了细化发布计划的一个机会。整体构架的任务变更需要整个应用的完整的构建和发布。然而,使用微服务,你只需要发布你要修改的服务就可以了。这将简化和加速你的发布周期。缺点是你需要为一个变更服务发布可能中断用户的体验而担心。传统的集成方法是使用版本来处理这些问题,但是微服务[版本仅是最后的通告手段](http://martinfowler.com/articl ... ioning)。我们需要在设计服务时尽可能的容忍供应商的变更,以避免提供多个版本。

微服务是未来吗?

我们写这篇文章的主要目的在于解释微服务的主要思想和原则。但是发时间做这事的时候,我们清醒的认识到微服务构架风格是一个非常重要的想法:一个值得企业应用中认真考虑的东西。我们最近使用这种风格构建了几个系统,认识那些也使用和喜欢这种方法的爱好者。

我们认识的使用这种方式的先行者,包含亚马逊、Netflix、The Guardian、The UK Government Digital Service、realestate.com.au、Forward和comparethemarket.com。2013看的巡回会议充满了向正在想成为微服务一分子的公司,包含Travis CI。此外,大量的组件正在从事我们认为是微服务的事,只是没有使用微服务的名字而已。(通常,它们被打上SOA的标签,尽管,我们认为SOA有许多不同的地方。)

尽管有这些积极的经验,然后,我们也不急于确认微服务是未来软件架构方向。至今为止,我们的经验与整体风格的应该中相比出来的是有优势的,但是我们意识知这样的事实,我们并没有足够的时间来证明我们的论证。

你所使用的架构通常是你开发出来后,使用的几年的实际成果。我们看到这些工程是在一个优秀的团队,带着对模块化的强烈追求,使用在过去几年中已经衰退的整体架构构建出来的。许多人相信,这种衰退不太可能与微服务有关,因为服务边界是清晰的并且很难再完善的。然而,当我们还没看到足够多的系统运行足够长时间时,我们不能肯定微服务构架是成熟的。

当然,还有原因就是,有人期望微服务构架不够成熟。在组件化方面的任何努力,其成功都依赖于软件如何拆分成适合的组件。指出组件化的准确边界应该在那,这是非常困难的。改良设计要承认边界的权益困境和因此带来的易于重构的重要性。但是当你的组件是被远程通信的服务时,重构比进程内的库又要困难的多。服务边界上的代码迁移是困难的,任务接口的变更需要参与者的共同协作,向后兼容的层次需要被增加,测试也变更更加复杂。

另一个问题在于,如果组件并没有清晰的划分,你的工作的复杂性将从组件内部转向组件间的关系。做这事不仅要围绕着复杂,它也要面对着不清晰和更难控制的地方。很容易想到,当你在一个小的、简单的组件内找东西,总比在没有关系的混乱的服务间要容易。

最后,团队技能也是重要的因素。新的技术倾向于被掌握更多的技能的团队使用。但是掌握多技能的团队中使用的技巧在较少技能的团队中并不是必需的。我们发现大量的少技能的团队构建混乱的整合构架,但是它要发时间去证明使用微服务在这种情况下会发生什么。一个糟糕的团队通常开发糟糕的系统:很难说,微服务在这种情况下是否能帮助它们,还是破坏它们。

  一个理性的争议在于,我们听说,你不应该从微服务构架开始做。最好从整体构架开发,做模块化开发,然后当整体构架出现问题是再把模块化拆分成服务。(尽管这种建议不是好主意,因为一个好的进程内接口并不是一个好的服务接口。)

因此我们持这种谨慎的乐观。到目前为止,我们还没有足够认识,关于微构架能否被大范围的推广。我们不能肯定的说,我们要终结什么,但是软件开发的挑战在于你只能在不完整的信息中决定你目前要处理的问题。

其它

微服务系统多大?

尽管“微服务”一词在架构风格中越来越流行,它的名字很不辛让人关注它的服务大小,以及对“微”这个组成的争议。在我们与微服务实践者的谈话中,我们发现了服务的大小范围。被报道的最大团队遵循亚马逊Tow Pizaa团队理念(比如,一个团队吃两个比萨就可以了。),这意味着不超过20号(一打)人。我们发现最小配置是半打的团队支撑起一打的服务。

这也引发这样的考虑:规模为一个服务一打人到一个服务一个人的团队打上微服务的标签。此刻我们认为,它们是一样的,但是随着对这种风格的深入研究,也存在我们改变我们的想法的可能。

微服务与SOA

当前我们谈到微服务时,通常会问,这是不是我们20年前讨论的面向服务架构(SOA)。这是一个很好的观点,因为微服务风格也SOA所提倡的一些优势非常相似。尽管如此,问题在于SOA意味的[太多不同的东西](http://martinfowler.com/bliki/ ... y.html)了,因此通常时候我们谈的所谓“SOA”时,它与我们谈论的风格不一至,因为它通常是指在整体风格应用中的ESB。

此外,我们发现面向服务的风格是这么的拙劣:从试图使用ESB隐藏复杂性, 到使用多年才认识到发费数百美元却没产生任务价值这样的失败,到集中治理模式抑制变更。而且这些问题往往很难发现。

可以肯定的时,微服务社区中使用的许多的技术都开发者是从大型机构的整合服务经验中发展来的。[Tolerant Reader](http://martinfowler.com/bliki/TolerantReader.html)模式就是这样的一个例子。由于互联网的发展,利用简单的协议这种方法,让它从这些经验传达的出来。这是从已经很复杂的集中式标准中的一种反模式,坦白的说,真让人惊叹。(无论何时,当你需要用一个服务来管理你的所有的服务,你就知道这很麻烦。)

SOA的这种常见行为让微服务的提倡者拒绝打上SOA的标签,尽管有人认为微服务是从SOA中发展而来的,或许面向服务是对的。无论如何,事实上SOA表达这么多的含义,它给一个团队清醒的认识到这种构架风格就已经值的了。

多语言,多选择

JVM做为一个平台,它的增长就是一个平台中运行多语言的最大的例子。过去二十年中,它通常做为更高层次语言的壳,以达到更高层次的抽象。比如,研究它的内部结构,、使用低级的语言写更高效的代码。尽管如此,许多整体风格并不需要这种层次的性能优化或者在语法及高层次上的抽象,这很常见(让我们很失望)。此外整体构架通常意味着使用单一的语言,这也限制着使用技术的数量。

实践标准和强制标准

它有点尴尬,微服务团队倾向于避免这种通常由企业架构队伍定制的僵硬的强制标准,但是它们却非常乐于甚至推广这些开放的标准,如HTTP、ATOM、其它微规范。

关键的不同在这些标准是怎么开发出来的,以及它们是怎么被推广的。标准被一些组件管理,如IETF认证标准,仅当它们在互联网上有几个在用的实现,通常源自于开源工程的成功应用。

这些标准单独分离出来,与那种在企业中通常有没有什么编码经验的或者没有什么影响力的厂商标准进行区别。

让做对事更容易

一方面,我们发现在持续发布、部署越来越多的使用自动化,是很多有用的工具开发出来帮助开发者和运营商的努力结果。为打包、代码管理、支撑服务的工具,或者增加标准监控的记录的工具,现在都非常常见了。网络中最好的,可能就是[Netflix's的开源工具](http://netflix.github.io/),但是包含[Dropwizard](http://dropwizard.codahale.com/)在内的其它工具也被广泛的使用着。

断路器(circuit breaker)和产品中现有的代码

[断路器](http://martinfowler.com/bliki/CircuitBreaker.html)(circuit breaker)出现在《[Realease It!](http://www.amazon.com/gp/produ ... 32NXZO)》一书中,与Bulkhead和Timeout这样的模式放在一起。实施起来,这些模式用于构建通信应用时相当的重要。[Netflix的博客](http://techblog.netflix.com/20 ... e.html)在解释它们的应用时,做了大量的工作。

同步是有害的

任务时候,你在服务间的调用使用同步的方法,都会遇到宕机时间的乘积效应。简单的说,你的系统宕机时间是你系统的单独组件的宕机时间的乘积。你面临的选择使用异步或者管理宕机时间。在[www.guardian.co.uk](http://www.guardian.co.uk/)中,它们在新平台中使用一种简单的规则来实现它:在Netflix中每次用户请求的同步调用,他们重新设计的平台API都会把它构建成异步的API来执行。


本文作者:船长&CAP
原文链接:http://www.cnblogs.com/liuning8023/p/4493156.html
版权归作者所有,转载请注明出处


微服务架构中服务端与客户端如何编码

回复

jackie 发起了问题 • 1 人关注 • 0 个回复 • 303 次浏览 • 2017-06-20 14:22 • 来自相关话题

Martin Fowler关于微服务的原文翻译(一)

flychao88 发表了文章 • 0 个评论 • 244 次浏览 • 2017-06-15 15:43 • 来自相关话题

原文如下:http://martinfowler.com/articl ... .html

微服务

一个新的架构术语

“微服务架构”一词是在过去几年里涌现出来的,它用于描述一种独立部署的软件应用设计方式。这种架构方式并没有非常明确的定义,但有一些共同的特点就是围绕在业务能力、自动化布署、端到端的整合以及语言和数据的分散控制上面。

“微服务”- 这是在软件架构领域这个非常拥挤的街道上,冒出的一个新名词而已。虽然我们对这个新出的名词不屑一顾,但是它所描述的软件系统的风格越来越吸引我们的注意力。在过去的几年里,我们发现越来越多的项目开始使用这个风格,并且到目前为止得到的反馈都是积极的,以至于我身边的许多同事在设计企业架构时,都把它作为默认的构建方式,然而很不幸,到底什么是微服务,我们又如何来使用它,外界并没有太多的信息可供参考。

总之,微服务这种架构风格就是把一组小服务演化成为一个单一的应用的一种方法。每个应用都运行在自己的进程中,并通过轻量级的机制保持通信,就像HTTP这样的API。这些服务要基于业务场景,并使用自动化布署工具进行独立的发布。可以有一个非常轻量级的集中式管理来协调这些服务,可以使用不同的语言来编写服务,也可以使用不同的数据存储。
 
在开始解释什么是微服务之前,先介绍一下单体应用还是很有用的:把一个单体应用构建成为一个部分。企业应用通过是由三个重要部分组成:客户端界面(由HTML、Javascript组成,使用浏览器进行访问)、数据库(由许多的表组件构成一个通用的、相互关联的数据管理系统)、服务端应用。服务端应用处理HTTP请求、执行领域逻辑、检索并更新数据库中的数据、选择和填充HTML视图发送给客户端。这个服务端应用是一个单块结构也就是一个整体,这是一个可执行的单一逻辑,系统中的任何修改都将导致服务端应用重新编译和布署一个新版本。

就这样一个单体应用很自然的被构建成了一个系统,虽然可以使用开发语言基本特性会把应用封装成类、函数、命名空间,但是业务中所有请求都要在单一的进程中处理完成,在某些场景中,你可以在开发人员的笔记本电脑中运行和测试,并且通过布署通道将测试通过的程序布署到生产环境中,你还可以水平扩展,利用负载均衡将实例布署到多台服务器中。

的确,单体应用也是很成功的,但是越来越多的人感觉到了不妥,特别是应用程序被发布到了云的时候,变更发布周期被绑定了 —- 原来可以划分成小的应用、小的需要的变更,需要统一的进行编译和发布。随着时间的推移,软件开发者很难保持原有好的模块架构,使得一个模块的变更很难不会影响到其它的模块,而且在扩展方面也只能进行整体的扩展,而不能根据进行部分的扩展。 
 

 
这些原因导致了微服务架构风格的出现:以服务构建应用。这些服务还可以被独立布署、独立扩展,每个服务也都提供了清晰的模块边界,甚至不同的服务都可以使用不同的编程语言来实现,也可以由不同的团队进行管理。

微服务的概念不是我们发明的,它至少起源于Unix时代的设计原则,我们认为这种风格所带来的好处,并没有引起足够多人的重视。

微服务架构特征

我们没有办法对微服务有一个正式的定义,但我们可以尝试表述适合这种架构的共同特征来给它打上特性标签,共同特性并不代表每个服务都具备这些特点,但是我们真的期望大多数微服务架构能具备其中大部分特点。虽然我们的作者已经是松散社区的核心成员,但是我们也在尝试描述我们工作中或者我们了解的组件中所理解的微服务。我们并不依赖于那些已经明确过的定义。
 
组件化与服务

只要我们一直在从事软件行业,我们的愿望就是,软件由很多组件组装在一起,如同物理现实世界中类似的构造方式。在过去的几十年里,我们已经看到了大部分语言平台公共库有了长足的进步。

当我们在谈论组件时,我们遇到了组件定义方面的困难,我们给定的定义是:一个组件是软件中的一个部分,可以独立的替换和升级。

微服务也会使用组件库,将一个软件组件化的主要方式就是将其分解成服务,我们定义的库是可以连接到程序并使用内存函数的的组件库,服务是进程外的组件,如Web请求服务或者远程调用来相互通信的组件。(这种定义的方式与其它面向对象程序中服务对象的概念是不一样的。)

把服务当成组件(而不是组件库)的一个原因是服务可以独立布署,如果你有一个应用是由多个库组成并且运行在一个进程中,那么任何一点的改变都会引起整个应用的重新发布,但是将这个应用拆解为多个服务,你可以期待每个服务的变更仅需要发布相应的服务就可以,当然这也不是绝对的,比如导致服务接口变更的更新就需要相应服务的变化,但是良好的架构设计是通过聚合服务边界并且按照合约实现服务演化,最大限度地减少因为改变影响其他地方。

把服务当成组件的另一个考虑是这会拥有更加清晰的接口,大多数的语言并没有一个很好的机制来定义一个明确显式的发布接口,通常只有文档和规范说明,让用户避免组件间过度紧密而导致高耦合,通过显示的远程调用机制,可以避免这种情况。
 
使用服务也有其自身的缺点,远程调用比进程内部调用更加消耗性能,而且远程的API往往是粗粒度的,用起来不是很友好,对组件的职责进行变更,也会影响到进程间的交互,那么操作起来也比较困难。

第一个可能性,我们看到每个服务是运行在独立的进程上的。注意,这只是第一个可能性。服务也可以由多个进程组成,它们是同时开发和部署的,如果一个应用进程和一个仅由该服务使用的数据库。

围绕业务能力进行组织

当我们把一个大的应用拆分成小的部分时,我们的注意力主要集中在技术层面,拆分成UI团队、服务端的逻辑团队和数据库团队。当使用这种标准对团队进行划分时,甚至一个非常小的更变都将导致跨团队间项目协作,从而消耗时间和预算审批。一个高效的团队会针对这种情况进行改善,关注它们所涉及的应用逻辑,并从中做出较好的选择。换句话说,逻辑无处不在。康威定律就是一个例子。
 
一个组织的沟通结构反映了其设计的系统的结构 -- Melvyn Conway, 1967
 
 

微服务的划分方法有所不同,它更倾向于围绕业务功能对服务结构进行划分、拆解,这些服务可以采用不同的技术栈来实现,包括用户界面,持久层存储,或任何对外协作,因此团队应该是跨职能的,包括开发所需要的全部技术:用户体验、数据库和项目管理。
 

 
按照这种方式组织的公司是 www.comparethemarket.com,跨职能团队负责建立和操作每个产品并且每个产品都被分成若干单独的服务通过消息进行通信。

大型的单体应用也可以按照业务功能进行模块化的,尽管这种例子不常见。当然,我们也会敦促一个大型团队在构建一个单体应用时按照业务线来进行划分,我们能看到主要问题在于,这种组件形式会导致很多的上下文依赖,如果这个系统跨越很多模块边界,对于一个单独团队是很难在短时间解决问题的。此外,我们发现模块化方式需要大量的规范去强制执行,而服务组件明确的划分,使得团队间的边界也变得清晰起来。

产品不是项目

大多数的开发工作是使用这样一种模型:其目的是完成可以交付的软件,软件开发完成就交给了维护团队,该项目组也就解散了。

微服务的支持者建议避免这种模型,认为一个团队应该负责产品的整个生命周期,一个很通用的概念就是Amazon’s的“you build, you run it”,它要求开发团队对软件产品的整个生命周期负责,这使得开发人员可以每天都关注产品的运行情况,而且也能够与用户保持紧密的联系,做一些必要的支持工作。

产品方式开发意味着与业务能力紧紧捆绑在一起,而不是将软件看成是一系列完成的功能,他们会关注如何让软件帮助其用户提升业务能力。
 
单体应用也可以采用上述产品的理念,但是更小粒度的服务可以更容易的创建开发者与用户之间的关系。

智能终端与弱管道

当在不同的进程之间构建各种通信结构时,我们已经看到许多产品和方法,来强调将大量的智能特性融入到通信机制本身,这种情况的一个典型例子就是“企业服务总线”(Enterprise Service Bus,ESB)。ESB产品经常包括高度智能的设施来进行消息的路由、编排、转换,并应用业务规则。

微服务社区主张采用另一种做法:智能终端和弱管道。使用微服务所构建的各个应用的目标都是尽可能实现“高内聚和低耦合”–他们拥有自己的领域逻辑,并且更多的是经典的UNIX的“过滤器”那样工作–即接收请求、处理逻辑、返回响应,这些应用通过使用简单的REST风格的协议来进行编排,而不去使用复杂的协议,比如:WS、BEPL或者集中式工具进行编排。

有两种协议最经常被使用到:包含资源API的HTTP的请求-响应和轻量级消息通信协议。最为重要的建议为:

微服务团队采用这样的原则和规范:基于互联网(广义上,包含Unix系统)构建系统。这样经常使用的资源几乎不用什么的代价就可以被开发者或者运行商缓存。

第二种做法是通过轻量级消息总线来发布消息。这种的通信协议非常的单一(单一到只负责消息路由),像RabbitMQ或者ZeroMQ这样的简单的实现甚至像可靠的异步机制都没提供,以至于需要依赖产生或者消费消息的终端或者服务来处理这类问题。
 
在一个单块系统中,各个组件在同一个进程中运行。它们相互之间的通信,要么通过方法调用,要么通过函数调用来进行。将一个单块系统改造为若干微服务的最大问题,在于对通信模式的改变。仅仅将内存中的方法调用转换为RPC调用这样天真的做法,会导致微服务之间产生繁琐的通信,使得系统表现变糟。取而代之的是,需要用更粗粒度的协议来替代细粒度的服务间通信。 查看全部
原文如下:http://martinfowler.com/articl ... .html

微服务

一个新的架构术语

“微服务架构”一词是在过去几年里涌现出来的,它用于描述一种独立部署的软件应用设计方式。这种架构方式并没有非常明确的定义,但有一些共同的特点就是围绕在业务能力、自动化布署、端到端的整合以及语言和数据的分散控制上面。

“微服务”- 这是在软件架构领域这个非常拥挤的街道上,冒出的一个新名词而已。虽然我们对这个新出的名词不屑一顾,但是它所描述的软件系统的风格越来越吸引我们的注意力。在过去的几年里,我们发现越来越多的项目开始使用这个风格,并且到目前为止得到的反馈都是积极的,以至于我身边的许多同事在设计企业架构时,都把它作为默认的构建方式,然而很不幸,到底什么是微服务,我们又如何来使用它,外界并没有太多的信息可供参考。

总之,微服务这种架构风格就是把一组小服务演化成为一个单一的应用的一种方法。每个应用都运行在自己的进程中,并通过轻量级的机制保持通信,就像HTTP这样的API。这些服务要基于业务场景,并使用自动化布署工具进行独立的发布。可以有一个非常轻量级的集中式管理来协调这些服务,可以使用不同的语言来编写服务,也可以使用不同的数据存储。
 
在开始解释什么是微服务之前,先介绍一下单体应用还是很有用的:把一个单体应用构建成为一个部分。企业应用通过是由三个重要部分组成:客户端界面(由HTML、Javascript组成,使用浏览器进行访问)、数据库(由许多的表组件构成一个通用的、相互关联的数据管理系统)、服务端应用。服务端应用处理HTTP请求、执行领域逻辑、检索并更新数据库中的数据、选择和填充HTML视图发送给客户端。这个服务端应用是一个单块结构也就是一个整体,这是一个可执行的单一逻辑,系统中的任何修改都将导致服务端应用重新编译和布署一个新版本。

就这样一个单体应用很自然的被构建成了一个系统,虽然可以使用开发语言基本特性会把应用封装成类、函数、命名空间,但是业务中所有请求都要在单一的进程中处理完成,在某些场景中,你可以在开发人员的笔记本电脑中运行和测试,并且通过布署通道将测试通过的程序布署到生产环境中,你还可以水平扩展,利用负载均衡将实例布署到多台服务器中。

的确,单体应用也是很成功的,但是越来越多的人感觉到了不妥,特别是应用程序被发布到了云的时候,变更发布周期被绑定了 —- 原来可以划分成小的应用、小的需要的变更,需要统一的进行编译和发布。随着时间的推移,软件开发者很难保持原有好的模块架构,使得一个模块的变更很难不会影响到其它的模块,而且在扩展方面也只能进行整体的扩展,而不能根据进行部分的扩展。 
 

 
这些原因导致了微服务架构风格的出现:以服务构建应用。这些服务还可以被独立布署、独立扩展,每个服务也都提供了清晰的模块边界,甚至不同的服务都可以使用不同的编程语言来实现,也可以由不同的团队进行管理。

微服务的概念不是我们发明的,它至少起源于Unix时代的设计原则,我们认为这种风格所带来的好处,并没有引起足够多人的重视。

微服务架构特征

我们没有办法对微服务有一个正式的定义,但我们可以尝试表述适合这种架构的共同特征来给它打上特性标签,共同特性并不代表每个服务都具备这些特点,但是我们真的期望大多数微服务架构能具备其中大部分特点。虽然我们的作者已经是松散社区的核心成员,但是我们也在尝试描述我们工作中或者我们了解的组件中所理解的微服务。我们并不依赖于那些已经明确过的定义。
 
组件化与服务

只要我们一直在从事软件行业,我们的愿望就是,软件由很多组件组装在一起,如同物理现实世界中类似的构造方式。在过去的几十年里,我们已经看到了大部分语言平台公共库有了长足的进步。

当我们在谈论组件时,我们遇到了组件定义方面的困难,我们给定的定义是:一个组件是软件中的一个部分,可以独立的替换和升级。

微服务也会使用组件库,将一个软件组件化的主要方式就是将其分解成服务,我们定义的库是可以连接到程序并使用内存函数的的组件库,服务是进程外的组件,如Web请求服务或者远程调用来相互通信的组件。(这种定义的方式与其它面向对象程序中服务对象的概念是不一样的。)

把服务当成组件(而不是组件库)的一个原因是服务可以独立布署,如果你有一个应用是由多个库组成并且运行在一个进程中,那么任何一点的改变都会引起整个应用的重新发布,但是将这个应用拆解为多个服务,你可以期待每个服务的变更仅需要发布相应的服务就可以,当然这也不是绝对的,比如导致服务接口变更的更新就需要相应服务的变化,但是良好的架构设计是通过聚合服务边界并且按照合约实现服务演化,最大限度地减少因为改变影响其他地方。

把服务当成组件的另一个考虑是这会拥有更加清晰的接口,大多数的语言并没有一个很好的机制来定义一个明确显式的发布接口,通常只有文档和规范说明,让用户避免组件间过度紧密而导致高耦合,通过显示的远程调用机制,可以避免这种情况。
 
使用服务也有其自身的缺点,远程调用比进程内部调用更加消耗性能,而且远程的API往往是粗粒度的,用起来不是很友好,对组件的职责进行变更,也会影响到进程间的交互,那么操作起来也比较困难。

第一个可能性,我们看到每个服务是运行在独立的进程上的。注意,这只是第一个可能性。服务也可以由多个进程组成,它们是同时开发和部署的,如果一个应用进程和一个仅由该服务使用的数据库。

围绕业务能力进行组织

当我们把一个大的应用拆分成小的部分时,我们的注意力主要集中在技术层面,拆分成UI团队、服务端的逻辑团队和数据库团队。当使用这种标准对团队进行划分时,甚至一个非常小的更变都将导致跨团队间项目协作,从而消耗时间和预算审批。一个高效的团队会针对这种情况进行改善,关注它们所涉及的应用逻辑,并从中做出较好的选择。换句话说,逻辑无处不在。康威定律就是一个例子。
 
一个组织的沟通结构反映了其设计的系统的结构 -- Melvyn Conway, 1967
 
 

微服务的划分方法有所不同,它更倾向于围绕业务功能对服务结构进行划分、拆解,这些服务可以采用不同的技术栈来实现,包括用户界面,持久层存储,或任何对外协作,因此团队应该是跨职能的,包括开发所需要的全部技术:用户体验、数据库和项目管理。
 

 
按照这种方式组织的公司是 www.comparethemarket.com,跨职能团队负责建立和操作每个产品并且每个产品都被分成若干单独的服务通过消息进行通信。

大型的单体应用也可以按照业务功能进行模块化的,尽管这种例子不常见。当然,我们也会敦促一个大型团队在构建一个单体应用时按照业务线来进行划分,我们能看到主要问题在于,这种组件形式会导致很多的上下文依赖,如果这个系统跨越很多模块边界,对于一个单独团队是很难在短时间解决问题的。此外,我们发现模块化方式需要大量的规范去强制执行,而服务组件明确的划分,使得团队间的边界也变得清晰起来。

产品不是项目

大多数的开发工作是使用这样一种模型:其目的是完成可以交付的软件,软件开发完成就交给了维护团队,该项目组也就解散了。

微服务的支持者建议避免这种模型,认为一个团队应该负责产品的整个生命周期,一个很通用的概念就是Amazon’s的“you build, you run it”,它要求开发团队对软件产品的整个生命周期负责,这使得开发人员可以每天都关注产品的运行情况,而且也能够与用户保持紧密的联系,做一些必要的支持工作。

产品方式开发意味着与业务能力紧紧捆绑在一起,而不是将软件看成是一系列完成的功能,他们会关注如何让软件帮助其用户提升业务能力。
 
单体应用也可以采用上述产品的理念,但是更小粒度的服务可以更容易的创建开发者与用户之间的关系。

智能终端与弱管道

当在不同的进程之间构建各种通信结构时,我们已经看到许多产品和方法,来强调将大量的智能特性融入到通信机制本身,这种情况的一个典型例子就是“企业服务总线”(Enterprise Service Bus,ESB)。ESB产品经常包括高度智能的设施来进行消息的路由、编排、转换,并应用业务规则。

微服务社区主张采用另一种做法:智能终端和弱管道。使用微服务所构建的各个应用的目标都是尽可能实现“高内聚和低耦合”–他们拥有自己的领域逻辑,并且更多的是经典的UNIX的“过滤器”那样工作–即接收请求、处理逻辑、返回响应,这些应用通过使用简单的REST风格的协议来进行编排,而不去使用复杂的协议,比如:WS、BEPL或者集中式工具进行编排。

有两种协议最经常被使用到:包含资源API的HTTP的请求-响应和轻量级消息通信协议。最为重要的建议为:

微服务团队采用这样的原则和规范:基于互联网(广义上,包含Unix系统)构建系统。这样经常使用的资源几乎不用什么的代价就可以被开发者或者运行商缓存。

第二种做法是通过轻量级消息总线来发布消息。这种的通信协议非常的单一(单一到只负责消息路由),像RabbitMQ或者ZeroMQ这样的简单的实现甚至像可靠的异步机制都没提供,以至于需要依赖产生或者消费消息的终端或者服务来处理这类问题。
 
在一个单块系统中,各个组件在同一个进程中运行。它们相互之间的通信,要么通过方法调用,要么通过函数调用来进行。将一个单块系统改造为若干微服务的最大问题,在于对通信模式的改变。仅仅将内存中的方法调用转换为RPC调用这样天真的做法,会导致微服务之间产生繁琐的通信,使得系统表现变糟。取而代之的是,需要用更粗粒度的协议来替代细粒度的服务间通信。