多云架构落地设计和实施方案

“不要把鸡蛋放在同一个篮子里”是一条知名的商业准则,在云平台选择上,不少公司也遵循这样的准则。基于多云平台构筑“业务中台”并非一件简单的事情,须要构建一种快速继承、可持续迭代的路径,帮助总体方案落地。本文以实际项目案例为例,分析项目的架构设计、实施步骤,以及多云架构面临的挑战和机遇。
整体思路
不一样云厂商提供的云服务不尽相同,相同的云服务在功能、性能上也会有或多或少的差别。越是深度使用某个云厂商的云服务,越是难于迁移到其余云厂商。选择本身构建云服务,则技术门槛,维护成本很高。肯定多云架构之后,首先须要在技术栈的选型上作好折中。一个基本的原则是经过业务架构的灵活性,去适配不一样的云厂商,尽量的使用云厂商提供的优秀特性,提高运行于该云平台的业务系统的可靠性,提高总体业务的竞争力。mysql

上面的思路和一些客户常见的思路有显著差异。有些客户选择采用开源软件,搭建本身的PaaS平台;有些客户则彻底采用云厂商的技术栈,开发两套业务系统。这两种方式是两个极端,前者开发和运维难度高,每每因为技术风险评估不足,项目没法如期交付,或者产品竞争力太弱,没有云厂商提供的服务好。后者则须要维护两套系统,代码重复度高,还会被云厂商彻底绑定,失去谈判的筹码,业务发展灵活性下降。还有些客户指望云厂商提供足够兼容性的框架支持,在不改造现有业务系统的逻辑的状况下实现多云部署,云厂商这方面的努力一般因为客户系统的复杂性和多样性得不到落地。spring

开发框架选择和架构设计
开发框架设计是多云架构的核心,也是抽象程度最高的部分。华为云主推ServiceComb做为微服务框架,阿里云主推HSF、Dubbo做为微服务框架,其余国外的云厂商,多数选择Spring Cloud做为微服务框架,另外还有其余不一样的语言和框架选择。虽然mesher等技术为多语言协同工做提供了良好的支持,可是为了最大限度的利用框架特性,帮助快速构建稳定可靠的业务系统,选择一个微服务开发框架仍然是必不可少的。sql

基于整体思路,多云架构指望在华为云上使用ServiceComb运行时,在阿里云上使用HSF运行时,而且支持Spring Cloud运行时。完成这个目标,首先须要对微服务运行框架的运行时和主要组成部分有所了解。对于多数中台系统,对于框架运行时的依赖,通常都是RPC框架,以及基于RPC框架作的服务治理能力,包括服务注册发现、熔断容错、限流等机制。将业务逻辑核心代码,与微服务框架能力进行解耦,是设计的第一步。数据库

上面的图形展示了基本的逻辑架构。api

业务核心:技术选型上使用Spring、Spring Boot。ServiceComb、HSF和Spring Cloud等微服务框架的技术底座,均可以基于Spring、 Spring Boot技术栈来构建。缓存

在逻辑架构下,须要将微服务代码进行分层,包含下面三个主要目录:
microservice-api:定义微服务的接口。该目录包含接口定义(interface)、数据结构定义(models)。为了支持不一样的微服务框架,对于接口定义和数据结构定义会有必定的要求。
microservice-service:业务逻辑实现代码。
microservice-endpoint-servicecomb:发布为ServiceComb的微服务项目。
microservice-endpoint-hsf:发布为HSF的微服务项目。
microservice-endpoint-springcloud:发布为Spring Cloud的微服务项目。服务器

这个代码分层实施的核心关键是api设计,以及业务逻辑实现和服务发布解耦。api设计须要知足不一样的微服务框架的设计要求。这里涉及到RPC编解码的基础。RPC编解码一般分为语言无关(跨平台)和语言相关(不跨平台)。好比HSF、Dubbo的缺省编解码是语言有关的,只可以支持JAVA程序之间的通讯,ServiceComb缺省采用Jackson编解码,或者protobuffer编解码,这两种方式都基于Open API 2.0进行定义,能够作到语言无关,Spring Cloud则相对复杂一些,它是一种混合型的编码格式,能够经过灵活的序列化定制,知足多样性须要,也能够严格遵循Jackson和Open API标准。一般语言无关的编解码能够彻底包含语言无关的编解码要求,所以api定义的时候,须要作到语言无关,才可以保证api可以在不一样的微服务开发框架下获得最好的实现。基于本文是描述工程实践,不详细探讨关于跨平台设计的原理,下面列出一些接口设计的准则:
使用简单的类型(好比Integer, String, Boolean等)定义参数或者返回值。或者使用包含简单类型的,符合Java
Bean规范的POJO Bean定义参数或者返回值。
尽量不使用interface, abstract class, 存在多个实现的基类,模板类做为参数或者返回值。 不使用运行环境强相关的对象做为接口参数或者返回值。好比HttpServletRequest,RpcContext,InvocationContext,ResonseEntity等。session

