在酒店行业,由于各类系统太过分散,进行整合势在必行。公司系统要与来自不同提供商的各类系统进行交互,这些系统的应用程序接口(API)也各不相同。此外,随着系统中录入的酒店客户增加,涉及的情况也越来越多,必须对其进行管理。由于这一行趋向互联化,资产管理系统(PMS)逐渐成为各大酒店的核心系统,而整合更是重中之重。
要想提供适用于酒店行业的软件解决方案,必须建立起与PMS供应商的双向集成系统。其中的挑战在于:我们需要大规模构建与管理这些联网的内容,并应对涉及多家酒店、多个PMS系统的情况。想要实现这些整合,有几种办法,本文将会介绍一个用于集成系统基础构建的简单架构设计方法。随着酒店发展,投资回报率(ROI)也会得以增加,而这种方法就使用到了微服务。
微服务是什么?
企业软件设计的思想领袖Martin Fowler曾为微服务下过一个全面的定义:
在过去几年中,“微服务架构”(Microservice Architecture)一词被广泛用于描述软件设计的某种特殊方式,即设计出一套独立的、可部署的服务。尽管这种架构风格尚无明确的定义,但在围绕着业务能力、自动化部署、终端智能、语言及数据的非集中控制方面,却有某些共同特征。
本质来讲,微服务实际就是若干很小的软件组件,各个组件只专注于一件工作做好。不同于大型整体式的应用,使用微服务的应用各个部分可以拆开,每个部分只负责指定领域中每部分只需要在指定领域内专注管理一项功能。
正因如此,它们是各自独立且自治的。一项服务的变化也不会影响其他服务,不需要求其他部分一同进行变化。所以,当你慢慢壮大成熟时,不需要担心一旦有变化会影响到其他微服务。
微服务的特点:小而专注、低耦合、高内聚
为何微服务如此强大?
微服务架构能提供众多好处,主要有:
可扩展性
根据不同的属性,单一的系统需要同多个PMS实体进行融合。假设需要与1000个属性进行融合,那么即使它们运行的是同一供应商提供的同一PMS系统,也需要对1000个不同的集成进行显示管理。为了添加更多的复杂性,这些实体可以是来自于不同的供应商。
当你加入更多的PMS实体和属性后,这一规模扩展得更大。如果你运行的是一个整体应用程序,那么你需要衡量所有事宜,因为它们是一个大整体。尤其是在访问量高峰期间,要想知道性能瓶颈在哪比较困难,而此时微服务会有更多的透明度。
如果你利用的是微服务,那么在运行过程中是哪个服务出现了性能问题就一清二楚,而你也可以轻松地调整它们的容量(基础硬件),无需为正常运行的其他服务增加容量。
弹性设计
酒店PMS系统可能会出现故障或有性能问题,但不会影响系统的性能或正常运行时间。
可以按需部署微服务,想部署多少都可以。部署的越多,容错性以及对变化的管理手段就越多。
独立技术栈(Tech Stack Independence)
你可以有多个技术栈,每项服务也配备了最适合的技术栈。你的顾客资料一般会在存储关系型数据库内,而相关请求却会放在非关系型数据库(NoSQL)中。
此外,没有一个特定的技术栈是长期适用的,毕竟,技术栈有很多个。
添加、变更、消除特性及重构
微服务是一些很小的服务,通常只有几百行的代码。
由于这些代码的内聚性使得它们简单易懂,也就是它们每个服务都运行在自己的进程中。
其中一个的变化对其他的服务不会有直接的影响。
删除整个微服务系统也比较容易,基本没有什么风险。
部署
酒店想要提供额外的服务,但必须在系统正常、无故障的情况下才能实现,比如正常将客户登记的信息以及更新状态的请求发送给PMS系统。
回滚也比较容易。假使过程中出现错误,相对于在整体单一式的数据库中回滚整个系统,通过自己的数据库回滚恢复到上一个微服务的操作也比较简单。
另外,对整体单一式的应用来说,部署下一个版本非常痛苦,即便只是新增一个单一特性,也需要对整个程序进行部署,而且一次性部署所有事宜也是很具有风险性的。
注意:如果只是与一个PMS集成,微服务就是大材小用了,但如果在大规模集成的情况下,微服务架构的好处就体现出来了。
传统方法:整体单一式的应用程序
当然,整体单一式的应用对开发新产品和开展新业务来说是比较有利的,因为它的操作简单。但在此期间,你仍需了解目前所在的领域,以及将它们集成整合到一起的方法。运用整体单一式的应用在开发和部署时更简单,也更方便模拟预订服务,以及在客户资料的微服务版块内实现顾客资料模块的设计。
不过随着公司的发展,整体规模也会迅速增长;因此随着规模的增大,系统的运行也越来越困难,需要增加新的特性和代码。而随着系统内容的增加,系统逐渐会不受控制,从而不能很好地执行运维。由于整体单一式的应用通常既不内聚也不耦合,变更系统时需要更多的回归测试,以确保新特性不会破坏系统的其他部分。
另外,由于代码之间互相依赖,会增加故障诊断与调试的难度。在更新服务时,需要对共享的基础设施代码执行修改,如果中间出现bug就会出问题。而且,这么大的代码库也很难在短时间内让新晋的开发人员熟练掌握。
此外,它对部署也有影响:应用程序越大,启动的时间就越长。也许新增更多的服务,并对其进行复制十分简单,但要记得我们只有一个单一的数据库。而且不仅如此,系统的一些部分可能会需要使用更多内存,而其他的部分则需要更多的CPU。因此,如果无法分别权衡各个组件的需求时该怎么办?没错,只能增加更多的新服务器——但这种做法的成本极高。所以,一旦有一定规模的公司,都会想要将系统分解成微服务。
架构概况
现在我们要来解释一下采用微服务架构所带来的长期性效益。不过,我们先来了解下微服务架构的设计细节。最重要的一点就是分离:集成系统应互相独立。举例来说,你的核心系统独立于X物业内运行的物业管理系统,同时也独立于Y物业运行的系统。
要想实现这种分离需要在核心系统和所有物业管理集成系统之间使用一个连接器,即中间件。中间件由两个方面组成:消息队列和background
worker。
可用于实现服务的例子:
消息队列:RabbitMQ,IronMQ等
后台worker:IronWorker,AWS Lambda等
消息队列为系统间提供异步通信。发送者系统(你的系统)发布了一个消息到队列中,之后消息就会留在那里,直到稍后有订阅过队列的后台worker来处理。之后,后台worker会负责处理这则消息(解析内容)并利用PMS
API进行管理集成,同时将数据保存到了中间件的数据库内。
注意:后台worker可以是像AWS Lambda的云服务,也可以是Java或Windows服务器内部开发的程序。下面我们将会详细对其介绍。
值得注意的是,消息队列遵循的是先进先出的原则(FIFO),因此处理队列中所有的消息都会按照它们输入的顺序。假设你有多个队列,发布到X队列的信息稍后会比Y队列中的信息优先处理。这一点在设计的时候需要考虑进去。不过,如果酒店的系统是PMS,那就不需要担心这些,因为它会在云或本地的系统中运行。
这项服务不仅仅是一个CRUD包装器,因为它还控制着与预订相关的所有生命周期事件。如果需要指定预订一间房、新增一位陪同客人或登记入住的话,就需要发送一个恰当的请求给同一worker。
现在我们根据Martin的描述来逐条分析一下微服务主要的几个特征,以及我们的架构又是如何实现这些特征的:
1.围绕业务能力的组织
在如何与PMS集成过程中,每个worker都负责实现一块逻辑。我们可以在一家资产产业的同一个PMS实例中接入多个worker,在另一家酒店添加与同一个PMS(相同运营商)相连的更多worker,也可以在其他产业中添加与其它PMS相连的其它worker。
因此,假如我们需要修改与某些API互动的方式,只需单独对其中一个worker进行新版部署即可,完全不会影响到其它worker。我们可以让一个worker处理预订事宜,让另一个处理顾客资料档案事宜;可以使用Linux的crontab来安排一些后台worker,按照指定日程来循环执行任务;令其它一些worker持续运行,并在收到消息时立即处理。其它后台worker也可以唤醒核心系统的API,以便插入或更新其自PMS那里收集到的新消息(比如从PMS获取或读取数据,将之载入核心系统)。
在Sam Newman所著的《构建微服务》一书中,他指出“基于较为小型的数据库工作的较小型团队生产率更高”——通过微服务就能实现。
不仅生产率的问题,将团队或个人从一个微服务切换到另一个(共享同一个数据库)也是可以的。
而且由于长时间在同一个项目中工作的团队或个人可能会产生局限性,这种做法还能鼓励创新:让团队在产品与项目上互换,可能会造就数以千计的新点子。
2. 自动化部署
微服务的部署需要以自动化形式实现,原因为何?首先是因为微服务数量太多,如果手动部署,不但很容易出错,也很浪费时间。具体情况要取决于所拥有的微服务数量,而且每个服务必须分别、独立的发布。
注意,这里说的是部署微服务的新版本,并不涉及新的worker实例——worker已经在运行中了,但需要部署新的代码版本。举例说明:
假设要集成1000家产业,其中有500家使用运营商1所提供的PMS(即PMS_1),另有500家使用运营商2所使用的PMS(即PMS_2)。
除了运营商之外,由于背景十分相似,这里各个PMS实例中的worker数量很可能也是差不多的,除非我们想要添加更多相同类型的worker。为了简单起见,我们假设每个PMS实例中有5个worker,一个负责预订,一个负责顾客档案等。
由于PMS_1的API与PMS_2的API不同,与PMS_1集成的预订服务其中的代码就和与PMS_2集成的预订服务代码不同。
在这1000家产业中,有5000个worker,其中:
PMS_1有2500个,500个负责预订工作,500个负责顾客资料工作,500个X worker,500个Y
worker,500个Z worker,与PMS_1集成,每家产业各一;
PMS_2有2500个,500个负责预订工作,500个负责顾客资料工作,500个X worker,500个Y
worker,500个Z worker,与PMS_2集成,每家产业各一。
假设我们对与PMS_1集成的预订服务做了代码修改,通过测试后准备发布。
假设源代码数据库只有一个,各个微服务也使用了持续集成工具,现在需要将代码部署到500个worker上,也就是与PMS_1集成的500个预订服务worker。
其次,使用微服务的目的之一就是为了敏捷灵活,因此我们需要自动化。这就是持续集成与持续交付(CI与CD)大显身手的时候了:
CI即需要开发人员一天数次将代码集成到共享数据库的开发实践,提交就会触发build,如果build失败就会向所有人发送警告。其中的关键在于,及早发现提交出现的问题(也即是代码的问题)。如果build成功,就会部署到应用服务器上,并触发持续交付(CD)。
CD即确保上面build成功的内容可以快速部署到生产环境中的实践,首先将应用部署到与生产环境特性相同的staging环境中,再点击“部署”按钮即可将应用部署到生产环境中。这里最棒的是:由于只需点击一个按钮,这样实现时就无需打断软件工程师的工作。
可用于执行CI/CD的服务样例包括:Atlassian Bamboo、TeamCity、Jenkins等。
不过,不要将持续交付与持续部署相混淆,本文不会深入讨论这个问题,不过PuppetLabs有一篇文章是讨论持续交付与持续部署的不同点的,值得一读。
还要注意:微服务在部署时比整体单一式的应用更安全,因此实现自动化也更容易。
3. 端点的智能化
后台运行的worker封装了与PMS集成的逻辑,如果需要变更逻辑,或者变更PMS的API,我们只需修改一个地方即可——但不是在主系统中(主系统需要与下游API的变更隔离)。
此外,各个PMS都有自己的API,因此要将与各个API通讯的逻辑与核心系统相隔离。
4. 语言与数据的分散控制
每个微服务都有自己的技术堆栈,因此我们可以运用技术异质性。
比如,需要改善特定组件的性能时,选择能够实现需要性能的技术堆栈即可。新的服务并不依赖于旧的技术定义,而且会在适合的时候使用新的语言或技术。
各个worker可使用不同的技术构建:worker 1使用Java语言,MySQL数据库,负责处理顾客资料;worker
2使用C#语言,NoSQL数据库,负责处理顾客留言。要记得:它们是彼此独立的。
我们需要考虑集成的问题,比如在实践中如何互相集成。你是否有计划要部署RESTful API来返回JSON,或者与XML对话的SOAP
API呢?
现在我们来深入探讨一下中间件的问题。
中间件
中间件将系统和我们与之交互的多个资产管理系统隔离开来,由消息队列与后台worker组成。
中间件不应当保存状态:各个端点的系统,比如你的系统与PMS系统负责保存酒店、顾客资料、预订信息等相关的状态,而中间件只负责创建两个系统之间的映射。
原因在于:我们不希望引入一个保存状态新组件,从而造成一致性问题。而且同一个事务存储在三个不同的系统——即酒店资产管理系统,中间件系统和核心应用中,如果出现bug就会很难集成。在这样的情况下,哪个系统保存的是真实的预订状态呢?
中间件必须为我们统一核心系统与PMS系统的内容提供方法,因此如果在核心系统中有新需求创建,但出于某些原因,比如离线、软件有bug、网络问题等,没能存入PMS,中间件就应当向用户发出警告,并提供重集成的方法。中间件必须为每条信息在队列中的位置,还有每个后台worker的状态提供清晰的可视化效果。让用户了解某条消息出错以及出错原因,并提供重试机制。
在中间件数据库的顶层要有一个缓存层,以便能更为迅速地访问常用对象,比如城市代码、信用卡类型等。
使用微服务所带来的挑战
在构建任何软件时都会有挑战,特别是大规模的集成系统时。在《构建微服务》一书中,Newman提醒我们“在大规模实现中,出错在所难免”,在部署微服务时也是如此。
我们要接受这一现实——无论硬盘、网络等等都会出错,处理多个独立服务的错误也非常困难。分布式与异步架构在部署和debug时都很难实现,我们需要查看分布在多个实例中的日志,查看分布式事务来了解为什么会出现奇怪的状态。如果在同步流程中出错,进行上一个状态的回退是很困难的。由于经常会并行工作,找到出错点也是很困难的,而且由于可能有竞争条件介入,也会难以管理。
确保微服务大规模实现中的一致性是另一个挑战。想象一下,一个服务管理顾客资料,另一个管理预订服务。如果有新顾客第一次预订你的酒店,预订微服务会创建新的预订记录,而顾客资料微服务需要创建新的顾客资料。如果顾客资料出现bug,没能成功创建新的顾客资料呢?如果没能正确执行管理,最终就只剩一个不与任何顾客资料相关联的单独预订记录。在大规模的情况下,这种情况很难追踪与管理。
异步分布式架构可能会导致其它问题:假设系统向事务队列发出特定类型的请求,导致worker崩溃,而且又添加了从同一个队列拉取消息的多个worker,来加快处理速度。第一个worker从队列拉取消息,然后挂掉了,请求超时锁定,原本的消息被丢回队列。然后第二个worker要从队列中拉取同一个消息,结果一样,也挂掉了。
另一个挑战在于,我们必须持续监控数百个服务的重新部署,从而导致有需求促发专门的DevOps资源或团队来管理如此数量巨大的服务。
当有许多远程调用的时候,由于是通过网络连接的,还需要考虑整体的系统性能。我们都知道,网络是不可靠的,可能会导致数据包延迟或丢包等等。此外,各个系统之间的消息也并非实时传达的:向消息发布一条消息之后,隔一阵子才能得到处理。
最后,通过微服务有效地实现版本控制也是很困难的,最终需要改变服务的接口。如何进行管理呢?
在各个架构中的处理方法都有所权衡,不但微服务中存在挑战,每种方法也存在挑战。管理多个PMS的大规模集成时,使用微服务的好处要远大于付出。
考虑到大规模部署的经济效益:
在微服务的部署中,一些比较成本如下:
在大规模部署中,100个不同的PMS集成可能需要100个服务器。
在单一整体式的方法中,这些服务器要持续运行。
而在微服务中,可以按需唤醒微服务,并在不需要的时候关闭。
使用类似AWS Lambda或者IronMQ之类的云服务时,AWS所提供的按需供应系统可以让我们按照需求来应用,从而更有效地控制自己的花费。
长期使用的情况下,微服务更加划算,而且能够更接近实际地管理开销,减少浪费。从未有过如此节省的架构方法。
下面要怎么做?
告别整体单一式架构
经常会有一个问题:“我的应用已经应用了整体单一式架构,是否需要从头重构,以便部署微服务架构呢?”
答案是:不需要。
我们可以一点点地改动,最终告别单一整体式架构。这应当是一个递增式的方案,我们可以从中更深入地了解核心功能,以及它与其它核心功能的互动方式。对服务应有的状态,以及与其它服务的通讯方式要建立起认知,这是很关键的。采用“边学边干”的方式,逐步定义系统的哪个部分应当使用微服务。
随着系统规模的扩大,微服务数量增长,这一方法会让你更灵活、更有效也成本更低地扩展系统。 |