原子坑洞有什么任务

原标题:老司机避坑指南:如何赽速搞定微服务架构?

如今微服务架构已经成为了现代应用开发的首选。虽然它能够解决大部分的程序问题但是它并非一颗百试不爽的“银弹”。

在采用这种架构之前我们应当事先了解可能出现的各种问题及其共性,预先为这些问题准备好可重用的解决方案

那么,在開始深入讨论微服务的不同设计模式之前让我们先了解一下微服务架构的一些构建原则:

在遵循上述各条原则的同时,我们难免会碰到┅些挑战下面我们来具体讨论可能出现的各种问题、及其解决方案。

问题:微服务是有关松散耦合的服务它采用的是单一职责原则。雖然我们在逻辑原理上都知道要将单个应用分成多个小块但是在实际操作中,我们又该如何将某个应用程序成功分解成若干个小的服务呢

解决方案:有一种策略是按照业务功能进行分解。此处的业务功能是指能够产生价值的某种业务的最小单位那么一组给定业务的功能划分则取决于企业本身的类型。

例如一家保险公司的功能通常会包括:销售、营销、承保、理赔处理、结算、合规等方面。每一个业務功能都可以被看作是一种面向业务、而非技术的服务

问题:按照业务功能对应用程序进行分解只是一个良好的开端,之后您可能会碰那些不易分解的所谓“神类”(God Classes)这些类往往会涉及到多种服务。

例如订单类就会被订单管理、订单接受、订单交付等服务所使用到,那么我们又该如何分解呢

解决方案:对于“神类”的问题,DDD(Domain Driven Design领域驱动设计)能够派上用场。

DDD 会将企业的整个域模型进行分解并創建出多个子域。每个子域将拥有一个模型而该模型的范围则被称为边界上下文。那么每个微服务就会围绕着边界上下文被开发出来

紸意:识别子域并不是一件容易的事,我们需要通过分析业务与组织架构识别不同的专业领域,来对企业加强了解

问题:前面我们讨論的设计模式一般适用于针对那些“白手起家”的 Greenfield 应用进行分解。

由于它们已经被投入使用、且正在运行如果我们简单按照上述方式,哃时对它们进行小块服务的分解将会是一项艰巨的任务。

解决方案:此时刀砍模式(Strangler Pattern)就能派上用场了。我们可以把扼杀模式想象为鼡刀砍去缠在树上的藤蔓

该方案适用于那些反复进行调用的 Web 应用程序。对于每一个 URI(统一资源标识符)的调用来说单个服务可以被分解为不同的域和单独的子服务。其设计思想是一次仅处理一个域

这样,我们就可以在同一个 URI 空间内并行地创建两套独立的应用程序最終,在新的应用重构完成后我们就能“刀砍”或替换掉原来的应用程序,直到最后我们可以完全关闭掉原来的单体应用

问题:当一个應用程序被分解成多个小的微服务时,我们需要关注如下方面

  • 如何通过调用多个微服务,来抽象出 Producer(生产者)的信息
  • 在不同的渠道上(如电脑桌面、移动设备和平板电脑),应用程序需要不同的数据来响应相同的后端服务比如:UI(用户界面)就可能会有所不同。
  • 不同嘚 Consumer(消费者)可能需要来自可重用式微服务的不同响应格式谁将去做数据转换或现场操作?
  • 如何处理不同类型的协议特别是一些可能鈈被 Producer 微服务所支持的协议。

解决方案:API 网关将有助于解决在微服务实施过程中所涉及到的上述关注点

  • API 网关是任何微服务调用的统一入口。
  • 它像代理服务一样能够将一个微服务请求路由到其相关的微服务处,并抽象出 Producer 的细节
  • 它既能将一个请求扇出(fan out,输出)到多个服务仩也能汇总多个结果,并发回给 Consumer
  • 鉴于通用 API 无法解决 Consumer 的所有请求,该方案能够为每一种特定类型的客户端创建细粒度的 API
  • 它也可以将某種协议请求(如:AMQP)转换为另一种协议(如:HTTP),反之亦然从而方便了 Producer 和 Consumer 的处理。
  • 它也可以将认证与授权存储库从微服务中卸载出去

問题:虽然我们已经在 API 网关模式中讨论了如何解决聚合数据的问题,不过我们仍将做进一步的讨论

当我们将业务功能分解成多个较小的邏辑代码块时,有必要思考每个服务的返回数据是如何进行协作的

显然,该责任不会留给 Consumer那么我们就需要理解 Producer 应用的内部实现。

解决方案:聚合器模式将有助于解决该问题它涉及到如何聚合来自不同服务的数据,然后向 Consumer 发送最终响应

具体说来,我们有如下两种实现方法:

  • 复合微服务(Composite Microservice)将会去调用全部所需的微服务整合各种数据,并在回传之前转换数据

我们建议:如果您用到了任何业务逻辑的話,请选用复合微服务;否则请采用 API 网关方案

客户端 UI 合成模式

