基于支付场景下的微服务改造与性能优化腾讯云开发者社区

近年来微服务架构已经成为大规模分布式架构的主流技术,越来越多的公司已经或开始转型为微服务架构。本书不以某一种微服务框架的使用为主题,而是对整个微服务生态进行系统性的讲解,并结合工作中的大量实战案例为读者呈现一本读完即可实际上手应用的工具书。

常见的互联网支付的使用场景主要有以下几种。

在支付场景下实现微服务的最终目标:能够将单体支付系统按业务进行解耦,利用微服务生态来实施支付系统,并且能够保证系统的可靠性和并发能力,建设完整的运维体系以支撑日益庞大的微服务系统。

我们在第2章介绍了领域建模的相关知识,由此可以知道几个关键词:领域、子域、限界上下文。有些读者对领域、子域的概念比较容易理解,但是限界上下文就理解得比较模糊,这里再对这个关键词简单做一下介绍。

可以把限界上下文理解为:一个系统、一个应用、一个服务或一个组件,而它又存在于领域之中。举个生活中的例子:我每天上班都会坐地铁,从家里出发到单位需要换乘三次地铁,分别是5号线、8号线和2号线。那么地铁就可以理解为限界上下文,从5号线走到8号线这个过程就是领域事件,而为了到达目的地中间换乘地铁,这个过程叫作上下文切换。

当我们在工作中遇到一个完整的业务场景时,首先需要识别出一共有哪些领域,根据大的领域再来划分子域,最后将具有相同领域或子域的限界上下文进行归类。正确识别出领域其实是比较难的,需要设计人员前期对业务有大量的调研,有比较深入的了解后才能识别领域。

从图11-1中可以看到整个业务架构图分两大部分,中间的是业务核心领域,两边的是支撑子域。

我们重点介绍中间的部分,每一层就是一个领域,领域中又包括特定子域。

(1)对接业务层:主要是一些业务系统对接支付系统,包括电商业务、互金业务和一键支付三个限界上下文。

(2)统一接入网关层:主要功能是对请求入口进行加解密、分流、限流和准入控制等。

图11-1

(3)产品服务层。

(4)业务服务层:包括五个限界上下文,分别是交易服务、支付服务、退款服务、计费服务和风控服务。

(5)基础服务层。

三、支付场景下微服务架构的详解与分析

使用微服务的核心是业务,没有业务进行支撑的微服务是“虚的”,但只有业务与微服务相结合的思想而没有微服务的架构体系也是无法将微服务落地的,所以本章重点介绍要做好微服务还需要完善哪些技术架构。

下面我们将以一个实际工作中的案例为出发点,分析在中小公司中如何落地微服务。如图11-2所示,左半部分是微服务的业务架构,右半部分是微服务的基础技术架构。

图11-2

3.1 业务架构分析

根据前面介绍的如何根据业务来划分领域可以看到,整个业务架构部分已经完成了领域的划分,我们重点来看服务层。服务层是一个核心域里面包含了多个子域,每个子域都是按功能进行划分的,比如支付中心子域里面包括支付服务、路由系统和银行渠道等限界上下文,这些限界上下文是一个服务,还是一个系统呢?这就要结合康威定律来综合考量团队的规模,小公司创业初期研发人员少,可以将支付中心子域定义为限界上下文,里面包括三个独立模块,分别是支付服务模块、路由模块和银行渠道模块,待人员逐步增加到一定规模后,多个项目组同时修改一个支付中心限界上下文会导致互相影响的时候,就需要将支付中心上升为一个业务领域,而将之前的三个独立模块拆分为独立系统,由不同的项目组分别接管,各自维护各自部署,如图11-3所示。

图11-3

可以看出左边是未拆分前的结构,交易服务想要调用支付模块就必须统一调用支付系统,然后才能调用支付模块,而右边是经过拆分后的结构,这时交易服务可以直接调用支付服务系统、路由系统和银行渠道系统中的任意一个,当然从业务流来讲肯定要先调用支付服务系统。

而数据层是根据业务进行数据库的拆分,拆分原则与应用拆分相同,如图11-4所示。

图11-4

可以看到业务、应用和数据库三者一体,物理上与其他业务隔离,不同应用服务的数据库是不能直接访问的,只能通过服务调用进行访问。

3.2 技术平台详解

当我们将整个支付业务根据微服务理念做了合理划分之后,业务架构的各层次就逐步清晰起来,而微服务架构的成功建设除了业务上面的划分,技术平台和运维体系的支撑也是非常重要的,图11-2的右半部分共分为三个层次,分别是统一平台业务层、微服务基础中间件层和自动化运维层。