上面的原则从使用者的视角来看,是很是容易理解的。接口语义清晰,没有歧义,直接经过接口定义就可以理解接口的参数个数以及如何传递,不须要提供额外的文档或者查看源代码。有利于经过接口定义生成文档、swagger定义。数据结构

在实际项目中,开发者须要理解microservice-api是微服务之间的接口定义,接口设计须要考虑数据的序列化和反序列化。这个不一样于内部接口设计。为了下降业务实现逻辑的重复度,加强内聚性,内部接口设计会更多的使用抽象、继承进行逻辑封装。内部接口的数据结构,还会包含一些额外的控制逻辑。好比数据库访问层的数据结构,提供懒加载等机制,当访问到getter方法的时候,实际上调用的是代理对象的getter方法。所以,须要有一些数据结构转换逻辑,将内部的数据结构转换为外部接口的数据结构,以保持服务之间接口和内部接口的界限清晰,防止将内部数据结构做为参数或者返回值,致使内部信息泄露,形成不可预期的处理结果。架构

api示例

interface LoginService {
SessionInfo login(String username, String password);
}

public class SessionInfo {
private String sessionId;
private String username;
}

service示例

@Service

@Primary

public class LoginServiceImpl implements LoginService {

public SessionInfo login(String username, String password) {

// do login

}
}

ServiceComb Endpoint示例

服务端:

@RpcSchema(schemaId = “LoginServiceEndpoint”)
public class LoginServiceEndpoint implements LoginService {
@Autowired
private LoginService service;

public SessionInfo login(String username, String password) {
return service.login(username, password);
}
}

客户端:

@Bean
public LoginService getLoginService() {
return Invoker.createProxy(SERVICE_NAME, "LoginServiceEndpoint", LoginService.class);
}

或者
@RpcReference(microserviceName=SERVICE_NAME, schemaId=”LoginServiceEndpoint”)
private LoginService loginService;

HSF Endpoint示例

服务端:

@HSFProvider(serviceInterface = LoginService.class,serviceVersion = "1.0.0")
public class LoginServiceEndpoint implements LoginService {
@Autowired
private LoginService service;

public SessionInfo login(String username, String password) {
return service.login(username, password);
}
}

客户端:
@HSFConsumer
private LoginService loginService;

从上面的代码示例看,Endpoint层须要作到尽量逻辑简单,实现逻辑所有交给Service层,只包含接口声明,或者包含必要的数据结构转换逻辑。上面的方式演示了RPC的接口声明,还能够加上REST标签(Spring MVC,或者JAX RS),来支持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定义不规范,工做量会显著增长。对于良好组织的代码,这个过程则会很是容易。实际改造的一些项目,有些一天时间便可完成,有些则花费了1、两个月时间。获取到产品的业务结构、代码规模和经过简单的识别现有代码的技术栈和开发方式,可以帮助有效的评估工做量。

遗留系统改造的核心工做在于microservice-api中接口定义的梳理。因为HSF不是一个跨语言的开发框架,所以在接口定义里面使用的数据结构ServiceComb可能存在不支持的状况。采用一个支持跨语言的框架的接口定义做为基准(通常的,能够将接口定义经过IDL、WSDL或者Open API描述的接口定义,好比gRPC、WebService、ServiceComb,),其余框架都实现这个接口,是微服务架构适配的核心关键。
数据库适配

业务系统都会使用数据库。各个云厂商支持的数据库形式多样,有MySQL、Postgre、Oracle等,此外还有分布式数据库,以及分库分表等特性,数据仓库支持等。虽然在链接池管理、事务管理、ORM框架等方面有大量的开源开发框架,好比dbcp、spring transaction、MyBatis等,它们仍然无法覆盖全部的应用场景。适配多云架构对于业务代码开发有以下建议:

尽量使用符合ANSI SQL Standard的SQL语句。好比MySQL在大小写、Column引用、Limit、Group by语句等方面都有本身的特殊用法。为了更好的适配多云数据库,应该避免使用特殊用法,除非对于业务价值具有明显的价值,并且没有对等的解决方案。

