diff --git a/README.md b/README.md index 24713d6..db6fb96 100644 --- a/README.md +++ b/README.md @@ -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) ### 第二季-高并发 diff --git a/docs/distributed-system/code/code3.zip b/docs/distributed-system/code/code3.zip new file mode 100644 index 0000000..e69de29 diff --git a/docs/distributed-system/code/code4.zip b/docs/distributed-system/code/code4.zip new file mode 100755 index 0000000..4862a03 Binary files /dev/null and b/docs/distributed-system/code/code4.zip differ diff --git a/docs/distributed-system/gray-environment.md b/docs/distributed-system/gray-environment.md new file mode 100644 index 0000000..5f047ec --- /dev/null +++ b/docs/distributed-system/gray-environment.md @@ -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的负载均衡策略 + + + io.jmnarloch + ribbon-discovery-filter-spring-cloud-starter + 2.1.0 + + +写一个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 + + + diff --git a/docs/distributed-system/images/registration-center-optimize.png b/docs/distributed-system/images/registration-center-optimize.png new file mode 100755 index 0000000..3fec6a5 Binary files /dev/null and b/docs/distributed-system/images/registration-center-optimize.png differ diff --git a/docs/distributed-system/interface-idempotence.md b/docs/distributed-system/interface-idempotence.md new file mode 100644 index 0000000..8062013 --- /dev/null +++ b/docs/distributed-system/interface-idempotence.md @@ -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,来保证幂等性 + + diff --git a/docs/distributed-system/registration-center-optimize.md b/docs/distributed-system/registration-center-optimize.md new file mode 100644 index 0000000..ffe41c5 --- /dev/null +++ b/docs/distributed-system/registration-center-optimize.md @@ -0,0 +1,6 @@ + +![分布式注册中心](/docs/distributed-system/images/registration-center-optimize.png) +#### eureka:peer-to-peer,每台机器都是高并发请求,有瓶颈 +#### zookeeper:服务上下线,全量通知其他服务,网络带宽被打满,有瓶颈 + +#### 分布式服务注册中心,分片存储服务注册表,横向扩容,每台机器均摊高并发请求,各个服务主动拉取,避免反向通知网卡被打满 diff --git a/docs/distributed-system/request-retry.md b/docs/distributed-system/request-retry.md new file mode 100644 index 0000000..7ae9e59 --- /dev/null +++ b/docs/distributed-system/request-retry.md @@ -0,0 +1,16 @@ + +订单服务 -> 创建订单 + + -> 库存服务 -> 扣减库存 + -> wms服务 -> 通知发货 + -> 积分服务 -> 增加积分 + +订单服务调用库存服务的时候,因为网络抖动,请求超时了,超过了秒钟,此时订单服务会重试,再次调用一下库存服务,发送一模一样的请求过去 + + + +比如说,订单服务第一次请求库存服务,库存服务其实是把扣减库存的业务逻辑执行成功了,只不过网络问题,导致响应迟迟没有返回给订单服务,可能在1.2s之后返回了响应给订单服务 + +订单服务就认为请求超时了,他就再次发送了一个一模一样的请求给库存服务,库存服务可能会再次对库存进行扣减 + + diff --git a/docs/distributed-system/service-register-gateway-router.md b/docs/distributed-system/service-register-gateway-router.md new file mode 100644 index 0000000..a65167b --- /dev/null +++ b/docs/distributed-system/service-register-gateway-router.md @@ -0,0 +1,12 @@ + +#### 生产环境,微服务生产实践 + +开发了一个新的服务,线上部署,配合网关动态路由的功能,在网关里配置一下路径和新服务的映射关系,此时请求过来直接就可以走到新的服务里去 + +对已有服务进行迭代和开发,新版本,灰度发布,新版本部署少数几台机器,通过一个界面,开启这个服务的灰度发布,**此时zuul filter启用,按照你的规则,把少量的流量打入到新版本部署的机器上去** + +观察一下少量流量在新版本的机器上运行是否正常 + +版本改成current,全量机器部署,关闭灰度发布功能,网关就会把流量均匀分发给那个服务了 + + diff --git a/docs/distributed-system/service-request-time-out.md b/docs/distributed-system/service-request-time-out.md new file mode 100644 index 0000000..4a14374 --- /dev/null +++ b/docs/distributed-system/service-request-time-out.md @@ -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秒就认为超时了,此时会先重试一下订单服务这台机器,如果还是不行就重试一下订单服务的其他机器 + + +org.springframework.retry +spring-retry + + +hystrix.command.default.execution.isolation.thread.timeoutInMilliseconds=10000 +``` \ No newline at end of file diff --git a/docs/distributed-system/system-dilatation.md b/docs/distributed-system/system-dilatation.md new file mode 100644 index 0000000..cbfbd00 --- /dev/null +++ b/docs/distributed-system/system-dilatation.md @@ -0,0 +1,12 @@ + +如果访问量扩大10倍,如何扩容? + +网关直接多部署10倍的机器即可,前面的Nginx做会负载均衡,把流量均匀分发给各个网关机器 + +服务扩容,都很简单的,多加机器,部署启动,自动注册到注册中心里去,此时其他服务会自动感知到你的服务多加了一些机器 + +服务实例变多了10倍,此时几十个服务实例,几百个服务实例,对eureka机器会造成每秒几百请求,没问题,eureka机器,8核16G的配置,单机抗上千请求,很轻松 + +数据库本来是每秒几百请求,10倍,每秒高峰期是三四千请求,横向扩容很麻烦,此时可以考虑给单个数据库部署的机器提高配置,32核128G高配物理机,每秒钟抗几千请求问题不大 + + diff --git a/docs/distributed-system/system-framework.md b/docs/distributed-system/system-framework.md new file mode 100644 index 0000000..66a1f5f --- /dev/null +++ b/docs/distributed-system/system-framework.md @@ -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负载很高,网络负载很高 + + + + + + + + + + diff --git a/docs/distributed-system/system-qps.md b/docs/distributed-system/system-qps.md new file mode 100644 index 0000000..eb4b910 --- /dev/null +++ b/docs/distributed-system/system-qps.md @@ -0,0 +1,42 @@ + +很常问,很多人回来跟我说,老师,我不知道我们系统每秒钟请求有多少,每天请求量有多大,我都没任何的概念,因为系统开发好直接部署,根本不管这些东西,有没有什么比较好的工具可以看到服务的访问量,qps + + + + + +每个服务每天多少请求量,高峰期每秒钟多少请求量,你完全可以在代码里,稍微加一些metrics的代码,如果你看过很多的开源项目的话,开源的分布式系统,eureka、hadoop、spark、hbase、kafka,metrics机制 + + +任何一个开源系统都需要对自己运行过程中各种请求量、每秒的请求量、成功次数、失败次数,在内存里直接做一些计数,他会给你开放一些端口号,比如http端口号,你只要请求他这个端口号,他就会把这些metrics统计返回给你 + + + + +在你负责的核心服务里,核心接口,开发一个简单的metric统计机制,AtomicLong,原子性,并发下数据统计准确,不会错误,每个接口被调用的时候,一个是可以对每个接口每分钟都做一个Metric统计 + +对每个接口每天的请求使用一个AtomicLong做一个计数,统计出来每天的请求次数 + +计算一下每个接口从请求到执行完毕,需要耗费多长时间,算一下每个接口平均的请求延时,TP99,TP95,TP90,TP50,TP99,99%的请求耗费的时间在100ms以内,但是1%的请求可能耗费的时间在100ms以上 + +**TP99 = 100ms** +**TP95 = 50ms,95%的请求耗费的时间多在50ms以内,但是5%的请求耗费的时间在50ms以上** + +平均响应延时 + +你可以计算出来这个接口平均响应延时,把每次调用的耗时跟历史总耗时加起来,除以当前的请求次数,不就是最新的接口响应平均延时 + + + + +你完全可以通过log4j,logback,日志组件,把每分钟每个接口被访问的次数直接打印到日志文件里去,除以60,不就知道高峰期每秒钟系统被访问的次数了吗,每天每个接口访问的总次数打印到日志里去 + + + +压测工具,百度一下:java压测工具,开源的可以用的,模拟出来同时有多少用户发起多少请求,每秒发起1000请求能抗住吗?每秒钟发起2000请求能抗住吗? + +假设你的系统每秒钟最多抗800请求,如果你的压测工具每秒发起了1000个请求,此时他会发现最多只有800个请求同时可以被处理,剩余200个请求需要进行排队被阻塞住了,告诉你,你的这个系统每秒钟最多抗800个请求 + + + + diff --git a/docs/distributed-system/work-interface-idempotence.md b/docs/distributed-system/work-interface-idempotence.md new file mode 100644 index 0000000..7fc72cd --- /dev/null +++ b/docs/distributed-system/work-interface-idempotence.md @@ -0,0 +1,17 @@ + +分布式系统,考察生产实践细节,常问的面试问题 + +服务框架、注册中心、网关系统、部署架构、超时重试、幂等性 + + +跟你自己的业务系统有关系了,你们的系统服务之间的调用,有没有超时和重试的配置,如果没有,如何优化配置,如果有,核心接口有没有幂等性机制,重复插入数据,重复跟新数据 + +如果有问题,结合你的业务,如何基于唯一索引、redis定制化防重机制 + + +可以在评论区提问,我们会给大家答疑,狸猫技术窝,知识店铺,训练营页面里,有评论区,提问,答疑 + +好好的完成作业,在作业里设计自己的系统业务逻辑对应的一套幂等性机制,每天的作业,都是可以提交,你可以把作业提交到店铺里去,每天我们都会给你们提交的作业进行点评,对你们作业里的问题进行答疑 + + +付费微信交流群,课程目录里有一个文档,入群步骤 diff --git a/docs/distributed-system/work-register.md b/docs/distributed-system/work-register.md new file mode 100644 index 0000000..5afd505 --- /dev/null +++ b/docs/distributed-system/work-register.md @@ -0,0 +1,13 @@ + +#### Dubbo框架原理 +#### Spring Cloud框架原理 +#### 服务框架的技术选型 + +#### 服务注册中心技术选型和核心原理 +#### 生产优化 +#### 架构优化 + +#### 网关系统技术选型和核心原理 +#### 生产优化(灰度发布、动态路由) + +#### 生产级的分布式系统里,新服务开发如何做,老服务迭代如何做 diff --git a/docs/distributed-system/work-system-dilatation.md b/docs/distributed-system/work-system-dilatation.md new file mode 100644 index 0000000..de45a64 --- /dev/null +++ b/docs/distributed-system/work-system-dilatation.md @@ -0,0 +1,2 @@ + +部署,机器配置,大概能抗多少并发;流量、QPS、性能,metrics;压测,借助一些小工具;扩容方案,横向加机器,还是说纵向提升机器的配置