1) 统一业务平台层

这一层主要是通用的平台业务系统,包括数据分析服务、商户运营服务、运维管控服务和进件报备服务,它们无法根据业务被归类到某一业务系统中,只能作为支撑域存在,所以放到统一业务平台层供所有业务线共同使用。

2)微服务基础中间件层

微服务本身是一个生态,为了支撑微服务这个庞大的体系,必须有很多基础中间件进行辅助才能使微服务平稳地运行。下面将根据笔者积累的实践经验对图中一些重要的组件进行技术选型方面的介绍,另外图中有很多组件在本书其他章节进行了详细介绍,这里就不再做说明。

目前市面上非常流行Spring Boot+Spring Cloud的微服务框架,这套框架确实是微服务的集大成者,涵盖的范围广,可以支持动态扩展和多种插件。但是作为公司的管理者来说,并不能因为出了新的技术就立刻将公司核心业务用新的技术进行更替,这样在生产上所带来的风险将会非常大。比较合理的做法是,如果公司或部门是新成立的,还没有做技术框架的选型,又想在公司内部推广微服务的时候,尝试使用Spring Boot和Spring Cloud框架,可以节省出公司或部门的很多时间来攻关前端业务,而不需要将更多精力放在如何进行微服务的建设上来。

目前很多互联网公司在生产过程中使用的微服务框架并不是Spring Boot和Spring Cloud,会使用如Dubbo、gRPC、Thrift等RPC框架进行服务治理,而公司内部自己研发出很多微服务的外围组件,比如APM监控系统、分库分表组件、统一配置中心、统一定时任务等。在这种情况下公司内部已经自建了比较完善的基础架构平台就没必要整体更换为Spring Boot和Spring Cloud,否则代价极大,甚至会对公司的业务造成严重的后果。公司发展的策略一般都是以客户(用户)稳定优先,但公司技术也需要更新,可以先尝试在公司边缘业务中使用,达到认可后逐步推广,循环渐进。

笔者在进行微服务改造的过程中实际上是基于原有的Dubbo做的改进,将Duboo和Spring Boot相结合形成服务治理框架。

我们在谈技术选型的时候,不能脱离业务空谈选型,每种消息中间件必定有其优点和不足,我们可以根据自身的场景择优选择,下面笔者结合自己使用的两种类型的MQ简单说一下选型与使用场景。

RabbitMQ是使用Erlang编写的一个开源的消息队列,本身支持很多协议:AMQP、XMPP、SMTP、STOMP,也正是如此,使它变得非常重量级,更适合企业级的开发。RabbitMQ是AMQP协议领先的一个实现,它实现了代理(Broker)架构,意味着消息在发送到客户端之前可以在中央节点上排队。对路由(Routing)、负载均衡(Load balance)或数据持久化都有很好的支持。但是在集群中使用的时候,分区配置不当偶尔会有脑裂现象出现,总的来说,在支付行业用RabbitMQ还是非常多的。

Kafka是LinkedIn于2010年12月开发并开源的一个分布式MQ系统,现在是Apache的一个孵化项目,是一个高性能跨语言分布式Publish/Subscribe消息队列系统,其性能和效率在行业中是领先的,但是原先的版本经过大量测试,因为其主备Partition同步信息的机制问题,偶尔会造成数据丢失等问题,所以更多的应用场景还是在大数据、监控等领域。

目前市面上有很多支付公司都在使用RabbitMQ作为消息中间件,虽然很“重”但是却具有支付行业的不丢消息、MQ相对稳定等特点。缺点则是不像ActiveMQ那样可以使用Java实现定制化,比如想知道消息队列中有多少剩余消息没有消费,哪些通道获取过消息,共有多少条,是否可以手动或自动触发重试等,还有监控和统计信息,目前做得还不是太完善,只能满足基本功能的要求。

接下来我们再来说说消息队列在技术领域的使用场景。

(1)可以做延迟设计。

比如一些数据需要过五分钟后再使用,这时就需要使用延迟队列设计,比如在RabbitMQ中利用死信队列实现。

(2)异步处理。

主要应用在多任务执行的场景。

(3)应用解耦。

在大型微服务架构中,有一些无状态的服务经常考虑使用MQ做消息通知和转换。

(4)分布式事务最终一致性。

可以使用基于消息中间件的队列做分布式事务的消息补偿,实现最终一致性。

(5)流量削峰。

(6)日志处理。

