MicroServices

Forces

  • There is a team of developers working on the application
  • New team members must quickly become productive
  • The application must be easy to understand and modify
  • You want to practice continuous deployment of the application
  • You must run multiple copies of the application on multiple machines in order to satisfy scalability and availability requirements
  • You want to take advantage of emerging technologies (frameworks, programming languages, etc)

Resulting context

This solution has a number of benefits:

  • Simple to develop - the goal of current development tools and IDEs is to support the development of monolithic applications
  • Simple to deploy - you simply need to deploy the WAR file (or directory hierarchy) on the appropriate runtime
  • Simple to scale - you can scale the application by running multiple copies of the application behind a load balancer

However, once the application becomes large and the team grows in size, this approach has a number of drawbacks that become increasingly significant:

  • The large monolithic code base intimidates developers, especially ones who are new to the team. The application can be difficult to understand and modify. As a result, development typically slows down. Also, because there are not hard module boundaries, modularity breaks down over time. Moreover, because it can be difficult to understand how to correctly implement a change the quality of the code declines over time. It‘s a downwards spiral.
  • Overloaded IDE - the larger the code base the slower the IDE and the less productive developers are.
  • Overloaded web container - the larger the application the longer it takes to start up. This had have a huge impact on developer productivity because of time wasted waiting for the container to start. It also impacts deployment too.
  • Continuous deployment is difficult - a large monolithic application is also an obstacle to frequent deployments. In order to update one component you have to redeploy the entire application. This will interrupt background tasks (e.g. Quartz jobs in a Java application), regardless of whether they are impacted by the change, and possibly cause problems. There is also the chance that components that haven’t been updated will fail to start correctly. As a result, the risk associated with redeployment increases, which discourages frequent updates. This is especially a problem for user interface developers, since they usually need to iterative rapidly and redeploy frequently.
  • Scaling the application can be difficult - a monolithic architecture is that it can only scale in one dimension. On the one hand, it can scale with an increasing transaction volume by running more copies of the application. Some clouds can even adjust the number of instances dynamically based on load. But on the other hand, this architecture can’t scale with an increasing data volume. Each copy of application instance will access all of the data, which makes caching less effective and increases memory consumption and I/O traffic. Also, different application components have different resource requirements - one might be CPU intensive while another might memory intensive. With a monolithic architecture we cannot scale each component independently
  • Obstacle to scaling development - A monolithic application is also an obstacle to scaling development. Once the application gets to a certain size its useful to divide up the engineering organization into teams that focus on specific functional areas. For example, we might want to have the UI team, accounting team, inventory team, etc. The trouble with a monolithic application is that it prevents the teams from working independently. The teams must coordinate their development efforts and redeployments. It is much more difficult for a team to make a change and update production.
  • Requires a long-term commitment to a technology stack - a monolithic architecture forces you to be married to the technology stack (and in some cases, to a particular version of that technology) you chose at the start of development . With a monolithic application, can be difficult to incrementally adopt a newer technology. For example, let’s imagine that you chose the JVM. You have some language choices since as well as Java you can use other JVM languages that inter-operate nicely with Java such as Groovy and Scala. But components written in non-JVM languages do not have a place within your monolithic architecture. Also, if your application uses a platform framework that subsequently becomes obsolete then it can be challenging to incrementally migrate the application to a newer and better framework. It’s possible that in order to adopt a newer platform framework you have to rewrite the entire application, which is a risky undertaking.

微服务架构的收益与缺点

这种架构能够带来很多的收益。首先,每个微服务相对比较小。对于开发人员来说,代码更易于理解。很小的代码基并不会拖慢IDE,因此能使开发人员更为高效。同时,每个服务在启动方面也比大型的整体应用快得多,这同样会使得开发人员更为高效,并且会加速部署。

第二,每个服务可以独立于其他服务进行部署。如果负责某个服务的开发人员需要部署一个变更,这个变更仅仅涉及到这个服务本地的内容,那么他们没有必要协调其他的开发人员。他们可以只部署自己的变更就可以了。微服务架构使得持续部署变得可行。

第三,每个服务可以独立于其他服务使用X轴克隆和Z轴分区进行扩展。此外,每个服务可以部署到最适合其资源需求的硬件上。这与使用整体架构时有很大的区别,在整体架构中,具有严重资源需求差异的组件(如CPU密集与内存密集)必须部署在一起。

