包含3种角色:html
Register Service:服务注册中心,它是一个 Eureka Server,提供服务注册和发现的功能。git
Provider Service:服务提供者,它是一个 Eureka Client,提供服务。github
Consumer Service:服务消费者,它是 一个 Eureka Cient,消费服务。面试
首先须要一个服务注册中心 Eureka Server,服务提供者 Eureka Client 向服务注册中心 Eureka Server 注册,将本身的信息(好比服务名和服务的 IP地址等) 经过REST API的形式提交给服务注册中心 Eureka Server。一样,服务消费者 Eureka Client也向服务注册中心Eureka Server 注册,同时服务消费者获取一份服务注册列表的信息 ,该列表包含了全部向服务注册中心Eureka Server注册的服务信息 获取服务注册列表信息以后 ,服务消费者就知道服务提供者的 IP地址,能够经过http远程调用来消费服务提供者的服务。缓存
服务注册(register)安全
eureka client向server注册时,提供自身的元数据,好比ip地址、端口等性能优化
服务续约(renew)服务器
eureka client在默认状况下会每隔30秒发送一次心跳来进行服务续约。经过服务续约来告知server该client仍然可用,没有出现故障。正常时间若是server在90秒内没有收到client的心跳,server会将client实例从注册列表中删除。网络
获取服务注册列表信息(fetch registries)架构
client从server获取服务注册表信息,将其缓存在本地,并根据服务注册列表信息查找其余服务的信息,从而进行远程调用。
服务下线(cancel)
client在程序关闭时能够向server发送下线请求。
服务剔除(eviction)
默认状况下,client连续90秒没有向server发送服务续约(即心跳)时,server会将服务从服务注册列表删除,即服务剔除。
eureka高可用架构以下图所示:
掘金-【双11狂欢的背后】微服务注册中心如何承载大型系统的千万级访问?
负载均衡是指将负载分摊到多个执行单元上,常见的负载均衡有两种方式。一种是独立进程单元,经过负载均衡策略,将请求转发到不一样的执行单元上,例如 Ngnix。另外一种是将负载均衡逻辑以代码的形式封装到服务消费者的客户端上服务消费者客户端维护了一份服务提供者的信息列表 ,有了信息列表,经过负载均衡策略将请求分摊给多个服务提供者,从而达到负载均衡的目的,如Ribbon。
在Spring Cloud构建的微服务系统中, Ribbon做为服务消费者的负载均衡器,有两种使用方式,一种是和RestTemplate相结合,另外一种是和Feign相结合。
负载均衡器的核心类为 LoadBalancerClient, LoadBalancerClient能够获取负载均衡的服务提供者的实例信息。LoadBalancerClient的choose(”eureka-client'’)方法能够轮流获得 eureka-client 的多个服务实例的信息。那么负载均衡器是怎么获取到这些客户端的信息的呢?查看官方文档能够知道 ,负载均衡器 LoadBalancerClient是从 Eureka Client获取服务注册列表信息的,并将服务注册列表信息缓存了一份。 在 LoadBalancerClient 调用 choose()方法时,根据负载均衡策略选择一个服务实例的信息,从而进行了负载均衡。 LoadBalancerClient也能够不从 Eureka Client获取注册列表信息, 这时须要本身维护一份服务注册列表信息。若是禁止Ribbon从Eureka获取注册列表信息(配置项ribbon.eureka.enabled=false),则须要本身去维护一份服务注册列表信息, 根据本身维护服务注册列表的信息, Ribbon也能够实现负载均衡。
Ribbon的负载均衡主要是经过LoadBalancerClient来实现的,而 LoadBalancerClient具体交给了ILoadBalancer来处理, ILoadBalancer经过配置IRule、IPing等,向EurekaClient获取注册列表的信息,默认每10秒向EurekaClient发送一次“ping”, 进而检查是否须要更新服务的注册列表信息。最后, 在获得服务注册列表信息后,ILoadBalancer根据IRule 的策略进行负载均衡。
Feign是一个伪Java Http客户端,Feign不作任何的请求处理。Feign经过处理注解生成Request模板,从而简化了Http API的开发。开发人员可使用注解的方式定制Request API模板。在发送Http Request请求以前 ,Feign经过处理注解的方式替换掉Request模板中的参数, 生成真正的Request,并交给 Java Http客户端去处理 。 利用这种方式,开发者只须要关注 Feign注解模板的开发 ,而不用关注Http请求自己,简化了Http请求的过程 ,使得Http请求变得简单和容易理解。
在Feign中,Client是一个很是重要的组件,Feign最终发送Request请求以及接收Response响应都是由Client组件完成的。Client在Feign源码中是一个接口,在默认的状况下,Client的实现类是Client.Default, Client.Default是由HttpURLConnnection来实现网络请求的。另外,Client还支持 HttpClient和OkHttp来进行网络请求。
Feign如何实现负载均衡?最终负载均衡交给loadBalancerContext来处理,即Ribbon。
Feign的源码实现过程以下
(1)首先经过@EnableFeignClients注解开启FeignClient的功能。只有这个注解存在,才会在程序启动时开启对@FeignClient注解的包扫描。
(2)根据Feign的规则实现接口,并在接口上面加上@FeignClient注解。
(3)程序启动后,会进行包扫描,扫描全部的@FeignClient的注解的类,并将这些信息注入IoC容器中。
(4)当接口的方法被调用时,经过JDK的代理来生成具体的RequestTemplate模板对象。
(5)根据RequestTemplate再生成Http请求的Request对象。
(6)Request对象交给Client去处理,其中Client的网络请求框架能够是HttpURLConnection、HttpClient和OkHttp。
(7)最后Client被封装到LoadBalanceClient类,这个类结合类Ribbon作到了负载均衡。
(1)Zuul、Ribbon以及Eureka相结合,能够实现智能路由和负载均衡的功能,Zuul可以将请求流量按某种策略分发到集群状态的多个服务实例。
(2)网关将全部服务的API接口统一聚合,并统一对外暴露。外界系统调用API接口时,都是由网关对外暴露的API接口,外界系统不须要知道微服务系统中各服务相互调用的复杂性。微服务系统也保护了其内部微服务单元的API接口,防止其被外界直接调用,致使服务的敏感信息对外暴露。
(3)网关服务能够作用户身份认证和权限认证,防止非法请求操做API接口,对服务器起到保护做用。
(4)网关能够实现监控功能,实时日志输出,对请求进行记录。网关能够用来实现流量监控,在高流量的状况下,对服务进行降级。
(5)API接口从内部服务分离出来,方便作测试。
Zuul是经过Servlet 来实现的,Zuul经过自定义的ZuulServlet (相似于 Spring MVC的DispatcServlet〕来对请求进行控制。Zuul的核心是一系列过滤器,能够在Http请求的发起和响应返回期间执行一系列的过滤器。Zuul包括如下4种过滤器。
PRE过滤器:它是在请求路由到具体的服务以前执行的,这种类型的过滤器能够作安全验证 ,例如身份验证、参数验证等。
ROUTING过滤器:它用于将请求路由到具体的微服务实例。在默认状况下, 它使用Http Client进行网络请求。
POST 过滤器:它是在请求己被路由到微服务后执行的。通常状况下,用做收集统计信息、指标,以及将响应传输到客户端 。
ERROR 过滤器:它是在其余过滤器发生错误时执行的。
Zuul请求的生命周期以下图所示:
若是服务存在多个实例,Zuul结合Ribbon会作负载均衡,将请求分发路由到不一样的服务实例。若是不须要用Ribbon作负载均衡,能够指定服务实例的url,直接访问指定的url(在实际开发中不可取),若是指定url,而且想作负载均衡,那么就须要本身维护负载均衡的服务注册列表,首先将ribbon.eureka.enabled设置为false,即Ribbon负载均衡客户端不向Eureka Client获取服务注册列表信息,而后须要本身维护一份注册列表。
在Zuul上配置熔断器,能够与Ribbon、Eureka和Hystirx等组件相结合,实现负载均衡、熔断器的功能。
Zuul的常见使用方式,Zuul是采用了相似于Spring MVC 的 DispatchServlet来实现的,采用的是异步阻塞模型,因此性能比Ngnix差。因为Zuul和其余 Netflix组件能够相互配合、无缝集成 , Zuul很容易就能实现负载均衡、智能路由和熔断器等功能。在大多数状况下,Zuul都是以集群的形式存在的。因为 Zuul的横向扩展能力很是好,因此当负载太高时,能够经过添加实例来解决性能瓶颈。
一种常见的使用方式是对不一样的渠道使用不一样的Zuul来进行路由,例如移动端共用一个Zuul网关实例 ,Web端用另外一个Zuul网关实例,其余的客户端用另一个Zuul实例进行路由。这种不一样的渠边用不一样 Zuul实例的架构以下图所示。
另一种常见的集群是经过Ngnix和Zuul相互结合来作负载均衡。暴露在最外面的是Ngnix主从双热备进行Keepalive, Ngnix通过某种路由策略,将请求路由转发到Zuul集群上, Zuul最终将请求分发到具体的服务上。架构图以下图所示。
掘金-拜托!面试请不要再问我Spring Cloud底层原理
掘金-【性能优化之道】每秒上万并发下的Spring Cloud参数优化实战
参考资料
掘金-大白话聊聊Java并发面试问题之微服务注册中心的读写锁优化【石杉的架构笔记】
芋道源码-注册中心 Eureka源码解析——应用实例注册发现(四)之自我保护机制
程序猿DD-微服务架构的基础框架选择:Spring Cloud仍是Dubbo?
cnblogs-两大微服务框架Dubbo和SpringCloud的对比
开源中国-服务注册中心,Eureka与Zookeeper比较
Eureka确保AP,当网络分割故障发生时,只要有一台Eureka还在就能保证注册服务可用(保证可用性),只不过查到的信息可能不是最新的(不保证强一致性)。内置心跳服务、自我保护模式,支持客户端缓存,设计哲学是同时保留”好数据“与”坏数据“总比丢掉任何”好数据“要更好,高可用与可伸缩的服务发现服务。
Zookeeper保证CP,因网络问题使得ZK集群失去master节点是较大几率会发生的事,选举耗时较长且期间整个ZK集群都是不可用的,任什么时候刻对ZK的访问请求能获得一致的数据结果,做为分布式协调服务其职责就是保证数据(注:配置数据,状态数据)在其管辖下的全部服务之间保持同步、一致。做为一个分布式协同服务,ZooKeeper很是好,可是对于Service发现服务来讲就不合适。
因为Eureka自己存在较多缓存,服务状态更新滞后,最多见的情况是:服务下线后状态没有及时更新,服务消费者调用到已下线的服务致使请求失败。
从CAP理论看,Eureka是一个AP系统,优先保证可用性(A)和分区容错性(P),不保证强一致性(C),只保证最终一致性,所以在架构中设计了较多缓存