选用一个被普遍采用的ORM框架。好比MyBatis、JPA等。这些框架被普遍支持,对于多数据库支持获得比较充分的验证。在使用数据库有差别的地方,提供了很是良好的扩展机制。好比使用MyBatis,可使用DatabaseProvider接口,来屏蔽语法差别。在使用JDBC或者JDBCTempate拼接URL的地方,则能够经过接口的不一样实现,返回不一样的SQL语句。

limit #{stratRow},#{rowCount}


limit #{rowCount} offset #{stratRow}

分布式数据库、分库分表、分析型数据库对于业务开发存在不一样的限制。好比对于惟一索引使用的限制、对于分库分表键的修改限制等。对于这些场景,尽量符合这些限制,调整业务实现方式,避免为了知足某个不重要的场景须要,陷入一些分布式场景没法解决的问题当中去,而没法适配其余云厂商的数据库。

缓存适配
各个厂商均支持Redis做为缓存,可是在Redis发展路径上,有不一样的分支。一个分支是Proxy集群模式,一个分支是Redis的原生集群方式。Redis提供的客户端API也存在两套,一个是Jedis,一个是JedisCluster,两套支持的命令集合不尽相同。好比Proxy集群模式可以很是好的支持全部的Jedis命令,而Redis的原生集群方式只支持JedisCluster命令。不少客户经常使用的pipeline指令,在Redis原生集群方式下不支持。

Proxy集群可以更好的屏蔽底层服务的差别,在没有特殊须要的状况下,建议用户使用Proxy集群模式,云厂商须要经过Proxy集群模式提供对于Jedis不一样指令的支持。用户也能够选择Redis的原生集群,这个在不一样的云产生也都提供了支持,用户能够在业务场景上避免使用原生集群不支持的命令,这样就能够在多云环境上部署。

总结起来,缓存的多云支持的最佳实践:

及时升级Client API版本,使用比较新的Client API,而且只使用JedisCluster提供的指令集合。

消息中间件适配
相对于数据库和缓存,消息中间件的适配的标准性更弱一点。虽然早期JAVA提出了JMS等标准,可是目前主流的消息中间件都没有提供JMS接口的实现。阿里的RocketMQ、华为基于Kafka的DMS的接口均不一致。并且消息中间件的服务质量并不同,好比在消息有序投递、重复投递等方面是经过消息中间件配置提供的,而不受客户端接口控制。有些客户的业务逻辑依赖于特定消息中间件的机制,所以须要对消息中间件使用的接口、接口行为进行抽象,分析其余云厂商的中间件可否提供对应的接口实现并知足对应的服务质量要求,有时候须要业务代码作一些补偿,以弥补不一样中间件服务质量的差别。

其余中间件适配
其余中间件包括日志服务器、定时任务服务、对象存储服务等等。适配的整体思路一致,这里不详细描述里面的细节。

多云架构的挑战和建议
和作协议标准同样,多云架构除了给客户带来商业上的灵活性,还会促进业务系统软件架构的改善,提高总体的软件质量。由于多云架构须要对使用的技术点进行权衡,技术选型上更多采用相对中立和标准的方案,这些方案被普遍验证,相对于使用一些私有特性来讲,更加稳定,同时能够促进项目开发人员之间的技术交流和能力继承。在给客户作多云架构落地的实践中,经过架构交流,帮助客户梳理了总体的架构演进方向,还发现了不少历史问题,对于客户的代码质量提高起到了很大做用。

多云架构也面临特别多的挑战。首先是短时间内企业维护成本的增长和技术成本的增长,须要投入专家解决前期的架构适配问题,为系统持续演进搭好框架。多云系统运行,还会增长业务数据同步,不一样云上系统如何进行协同的问题。只有当客户多云系统相对独立,没有数据共享和业务交互的场景才比较简单。多云系统还会影响到客户的交付效率,不一样云的持续交付方式存在较大的差别,运维人员的体验不一样,会下降平常的效率。

所以,多云架构更多的是准备“备胎”,客户的主要业务仍是以单一云厂商提供。多云架构给客户对比不一样云厂商的服务质量提供了很是准确的参考,客户可以更加准确的选择优质的供应商。

微服务框架层的抽象是作多云架构最难抽象的部分,也是多数开发者最难把握的一层。ServiceComb 微服务开发框架做为参考接口规范,能够很是好的适配到HSF、Spring Cloud等框架。在接口定义的时候,能够采用它做为基线,先测试ServiceComb,再测试HSF和Spring Cloud等适配,对于新接口开发、历史系统迁移都是一个很是好的中间产品。

相关文章
相关标签/搜索