微服务架构使得开发的扩展也变得更为容易。你可以围绕多个小团队(如两个披萨原则)组织研发。每个团队只需负责开发和部署一个服务或一组相关的服务。每个团队能够独立于其他的团队开发、部署和扩展他们的服务。

微服务架构同时也能提升故障的隔离性。例如,某个服务中的内存泄露只会影响到这个服务自己。其他服务能够继续正常地处理请求。与之形成对比的是,整体架构中如果有一个不合格的组件,那么将会拖垮整个系统。

最后但并非最不重要的,微服务架构消除了对某个技术栈的长期依赖。原则上,当开发新的服务时,开发人员可以任意选择最适合该服务的语言和框架。当然,在很多组织中,限制可选的技术方案是有一定的道理的,不过关键的一点在于,你不再受制于过去所作出的决策。

另外,因为服务比较小,使用更好的语言和技术重写它们变得更为可行。这也意味着,如果对一项新技术的尝试失败的话,你尽可以抛弃这个成果,而不会对整个项目带来风险。这与采用整体架构有很大的区别,在整体架构中,初始的技术选择严重限制你将来采用不同语言和框架的能力。

缺点

当然,没有什么技术是银弹,微服务架构有一些明显的缺点和问题。首先,开发人员必须要处理创建分布式系统所带来的额外的复杂性。开发人员必须要实现进程间的通信机制。要实现跨多个服务的用例的话,如果不使用分布式事务是很难完成的。IDE以及其他的开发工具关注于开发整体架构的应用,并没有为开发分布式应用提供明确的支持。编写涉及到多个服务的自动化测试用例也是很有挑战的。在开发整体架构应用时,你无需处理这样的问题。

微服务架构同样引入了明显的运维复杂性。在这种架构下,有了更多正在运行中的不同部件,也就是不同类型服务的多个实例,在生产环境下必须对它们进行管理。为了成功做到这一点,你需要一个高度自动化的工具,要么是自己编写代码,要么是类似于PaaS的技术,如Netflix Asgard及其关联组件,或者是现成的PaaS,如Pivotal Cloud Foundry。

同时,部署跨多个服务的特性时,需要认真地协调不同的开发团队。根据服务间的依赖关系,你要创建服务如何按顺序部署的规划。这与整体架构有着很大的差异,在整体架构模式下,你可以很容易一次性地更新多个组件。

使用微服务架构的另外一个挑战就是要决定在应用生命周期的哪个时间点上采用这种架构。当开发应用的第一个版本时,这种架构所能解决的问题你通常还不会遇到。另外,使用复杂的、分布式的架构将会延缓开发。

对于初创公司来讲,这可能是一个两难的困境,它们最大的挑战通常在于如何快速地演化业务模型以及相关的应用。使用Y轴分解可能会使得快速迭代更为困难。稍后,当挑战变成如何扩展的话,你需要进行功能分解,此时混乱的依赖关系可能让你很难将整体的应用分解为一组服务。

鉴于这些问题的存在,采用微服务架构不应该是草率做出的决策。但是,对于需要进行扩展的应用,如面向客户的Web应用或SaaS应用,这通常是正确的选择。知名的站点如eBay[PDF]、Amazon.comGroupon以及Gilt都已经从整体架构转移到微服务架构上来了。

既然已经看过了其收益和缺点,现在我们来看一下在微服务架构中几个关键的设计问题,首先是应用内部以及应用与客户端之间的通信机制。

微服务架构中的通信机制

在微服务架构中,客户端和应用之间以及应用的组件之间的通信机制与整体架构是有所不同的。我们首先看一下应用的客户端与微服务交互的问题,稍后,会看到应用内部的通信机制。

API网关模式

在一个整体架构的应用中,应用的客户端,如Web浏览器和原生的应用,会发起HTTP请求,负载均衡器将请求转发到N个完全相同的应用实例中的某一个上面。但是在微服务架构之中,庞大的应用被替换为一组服务。所造成的结果就是,我们需要回答客户端到底与谁交互的问题。

应用的客户端,如原生的移动应用,可以对单个服务发起RESTful HTTP请求,如图4所示。

图4——直接调用服务

表面上看,这种方式可能会很有吸引力。但是,在粒度方面,单个服务的API与客户端所需要的数据之间可能会存在明显的不匹配。例如,展现一个Web页面可能需要调用大量的服务。举例来说,Amazon.com描述了一些页面需要调用100个以上的服务。发起如此之多的服务,即使是在高速互联网连接之中也会导致低效和很差的用户体验,更别提更低带宽、更高延迟的移动网络了。

