两个服务做远程通讯.现在主流的有两种方式,一个是基于http ,一个是基于RPC.
http是应用层协议,RPC是远程过程调用协议.







 RPC

什么是RPC?

RPC(Remote Procedure Call ——远程过程调用),它是一种通过网络从远程计算机程序上请求服务,而不需要了解底层网络的技术。
分布式和微服务[笔记] - 图1
一次完整的RPC同步调用流程:
1)服务消费方(client)以本地调用方式调用客户端存根;
2)什么叫客户端存根?就是远程方法在本地的模拟对象,一样的也有方法名,也有方法参数,client stub接收到调用后负责将方法名、方法的参数等包装,并将包装后的信息通过网络发送到服务端;
3)服务端收到消息后,交给代理存根在服务器的部分后进行解码为实际的方法名和参数
4) server stub根据解码结果调用服务器上本地的实际服务;
5)本地服务执行并将结果返回给server stub;
6)server stub将返回结果打包成消息并发送至消费方;
7)client stub接收到消息,并进行解码;
8)服务消费方得到最终结果。
RPC框架的目标就是要中间步骤都封装起来,让我们进行远程方法调用的时候感觉到就像在本地调用一样。

如果上面看不懂看下面的
当客户端想要调用远程的服务的时候,客户端会以本地调用的方式调用在客户端的一个所谓的存根,存根(Client Stub)会把方法名和方法参数打包成可以在网络上传输的0101串儿,然后把这个0101串通欧网络传输到服务器端.
服务器也会有服务器的存根(Server Stub),它负责把网络上0101串转化成为方法名和方法参数的等等东西,根据解码后的方法名调用在服务器上某一个具体的服务,然后服务器再把结果通过服务器的存根再通过网络传给我们的客户端存根.
客户端的存根再把结果解码返回给我们真正意义上的调用的客户端(服务的消费方).

RPC框架目的

给客户端调用服务器的过程封装起来,让我们在进行远程调用的服务器(服务的提供者)方法服务,感觉像是在调用自己本地的方法. 这就是RPC框架的目的,不管你用Dubbo还是其它的PRC框架无外乎就是在做上面的事情,把所有复杂的网络调用相关的部分都封装起来.只不过是实现上和性能上各有不同而已.

RPC和HTTP


RPC字面意思就是远程过程调用,只是对不同应用间相互调用的一种描述,一种思想。具体怎么调用?实现方式可以是最直接的tcp通信,也可以是http方式,在很多的消息中间件的技术书籍里,甚至还有使用消息中间件来实现RPC调用的,我们知道的dubbo是基于tcp通信的,gRPC是Google公布的开源软件,基于最新的HTTP2.0协议,底层使用到了Netty框架的支持。所以总结来说,rpc和http是完全两个不同层级的东西,他们之间并没有什么可比性。




传输协议

RPC,可以基于TCP协议,也可以基于HTTP协议
HTTP,基于HTTP协议

传输效率
RPC,使用自定义的TCP协议,可以让请求报文体积更小,或者使用HTTP2协议,也可以很好的减少报文的体积,提高传输效率
HTTP,如果是基于HTTP1.1的协议,请求中会包含很多无用的内容,如果是基于HTTP2.0,那么简单的封装以下是可以作为一个RPC来使用的,这时标准RPC框架更多的是服务治理

性能消耗,主要在于序列化和反序列化的耗时

RPC,可以基于thrift实现高效的二进制传输
HTTP,大部分是通过json来实现的,字节大小和序列化耗时都比thrift要更消耗性能

负载均衡

RPC,基本都自带了负载均衡策略
HTTP,需要配置Nginx,HAProxy来实现

服务治理(下游服务新增,重启,下线时如何不影响上游调用者)

RPC,能做到自动通知,不影响上游
HTTP,需要事先通知,修改Nginx/HAProxy配置


 分布式系统是什么


分布式系统:一个硬件或软件组件分布在不同的网络计算机上,彼此之间仅仅通过消息传递进行通信和协调的系统

这是分布式系统,在不同的硬件,不同的软件,不同的网络,不同的计算机上,仅仅通过消息来进行通讯与协调

这是他的特点,更细致的看这些特点又可以有:分布性、对等性、并发性、缺乏全局时钟、
故障随时会发生。

分布性


既然是分布式系统,最显著的特点肯定就是分布性,从简单来看,如果我们做的是个电商项目,整个项目会分成不同的功能,专业点就不同的微服务,比如用户微服务,产品微服务,订单微服务,这些服务部署在不同的tomcat中,不同的服务器中,甚至不同的集群中,整个架构都是分布在不同的地方的,在空间上是随意的,而且随时会增加,删除服务器节点,这是第一个特性

对等性