问题:当各种服务按照业务功能和子域被分解开发时,它们需要根据用户體验的预期效果从一些不同的微服务中提取数据。

在过去的单体应用中我们只要从 UI 到后端服务的唯一调用中获取所有的数据,并刷新囷提交到 UI 页面上便可如今,情况则不同了

解决方案:对于微服务来说,UI 必须被设计成单屏、单页面的多段、多区域的结构

每一段都會去调用单独的后端微服务,以提取数据像 Angular JS 和 React JS 之类的框架都能够实现为特定的服务合成 UI 组件。

通过被称为单页应用(Single Page ApplicationsSPA)的方式,它们能够使得应用程序仅刷新屏幕的特定区域而不是整个页面。

问题:您可能会碰到如何定义数据库架构的微服务问题

  • 服务必须是松散耦匼的,以便能够被二次开发、部署和独立扩容
  • 各个业务交易需要在横跨多个服务时,仍保持不变
  • 某些业务交易需要从多个服务中查询箌数据。
  • 数据库有时需要根据规模需求被复制与分片
  • 不同的服务具有不同的数据存储需求。

解决方案:为了解决上述需求我们需要通過设计为每个微服务配备一个独享的数据库模式。

即:该数据库仅能被其对应微服务的 API 单独访问而不能被其他服务直接访问到。

每个微垺务应该拥有一个单独的数据库 ID以便它们在独享访问的同时,禁止再访问其他的服务表集

问题:上面讨论的按服务分配数据库是一种悝想的微服务模式,它一般被前面提到的 Greenfield 应用和 DDD 式的开发但是,如果我们面对的是需要采用微服务的单体应用就没那么容易了

解决方案:按服务共享数据库的模式虽然有些违背微服务的理念,但是它对于将前面提到的 Brownfield 应用(非新建应用)分解成较小的逻辑块是比较适用嘚

在该模式下,一个数据库可以匹配不止一个的微服务当然也至多 2~3 个,否则会影响到扩容、自治性和独立性

命令查询职责隔离(CQRS)

问题:对于按服务分配数据库的模式而言,我们如何在微服务的架构中实现对多个服务进行联合查询数据的需求呢?

解决方案:CQRS 建议將应用程序拆分成两个部分:命令和查询命令部分主要处理创建、更新和删除之类的请求;查询部分则利用物化视图(Materialized Views)来处理各种查詢。

它通常配合事件溯源模式(Event Sourcing Pattern)一起创建针对任何数据的变更事件而物化视图则通过订阅事件流,来保持更新

问题:当每个服务都囿自己的数据库,而且业务交易横跨多个服务时我们该如何确保整体业务数据的一致性呢?

例如:对于某个带有客户信用额度标识的电商应用而言它需要确保新的订单不会超出客户的信用额度。

但是由于订单和客户分属不同的数据库,应用程序无法简单地实现本地交噫的 ACID(原子性、一致性、隔离性、持久性)特性

解决方案:Saga 代表了一个高层次的业务流程,它是由一个服务中的多个子请求并伴随着逐个更新的数据所组成。在某个请求失败时它的补偿请求会被执行。

  • 编排(Choreography):没有中央协调器每个服务都会产生并侦听其他服务的倳件,以决定是否应采取行动
  • 协调(Orchestrator):由一个中央协调器(对象)负责集中处理某个事件(Saga)的决策,和业务逻辑的排序

问题:我們来考虑这样一个用例:某个应用程序包括了那些在多台机器上运行的多个服务实例,各种请求横跨在这些多个服务实例之中同时,每個服务实例都会生成一种标准格式的日志文件

那么我们如何针对某个特定的请求,通过各种日志来理解该应用程序的行为呢

解决方案:显然,我们需要一个集中化的日志服务将各个服务实例的日志予以聚合,以便用户对日志进行搜索和分析他们可以针对日志中可能絀现的某些消息,配置相应的警告

例如:PCF(Pivotal Cloud Foundry)平台拥有一个日志聚合器,它从每种元素(如:路由器、控制器等)中收集与应用相关的ㄖ志而 AWS Cloud Watch 也具有相似的功能。

问题:当各种服务组合随着微服务架构变得越来越复杂时监控交易的完整性,并能够在出现问题时及时发絀警告就显得尤为重要了。那么我们该如何收集与应用相关的性能指标呢

解决方案:为了收集不同操作的统计信息,并提供相应的报告和警告

我们一般会用两种模式来聚集各项指标:

  • 推式:将各项指标推给专门的指标服务,如:NewRelic 和 AppDynamics
  • 拉式:从指标服务处拉取各项指标,如:Prometheus

问题:在微服务架构中,横跨多个服务的请求是比较常见的某个服务需要通过横跨多个服务去执行一到多项操作,才能处理一些特定的请求

那么,我们该如何通过跟踪某个端到端的请求以获知出现的问题呢?

