唯品会、滴滴、沪江架构师,关于微服务粒度、高可用、持续交互的实践分享交流(下)

架构师小组交流会:每期选择一个时下最热门的技术话题进行实践经验分享。

本期小组交流会邀请到了沪江黄凯、唯品会郑明华、滴滴赵伟、七牛云肖勤,对微服务粒度、高可用、持续交互展开了交流。

本期接着上期唯品会、滴滴、沪江架构师,关于微服务粒度、高可用、持续交互的实践分享交流(上)进行了交流。

第一轮:话题交流

滴滴赵伟:在整个服务,从单体服务到微服务的演进过程当中,如何去影响业务的这种正常发展?

唯品会郑明华:从单体服务到微服务的改造,有两种方式,一种是小打小闹,每次稍微改一点,这个时间会非常长,有时候会发现改不动了,改的成本非常高。我举个例子,以前我们做订单改造的时候,后来我们怎么做呢?就是一个个来,先把物流分出去,因为物流是相对独立的。先把物流的订单拆除,怎么做呢?让物流把他的业务和数据先存到,建套模型、物流的上层业务会有另外东西就是做双写。写的时候写两边,老的订单系统写,新的订单系统也写,两个都写。双写带来的复杂度还是挺高的。

滴滴赵伟:对,你的数据的一致性,一边写成功一边没写成功,这个确实是很麻烦的。

唯品会郑明华:我们读的时候会有校验。写的时候是双写,但读起来会有校验,就是把新老两个地方读,然后校验。如果校验对两个数据都可以,但是如果发现不对,以老的数据为主,并且告警。

滴滴赵伟:你怎么去判断哪个是老的数据?比如我的隐私的操作,我下了个订单,下完订单,新系统和老系统。

唯品会郑明华:写的时候并没有对比,但是读的时候会有对比。我举个例子,回顾一下刚才讲的那个同学,写的时候的确是写了两套不同的数据库。我们对比有两种方式,一种是我们有个 bcp 系统,两个数据都会归结到另外一个地方帮你做检查,这种方式我们很少做,所有的机器平台以外,物流系统是不做的。比如说客户在读订单,他在打订单的时候会把这两个订单都读出来,读出来接口会帮他做对比,这两个订单是否一致,如果不一致,以老接口读出来的数据为主。新的接口数据告警,要么人工参与,看问题是出现在哪里。

滴滴赵伟:现在运行的效果怎么样?



唯品会郑明华:这是一种传统的做法,还是可以的。我在前公司的时候基本上是按照刚才我讲的方法解决了,而且我们该拆的都拆了很多,物流、金融、外汇、退税、订单,基本都是按照这种模式一个个拆的。但是你说不影响业务根本是不可能的,其中人力参与对订单的拆分,对业务系统的拆分,本身参与的人力都会影响到业务系统的。还有另外一种比较干脆的方式,就是做另外一个系统。整个系统一旦上线以后,新的业务能在两个系统实现,直到有一天,我把老系统卸掉。

滴滴赵伟:那你牵扯到数据迁移吧?

唯品会郑明华:无论哪种方式都会涉及到数据的迁移,一旦数据模型变化以后,都会涉及到数据的清洗迁移。如果新老两个数据模型不能兼容,数据没法迁移,数据的迁移会带来数据损失。

滴滴赵伟:那你在迁移当中你需要停服务吗?

唯品会郑明华:基本上不需要停服务,目前为止,我在阿里的时候,不会停止服务。因为新的业务都已经在设计,两边的数据都有,我把老的数据迁回来就可以了,不会影响到我真正的业务。

滴滴赵伟:我们最早就是按微服务的架构去设计的,并没有经历从单体服务到微服务的演变过程,所以我很好奇的一点,在做单体到微服务,整个过程当中你觉得有哪些点是你要去关注的,或者是那个地方有这种挑战性,或者那些点是必须去考虑的,或者那些是要去做好的?