对等性是分布式设计的一个目标,还是以电商网站为例,来说明下什么是对等性,要完成一个分布式的系统架构,肯定不是简单的把一个大的单一系统拆分成一个个微服务,然后部署在不同的服务器集群就够了,其中拆分完成的每一个微服务都有可能发现问题,而导致整个电商网站出现功能的丢失。

比如订单服务,为了防止订单服务出现问题,一般情况需要有一个备份,在订单服务出现问题的时候能顶替原来的订单服务。

这就要求这两个(或者2个以上)订单服务完全是对等的,功能完全是一致的,其实这就是一种服务副本的冗余。如果两个服务器部署的代码不同,比如说一个是今年的,一个是去年的,代码不同,那么就不是对等的.不能相互替代的,就代表它没有对等性.

还一种是数据副本的冗余,比如数据库,缓存等,都和上面说的订单服务一样,为了安全考虑需要有完全一样的备份存在,这就是对等性的意思。

并发性


并发性其实对我们来说并不陌生,在学习多线程的时候已经或多或少学习过,多线程是并发的基础。

但现在我们要接触的不是多线程的角度,而是更高一层,从多进程,多JVM的角度(多个项目部署就是多个jvm),例如在一个分布式系统中的多个节点,可能会并发地操作一些共享资源,如何准确并高效的协调分布式并发操作。

分布式锁其实就是解决这问题的。

锁是单节点的话,它能锁住一个jvm的并发性,但是分布式项目就是多个Tomcat多个jvm了,传统的锁就解决不了分布式的架构下的并发问题,缺乏全局时钟.如果是分布式应用,我怎么来判断两个服务器之间都做过那种超时的应用,

缺乏全局时钟


在分布式系统中,节点是可能反正任意位置的,而每个位置,每个节点都有自己的时间系统,因此在分布式系统中,很难定义两个事务纠结谁先谁后,原因就是因为缺乏一个全局的时钟序列进行控制,当然,现在这已经不是什么大问题了,已经有大把的时间服务器给系统调用

如果时间不统一的话,整个集群都启动不起来.

故障随时会发生


任何一个节点都可能出现停电,死机等现象,服务器集群越多,出现故障的可能性就越大,随着集群数目的增加,出现故障甚至都会成为一种常态,怎么样保证在系统出现故障,而系统还是正常的访问者是作为系统架构师应该考虑的。

 分布式系统带来的问题


如果把分布式系统和平时的交通系统进行对比,哪怕再稳健的交通系统也会有交通事故,分布式系统也有很多需要攻克的问题,比如:通讯异常,网络分区,三态,节点故障等。

(一)通信异常


通讯异常其实就是网络异常,网络系统本身是不可靠的,由于分布式系统需要通过网络进行数据传输,网络光纤,路由器等硬件难免出现问题。只要网络出现问题,也就会影响消息的发送与接受过程,因此数据消息的丢失或者延长就会变得非常普遍。

(二)网络分区


网络分区,其实就是脑裂现象,本来有一个交通警察,来管理整个片区的交通情况,一切井然有序,突然出现了停电,或者出现地震等自然灾难,某些道路接受不到交通警察的指令,可能在这种情况下,会出现一个零时工,片警零时来指挥交通。

但注意,原来的交通警察其实还在,只是通讯系统中断了,这时候就会出现问题了,在同一个片区的道路上有不同人在指挥,这样必然引擎交通的阻塞混乱。

这种由于种种问题导致同一个区域(分布式集群)有两个相互冲突的负责人的时候就会出现这种精神分裂的情况,在这里称为脑裂,也叫网络分区。

(三)三态


三态是什么?三态其实就是成功,与失败以外的第三种状态,当然,肯定不叫变态,而叫超时态。
在一个jvm中,应用程序调用一个方法函数后会得到一个明确的相应,要么成功,要么失败,而在分布式系统中,虽然绝大多数情况下能够接受到成功或者失败的相应,但一旦网络出现异常,就非常有可能出现超时,当出现这样的超时现象,网络通讯的发起方,是无法确定请求是否成功处理的。

(四)节点故障


节点故障在分布式系统下是比较常见的问题,指的是组成服务器集群的节点会出现的宕机或“僵死”的现象,这种现象经常会发生。

 分布式系统常见问题处理

(一)分布式系统接口调用顺序性

分布式和微服务[笔记] - 图2

正常情况下用户请求1 ,请求2,请求3 三个请求分别被负载均衡算法在系统A集群的三个机器上执行.如果有更高的要求的话,要求顺序性的话.

1. 方案是让这三个请求都到一台机器上去执行,这样的话就成了排队串行. 还是还得需要考虑一个问题,这台机器可能是并发去执行的话,也有可能三个请求顺序变了.
2. 如果要求百分之百强顺序,就直接搞分布式锁.不过这样系统吞吐量可能会降低.

 分布式事务


