Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -206,6 +206,10 @@
- [30、你们系统每天有多大访问量?每个服务高峰QPS多少?压测过服务最大QPS吗?](/docs/distributed-system/system-qps.md)
- [31、如果系统访问量比现在增加10倍,你们考虑过系统的扩容方案吗?](/docs/distributed-system/system-dilatation.md)
- [32、作业:独立画出自己系统的生产部署架构图,梳理系统和服务的QPS以及扩容方案](/docs/distributed-system/work-system-dilatation.md)
- [33、你们生产环境的服务是怎么配置超时和重试参数的?为什么要这样配置?](/docs/distributed-system/service-request-time-out.md)[代码下载点击这里哦!](https://github.com/shishan100/Java-Interview-Advanced/raw/master/docs/distributed-system/code/code4.zip)
- [34、如果出现服务请求重试,会不会出现类似重复下单的问题?](/docs/distributed-system/request-retry.md)
- [35、对于核心接口的防重幂等性,你们是怎么设计的?怎么防止重复下单问题?](/docs/distributed-system/interface-idempotence.md)
- [36、作业:看看自己系统的核心接口有没有设计幂等性方案?如果没有,应该怎么设计?](/docs/distributed-system/work-interface-idempotence.md)


### 第二季-高并发
Expand Down
Binary file modified docs/distributed-system/code/code3.zip
100755 → 100644
Binary file not shown.
Binary file added docs/distributed-system/code/code4.zip
Binary file not shown.
84 changes: 84 additions & 0 deletions docs/distributed-system/interface-idempotence.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@

接口幂等性实现起来非常的简单,不打算带着大家手写代码

(1)数据库唯一索引
(2)基于Redis实现一套幂等性防重框架



对于插入类的操作,一般都是建议大家要在数据库表中设计一些唯一索引


你如果有一个订单被支付了,此时就要通知wms创建一个对应发货单,也是数据库里的一个表,仓库里的人会看到这个发货单,此时他就会根据发货单的信息从仓库里进行拣货,打包,封装,交给物流公司



发货单

id order_id 订单金额 发货地址 xxxx

对order_id就可以建立一个唯一索引,你插入发货单的时候,同一个order_id最多只能对应一个发货单,不可能说同样的一个order_id对应了多个发货单


订单服务 -> wms服务,出现了重试,导致第二次请求再次让人家创建这个订单的发货单,create语句,order_id触发了唯一索引约束





扣减库存、累加积分,更新,很难通过数据库唯一索引来保证


基于Redis实现一套接口的防重框架

你得做一个类似spring mvc里的拦截器这样的东西,在这个拦截器里,他会拦截所有的请求,对所有的请求都会提取请求对应的参数,GET请求、POST请求、PUT请求,有些参数是跟在URL地址里的,?xx=xx&xx=xx

POST、PUT,可能是请求体里的,可能是一个JSON格式

把参数拼接在一起,作为key去redis中判断一下,是否存在这个key,之前附加这些参数的请求是否发起过,如果没有的话,此时就可以把这些参数+接口名称,作为一个key,存储到redis中去

然后呢,把请求放行,去执行这个请求

如果说人家重试再次发起一个这个请求,此时就可以判断出来,参数组成的key在redis中已经存在了,此时就不让执行这个请求了,认为是重复调用了





考虑很多问题,幂等不幂等,通用框架,需要一个公司所有的接口都按照指定的参数来传递,还有很多业务语义的问题

第一次发起一个请求,直接把请求key放入redis,但是他执行的过程中失败了,而且还阻塞了一段时间,此时人家再次重试发起第二次请求,这个时候按照上述的框架逻辑,就会把请求拦截下来了


到底是不是要对所有接口都开启这么一个东西呢?


每个接口如果执行成功了之后,我可以设置一个每个接口调用的时候执行成功之后,做一个后拦截器,如果成功了,就把请求对应的参数拼接为key放入redis中

有没有可能是第一次请求发送过来,在执行过程中,时间长了,比如需要1.3秒才执行完毕;此时人家发现超过1s了,直接重试,第二次请求过来了,也在正常的执行

第一次请求1.3秒之后执行成功了,第二次请求也执行成功了

只要一个服务希望对自己的接口开启幂等性防重功能,就把你开发好的拦截器对应的jar包,通过maven引入一个依赖就可以了



中大型互联网公司里也没做一个统一的防重幂等框架,其实一般都是各个服务对自己核心的接口,如果要保证幂等性的话,每个服务根据自己的业务逻辑来实现,而且仅仅是对少数核心接口做幂等性保障


核心接口,库存服务,扣减库存接口

定制化的去针对接口开发幂等性的机制,比如说一旦库存扣减成功之后,就立马要写一条数据到redis里去,order_id_11356_stock_deduct,写入redis中,如果写入成功,就说明之前这个订单的库存扣减,没人执行过

但是如果此时有一些重试的请求过来了,调用了你的库存扣减接口,他同时也进行了库存的扣减,但是他用同样的一个key,order_id_11356_stock_deduct,写入redis中,此时会发现已经有人写过key,key已经存在了

此时你就应该直接对刚才的库存扣减逻辑做一个反向的回滚逻辑,update product_stock set stock = stock - 100,update product_stock set stock = stock + 100,反向逻辑,回滚掉,自己避免说重复扣减库存






核心接口,幂等性都是自己保证的,人家可能会重试调用你的接口,对于create类的操作,用唯一索引来保证;对update类的操作,建议在核心接口里基于自己的业务逻辑,配合上redis,来保证幂等性


16 changes: 16 additions & 0 deletions docs/distributed-system/request-retry.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@

订单服务 -> 创建订单

-> 库存服务 -> 扣减库存
-> wms服务 -> 通知发货
-> 积分服务 -> 增加积分

订单服务调用库存服务的时候,因为网络抖动,请求超时了,超过了秒钟,此时订单服务会重试,再次调用一下库存服务,发送一模一样的请求过去



比如说,订单服务第一次请求库存服务,库存服务其实是把扣减库存的业务逻辑执行成功了,只不过网络问题,导致响应迟迟没有返回给订单服务,可能在1.2s之后返回了响应给订单服务

订单服务就认为请求超时了,他就再次发送了一个一模一样的请求给库存服务,库存服务可能会再次对库存进行扣减


76 changes: 76 additions & 0 deletions docs/distributed-system/service-request-time-out.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@

分布式系统,拆分为很多个服务之后,他们互相之间要进行调用,平时服务内要优化的一些参数其实不多,服务与服务之间的调用,会不会出现调用的超时,每个服务超时的时间是多长,超时之后是否要进行重试,重试几次

高可用,hystrix进行资源隔离、熔断、降级,zuul网关层直接进行限流


网关 ->(卡住) 订单服务 ->(卡住) wms服务

网关收到的一个http响应,可能就是一个500,internal error





Spring Cloud生产优化,系统第一次启动的时候,人家调用你经常会出现timeout

每个服务第一次被请求的时候,他会去初始化一个Ribbon的组件,初始化这些组件需要耗费一定的时间,所以很容易会导致。让每个服务启动的时候就直接初始化Ribbon相关的组件,避免第一次请求的时候初始化

```
ribbon:
eager-load:
enabled: true


zuul:
ribbon:
eager-load:
enabled: true

feign:
hystrix:
enabled: false

```

我们刚才启动了wms服务之后,其实订单服务和积分服务、wms服务、库存服务之间的请求都是没问题的,日志全部都打印出来了,不会说第一次请求因为ribbon加载过慢导致请求失败的问题

但是zuul网关层面去请求订单服务的时候,他还是可能会认为自己超时了,windows电脑上跑这样的一套系统,网络请求都是比较慢的,因为你有很多服务与服务之间的调用,order和另外3个服务一套交互下来,比如超过了1秒钟

zuul而言感觉耗时太久了,还是会认为是超时的

windows电脑走的都是家用网络,我家里的网络情况不是太好,网卡,网速慢,信号弱




线上的服务,每个服务部署上线的时候,一般来说都需要配置相关的超时时间还有重试次数

订单服务 -> 积分服务、库存服务、仓促服务

订单服务对于其他服务的调用,一般来说限制在多长时间就必须认为是超时了,如果超时之后如何进行重试

积分服务部署了两台机器,机器1和机器2

订单服务在一次请求积分服务的机器1的时候,超过1秒钟,超时了;此时需要进行重试,对积分服务当前的这台机器1重试几次?如果说机器1不行,是否可以重试一下积分服务的机器2?


```
ribbon:
ConnectTimeout: 3000
ReadTimeout: 3000
OkToRetryOnAllOperations: true
MaxAutoRetries: 1
MaxAutoRetriesNextServer: 1

中小型的系统,没必要直接开启hystrix,资源隔离、熔断、降级,如果你没有设计好一整套系统高可用的方案

zuul请求一个订单服务,超过1秒就认为超时了,此时会先重试一下订单服务这台机器,如果还是不行就重试一下订单服务的其他机器

<dependency>
<groupId>org.springframework.retry</groupId>
<artifactId>spring-retry</artifactId>
</dependency>

hystrix.command.default.execution.isolation.thread.timeoutInMilliseconds=10000
```
17 changes: 17 additions & 0 deletions docs/distributed-system/work-interface-idempotence.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@

分布式系统,考察生产实践细节,常问的面试问题

服务框架、注册中心、网关系统、部署架构、超时重试、幂等性


跟你自己的业务系统有关系了,你们的系统服务之间的调用,有没有超时和重试的配置,如果没有,如何优化配置,如果有,核心接口有没有幂等性机制,重复插入数据,重复跟新数据

如果有问题,结合你的业务,如何基于唯一索引、redis定制化防重机制


可以在评论区提问,我们会给大家答疑,狸猫技术窝,知识店铺,训练营页面里,有评论区,提问,答疑

好好的完成作业,在作业里设计自己的系统业务逻辑对应的一套幂等性机制,每天的作业,都是可以提交,你可以把作业提交到店铺里去,每天我们都会给你们提交的作业进行点评,对你们作业里的问题进行答疑


付费微信交流群,课程目录里有一个文档,入群步骤