唯品会郑明华:把一个大的系统拆成了多个服务,很多个服务到了数据库以后,这个分布式的问题怎么解决?这的确有考虑的问题,包括消息中间件,多个服务,每个服务我通过消息的方式来做通讯的时候,那消息的稳定性、顺序性怎么去解决,这就是从技术角度考虑的。

然后我怎么拆服务,还是从业务角度来拆。我希望每个业务是自包含的,可独立部署的。比如订单的拆单和订单的寻仓,这是两个不同的服务,而这两个服务应该完全解耦关系,按供应商或者商户拆单,做完拆单以后,你才能把订单分发到不同仓库里面去,这是两个先后的动作。这两个动作不应该纠缠在一起,这两个动作对应的是不同服务,应该是完全隔离开的,而且他们之间的数据库也是隔离开的。所以如果从业务角度考虑,只要业务了解深熟以后,对服务的拆分不是很大难题。

七牛肖勤:服务的分拆肯定会让结构更加复杂,但微服务在理念描述上已经意识到,从服务架构着眼,设计上考虑了部署的问题,运营在架构中的优先级也是排在第一位的,而以往在设计模式、软件架构基本不会考虑到部署、运营的问题。所以,如果要支持微服务架构,必须有一套行之有效的运营、部署的工具和方式,这也是容器相关技术和容器云现在备受关注的一个原因。

依赖的问题,包括一部分的复杂性问题,取决于拆分时候边界和接口的定义、数据联通的方式,设计得是不是足够合理,服务提供者是不是清楚需求的方式,服务调用者是不是理解接口的意图,也就是说团队针对每个服务的沟通,对事情的定位,对接口的抽象,是不是有一个同样的认知水平,达成一个共识。只要保证接口稳定、合理,实现不管怎么变化,对整合架构就不会有负面影响。服务的局部修改反而可以更快速,因为不会涉及一个大系统的调整。

所以说,不能为了拆分而拆分,拆分的意图要准确描述问题的解决。在一个系统里面,定义接口比怎么实现更重要,不要设计不好理解、不合理的接口。

滴滴赵伟:因为我也在考虑如果让我去做单体服务到微服务,我觉得监控告警必须先行,然后这种服务的降级之类的,可能要去考虑到位的。

唯品会郑明华:你说的是大的问题。如果没有很好的监控对大的互联网来说是个灾难。

滴滴赵伟:据我所知从这种单体服务到微服务的这种转变过程当中,可能像他们这种东西都是缺失的,除了这些还包括人员能力的问题。他以前做单体服务,你让他突然转成这种微服务它可能有种不适应,包括他定位问题的这种方式转化都有些发生变化了。

唯品会郑明华:无论在阿里还是唯品会都是有比较完善的监控。包括一旦用了我们服务框架以后,从外部调用到我们后台服务,到数据层的调用。每个调动服务,包括操作都有很详细的监控,包括每一个环节的耗时,它都能帮你监控出来。举个淘宝的例子,淘宝有个鹰眼系统,那个鹰眼系统就能检测到所有的电容链路。包括每个链路的后期说话。所以淘宝也有详细的告警系统。包括流控、限流、降级,这些都有详细的方案。比如服务怎么去注册,怎么去发现,机器出现故障以后,它怎么去告警,怎么从你的服务器中去拆掉,卸下来,这些都是你要做微服务的基础措施吧。没有这东西你靠员工去做,如果几百台机器,几百个服务,还能应付。就几万个服务,几万个机器,可能就搞不下去。

所以这种基础措施应该是搞大型互联网系统的必备的。没有这东西我觉得搞互联网简直吃不好,睡不着,你都不知道那个地方出了点小毛病。如果有很好的监控设施,我们就能及时发现问题。小毛病也有可能后面藏着重大隐患。

我曾经碰到一个事故就是我的硬盘满了,因为所有应用日志都存进去了,当时没有监控硬盘的利用率,结果线上服务异常。如果有监控的话,包括 CPU、IP,包括硬盘、监控系统、降级限流,还有另外一种是系统压测,都是我认为大型互联网系统必备的手段方法。