分布式和微服务[笔记] - 图3
假设请求系统出现了异常,那么就需要将员工系统和财务系统的操作进行回滚.

(一)分布式事务方案

1.可靠消息最终一致性方案(用的比较多的)

分布式和微服务[笔记] - 图4

直接基于MQ来实现事务。比如阿里的RocketMQ就支持消息事务。

大概的意思就是:
1)A系统先发送一个prepared消息到mq,如果这个prepared消息发送失败那么就直接取消操作别执行了,如果发送成功说明MQ是活着的,MQ能正常工作.
2)如果这个消息发送成功过了,那么接着执行本地事务,如果成功就告诉mq发送确认消息,如果失败就告诉mq回滚消息
3)如果发送了确认消息,那么此时B系统会接收到确认消息,然后执行本地的事务
4)mq会自动定时轮询所有prepared消息回调你的接口,问你,这个消息是不是本地事务处理失败了,所有没发送确认消息?那是继续重试还是回滚?一般来说这里你就可以查下数据库看之前本地事务是否执行,如果回滚了,那么这里也回滚吧。这个就是避免可能本地事务执行成功了,别确认消息发送失败了。


5)这个方案里,要是系统B的事务失败了咋办?重试咯,自动不断重试直到成功,如果实在是不行,要么就是针对重要的资金类业务进行回滚,比如B系统本地回滚后,想办法通知系统A也回滚;或者是发送报警由人工来手工回滚和补偿
也可以B系统事务失败了,通过Zookeeper来通知A系统,让A系统再重新给消息再发送一遍,让B系统再处理一遍.

这个还是比较合适的,目前国内互联网公司大都是这么玩儿的,要不你举用RocketMQ支持的,要不你就自己基于类似ActiveMQ?RabbitMQ?自己封装一套类似的逻辑出来,总之思路就是这样子的

2.最大努力通知方案(用的比较少)


分布式和微服务[笔记] - 图5
这个方案的大致意思就是:

1)系统A本地事务执行完之后,发送个消息到MQ
2)这里会有个专门消费MQ的最大努力通知服务,这个服务会消费MQ然后写入数据库中记录下来,或者是放入个内存队列也可以,接着调用系统B的接口
3)要是系统B执行成功就ok了;要是系统B执行失败了,那么最大努力通知服务就定时尝试重新调用系统B,反复N次(比如说设置10次),最后还是不行就放弃.


收款通知,注册通知,微信支付宝付款等等,就是最大努力通知方案.(貌似是)

比如支付系统,订单页面客户支付成功之后先到微信那里,微信收到订单的钱,此时我支付系统还没这个钱,此时微信发起回调请求,调用我们支付回调的http接口,告诉我们钱到账了,然后支付系统给订单状态改过来,微信在调用回调接口的时候就会非常努力的回调,来回调用5次,这就是最大努力通知型



3.两阶段提交方案(XA方案,很少用)


性能不好,操作数据库的时候,多张被操作的表同时被锁定,当发送commit命令之后需要协调多个表提交事务处理,表被锁定的时间太长了,吞吐量比较差.,降低数据库性能百分之10左右.


如果是一个系统操作多个数据库的时候,这个方案比较常见.不过这种方案在实际的生产环境是很少见的,因为这种操作是不合规的,现在的微服务都是一个大的系统分成几百个微服务,或者几十个微服务,我们的规范是要求每个微服务只能操作自己对应的一个数据库..如果你要操作别的服务对应的库,不允许直连别的服务的库.违反微服务架构的规范的,如果是几百个微服务交叉访问的话,那么就乱套了.这样的一套服务是没法治理的.到时候经常出现数据被别人改错,自己的库被别人用挂掉了.简直是一塌糊涂.

如果是你要操作别人的服务的库,必须是调用别人的服务的接口来实现,绝对不允许交叉访问别人的数据库.



XA方案在Java代码实现是 Spring+JTA就能实现.不过这种分布式事务严重依赖于数据库层面来搞定复杂的事务,效率很低,绝对不适合高并发场景.


分布式和微服务[笔记] - 图6

首先来看下2PC,翻译过来叫两阶段提交算法,它本身是一致强一致性算法,所以很适合用作数据库的分布式事务。其实数据库的经常用到的TCC本身就是一种2PC.

两阶段提交经常会用到分布式事务里面

回想下数据库的事务,数据库不管是MySQL还是MSSql,本身都提供的很完善的事务支持。

2PC就是两阶段提交,它会把整个事务分成两个阶段,第一个阶段叫做准备阶段,事务在请求都会发一个事务管理器,由事务管理器来管理各种各样的分布式事务,你提交 资源1 的时候,会先告诉事务管理器我已经准备好了,当你提交 资源2 的时候,也会告诉事务管理器我已经准备好了.

