Q&A

Biz-SIP业务中台使用中的常见问题:

1. 数据库操作只能局限在sink层服务中进行吗?

A:不一定,在考虑数据库操作时,应注意以下几点:
1、数据库连接是有限的,一般总共不能超过1000个,而微服务由于并发和服务治理,导致应用多起,对数据库连接的占用也是比较多的,从这点来考虑,建议数据库操作尽量往sink层服务集中。
2、但是对于CQRS(Command Query Responsibility Segregation)模式来说,查询(Query)操作实际上并不受限于数据库的连接数限制,这是可以通过在读写master库周边建立只读slave库来解决的,所以对于查询操作实际上可以封装到基础设施层,这样source层、app层、sink层服务都能直接调用。CQRS模式中的Command命令操作,还是建议集中在sink层服务中的。
3、如果有只服务于source层的数据库,例如针对移动APP终端TSM管理的数据库,由于只被source层调用,那么对这个库的操作,都应该在source层的渠道接入服务中调用。

2. sink层服务如何切分?

A:针对sink层服务,需要进行合理的切分,既不能划得太粗,也不能划得太细,坦白说,对一个系统来说,没有标准的划分答案。但在划分时,可以遵循以下原则:
1、按业务来划分:如果一个sink服务中的业务涉及到二个业务部门或业务体系,在可能的情况下,尽量划分成二个sink服务。反过来说,如果二个sink服务几乎都是一个业务部门来管,可以考虑合并成一个sink服务。
2、按数据库来划分:如果一个sink服务中的数据表,可以完全拆分成二个数据库,这二个数据库之间的关联关系很小或完全分离,从理论上来说,可以拆分成二个sink服务。反之,如果二个sink服务只能共用一个数据库,可能就有合并的必要了。
3、按分布式事务来划分:如果在app服务编排时,二个sink服务经常被编排在一起,而且都是Command命令操作,涉及到比较难处理的分布式事务,需要考虑通过补偿服务来满足分布式事务的完整性,从省事角度,可以考虑把这二个sink服务合并成一个,以避免分布式事务的处理。
4、按第三方应用接入来划分:每个调用第三方应用,都是一个独立的Sink服务,另外在调用第三方应用的Sink服务中,原则上要把相关数据库更新的操作切到外面,到另一个Sink服务中去处理。

3. 什么时候使用向前补偿,什么时候使用向后补偿?

A:向前补偿是异常后尽力让服务完成的模式,向后补偿是异常后马上对前面已经完成的服务进行取消操作。
具体在服务异常处理过程中,采用哪种模式,关键在于对方系统提供的服务接口能力:
1、对方系统支持服务补偿接口:在对方系统服务超时后,可以采用向后补偿机制来保障服务的最终一致性。
2、对方系统提供服务状态查询接口:在对方系统服务超时后,可以采用向前补偿机制来保障服务的最终一致性。

4. app层延迟服务和sink层rabbitmq模式调用都是异步的,如何选择使用?

A:延迟服务和rabbitmq模式调用sink服务,都是采用RabbitMQ中间件来实现的,在调用后都是异步返回的,不需要同步等待服务的结果,可以避免长时间分布式事务。
但是这二种调用方式有以下区别:

  • app层延迟服务:调用的是app层的app服务,需要封装一个app服务来作为延迟服务的,app服务调用时,通过“AppClientFactory.getDelayAppServiceClient()”来获得调用接口,调用时可以设置调用频次和时间间隔,存在失败重发的机制。
  • rabbitmq模式调用sink服务:调用的是sink层的sink服务,需要在sink.yml中定义,并且把这个sink服务的类型设置为rabbitmq,app服务调用时,是通过“AppClientFactory.getSinkClient()”来获得调用接口的,调用是一次性通过RabbitMQ发送的,不存在失败重发的机制。

