当前位置: 首页 > 科技观察

多云架构的设计与实现

时间:2023-03-14 00:37:30 科技观察

“不要把所有的鸡蛋都放在一个篮子里”是众所周知的商业法则,很多公司在选择云平台时也遵循这一法则。构建基于多云平台的“业务中台”并不是一件简单的事情。需要构建快速继承、可持续迭代的路径,助力整体解决方案落地。本文以一个实际项目案例为例,分析项目架构设计、实施步骤,以及多云架构面临的挑战和机遇。总体思路不同的云厂商提供不同的云服务,同样的云服务在功能和性能上都会有或多或少的差异。你使用某个云提供商的云服务越深入,就越难迁移到另一个云提供商。如果选择自己搭建云服务,技术门槛高,维护成本高。确定多云架构后,首先需要在技术栈的选择上做出妥协。一个基本原则是通过业务架构的灵活性来适应不同的云厂商,尽可能利用云厂商提供的优秀特性,提高运行在云平台上的业务系统的可靠性,增强企业的竞争力。整体业务。以上思路与部分客户的普遍思路存在明显差异。部分客户选择使用开源软件搭建自己的PaaS平台;有些客户完全使用云厂商的技术栈开发两套业务系统。这两种方法是两个极端。前者开发和运营难度大。往往因为技术风险评估不够充分,导致项目无法如期交付,或者产品竞争力太弱,云厂商提供的服务不如云厂商。后者需要维护两套系统,代码重复率高,将完全被云厂商绑定,失去筹码,降低业务发展的灵活性。一些客户还期望云厂商能够提供足够的兼容框架支持,在不改变现有业务系统逻辑的情况下实现多云部署。由于客户系统的复杂性和多样性,云厂商在这方面的努力通常得不到落实。开发框架选择和架构设计开发框架设计是多云架构的核心,也是最抽象的部分。华为云主推ServiceComb作为微服务框架,阿里云主推HSF和Dubbo作为微服务框架,其他国外云厂商大多选择SpringCloud作为微服务框架,还有其他不同的语言和框架可以选择。虽然mesher等技术为多语言协同工作提供了良好的支持,但为了最大限度地利用框架特性,帮助快速构建稳定可靠的业务系统,选择微服务开发框架仍然必不可少。基于大体思路,多云架构希望在华为云上使用ServiceComb运行时,在阿里云上使用HSF运行时,并支持SpringCloud运行时。要完成这个目标,首先需要了解微服务运行时框架的运行时和主要组件。对于大多数中台系统来说,框架运行时的依赖一般是RPC框架,基于RPC框架的服务治理能力,包括服务注册发现、熔断容错、限流等机制。将业务逻辑的核心代码与微服务框架的能力解耦是设计的第一步。上图展示了基本的逻辑架构。业务核心:在技术选型上使用了Spring和SpringBoot。ServiceComb、HSF、SpringCloud等微服务框架的技术基础都可以基于Spring和SpringBoot技术栈构建。在逻辑架构下,微服务代码需要分层,主要包括以下三个目录:microservice-api:定义了微服务的接口。该目录包含接口定义(interface)和数据结构定义(models)。为了支持不同的微服务框架,对接口定义和数据结构定义都有一定的要求。microservice-service:业务逻辑实现代码。microservice-endpoint-servicecomb:作为ServiceComb发布的微服务项目。microservice-endpoint-hsf:作为HSF发布的微服务项目。microservice-endpoint-springcloud:作为SpringCloud的微服务项目发布。这种代码分层实现的核心关键是API设计,以及业务逻辑实现和服务发布的解耦。API设计需要满足不同微服务框架的设计需求。这就涉及到RPC编解码的基础。RPC编解码器一般分为语言无关(跨平台)和语言相关(不跨平台)。比如HSF和Dubbo默认的codecs都是语言相关的,只能支持JAVA程序之间的通信。ServiceComb默认使用Jackson编解码器或protobuffer编解码器。这两种方法都是基于OpenAPI2.0定义的,并且可以是语言无关的,SpringCloud相对复杂一些。它是一种混合编码格式,可以通过灵活的序列化来定制以满足多样化的需求,也可以严格遵循Jackson和OpenAPI标准。通常,与语言无关的编解码器可以完全包含与语言无关的编解码器要求。因此,在定义API时,需要做到与语言无关,以保证API在不同的微服务开发框架下都能得到最好的实现。由于本文描述的是工程实践,并没有详细讨论跨平台设计的原则,下面列出一些接口设计的指导原则:使用简单的类型(如Integer、String、Boolean等)来定义参数或返回值.或者使用包含简单类型并符合JavaBean规范的POJOBean来定义参数或返回值。尽量不要使用接口、抽象类、具有多个实现的基类、模板类作为参数或返回值。与运行环境强相关的对象不作为接口参数或返回值。比如HttpServletRequest、RpcContext、InvocationContext、ResonseEntity等。以上原理从用户的角度来说是非常容易理解的。界面语义清晰,没有歧义。您可以直接通过接口定义了解接口的参数个数以及如何传递参数,无需提供额外的文档或查看源代码。通过接口定义生成文档和swagger定义是有好处的。在实际项目中,开发者需要了解microservice-api是微服务之间的接口定义,接口设计需要考虑数据的序列化和反序列化。这与内部接口设计不同。为了减少业务实现逻辑的重复,增强内聚性,内部接口设计会使用更多的抽象和继承进行逻辑封装。内部接口的数据结构也会包含一些额外的控制逻辑。例如,数据库访问层的数据结构提供了延迟加载等机制。当访问getter方法时,实际上是调用代理对象的getter方法。因此,需要一些数据结构转换逻辑,将内部数据结构转换成外部接口的数据结构,从而保持服务与内部接口之间的接口清晰,防止内部数据结构被用作参数或返回值,造成内部信息泄露,造成不可预知的处理结果。api示例interfaceLoginService{SessionInfologin(Stringusername,Stringpassword);}publicclassSessionInfo{privateStringsessionId;privateStringusername;}service示例@Service@PrimarypublicclassLoginServiceImplimplementsLoginService{publicSessionInfologin(Stringusername,Stringpassword){//dologin}}ServiceCombEndpointLoginServiceEndpoint”)publicclassLoginServiceEndpointimplementsLoginService{@AutowiredprivateLoginServiceservice;publicSessionInfologin(Stringusername,Stringpassword){returnservice.login(username,password);}}客户端:@BeanpublicLoginServicegetLoginService(){returnInvoker.createProxy(SERVICE_NAME,"LoginServiceEndpoint",LoginService.class);}或者@RpcReference(microserviceName=SERVICE_NAME,schemaId=”LoginServiceEndpoint”)privateLoginServiceloginService;HSFEndpoint示例服务端:@HSFProvider(serviceInterface=LoginService.class,serviceVersion="1.0.0")publicclassLoginServiceEndpointimplementsLog在服务中{@AutowiredprivateLoginService服务;publicSessionInfologin(Stringusername,Stringpassword){returnservice.login(username,password);}}客户端:@HSFConsumerprivateLoginServiceloginService;从上面的代码示例来看,Endpoint层需要在逻辑上尽可能简单,实现所有的逻辑,对于Service层,只包含接口声明,或者包含必要的数据结构转换逻辑。以上方法演示了RPC接口声明。您还可以添加REST标记(SpringMVC或JAXRS)以支持REST接口。遗留系统的重构策略大多数公司的现有系统都运行在某个云上。推翻现有系统并开始新设计通常不是一个好主意。遗留系统的改造需要遵循不断迭代、继承改造的思想。以阿里云系统向华为云系统的改造为例,将原来基于HSF开发的微服务应用,改造为基于ServcieComb开发的微服务应用。按照之前的大体思路,首先需要将原项目中强依赖HSF的代码部分分离出来,建立如下目录结构:microservice-api:定义了微服务的接口。该目录包含接口定义(interface)和数据结构定义(models)。为了支持不同的微服务框架,对接口定义和数据结构定义都有一定的要求。microservice-service:业务逻辑实现代码。microservice-endpoint-hsf:作为HSF发布的微服务项目。这个过程比较简单,不涉及任何业务逻辑的调整,只是代码目录结构的改变和POM依赖的调整。调整完成后,可以对现有功能进行简单的自动化验证,确保项目的自动化测试用例能够通过。调整后的项目功能与遗留系统保持一致,拥有无损运行的系统,对于功能对比和问题发现非常有帮助。项目调整后,可以添加华为云项目:microservice-endpoint-servicecomb:发布为ServiceComb的微服务项目。本项目可以复制microservice-endpoint-hsf,将POM依赖改为ServiceComb的内容,将发布的接口(Endpoint)调整为ServiceComb的发布方式。项目调整后,可以用一段代码构建两个可执行的jar包,两个jar包分别部署在华为云和阿里云上。这个过程的工作量需要通过原始代码本身的复杂度、API接口的标准化程度、代码的大小来评估。如果原有代码结构复杂,api定义不规范,工作量会明显增加。使用组织良好的代码,这个过程会非常容易。实际的改造项目,有的一天就能完成,有的则需要一两个月的时间。获取产品的业务结构、代码量,简单识别现有代码的技术栈和开发方式,有助于有效评估工作量。遗留系统改造的核心工作在于梳理microservice-api中的接口定义。由于HSF不是跨语言开发框架,可能不支持接口定义中使用的数据结构ServiceComb。以支持跨语言的框架的接口定义为基准(一般可以通过IDL、WSDL或OpenAPI描述的接口定义来定义接口,如gRPC、WebService、ServiceComb),其他框架实现这个界面。服务架构适配的核心关键。数据库适配所有业务系统都使用数据库。每个云厂商都支持多种数据库形式,包括MySQL、Postgre、Oracle等,此外还有分布式数据库,分库分表等特性,支持数据仓库。虽然在连接池管理、事务管理、ORM框架等方面有大量的开源开发框架,如dbcp、springtransaction、MyBatis等,但仍然不能覆盖所有的应用场景。适配多云架构对业务代码开发有如下建议:尽量使用符合ANSISQL标准的SQL语句。例如MySQL在case、Columnreference、Limit、Groupbystatement等方面都有自己的特殊用法,为了更好的适配多云数据库,除非对业务价值有明显价值,否则应该避免特殊用法,并且没有等效的解决方案。选择广泛采用的ORM框架。比如MyBatis、JPA等,这些框架的支持很广泛,对多数据库的支持也得到了比较充分的验证。在使用数据库有差异的地方,提供了很好的扩展机制。比如使用MyBatis,可以使用DatabaseProvider接口来屏蔽语法差异。在使用JDBC或者JDBCTempate拼接URL的地方,可以通过接口的不同实现返回不同的SQL语句。limit#{stratRow},#{rowCount}limit#{rowCount}offset#{stratRow}限制#{rowCount}偏移量#{stratRow}if>分布式数据库,分库分表,分析型数据库对业务发展有不同的限制。比如唯一索引的使用限制,修改分库分表键的限制等。对于这些场景,尽量满足这些限制,调整业务实现方式,避免陷入问题为了满足一个不重要的场景的需求,在一些分布式场景下无法解决,无法适配其他云厂商的数据库。缓存适配所有厂商都支持Redis作为缓存,但是在Redis的发展道路上有不同的分支。一个分支是Proxy集群模式,一个分支是Redis原生集群模式。Redis也提供了两套客户端API,一套是Jedis,一套是JedisCluster。两组支持的命令集不同。比如Proxy集群模式可以很好的支持所有Jedis命令,而Redis原生集群模式只支持JedisCluster命令。Redis原生集群模式不支持很多客户常用的pipeline命令。代理集群可以更好的屏蔽底层服务的差异。如果没有特殊需要,建议用户使用Proxy集群模式。云厂商需要通过Proxy集群模式提供对不同Jedis命令的支持。用户也可以选择Redis的原生集群,在不同的云中也是支持的。用户在业务场景中可以避免使用原生集群不支持的命令,从而可以部署在多云环境中。总结一下,缓存多云支持的最佳实践:及时升级ClientAPI版本,使用比较新的ClientAPI,只使用JedisCluster提供的指令集。消息中间件的适配与数据库和缓存相比,消息中间件的适配不够规范。虽然早期JAVA提出了JMS等标准,但是目前主流的消息中间件都没有提供JMS接口的实现。阿里的RocketMQ和华为基于Kafka的DMS接口不一致。而且,消息中间件的服务质量也不尽相同。例如消息的有序传递、重复传递等都是通过消息中间件配置来提供的,不受客户端接口的控制。部分客户的业务逻辑依赖于特定消息中间件的机制,因此需要对消息中间件使用的接口和接口行为进行抽象,分析其他云厂商的中间件是否可以提供相应的接口实现并满足相应的服务质量要求,有时候业务代码需要做一些补偿来弥补不同中间件在服务质量上的差异。其他中间件的适配其他中间件包括日志服务器、计划任务服务、对象存储服务等。适配的整体思路是一样的,里面的细节这里就不详述了。多云架构的挑战与建议与协议标准一样,多云架构在给客户带来业务灵活性的同时,也促进了业务系统软件架构和整体软件质量的提升。因为多云架构需要权衡所用的技术点,所以在技术选型上采用更加中立和标准的方案。这些解决方案已经过广泛验证,比使用一些私有功能更稳定。同时,他们可以促进项目开发商。他们之间的技术交流和能力传承。在为客户实现多云架构的实践中,通过架构交流,我们帮助客户梳理了整体架构演进方向,也发现了很多历史问题,对客户代码质量的提升起到了很大的作用。多云架构也面临着许多挑战。首先是短期内企业维护成本和技术成本的增加。需要投入专家解决早期的架构适配问题,构建系统持续演进的框架。多云系统的运行也会增加业务数据的同步,以及如何协调不同云上的系统。只有当客户的多云系统相对独立,没有数据共享和业务交互场景时,才相对简单。多云系统也会影响客户的交付效率。不同云的持续交付方式差异较大,运维人员的经验不同,会降低日常效率。因此,多云架构更像是一个“备胎”,客户的主营业务仍由单一的云提供商提供。多云架构为客户比较不同云厂商的服务质量提供了非常准确的参考,客户可以更准确地选择优质供应商。微服务框架层的抽象是多云架构中最难的部分,也是大多数开发者最难掌握的一层。ServiceComb微服务开发框架作为参考接口规范,可以很好的适配HSF、SpringCloud等框架。定义接口时,可以将其作为基线先测试ServiceComb,再测试HSF、SpringCloud等适配。是一款非常好的新界面开发和历史系统迁移的中间产品。