这个是第一阶段,事务会接收到一个个资源的请求,这个资源可以是数据库,如果是分布式事务的话,这个资源1也好,资源2也好,它都是一个个的事务,资源1或者资源2 会写undo和redo事务日志(undo和redo就是回退和回滚的日志),但是资源1和资源2都不会提交.

我们数据库里面怎么判断我们的资源是应该提交还是回滚?接收到commit就会提交,否则就是回滚.

第二阶段是当事务管理器接收到所有资源(资源1和资源2)的反馈的时候,此时两个资源都没报错啥的(说明执行成功了),此时事务管理器会发送commit命令分别给资源1和资源2,让这两个资源给我一个一个的提交,这个就是第二个阶段.
如果事务管理器发现commit失败了(至少一个资源失败了),它会发送rollback命令给两个资源, 让它们全部都回滚.这就是2PC的一个逻辑.


分布式和微服务[笔记] - 图7

MySQL后面学分表分库的时候会讲到在innodb存储引擎,对数据库的修改都会写到undo和redo中,不只是数据库,很多需要事务支持的都会用到这个思路。

对一条数据的修改操作首先写undo日志,记录的数据原来的样子,接下来执行事务修改操作,把数据写到redo日志里面,万一捅娄子,事务失败了,可从undo里面回复数据。

不只是数据库,在很多企业里面,比如华为等提交数据库修改都回要求这样,你要新增一个字段,首先要把修改数据库的字段SQL提交给DBA(redo),这不够,还需要把删除你提交字段,把数据还原成你修改之前的语句也一并提交者叫(undo)

数据库通过undo与redo能保证数据的强一致性,要解决分布式事务的前提就是当个节点是支持事务的。

这在个前提下,2pc借鉴这失效,首先把整个分布式事务分两节点,首先第一阶段叫准备节点,事务的请求都发送给一个个的资源,这里的资源可以是数据库,也可以是其他支持事务的框架,他们会分别执行自己的事务,写日志到undo与redo,但是不提交事务。

当事务管理器收到了所以资源的反馈,事务都执行没报错后,事务管理器再发送commit指令让资源把事务提交,一旦发现任何一个资源在准备阶段没有执行成功,事务管理器会发送rollback,让所有的资源都回滚。这就是2pc,非常非常简单。

说他是强一致性的是他需要保证任何一个资源都成功,整个分布式事务才成功。

参考:
https://www.yuque.com/docs/share/cf1fefd3-815d-47e8-a25b-47e29d1b4d6a#


优缺点


优点:原理简单,实现方便
缺点:同步阻塞,单点问题,数据不一致,容错性不好

同步阻塞
在二阶段提交的过程中,所有的节点都在等待其他节点的响应(就是等待第一阶段事情都做完了才能接着执行第二阶段,否则都得等着.),无法进行其他操作。这种同步阻塞极大的限制了分布式系统的性能。

单点问题
协调者在整个二阶段提交过程中很重要,如果协调者(事务管理器)在提交阶段出现问题(比如事务管理器死机了),那么整个流程将无法运转。更重要的是,其他参与者将会处于一直锁定事务资源的状态中,而无法继续完成事务操作。

数据不一致
假设当协调者向所有的参与者发送commit请求之后,发生了局部网络异常,或者是协调者在尚未发送完所有 commit请求之前自身发生了崩溃,导致最终只有部分参与者收到了commit请求。这将导致严重的数据不一致问题。
什么是局部网络异常呢?
假如说资源A发送commit命令,资源B发送commit命令,资源C正在准备发送commit命令,忽然断电了,这个就是局部网络异常.


容错性不好
二阶段提交协议没有设计较为完善的容错机制,任意一个节点(事务管理器,多个资源都可能会失败)是失败都会导致整个事务的失败。

4.三阶段提交




由于二阶段提交存在着诸如同步阻塞、单点问题,所以,研究者们在二阶段提交的基础上做了改进,提出了三阶段提交。

三阶段提交和二阶段提交的区别是什么?

1.3PC在第二阶段才写undo和redo事务日志.
假如2PC,资源1,资源2,资源3在写undo和redo的时候,资源1和资源2成功了,而资源3失败死机了, 此时资源1和资源2做的就是无用功(因为资源3死机了,也资源1和资源2写undo和redo也没用了)
而3PC是在第二阶段才写undo和redo,在第一阶段先判断 资源1,资源2,资源3 这三个节点是不是正常运行状态的,如果根本就不是正常运行状态的,那我就没必要执行写undo和redo了.因为写了也是白写.纯粹浪费时间

2.第三阶段协调者出现异常或者网络超时,参与者也会commit


分布式和微服务[笔记] - 图8

第一阶段canCommit

确认所有的资源是否都是健康、在线的,如果第一阶段有一个资源已经不能用了,比如死机等等,其它资源写了undo和redo也是浪费时间.