所以这二种模式会根据场景,有选择使用:

  • app层延迟服务:一般用于分布式事务的补偿交易,以及有明确失败重发要求的消息通知场景。
  • rabbitmq模式调用sink服务:用于系统内部服务之间的异步解耦,以避免产生长时间分布式事务,提高系统处理时效,这种场景是系统内的,网络通讯能保障通畅,一般不需要失败重发机制。

一句话总结,app层延迟服务用于触发补偿交易和失败重发的场景,sink层rabbitmq模式调用sink服务是用于服务异步解耦。

5. 高并发系统开发时,需注意的要点?

A:在开发高并发系统时,主要瓶颈在于:

  • 微服务并发导致数据库连接数达到极限:由于高并发导致的微服务数量剧增,一个并发微服务一般会绑定数据库连接池中的一个连接,如果处理不好,会导致数据库连接池中的连接数很快达到极限,导致微服务无法获取数据库连接。
  • 数据库连接迟迟不释放导致数据库连接达到极限:由于在微服务中处理不当,引起调用其它微服务和第三方应用同步等待时间过长,使数据库连接利用率不高,很快达到连接数极限。

所以在开发高并发系统时,就注意以下几点:

  • Source模块、App模块原则上不对数据库进行更新操作:由于Source模块会调用App模块,App模块又会调用Sink模块,Sink模块中又会调用第三方应用,导致调用的同步等待,如果在Source、App模块中进行数据库更新操作,可能会导致数据库长事务,使数据库连接很快消耗完毕。
  • Source模块、App模块可以进行数据库查询操作,但在高并发部署时,应把这些模块的数据库连接切换成只读数据库。
  • 有调用第三方应用的Sink模块,原则上不对数据库进行更新操作:调用第三方应用,同样会导致数据库长事务。
  • 有调用第三方应用的Sink模块,建议也不要进行数据库查询操作,如果真有需要进行数据库查询操作,应在部署时,把模块的数据库连接切换成只读数据库。


6. 交易处理出错异常后,应如何处理?

A:在Biz-SIP平台中的服务处理碰到异常出错后,针对Source层、App层和Sink层,会有不同的处理建议:

  • Source层服务异常:处理最简单,直接向调用端返回错误信息即可。
  • Sink层服务异常:建议抛出BizException异常,并在BizException异常中加入code、message甚至extMessage,让App层来统一处理。
  • App层服务异常:Sink层抛出BizException异常,以及App自身服务都会导致App层服务异常,这种服务异常如何应对,下面会具体介绍。

App服务出现异常后,一般有二种处理方式:
一种是直接触发补偿服务,补偿服务有向前补偿和向后补偿二种,这一般是通过调用应用层的延迟服务来实现,具体请参见延迟服务的具体介绍,这里不再展开说明。
另一种是直接抛出BizException异常,并建议在BizException异常中加入code、message甚至extMessage,让Source层来直接返回错误信息给调用端;同时Biz-SIP平台在App服务抛出BizException异常后,会向RabbitMQ交易日志队列发送一条失败的交易通知(前提是要打开交易日志开关:application.yml中设置“bizsip.rabbitmq-log”,可设置为success、suspend、fail),开发者可以写一个RabbitMQ消息队列消费者应用,侦听消息参数如下:

  • exchange:exchange.direct.bizsip.log
  • routing key:key.bizsip.log

接收到的交易日志,为Map数据类型,约定如下:

KEY键 值类型 值说明
type int 0-App服务成功,1-App服务失败,2-App服务挂起,3-Sink服务成功,4-Sink服务失败
request BizMessage App服务的最初请求报文
response BizMessage App服务的最终响应报文

开发者可以在收到交易日志消息后(response中存放的BizMessage会带有错误异常的code、message和extMessage),对失败的消息,进行个性化处理,有以下几种处理方式:

  • 通过调用应用层的延迟服务来触发补偿流程;
  • 通过程序代码处理来自动进行错误修复;
  • 通知运营人员,尽快进行人工干预。

建议在交易日志消息队列消费者应用中,对所有失败类型的交易日志,都要进行妥善的处理。

上一页