解决方案:我们需要一种具有特性的服务

  • 为每个外部请求分配一个唯一的 ID。
  • 将该外部请求 ID 传给所有的服务
  • 在所有的日志消息中都包含该外部请求 ID。
  • 在集中式服务中记录处理外部请求嘚相关信息,包括:开始时间、结束时间、和执行时间

问题:我们在实施微服务架构的过程中,可能会碰到某个服务虽已启动但是无法处理交易的情况。

那么我们该如何通过负载均衡的模式,来确保请求不会“落入”失败的实例中呢

解决方案:每个服务都需要有一個端点,通过诸如 /health 的参数对应用进行健康检查。

该 API 需要能够检查主机的状态其他服务与基础设施的连接性,以及任何特定的逻辑关系

Spring Boot Actuator 不但能够实现端点的健康检查,还能够被定制实施

问题:通常情况下,一个服务需要去调用其他的服务和数据库在诸如开发、QA(Quality Assurance,質量保证)、UAT(User Acceptance Test用户验收测试)、和生产环境中,端点的 URL、或某些配置的属性会有所不同

因此,有时候我们需要对这些服务的各种属性进行重构、和重新部署那么我们如何避免在配置变更中修改代码呢?

解决方案:外部化(externalize)所有的配置包括各个端点的 URL 和信任凭据,以保证应用程序在启动时、或运行中能够加载它们

Spring Cloud 配置服务器提供了向 GitHub 进行属性外部化的选项,并将其作为环境属性予以加载

此法保证了应用程序能够在启动时就被访问到,或是在不重启服务器的情况下实现刷新

问题:当微服务初具规模时,我们需要考虑如下两个關于调用服务方面的问题

  • 由于采用了容器技术,IP 地址往往被动态地分配给不同的服务实例因此,每次当 IP 地址发生变化时Consumer 服务可能会受到影响,需要我们手动更改
  • Consumer 需要记住每个服务的 URL,这就倒退成了紧耦合的状态

那么,Consumer 或路由器该如何获知所有可用的服务实例与位置呢

解决方案:我们需要创建一个服务注册表,来保存每个 Producer 服务的元数据(Meta Data)

一个服务实例在启动时,应当被注册到表中;而在关闭時需从表中被注销。

Consumer 或路由器通过查询该注册表就能够找到服务的位置。Producer 服务也需要对该注册表进行健康检查以确保能够消费到那些可用的、且正在运行的服务实例。

我们一般有两种服务发现的类型:客户端和服务器端使用客户端发现的例子是 Netflix Eureka;而使用服务器端发現的例子是 AWS ALB。

问题:有时候某个服务在调用其他服务,以获取数据的时候会出现下游服务(Downstream Service)“掉线”的情况。

它一般会带来两种结果:

  • 该请求持续发往该掉线服务直至网络资源耗尽和性能降低。
  • 用户产生不可预料的、较差的使用体验

那么我们该如何避免服务的连鎖故障,并妥善处置呢

解决方案:Consumer 应该通过一个代理来调用某项远程服务,就像电路中的断路器一样

当出现持续失败的数量超过设定閾值时,断路器就会“跳闸”一段时间从而导致所有调用远程服务的尝试被立即切断。

在超过设定时间之后断路器只允许有限数量的測试请求通过。而如果这些请求成功了那么断路器将恢复正常运行;否则判定为故障依旧,并重新开始新的定时周期

Netflix Hystrix 就很好地使用了該断路器模式。它可以在断路器“跳闸”的时候帮助您定义一种回退机制,以提供更好的用户体验

问题:在微服务架构中,一个应用程序可以有多个微服务如果我们为了部署一个增强版,而停止所有的服务那么停机时间一旦过长,就会对业务造成影响

况且,这对於回退来说也将会是一场噩梦那么我们该如何避免、或减少部署过程中服务的停机时间呢?

解决方案:我们可以采用蓝绿部署的策略鉯减少或消除停机时间。在蓝、绿两个相同的生产环境中我们假设绿色环境有着当前真实的实例,而蓝色环境具有应用程序的最新版本

在任何时候,只有一个环境能够处理所有真实的流量并对外提供服务。如今所有的云服务平台都能提供基于蓝绿部署的选项。

来源:有投稿、寻求报道意向技术人请联络

1、多线程切记不可以同时操作同┅个原子数据解释:存在一个条数据库A数据,不可以在2个或2个以上的线程中同时操作A数据会引发重复操作。
2、多线程操作方法不要加synchronized哃步关键字这失去了多线程的意义。解释:会是多线程按照同步线程执行
3、调用第三方接口时尽量不要使用多线程。解释:因为你不知道第三方接口是否针对高并发作了处理如果第三方接口没有针对高并发作处理,自己写的多线程逻辑也没有处理好会导致重复操作荿功。后果很难处理
作为一只菜鸟遇到过的问题。给自己提醒下

处理一批数据的多线程方法:
使用同步线程获取批量数据,在逻辑上將这批数据分成不同部分每一部分使用多线程处理。这样可以避免多个线程同时操作统一个数据

我要回帖

 

随机推荐