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
12 changes: 12 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -198,6 +198,18 @@
- [22、说说生产环境下,你们是怎么实现网关对服务的动态路由的?](/docs/distributed-system/dynamic-route.md)[代码下载点击这里哦!](https://github.com/shishan100/Java-Interview-Advanced/raw/master/docs/distributed-system/code/code2.zip)
- [23、如果网关需要抗每秒10万的高并发访问,你应该怎么对网关进行生产优化?](/docs/distributed-system/gateway-high-concurrency.md)
- [24、作业:你们公司的网关是怎么技术选型的,假设有高并发场景怎么优化?](/docs/distributed-system/gateway-technical.md)
- [25、如果需要部署上万服务实例,现有的服务注册中心能否抗住?如何优化?](/docs/distributed-system/registration-center-optimize.md)
- [26、你们是如何基于网关实现灰度发布的?说说你们的灰度发布方案?](/docs/distributed-system/gray-environment.md)[代码下载点击这里哦!](https://github.com/shishan100/Java-Interview-Advanced/raw/master/docs/distributed-system/code/code3.zip)
- [27、说说你们一个服务从开发到上线,服务注册、网关路由、服务调用的流程?](/docs/distributed-system/service-register-gateway-router.md)
- [28、作业:看看你们公司的服务注册中心能否支撑上万服务实例的大规模场景?](/docs/distributed-system/work-register.md)
- [29、画一下你们系统的整体架构图,说说各个服务在生产环境怎么部署的?](/docs/distributed-system/system-framework.md)
- [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
Empty file.
Binary file added docs/distributed-system/code/code4.zip
Binary file not shown.
76 changes: 76 additions & 0 deletions docs/distributed-system/gray-environment.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@

#### 准备一个数据库和一个表(也可以用Apollo配置中心、Redis、ZooKeeper,其实都可以),放一个灰度发布启用表

```
id service_id path enable_gray_release

CREATE TABLE `gray_release_config` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`service_id` varchar(255) DEFAULT NULL,
`path` varchar(255) DEFAULT NULL,
`enable_gray_release` int(11) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8



在zuul里面加入下面的filter,可以在zuul的filter里定制ribbon的负载均衡策略

<dependency>
<groupId>io.jmnarloch</groupId>
<artifactId>ribbon-discovery-filter-spring-cloud-starter</artifactId>
<version>2.1.0</version>
</dependency>

写一个zuul的filter,对每个请求,zuul都会调用这个filter

@Configuration
public class GrayReleaseFilter extends ZuulFilter {

@Autowired
private JdbcTemplate jdbcTemplate;

@Override
public int filterOrder() {
return PRE_DECORATION_FILTER_ORDER - 1;
}

@Override
public String filterType() {
return PRE_TYPE;
}

@Override
public boolean shouldFilter() {

}

@Override
public Object run() {
RequestContext ctx = RequestContext.getCurrentContext();
HttpServletRequest request = ctx.getRequest();

Random random = new Random();
int seed = random.getInt() * 100;

if (seed = 50) {
// put the serviceId in `RequestContext`
RibbonFilterContextHolder.getCurrentContext()
.add("version", "new");
} else {
RibbonFilterContextHolder.getCurrentContext()
.add("version", "old");
}

return null;
}
}
```

eureka:
instance:
metadata-map:
version: new



Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
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,来保证幂等性


6 changes: 6 additions & 0 deletions docs/distributed-system/registration-center-optimize.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@

![分布式注册中心](/docs/distributed-system/images/registration-center-optimize.png)
#### eureka:peer-to-peer,每台机器都是高并发请求,有瓶颈
#### zookeeper:服务上下线,全量通知其他服务,网络带宽被打满,有瓶颈

#### 分布式服务注册中心,分片存储服务注册表,横向扩容,每台机器均摊高并发请求,各个服务主动拉取,避免反向通知网卡被打满
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之后返回了响应给订单服务

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


12 changes: 12 additions & 0 deletions docs/distributed-system/service-register-gateway-router.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@

#### 生产环境,微服务生产实践

开发了一个新的服务,线上部署,配合网关动态路由的功能,在网关里配置一下路径和新服务的映射关系,此时请求过来直接就可以走到新的服务里去

对已有服务进行迭代和开发,新版本,灰度发布,新版本部署少数几台机器,通过一个界面,开启这个服务的灰度发布,**此时zuul filter启用,按照你的规则,把少量的流量打入到新版本部署的机器上去**

观察一下少量流量在新版本的机器上运行是否正常

版本改成current,全量机器部署,关闭灰度发布功能,网关就会把流量均匀分发给那个服务了


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
```
12 changes: 12 additions & 0 deletions docs/distributed-system/system-dilatation.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@

如果访问量扩大10倍,如何扩容?

网关直接多部署10倍的机器即可,前面的Nginx做会负载均衡,把流量均匀分发给各个网关机器

服务扩容,都很简单的,多加机器,部署启动,自动注册到注册中心里去,此时其他服务会自动感知到你的服务多加了一些机器

服务实例变多了10倍,此时几十个服务实例,几百个服务实例,对eureka机器会造成每秒几百请求,没问题,eureka机器,8核16G的配置,单机抗上千请求,很轻松

数据库本来是每秒几百请求,10倍,每秒高峰期是三四千请求,横向扩容很麻烦,此时可以考虑给单个数据库部署的机器提高配置,32核128G高配物理机,每秒钟抗几千请求问题不大


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

### 服务框架、注册中心、网关系统

即使你没有用很多微服务架构里的东西,只要有上述三个东西,配合上写一些文档,接口文档,分布式系统架构,其实就出来了,很多中小型公司,一个小型技术团队,后端开发工程师总共就10多个人

关注分布式系统一些生茶实践的问题,应对你们公司的流量,各个服务、网关、注册中心,都是如何部署的,部署几台机器,每台机器的配置是什么,每天整体的流量有多少,高峰期的请求量有多少,你的整体系统是否抗住了

各个服务之间的超时、重试、幂等,是否搞定了

数据库配置和部署


很多同学,他们因为在自己公司里面开发的都是一些单块系统,微服务架构,但是公司没什么流量,他对机器的配置抗多少并发请求,其实没多大的概念,都不是什么技术问题,很多同学出去面试的时候

死扣生产细节问题,每秒3000并发请求,各个服务部署多少机器,机器的配置,数据库的机器和配置,网关的机器和配置,注册中心的机器和配置,什么样的机器配置能抗多大的流量和请求



中小型的系统,拆分为10~20个服务,微服务,庞大的互联网公司,都有几百个、几千个、几万个服务,服务规模,服务注册中心,妥妥的,2~3台机器就足够了,把服务的上线、故障以及发现优化到极致

服务上线,注册表多级缓存同步1秒,注册表拉取频率降低为1秒
服务心跳,1秒上报1次
故障发现,1秒钟检查一次1心跳,如果发现2秒内服务没上报心跳,认为故障了

服务注册中心都没任何压力,最多就是每秒钟几十次请求而已

服务注册中部署个2台机器,每台机器就是4核8G,高可用冗余,任何一台机器死掉,不会影响系统的运行

服务注册中心这样的一个处理逻辑,4核8G的机器,每秒钟轻松抗几百请求,上千请求也是可以的

通常来说,如果每秒钟的并发在1000以内的话,很少部署的,每个服务部署2台机器,每台机器4核8G,每台机器每秒抗个几百请求,一点问题都没有,别搞出来一个请求过来,查数据库SQL写的太烂了,一条SQL要跑3秒钟

大部分的系统,高峰期每秒几百请求,低峰期每秒几十请求,甚至几个请求



网关系统,4核8G的机器,一台机器抗每秒几百请求,部署3~4台机器,保证可以网关系统每台机器的压力比较小,进一步保证网关系统可靠性



数据库,MySQL,16核32G,物理机最佳,32核64G,最多抗个每秒钟几千请求问题不大,平时抗个每秒钟几十或者几百请求,三四千请求,但是只不过此时会导致MySQL机器的负载很高,CPU使用率很高,磁盘IO负载很高,网络负载很高










Loading