我们在做监控或日志采集的时候经常用队列来做消息的传输和暂存。

目前市面有很多种开源的统一配置中心组件可供使用,如携程开源的Apollo、阿里的Diamond、百度的Disconf,每种组件都各有特点,我们在使用的过程中还需要根据实际情况来综合考量。笔者公司目前采用的微服务架构是Spring Boot+Dubbo的方式,Apollo的架构使用了Spring Boot+SpringCloud的方式,在架构方式上正好可以无缝对接,同时Apollo可以解决同城双活方面的问题,所以从这些角度来看比较适合目前的场景。

由于每家银行提供的业务及产品不同,例如B2C、B2B、大额支付、银企直连、代收代付、快捷支付等,这些产品及服务并无统一的接口,要使用这些产品服务,支付机构只能一家家银行进行接入,当对接的银行通道过多时,每条通道的稳定性就是支付工作中的重中之重,这是涉及用户支付是否成功的关键,也是支付机构支付成功率的重要指标,基于此,要有针对性地进行银行通道稳定性的监控与故障切换系统的建设,如图11-5所示。

图11-5

图11-5是通道监控与切换系统的整体架构,通过在相应组件或应用上面增加Agent监控代理拦截通道的请求情况,经过Collector进行数据汇总,然后将通道评分数据发送给Redis集群,而支付路由系统在进行通道选取的时候会从Redis集群中获取通道的评分及通道相应的配置项进行综合评定从而选取合适的通道,另外采集所有的监控数据都会存放到InfluxDB中,通过Grafana进行预警展示,如果通道不可用则自动将通道关闭,同时通知研发部门进行问题排查。

四、从代码层面提升微服务架构的性能

很多架构变迁或演进方面的文章大多是针对架构方面的介绍,很少有针对代码级别的性能优化介绍,这就好比盖楼一样,楼房的基础架子搭得很好,但是盖房的工人不够专业,有很多需要注意的地方忽略了,在往里面添砖加瓦的时候出了问题,后果就是房子经常漏雨、墙上有裂缝等各种问题出现,虽然不至于楼房塌陷,但楼房已经变成了危楼。

判断一个项目是否具有良好的设计需要从优秀的代码和高可用架构两个方面来衡量,如图11-6所示。

图11-6

优秀的代码是要看程序的结构是否合理,程序中是否存在性能问题,依赖的第三方组件是否被正确使用等。而高可用架构是要看项目的可用性、扩展型,以及能够支持的并发能力。可以说一个良好的项目设计是由两部分组成的,缺一不可。

在实战的过程中,不同的公司所研发的项目和场景也不一样,下面主要以支付场景为出发点,从代码和设计的角度总结一些常见的问题。

1)数据库经常发生死锁现象

以MySQL数据库为例,select......for update语句是手工加锁(悲观锁)语句,是一种行级锁。通常情况下单独使用select语句不会对数据库数据加锁,而使用for update语句则可以在程序层面实现对数据的加锁保护,如果for update语句使用不当,则非常容易造成数据库死锁现象的发生,如表11-1所示。

表11-1

时  间

会话A

会话B

事务开始

Select * from test where age = 10 for update

事务开始

Select * from test where age = 20 for update

Select * from test where age = 20 for update此时事务一直等待会话B

Select * from test where age = 10 for update数据库报错:Deadlock found when trying to get lock; try restarting transaction

在上述事例中,会话B会抛出死锁异常,死锁的原因就是A和B两个会话互相等待,出现这种问题其实就是我们在项目中混杂了大量的事务+for update语句并且使用不当所造成的。

MySQL数据库锁主要有三种基本锁。

当for update语句和gap lock、next-key lock锁相混合使用,又没有注意用法的时候,就非常容易出现死锁的情况。

先看一段伪代码:

3)滥用线程池,造成堆和栈溢出

Java通过Executors提供了四种线程池可供我们直接使用。

JDK提供的线程池从功能上替我们做了一些封装,也节省了很多参数设置的过程。如果使用不当则很容易造成堆和栈溢出的情况,示例代码如下所示。

以上代码的场景是每次请求过来都会创建一个线程,将DUMP日志导出进行分析,发现项目中启动了一万多个线程,而且每个线程都显示为忙碌状态,已经将资源耗尽。我们仔细查看代码会发现,代码中使用的线程池是使用以下代码来申请的。

在高并发的情况下,无限制地申请线程资源会造成性能严重下降,采用这种方式最大可以产生多少个线程呢?答案是Integer的最大值!查看如下源码:

