本文整理自神州信息新动力数字金融研究院应用基础架构专家薛春雨在QCon广州2022全球软件开发大会的演讲分享,主题为《银行核心系统多活架构实践》。
随着银行数字化转型的不断深入,对核心系统的双活甚至多活的要求越来越高,传统方案在数据层面的双活有一定欠缺,本分享结合行业的发展情况及我们公司的创新实践,将深入对“单元化”及“本中心优先”两种方案进行深入的剖析及对比,希望能给行业更多的启发,以选择更加合适的解决方案。
以下是演讲实录。
在开始之前,先做一个简单的自我介绍,我来自神州信息,2006 年进入公司一直在从事银行核心系统底层技术架构的研发。2014 年的时候,提出并带领团队研发了公司的分布式技术平台的产品 Sm@rtGalaxy,当然这不仅仅是纯产品的研发,跟我们的核心系统是紧密衔接,我们强调的是技术和业务系统的关联。
虽然今天谈的是银行的多活架构,但其实银行在好多年前的时候,基本只有一个数据中心,后来监管要求必须有备中心,但大部分备中心基本上都是处于冷备状态,这也是造成了机房资源比较大的浪费。
双活的程度及关注点
先看一下银行业在多活发展历程的几个典型方式。第一个我把它总结为“基本的活着”。
“基本的活着”
怎么理解“基本的活着”?银行如果只用一个数据中心,另外一个数据中心做冷备,对银行来说肯定是资源浪费,所以早期一些银行,会把不同的业务系统分别部署在不同的数据中心,比方上图中的第一个数据中心可能以核心系统为主,可能配合一些“支付”这类相对比较重要的系统。第二个数据中心,基本上就是一些相对来说不是非常关键的业务系统。
另外,对核心来说,一般在另外一个机房是有“备“的概念,但是一般不会没事就切过去。如果真的要切的话,可能还要做比较长时间的准备工作,这个就是跟现在谈的 RTO/RPO 差距还是大一些。但是从另外一个角度来说,虽然这个架构已经有些年头了,但他至少两个数据中心都是活着。一般聊到多活,甚至有些行业会缩减到同城双活去谈这个事情,但首先关键的是,两个中心活着是首要因素。
虽然现在像阿里这种大厂,他们经常会谈三地五中心,但是在银行业,现在真正能做到两地三中心的相对来说还比较少一些,尤其在核心系统。大部分银行现在以同城双活为主,比如邮储,在国内银行业有非常大影响力的银行核心,他的异地也是作为备的,同城两个中心是做双活的处理。所以这种模式并不是说比较落后,这是跟当时的背景有关。
“更好的活着”
我们再往前进一步,下面这个模式我把它叫“更好的活着”。
我们这里聚焦在核心系统,比如说现在在广州,同城有两个数据中心,第二个城市假设是在上海。在大部分城商行做的相对好一些的银行,在前些年,下面的核心系统数据库基本上很多还是用的 Oracle。但是对于很多城商行,这两个中心中的第一个是主中心,全量的主数据也存放在这个中心的数据库中,第一个数据中心的应用连接它做读写,同城的另外一个中心也是连着第一个数据中心的数据库做读写。
第二个数据中心的数据库只是做一个备份的概念,如果从 Oracle 技术来说,更多就是 ADG 去做一个备的处理,ADG 肯定是有一些时延或者其他方面的考虑。在同城的话,网络方面相对 1~2 个毫秒还可以。大家能看到,在这个架构里面正常情况来说,假设同城的第一和第二个数据中心,流量各自是 50%的流量,基本上应该有一半的流量的数据访问是要跨机房的,或者跨同城数据中心的。
就比方我们平常的存款交易,业务逻辑里面基本上有 30 次到 40 次的 SQL 访问,这个跟传统的行业差距还是比较大,如果是每一个 SQL 都多 1~2 个毫秒,40 次 SQL 就至少多 40 毫秒。很多银行他们说,一个转账下来就 40 个毫秒,所以有 50%的请求,其实它的单笔交易响应时间相对比较长一些的。但我们还是要结合所在的行业和客户的需求来谈,对于很多银行来说他并不需要非常高的 TPS。
就比如说大一点的一般的城商行,现在的 TPS 基本上能维持在百级到千级之间已经是非常高了,他们平常的交易量相对比较少,在这个情况下即使同城两个中心,比方说第一个中心宕掉了,他把这个数据全部切到备中心去做,他中间涉及到 ADG 的备份,加上把备中心的数据库由备切换为主,中间需要花掉几分钟的时间,就是现在谈的 RTO/RPO 这方面的指标相对要弱一些,但是在前些年,这是完全能满足很多银行业务系统诉求的。
另外像异地的这个备中心完全就是一个冷备,甚至有些行做的好点,可能是热备份的,如果有需要,数据只要能过去了之后直接就切,这也是一种方式。当然这块的 RTO 和 RPO 跟下层选择的数据库有比较大的关系。
“平等的活着”
前面说的同城两个中心的模式,基本上有一个主中心的概念。第二个中心的数据他并不是一个完全的活动,有些客户可能会把一些只读的交易放到备中心去做,这两个数据中心在我们看来,活动的度是不一样的,第一个数据中心是数据与应用全部是读写多活,第二个数据中心,数据层面只支持读操作,这是它最大一个区别。
再往下进化,我把它叫“平等的活着”,同城两个中心假设都是一个应用系统,都是三个节点,五个节点,你的数据库也都是全量的数据,也可以支持读写操作,这样来说,它两个数据中心活动的情况是完全一样的,就是我说的平等的方式(异地我们暂时先放一下)。
我们在五六年前做分布式的时候有这样一个想法,就是两个数据中心都存储的是全量数据,但是之间做双向同步。我们来剖析一下这个方案,如果是同一账户,从第一个中心做一笔交易,同一个时刻(比方说他的家人)从第二个数据中心也做一笔交易的时候,相当于在同一个时间切面下,两个中心同时对一个账户进行更新,这对数据库的双向同步是一个很大的挑战。
我们当初也想到一个方式,假设这是我们所有银行的客户,这半的人入口放在 IDC1 上,另一半放在 IDC2 上,这样看似他们在一个时间点上产生冲突的可能性几乎很小,理论上又往前进了一步。我们再深入分析一下,以转账为例,转账时必须保证一个事务的完整度,就是刚才说的,这边的客户在这边做交易的时候,同时转账转到那边的一个客户,他在这个数据库去更新客户 2 的时候,那边客户 2 本身又在同时处理交易,他还会面临一个碰撞的问题。
要解决这个问题,理论上来说,我们在刚才两边分流的情况下,再往下进行深入:如果同一个时间,同一个客户的账户要做处理的时候,我们可以通过一个队列,或者是通过分布式锁的机制,让它由并行变成一个串行等待的模式去处理,理论上也是完全可以去解决这个问题的。但是说实话,目前在行业内,暂时这个方案是没有客户真正这样去落地的,但是从理论推导层来说,这确实是一种解决方案。
前面简单地花了一点时间把几个典型的双活或者多活的思路探讨了一下,包括一些我们希望达到的目标,但下面还有几个大的解决方案想谈。
在谈具体的方案之前,我们先探讨一下核心系统对多活的“第一性诉求”,很多人在谈这个事的时候,比方说是某某银行用的什么方案、什么架构,所以我们就用什么架构。
我觉得大家可能也听过马斯克提到的第一性原理,我们一定要剖析问题的本质是要做什么,再提对应的解决方案,也许我们会有创新的一些解决方案,并不是说别人怎么做,我们就怎么做,这是我的一个观点。然后我们再看一下,银行在谈双活或者叫多活的时候,到底是要解决什么问题?第一个点就是刚才提到的叫数据中心完全对等,就是你的不同的数据中心,基本上从应用到数据他的活动率都是差不多的,可能流量到时候还可以随时调拨,这样就可以对关注的 RTO/RPO 指标,包括各种异常情况都有非常好的一些应对的基础,这是最大的一个前提。
再往前走一点,在保证上述前提下,你的性能是否 OK?比如说跨中心的 SQL 访问,如果有四五十次,如果从客户角度要求 50 个毫秒搞定,你光网络延迟就 50 个毫秒,那就没得谈。这也是为什么银行的核心系统现在更多谈的是同城多活,异地还是作为一个特殊的考虑,也是跟性能有关。
比如说,现在广州到北京,如果你第三个异地机房在北京的话,广州到北京的转账如果是联机去处理,光广州到北京的网络开销就 20~30 毫秒,这一来回的链路就比较长,然后业务系统在这种跨地域的访问时就必须做一些特殊处理,复杂度一下子就会蔓延得比较高,很多方面都要去考虑这个问题。现在很多行跨异地的联机交易,还是相对做得比较少的,所以说在性能保证方面,首先,同城尽量避免跨中心的 SQL 访问,这是第一个要求。
我们谈多活,活着只是一个最基本的要求,但最高级的要求,本质想解决什么问题?出了故障之后,对现在当下正进行的交易的影响面非常小,或者几乎没有影响。还有就是,如果这个故障过去之后,能马上快速的恢复,又恢复了以前的处理能力,这是一个最极致的要求,并不是说,我必须要有什么方案,本质上由低到高也就这三个层面的诉求。
单元化架构双活实践
下面结合实践来看一下,现在在行业里比较热门的架构实践:单元化。
我相信很多有银行从业背景的,可能对单元化也有所了解,但是一些非银行的领域可能对单元化了解的比较少。
考虑到听众覆盖的行业比较多,我们就什么是单元化,来做一个简单的探讨。大家来看一下右上角那段话,单元化的定义五花八门,大家可能到网上也搜到很多,这是我们接触过的一个客户,他提到的单元化概念。从我的角度,我觉得他写的还是特别好的,能谈到它的本质:就是将业务划分为一个个小的业务单元,每一个单元的功能是完全相同,就在应用层面是完全对等的,但是他只处理一部分数据,所有单元的数据合并一起才是完整的数据;麻雀虽小五脏俱全,每个单元内部都能处理完整的业务流程,这是他对单元化的一个定义。这里提到的“他只处理一部分数据,所有单元的数据合并一起才是完整的数据”,就是分布式理念在数据层面的具体体现,所以单元化的本质上就是数据层分布式,但是它跟我们平常谈的数据层的分布式还是有区别的。
我们看图中的竖线,一个单元的应用只能访问本单元的数据,不能斜着去访问其他那三个分库,相当于把大家完全割裂起来,就是我们以前谈银行架构,谈竖井架构,这有点竖井架构的那种意思,建行以前是南北中心,南中心处理南方的客户,北中心处理北方的客户,如果只分两个分库,这是南方的客户,这是北方的客户。其实建行在很多年前的时候,单元化理念的雏形就已经出现了。
现在在座的应该很多人听过分布式数据库,比如 Mycat、ShardingSphere 等,他们都是解决数据层分布式、解决数据量问题的主要解决方案,但是他们的理念是,尽可能的对业务系统完全透明、无侵入,也就跟以前那样,该怎么写 SQL 就怎么写,直接执行就行了。
但是单元化是强调什么?架构级的解决方案。一个交易进来,他首先要确定在哪个单元里面,比如说像上图那样分 1234 四个单元,如果是第一个客户的帐务要处理,如果一进来进到第 2 个单元时,从这里边是找不到你的客户信息的,执行绝对会报错,所以它最上面有个全局路由,全局路由还要找一个单元寻址,单元寻址可以是很简单一个算法,可以是很复杂的一个算法,有些客户落地的时候还把它跟数据库结合起来,把每一个客户在哪个单元的信息存储在数据库,然后再去查询,这个复杂度相对就比较高了,但关键就是他要做单元寻址,寻址完再到一个单元里面去执行,这就是架构级的解决方案。
但是我们今天的关注话题是多活或者双活,从这个视觉来剖析一下单元化。如果还是刚才那个例子,比如说分成四个单元,每个单元只访问自己的库,假设这是同城两个数据中心,IDC1 和 2,第一个中心有两个单元,第二个中心有另外两个单元,访问的时候,上面的全局路由是横跨一个城市两个中心的逻辑上的全量,这里我们把那个单元寻址给忽略掉了。它进来之后,首先单元寻址算出他是哪个单元,再去转发到某个单元的入口里面去,这是单元化的一个最关键内容,每一个只能访问自己,上层必须由全局路由去控制单元的入口。
我们再看一下,在这里边就是两个中心的应用完全是对等的,数据库的数据都是读和写同时处理,差别只是“你处理你的数据,我处理我的数据”,每个中心基本上是完全对等的一个概念,这也不存在前面说的,它需要跨中心去访问某一个数据库的 SQL,这个从性能角度来说相对也是 OK 的,基本上满足前面谈的前两个诉求。
但是我们再往下看,出了故障之后,该怎么应对?
整体是第一个城市中心多活,另外一个城市做全量备的情况,假设我把整个数据划分为四个单元,上面这个彩色部分是正常运行的情况下,四个单元都在里面,上面有全局路由,但是如果你要做到他出现故障的时候可以快速的切换,对 RTO/RPO 影响都非常低的话,就要做下面这些单元备份的考虑。
就比方说,三四单元,在这边第一个数据中心里边,同时有个备,当然这边一二在那边有个互备的情况,这个是相对比较奢侈的一种方式,就是所有的应用跟数据库全部在同城另外中心都有一个备,异地会把这四个单元全部做一个备的处理,但是这个情况我们再看一下,如果第一个单元出现整体的故障情况下的时候,第二个中心里的备单元会快速拉起,由于同城数据库保证实时同步的机制,之间几乎可以理解为数据库的数据是无差异的,这个时候流量从那边断掉,直接切到这边来,立马就会完成整个第一个单元故障的切换,如果不做人工确认的步骤,RTO/RPO 几乎是都趋近于 0 的。
但是银行很多还是眼见为真,一般还是要做一些确认的动作,但这个控制在分钟级是没有问题的。这里面就是大家能看到,刚才的灰色部分比蓝色部分的还要多,这就带来一个问题:成本高。总体加起来是三倍的资源投入,如果在考虑到微服务化拆分,应用与数据库同样也面临这个问题,资源的数量会更多。
接下来再聊另外一个话题——数据量的问题。
我们假设现在某个行有五个亿的客户,如果底层的数据库用 MySQL,一个 MySQL 数据库,单表上限按生产级五百万考虑,按客户号来拆,如果按照这样来说,算下来基本上拆成一百份,也就是按我们数据拆分的理念是一百个分片。如果是一个分片对一个单元,一个分片用一个数据库承载,这加起来就有一百个单元。大家对这一百个单元不知道有概念没有,如果再考虑上前面提到的异地多活的备份,其实加上备就三百个单元了。
我当年在邮储去实施的时候,刚开始跟他们去谈,他说至少拆分一百多个单元,我说数量怎么这么多?他说我们客户量比较大,按照这样来说他这样算下来,确实要上百个单元,没有问题,邮储也基本上就是五个亿左右的客户,但是最后他们在实施的时候算,每个单元里边有很多的业务系统,每个业务系统又划分很多微服务,每个微服务有多少个实例,每一个微服务又对应一个数据库,他们算下来之后,他们的硬件投入是一个天文数字,他们的机房都装不下那些东西了,所以后续就不断压缩,最后那个单元数基本上控制在二三十个。
我们在谈架构的时候,他不仅仅是个理论层面,跟你的落地、跟你的各个方面都有关联,所以说这也是个妥协的过程,所以才会变成第二种方式。比方刚才的邮储,如果是一百个单元,每个单元里面只访问一个数据分片,但是如果把它缩一下,比方说总共划分十个单元,但我一个单元里边可以控制十个分片的数据,如果是用 MySQL 数据库,一个单库去处理,你可能单表就是五百万,如果说客户和账户数据,你再做一个分表的处理,或者是你里边还可以做单元内再做分库的处理,这可以理解为是一个单元内的二级拆分,这样就会变成一个 10×10,加起来也是一百。如果到时候你每个单元里边处理的数据量确实比较大的,可以按分片再去拆分,就像单元裂变,这样就能满足你的要求。
因为现在没有这么多客户,只预期未来可能到那个客户量,所以前期的数据量比较小,如果一下子扩得很大,那前期的资源投入浪费是比较高的,所以一定要灵活处理,只有这样才能达到一个合理的投入产出比。
我们谈分布式数据库,刚才又谈单元化,单元化是解决什么?单元化就解决数据分布的问题,分布式数据库是啥?也是解决数据分布的问题,当这两个碰撞在一起的时候,这个事情就有意思了。
举一个 TDSQL 的例子,回到我们刚才说的那种方案,还是把数据拆成四个分库的模式,每一个单元只访问自己的分库,但是数据库可以不用单个 MySQL 集群去提供,它是用 TDSQL 大的集群去做,TDSQL 里边可以给每一个单元划分一个租户,每一个单元只访问租户下的这一个数据库,相当于是只带了个帽子,他本质上里边还是四个 MySQL 的集群,只是他在管控层面上来说,由 TDSQL 做统一管理。
这样来说,可能这块对于数据库的管理,包括数据库的故障,包括数据备份都是 TDSQL 帮你去搞定的,这是他的一个特点。然后如果是刚才说的数据量非常大的模式,我在一个单元内还做数据再次拆分,但这个时候,我们就考虑右边那个模式,用分布式的 TDSQL,每一个租户下的数据库也是一个分布式的,但是本质上还是 TDSQL 内部去做分库或者分表的一个处理,但是对应用来说,你是看不到的,但是这个内部的分库分表的策略,还是尽量跟我们上面的单元划分的拆分策略保持完全一致,这样到时候裂变的时候规则是完全一致的,这是从实践角度来考虑的一个点。
这里再花一点时间对单元化架构做一个总结。
单元化架构只在入口做拆分及路由,其他的事都是业务系统自己要处理的。比如说转账,以前的转账可能在一个交易里直接写两个账户的转账 SQL 就可以,现在的转账需要用存入+支取组合去实现,但是存入有可能在第一个单元,支取在第二个单元,这个时候你就要涉及到跨单元的处理,每一次访问的时候,都要去做第二个账户的时候你要判断,他到底在哪一个单元里面去,再去转发到另外一个单元里面去,复杂度比之前要上升。
如果某个交易进来,压根就没有客户号(拆分键),比方我查这一个月开了多少个新账户,他要到所有的单元里面去查一遍,如果到全局路由里面他直接就晕掉,这个没办法处理,怎么解决?业务自己解决,像很多大行,实施到单元化的时候,他都是把很多个单元的数据归集在一个分布式数据库,或者是一个大数据平台,这种交易全部走这边,这个架构的复杂度和投入成本一下子就上去了。
另外就是性能,大家知道每次处理,每次只要涉及到跨账户的处理,都需要跟单元寻址做一次交互,这不可避免的有一些额外开销。另外,大家也感觉到单元寻址特别关键,你不管划分多少单元,如果上层这个单元寻址,或者叫全局路由这块出问题就是个灾难性的,所有的交易器都是无头苍蝇,这块对于行内的科技人员来说是非常重要的,一定要把这块维护好,他的可用性、稳定性、性能,如果路由信息发生变化,还涉及到数据一致性的处理,这些东西都是必须考虑的因素。
当然单元化也有很多好处,比方上图绿色字体的,他是以单元为单位的,更强调应用加数据是一个整体,单独拉出去在任何地方都可以独立处理那部分的交易(当然跨单元的转账是个特殊情况),他最核心的理念是解决这个问题,所以他就是以单元为最小单位,所以说他的扩展、备份、恢复都是以单元为单位处理的,一方面是他相对比较便利,但是投入就比较高。物理隔离性上来说,完全是物理隔离的,这个跟普通的分布式还是有比较大的区别。
跨单元查询的创新解决方案
关于跨单元的查询,我们在实施时有个非常经典的解决方案,是行业的一个创新方式。如图所示有个公共单元,公共单元里边有所谓的聚合查询服务,比方说一个月开多少个账户的查询,我们通过全局路由直接路由到公共单元的聚合查询服务,其通过我们自研的一个分布式数据中间件(Libra),反向到多个单元的数据库中去执行,然后自动在内部做合并,对业务开发来说完全透明,这是我们把传统的分布式跟单元化理念做整合非常好的一个方案。这个在好多客户里边,相对刚才那个方案的投入产出比非常高。
再对单元化简单做个思考,前面提到三个大的诉求,单元化大部分都是满足的,但是是不是就必须用单元化?另外一个就是,是不是所有的银行,不管是工农中建这种大行,还是股份制或其他一些城商行,都可以用这单元化?这个是我希望大家来思考的问题。
轻量的双活解决方案实践
下面说下另外一种比较轻量级的解决方案。假设这是一般谈的标准的分布式架构,微服务应用里面有一个分库分表的组件,如果我们把数据分成四个部分,他直接可以连四个分库。但是我们如果用同城两个中心来处理,每个中心相当于是部署两个分库,应用也可以跨中心的去访问三四分库,但这样一来,同样的从两个中心进来的交易,一半的交易存在跨数据中心的 SQL 访问的可能性,这个就是前面提到的对性能方面的影响,这也是方案里边的一个缺点。
如果我们把它做一些增强呢?
就像刚才说的,可能有一部分进来,比方从第二个中心进来的,当他访问分库一和分库二,他就涉及到跨网络的 SQL 访问,但是如果在最上层网关级那个入口,我一开始就可以做一个判断,如果数据是在分库一、二,立马就转发到中心一的应用上,只多了一次的网络开销,就规避了你下面很多次的 SQL 访问的这种网络开销。
从这个层面来说,稍微做一些改进之后会发现,跨数据中心的 SQL 访问就可以完全避免。对于跨库的这种聚合查询,比如说我查一个月开立的账户,他从应用进来的时候,同时可以直接下发,还可以通过这两个灰色的线,同时下发到三库和四库去,最后内部再自动进行合并,对业务完全透明,就跟刚才的数据中间件解决方案有点类似,这个相比单元化,复杂度就大幅降低,但是我们的诉求都可以满足。
刚才是拿 MySQL 数据库来举例,如果下面放上一个分布式数据库,跟刚才那个单元化是类似的。
如果是 TDSQL,也是多个分库,每个数据中心各两个,如果分库是在本中心的,他的连接也是在本中心去访问,也是一个最短链路。但是如果你要去做跨多个库的查询的时候,他自己内部去做到多个库的并行下发加合并,只是要把分库分表功能下发到数据库层面去处理了,逻辑上是一样,只是上层和下层的概念区别。
另外我们再看,刚才很多人说的几亿的客户必须用单元化。但是我们再想一下,就是刚才说的这种模式,能不能解决上亿级的客户的处理能力?
跟刚才的单元的划分一样的,比方说我们按照这个模式,一亿可能要拆分成 20 个分库,如果 20 个分库,数据库的连接数就比较多,如果我们把它变成四个分库,每个分库里面又有五个分表,跟前面那个举例一样,就是每个中心两个分库,每个里边加上五个分表,其实对于 Sharding-JDBC 或者 TDSQL 这种分布式数据库完全都是很轻松搞定的事情,这样搞定完之后,发现我们前面的第一性诉求,它就都可以满足。这样一来,我们是不是还需要做那么重量级类的解决方案?这个是我们后面需要考虑的一件事情。
再总结一下轻量化方案,数据中心的完全对等没有问题。更好的性能,本中心内部调用,并且没有跨异地的,通过很简单的增强就可以达到这样的效果。故障的影响面,我觉得这个比单元化的影响面还小,比方说我第一个中心三个实例,我有一个实例宕掉,我其他的实例完全可以接管它到第一个分库,第二个分库的交易,甚至我两个中心只要有一个应用是活着,都没有问题。这点上单元里面这几个实例宕掉之后,这个单元立马就摊掉,立马就要做切换,这个方面它比单元化还优,所以说轻量化投入绝对比单元化要小,架构简单还可以维护,对于开发还是透明的,支持亿级数据量,我们为什么要找一个那么重的去解决这个问题?
最后就是抛出几个需要大家思考的问题,如果单元化架构跟分布式数据库配合。我原来有一次跟一个客户在聊,他单元化下面也是挂分布式数据库,最后应用也是连分布式数据库(通过代理连接),代理根据传入的分片键到多个分库去访问,这种方式出现故障切换时特别方便。我问他,你这样从上层,每一层都可以访问多个数据库,每一层都可以斜着去处理,你的单元化的目的到底是什么?他最后突然想,是哦,我们为什么要做单元化?就是为了单元化那三个字吗?当时为了这点,他还考虑了很久。
另外一个就是,如果分布式数据库真的越来越成熟,处理几亿级的数据完全没有问题,我们是不是又有一些新的方案去解决这个问题。
还有第三个,如果纯粹从技术角度来说,是不是只要做到本中心优先,就完全可以满足我们的诉求了,并不是说“必须是这样,必须是那样”,本质上可能就是“本中心优先”解决就可以了。
最后,不管是谈什么问题的时候,我还是比较喜欢用第一性原理去考虑问题,我们还是希望看到它的本质,然后再去结合我们的实际情况去探索、去解决这个问题,并不是说别人怎么样我们就怎么样。
谢谢大家。
评论 1 条评论