一种更好的方式是让客户端在每个页面发送少量的请求,可能只有一个,这样的请求会通过互联网发送到前端服务器上,这种服务器称之为API网关,如图5所示。

图5——API网关

API网关位于应用的客户端和微服务之间。它提供了针对客户端定制的API。API网关为移动客户端提供粗粒度的API,为桌面客户端提供细粒度的API,因为桌面客户端能够使用高性能的网络。在本例中,桌面客户端发起多次请求来检索关于某个产品的信息,而移动客户端只发送一次请求。

API网关在处理传入的请求时,会通过高性能的LAN对一定数量的微服务发起请求。例如,Netflix描述了如何将每个请求发散为平均6个后端服务。在本例中,来自于桌面客户端的细粒度请求只是简单代理到对应的服务上,而来自移动客户端的每个粗粒度请求得到了处理,在这里会将调用多个服务的结果进行聚合。

API网关不仅优化了客户端和应用之间的通信,而且还封装了微服务的细节。这样的话,就能使微服务的演化不至于影响到客户端。例如,两个微服务可能会进行合并。另外的某一个微服务则可能会拆分为两个或更多的服务。只有API网关需要更新以适应这些变化,客户端并不会受到影响。

我们已经看到了API网关如何在应用及其客户端之间进行协调,现在来看一下如何在微服务之间实现通信。

服务间通信机制

微服务架构的另外一个重要差异就是应用的不用组件之间如何进行交互。在整体架构的应用之中,组件调用其他的组件会使用常规的方法调用。但是在微服务架构中,不同的服务运行在不同的进程之中。由此导致的结果就是服务必须使用进程间通信机制(inter-process communication,IPC)来实现通信功能。

同步HTTP

在微服务架构中,有两种主要的方式来实现进程间的通信。一种可选的方案是基于HTTP的同步机制,如REST或SOAP。这是一种简单且熟悉的IPC机制。它对防火墙友好,所以在互联网上广泛使用并且请求/响应(request/reply)式的通信较为简单。HTTP的不足之处在于它不支持其他的通信模式,如发布-订阅(publish-subscribe)。

另外一个限制就是客户端和服务器必须同时可用,但是这并不是永远都能保证的,因为分布式系统容易产生局部的故障。同时,HTTP客户端需要知道服务器的主机和端口。尽管听起来这没什么问题,但实际上并没有那么简单,尤其是在云部署的时候,会使用自动扩展的机制,服务实例的生命周期是很短暂的。应用需要使用一种服务发现机制。有些应用使用服务注册器,如Apache ZooKeeper或Netflix Eureka。在其他的应用中,服务必须注册到负载均衡器中,如在一个Amazon VPC内部使用的ELB。

异步消息

同步HTTP的一个替代方案就是基于消息的异步机制,如基于AMQP的消息broker。这种方式有一些优点。它解耦了消息的生产者和消费者。消息broker会一直缓存消息,直到消费者能够处理它们为止。生产者完全不用关心消费者。生产者只需与消息broker交互就可以,无需使用服务发现机制。基于消息的通信机制同时支持多种通信模式,包括单向请求以及发布-订阅。使用消息的一个不足之处在于需要有消息broker,这个运转部件会为系统添加复杂性。另外的一个不足之处在于它与请求/响应式的通信机制并不能自然融合。

两种方式各有利弊。应用通常会混合使用这两种方式,例如,在下一节中将会讨论如何解决架构拆分所带来的数据管理问题,你就会看到怎样使用HTTP与消息了。

去中心化的数据管理

将应用拆分为服务的一个结果就是数据库也会进行拆分。为了确保低耦合,每个服务有自己的数据库(模式)。另外,不同的服务可能使用不同类型的数据库——这就是所谓的多种类持久化架构(polyglot persistence architecture)。例如,需要ACID事务的某个服务可能会使用关系型数据库,而管理社交网络的服务可能会使用图数据库。拆分数据库是很必要的,但是我们现在有了一个新的问题需要解决:有些请求要访问多个服务的数据,这样的请求该如何处理。让我们先看一下如何处理读请求,然后再看一下更新请求。

处理读

例如,考虑在线商店中的每个顾客都有一个信用额度。当顾客试图下订单时,系统必须检查所有订单的总额不超过他们的信用额度。在整体架构的应用中实现这种逻辑规则会很简单。但是如果在系统中,顾客要通过CustomerService来管理,订单要通过OrderService来管理,那么实现这种检查就困难多了。OrderService必须要以某种方式访问CustomerService所维护的信用额度。