主持人:单体服务到微服务这种重构拆封的会遇到什么样的难点?或者怎么样去解决?

唯品会郑明华:刚才都讨论了,像服务框架、架构框架、基础设施,是要解决的问题,没有这些储备,服务或者微服务根本就是实现不了。

滴滴赵伟:另外的一个的话,我觉得微服务的话整个成本会很高。微服务的成本,比如机器,运维人员的能力成本,对吧?这都要提升的。

唯品会郑明华:包括这个测试定位都是成本。

滴滴赵伟:对。都是成本,整个成本都会上升的,小公司的话他可能这个在这方面投入应该不会大的,因为没钱了。

唯品会郑明华:所以服务后微服务是要看团队的能力,要看公司。



主持人:API 网关是怎么设计的?

七牛肖勤:在微服务架构中,每一个微服务都可以做为服务的提供点,客户端可以直接调用。当要同时调用多个微服务时,客户端则需要逐一发送多个独立的请求,这样效率会很低效。通过设计 API 网关做为系统唯一的接入点,客户端所有请求都先经过 API 网关,然后再由它将请求路由到合适的微服务。这样客户端只需要同 API 网关交互,而不必调用特定的服务,而且 API 网关可以调用多个微服务来处理同一个请求,这样简化了客户端与服务之间交互的次数,同时简化了客户端的代码。我们的 API 网关目前具备流量转发,服务发现,流量控制,服务降级,权限控制,容错及监控的功能。

流量转发具有负载均衡的功能,采用的是轮询的方式,服务发现则是基于 Consul 做的。用户请求进来后通过 Consul 查询到所有可用节点的访问地址,再通过轮询的方式将请求发给后端的服务进行处理,对于返回的结果仅作转发,由请求方解释和使用。并且在 API 网关中带有监控的组件,对请求数,失败数等进行监控,传送到 Prometheus 服务器上。通过监控数据对请求进行流量控制及服务降级等相应的处理。

滴滴赵伟:我们的 API 网关,主要分两部分。一部分是承接外部的请求,即所谓的 API 网关。另外一部分是对 API 进行生命周期后台管理服务。这两套东西全部都是以外部方式提供服务的。我们当时使用网关也就解决几个问题。

第一是解耦,对于前端跟后端,内部服务不要在公网直接暴露,通过网关可除去,外部服务通过 HTTP 请求过来,外部也不需要依赖 Client 的这种东西。然后后端的话也是可以去持续开发,降低外部调用成本。然后通过这种调用的,也通过 Json 的这种格式。网关对 Json 进行入参,然后到后面把 Json 转成对象,然后出参数才能对象转 Json 这种屏蔽这种复杂性。通过网关,我们也会去做一些这种鉴权、流控、降级、监控这方面的。通过网关可以把整个口就收拢掉了。然后管理后台,主要是对 API 的生命周期的管理,包括鉴权,还有 API 这种后端这种服务之间的映射,这种关系之类。我们就通过在管理后台,一旦改完之后,所以互联网关不是单台机器,然后所有的网关都要同时要生效之类的这种,也通过 MQ 这种去广播这种消息。

沪江黄凯:那这个 API 网管是不是相当于一个单点?这样会导致所有的流量全部通过网关转送到下一个服务中去。网关一旦出现什么事情,或者是它的效率很慢,会导致整个的业务流程都慢下来了。

滴滴赵伟:我们首先看整个业务量吧,对代驾来说,它业务量其实并没有那么高。因为它不是很高频。我们虽然是单点,但是我们机器不是单点的,有多台机器的。一般要出问题的话,网关不太会出问题。可能后端服务会出问题。后端的服务出问题,不是它做一次新的这种发布,有 bug,或者他的这种服务的耗时从过去。比如说 5 毫秒,6 毫秒,突然变成 500,600 毫秒的这种,可能会导致一些问题。所以我们在网关上面,会去做流控、降级、监控这些事情,以保证它的这种。在网关上面做流控。做流控降级这种去保证它的这种金融健康。