既然使用newCachedThreadPool可能带来栈溢出和性能下降,如果使用newFixedThreadPool设置固定长度是不是可以解决问题呢?使用方式如以下代码所示,设置固定线程数为50:

可以看到采用的是无界队列,也就是说队列可以无限地存放可执行的线程,造成大量对象无法释放和回收。

其实JDK还提供了原生的线程池ThreadPoolExecutor,这个线程池基本上把控制的权力交给了使用者,使用者设置线程池的大小、任务队列、拒绝策略、线程空闲时间等,不管使用哪种线程池,都是建立在我们对其精准把握的前提下才能真正使用好。

4)常用配置信息依然从数据库中读取

不管是什么业务场景的项目,只要是老项目,我们经常会遇到一个非常头疼的问题就是项目的配置信息是在本地项目的properties文件中存放的,或者是将常用的配置信息存放到数据库中,这样造成的问题是:

比较合理的解决方案之一:使用统一配置中心利用缓存对配置信息进行统一管理,具体的实现方案可以参考《深入分布式缓存》这本书。

5)从库中查询数据,每次全部取出

我们在代码中经常会看到如下SQL语句:

这句SQL从语法上确实看不出什么问题,但是放在不同的环境上却会产生不同的效果,如果此时我们的数据库中状态为init的数据只有100条,那么这条SQL会非常快地查询出来并返回给调用端,在这种情况下对项目没有任何影响。如果此时我们的数据库中状态为init的数据有10万条,那么这条SQL语句的执行结果将是一次性把10万记录全部返回给调用端,这样做不仅会给数据库查询造成沉重的压力,还会给调用端的内存造成极大的影响,带来非常不好的用户体验。

比较合理的解决方案之一:使用limit关键字控制返回记录的数量。

6)业务代码研发不考虑幂等操作

幂等就是用户对于同一操作发起的一次请求或多次请求所产生的结果是一致的,不会因为多次点击而产生多种结果。

以支付场景为例,用户在网上购物选择完商品后进行支付,因为网络的原因银行卡上面的钱已经扣了,但是网站的支付系统返回的结果却是支付失败,这时用户再次对这笔订单发起支付请求,此时会进行第二次扣款,返回结果成功,用户查询余额返发现多扣钱了,流水记录也变成了两条,这种场景就不是幂等。

实际工作中的幂等其实就是对订单进行防重,防重措施是通过在某条记录上加锁的方式进行的。

针对以上问题,完全没有必要使用悲观锁的方式来进行防重,否则不仅对数据库本身造成极大的压力,对于项目扩展性来说也是很大的扩展瓶颈,我们采用了三种方法来解决以上问题:

7)使用缓存不合理,存在惊群效应、缓存穿透等情况

我们在项目中使用缓存通常先检查缓存中数据是否存在,如果存在则直接返回缓存内容,如果不存在就直接查询数据库,然后进行缓存并将查询结果返回。如果我们查询的某一数据在缓存中一直不存在,就会造成每一次请求都查询DB,这样缓存就失去了意义,在流量大时,可能DB就“挂掉”了,这就是缓存穿透,如图11-7所示。

图11-7

要是有黑客利用不存在的缓存key频繁攻击应用,就会对数据库造成非常大的压力,严重的话会影响线上业务的正常进行。一个比较巧妙的做法是,可以将这个不存在的key预先设定一个值,比如“key”“NULL”。在返回这个NULL值的时候,应用就可以认为这是不存在的key,应用就可以决定是继续等待访问,还是放弃掉这次操作。如果继续等待访问,则过一个时间轮询点后,再次请求这个key,如果取到的值不再是NULL,则可以认为这时候key有值了,从而避免透传到数据库,把大量的类似请求挡在了缓存之中。

看完上面的缓存穿透方案后,可能会有读者提出疑问,如果第一次使用缓存或缓存中暂时没有需要的数据,那么又该如何处理呢?

在这种场景下,客户端从缓存中根据key读取数据,如果读到了数据则流程结束,如果没有读到数据(可能会有多个并发都没有读到数据),则使用缓存系统中的setNX方法设置一个值(这种方法类似加锁),没有设置成功的请求则“sleep”一段时间,设置成功的请求则读取数据库获取值,如果获取到则更新缓存,流程结束,之前sleep的请求唤醒后直接从缓存中读取数据,此时流程结束,如图11-8所示。

图11-8

