微服务从0到1落地实战问题点与解决方案

分布式锁线上真实案例架构设计哲学本质解剖

1. 业务场景驱动:

. 交易商品库锁定,防止用户重复下单.. MQ消息去重,防止消息重复消费:
   a. 发送端去重
   b. 消费端去重

③. 订单操作变更协同:
   a. 在用户对商品下单后,订单状态为待支付,在某一时刻用户正在对该订单做支付操作,商家对该订单进行改价操作.
   b. 其它类似状态的修改行为,也需要做串行处理,避免出现数据不一致性.

2. 业务场景共性:

(1). 共享资源:

. 有哪些共享资源?
   a. 用户、订单消息、订单.. 举例:
   a. 比如分布式系统中,下单过程中,请求两台order服务的过程中,什么资源是可以控制的?
   b. 如用户id下过单,用一种方式记录下来,再来下单的时候,就不能下单了.
   c. 此时,用户id就变成一个共享资源了.

(2). 解决思路:

. 共享资源互斥.. 共享资源串行化.

(3). 解决方案:

. 在并发下,通过加锁对共享资源进行串行化.. 锁的问题:
   a. 本地锁弊端
      (1). 如果只是在分布式某一个服务加本地锁,是没有作用的.
      (2). 可以使用分布式锁(集中化管理).
   b. 分布式锁
      (1). 只有申请共享资源通过,才能使用.否则,拒绝.

3. 基于redis的分布式锁实现方案:

redis集群,原理是因为redis单线程串行处理.

(1). SETNX方案:

. SETNX(Set if not exists):
   a. 命令在指定的key不存在时,为key设置指定的值.
   b. SETNX Key Value设置成功,返回1.设置失败,返回0.
   c. 没有有效期的

②. 原子操作(多个执行命令):
   Multi
   SETNX Key Value
   expire key seconds        // 设置失效时间
   exec

③. 两条命令要求是原子操作:
   a. 如果有可能会某一条命令失败,如expire失败了,这把锁就没有有效期,就会变成死锁.
   b. 要求是要么都成功、要么都失败.
   c. redis把原子性的操作变成一个lua脚本.. 弊端:
   a. Multi不检查语义本身,导致后果是有可能一个命令执行失败.
      (1). set name 'david'
           incr name         // 报错了,但是multi还是会执行成功.
   b. 事务是不严谨的.

(2). set方案:

. set key value NX PX milliseconds:
   a. 命令在指定的key不存在时,为key设置指定的值,并设置生存时间.. 弊端 - 单机redis一般不会开持久化:
   a. 在用户1拿到锁的时候,这个锁还没有释放,突然进程挂了.
   b. 马上redis重启,内存是空的.
   c. 此时,用户2又可能拿到同一把锁,存在同一把锁会被拿多次的情况.. 改进 - 分布式redis:
   a. 一台主节点、一台从节点,主从是异步来同步数据的(不是同步的过程).
   b. 用户A在主节点加锁后,会在一定时间内同步到从节点.
   c. 在用户B访问时,主节点挂了,从节点会成为主节点,再去拿同一把锁,发现是存在的.. 疑问?
   a. 当主节点锁还没有同步过从节点时,主节点挂了.
   b. 此时,从节点升级为主节点,还是会产生同一把锁被拿多次的情况.. 深层次挖掘:
   a. 因为锁只有能一个,所以是CP模型.
   b. redis主从分布式来实现这个锁,它的模型是AP模型.
      (1). 主从缓存走的是吞吐量.
   c. 总结:
      (1). 这个锁是CP模型,但是用的redis主从是AP模型.
      (2). 所以,最终还是会达到一个锁拿两次的情况.

4. 一切架构都不能脱离业务场景来设计:

. 短信消息场景(AP模型):
   a. 利用分布式锁来对消息去重,比如发一个短信,没有锁住,重复又发了一次.
   b. 只是说体验不是太好,目的最终还是达到了.. 交易的场景(CP模型):
   a. 重复转了两次,肯定是业务不能接受的.. 总结:
   a. 业务的容忍度决定架构的设计.