第二阶段PreCommit

在第一阶段判断所有服务都ok,就会进入第二阶段,可以接收事务请求,这一阶段就可以执行事务了,这时候也是每个资源都回写redo与undo日志,事务执行成功,返回ack(yes),否则返回no

第三阶段doCommit

这个时候协调者发现所有提交者事务提交者事务都正常执行后,给所有资源发送commit指令。

和2PC有所不同的是,他要求所有事务在协调者出现问题,没给资源发送commit指令的时候,三阶段提交算法要求资源在一段时间超时后回默认提交做commit操作。

这样的要求就减少了前面说的单点故障,万一事务管理器出现问题,事务也回提交。

但回顾整个过程,不管是2pc,还是3pc,同步阻塞,单点故障,容错机制不完善这些问题都没本质上得到解决,尤其是前面说得数据一致性问题,反而更糟糕了。

所有数据库的分布式事务一般都是二阶段提交,而者三阶段的思想更多的被借鉴扩散成其他的算法。


参考:
https://www.yuque.com/docs/share/cf1fefd3-815d-47e8-a25b-47e29d1b4d6a#

优缺点

改善了同步阻塞的时间(第一阶段确定了所有资源都是活着的,才去插入redo和undo日志)
改善了单点故障

缺点
同步阻塞,单点故障,数据不一致,容错机制不完善





5.TCC方案

案例:ZJJ_Distributed_Transaction_2020/06/12_19:36:36_7z0om


每个服务对一个接口要提供三个接口,每个服务先走try,任何人try失败了都会走cancel回滚,如果是try走成功了,就会调用confirm.

TCC方案建议用阿里的seata来做.

分布式和微服务[笔记] - 图9

TCC的全程是:Try、Confirm、Cancel。

这个其实是用到了补偿的概念,分为了三个阶段:

1)Try阶段:这个阶段说的是对各个服务的资源做检测以及对资源进行锁定或者预留
2)Confirm阶段:这个阶段说的是在各个服务中执行实际的操作
3)Cancel阶段:如果任何一个服务的业务方法执行出错,那么这里就需要进行补偿,就是执行已经执行成功的业务逻辑的回滚操作

给大家举个例子吧,比如说跨银行转账的时候,要涉及到两个银行的分布式事务,如果用TCC方案来实现,思路是这样的:

1)Try阶段:先把两个银行账户中的资金给它冻结住就不让操作了
2)Confirm阶段:执行实际的转账操作,A银行账户的资金扣减,B银行账户的资金增加
3)Cancel阶段:如果任何一个银行的操作执行失败,那么就需要回滚进行补偿,就是比如A银行账户如果已经扣减了,但是B银行账户资金增加失败了,那么就得把A银行账户资金给加回去(通过业务相关代码.)

这种方案说实话几乎很少用人使用,我们用的也比较少,但是也有使用的场景。因为这个事务回滚实际上是严重依赖于你自己写代码来回滚和补偿了(不同的回滚逻辑是不一样的),会造成补偿代码巨大,非常之恶心。

比如说我们,一般来说跟钱相关的,跟钱打交道的,支付、交易相关的场景,我们会用TCC,严格严格保证分布式事务要么全部成功,要么全部自动回滚,严格保证资金的正确性,在资金上出现问题.

比较适合的场景:这个就是除非你是真的一致性要求太高,是你系统中核心之核心的场景,比如常见的就是资金类的场景,那你可以用TCC方案了,自己编写大量的业务逻辑,自己判断一个事务中的各个环节是否ok,不ok就执行补偿/回滚代码。

而且最好是你的各个业务执行的时间都比较短。

但是说实话,一般尽量别这么搞,自己手写回滚逻辑,或者是补偿逻辑,实在太恶心了,那个业务代码很难维护。



6.本地消息表(很少用)

分布式和微服务[笔记] - 图10
国外的ebay搞出来的这么一套思想





这个大概意思是这样的

1)A系统在自己本地一个事务里操作同时,插入一条数据到消息表(如果都插入成功代表我这里事务执行成功了)
2)接着A系统将这个消息发送到MQ中去
3)B系统接收到消息之后,在一个事务里,往自己本地消息表里先插入一条数据,同时执行其他的业务操作,如果这个消息已经被处理过了(MQ重复发送导致的),那么此时这个事务会回滚,这样保证不会重复处理消息.

4)B系统执行成功之后,就会更新自己本地消息表的状态以及A系统消息表的状态
5)如果B系统处理失败了,那么就不会更新消息表状态,那么此时A系统会定时扫描自己的消息表,如果有没处理的消息,会再次发送到MQ中去,让B再次处理
6)这个方案保证了最终一致性,哪怕B事务失败了,但是A会不断重发消息,直到B那边成功为止.