这个流程里面有一个漏洞,如果数据库中没有我们需要的数据该怎么处理?如果不处理请求则会造成死循环,不断地在缓存和数据库中查询,这时就可以结合缓存穿透的思路,这样其他请求就可以根据“NULL”直接进行处理,直到后台系统在数据库成功插入数据后同步更新清理NULL数据和更新缓存。

我们在使用缓存组件的时候,经常会使用缓存过期这一功能,这样可以不定期地释放使用频率很低的缓存,节省出缓存空间。如果很多缓存设置的过期时间是一样的,就会导致在一段时间内同时生成大量的缓存,然后在另外一段时间内又有大量的缓存失效,大量请求就直接穿透到数据库中,导致后端数据库的压力陡增,这就是“缓存过期导致的惊群效应”!

缓存的最终一致性是指当后端的程序在更新数据库数据完成之后,同步更新缓存失败,后续利用补偿机制对缓存进行更新,以达到最终缓存的数据与数据库的数据是一致的状态。

常用的方法有两种,分别是基于MQ和基于binlog的方式。

(1)基于MQ的缓存补偿方案。

这种方案是当缓存组件出现故障或网络出现抖动的时候,程序将MQ作为补偿的缓冲队列,通过重试的方式机制更新缓存,如图11-9所示。

图11-9

说明:

问题点:

如果更新Redis失败,同时在将数据发到MQ之前应用重启了,那么MQ就没有需要更新的数据,如果Redis对所有数据没有设置过期时间,同时在读多写少的场景下,那么只能通过人工介入来更新缓存。

(2)基于binlog的方式来实现统一缓存更新方案。

第一种方案对于应用的研发人员来讲比较“重”,需要研发人员同时判断据库和Redis是否成功来做不同的考虑,而使用binlog更新缓存的方案能够减轻业务研发人员的工作量,并且也有利于形成统一的技术方案,如图11-10所示。

图11-10

说明:

可以看到这种方案对研发人员来说比较轻量,不用关心缓存层面,虽然这个方案实现起来比较复杂,但却容易形成统一的解决方案。

问题点:

这种方案的弊端是需要提前约定缓存的数据结构,如果使用者采用多种数据结构来存放数据,则方案无法做成通用的方式,同时极大地增加了方案的复杂度。

8)程序中打印了大量的无用日志,并且引起性能问题

先来看一段伪代码:

通过上面的代码,发现了以下需要注意的点:

合理地日志打印,可以参考如下格式:

在程序中大量地打印日志,虽然能够打印很多有用信息帮助我们排查问题,但日志量太多不仅影响磁盘I/O,还会造成线程阻塞,对程序的性能造成较大影响。在使用Log4j1.2.14设置ConversionPattern的时候,使用如下格式:

在对项目进行压测的时候却发现了大量的锁等待,如图11-11所示。

图11-11

可以看出在该方法中用了synchronized锁,然后又通过打印堆栈来获取行号,于是将ConversionPattern的格式修改为%d %-5p %c [%t] - %m%n后,线程大量阻塞的问题解决了,极大地提高了程序的并发能力。

9)关于索引的优化

只要列中包含NULL值都不会被包含在索引中,复合索引中只要有一列含有NULL值,那么这一列对于此复合索引就是无效的。所以我们在设计数据库时不要让字段的默认值为NULL。

MySQL查询只使用一个索引,如果where子句中已经使用了索引,那么order by中的列是不会使用索引的。因此数据库默认排序可以在符合要求的情况下不使用排序操作;尽量不要包含多个列的排序,如果需要,最好给这些列创建复合索引。

以下操作符可以应用索引:

m 大于等于;

m Between;

m IN;

m LIKE 不以%开头。

以下操作符不能应用索引:

m NOT IN;

m LIKE %_开头。

4.2 从整体架构的角度看

1)采用单体集群的部署模式

当团队和项目发展到一定规模后,就需要根据业务和团队人数进行适当拆分。如果依然使用单体项目做整体部署,则项目之间互相影响极大,再加上团队人员达到一定规模后,没有办法进行项目的维护和升级。

2)采用单机房的部署方式

现在互联网项目对稳定性的要求越来越高,采用单机房部署的风险性也越来越高,像黑客恶意攻击、机房断电、网线损坏等不可预知的故障发生时,单机房是无法提供稳定性保障的,这就需要互联网企业开始建设同城双活、异步多活等确保机房的稳定性。

3)采用Nginx+Hessian的方式实现服务化