一种解决方案是OrderService通过发起对CustomerService的RPC调用检索到信用额度。这种方式实现起来很简单,并且能够确保OrderService能够得到最新的信用额度值。不足之处在于它降低了可用性,因为为了下一个订单,必须要保证CustomerService处于正在运行的状态。它也会因为额外的RPC调用增加响应的时间。

另外一种方式就是OrderService保存信用额度的一个副本。这消除了对CustomerService所发起的请求,所以能够增加可用性并降低响应时间。但是,它意味着我们必须实现一种机制,每当CustomerService服务中的信用额度更新时,同时要更新OrderService中的副本。

处理更新请求

确保OrderService中的信用额度保持最新只是一类更通用问题的特例,这类问题就是如何处理多个服务共有数据的更新请求。

分布式事务

当然,一种解决方案就是使用分布式事务。例如,当更新顾客的信用额度时,CustomerService可以使用分布式事务同时更新自身的信用额度以及由OrderService维护的对应的信用额度。使用分布式事务能够确保数据始终是一致的,不足之处在于使用它会降低系统的可用性,因为为了事务能够提交,必须要求所有的参与者均可用。另外,分布式事务确实有些不再受欢迎了,现代的软件栈通常都不会支持它,如REST、NoSQL数据库等等。

事件驱动的异步更新

另外一种方式是使用事件驱动的异步复制(asynchronous replication)。服务发布事件声明有些数据发生了变化,其他的服务订阅这些事件并更新它们的数据。例如,当CustomerService更新顾客的信用额度时,它发布一个CustomerCreditLimitUpdatedEvent,这个事件中包含了顾客的id以及新的信用额度。OrderService订阅这些事件并更新其信用额度的副本。事件的流程如图6所示。

图6——使用事件复制信用额度

这种方式主要的优点在于事件的生产者和消费者实现了解耦。这不仅简化了开发,相对于分布式事务,它还提升了可用性。如果某个消费者不能处理事件的话,消息broker会将消息放到队列中,直至消费者处理为止。这种方式的主要缺点在于它为了可用性牺牲了一致性。应用的编写方式必须能够容忍最终一致性数据。开发人员可能也需要实现事务补偿来执行逻辑回滚。尽管存在这些缺点,对于很多应用来说,这是推荐的方式。

重构整体架构的庞然大物

令人遗憾的是,我们通常并没有这样的好运气从事一个全新的、尚未进行开发的项目。很可能你所在的团队负责一个庞大的、令人生畏的整体架构应用。每天你都在处理本文开头时所提到的那些问题。好消息是你可以使用一些技术将庞大的应用拆分为一组服务。

首先,停止让问题变得更糟糕。不要继续在这个庞然大物上添加代码来实现重要的新功能。与之相反,你应该以某种方式将功能实现为独立的服务,如图7所示。这可能并不容易做到,你需要编写凌乱复杂的胶水代码来实现服务与庞大应用的集成。但是,对于拆分整体架构的应用来说,这是很好的第一步。

图7——抽取服务

第二,识别整体应用中的一个组件,然后将其转换为内聚且独立的服务。要进行抽取时,比较好的可选组件包括那些经常变化的或具备不一致资源需求的组件,如需要大量内存缓存或CPU密集型操作。展现层也是一个很好的可选项。你将组件转换为服务,并编写胶水代码使其能够与应用的其他部分进行交互。再次强调一遍,这可能会比较痛苦,但是能够让你渐进式地迁移到微服务架构。

总结

在构建企业级应用时,整体架构模式是一种常用的模式。对小型应用来说,它的确运行的很不错:开发、测试以及部署小的整体架构应用相对很简单。但是,对于大型的复杂应用,整体架构就成为开发和部署的障碍了。很难做到持续交付,并且通常会长期局限于最初所作出的技术选择。对于大型的应用,使用微服务架构会更好,这种架构会将应用拆分为一组服务。

微服务架构有很多的优势。例如,单个服务更易于理解,可以独立于其他服务进行开发和部署,更易于使用新的语言和技术,因为你可以每次在服务中尝试新的技术。微服务架构也有一些明显的不足。尤其是,应用要复杂得多,会有更多的运行时组件。你需要高度的自动化,如PaaS,才能有效地使用微服务。在开发微服务时,你还要处理一些复杂的分布式数据管理问题。尽管有这些不足,但是对于快速演化的大型复杂应用,尤其是SaaS风格的应用来讲,微服务架构是很有意义的。