5. 如何实现存储模型为CP模型的锁呢?

(1). 中间件对比:

              redis          zookeeper            etcd
一致性算法      无              paxos              raft
CAP            AP               CP                 CP
高可用         主从            N+1可用            N + 1可用
接口类型       客户端           客户端            http/grpc
实现           setNX       createEphemeral      restful api

zookeeper对锁实现使用创建临时节点和watch机制,并发执行效率、扩展能力、社区活跃度等方面低于etcd.

1. 架构师职责:

1: 业务需求的分析能力:
       a. 对项目与业务负责,背后真实的需求是什么?2: 架构设计:
       a. 单机、SOA、微服务、ServiceMesh

第3: 架构选型:
       a. 语言技术栈的选型

第4: 落地实现

2. 架构思维模型:

(1). 结合场景将静态的知识使用思维模型动态的来运用实践.

. 如什么样的场景用什么样的数据库.

(2). 七种思维模型:

. 业务需求至简抽象分析能力思维模型[1].. 哲学本质架构设计思维模型[2]:
   a. 每个架构的本质的分析:
      (1). 场景、优缺点、方案、资源、预期的收益等.
      (2). 比较抽象,概念太大,将架构细化抽象分析,并映射成能符合的几个基本定律思考?
           1. CAP定律
           2. BASE定律
   b. CAP架构设计思维模型[3]
   c. BASE架构设计思维模型[4]. 根据场景Balance架构设计思维模型[5]:
   a. 折中设计能力
   b. 阿里的大中台方案转移到其它公司,不一定适合:
      (1). 因为业务需求、量级、人员、运维、成本、新旧系统兼容不同...etc
      (2). 其它因素.
   c. 并不是根据潮流技术选型
   d. 折中引发的"合适"架构设计思维模型[6]
      (1). 适度超前半年、1年
      (2). 防止过渡设计
      (3). 适度超前架构设计思维模型[7]

(3). 架构设计能力:

. 以达到以不变应万变的架构设计能力:
   a. 不变的是指7种思维模型
   b. 万变的是业务场景

②. 以极低的成本、更高的效率、适度超前给出优雅的架构设计方案.

(4). 架构设计哲学本质(降本增效):

. 降低人力成本、运维成本、开发成本

②. 增加公司开发、人员、运营效率

1. 架构演进方向:

单机  =>  垂直架构  =>  SOA  =>  微服务  =>  中台

(1). 单体架构:

. 表现:
   a. 功能集中:
      (1). 单体就会把新业务与旧业务合并在一起.
   b. 同一个包发布
      (1). 有bug会延缓整个团队的上线
      (2). 没有并行发布效率高
   c. 运行在一个进程中

②. 问题点:
   a. 开发效率低:
      (1). 需要排期,考虑现有业务是否有冲突、兼容
   b. 功能交付周期长
   c. 新人培养周期长

(2). 垂直架构(加个负载均衡,方便扩展)

(3). soa(通过service解决应用系统间集成和互通)


(4). 微服务:

. 表现:
   a. 微服务是网状结构、敏捷速度快、每个服务开发的周期就短.
   b. 业务可以并行开发,而不是线行开发.  =>  单体架构是线行开发

②. 问题点:
   a. 微服务分的太多、松散、复杂、凌乱  =>   逐渐压缩成中台.. 考量的指标:
   a. 应用体量大不大
   b. 分布式服务需要更新迭代快,不同的branch需要快速的响应和部署、新业务诞生.

2. 转型:

. 信息化转型:
   a. 对单体架构进行转型
   b. 主要是需要面向外部客户,来支撑企业的数字化转型.. 云化转型:
   a. 把所有的业务变成云化体系
   b. 分布式云端应用,带来更多的业务和合作.. 智能化转型:
   a. 在产品经理的角度有很多新的feature用AI的技术来解决,但不影响前面的云化.
   b. 智能化的前提是业务变的非常大,这样的架构才能支撑这么大的业务.