这个方案说实话最大的问题就在于严重依赖于数据库的消息表来管理事务啥的???这个会导致如果是高并发场景咋办呢?咋扩展呢?所以一般确实很少用.

(二)什么时候会用到分布式事务


一个分布式事务方案,复杂度可能会多10倍,很多情况下系统A调用系统B,系统C,系统D,可能就根本不用分布式事务,如果调用报错会打印异常日志,
每个月也就那么几个bug,很多bug都是功能性的,体验性的,真涉及到数据层面的一些bug一个月也就几个,如果为了确保数据百分之百不能出错,上了几十个分布式事务,那么代码太复杂,性能太差,系统吞吐量性能大幅度下跌.
几乎百分之99的分布式接口调用不要做分布式事务,直接就是监控,比如说如果出错了就发送邮件发送短信等等,记录日志啥的, 然后事儿后快速的定位排查和出解决方案去修复数据.

每个月,或者每隔几个月都会对少量的因为代码的bug导致出错的数据,进行人工的修复数据,比如自己临时手写某个程序,可能要补一些数据,或者删除一些数据等等,或者修改一些字段的值.

比你做一堆分布式事务成本要低很多很多倍.

用分布式事务缺点就是性能和吞吐量下跌,而且潜在bug可能会更多.

像资金 , 交易, 订单业务 这种是一定不能错的,肯定会用分布式事务来保证
如果是像会员积分,优惠券,商品信息等等就不需要分布式事务,直接就是记录异常日志.


(三)LCN

LCN 名称是由早期版本的 LCN 框架命名,在设计框架之初的 1.0 ~ 2.0 的版本时框架设计的步骤是如下,各取其首字母得来的 LCN 命名。
锁定事务单元(lock)
确认事务模块状态(confirm)
通知事务(notify)

LCN默认是3pc事务,也可以替换成TCC事务,详情参考 ZJJ_Distributed_Transaction下的trans-lcn项目.

框架定位
LCN 并不生产事务,LCN 只是本地事务的协调工
TX-LCN 定位于一款事务协调性框架,框架其本身并不操作事务,而是基于对事务的协调从而达到事务一致性的效果。

事务控制原理

TX-LCN 由两大模块组成, TxClient、TxManager,
TxClient 作为模块的依赖框架,提供 TX-LCN 的标准支持,TxManager 作为分布式事务的控制方。事务发起方或者参与反都由
TxClient 端来控制。
原理图:

分布式和微服务[笔记] - 图11

 1.创建事务组
是指在事务发起方开始执行业务代码之前先调用 TxManager 创建事务组对象,然后拿到事务标示 GroupId 的过程。
 2.加入事务组
添加事务组是指参与方在执行完业务方法以后,将该模块的事务信息通知给 TxManager 的操作。
 3.通知事务组
是指在发起方执行完业务代码以后,将发起方执行结果状态通知给 TxManager,TxManager 将根据事务最终状态和事务组的信息
来通知相应的参与模块提交或回滚事务,并返回结果给事务发起方。






 架构模式

1.单体架构以及缺点

所有的功能和程序都打到一个war包,就是单体架构程序.
很多项目都是从单体应用开始的。单体应用比较容易部署、测试,在项目的初期,单体应用可以很好地运行。然而随着需求的不断增加,越来越多的人加入开发团队,代码库也在飞速膨胀。慢慢地,单体应用变得越来越臃肿,可维护性、灵活性逐渐减低,维护成本越来越高。

1业务越来越复杂,单体应用的代码量越来越大,代码的可读性、可维护性和可扩展性下降,新人接手代码所需的时间成倍增加,业务扩展带来的代价越来越大。
2随着用户越来越多,程序承受的并发越来越高,单体应用的并发能力有限。
3、测试的难度越来越大,单体应用的业务都在同一个程序中,随着业务的扩张、复杂度的增加,单体应用修改业务或者增加业务或许会给其他业务带来一定的影响,导致测试难度增加。
4、部署时间长
5、某个应用坏了可能会导致整个程序崩溃
6、扩展能力受限:单体应用只能作为一个整体进行扩展,无法根据业务模块的需要进行伸缩。例如,应用中有的模块是计算密集型的,它需要强劲的CPU;有的模块则是IO密集型的,需要更大的内存。由于这些模块部署在一起,不得不在硬件的选择上做出妥协。
7、阻碍技术创新:单体应用往往使用统一的技术平台或方案解决所有的问题,团队中的每个成员必须使用相同的开发语言和框架,要想引入新框架或新技术平台会非常困难。例如,一个使用Struts2构建的、有百万行代码的单体应用,如果想要换用Spring MVC,毫无疑问切换的成本是非常高的。

2.08 09年的架构模式

分布式和微服务[笔记] - 图12
缺点 多人项目开发出现缺点代码冲突