Hessian是一个轻量级的Remoting on HTTP框架,采用的是Binary RPC协议。因为其易用性等特点,直到现在依然有很多企业还在使用Hessian作为远程通信工具,但Hessian并不具备微服务的特点,只作为远程通信工具使用,而且Hessian多偏重于数据如何打包、传输与解包,所以很多时候需要借助Nginx来做服务路由、负载和重试等,而且还需要在Nginx中进行配置,也不能动态对服务进行加载和卸载,所以在业务越来越复杂,请求量越来越多的情况下,Hessian不太适合作为微服务的服务治理框架,这时就需要Spring Cloud或Dubbo了。

4)项目拆分不彻底,一个Tomcat共用多个应用(见图11-12)

图11-12

注:一个Tomcat中部署多个应用war包,彼此之间互相牵制,在并发量非常大的情况下性能降低非常明显,如图11-13所示。

图11-13

注:拆分前的这种情况其实还是挺普遍的,之前一直认为项目中不会存在这种情况,但事实上还是存在了。解决的方法很简单,每一个应用war只部署在一个Tomcat中,这样应用程序之间就不会存在资源和连接数的竞争情况,性能和并发能力提升较为明显。

5)无服务降级策略

举个例子来说明什么是服务降级,我们要出门旅游但只有一只箱子,我们想带的东西太多了把箱子都塞满了,结果发现还有很多东西没有放,于是只能把所有东西全部再拿出来做对比和分类,找到哪些是必须要带的,哪些是非必需的,最终箱子里面放满了必需品,为了防止这种情况再次发生,下次再旅游的时候就可以提前多准备几只箱子。其实服务降级也是类似的思路,在资源有限的情况下舍弃一些东西以保证更重要的事情能够进行下去。

服务降级的主要应用场景就是当微服务架构整体的负载超出了预设的上限阈值或即将到来的流量预计超过预设的阈值时,为了保证重要的服务能正常运行,将一些不重要、不紧急的服务延迟或暂停使用。

6)支付运营报表,大数据量查询

我们先来回顾一下微服务的数据去中心化核心要点:

通过上述核心要点可以看到,微服务中关于数据的描述是去中心化,也就是说要根据业务属性独立拆分数据库,使其业务领域与数据库的关系是一一对应的。我们还是以支付业务场景为例,单体支付项目进行微服务改造后,业务架构如图11-14所示。

图11-14

可以看到将单体支付项目进行微服务改造后增加了多个服务项目,我们可以把每个服务项目都理解为一个限界上下文,每个服务项目又对应一个数据库,这样数据库由原来适应单体支付系统的大库拆分成了多个独立的数据库。问题来了,对于后台运营统计来说这就是噩梦的开始,因为运营报表经常会跨业务进行统计和汇总,在原有运营系统上面做报表会给运营人员额外增加巨大的工作量,需要逐库进行统计,然后进行汇总。

凡事都有两面性,微服务给我们带来去中心化高度解耦的同时,也会带来报表数据及历史数据无法统一汇总和查询的问题,这时我们就需要从各个服务数据库中抽取数据到大数据平台做数据集中化,如图11-15所示。

图11-15

通常大数据平台也会和每个服务的读库配合使用,大数据平台存放的往往是大而全的数据。可以把大数据平台理解为一个数据仓库里面存放若干年的数据,研发人员可以根据数据量的大小及业务情况合理利用服务的读库,这样也可以减轻查询大数据平台的压力。比如用户要查询某个服务一周内的订单情况,则可以直接从读库中进行查询,这样既可以查询到最新的订单详细信息,也可以充分发挥读库的作用。如果用户要查询半年以上的数据,因为数据量大的原因历史数据早已经被迁移走,这时可以在大数据平台进行查询。

7)运维手动打包和上线

微服务架构的顺利实施还需要强有力的运维做支撑,这就相当于一辆宝马车表面看上去特别豪华,但里面装的却是老旧的发动机。这时就需要将DevOps在全公司推广,让自动化运维和部署成为微服务的“发动机”。

五、微服务架构中常见的一些故障分析技巧

1)开发者的自测利器——hprof命令

示例程序如下所示。

注:这是一段测试代码,通过sleep方法进行延时。

如何分析程序中哪块代码出现延时故障呢?

在程序中加上如下运行参数:

hprof不是独立的监控工具,它只是一个Java Agent工具,它监控Java应用程序在运行时的CPU信息和堆内容,使用Java -agentlib:hprof=help命令可以查看hprof的使用文档。

我们在用JUnit自测代码的时候结合hprof,既可以解决业务上的bug,又能够在一定程度上解决可发现的性能问题,非常实用。

2)性能排查工具——pidstat