高并发下接口幂等性解决方案

1. 幂等性概念:

①. 一个幂等操作指任意多次执行所产生的影响跟一次执行的影响相同.

②. 幂等函数(幂等方法):
   a. 可以使用相同参数重复执行,并能获得相同结果的函数.
   b. 这些函数不会影响数据状态,也不用担心重复执行会对数据造成改变.
   c. 如:"getUsername()"、"setTrue()"就是一个幂等函数.

③. 总结:
   幂等就是一个操作不论执行多少次,产生的效果和返回的结果都是一样的.

2. 业务场景:

①. 前端重复提交选中的数据,后台只能产生一个反应结果.
②. 发起一笔付款请求,只能扣用户账户一次钱,当遇到网络重发或系统bug重发,也只能扣一次钱.
③. 发送短信、极光推送等消息,也只能发一次.
④. 创建业务订单,一次业务请求只能创建一个.

3. 技术场景:

(1). mysql:

①. 查询操作:
   a. 在数据不变的情况下,查询一次和多次的结果是一样的.
   b. select是天然的幂等操作.

②. 删除操作:
   a. 删除一次和多次都是把数据删除,会体现在返回结果不一样.
   b. 删除的数据不存在,返回0.
   c. 删除的数据多条,返回多条值.

③. 唯一索引:
   a. 防止新增脏数据.
   b. 如:支付宝每个用户只能有一个资金账户,将资金账户表的用户ID加唯一索引,可以防止给用户创建资金账户多个.
   c. 唯一索引或唯一组合索引是用来防止新增数据存在脏数据.

④. 悲观锁:
   a. 加锁获取数据:
      select * from table_xxx where id='xxx' for update;
   b. 注:id字段一定是主键或唯一索引,否则会锁表.
   c. 悲观锁使用时,一般伴随事务一起使用,数据锁定时间可能会很长(根据实际情况选用).

⑤. 乐观锁:
   a. 只在更新数据那一刻锁表,其它时间不锁表.
   b. 相对于悲观锁,效率更高.
   c. 实现方式可以通过version或其他状态条件:
      1. 通过版本号实现:
         update table_name set name=xx, version=version+1 where id=#id# and version=xxx
      2. 通过条件限制实现:
         update table_name set avai_amount=avai_amount-#subAmount# where  id=#id#  and  avai_amount-#subAmount# >= 0
         要求:quality-#subQuality# >= ,这个情景适合不用版本号,只更新是做数据安全校验,适合库存模型,扣份额和回滚份额,性能更高;
   d. 注:乐观锁的更新操作,最好加主键或唯一索引来更新,这样是行锁,否则更新时会锁表.

(2). redis:

①. token机制:
a. 防止页面重复提交.
b. 当客户端请求页面时,服务器会生成一个随机数Token保存到redis或session中,再将Token发给客户端(hidden表单).
c. 下次客户端提交请求时,Token会随着表单一起提交到服务器端.
d. 服务器端第一次验证相同通过后,会将Token值更新,若是用户重复提交,第二次的验证判断将会失败.


(3). 分布式锁:

如果是分布是系统,构建全局唯一索引比较困难,例如唯一性的字段没法确定,这时候可以引入分布式锁,通过第三方的系统(redis或zookeeper),在业务系统插入数据或者更新数据,获取分布式锁,然后做操作,之后释放锁,这样其实是把多线程并发的锁的思路,引入多多个系统,也就是分布式系统中得解决思路。要点:某个长流程处理过程要求不能并发执行,可以在流程执行之前根据某个标志(用户ID+后缀等)获取分布式锁,其他流程执行时获取锁就会失败,也就是同一时间该流程只能有一个能执行成功,执行完成后,释放分布式锁(分布式锁要第三方系统提供);




  • 36
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值