3.SOA服务化架构模式

大型互联网公司会将项目拆分成多个项目,分配到不同的项目组进行开发的,根据业务模块拆分分给不同的项目小组,
大公司都是6个人左右一个团队,一个团队只是负责一个模块,比如只是登录模块,只是搜索模块.

拆分是根据业务需求拆分项目,由架构师来完成,每个项目都有自己单独的数据库,由不同的团队进行开发.

项目直接会暴露接口供别的项目调用,这个过程就叫RPC远程调用,比如订单服务需要调用商品服务.
RPC一般都是HTTP ,Dubbo,SpringCloud 等等.

现在大公司都是SpringCloud +Resuftl+Json
微服务
优点:就是解耦,一个服务挂了 不影响别的服务,假如淘宝双十一订单模块挂了,但是首页不会挂.
分子项目互不影响
缺点:拆分成本高(适合大公司),开发效率低(RPC通信要写接口)
分布式和微服务[笔记] - 图13

4.什么是微服务


微服务是一种架构风格,这个架构风格提倡我们在开发应用的时候,应该是作为一系列小服务的组合,它应该是一组小型的服务.而这些小型服务都运行在自己的进程之内,这些小型服务可以通过http的方式进行沟通.



就是将一个项目进行拆分成多个模块,多个模块之间通讯使用RPC远程调用技术

架构设计概念,各服务间隔离(分布式也是隔离),自治(分布式依赖整体组合)其它特性(单一职责,边界,异步通信,独立部署)是分布式概念的跟严格执行
SOA到微服务架构的演进过程
作用:各服务可独立应用,组合服务也可系统应用(巨石应用[monolith]的简化实现策略-平台思想)


单体项目缺点,慢慢项目越来越大,项目问题,非常臃肿,改一个模块整个项目都要全部发布,每次发布都有不同问题,解决办法就是把项目进行拆分.
分布式和微服务[笔记] - 图14
微服务定位
分布式和微服务[笔记] - 图15
好处:
每个模块功能都是单一的,代码量不如整到一起多,项目维护方便,每个微服务启动比较快(因为代码少).

技术栈不受限,单一项目只能是一个技术,(一个工程)而微服务架构(多个工程)每个小微服务都可以用自己想要的技术栈(A微服务是java语言,B微服务是PHP语言,哪个微服务的功能适合哪个语言做就用什么语言).你修改的只是你自己的微服务.

假如微服务A 要求运算能力高,你可以给这个微服务部署到性能好的机器, 微服务B是IO那种的,就给他部署到磁盘好的,单一的就不行了,如果一个微服务性能到了瓶颈了可以单独优化,比如A服务运算到瓶颈了,你可以只给A微服务的cpu进行升级.可以每个服务针对性的改进,

不会因为一个模块崩溃而所有的功能都崩溃.用户微服务出问题了 不影响搜索微服务的使用.

缺点:
测试复杂,多个团队需要坐在一起联调(大公司沟通成本太高,毕竟好几个部门沟通)

需要使用分布式事务

接口调整的成本高,微服务之间接口通信,如果修改了一个微服务API,可能所有用该微服务的接口都需要调整.

运维要求比较高,微服务是多个war包,维护起来比单体项目(一个war包)难

分布式系统的复杂性,容错性,网络延迟等等都需要处理

重复劳动: 多个微服务的工具类可能因为语言不通用而工具类不通用

5.微服务设计原则

单一职责原则

服务自治原则
每个微服务都得有自己独立运行的能力,要有自己的数据库.可以单独运行,尽量不依赖其它的服务.
接口
每个服务对外接口应该明确定义,并且尽量保持不变

 如何设计一个高并发的系统

分布式和微服务[笔记] - 图16


假设你在某知名电商公司干过高并发系统,用户上亿,一天流量几十亿,高峰期并发量上万,甚至是十万。那么人家一定会仔细盘问你的系统架构,你们系统啥架构?怎么部署的?部署了多少台机器?缓存咋用的?MQ咋用的?数据库咋用的?就是深挖你到底是如何抗下高并发的。

因为真正干过高并发的人一定知道,脱离了业务的系统架构都是在纸上谈兵,真正在复杂业务场景而且还高并发的时候,那系统架构一定不是那么简单的,用个redis,用mq就能搞定?当然不是,真实的系统架构搭配上业务之后,会比这种简单的所谓“高并发架构”要复杂很多倍。

如果有面试官问你个问题说,如何设计一个高并发系统?那么不好意思,一定是因为你实际上没干过高并发系统。面试官看你简历就没啥出彩的,感觉就不咋地,所以就会问问你,如何设计一个高并发系统?其实说白了本质就是看看你有没有自己研究过,有没有一定的知识积累。