示例代码如下所示。

将示例代码运行起来后,在命令行中输入:

结果如图11-16所示。

图11-16

注:其中TID就是线程ID,%usr表示用户线程使用率,从图中可以看到855这个线程的CPU占用率非常高。

THE END
0.如何诊断和解决手动挡故障诊断和解决手动挡故障,需先明确故障表现,再针对性排查维修。手动挡汽车常见故障多样,比如换挡困难,可能是离合器故障或变速器同步器损坏,要检查离合器踏板自由行程、离合器片磨损程度等;变速器出现异响、跳挡等问题,需拆解检查并更换损坏部件;车辆行驶时抖动、异响,可能是传动轴和万向节的问题,要检查传动轴平衡和万向节jvzquC41yy}/rlfwvq4dqv3ep1pyym46;8706B;354640qyon
1.液压车压不起来?盘点手动液压搬运车常见故障及解决方法手动液压搬运车,车型小巧,载重量大,如今已广泛运用在物流运输等行业。很多人在使用过程中,经常会碰到例如“液压车压不起来”“上升缓慢”等问题,却不知是什么原因导致的。今天瑞氪维尔Raxwell来给大家盘点下手动液压搬运车常见故障及解决方法。 打开有驾APP 看高清速腾大图 jvzquC41o0pqsnc0eun1jwvkerf1B=47:<18B=;6;77799;0jznn
2.硬盘录像机常见故障和硬盘录像机问题解决办法解决方法:更换优质的3c认证电源线。 6、主板的电源atx20插座有虚焊,接触不良 这种故障不常见,但的确存在,主要是在主机正常工作时,左右移动atx20针插头,看主机是否会自动重启。同时还要检查20针的电源插头内部的簧片是否有氧化现象,这也很容易导致接触电阻大,接触不良,引起主机死机或重启。有时还需要检查20针插头尾部jvzquC41o0972mteu0tfv8iqe1jb49>c47:c5>jghfi9f<85h64ivvq
3.手动挡车型出现故障怎么办要是问题依旧存在,那就需要对发动机积碳进行清洗,或者检查汽车传感器、火花塞等零部件的状态。 总之,手动挡车型出现故障并不可怕,只要我们熟悉常见故障类型,掌握正确的处理方法,就能在遇到问题时冷静应对,及时解决故障,保障行车安全与顺畅。 (图/文/摄:太平洋汽车 整理于互联网)jvzquC41o0vdc~yq0eun0ls1z1::4=46;4:649>0jvsm
4.速腾手动挡车型有哪些常见的故障问题?速腾手动挡车型常见故障问题有离合器分离不完全影响换挡、冷车时倒档和一档挂挡难、挂挡困难以及发动机相关故障等。离合器分离行程不足或油管积气会导致分离不完全,影响换挡操作;冷车时,倒档和一档同步机制受低温影响,挂挡不易;传感系统故障等会造成挂挡困难;发动机保养、燃油等方面的问题也可能引发故障。这些问题需针对jvzquC41yy}/rlfwvq4dqv3ep1gtm883677477mvon
5.液压平台车如何应对紧急情况液压平台车作为重要的高空作业设备,广泛应用于建筑、仓储、制造等行业。由于其工作环境的特殊性,操作过程中可能面临各种紧急情况。掌握正确的应急处理方法,不仅能够保障作业人员安全,还能减少设备损坏和经济损失。本文将系统介绍液压平台车在各类紧急情况下的应对策略。 jvzquC41yy}/luynkhz/exr1pg}t1=9;0jznn
6.如何诊断和解决手动挡故障诊断和解决手动挡故障,需先明确故障表现,再针对性排查维修。手动挡汽车常见故障多样,比如换挡困难,可能是离合器故障或变速器同步器损坏,要检查离合器踏板自由行程、离合器片磨损程度等;变速器出现异响、跳挡等问题,需拆解检查并更换损坏部件;车辆行驶时抖动、异响,可能是传动轴和万向节的问题,要检查传动轴平衡和万向节jvzquC41o0vdc~yq0eun0ls1z1::8:46;8744980jvsm
7.Excel序号填充技巧详解,如何快速自动生成序号?•简道云1、Excel序号填充主要依赖“自动填充”功能,可通过拖拽或自定义序列实现;2、借助简道云零代码开发平台(官网地址:https://s.fanruan.com/prtb3),可以实现更智能、高效的数据录入和序列管理;3、熟悉常见故障及其解决方法,有助于提升数据处理效率。 以“自动填充”为例,用户只需在相邻单元格输入1和2,选中这两个单元jvzquC41yy}/lrfpfcuzww3eqo5oduti19936>4
8.移动式冷却风扇平台设计与应用简介:本发明提出了一种移动式冷却风扇平台,用于提供有效的局部冷却解决方案。该装置通过集成风扇、动力系统、散热片和控制系统等核心部件,实现可移动性与高效冷却。其应用场景包括工业生产、建筑工地、室内活动和数据中心,具有灵活性、高效冷却、智能控制和节能环保等优势。本发明还提供了设备的维护和保养指南,以确保其jvzquC41dnuh0lxfp0tfv8|gkzooa<5896:438ftvkimg8igvcomu86644>46?=
9.某某煤矿综合自动化系统操作规程通用制度管理制度煤矿管理(1). 自动化系统维护人员每天对集成平台进行巡检,每周环网设备及线路、电源箱、控制主机等设备巡回检查一次,对各传感器的迁移、标校及检修情况,缆线放挂及回收情况进行检查,发现问题及时处理。 (2).交接班内容包括: ①.设备运行状况。 ②.当班存在的问题及处理情况。 jvzquC41yy}/otfs0qxh1qyon1813;4261691:76727/uqyon
10.自动化控制范文(2)控制系统:控制系统由控制器(PLC)、输出设备、变频器、给水泵、软水泵、滑差电机及阀等组成。包括手动和自动操作部分,手动控制时由操作人员手动控制,在微机上控制变频器、滑差电机及阀等,自动控制时根据检测装置所检测的数据,围绕所控制的参数的理想值对微机发出控制信号经执行部分进行自动操作。 jvzquC41yy}/i€~qq0ipo8mcqyko1;;5694ivvq
11.mini车sos怎么关闭,操作步骤是什么,常见问题如何解决?Mini车SOS『mini车sos怎么关闭,操作步骤是什么,常见问题如何解决?』 是不是一看到MINI车顶那个红色SOS灯亮就心慌?😊 ​​网上方法泛泛而谈,有人说按一下就能关,有人却吐槽长按也没用;MINI车型的设计到底有什么特殊之处?误触了会不会真的呼叫救援?​​ 作为专研MINI车型多年的汽车博主,今天就用2025年最新实车测jvzq<84ck0=:9=3ep1;93
12.电路改造范文10篇(全文)3.1.1 ZYJ7室内控制电路模拟试验方法。在室内ZYJ7电路模拟试验前要做好以下准备工作:一是室内ZYJ7电路有关配线;二是380V提速电源屏的通电试验,核对微机联锁控显软件增加的提速电源屏主副电源、故障报警表示灯;三是按照图1、图3做好模拟试验电路,进行室内电路模拟试验。 jvzq<84yyy4xgwrk0eun1mncpn{hcrcq1
13.视频监控论文通用12篇现有系统采用单核心二层网络结构,在这种结构中,核心层是所有流量的最终承受者和汇聚者。同时,由于视频监控传输数据量非常大,如果接入传输链路出现问题,则与这条链路相关联的所有摄像机的录像无法查看;如果这台核心交换机出现故障,会导致整个监控系统不可用,且故障解决时间相对较长,风险较大。 jvzquC41jmpz0zguj{/exr1jcuxgw483;;90qyon
14.基于Arduino的智能循迹小车PID控制实战项目Arduino Uno作为最广泛使用的开发板,基于ATmega328P微控制器,具备14个数字I/O引脚(其中6个支持PWM输出)和6个模拟输入通道,工作频率为16MHz。其核心优势在于集成化的开发环境(IDE),支持跨平台编程,通过简洁的C/C++语法降低嵌入式开发门槛。开发者仅需配置USB连接后选择对应板型与端口,即可完成程序烧录与串口监控。 jvzquC41dnuh0lxfp0tfv8|gkzooa<78:9>878ftvkimg8igvcomu8674371:?<
15.快科技资讯2023年05月24日Blog版资讯中心快科技5月24日消息,博主数码闲聊站暗示,真我GT Neo6将会搭载高通骁龙8 Gen2移动平台,采用1.5K超窄边框直屏,没有塑料支架,支持144Hz高刷和2160Hz高频PWM调光。 从配置来看,真我GT Neo6最大的看点就是骁龙8 Gen2芯片。这是高通今年最强悍的5G Soc,CPU采用了全新的1+4+3八核架构,包括一个超大核Kryo Prime核jvzquC41pg}t0v~ftk|ft|3eqo5cnxl142842>760jzn