本文假设你已经阅读过往期文章:为什么要重构为微服务重构在重构支付网关中的外部准备RefactorInternalPreparationsinRefactoringPaymentGatewaysUsingMicroservicesArchitecture从横向的角度分解服务,建立微服务之间的关系。本文详细介绍了如何对SSH框架的支付系统进行具体的技术改进。这里不涉及具体的代码编写方法,重点讲解方法论。虽然以SSH(ApacheStruts+Springframework+Hibernate)框架为例,但也适用于各种常用的Web架构(ApacheStruts/SpringMVC/ApacheVelocity+Springframework+Mybatis/Hibernate)。选择起始模块如果遗留系统规模较大,应该如何选择起始点?以支付系统为例。如前所述,支付系统一般包括账户、交易、订单、优惠券、钱包、支付通道、清算结算、支付网关、操作系统等模块。模块众多,如何选择切入点?我们采用的方法是找到外部依赖最少的模块,从那里开始调整。首先,梳理模块依赖关系。从上图可以看出,账户系统处于依赖树的根部,其调整相对容易。保持对外接口不变即可。重构策略重构就像在飞行中更换引擎,必须非常小心。我们采取的战略是:小步快跑,积小胜成大胜。每个改进点需要在1到3天内完成,不超过一周。每一个改进都可以直接上线运行,不需要长期的AB测试。在功能,即对外接口不变的前提下,开始拆分工作。从结构上看,原来的SSH系统是个大工程,所有的代码都是层层堆砌,模块层层叠叠。微服务系统需要将它们分开。直观的拆分方法是根据层级和模块的同构性,将系统拆分成独立的运维系统。这导致了一系列的调整。在SSH架构下,可以自上而下的方式进行重构,保证了每一层的重构输入输出清晰,可测试。总体来说,重构分为三个步骤:参考原系统的DAO层和业务逻辑层,实现基础服务,通常使用RPC。重构对外接口层,调整为调用RPC服务。将原服务的部分流量切掉到新服务试运行。试运行成功,所有流量都被切断了。旧服务已过时。API网关在SSH架构下。API网关一般通过NGINX的rewtite模块实现。逻辑简单,手动维护即可。一旦按照业务拆分了接口层,网关的路由逻辑就复杂了很多,手动维护配置文件的难度急剧增加,需要调整为自动注册和更新路由。即每个服务都需要向API网关注册自己的服务和API,API网关需要自动识别并加载新的路由。针对这个需求,我们开发了一个connector,其工作原理如下:服务启动后,注册到zookeeper。connector监听zookeeper,一旦有变化,就获取服务列表,更新nginx.conf文件。connector更新nginx.conf文件后,执行nginxreload命令使配置生效。负载均衡器将服务发送给nginx,nginx可以根据新的配置执行服务路由。当服务停止时,做类似的事情。第一步是在服务关闭之前从zookeeper中删除该服务。注意一定要在服务关闭前删除,否则会出现服务不可用的错误。服务注册项从zookeeper中删除,只有本地服务没有流量后才能关闭服务。连接器可以部署在与nginx相同的机器上。服务接口调整使用SpringMVC实现的Controller或者使用ApacheStruts实现的Actions需要根据业务进行组合,拆分到具体的项目中。原则上一个项目的接口不要超过5个,避免接口过于复杂。本次调整需要做的工作包括:增加服务注册机制,服务启动时将服务注册到zookeeper上;添加服务退出机制,服务关闭时注销服务;根据企业合并为服务创建相应的项目;将服务依赖的业务逻辑层打包到同一个jar中,作为后续改进的基础。该服务上线,取代现有服务。业务逻辑层调整在springframework框架下,业务逻辑层实现为服务层和DAO层之间的桥梁。对于大多数应用程序,业务逻辑层是一个非常薄的封装层。调整到微服务架构,业务逻辑层有三种处理方式:抽象为一个独立的RPC服务。对于更复杂的业务逻辑,你可以使用这种方式。下沉到DAO层,如果逻辑涉及到很多数据访问操作或者需要事务处理,可以合并到DAO中一起作为RPC实现。上浮到界面层。如果业务逻辑比较简单,也可以浮动。DAO层的重构DAO层的重构工作量比较大。需要将原来访问数据库的逻辑调整为远程RPC调用:为DAO接口开发RPC服务,通过RPC隔离数据访问逻辑;提供DAO的RPC接口客户端,替代原有DAO服务接口的实现。这样,业务逻辑层和服务层调用的DAO调整为RPC调用,实现了数据访问逻辑和业务逻辑的分离。这样在DAO层就可以根据业务需要选择合适的存储数据库。性能优化1、完成上述调整,是万里长征的第一步。接下来就是码农们最钟爱的性能优化了。对于大多数在线应用来说,性能优化的主要任务是选择合适的存储介质来满足性能需求。2.数据可直接写入MySQL或其他持久化存储。这个库也将被称为主库。但如果写性能要求高,可以调整为先写入内存数据库,再同步到持久化存储。3、在线数据访问是指根据ID或其他一些属性值读取1-2条数据。一般不直接从MySQL等持久化存储中获取数据,而是使用couchbase、redis等内存数据库。4.在线数据检索,检索和访问需要分开。按照关键词、时间等条件进行检索,Elastic一般都能满足。线上数据列表,比如最新、最热、推荐等,需要预先计算好数据,放到内存数据库中,比如couchbase或者redis。读取时直接从数据库中提取数据,无法进行实时计算。通过这些步骤,可以优化在线服务的性能。归根结底,瓶颈不应该在数据库或CPU上,而是在带宽上。那么这就是砸钱的商业行为。这种处理带来的最大问题是空间的浪费,是典型的以空间换时间的做法。技术挑战是数据一致性。对于不需要强一致性的需求,这种方式是没有问题的。那么如何在不同的存储之间同步数据呢?主要方法有:5.使用数据库本身自带的同步机制。优点是一般不需要开发。问题是数据库的同步机制,比如MysQL和HBase的复制机制,只支持少数几种备份数据库,这也给数据库本身带来了压力。6.使用公共同步工具,比如阿里的运河。7、使用消息中间件实现数据同步。目前主流的使用消息中间件的方式适用于对数据实时性要求不高的场景。如下图所示,在数据写入服务中,写入完成后,抛出一个消息。其他数据库通过接受消息来更新数据。优点是系统灵活,无论是同一个DC还是跨DC都可以正常工作。通过MQ提供的监控系统也可以了解数据同步的状态。缺点是开发工作量大,数据同步的实时性不高。如果时间不够,上述重构是一个理想的场景。如果时间不够,只能做部分重构。一种方法是改进DAO层。一般来说,重构往往意味着数据结构的改变。另一方面,由于写数据服务比读数据服务少很多,可以采取的策略是:调整DAO服务中的写数据操作,将数据写到新的数据库表中;使用MQ来实现新数据库表和旧数据库表之间的数据同步。见上图。旧DAO服务中读取数据的操作保持不变。毕竟大部分服务对性能的要求都不会太高。只需要对性能要求高的服务进行重构,即可实现读写分离。重建方法如上所述。总之,技术改造的难点在于如何梳理线索。本文也算是介绍一下,欢迎大家踊跃讨论。【本文为专栏作者《凤凰牌老熊》原创稿件,转载请微信联系作者公众号《凤凰牌老熊》转载】点此阅读作者更多好文