最好的当然是招聘个真正干过高并发的哥儿们咯,但是这种哥儿们人数稀缺,不好招。所以可能次一点的就是招一个自己研究过的哥儿们,总比招一个傻也不会的哥儿们好吧!

所以这个时候你必须得做一把个人秀了,秀出你所有关于高并发的知识!

其实所谓的高并发,如果你要理解这个问题呢,其实就得从高并发的根源出发,为啥会有高并发?为啥高并发就很牛逼?

我说的浅显一点,很简单,就是因为刚开始系统都是连接数据库的,但是要知道数据库支撑到每秒并发两三千的时候,基本就快完了。所以才有说,很多公司,刚开始干的时候,技术比较low,结果业务发展太快,有的时候系统扛不住压力就挂了。

当然会挂了,凭什么不挂?你数据库如果瞬间承载每秒5000,8000,甚至上万的并发,一定会宕机,因为比如mysql就压根儿扛不住这么高的并发量。

所以为啥高并发牛逼?就是因为现在用互联网的人越来越多,很多app、网站、系统承载的都是高并发请求,可能高峰期每秒并发量几千,很正常的。如果是什么双十一了之类的,每秒并发几万几十万都有可能。

那么如此之高的并发量,加上原本就如此之复杂的业务,咋玩儿?真正厉害的,一定是在复杂业务系统里玩儿过高并发架构的人,但是你没有,那么我给你说一下你该怎么回答这个问题:

(1)系统拆分,将一个系统拆分为多个子系统,用dubbo来搞。然后每个系统连一个数据库,这样本来就一个库,现在多个数据库,不也可以抗高并发么。

(2)缓存,必须得用缓存。大部分的高并发场景,都是读多写少,那你完全可以在数据库和缓存里都写一份,然后读的时候大量走缓存不就得了。毕竟人家redis轻轻松松单机几万的并发啊。没问题的。所以你可以考虑考虑你的项目里,那些承载主要请求的读场景,怎么用缓存来抗高并发。

(3)MQ,必须得用MQ。可能你还是会出现高并发写的场景,比如说一个业务操作里要频繁搞数据库几十次,增删改增删改,疯了。那高并发绝对搞挂你的系统,你要是用redis来承载写那肯定不行,人家是缓存,数据随时就被LRU了,数据格式还无比简单,没有事务支持。所以该用mysql还得用mysql啊。那你咋办?用MQ吧,大量的写请求灌入MQ里,排队慢慢玩儿,后边系统消费后慢慢写,控制在mysql承载范围之内。所以你得考虑考虑你的项目里,那些承载复杂写业务逻辑的场景里,如何用MQ来异步写,提升并发性。MQ单机抗几万并发也是ok的,这个之前还特意说过。

(4)分库分表,可能到了最后数据库层面还是免不了抗高并发的要求,好吧,那么就将一个数据库拆分为多个库,多个库来抗更高的并发;然后将一个表拆分为多个表,每个表的数据量保持少一点,提高sql跑的性能。

(5)读写分离,这个就是说大部分时候数据库可能也是读多写少,没必要所有请求都集中在一个库上吧,可以搞个主从架构,主库写入,从库读取,搞一个读写分离。读流量太多的时候,还可以加更多的从库。

(6)Elasticsearch,可以考虑用es。es是分布式的,可以随便扩容,分布式天然就可以支撑高并发,因为动不动就可以扩容加机器来抗更高的并发。那么一些比较简单的查询、统计类的操作,可以考虑用es来承载,还有一些全文搜索类的操作,也可以考虑用es来承载。


上面的6点,基本就是高并发系统肯定要干的一些事儿,大家可以仔细结合之前讲过的知识考虑一下,到时候你可以系统的把这块阐述一下,然后每个部分要注意哪些问题,之前都讲过了,你都可以阐述阐述,表明你对这块是有点积累的。

说句实话,毕竟真正你厉害的一点,不是在于弄明白一些技术,或者大概知道一个高并发系统应该长什么样?其实实际上在真正的复杂的业务系统里,做高并发要远远比我这个图复杂几十倍到上百倍。你需要考虑,哪些需要分库分表,哪些不需要分库分表,单库单表跟分库分表如何join,哪些数据要放到缓存里去啊,放哪些数据再可以抗掉高并发的请求,你需要完成对一个复杂业务系统的分析之后,然后逐步逐步的加入高并发的系统架构的改造,这个过程是务必复杂的,一旦做过一次,一旦做好了,你在这个市场上就会非常的吃香。

其实大部分公司,真正看重的,不是说你掌握高并发相关的一些基本的架构知识,架构中的一些技术,RocketMQ、Kafka、Redis、Elasticsearch,高并发这一块,次一等的人才。对一个有几十万行代码的复杂的分布式系统,一步一步架构、设计以及实践过高并发架构的人,这个经验是难能可贵的。