沪江黄凯:服务注册进来以后,你的网关是怎么样发现这个服务呢?

滴滴赵伟:我们有管理后台人工去配置的。每上一个服务要暴露的话,是需要去到我管理后台上面去配它的那个 Class,那个什么类名,方法,参数之类的都是要配进去的,然后跟我的 API Gateway 对外暴露的名字要做映射关系的。

唯品会郑明华:网关我们也在做,我们网关是一个集群,或者多个集群,不是单个集群。首先不是一个单台服务器,而是一堆服务器,而且这对服务器可能还不是一个小集群,而是一个大的集群,这个大集群可能还有多个小集群,他每个集成可能是对应不同业务的,所以说即使是像你说的某个网络出问题了,它也不影响他所有的业务,这是第一个;第二个是网关主要就是一个通道,无状态、无业务逻辑,所以就是说他出问题的可能性很小。

沪江黄凯:你们用什么语言开发?

唯品会郑明华:我们一般是用 Java 开发。

沪江黄凯:如果我是 HTTP 请求,会使用类似 HTTP client 作为转发工具对不对?

滴滴赵伟:我们是以 Doubbo 的方式,那个 API 的 Gateway,他实际上也是在 Doubbo 上面,会去从 Doubbo 上面拿到你底层的服务,他的这个净化之类的,去调拨去的,他不会走那种的,就是说他会网关他会进来,我们就进来计算,接受的是 Json,转成对象,然后打成二进制流,通过这种 Double 的这种 RPC 给调到后台去了。

沪江黄凯:所以你服务都是 Java 开发了,没有任何的其他的语言?

唯品会郑明华:这个不是。这个首先是服务跟语言没有关系,跟序列化协议有关系。

滴滴赵伟:他这种主要是序列化,因为语言是序列号的这种前后高地位之类的,这种可能不太一样,你自己的长度可能不一样,所以可能就是用这种业界比较通用的一些,比如 Stream 的这类这些东西,或者说你自己去搞一套这种二进制的这种序列化。

滴滴赵伟:有些语言他可能不太支持,比如说像可能新进来的这种 Go,可能有些不太支持,所以这个有时候也比较麻烦一点。

沪江黄凯:我们很多服务都是用其他语言写的,比如 Go,C++ 和 .NET 语言,如果要推 RPC协议,会造成其他的部门的反对,因为他们接不进去,这样对于驱动项目效率太低。为统一这些项目的管理,我们都统一使用 Restful 作为服务间的交流方式。

滴滴赵伟:另外 HTTP,他的性能可能要比 RPC,TCP 这种纯的这种 TCP 可能要性能要低。因为我觉得公司发展到最后,技术框架应该会统一。从业界来看,除了阿里,阿里做的方法他 HTTP 转成纯 Java。业界其他的一些大的公司,比如腾讯、百度好像都是多语言开发的,前面用 PHP,后面用 C。但是好像只有阿里做的比较好,整个技术框架统一。我觉得可能是要方向,这样成本会很低,业务间打通成本就会很低,后面发展会加速的,但是去做这件事确实很难。

唯品会郑明华:我现在在唯品会就是做这种事情,把 PHP 的语言逐步改成 Java 的开发。但我一直相信这个事情要做的。因为在公司内部,团队是流动的。如果都是 Java 的,流动还好。但是一个是 C,一个是 PHP 的,流动的难度就大。比如我们有的业务不做了,技术团队也不好转成其他的语言。从另外一个角度看,技术统一以后,开发维护或者沟通协调的成本都会降低很多。



主持人:如何提高服务的高可用?

沪江黄凯:微服务天生就是高可用的吧。微服务大部分属于无状态的服务,只要解决了横向的拓展问题就解决了高可用问题。对于这种无状态的微服务,我们公司提供了Docker+mesos+marathon 三剑客的解决方案,所以应用程序不会死掉,如果死掉也会自动重启。那对于有状态的服务来说高可用比较难,所以我之前就在问,大家的微服务都是无状态的吗。如果是有状态的,那就必须解决高可用的问题。