要将已有的整体架构应用演进为微服务架构,可以采用多种策略。开发人员应该将新功能实现为单独的服务,并编写胶水代码将服务与庞大的应用进行集成。另外一种很好的办法就是迭代式地识别组件并将其从整体架构中抽取出来,然后将其转换为服务。尽管演进过程并不简单,但是总比开发和维护笨重的整体架构应用要强得多。

关于作者

Chris Richardson是一名开发人员和架构师。他是Java专家以及JavaOne上的明星,以及POJOs in Action一书的作者,这本书描述了如何使用POJO和Spring、Hibernate这样的框架编写企业级应用。Chris也是Cloud Foundry最初的创立者,这是针对Amazon EC2的早期Java PaaS。他为组织提供咨询服务,促使他们采用云计算、微服务以及NoSQL这样的技术提升开发和部署应用的水平。他的Twitter @crichardson

参考 :

http://microservices.io

http://www.infoq.com/cn/articles/microservices-intro

时间: 2024-08-13 15:36:46

MicroServices的相关文章

Introduction to Microservices

原文出自:http://nginx.com/blog/introduction-to-microservices .我在其中加入了简单的翻译和自己的一些看法.  This is a guest post by Chris Richardson. Chris is the founder of the original CloudFoundry.com, an early Java PaaS (Platform-as-a-Service) for Amazon EC2. He now consul

【微服务那些事】Microservices场景下的持续部署

近两年作者在海外交付中参与 microservices 下的团队,为客户提升 Finance 系统的扩展性.作者所在团队,3 对开发(pair programming, 2 个 dev 为 pair)为客户支撑着 11 个 services,持续部署流水线(CD pipeline)是其中必不可少的一个技术实践.本次分享作者将从实践的角度分享 microservices 架构下的持续部署(CD). 内容概述 1. microservice 概述:简要介绍 microservice 架构下的挑战 2

Netflix分享构建Microservices的经验

Netflix分享构建Microservices的经验 http://t.cn/Rwjgutw 新词:Microservices.我觉得就是service oriented architecture的另一种表述,就是要构建一堆小的功能单一的stateless的immutable的service. 觉得和以前推荐的这篇[The 12 factor app]( http://t.cn/RZoWEku )有点大同小异#湾区日报#

Building microservices with ASP.NET Core (without MVC)(转)

There are several reasons why it makes sense to build super-lightweight HTTP services (or, despite all the baggage the word brings, “microservices”). I do not need to go into all the operational or architectural benefits of such approach to system de

Introducing the Microservices Reference Architecture from NGINX

Introducing the Microservices Reference Architecture from NGINX https://www.nginx.com/blog/introducing-the-nginx-microservices-reference-architecture/

Windows Server 2016 Modern Application Platform Features -1: Microservices

Microsoft has made a focused shift to ensuring that customers today, whether they are in the public or private cloud, have a solid foundation for their application portfolio. There are two new technologies introduced in Microsoft Windows Server 2016:

【总结】AWS 云计算环境中的Microservices(微服务)架构

下载地址:下载完整MP4文件 1.邱洋总结 微服务不是石头缝里面蹦出来的,是基于类似SOA.Blackboard.C/S等应用架构基础上,并融合敏捷开发.DevOps等理念的基础上发展而来 微服务相比传统应用优点明显(快速部署.去中心.良好的隔离性等),缺点也不少(更复杂.通信损耗.测试成本高) 微服务不仅仅是一种新型的应用设计方法,使用这种架构的企业的组织架构可能也需要作出适当调整 AWS针对微服务设计了ECS(基于EC2的容器服务).Lambda(基于事件驱动的计算平台,开发人员只要编写ja

Securing Spring Cloud Microservices With OAuth2

From Zero to OAuth2 in Spring cloud Today I am presenting hours of research about a (apparently) simple question: “How can I maintain security in my microservices architecture?”. The task is to enable a simple but mighty possibility to secure spring

Microservices Reference Architecture - with Spring Boot, Spring Cloud and Netflix OSS--转

原文地址:https://www.linkedin.com/pulse/microservices-reference-architecture-spring-boot-cloud-anil-allewar What is "Microservices"? Microservices is the "new kid" on the block; a new paradigm that seeks to replace monolithic enterprise ap