架构师小组交流会:每期选一个时下最热门的技术话题进行实践经验分享。
第三期:微服务。微服务架构以其高度的弹性、灵活性和效率的巨大提升,快速受到各领域架构师和技术决策者的关注。它的基本理念是将一个肥大的系统拆分成若干小的服务组件,组件之间的通讯采用轻量的协议完成。我们本期小组交流会来探讨一下,现在互联网公司的微服务实践情况。
嘉宾:京东章耿、原唯品会石廷鑫、七牛陈爱珍
本文是对此次交流的整理,分了上下两篇文章。
第一轮:自由交流 京东章耿:大家好,我是京东基础架构部平台中间件的章耿,主要负责京东的服务框架,配置中心等项目。京东 11年底进行.NET转 Java 的技术变革,当时就提出了接口的 SOA 化的概念。那时京东业务发展非常快,项目越来越多,服务化是必然的趋势。京东的服务化框架是一共有两代。第一代是 12 年开始,我们使用开源的一个实现,用 Dubbo 作为 RPC 框架, Zookeeper 作为注册中心,那时应该算一个主流的技术选型。我们在开源的基础上做了一些易用性和功能扩展,比如说支持 REST 、支持 kryo/thrift 等序列化,服务监控等,这个框架在那时的业务规模和节点数量下面还是比较稳定的。 14 年初我们开始自研服务框架。为什么这么做呢?一个是之前的服务框架还是有很多可以提升的地方,不管是性能还是服务治理的一些功能,因为京东的机器数,机房也在不停的建,网络拓扑的复杂直接需要高级的服务治理功能;还有一个就是接口节点等数量级的增加,当时我们的接口规模慢慢的已经好几千了,接口的实例也几十万;另外就是用开源的有些内部系统打通比较麻烦,所以我们要做升级换代。当时也是考量了用开源的改改,还是全部自己重新做的抉择。后来觉得一个是有时间,二是觉得自己有能力做一个符合京东自己的定制化的服务框架,所以 14 年我们就开始自研框架,做了整整一年。 15 年初我们上线新版的服务框架。
我们新的注册中心,是无状态的一些节点,基于数据库做最终一致性。为什么选数据库,替换以前的 Zookeeper ?因为 Zookeeper 是树状结构,从某些维度查询它要遍历整棵数,用数据库的好处就是可以从多维度的查询分析过滤,不管是机房、 IP ,都可以去查询,分析,过滤比较结果。然后 Zookeeper 在跨机房的时候有一个问题,它是半数节点存活才可以用,所以如果跨机房部署的话,最起码得 3 个机房,才能保证集群整体可用。还有 Zookeeper 它是强一致性的的,比较依赖网络。如果网络不好,如果跨机房断网的时候,它其实是不可用的,读都不能读,所以会带来一定的问题。以前用 zookeeper 注册服务端,就是写一个临时节点,等服务端死了节点自动消失的。但是你在网络一抖的时候,或者是服务端和 Zookeeper 之间有网络故障的时候,它其实会不小心把它摘掉,因为 Zookeeper 认为它死了,但其实它不一定真的死了,所以这个时候就需要有一些辅助的去判断它是不是真的死了。第一次遇到的情况是那个临时节点,后来我们是把它改成永久节点,然后定时的去 telnet 它的端口,像 Dubbo 是直执行 ls 命令,我们也是直接执行一个命令,看它有没有响应,那是第一代的时候。在 JSF 的框架里有一个哨兵的组件,我们的 Provider 会定时的发心跳,对于注册中心来说,它知道最后的心跳时间,然后定时刷到数据库里面,看哨兵的情况,如果没有哨兵,数据库里面保存一个最后心跳时间,这时候你可以用定时任务去找它,这是我们最早的一个版本。后来我们改进了,因为有时断网了,这个心跳时间就不准了。所以在每个机房还布了一套独立的程序,没心跳的同时再由它来判断是否可用,如果这个同机房的程序都连不上,我们再把它置成一个不可用的状态,双重保证。另外以前 Zookeeper 的时候服务列表是下发的全量列表,例如 1000 台加一台,下发的是 1001 台,而新版注册中心我们是推变化的部分,只推加 1 台,大大节省了数据的推送量。
RPC 框架,我们内部叫杰夫, JSF ,京东服务框架的简称。我们是用基于 Netty 自己研发的。为了兼容上一代版本,兼容之前的 dubbo 协议,所以我们也是用的无代码入侵的;我们在同一个端口支持多协议,包括自定义的 JSF 协议, http 协议, dubbo 协议等。目前我们只有 C++和 Java 的客户端,然后如果是其它语言例如 Golang , python , PHP 的话,我们都会让他向我们的一个 HTTP 的网关发请求,这个网关主要就是转发。把前面的 http 请求转换到后面一个 JSF 请求,也是基于 Netty 做的。序列化默认是 Message Pack ,是个跨语言的协议,也支持 hessian , json , Java , Protobuf 等。注册中心和 RPC 框架直接是长连接,可以进行一个通讯。
唯品会石廷鑫:大家好,我是石廷鑫,原来在京东、唯品会,现在在宅急送。我基本上都是在仓储物流这方面。以前在亚洲一号是按照仓库的整个操作细节分成各个模块来做的服务拆分,每个模块是单独部署的。仓储的每个仓,根据种品类不一样,整个的操作流程是不一样的。但是核心的东西,如库存、订单管理,都是一样的,只是一些组合不一样。举个例子,小件商品上下架都要做的,但是对于大件商品,比如说大家电,基本上都不需要放架。打订单也不需要,完成配送才需要订单,实际上是到了最后才绑定订单。所以每个流程不一样,需要的组合也不一样。那时我们就想到了,把每个模块先拆出来,主要是用 KVM 运行整个节点,业务部分就用这些节点来整个串联起来。核心的东西比如说库存、订单、商品资料,基本上都是用这个简单的模块。
唯品会的仓储也是按这种思路来做的。当时唯品会用的是 Dropwizard ,实际上是我们用了它的一个壳,后面还是用的 Spring ,也做了一个模块,把 Spring 集成到一块。后来 Springboot 做了出来,基本上就把 Dropwizard 抛弃掉了。因为 Dropwizard 版本升级变化比较大。但它有一个好处,就是 Bundle 比较好,服务化拆分的时候可以根据它的大小进行可分可合,如果不需要拆分的时候,我们就把 Bundle 合到一块,如果需要拆分的话,就把 Bundle 再拆出来,单独这个应用是非常容易的。
我去年到的宅急送,我们的服务注册、服务发现、网关都使用的 Spring Cloud 的这一套。目前来说还是不错的,基本没发现大的毛病。唯一的问题是如果前期没有做好的整个数据抽取,整个报表可能会比较麻烦一些。
七牛陈爱珍:大家好,我是七牛的陈爱珍。微服务架构的设计理念非常适合七牛的业务特点,每个服务只负责单一的职责。比如音视频的处理服务:音视频转码 , 音视频拼接 , 视频帧缩略图,点播流式转码,都是以微服务的方式构建,这样每个服务都拥有独立的运行环境,并且可以根据自身的业务压力情况进行独立扩展,动态的弹性扩展还可以提高资源的利用率。一个可落地的微服务架构应该为微服务提供独立的运行环境,调度框架,注册中心,配置管理中心和监控平台。 七牛采用的是 Mesos+Docker+自研调度系统的架构。 Docker 做环境封将, Mesos 做资源调度,自研的调度系统负责对 Docker 进行弹性的调度。
我们使用 Consul 做注册中心,实现服务的注册与发现。 Consul 自带 key/value 存储,可通过 DNS 接口做服务发现,且具体健康检查的功能,并支持跨数据中心的服务发现。 API Gateway 可以通过 Consul 提供的 DNS 接口查询到服务所有的可用实例的列表信息,并将请求进行转发。流量转发具有负载均衡的功能,采用的是轮询的方式,服务发现则是基于 Consul 做的。用户请求进来后通过 Consul 查询到所有可用节点的访问地址,再通过轮询的方式将请求发给后端的服务进行处理,对于返回的结果仅作转发,由请求方解释和使用。并且在 API 网关中带有监控的组件,对请求数,失败数等进行监控,传送到 Prometheus 服务器上。通过监控数据对请求进行流量控制及服务降级等相应的处理。
当需要调用多个微服务时,根据七牛云的数据处理的业务特点我们使用管道( pipeline )来进行串行的处理。每一个微服务的输出都是下一个微服务的输入,直到最后一个微服务执行结束才是最终数据处理的内容。比如上传一个视频资源后,需要做两个数据处理操作: 转成 mp4 资源和进行 HLS 切片,这种场景下不能对原资源同时执行两个数据处理的操作,必须按序执行。
第二轮:话题交流 主持人:服务与服务之间的依赖关系怎么管理?服务编排怎么做?把这些服务节点串起来。
唯品会石廷鑫:比方说不是业务的,基础服务,如图片服务器这些,都是独立的。唯一相关的是下面的业务层和底下的基础服务有之间调用,还有是业务模块之间有服务的调用,在系统里是没做体现的,但是我们在服务和服务之间,就加了类似于熔断的那个默认的东西。系统之间的依赖关系,我们自己做了一个简单的系统进行维护。 Spring cloud eureka 是都是存在内存里,我们改良过,我都改到一个数据库。我们交互全部都是 Rest ,我们后边还写过一套, Google protobuf 。我们仓储的内部的业务模块之间是用 Google protobuf 来做的。我们自个做了一套虚拟化的在里边的。
唯品会石廷鑫:我现在用的是 Apache camel 做编排,可以用 DSL 描述把服务串起来。其实更多的是业务的流程怎么组织,怎么去把基础服务让串起来,形成一个真正大的业务场景。
京东章耿:目前京东大部分一个接口一个方法就已经是操作多张表了,一般认为是一个比较原子性的操作了,如果再在外面有一个东西把它包一下,例如把 3 个方法包成一个方法,其实意义不大,所以我们现在是没有做这方面的工作。另外京东有弹性云,用的 Docker ,不过不是你们想象中那种微服的那个 Docker 用法,京东现在用叫“胖容器”,更像一个虚拟机一样的一个东西。 Docker 里面运行的是一个应用,然后这个应用,它可能包含多个接口多个方法。可能大家理解微服务的 Docker 用法应该是一个容器里面,他只跑一个独立的逻辑,对数据的一个操作,或者对一个资源的操作。比如说下订单、扣库存,这两步操作,他可能跑了两个容器。把它编排成一个 service 的这种,我们好像没有这种。
主持人:服务拆分是怎么做的? 京东章耿:京东现在的服务其实也是没拆太细,主要还是业务部门自己控制服务粒度。京东的业务还是比较复杂的,各个应用之间互相依赖,互相调用,每个操作基本都会涉及到很多资源的变更,像微服务推崇的那种对单一资源的操作,基本上没有。说实话,这种大型互联网公司里面的服务根本就微不起来的,不可能像 RESTful 一样,对一个资源的一个 put post 操作基本不可能,我们现在都是力度还是由使用我们框架的研发人员自己定的,然后一般他们都是根据这种方法之间的一些关联性或者原子性,或者是扩展性来进行组合或者拆分。所以我们一般叫自己服务化框架,不叫微服务框架。另外他们可能还会考虑一些服务是否可以独立部署,拆的时候可能会考虑一下这些。
主持人:其实很多大型电商互联网也拆不开,也不能称之微服务,是那种服务力度很粗,然后每个服务号有若干个接口,其实耦合性很高的合在一起,相关度很高,数据都在一起。
京东章耿:都差不多,都是业务开发自己来折分服务粒度的。就像刚才说的下单。肯定是一次要操作好多表的。业务开发认为这是几次表操作其实是一个下单的原子操作,那么他就拆到这么细了。
唯品会石廷鑫:我们服务不是说一个部一个,实际上它是和数据域相关的,都搁到一块。
唯品会石廷鑫:按功能分的,功能跟功能之间调用,如果不是同一个数据域里边的,还是用 RPC 来调用的。
京东章耿:那个听起来很美好,你想我们现在这样拆,都已经上万了接口,方法基本都上十万了。你这样拆的话,起码乘以 10 的接口,而且这样搞的话就得整个公司都要动用非常非常大的能力去搞好这个事情。
主持人:单体他如果拆成像市场上去提倡的那种微服务,其实他对内部的消耗是非常大的,因为我们很多的服务内部的调用,其实是非常频繁,如果都把它拆开了去独立部署的话,它其实对网络的消耗是要求非常高的。
唯品会石廷鑫:就是最难的点,一开始你是拆不开这个数据。
主持人:数据只要能拆开,你什么都好干。你只要能把数据粒度拆的很细的话,那就没问题了,那也能做的很细,但就是拆不开。其实好多数据都是陈年老表,都是很都是从原来系统继承下来的,就很难拆。
唯品会石廷鑫:所以新做一个系统还可以。要做这个老系统计划很难的,拆个表就要拆很多天。
京东章耿:而且是数据量比较少,然后逻辑比较简单的,例如那种创业型公司,使用开源的方案,这种可以很方便。
主持人:在双 11 时各个电商肯定会有大型的促销,这时服务的压力肯定会有很大的增长,怎么解决服务的伸缩的问题?
唯品会石廷鑫:就像我们这边的话,就看哪个基点的压力比较大一些,然后你多布几个基点就可以了。现在基本上都是中泰的那个,就是 Spring cloud 那套。扩展都是提前先做一步。自动扩的话,就是相当于我们租了一个类似于简单的监控。就是根据他的实例,然后因为微服务,我们现在是 Spring cloud ,基本上都是 java -jar ,然后自己启就得了,反正那个 jar ,你可以放到一个公用的地,远程 java -jar 要干架就起来了,监控的时候根据他的量,然后随时可以启就行了。
京东章耿:京东的服务发布都是挂在应用下面的,而应用发布的平台其实会和各个系统打通,比如说数据库授权的系统,然后自动挂 VIP 的一些系统,挂监控平台,日志系统等。然后我们还有个监控平台,监控一些指标,例如机器情况,应用情况,还有自己业务埋点的性能等数据。至于服务是否有压力,都是自己评估,自己提前扩展。 一般大促前会进行大规模的压测,他们自己压测,然后根据结果自己扩就可以了。京东是没有开自动弹性扩展的,基本都是大促前提前申请容器,然后提前扩完。
唯品会石廷鑫:并发量大的业务都是放在公有云,双十一比平常多了两倍的机器,我们只买 了一个月的云主机。临时用几天,结束就不用了。