唯品会郑明华:如果有状态的服务,必须做到客户 Session 粘连,但性能会大幅度下降。

沪江黄凯:解决方法是不是 Active+standby 模式?客户端通过 VIP 访问?

唯品会郑明华:但是你还是解决不了这个,一旦它死掉以后,客户连上来的状态也丢了。

唯品会郑明华:因为你现在客户都是连的 Tcp。

滴滴赵伟:它肯定会断掉的,要重新连一下的,这肯定的,你这边使用那个虚拟 IP 的话。当它连到后面还是连到某一台固定的机器上,机器就死掉了,就是内部的那个 socket 就断掉了,虽然外部的没有断,实际上还是要重新建。

滴滴赵伟:一旦一键 Retry 过了嘛,整个链路就打通了,这个时候断了其实用户实际上还是要重新连一下的。

主持人:客户端 Retry 一下。

唯品会郑明华:可能还有难度的,因为当时你第一次建连接的时候,你用的那个 Docker,可能都没了,你再连接再重试也没用了,是看你连的是哪一个。

沪江黄凯:那是不是要有一个 Session 的服务来存储状态?服务状态会存在 Redis 或内存中吗?

滴滴赵伟:所以你的这种服务就是无状态的了,因为你把状态已经搞到 Redis,所以说你的服务就成无状态的了,所以你得服务是可以互相扩展的,或者扩服务,减服务都没问题。

唯品会郑明华:要做到客户的黏粘,那个服务器起来以后才还能用,否则的话基本上很难做。另外一个是你选择什么服务框架,就比如 Doubbo,这也是一个常见的模式。所以这里面已经解决了服务故障以后怎么重试的的问题。



主持人:线上的微服务怎么做持续交付?

滴滴赵伟:我们其实还没有做持续交付,还在尝试持续集成。因为你要做持续交互的话,不是你一个业务部门能够单独去做的这件事情。因为你会涉及到很多,比如 IP 的分配,服务层报道。我们是想通过 Docker 来做持续交互,你将来的 IP 怎么去分配,端口怎么去分配,包括 Docker 的持续分配。我们原先最早的时候用的是腾讯的机器人,他那个系统,现在出来镜像都没办法用,你知道吗?真的搞得就很蛋疼,包括以后将来日志收集的查看,这些问题不是一个业务系统能够解决的,它必须跟运维部门去合作做这件事情,包括你要牵涉到一个发布的流程,我们现在在尝试这种持续集成,持续交互我们现在还做不了。

唯品会郑明华:我简单说下,就是持续集成/交付是一个比较大的东西。从开发到测试环境,包括灰度环境,包括预发布环境。比如在测试环境里怎么去集成,因为你是一个微服务,你想再把整个业务顺起来,需要周边的几十个,几百个服务才能把这个业务跑起来,那怎么解决开发的版本跟其他服务的测试问题,这是个大的问题。在以前的工序里面,它做了很多工作,包括建个人的测试环境,然后建整个团队的那种联调测试环境,以及你持续发布测试环境?那么这东西都要把它建立起来才能做到,才能正真做持续发布。

滴滴赵伟:就这个问题,这样除了你的业务之外还要牵扯到存储,比如 MySQL,Redis 这类东西,确确实实持续集成持续交互都是比较难的,现在 Docker,Kubernetes 是在解决这个问题,但是现在很多公司目前都还在尝试 Docker ,但业界玩的很好的还没怎么见过。

沪江黄凯:我们已经有持续集成/发布的方案。由于我们课件系统所有的服务全部使用 Docker部署。基于 Devops 和 Openstack 的启发,我们充分利用了 Docker 一次制作随处使用的特性,利用 Jenkins pipeline 的功能,使开发只要提交代码就自动进入全自动交付流程,直至上线。

其基本流程是这样的:

