⼤规模微服务稳定性保障实践
出品:百林哲
CSDI
峰会
PART 00
引言
微服务架构通过⼀系列松散的低耦合服务赋予了企业应对业务变化的灵活性,相⽐单体应⽤,微服务架构的整体复杂度更⾼。微服务的成功实施通常⽆法⼀蹴⽽就,过程中基于传统服务框架和Service Mesh技术构建的微服务可能并存,单⼀服务可能同时运⾏于不同数据中⼼的物理机、VM和容器之上,技术栈和运⾏时的异构增加了微服务的运维成本,并可能对敏捷交付产⽣影响。
为了应对以上挑战,需要从研发流程和基础设施维度审视微服务⽣命周期稳定性保障需要解决哪些关键问题以及如何建设相应的配套设施平台。
CSDI
峰会
PART 01
微服务稳定性保障的概述
实施落地挑战
• 对微服务设计者来说,不管是按照业务能⼒拆分还是按照领域拆分,设计者都必须站在全局视⻆对服务边界进⾏明确的定义,“过⼤”的服务不利于团队敏捷迭代,“过⼩”的微服务则会引起服务 爆炸,造成后期治理维护困难。
• 对微服务开发者来说,从进程内⽅法调⽤转换到了以服务契约为主的远程调⽤,甚⾄是事件驱动的异步调⽤,服务的健壮性很⼤程度上要依赖“⾯向失败”的编程模式被贯彻执⾏的程度,同时单个服务需要具备限流、熔断、降级等⾃我保护和保护他⼈的能⼒。
• 对微服务的运维者来说,越来越多的通⽤能⼒下沉到了基础设施,业务稳定性依赖所有服务的稳定性和基础设施的稳定性,在这种情况下,短板效应特别明显,需要不断识别并消除链路的短板。在⼤规模微服务下,有限的资源可能并不能完全满⾜所有服务的需要,求取全局最优解是亟需回答的重要问题。
字节跳动微服务实施概况
介绍⼀下字节跳动微服务实施的概况,我们从2015年开始正式实施微服务架构,初期以python和go 的微服务为主,并配套了相关的开发框架和运维平台,后⾯随着业务发展,⼜⽀持了使⽤C++、Java、Node等构建微服务,不同语⾔构建的微服务可以通过统⼀的平台⼀起管控,所以在字节,研发⼈员可以完全⾃主选择服务的技术栈⽽不必担⼼管控平台是否⽀持的问题。近⼏年,我们积极拥抱云原⽣架构,部署平台从物理机和虚拟机的底座逐渐迁移到了以kubernetes为底座的容器平台,微服 务管控和治理相关能⼒逐渐过渡到了Mesh技术,⽬前内部开启了mesh能⼒的服务规模巨⼤,并且仍 然在迅速扩⼤。字节跳动的产品较多,⽤⼾体量庞⼤,据统计,涵盖数据、算法、内容等核⼼能⼒的相关微服务已经超过10w,这些服务通常部署在多个甚⾄多级数据中⼼,在如此规模的复杂场景下, 我们正⾯临严峻的线上稳定性挑战。
微服务的成功实施离不开持续集成和持续交付。特别是在新产品研发推⼴阶段,业务可能灵活多变, 我们希望以周甚⾄是更短时间去迭代交付产品新功能,帮助业务快速试错和纠偏。微服务架构的实 施,分离了⾃治团队的关注点,结合有限的上下⽂共享,可以⽐较好的帮助团队实现上述敏捷迭代的⽬标。据数据统计,字节跳动所有开发者对所有微服务每天会进⾏总计超过2w次构建和7k次部署。在 如此⾼的集成和交付频率下,微服务的线下活动需要强有⼒的平台进⾏保障,⼀⽅⾯,需要确保研发活动可以独⽴并⾏,另⼀⽅⾯,需要确保只有经过验证的变更才能被发布到线上。
字节跳动微服务稳定性保障的挑战
微服务的稳定性体系分为研发阶段稳定性和线上运维阶段稳定性。
线下研发是服务⽣命周期的开始,研发环境稳定性对交付速度会产⽣直接影响。我们希望每个研发⼈员可以拥标准统⼀的研发环境,环境之间要能够互相隔离,对⼩规模微服务可以借助云原⽣技术⽐如helm构建研发环境,在⼤规模微服务下,为每个开发者完整的部署⼀个测试环境资源消耗是巨⼤的,所以研发稳定性的保障需要在资源成本和环境管理成本之间找到平衡。
微服务架构的研发需要持续集成,在集成阶段,服务质量稳定性是该阶段的重要关注点之⼀,随着产品功能叠加,回归测试将需要投⼊越来越多的⼈⼒,如果能够通过⾃动化的回归,验证已有功能,将能帮助研发⼈员更专注于新功能相关的测试,在此基础上,辅以研发流⽔线也能更早的暴露问题,并防⽌问题往线上蔓延。
对微服务线上稳定性的保障,第⼀⽬标是保障业务⽆损,第⼆⽬标是受损可控并及时⽌损,熔断/降级/限流等常规治理能⼒是实现第⼆个⽬标的重要⼿段,这⾥不做展开。对于第⼀个⽬标,我们认为服务健壮性是关键点之⼀。容错性是微服务架构的⾃然属性,提前探测服务对各种预期内异常的⾏为,可以帮助持续加固系统,对业务稳定有重要意义。此外,对线上容量的合理规划则是另外⼀个关键点,因为即使在云的时代,基础设施的资源依然是有限的,合理规划服务的各种服务资源配⽐,平衡稳定性和成本,对⼤规模微服务保障来说也⾄关重要。
开源方案介绍
业界开源的⼀些应对上述问题的解决⽅案(这⾥并没有列出某个问题的所有可选项,针对单个⽅向只选取了⼀个典型的开源⽅案)。
⾸先是研发阶段稳定性-多研发环境⽅案KtVirtualEnvironment,简称KTVE,该⽅案由阿⾥巴巴开源,来⾃阿⾥内部关于项⽬环境的使⽤经验,KTVE解决了研发环境之间的隔离和联通问题,项⽬环境是每个开发者独占的虚拟环境⽐如图中的dev.proj,同⼀个项⽬环境的服务会被优先调⽤,如果调⽤的⽬标服务在项⽬环境中没有部署则调⽤公共环境,图中以dev作为标识,通过这种⽅式服务开发者可以互不⼲扰的完成跨服务的联调测试。这种模式下,除了公共基础环境需要部署业务链路的所有微服务外,项⽬环境可以仅部署开发者需要变更的服务,整体环境的资源占⽤是⽐较可控的。
实现上KTVE依赖kubernetes的原⽣服务发现,需要通过监听deployment和service对象,持续更新mesh规则,从⽽实现项⽬环境和公共研发环境之间的流量穿梭机制,⽐如对来⾃QA的测试请求会在公共基础环境中流转,对来⾃开发者的联调请求会在项⽬环境和公共环境中穿梭。
KTVE开源⽅案有两个局限点,第⼀点,⽬前开源版本的KTVE只适配了istio控制⾯,受限当前可⽤的能⼒,还⽆法⽀持基于tcp的⾃定义rpc调⽤路由,第⼆点,由于依赖kubernetes原⽣服务发现,其也⽆法⽀持传统的基于框架sdk的服务发现机制,也就意味着基于spring cloud或者阿⾥开源的dubbo框架开发的微服务将⽆法直接使⽤该⽅案去构建多研发环境能⼒。虽然可以通过⼿动适配解决上述问题,但是对业务有⼀定侵⼊,并且可能会与框架的断路器等功能冲突,进⽽增⼤线上问题发⽣⻛险。
功能质量保障⽅⾯,前⾯提到过在集成阶段,最理想情况是,开发者只关⼼新功能的测试,已有功能的验证通过⾃动化的⼿段解决,⾃动化测试⽤例是⼿段之⼀,但是⽤例的编写需要耗费⽐较多的⼈⼒。2015年twitter开源了diffy⾃动化差异⽐对⽅案,思想是将同⼀个请求⾃动复制到运⾏不同服务版本的实例上,校验不同版本的服务返回差异得到diff结果。⼀个完整的diff环境包含candidate、primary、secondary三种⻆⾊,其中candidate部署的是变更后的版本,primary和secondary部署的是基准版本,secondary和primary响应结果差异会被认为是预期内的差异⽐如不同时间的请求响应中的时间戳字段肯定是不同的。借助Diffy,开发者⽆需编写维护⾃动化测试⽤例,通过构造测试数据即可完成对服务的回归测试。
虽然diffy将开发者从繁杂的测试⽤例编写⼯作中释放了出来,对⼤规模微服务来说,测试流量的构造依然需要测试⼈员花费⽐较多的精⼒,特别是需要构造涵盖各种业务场景和边界case的测试流量,更进⼀步的,diffy还需要和组织内的研发流程联通才能更好的发挥作⽤,⽐如diff环境的管理,将diffy⽐对的结果作为持续集成中的验证卡点等。
线上稳定性⽅⾯,虽然容错性是微服务架构的重要⾮功能属性,但是遗憾的是通常只有在错误发⽣时才能真正的验证服务是否真的做到了“容错”,混沌⼯程通过向⽬标服务⼈为注⼊预期内的错误并观察系统⾏为是否符合预期可以提前预演该过程。CNCF的沙盒项⽬Chaos Mesh是PingCAP开源的云原⽣混沌⼯程解决⽅案,从TiDB⾃动化测试平台演进⽽来,Chaos mesh基于kubernetes ⾃定义资源实现了常⻅的故障⽐如pod故障、⽹络故障、io故障等,⽤⼾可以声明式的编排各种故障完成故障注⼊,并观察系统⾏为是否符合预期。
Chaos mesh是完全基于云原⽣技术的实现,chaos-daemon具有privilege权限,可以操作节点⽹络和cgroup,chaos-sidecar会运⾏ fuse-daemon,⽤来劫持应⽤容器的 I/O 操作。这也就意味着对部署在物理机或者虚拟机平台上的基础服务则⽆法使⽤该⽅案,我们认为不仅业务层⾯需要混沌⼯程,基础设施同样也需要通过混沌⼯程验证基础服务的健壮性,所以有必要构建⼀个统⼀的混沌平台。此外,在⼤规模微服务下,对某个服务注⼊故障后潜在的影响⾯依赖⼈⼯的⽅式难以准确监控,业务链路上,上游服务对下游故障感知存在延迟,深度越深感知故障的延迟越⼤,甚⾄可能造成服务雪崩,⾃动化的、业务视⻆的指标监控和分析能⼒是安全开展故障演练的必要前提。
在容量⽅⾯,Kubernetes作为云原⽣的paas标准构建选项,正在迅速接管微服务的托管底座。Kubernetes平台原⽣提供了pod的⽔平扩容能⼒、垂直伸缩能⼒和集群的整体容量的弹性能⼒。对于⽆状态服务,利⽤HPA,⽤⼾可以配置cpu、内存或者其他metric指标规则,kubernetes会在需要的时候调整pod数量。对于有状态服务,VPA可以⾃动调整容器CPU和内存配置。CA是集群容量的调节⼿段,通常依赖iaas层基础能⼒,在调度POD所需资源不⾜或者集群存在资源浪费时,可以增减集群节点数量。
从业务视⻆看,⽆论是HPA还是VPA,都属于“事中”的处理或者“事后”的补救措施,对⼤规模微服务启⽤HPA其执⾏效率和对业务的影响结果难以预测,⽐如上游服务先于下游扩容有可能造成服务雪崩,此外,kubernetes只解决了计算资源的扩缩问题,但是对某些服务来说,资源瓶颈可能是数据库不够⼤或者⽀持的连接数不够多。
从基础设施视⻆看,⾃建机房容量调整依赖资源采购、⽹络规划、机房建设等诸多因素,具有⼀定的周期性,如果不提前规划,很可能受限于物理容量极限⽽⽆法满⾜突发的容量调整需要。
CSDI
峰会
PART 02
字节跳动稳定性平台实践
字节跳动的服务环境分为线上和线下两类,线上和线下⽹络隔离。线下环境分为基准环境和功能环境,其中基准环境类似迭代开发基线,通常和线上版本保持⼀致,功能环境类似KTVE的项⽬环境,所有的功能环境会共享基准环境,每个开发者都可以独占⼀个功能环境。为了进⼀步控制研发环境的成本以及更好的⽀持功能的并⾏开发,我们还允许开发者在功能环境中直接mock掉下游依赖的服务,举个例⼦,跨多个微服务的功能并⾏开发时下游服务开发者可以根据接⼝设计先提供mock的接⼝,⽅便上游依赖⽅并⾏开发。如同KTVE,环境间的隔离和联通是多环境平台的核⼼能⼒,但是需要兼容传统框架和mesh并⽀持http和rpc调⽤。
研发稳定性
⾸先,回顾一下微服务的调⽤过程,不管是基于框架还是基于mesh,微服务之间的调⽤都要根据被调⽤的⽬标服务拉取可⽤实例列表,根据负载均衡算法选择⼀个⽬标实例后发起点对点的调⽤。基于以上调⽤过程,在创建功能环境时,服务发往注册中⼼的信息会带上唯⼀的环境标签,框架和mesh侧会识别环境标签完成调⽤路由。通⽤Mock能⼒的集成遵循类似的过程,与实际部署服务不同,mock server会注册⼀个具有相同环境标签的同名虚拟服务到注册中⼼,实际调⽤发⽣时,会根据预先配置的mock规则响应请求。这就是多环境之间的调⽤流量隔离的实现思路,可以满⾜⼤部分业务需要。具体实践时,当多个功能环境依赖同⼀个存储服务时,由于数据不隔离,依然会存在相互影响的可能,不利于某些场景下的问题排查和测试,不满⾜某些业务场景的需求。
解决存储隔离问题的⼀种⽅案是采⽤类似影⼦表的机制,从基准存储中fork出⼀个影⼦存储服务。如果当前环境配置了存储隔离的要求,则检查调⽤的下游服务是否依赖了存储,如果依赖了存储服务则拉起该下游服务和对应的影⼦存储服务,⽐如创建bbb环境时,服务B依赖了服务C,⽽服务C未部署,对存储隔离的功能环境,平台需要⾃动拉起C服务的功能环境和影⼦存储服务。该⽅案的优点是实现⽐较简单,但是对资源消耗⽐较⼤,极端情况下甚⾄会退化为业务链路的完整部署,不应该作为默认的功能提供,需要结合流程,管控存储隔离类型的功能环境,这对⼤规模微服务下,研发环境资源成本的控制尤为重要。
接下来我们⼀起看下功能质量保障平台-通⽤diff平台,从测试⻆度来看, 测试流量分布对真实⽤⼾流量的拟合程度对潜在问题的发现是⽐较重要的,我们认为⼀段时间窗⼝内的线上真实流量,是⽐较具有代表性的,可以⽤于已有功能的回归验证。根据以上思路,我们构建了线上流量采集和回放能⼒,⾸先根据⽤⼾的配置进⾏线上流量的采集,预处理后存储到⽤⼾指定的存储服务中,接下来通⽤diff平台会通过流量回放服务,回放请求到diff环境,并记录相关的观测数据和分析结果,这些数据会作为研发流⽔线的输⼊,只有验证通过才能推进⾄下个研发阶段。Diff⽐对可以参考diffy实现,这⾥不做展开。
字节跳动当前处于微服务容器化过渡期,所以流量采集需要⽀持多种负载平台,统⼀采集线上服务的流量。以容器化的服务流量采集为例,利⽤容器内置的管控agent拉起采集进程sniffer,采集进程sniffer和服务进程会位于同⼀个容器内,sniffer使⽤ libpcap 监听特定端⼝流量,并在⽤⼾态重组TCP 层数据包后,批量发送⾄Kafka,采集的流量经过预处理后进⾏存储。默认场景下,流量采集模块只会对被采集的服务⼊流量进⾏抓包。为了提供出⼝流量采集(即采集某⼀服务对其下游服务的调⽤流量),流量采集模块需要对接服务注册中⼼,查询下游依赖服务所有实例的 IP 和端⼝,对这部分流量也进⾏采集。
已采集的流量需要通过线上流量回放并收集响应来完成diff⽐对。具体的,通⽤diff平台发起流量回放任务后,scheduler负责调度,将回放任务分发到指定的回放服务emitter执⾏,注意这⾥的emmiter回放服务不是⽆状态服务,原因是如果每个任务均摊到所有实例上执⾏,那每个实例都需要和回放⽬标服务实例建⽴连接,在⼤规模微服务下,海量任务的 goroutine、连接、内存等资源占⽤是不可接受的,另外⼀点考虑是通过将单个任务的处理收敛到少数实例上,可以提⾼请求密度,从⽽避免因为连接空闲时间过⻓被对端关闭,实现连接⾼效复⽤。
线上稳定性
在字节跳动我们的⽬标是建⽴⼀个统⼀的混沌⼯程平台,不仅能够帮助加固业务服务,也可以帮助加固基础设施的稳定性,所以故障注⼊能⼒需要兼容容器和虚拟机/物理机。故障注⼊和稳定性检测分析是混沌⼯程平台两⼤核⼼能⼒,故障中⼼对故障做了抽象,以统⼀的模型暴露给⽤⼾,⽤⼾在演练平台可以灵活编排各种故障并制定演练计划,通过平台⾃动化指标检测和分析能⼒,可以监控演练过程中,各个服务的稳定性状态,并分析潜在⻛险。
故障中⼼由三个核⼼组件组成,分别是 API Server, Scheduler, Controller,以 etcd 作为核⼼存储。其中 API Server 负责包装 etcd 并对外提供声明式接⼝;Scheduler 则负责解析故障声明,并根据声明持续更新故障⽬标;在这之后,Controller 将故障解析为可执⾏指令,下发⾄对应实例的 agent,或者调⽤对应中间件的 API,达到精准的故障注⼊。
以强弱依赖分析作为例⼦,说明下混沌⼯程的⾃动化指标检测和分析能⼒。分析强弱依赖时,对每个下游依赖依次注⼊声明的故障⽐如服务超时、服务不可⽤等,通过故障指标观测注⼊是否成功,成功后通过⽌损指标判定业务是否稳定,如果稳定则说明是弱依赖,反之亦然,结合历史数据可以构建异常检测能⼒,⾃动检测观察指标的异常,可以探测服务潜在⻛险,⽐如在故障注⼊后,是否有某些指标出现不符合历史⾏为的异常点。
我们认为对业务最直观的容量数据是以业务指标来定义的,⽐如A系统还能⽀持多少并发⽤⼾,B系统最⾼能抗多少QPS。反过来,当业务⽬标是⽀持100w QPS的时候,我们希望平台可以清晰明了的告知研发⼈员该业务链路内的服务各⾃需要多少资源,当前部署结构距离⽬标差距是多少。基于前述⽬标进⼀步拆解后,我们得到了构建的容量平台的三个关键基本要素:容量评估,观察分析和规划调节。⼤规模微服务下,我们期望可以控制评估成本,在安全可控的前提下得到⾼可信度的容量数据,就像CAP理论不可三样兼得⼀样,安全可控和可信度之间也有类似的冲突,⽐如线下环境完全可控但是很难完全模拟线上的情况,直接使⽤线上进⾏评估可信度⽐较⾼,但是可能会有⽐较⼤的⻛险,在字节综合考虑后决定直接在线上环境进⾏评估。
线上评估第⼀个问题就是如何控制对线上⽤⼾的影响,我们的做法是从线上隔离出⼀部分服务实例组成隔离链路,除⾮服务本⾝就处在很⾼的流量⽔位,不然通常隔离少量实例都是安全的。经过实践,我们也不建议隔离链路的实例数量太少⽐如⼀两个,因为太少容易被单点误导。容量评估的常⻅评估⽅式有基于历史数据的分析和压⼒测试等,我们采⽤的的压⼒测试的⽅案,⽀持⽤⼾使⽤线上真实流量和模拟流量发起容量评估任务,基于线上流量的评估对⽤⼾来说基本是零成本,适合线上有⼀定的流量并某个较⻓的时间段波动不⼤的服务;对基于模拟流量的评估来说则需要业务做⼀定程度的适配改造,确保模拟的流量不会污染线上的数据。由于隔离链路全部由线上服务的实例组成,这样得到的容量数据是⽐较可信的。压⼒控制器和智能分析模块会处理在评估过程中会产⽣各种数据,包括metric打点、⽇志、报警等,通过这些可观测数据,第⼀个⽬的是在评估过程中发现异常,防⽌线上业务受损,第⼆个⽬的是评估完成后,对数据分析处理,产出容量相关的数据,并分析潜在的可优化的点。
对不同的评估⽅式,调⽤链路隔离需要注意的关键点不同,但是由于待隔离的资源是线上资源的⼀部分,所有操作都要保证是可逆的。
对模拟流量
评估链路和⽣产链路需要完全隔离,不能有任何⽣产的正式流量流⼊,评估流量也不能流出评估链路,涉及存储(redis、mysql等)要读写影⼦库,异步的业务处理如消息队列(mq、kafka)只能在评估链路中流转,⽐如评估流量产⽣的消息也只能评估链路的消费者实例来消费。
对流量调度
要保证只有满⾜指定特征的正式流量可以流⼊,这⾥可以认为是对流量进⾏染⾊,染⾊后的流量同样不能流出评估链路,但是不同于模拟流量,涉及存储⽆需特殊对待。异步的业务 处理与模拟流量相同。
这是⼀次基于流量调度⽅式的评估⽰例,正常情况下,评估链路会承载10%的线上流量,开始评估后以5%的速度递增压⼒,并观测隔离链路的服务情况。这⾥选取了两个关键指标CPU和吞吐量作为观测的指标代表。
如何确定服务能够承载的最⼤⽆损容量呢?假设对该服务CPU最⾼不能超过80%,逐渐递增评估链路的流量,当流量压⼒调度到35%,CPU达到极限,持续观察⼀段时间,确保系统持续稳定,此时就确定了最⼤的安全容量点。
接下来如果继续增压,⽐如到40%的流量⽔位,此时触发了业务⾃动降级等措施,业务有损运⾏,虽然qps上升,但是cpu利⽤率下降,此时可以确定了极限有损压⼒点,因为再增加压⼒服务就会崩溃。对潜在⻛险的分析,本质上和时序数据的异常检测是类似的,⽐如⽰例中的cpu突增可能就是⼀个潜在⻛险,这⾥可以利⽤⼀些常⻅的异常检测算法或者机器学习算法得到。
CSDI
峰会
PART 03
向下演进的方向
字节跳动的的业务正在快速发展,微服务构建相关技术也在不断演进,在可预期的未来我们会继续加强前述稳定性平台的能⼒建设和完善。具体的:
多环境平台:构建分布式调试能⼒,使研发者可以像调试单体应⽤⼀样跨服务完成调试和问题排查。完善环境构建的跨服务编排能⼒,把各种容器、中间件、服务、存储等原⼦资源的创建和配置串联起来。
通⽤diff平台:⽀持线上已录制流量的改写,⽤⼾可以通过改写流量⾃动⽣成适⽤其他测试⽬的的测试⽤例。此外,探索对性能diff的观测分析,防⽌服务出现明显的性能退化。
混沌⼯程平台和业务容量平台,往常态化的⽅向演进,沉淀业务保障的最佳实践并泛化,通过智能化的⽅式进⼀步的保障线上业务⽆损运⾏。
CSDI
峰会
PART 04
小结
多环境平台和通⽤diff平台保障了线下研发活动顺利进⾏,混沌⼯程和业务容量平台保障了线上业务的可持续发展,即微服务稳定性需要全⽣命周期的保障,线下保障研发和质量实现敏捷交付,线上保障容错和容量,共同赋能业务快速⽆损演进。
作者简介
高威,字节跳动基础架构部资深架构师。曾就职于英特尔亚太研发中⼼、蚂蚁⾦服和阿⾥云,⽬前在字节跳动负责微服务保障平台研发,在微服务、云原⽣和DevOps领域有较多的经验和研究。
会 / 议 / 回 / 顾
参会企业:微软、阿里巴巴、小米、腾讯、华为、360、平安集团、渣打银行、巟商银行、招商银行、随行付、易方达、长亮科技、乐融软件、广州银联、穆迪信息、拍拍贷、宇信集团、投哪儿金融、天维信息、萨摩耶、招商证券、国信证券、陆金所、广发基金、中国银联、恒天软件、天阳宏业、中数通、电信规划设计院、oppo、步步高、vivo、爱立信、百富计算机、厦门航空、福建联迪、恒大物联网、星网视易、升腾科技、视睿电子、飞利浦、金山软件、金山游戏、欧特克、顺丰、深信服、yy、虎牙信息、珠海健康云、优视科技(UC)52TT、21cn、凯米网络、苏州耕耘无忧、ADmaster、博思软件、网宿科技、珍爱网、金蝶、唯品会、大宇无限、华讯网络、传动数码、无限极、云湾信息、珠海网博、上海别样红、同盾科技、杭州顺网、蓝凌软件、诚毅科技、长园深瑞、中南民航、远光软件、中国移动、中国电信、中国联通、物理研究所、深研院等。
特 / 别 / 鸣 / 谢
精 / 彩 / 推 / 荐
+
+
精品推荐
欢迎点击“阅读原文”,时刻学习一线大咖技术经验,同时欢迎大家分享、评论