前言:
此时看官们对“2000的并发量需要几台服务器”可能比较讲究,我们都需要学习一些“2000的并发量需要几台服务器”的相关内容。那么小编也在网络上收集了一些对于“2000的并发量需要几台服务器””的相关文章,希望你们能喜欢,大家一起来了解一下吧!本文主要介绍的微服务是spring cloud,它一个服务治理框架和一系列框架的由序集合,其利用springboot的开发便利性巧妙的简化了分布式系统基础设施的开发,如服务发现注册、负载均衡、断路器、数据监控等,都可以用springboot的开发风格做到一键启动和部署。学习和了解微服务对快速熟悉和掌握不同项目的服务架构大有益处
分为以下几个部分进行介绍:
一、微服务应用的简单业务场景
二、Spring Cloud核心组件:Eureka
三、Spring Cloud核心组件:Feign
四、Spring Cloud核心组件:Ribbon
五、Spring Cloud核心组件:Hystrix
六、Spring Cloud核心组件:Zuul
七、高并发场景下配置优化
八、总结
一、业务场景介绍
先来给大家说一个都熟悉的支付订单的功能,大体的流程如下:
用户创建一个订单并支付,需要将订单状态更新为“已支付”更新商品库存通知物流发货增加用户购物积分
上述业务场景的流程涉及到订单服务、库存服务、物流服务、用户积分服务。其调用关系如下图:
二、Spring Cloud核心组件:Eureka
考虑一个问题:订单服务想要调用库存服务、物流服务,或者是积分服务,怎么调用?
传统方式:通过传统的ip:port的方式调用各种服务,如果订单服务依赖的其他服务地址更改了,就需要修改订单服务配置,然后重新部署。阿西吧!eureka出场,eureka是微服务架构的注册中心,专门负责服务的注册和发现。Eureka有两个组件组成:Eureka server和Eureka client,一张图认识一下: 上面的图描述了Eureka的基本架构,有3个角色组成Eureka Server:提供服务注册和发现Service Provider:服务提供方,将自身服务注册到Eureka Server,从而使消费方能够找到Service Consumer:服务消费方,定时从Eureka获取注册服务列表,消费服务再来看一下业务场景中,这些服务上都有Eureka Client组件,Service Provider启动时将自己注册到Eureka Server上,注册的意思就是告诉Eureka Server 自己在哪台服务器,端口号等,Eureka Server 的注册表会保存这些服务的元数据信息。这个时候如果订单服务调用库存服务,Service Consumer通过Eureka Client组件 向Eureka Server 问一下库存服务的ip是什么,端口是什么,然后调用库存服务。订单服务调用其他服务亦是如此。
千万级访问量:eureka各个服务默认每隔30秒拉取注册表,每隔30s发起一次心跳,看看Eureka是如何实现千万级别访问量假设现在有一个大型的分布式系统,一共100个服务,每个服务部署在20台机器上,机器是4核8G的标准配置。也就是说,相当于你一共部署了100 * 20 = 2000个服务实例,有2000台机器。每台机器上的服务实例内部都有一个Eureka Client组件,它会每隔30秒请求一次Eureka Server,拉取变化的注册表。此外,每个服务实例上的Eureka Client都会每隔30秒发送一次心跳请求给Eureka Server。
那么大家算算,Eureka Server作为一个微服务注册中心,每秒钟要被请求多少次?一天要被请求多少次?
按标准的算法,每个服务实例每分钟请求2次拉取注册表,每分钟请求2次发送心跳
这样一个服务实例每分钟会请求4次,2000个服务实例每分钟请求8000次换算到每秒,则是8000 / 60 = 133次左右,我们就大概估算为Eureka Server每秒会被请求150次那一天的话,就是8000 * 60 * 24 = 1152万,也就是每天千万级访问量
深入原理:
Eureka server注册数据结构: 如上图所示,图中的这个名字叫做registry的CocurrentHashMap,就是注册表的核心结构。各个服务的注册、服务下线、服务故障,全部会在内存里维护和更新这个注册表。各个服务每隔30秒拉取注册表,每隔30s发起一次心跳,都在这Map数据结构中操作
一句话概括:维护注册表、拉取注册表、更新心跳时间,全部发生在内存里!
这个ConcurrentHashMap的key就是服务名称,比如“订单服务的名字是:order-service,这里就是order-service”, value则代表了一个服务的多个服务实例。Lease的类,它的泛型是一个叫做InstanceInfo,代表服务实例的具体信息,比如机器的ip地址、hostname以及端口号。而这个Lease,里面则会维护每个服务最近一次发送心跳的时间Eureka Server为了避免同时读写内存数据结构造成的并发冲突问题,采用了多级缓存机制(ReadOnlyCacheMap、ReadWriteCacheMap)提升服务请求的响应速度。
在拉取注册表的时候:
首先从ReadOnlyCacheMap里查缓存的注册表。若没有,就找ReadWriteCacheMap里缓存的注册表。如果还没有,就从内存中获取实际的注册表数据。
在注册表发生变更的时候:
会在内存中更新变更的注册表数据,同时过期掉ReadWriteCacheMap。此过程不会影响ReadOnlyCacheMap提供人家查询注册表。一段时间内(默认30秒),各服务拉取注册表会直接读ReadOnlyCacheMap30秒过后,Eureka Server的后台线程发现ReadWriteCacheMap已经清空了,也会清空ReadOnlyCacheMap中的缓存下次有服务拉取注册表,又会从内存中获取最新的数据了,同时填充各个缓存。
总结一下:
Eureka Client:负责将这个服务的信息注册到Eureka Server中Eureka Server:注册中心,里面有一个注册表,保存了各个服务所在的机器和端口号
三、Spring Cloud核心组件:Feign
实现优雅的rpc调用:解决完服务在哪里之后,该如何去调用服务呢?难道订单服务要自己写一大堆代码,跟其他服务建立网络连接,然后构造一个复杂的请求,接着发送请求过去,最后对返回的响应结果再写一大堆代码来处理吗?想到这里是不是直想挠头,为了保住自己本来就不多的头发!来看看Feign是如何处理的吧!
看完上面的代码是不是感觉干净清爽!没有底层的建立连接、构造请求、解析响应的代码,直接就是用注解定义一个 FeignClient接口,然后调用那个接口就可以了,Feign都帮你做好了。
Feign实现这些的原理:一个关键机制就是使用了动态代理。
首先,如果你对某个接口定义了@FeignClient注解,Feign就会针对这个接口创建一个动态代理接着你要是调用那个接口,本质就是会调用 Feign创建的动态代理,这是核心中的核心Feign的动态代理会根据你在接口上的@RequestMapping等注解,来动态构造出你要请求的服务的地址最后针对这个地址,发起请求、解析响应
四、Spring Cloud核心组件:Ribbon
服务集群部署:如果库存服务部署了3台怎么办,如下所示:
192.168.10.10:7000192.168.10.11:7000192.168.10.13:7000
问题来了,Feign怎么知道该请求哪台机器呢?这时就轮到Ribbon出马了,其提供了解决微服务负载均衡的问题。Ribbon 是一个基于Http和TCP的客服端负载均衡工具,它是基于Netflix Ribbon实现的。它不像spring cloud服务注册中心、配置中心、API网关那样独立部署,但是它几乎存在于每个spring cloud 微服务中。包括feign提供的声明式服务调用也是基于该Ribbon实现的。
Ribbon的负载均衡默认使用的最经典的Round Robin轮询算法。如果订单服务对库存服务发起10次请求,那就先让你请求第1台机器、然后是第2台机器、第3台机器,接着再来—个循环,第1台机器、第2台机器。。。以此类推。其他常见的负载均衡策略还包括权重轮询WeightedResponseTimeRule,随机策略RandomRule,最少并发数策略BestAvailableRule等等
此外,Ribbon是和Feign以及Eureka紧密协作,完成工作的,具体如下:
首先Ribbon会从 Eureka Client里获取到对应的服务注册表,也就知道了所有的服务都部署在了哪些机器上,在监听哪些端口号。然后Ribbon就可以使用默认的Round Robin算法,从中选择一台机器Feign就会针对这台机器,构造并发起请求
对上述整个过程,再来一张图:
五、Spring Cloud核心组件:Hystrix
断路器,旨在通过熔断机制控制服务和其依赖服务之间的延迟和故障。比如:在一个大型的微服务架构里,一个服务可能要依赖很多服务,像本文的业务场景,订单服务依赖库存服务、物流服务、积分服务三个服务。假设订单服务最多只有100个线程可以处理请求,如果库存服务挂了,一般会抛出一个异常。如果系统处于高并发的场景下,大量请求涌过来的时候,订单服务的100个线程都会卡在请求库存服务这块,这导致订单服务没有一个线程可以处理请求,服务器完全不响应任何请求。
如上图,这么多服务互相调用,要是不做任何保护的话,某一个服务挂了,就会引起连锁反应,导致别的服务也挂。比如服务B挂了,会导致服务A的线程全部卡在请求服务B这里,没有一个线程可以工作。上面这个,就是微服务架构中恐怖的服务雪崩问题。
解决方式:
使用Hystrix。Hystrix是隔离、熔断以及降级的一个框架。Hystrix处理方式是提供很多个小小的线程池(其中一种解决方式),比如订单服务请求库存服务是一个线程池,请求物流服务是一个线程池,请求积分服务是一个线程池。每个线程池里的线程就仅仅用于请求那个服务。
打个比方:现在很不幸,服务B挂了,会咋样?
当然会导致服务A的那个用来调用服务B的线程都卡死不能工作了啊!但是由于服务A调用服务C、服务D的这两个线程池都是正常工作的,所以这两个服务不会受到任何影响。但是如果服务B都挂了,服务A每次调用都要去卡住几秒钟干啥呢?有意义吗?当然没有!所以我们直接对服务B熔断不就得了,比如在5分钟内请求积分服务直接就返回了,不要去走网络请求卡住几秒钟,这个过程,就是所谓的熔断!如果服务A知道服务B已经熔断了,可以做一些特殊的处理,比如返回一些兜底的数据或者友好的提示,这个过程,就是所谓的降级。
为帮助大家更直观的理解,接下来用一张图,梳理一下Hystrix隔离、熔断和降级的全流程:
六、Spring Cloud核心组件:Zuul
前面的文章我们介绍了,Eureka用于服务的注册于发现,Feign和Ribbon支持服务的调用以及均衡负载,Hystrix处理服务的熔断防止故障扩散。我们还是少考虑了一个问题,外部的应用如何来访问内部各种各样的微服务呢?在微服务架构中,后端服务往往不直接开放给调用端,而是通过一个API网关根据请求的url,路由到相应的服务。当添加API网关后,在第三方调用端和服务提供方之间就创建了一面墙,这面墙直接与调用方通信进行权限控制,后将请求均衡分发给后台服务
为什么需要微服务网关?
在微服务架构模式下后端服务的实例数一般是动态的,动态改变的服务实例的访问地址信息很难被客户端发现。因此在基于微服务的项目中为了简化前端的调用逻辑,引入API Gateway作为轻量级网关,而且有一个网关之后,还有很多好处,比如可以做统一的降级、限流、认证授权、安全,等等。
七、高并发下配置优化
合理的hystrix线程池大小:
假设你的服务A,每秒钟会接收30个请求,同时会向服务B发起30个请求,然后每个请求的响应时长经验值大概在200ms,那么你的hystrix线程池需要多少个线程呢?计算公式是:30(每秒请求数量) * 0.2(每个请求的处理秒数) + 4(给点缓冲buffer) = 10(线程数量)。
为什么10个线程可以轻松抗住每秒30个请求?
一个线程200毫秒可以执行完一个请求,那么一个线程1秒可以执行5个请求,理论上,只要6个线程,每秒就可以执行30个请求。也就是说,线程里的10个线程中,就6个线程足以抗住每秒30个请求了。剩下4个线程都在玩儿,空闲着。那为啥要多搞4个线程呢?很简单,因为你要留一点buffer空间。万一在系统高峰期,系统性能略有下降,此时不少请求都耗费了300多毫秒才执行完,那么一个线程每秒只能处理3个请求了,10个线程刚刚好勉强可以hold住每秒30个请求。所以你必须多考虑留几个线程。
合理的超时时间设置
一个接口,理论的最佳响应速度应该在200ms以内,或者慢点的接口就几百毫秒。如果一个接口响应时间达到1秒+,建议考虑用缓存、索引、NoSQL等各种你能想到的技术手段,优化一下性能。否则你要是胡乱设置超时时间是几秒,甚至几十秒,万一下游服务偶然出了点问题响应时间长了点呢?那你这个线程池里的线程立马全部卡死!合理的超时时间设置为多少?答案应该不超过300毫秒(这个需要根据实际压测数据做些优化)。为啥呢?如果你的超时时间设置成了500毫秒,想想可能会有什么后果?考虑极端情况,如果服务B响应变慢,要500毫秒才响应,你一个线程每秒最多只能处理2个请求了,10个线程只能处理20个请求。大量的线程会全部卡死,来不及处理那么多请求,最后用户会刷不出来页面。如果你的线程池大小和超时时间没有配合着设置好,很可能会导致服务B短暂的性能波动,瞬间导致服务A的线程池卡死,里面的线程要卡顿一段时间才能继续执行下一个请求。哪怕一段时间后,服务B的接口性能恢复到200毫秒以内了,服务A的线程池里卡死的状况也要好一会儿才能恢复过来。你的超时时间设置的越不合理,比如设置的越长,设置到了1秒、2秒,那么这种卡死的情况就需要越长的时间来恢复。所以说,此时你的超时时间得设置成300毫秒,保证一个请求300毫秒内执行不完,立马超时返回。这样线程池里的线程不会长时间卡死,可以有条不紊的处理多出来的请求,大不了就是300毫秒内处理不完立即超时返回,但是线程始终保持可以运行的状态。这样当服务B的接口性能恢复到200毫秒以内后,服务A的线程池里的线程很快就可以恢复。
八、总结:
总结一下,上述几个Spring Cloud核心组件,在微服务架构中,分别扮演的角色:
Eureka:各个服务在启动时,Eureka Client组件会将本服务注册到Eureka Server,集群以应用名做区分,并且Eureka Client还可以反过来从Eureka Server拉取注册表,从而知道其他服务在哪里Ribbon:微服务之间发起请求的时候,基于Ribbon做负载均衡,从服务集群中选择一台Feign:基于Feign的动态代理机制,根据注解和选择的机器,拼接请求URL地址,发起请求Hystrix:发起请求是通过Hystrix的线程池来走的,不同的服务走不同的线程池,实现了不同服务调用的隔离,避免了服务雪崩的问题Zuul:微服务路由
当然spring cloud家族还有Spring Cloud Config、Spring Cloud Bus、Spring Cloud for Cloud Foundry、Spring Cloud Cluster、Spring Cloud Consul、Spring Cloud Security、Spring Cloud Sleuth、Spring Cloud Data Flow、Spring Cloud Stream、Spring Cloud Task、Spring Cloud Zookeeper、Spring Cloud Connectors、Spring Cloud Starters、Spring Cloud CLI等等,具体信息请查看:
中文文档:
英文文档:
来源:网易工程师-周延旭
有任何问题欢迎留言交流~
整理总结不易,如果觉得这篇文章有意思的话,欢迎转发、收藏,给我一些鼓励~
有想看的内容或者建议,敬请留言!
最近利用空余时间整理了一些精选Java架构学习视频和大厂项目底层知识点,需要的同学欢迎私信我发给你~一起学习进步!有任何问题也欢迎交流~
Java日记本,每日存档超实用的技术干货学习笔记,每天陪你前进一点点~
标签: #2000的并发量需要几台服务器 #500并发需要什么配置