开发成功提交代码触发 Jenkins pipeline。

Pipeline 由这样几部构成:测试、编译、打成 Docker 镜像、上传镜像、调用 Marathon API发布到 QA 环境。

QA 通过自动或手动测试对 QA 环境进行验证,如有问题返回开发修改并重新触发 Pipeline。

测试通过后还是由 Pipeline 自动发布到验证环境并调用脚本进行 FBT。

所有验证无问题后 Pipeline 先对产线使用金丝雀发布确认有无问题,如果没有问题即可全面部署。

整个持续交付流程中有几个特点:

整个发布,全部是由 Pipeline 脚本调用 Marathon API 进行的,没有任何人工参与。在实际使用可以根据业务的敏感性做一定的人工干预,比如发布到产线环境可能是由运维人员协助完成。

交付过程中的几个环境除了产线环境以外,并不是物理隔离的。而是根据需求动态创建或消亡的,类似于每次部署都是一个全新的环境,这样做的好处是不容易产生发布的冗余数据和配置错误,这也是整个流程中最有特色的一个。

Docker 的一次制作,随处可用特性在交付流程中大大的避免了环境不一致问题造成的部署复杂度。

滴滴赵伟:我们现在想做这些事情,但是其中有一个比较麻烦的事,现在用这个的话。我们肯定要尝试新的技术的,不可能就是一刀切,就直接全部就上去了,每次全部就上去了,因为你并不熟这个东西,也没有经过现场的验证,肯定也是逐步去试的。逐步去试问题就在于我们有一部分服务是在 Docker 类似里面改的,有一部分服务它在外部的。服务的注册和发现,你们现在是怎么搞的?包括像这种日志,因为它放到镜像里面去,日志相当于输出,你怎么定位问题这个查日志,包括你的监控这块会怎么去做呢?

沪江黄凯:其实在整个框架中注册与发现有两种方式,一种是服务启动之后主动地向注册服务器注册信息。我们现在使用 Consul,也就是说服务在启动后会自动调 Consul 的注册服务API,告诉 Consul 服务的 IP 地址和端口。这里 IP 地址是宿主机的 IP+ 映射的端口,宿主机 IP 通过环境变量自动注入 Docker 中,每个 Docker 就自动知道了宿主机 IP。这种方法有侵入性,其实在我们使用编排框架,比如 Kubernetes 或 Mesos,是知道启动在资源池中的 Docker 服务的端口是什么,名字叫什么,尽管端口和 IP 都动态的。还有一点是要解决内网和外网互通,这是我们实现 Docker 微服务编排的必要步骤。如果启动了很多微服务,如果这些微服务都是无状态的,而且是平行服务,一定需要一个 Load balance。只要在这个 Load balance 上开端口映射,内外网就打通了。

沪江黄凯:另外一种服务注册与发现是通过三方的工具不断监控编排服务的变化,如果发现有新服务启动,则自动注册。关于日志的输出,我们使用 Volume 挂载 HOST 本地文件系统的方式,把 Docker 中的日志文件“引”到宿主机上,再通过ELK套件管理和查询。每个日志文件都会有具体的 Docker 的 IP,查询问题非常方便。

滴滴赵伟:对于业务部门这个事情很难做很难做,除非你进来之后他们帮你解决这种问题或者系统部进来帮你解决这种问题。

滴滴赵伟:但问题是我们现在还没有 Loadbalance 服务,他们都是自己通过 ZK 去搞得这种,Doubbo 的那个东西就相当于我们在用 Loadbalance 这种东西。所以对我们来说,这东西就很麻烦很难去做。所以就一直搞不定,肯定要先拿小服务去试一下,不能强上去,全部放到 Docker 里面去,这个受不了的,万一有问题的话,你承担不起这个责任吧。

沪江黄凯:对,是从一些小的应用开始实验的。其实一个流程验证跑通了,那么接下来就可以大规模的推动了,因为流程都是一样的。

猜你喜欢

转载自myl123.iteye.com/blog/2367071