- 浏览: 568827 次
- 性别:
- 来自: 北京
文章分类
- 全部博客 (267)
- 随笔 (4)
- Spring (13)
- Java (61)
- HTTP (3)
- Windows (1)
- CI(Continuous Integration) (3)
- Dozer (1)
- Apache (11)
- DB (7)
- Architecture (41)
- Design Patterns (11)
- Test (5)
- Agile (1)
- ORM (3)
- PMP (2)
- ESB (2)
- Maven (5)
- IDE (1)
- Camel (1)
- Webservice (3)
- MySQL (6)
- CentOS (14)
- Linux (19)
- BI (3)
- RPC (2)
- Cluster (9)
- NoSQL (7)
- Oracle (25)
- Loadbalance (7)
- Web (5)
- tomcat (1)
- freemarker (1)
- 制造 (0)
最新评论
-
panamera:
如果设置了连接需要密码,Dynamic Broker-Clus ...
ActiveMQ 集群配置 -
panamera:
请问你的最后一种模式Broker-C节点是不是应该也要修改持久 ...
ActiveMQ 集群配置 -
maosheng:
longshao_feng 写道楼主使用 文件共享 模式的ma ...
ActiveMQ 集群配置 -
longshao_feng:
楼主使用 文件共享 模式的master-slave,produ ...
ActiveMQ 集群配置 -
tanglanwen:
感触很深,必定谨记!
少走弯路的十条忠告
SpringCloud分布式开发五大组件:
服务发现——Netflix Eureka
客服端负载均衡——Netflix Ribbon
断路器——Netflix Hystrix
服务网关——Netflix Zuul
分布式配置——Spring Cloud Config
一、Eureka
由两个组件组成:Eureka服务器和Eureka客户端
Eureka Client:负责将这个服务的信息注册到Eureka Server中
Eureka Server:注册中心,里面有一个注册表,保存了各个服务所在的机器和端口号
Eureka的工作管理:
服务需要有一个统一的名称(或服务ID)并且是唯一标识,以便于接口调用时各个接口的区分。并且需要将其注册到Eureka Server中,其他服务调用该接口时,也是根据这个唯一标识来获取。
服务下有多个实例,每个实例也有一个自己的唯一实例ID。因为它们各自有自己的基础信息如:不同的IP。所以它们的信息也需要注册到Eureka Server中,其他服务调用它们的服务接口时,可以查看到多个该服务的实例信息,根据负载策略提供某个实例的调用信息后,调用者根据信息直接调用该实例。
→在Eureka Client启动的时候,将自身的服务的信息发送到Eureka Server。然后进行获取服务信息,调用当前服务器节点中的其他服务信息,保存到Eureka Client中。当服务间相互调用其它服务时,在Eureka Client中获取服务信息(如服务地址,端口等)后,进行根据获取到的服务信息直接调用服务。(注:服务的调用通过http(s)调用)
→当某个服务仅需要调用其他服务,自身不提供服务调用时。在Eureka Client启动后会拉取Eureka Server的其他服务信息,需要调用时,在Eureka Client的本地缓存中获取信息,调用服务。
→Eureka Client通过向Eureka Serve发送心跳(默认每30秒)来续约服务的。 如果客户端持续不能续约,那么,它将在大约90秒内从服务器注册表中删除。 注册信息和续订被复制到集群中的Eureka Serve所有节点。 以此来确保当前服务还“活着”,可以被调用。
→来自任何区域的Eureka Client都可以查找注册表信息(每30秒发生一次),以此来确保调用到的服务是“活的”。并且当某个服务被更新或者新加进来,也可以调用到新的服务。
Eureka Server:
提供服务注册:各个微服务启动时,会通过Eureka Client向Eureka Server进行注册自己的信息(例如服务信息和网络信息),Eureka Server会存储该服务的信息。
提供服务信息提供:服务消费者在调用服务时,本地Eureka Client没有的情况下,会到Eureka Server拉取信息。
提供服务管理:通过Eureka Client的Cancel、心跳监控、renew等方式来维护该服务提供的信息以确保该服务可用以及服务的更新。
信息同步:每个Eureka Server同时也是Eureka Client,多个Eureka Server之间通过P2P复制的方式完成服务注册表的同步。同步时,被同步信息不会同步出去。也就是说有3个Eureka Server,Server1有新的服务信息时,同步到Server2后,Server2和Server3同步时,Server2不会把从Server1那里同步到的信息同步给Server3,只能由Server1自己同步给Server3。
每个可用区有一个Eureka集群,并且每个可用区至少有一个eureka服务器来处理区内故障。为了实现高可用,一般一个可用区中由三个Eureka Server组成。
Eureka Client:
Eureka Client是一个Java客户端,用于简化与Eureka Server的交互。并且管理当前微服务,同时为当前的微服务提供服务提供者信息。
Eureka Client会拉取、更新和缓存Eureka Server中的信息。即使所有的Eureka Server节点都宕掉,服务消费者依然可以使用缓存中的信息找到服务提供者。
Eureka Client在微服务启动后,会周期性地向Eureka Server发送心跳(默认周期为30秒)以续约自己的信息。如果Eureka Server在一定时间内没有接收到某个微服务节点的心跳,Eureka Server将会注销该微服务节点(默认90秒)。
Eureka Client包含服务提供者Applicaton Service和服务消费者Application Client
Applicaton Service:服务提供者,提供服务给别个调用。
Application Client:服务消费者,调用别个提供的服务。
往往大多数服务本身既是服务提供者,也是服务消费者。
Register:服务注册
当Eureka客户端向Eureka Server注册时,它提供自身的元数据,比如IP地址、端口,运行状况指示符URL,主页等。
Renew:服务续约
Eureka Client会每隔30秒发送一次心跳来续约。 通过续约来告知Eureka Server该Eureka客户仍然存在,没有出现问题。 正常情况下,如果Eureka Server在90秒没有收到Eureka客户的续约,它会将实例从其注册表中删除。 建议不要更改续约间隔。
Fetch Registries:获取注册列表信息
Eureka客户端从服务器获取注册表信息,并将其缓存在本地。客户端会使用该信息查找其他服务,从而进行远程调用。该注册列表信息定期(每30秒钟)更新一次。每次返回注册列表信息可能与Eureka客户端的缓存信息不同, Eureka客户端自动处理。如果由于某种原因导致注册列表信息不能及时匹配,Eureka客户端则会重新获取整个注册表信息。 Eureka服务器缓存注册列表信息,整个注册表以及每个应用程序的信息进行了压缩,压缩内容和没有压缩的内容完全相同。Eureka客户端和Eureka 服务器可以使用JSON / XML格式进行通讯。在默认的情况下Eureka客户端使用压缩JSON格式来获取注册列表的信息。
Cancel:服务下线
Eureka客户端在程序关闭时向Eureka服务器发送取消请求。 发送请求后,该客户端实例信息将从服务器的实例注册表中删除。该下线请求不会自动完成,它需要调用以下内容:
DiscoveryManager.getInstance().shutdownComponent();
Eviction:服务剔除
在默认的情况下,当Eureka客户端连续90秒没有向Eureka服务器发送服务续约,即心跳,Eureka服务器会将该服务实例从服务注册列表删除,即服务剔除。
服务续约有两个参数是可以配置的,即 Eureka Client 发送续约心跳的时间参数和 Eureka Server 在多长时间内没有收到心跳将实例剔除的时间参数。在默认情况下,这两个参数分别为 30 秒和 90 秒, 官方的建议是不要修改,如果有特殊需求还是可以调整的,只需要分别
在 Eureka Client 和 Eureka Server 的配置文件 application.yml 中加以下的配置:
eureka.instance.leaseRenewalIntervalInSeconds
eureka.instance.leaseExpirationDurationInSeconds
Eureka Client 获取服务实例慢原因:
( 1 ) Eureka Client 的注册延迟
Eureka Client 启动之后,不是立即向Eureka Server 注册的,而是有一个延迟向服务端注册的时间。通过跟踪源码,可以发现默认的延迟时间为40 秒,代码如下:
public int getini tialinstanceinfoReplicationintervalSeconds () (
return configinstance.getintProperty(
namespace + INITIAL_REGISTRATION_REPLICATION_DELAY_KEY, 40) .get();
}
( 2) Eureka Server 的响应缓存
Eureka Server 维护每30 秒更新一次响应缓存,可通过更改配置eureka.server.responseCache.UpdatelntervalMs 来修改。所以即使是刚刚注册的实例,也不会立即出现在服务注册列表中。
( 3 ) Eureka Client 的缓存
Eureka Client 保留注册表信息的缓存。该缓存每 30 秒更新一次。因此, Eureka Client
刷新本地缓存并发现其他新注册的实例可能需要 30 秒。
( 4) LoadBalancer 的缓存
Ribbon 的负载平衡器从本地的 Eureka Client 获取服务注册列表信息。Ribbon 本身还维护了缓存,以避免每个请求都需要从Eureka Client 获取服务注册列表。此缓存每 30 秒刷新一次(可由ribbon.ServerListRefreshlnterval 配置) ,所以可能至少需要 30 秒的时间才能使用新注册的实例。
综上因素,一个新注册的实例,默认延迟 40 秒向服务注册中心注册,所以不能马上被Eureka Server 发现。另外,刚注册的Eureka Client 也不能立即被其他服务调用,原因是调用方由于各种缓存没有及时获取到最新的服务注册列表信息。
Eureka还有一种自我保护机制,如果在15分钟内超过85%的节点都没有正常的心跳,那么Eureka就认为客户端与注册中心出现了网络故障,此时会出现以下几种情况:
1. Eureka不再从注册列表中移除因为长时间没收到心跳而应该过期的服务
2. Eureka仍然能够接受新服务的注册和查询请求,但是不会被同步到其它节点上(即保证当前节点依然可用)
3. 当网络稳定时,当前实例新的注册信息会被同步到其它节点中
Eurka 保证 AP:
Eureka Server 各个节点都是平等的,几个节点挂掉不会影响正常节点的工作,剩余的节点依然可以提供注册和查询服务。而 Eureka Client 在向某个 Eureka 注册时,如果发现连接失败,则会自动切换至其它节点。只要有一台 Eureka Server 还在,就能保证注册服务可用(保证可用性),只不过查到的信息可能不是最新的(不保证强一致性)。
二、Ribbon
主要提供客户侧的软件负载均衡算法。
Ribbon客户端组件提供一系列完善的配置选项,比如连接超时、重试、重试算法等。Ribbon内置可插拔、可定制的负载均衡组件。下面是用到的一些负载均衡策略:
BestAvailableRule : 选择最小请求数。
ClientConfigEnabledRoundRobinRule :轮询。
RandomRule : 随机选择一个server 。
RoundRobinRule : 轮询选择server 。
RetryRule : 根据轮询的方式重试。
WeightedResponseTimeRule : 根据响应时间去分配一个weight , weight 越低,被选择的可能性就越低。
ZoneAvoidanceRule :根据server 的zone 区域和可用性来轮询选择。
Ribbon的工作原理:
负载均衡是指将负载分摊到多个执行单元上,常见的负载均衡有两种方式。一种是独立进程单元,通过负载均衡策略,将请求转发到不同的执行单元上,例如 Ngnix 。另一种是将负载均衡逻辑以代码的形式封装到服务消费者的客户端上,服务消费者客户端维护了一份服务提供者的信息列表,有了信息列表,通过负载均衡策略将请求分摊给多个服务提供者,从而达到负载均衡的目的。
Ribbon 它属于上述的第二种方式,是将负载均衡逻辑封装在客户端中,并且运行在客户端的进程里。
在Spring Cloud 构建的微服务系统中, Ribbon 作为服务消费者的负载均衡器,有两种使用方式, 一种是和 RestTemplate 相结合,另一种是和 Feign 相结合。Feign 已经默认集成了Ribbon。
Ribbon 的负载均衡主要是通过 LoadBalancerClient 来实现的,而 LoadBalancerClient具体交给了 ILoadBalancer 来处理, ILoadBalancer 通过配置IRule、IPing 等,向EurekaClient获取注册列表的信息,默认每 10 秒向 EurekaClient 发送一次“ping ”, 进而检查是否需要更新服务的注册列表信息。最后,在得到服务注册列表信息后,ILoadBalancer 根据 IRule 的策略进行负载均衡。
而RestTemplate 加上@LoadBalance 注解后,在远程调度时能够负载均衡, 主要是维护了一个被@LoadBalance 注解的 RestTemplate 列表,并给该列表中的RestTemplate 对象添加了拦截器。在拦截器的方法中,将远程调度方法交给了 Ribbon 的负载均衡器LoadBalancerClient 去处理,从而达到了负载均衡的目的。
LoadBalancerClient 是在初始化时向 Eureka 获取服务注册列表信息, 井且每 10 秒向EurekaClient 发送“ ping ”,来判断服务的可用性。如果服务的可用性发生了改变或者服务数量和之前的不一致,则更新或者重新拉取。LoadBalancerClient 有了这些服务注册列表信息,就可以根据具体的 IRule 的策略来进行负载均衡。
三、Hystrix
在分布式系统中,服务与服务之间的依赖错综复杂, 一种不可避免的情况就是某些服务会出现故障,导致依赖于它们的其他服务出现远程调度的线程阻塞。
在高并发的情况下,单个服务的延迟会导致整个请求都处于延迟状态,可能在几秒钟就使整个服务处于线程负载饱和的状态。
某个服务的单个点的请求故障会导致用户的请求处于阻塞状态,最终的结果就是整个服务的线程资源消耗殆尽。由于服务的依赖性,会导致依赖于该故障服务的其他服务也处于线程阻塞状态,最终导致这些服务的线程资源消耗殆尽, 直到不可用,从而导致整个问服务系统都不可用,即雪崩效应。为了防止雪崩效应,因而产生了熔断器模型。
Hystrix 的设计原则如下:
1.防止单个服务的故障耗尽整个服务的 Servlet 容器( 例如Tomcat )的线程资源。
2.快速失败机制,如果某个服务出现了故障,则调用该服务的请求快速失败,而不是线程等待。
3.提供回退( fallback )方案,在请求发生故障时,提供设定好的回退方案。
4.使用熔断机制,防止故障扩散到其他服务。
Hystrix 的工作机制:
首先,当服务的某个API 接口的失败次数在一定时间内小于设定的阀值时,熔断器处于关闭状态,该API 接口正常提供服务。当该API 接口处理请求的失败次数大于设定的阀值时,Hystrix 判定该API 接口出现了故障,打开熔断器,这时请求该API 接口会执行快速失败的逻辑(即fallback 回退的逻辑),不执行业务逻辑,请求的线程不会处于阻塞状态。处于打开状态的熔断器, 一段时间后会处于半打开状态,并将一定数量的请求执行正常逻辑。剩余的请求会执行快速失败,若执行正常逻辑的请求失败了,则熔断器继续打开;若成功了,则将熔断器关闭。这样熔断器就具有了自我修复的能力。
四、Zuul
是一个网关组件。提供动态路由,监控,弹性,安全等边缘服务的框架。
Zuul 作为微服务系统的网关组件,用于构建边界服务( EdgeService ),致力于动态路由、过滤、监控、弹性伸缩和安全。
1.Zuul 、Ribbon 以及Eureka 相结合,可以实现智能路由和负载均衡的功能, Zuul 能够将请求流量按某种策略分发到集群状态的多个服务实例。
2.网关将所有服务的API 接口统一聚合,并统一对外暴露。外界系统调用API 接口时,都是由网关对外暴露的API 接口,外界系统不需要知道微服务系统中各服务相互调用的复杂性。微服务系统也保护了其内部微服务单元的API 接口, 防止其被外界直接调用,导致服务的敏感信息对外暴露。
3.网关服务可以做用户身份认证和权限认证,防止非法请求操作API 接口,对服务器起到保护作用。
4.网关可以实现监控功能,实时日志输出,对请求进行记录。
5.网关可以用来实现流量监控, 在高流量的情况下,对服务进行降级。
Zuul的工作原理:
Zuul 是通过Servlet 来实现的, Zuul 通过自定义的ZuulServlet (类似于Spring MVC 的DispatcServlet〕来对请求进行控制。Zuul 的核心是一系列过滤器,可以在Http 请求的发起和响应返回期间执行一系列的过滤器。Zuul 包括以下4 种过滤器:
1.PRE 过滤器: 它是在请求路由到具体的服务之前执行的,这种类型的过滤器可以做安全验证,例如身份验证、参数验证等。
2.ROUTING 过滤器: 它用于将请求路由到具体的微服务实例。在默认情况下,它使用Http Client 进行网络请求。
3.POST 过滤器:它是在请求己被路由到微服务后执行的。一般情况下,用作收集统计信息、指标,以及将响应传输到客户端。
4.ERROR 过滤器:它是在其他过滤器发生错误时执行的。
Zuul 采取了动态读取、编译和运行这些过滤器。过滤器之间不能直接相互通信,而是通过RequestContext 对象来共享数据, 每个请求都会创建一个RequestContext 对象。Zuul 过滤器具有以下关键特性。
1) Type (类型) : Zuul 过滤器的类型,这个类型决定了过滤器在请求的哪个阶段起作用,例如Pre 、Post 阶段等。
2) Execution Order (执行顺序) :规定了过滤器的执行顺序, Order 的值越小,越先执行。
3) Criteria (标准) : Filter 执行所需的条件。
4) Action (行动〉: 如果符合执行条件,则执行Action (即逻辑代码)。
当一个客户端Request 请求进入Zuul 网关服务时,网关先进入“pre filter”飞进行一系列的验证、操作或者判断。然后交给“routing filter”进行路由转发,转发到具体的服务实例进行逻辑处理、返回数据。当具体的服务处理完后,最后由“post filter”进行处理,该类型的处理器处理完之后,将Response 信息返回给客户端。
根据用户名来过滤代码:
package com.test.springcloud.zuul;
import javax.servlet.http.HttpServletRequest;
import com.netflix.zuul.ZuulFilter;
import com.netflix.zuul.context.RequestContext;
public class AccessUserNameFilter extends ZuulFilter {
@Override
public Object run() {
RequestContext ctx = RequestContext.getCurrentContext();
HttpServletRequest request = ctx.getRequest();
System.out.println(String.format("%s AccessUserNameFilter request to %s", request.getMethod(), request.getRequestURL().toString()));
String username = request.getParameter("username");// 获取请求的参数
if(null != username && username.equals("tester")) {// 如果请求的参数不为空,且值为chhliu时,则通过
ctx.setSendZuulResponse(true);// 对该请求进行路由
ctx.setResponseStatusCode(200);
ctx.set("isSuccess", true);// 设值,让下一个Filter看到上一个Filter的状态
return null;
}else{
ctx.setSendZuulResponse(false);// 过滤该请求,不对其进行路由
ctx.setResponseStatusCode(401);// 返回错误码
ctx.setResponseBody("{\"result\":\"username is not correct!\"}");// 返回错误内容
ctx.set("isSuccess", false);
return null;
}
}
@Override
public boolean shouldFilter() {
return true;// 是否执行该过滤器,此处为true,说明需要过滤
}
@Override
public int filterOrder() {
return 0;// 优先级为0,数字越大,优先级越低
}
@Override
public String filterType() {
return "pre";// 前置过滤器
}
}
@SpringBootApplication
@EnableDiscoveryClient
@EnableZuulProxy
public class ZuulServerApplication {
public static void main(String[] args) {
SpringApplication.run(ZuulServerApplication.class, args);
}
@Bean
public AccessUserNameFilter accessUserNameFilter(){
return new AccessUserNameFilter();
}
}
filterType:返回一个字符串代表过滤器的类型,在zuul中定义了四种不同生命周期的过滤器类型,具体如下:
pre:可以在请求被路由之前调用
route:在路由请求时候被调用
post:在route和error过滤器之后被调用
error:处理请求时发生错误时被调用
Zuul的主要请求生命周期包括“pre”,“route”和“post”等阶段。对于每个请求,都会运行具有这些类型的所有过滤器。
filterOrder:通过int值来定义过滤器的执行顺序
shouldFilter:返回一个boolean类型来判断该过滤器是否要执行,所以通过此函数可实现过滤器的开关。在上例中,我们直接返回true,所以该过滤器总是生效
run:过滤器的具体逻辑。需要注意,这里我们通过ctx.setSendZuulResponse(false)令zuul过滤该请求,不对其进行路由,然后通过ctx.setResponseStatusCode(401)设置了其返回的错误码
过滤器间的协调:过滤器没有直接的方式来访问对方。 在 Filter 之间,通过com.netflix.zuul.context.RequestContext 类来进行通信,内部采用 ThreadLocal 保存每个请求的一些信息,包括请求路由、错误信息、HttpServletRequest、HttpServletResponse,这使得一些操作是十分可靠的,它还扩展了 ConcurrentHashMap,目的是为了在处理过程中保存任何形式的信息。
请求上下文RequestContext通过ThreadLocal存储,需要在请求完成后删除该对象。RequestContext提供了执行filter Pipeline所需要的Context,因为Servlet是单例多线程,这就要求RequestContext即要线程安全又要Request安全。context使用ThreadLocal保存,这样每个worker线程都有一个与其绑定的RequestContext,因为worker仅能同时处理一个Request,这就保证了Request Context 即是线程安全的由是Request安全的。
通过 ctx.setSendZuulResponse(false) 设置了不路由到服务,并且返回 null,那只是当前的过滤器执行完成了,后面所有的过滤器都会执行,解决这个问题的办法就是通过shouldFilter 来处理,即在拦截之后通过数据传递的方式告诉下一个过滤器是否要执行。
增加一行数据传递的代码:
ctx.set("isSuccess", false);
在 RequestContext 中设置一个值来标识是否成功,当为 true 的时候,后续的过滤器才执行,若为 false 则不执行。
利用这种方法,在后面的过滤器就需要用到这个值来决定自己此时是否需要执行,此时只需要在 shouldFilter 方法中加上如下所示的代码即可:
public boolean shouldFilter() {
RequestContext ctx = RequestContext.getCurrentContext();
Object success = ctx.get("isSuccess");
return success == null ? true : Boolean.parseBoolean(success.toString());
}
ZuulServlet 是Zuul 的核心Servlet。ZuulServlet 的作用是初始化ZuulFilter , 并编排这些ZuulFilter 的执行顺序。该类中有一个 service()方法,执行了过滤器执行的逻辑。
@Override
public void service ( ) throws ServletException , IOException {
try {
try {
preRoute() ;
} catch (ZuulException e) {
error (e) ;
postRoute ();
return ;
}
try {
route () ;
} catch (ZuulException e) {
error (e) ;
postRoute() ;
return ;
}
try {
postRoute() ;
) catch ( ZuulException e) {
error(e) ;
return ;
) catch (Throwable e) {
error(new ZuulException(e, 500,”UNHANDLED EXCEPTION_" + e . getClass () . getNarne () ) ) ;
) finally {
RequestContext . getCurrentContext() .unset();
}
}
Zuul 是采用了类似于Spring MVC 的DispatchServlet 来实现的,采用的是异步阻塞模型,所以性能比 Ngnix 差。由于Zuul 和其他 Netflix 组件可以相互配合、无缝集成, Zuul 很容易就能实现负载均衡、智能路由和熔断器等功能。在大多数情况下, Zuul 都是以集群的形式存在的。由于Zuul 的横向扩展能力非常好,所以当负载过高时,可以通过添加实例来解决性能瓶颈。
一种常见的使用方式是对不同的渠道使用不同的Zuul 来进行路由,例如移动端共用一个Zuul 网关实例, Web 端用另一个Zuul 网关实例,其他的客户端用另外一个Zuul 实例进行路由。
另外一种常见的集群是通过 Ngnix 和 Zuul 相互结合来做负载均衡。暴露在最外面的是Ngnix 主从双热备进行 Keepalive, Ngnix 经过某种路由策略,将请求路由转发到Zuul 集群上,Zuul 最终将请求分发到具体的服务上。
五、Spring Cloud Config
远程配置服务,这个还是静态的,得配合Spring Cloud Bus实现动态的配置更新。
远程配置是每个都必不可少的中间件,远程配置的特点一般需要:多节点主备、配置化、动态修改、配置本地化缓存、动态修改的实时推送等。
config允许配置文件放在git上或者svn上,和spring boot的集成非常容易。
使用Spring Cloud Bus 刷新配置:
Spring Cloud Bus 是用轻量的消息代理将分布式的节点连接起来,可以用于广播配置文件的更改或者服务的监控管理。一个关键的思想就是,消息总线可以为微服务做监控,也可以实
现应用程序之间相互通信。Spring Cloud Bus 可选的消息代理组建包括RabbitMQ 、AMQP 和
Kafka 等。
如果有几十个微服务,而每一个服务又是多实例,当更改配置时,需要重新启动多个微服务实例,会非常麻烦。Spring Cloud Bus 的一个功能就是让这个过程变得简单,当远程 Git 仓库的配置更改后,只需要向某一个微服务实例发送一个 Post 请求,通过消息组件通知其他微服务实例重新拉取配置文件。
六、总结
Eureka:各个服务启动时,Eureka Client都会将服务注册到Eureka Server,并且Eureka Client还可以反过来从Eureka Server拉取注册表,从而知道其他服务在哪里
Ribbon:服务间发起请求的时候,基于Ribbon做负载均衡,从一个服务的多台机器中选择一台
Hystrix:发起请求是通过Hystrix的线程池来走的,不同的服务走不同的线程池,实现了不同服务调用的隔离,避免了服务雪崩的问题
Zuul:如果前端、移动端要调用后端系统,统一从Zuul网关进入,由Zuul网关转发请求给对应的服务
Dubbo 和 SpringCloud 的区别(使用Dubbo的RPC来实现服务间调用的一些痛点):
1.服务提供方与调用方接口依赖方式太强:
我们为每个微服务定义了各自的service抽象接口,并通过持续集成发布到私有仓库中,调用方应用对微服务提供的抽象接口存在强依赖关系,因此不论开发、测试、集成环境都需要严格的管理版本依赖,才不会出现服务方与调用方的不一致导致应用无法编译成功等一系列问题,以及这也会直接影响本地开发的环境要求,往往一个依赖很多服务的上层应用,每天都要更新很多代码并install之后才能进行后续的开发。若没有严格的版本管理制度或开发一些自动化工具,这样的依赖关系会成为开发团队的一大噩梦。
而REST接口相比RPC更为轻量化,服务提供方和调用方的依赖只是依靠一纸契约,不存在代码级别的强依赖,当然REST接口也有痛点,因为接口定义过轻,很容易导致定义文档与实际实现不一致导致服务集成时的问题,但是该问题很好解决,只需要通过每个服务整合swagger,让每个服务的代码与文档一体化,就能解决。所以在分布式环境下,REST方式的服务依赖要比RPC方式的依赖更为灵活。
2.服务对平台敏感,难以简单复用:
通常我们在提供对外服务时,都会以REST的方式提供出去,这样可以实现跨平台的特点,任何一个语言的调用方都可以根据接口定义来实现。那么在Dubbo中我们要提供REST接口时,不得不实现一层代理,用来将RPC接口转换成REST接口进行对外发布。若我们每个服务本身就以REST接口方式存在,当要对外提供服务时,主要在API网关中配置映射关系和权限控制就可实现服务的复用了。
服务发现——Netflix Eureka
客服端负载均衡——Netflix Ribbon
断路器——Netflix Hystrix
服务网关——Netflix Zuul
分布式配置——Spring Cloud Config
一、Eureka
由两个组件组成:Eureka服务器和Eureka客户端
Eureka Client:负责将这个服务的信息注册到Eureka Server中
Eureka Server:注册中心,里面有一个注册表,保存了各个服务所在的机器和端口号
Eureka的工作管理:
服务需要有一个统一的名称(或服务ID)并且是唯一标识,以便于接口调用时各个接口的区分。并且需要将其注册到Eureka Server中,其他服务调用该接口时,也是根据这个唯一标识来获取。
服务下有多个实例,每个实例也有一个自己的唯一实例ID。因为它们各自有自己的基础信息如:不同的IP。所以它们的信息也需要注册到Eureka Server中,其他服务调用它们的服务接口时,可以查看到多个该服务的实例信息,根据负载策略提供某个实例的调用信息后,调用者根据信息直接调用该实例。
→在Eureka Client启动的时候,将自身的服务的信息发送到Eureka Server。然后进行获取服务信息,调用当前服务器节点中的其他服务信息,保存到Eureka Client中。当服务间相互调用其它服务时,在Eureka Client中获取服务信息(如服务地址,端口等)后,进行根据获取到的服务信息直接调用服务。(注:服务的调用通过http(s)调用)
→当某个服务仅需要调用其他服务,自身不提供服务调用时。在Eureka Client启动后会拉取Eureka Server的其他服务信息,需要调用时,在Eureka Client的本地缓存中获取信息,调用服务。
→Eureka Client通过向Eureka Serve发送心跳(默认每30秒)来续约服务的。 如果客户端持续不能续约,那么,它将在大约90秒内从服务器注册表中删除。 注册信息和续订被复制到集群中的Eureka Serve所有节点。 以此来确保当前服务还“活着”,可以被调用。
→来自任何区域的Eureka Client都可以查找注册表信息(每30秒发生一次),以此来确保调用到的服务是“活的”。并且当某个服务被更新或者新加进来,也可以调用到新的服务。
Eureka Server:
提供服务注册:各个微服务启动时,会通过Eureka Client向Eureka Server进行注册自己的信息(例如服务信息和网络信息),Eureka Server会存储该服务的信息。
提供服务信息提供:服务消费者在调用服务时,本地Eureka Client没有的情况下,会到Eureka Server拉取信息。
提供服务管理:通过Eureka Client的Cancel、心跳监控、renew等方式来维护该服务提供的信息以确保该服务可用以及服务的更新。
信息同步:每个Eureka Server同时也是Eureka Client,多个Eureka Server之间通过P2P复制的方式完成服务注册表的同步。同步时,被同步信息不会同步出去。也就是说有3个Eureka Server,Server1有新的服务信息时,同步到Server2后,Server2和Server3同步时,Server2不会把从Server1那里同步到的信息同步给Server3,只能由Server1自己同步给Server3。
每个可用区有一个Eureka集群,并且每个可用区至少有一个eureka服务器来处理区内故障。为了实现高可用,一般一个可用区中由三个Eureka Server组成。
Eureka Client:
Eureka Client是一个Java客户端,用于简化与Eureka Server的交互。并且管理当前微服务,同时为当前的微服务提供服务提供者信息。
Eureka Client会拉取、更新和缓存Eureka Server中的信息。即使所有的Eureka Server节点都宕掉,服务消费者依然可以使用缓存中的信息找到服务提供者。
Eureka Client在微服务启动后,会周期性地向Eureka Server发送心跳(默认周期为30秒)以续约自己的信息。如果Eureka Server在一定时间内没有接收到某个微服务节点的心跳,Eureka Server将会注销该微服务节点(默认90秒)。
Eureka Client包含服务提供者Applicaton Service和服务消费者Application Client
Applicaton Service:服务提供者,提供服务给别个调用。
Application Client:服务消费者,调用别个提供的服务。
往往大多数服务本身既是服务提供者,也是服务消费者。
Register:服务注册
当Eureka客户端向Eureka Server注册时,它提供自身的元数据,比如IP地址、端口,运行状况指示符URL,主页等。
Renew:服务续约
Eureka Client会每隔30秒发送一次心跳来续约。 通过续约来告知Eureka Server该Eureka客户仍然存在,没有出现问题。 正常情况下,如果Eureka Server在90秒没有收到Eureka客户的续约,它会将实例从其注册表中删除。 建议不要更改续约间隔。
Fetch Registries:获取注册列表信息
Eureka客户端从服务器获取注册表信息,并将其缓存在本地。客户端会使用该信息查找其他服务,从而进行远程调用。该注册列表信息定期(每30秒钟)更新一次。每次返回注册列表信息可能与Eureka客户端的缓存信息不同, Eureka客户端自动处理。如果由于某种原因导致注册列表信息不能及时匹配,Eureka客户端则会重新获取整个注册表信息。 Eureka服务器缓存注册列表信息,整个注册表以及每个应用程序的信息进行了压缩,压缩内容和没有压缩的内容完全相同。Eureka客户端和Eureka 服务器可以使用JSON / XML格式进行通讯。在默认的情况下Eureka客户端使用压缩JSON格式来获取注册列表的信息。
Cancel:服务下线
Eureka客户端在程序关闭时向Eureka服务器发送取消请求。 发送请求后,该客户端实例信息将从服务器的实例注册表中删除。该下线请求不会自动完成,它需要调用以下内容:
DiscoveryManager.getInstance().shutdownComponent();
Eviction:服务剔除
在默认的情况下,当Eureka客户端连续90秒没有向Eureka服务器发送服务续约,即心跳,Eureka服务器会将该服务实例从服务注册列表删除,即服务剔除。
服务续约有两个参数是可以配置的,即 Eureka Client 发送续约心跳的时间参数和 Eureka Server 在多长时间内没有收到心跳将实例剔除的时间参数。在默认情况下,这两个参数分别为 30 秒和 90 秒, 官方的建议是不要修改,如果有特殊需求还是可以调整的,只需要分别
在 Eureka Client 和 Eureka Server 的配置文件 application.yml 中加以下的配置:
eureka.instance.leaseRenewalIntervalInSeconds
eureka.instance.leaseExpirationDurationInSeconds
Eureka Client 获取服务实例慢原因:
( 1 ) Eureka Client 的注册延迟
Eureka Client 启动之后,不是立即向Eureka Server 注册的,而是有一个延迟向服务端注册的时间。通过跟踪源码,可以发现默认的延迟时间为40 秒,代码如下:
public int getini tialinstanceinfoReplicationintervalSeconds () (
return configinstance.getintProperty(
namespace + INITIAL_REGISTRATION_REPLICATION_DELAY_KEY, 40) .get();
}
( 2) Eureka Server 的响应缓存
Eureka Server 维护每30 秒更新一次响应缓存,可通过更改配置eureka.server.responseCache.UpdatelntervalMs 来修改。所以即使是刚刚注册的实例,也不会立即出现在服务注册列表中。
( 3 ) Eureka Client 的缓存
Eureka Client 保留注册表信息的缓存。该缓存每 30 秒更新一次。因此, Eureka Client
刷新本地缓存并发现其他新注册的实例可能需要 30 秒。
( 4) LoadBalancer 的缓存
Ribbon 的负载平衡器从本地的 Eureka Client 获取服务注册列表信息。Ribbon 本身还维护了缓存,以避免每个请求都需要从Eureka Client 获取服务注册列表。此缓存每 30 秒刷新一次(可由ribbon.ServerListRefreshlnterval 配置) ,所以可能至少需要 30 秒的时间才能使用新注册的实例。
综上因素,一个新注册的实例,默认延迟 40 秒向服务注册中心注册,所以不能马上被Eureka Server 发现。另外,刚注册的Eureka Client 也不能立即被其他服务调用,原因是调用方由于各种缓存没有及时获取到最新的服务注册列表信息。
Eureka还有一种自我保护机制,如果在15分钟内超过85%的节点都没有正常的心跳,那么Eureka就认为客户端与注册中心出现了网络故障,此时会出现以下几种情况:
1. Eureka不再从注册列表中移除因为长时间没收到心跳而应该过期的服务
2. Eureka仍然能够接受新服务的注册和查询请求,但是不会被同步到其它节点上(即保证当前节点依然可用)
3. 当网络稳定时,当前实例新的注册信息会被同步到其它节点中
Eurka 保证 AP:
Eureka Server 各个节点都是平等的,几个节点挂掉不会影响正常节点的工作,剩余的节点依然可以提供注册和查询服务。而 Eureka Client 在向某个 Eureka 注册时,如果发现连接失败,则会自动切换至其它节点。只要有一台 Eureka Server 还在,就能保证注册服务可用(保证可用性),只不过查到的信息可能不是最新的(不保证强一致性)。
二、Ribbon
主要提供客户侧的软件负载均衡算法。
Ribbon客户端组件提供一系列完善的配置选项,比如连接超时、重试、重试算法等。Ribbon内置可插拔、可定制的负载均衡组件。下面是用到的一些负载均衡策略:
BestAvailableRule : 选择最小请求数。
ClientConfigEnabledRoundRobinRule :轮询。
RandomRule : 随机选择一个server 。
RoundRobinRule : 轮询选择server 。
RetryRule : 根据轮询的方式重试。
WeightedResponseTimeRule : 根据响应时间去分配一个weight , weight 越低,被选择的可能性就越低。
ZoneAvoidanceRule :根据server 的zone 区域和可用性来轮询选择。
Ribbon的工作原理:
负载均衡是指将负载分摊到多个执行单元上,常见的负载均衡有两种方式。一种是独立进程单元,通过负载均衡策略,将请求转发到不同的执行单元上,例如 Ngnix 。另一种是将负载均衡逻辑以代码的形式封装到服务消费者的客户端上,服务消费者客户端维护了一份服务提供者的信息列表,有了信息列表,通过负载均衡策略将请求分摊给多个服务提供者,从而达到负载均衡的目的。
Ribbon 它属于上述的第二种方式,是将负载均衡逻辑封装在客户端中,并且运行在客户端的进程里。
在Spring Cloud 构建的微服务系统中, Ribbon 作为服务消费者的负载均衡器,有两种使用方式, 一种是和 RestTemplate 相结合,另一种是和 Feign 相结合。Feign 已经默认集成了Ribbon。
Ribbon 的负载均衡主要是通过 LoadBalancerClient 来实现的,而 LoadBalancerClient具体交给了 ILoadBalancer 来处理, ILoadBalancer 通过配置IRule、IPing 等,向EurekaClient获取注册列表的信息,默认每 10 秒向 EurekaClient 发送一次“ping ”, 进而检查是否需要更新服务的注册列表信息。最后,在得到服务注册列表信息后,ILoadBalancer 根据 IRule 的策略进行负载均衡。
而RestTemplate 加上@LoadBalance 注解后,在远程调度时能够负载均衡, 主要是维护了一个被@LoadBalance 注解的 RestTemplate 列表,并给该列表中的RestTemplate 对象添加了拦截器。在拦截器的方法中,将远程调度方法交给了 Ribbon 的负载均衡器LoadBalancerClient 去处理,从而达到了负载均衡的目的。
LoadBalancerClient 是在初始化时向 Eureka 获取服务注册列表信息, 井且每 10 秒向EurekaClient 发送“ ping ”,来判断服务的可用性。如果服务的可用性发生了改变或者服务数量和之前的不一致,则更新或者重新拉取。LoadBalancerClient 有了这些服务注册列表信息,就可以根据具体的 IRule 的策略来进行负载均衡。
三、Hystrix
在分布式系统中,服务与服务之间的依赖错综复杂, 一种不可避免的情况就是某些服务会出现故障,导致依赖于它们的其他服务出现远程调度的线程阻塞。
在高并发的情况下,单个服务的延迟会导致整个请求都处于延迟状态,可能在几秒钟就使整个服务处于线程负载饱和的状态。
某个服务的单个点的请求故障会导致用户的请求处于阻塞状态,最终的结果就是整个服务的线程资源消耗殆尽。由于服务的依赖性,会导致依赖于该故障服务的其他服务也处于线程阻塞状态,最终导致这些服务的线程资源消耗殆尽, 直到不可用,从而导致整个问服务系统都不可用,即雪崩效应。为了防止雪崩效应,因而产生了熔断器模型。
Hystrix 的设计原则如下:
1.防止单个服务的故障耗尽整个服务的 Servlet 容器( 例如Tomcat )的线程资源。
2.快速失败机制,如果某个服务出现了故障,则调用该服务的请求快速失败,而不是线程等待。
3.提供回退( fallback )方案,在请求发生故障时,提供设定好的回退方案。
4.使用熔断机制,防止故障扩散到其他服务。
Hystrix 的工作机制:
首先,当服务的某个API 接口的失败次数在一定时间内小于设定的阀值时,熔断器处于关闭状态,该API 接口正常提供服务。当该API 接口处理请求的失败次数大于设定的阀值时,Hystrix 判定该API 接口出现了故障,打开熔断器,这时请求该API 接口会执行快速失败的逻辑(即fallback 回退的逻辑),不执行业务逻辑,请求的线程不会处于阻塞状态。处于打开状态的熔断器, 一段时间后会处于半打开状态,并将一定数量的请求执行正常逻辑。剩余的请求会执行快速失败,若执行正常逻辑的请求失败了,则熔断器继续打开;若成功了,则将熔断器关闭。这样熔断器就具有了自我修复的能力。
四、Zuul
是一个网关组件。提供动态路由,监控,弹性,安全等边缘服务的框架。
Zuul 作为微服务系统的网关组件,用于构建边界服务( EdgeService ),致力于动态路由、过滤、监控、弹性伸缩和安全。
1.Zuul 、Ribbon 以及Eureka 相结合,可以实现智能路由和负载均衡的功能, Zuul 能够将请求流量按某种策略分发到集群状态的多个服务实例。
2.网关将所有服务的API 接口统一聚合,并统一对外暴露。外界系统调用API 接口时,都是由网关对外暴露的API 接口,外界系统不需要知道微服务系统中各服务相互调用的复杂性。微服务系统也保护了其内部微服务单元的API 接口, 防止其被外界直接调用,导致服务的敏感信息对外暴露。
3.网关服务可以做用户身份认证和权限认证,防止非法请求操作API 接口,对服务器起到保护作用。
4.网关可以实现监控功能,实时日志输出,对请求进行记录。
5.网关可以用来实现流量监控, 在高流量的情况下,对服务进行降级。
Zuul的工作原理:
Zuul 是通过Servlet 来实现的, Zuul 通过自定义的ZuulServlet (类似于Spring MVC 的DispatcServlet〕来对请求进行控制。Zuul 的核心是一系列过滤器,可以在Http 请求的发起和响应返回期间执行一系列的过滤器。Zuul 包括以下4 种过滤器:
1.PRE 过滤器: 它是在请求路由到具体的服务之前执行的,这种类型的过滤器可以做安全验证,例如身份验证、参数验证等。
2.ROUTING 过滤器: 它用于将请求路由到具体的微服务实例。在默认情况下,它使用Http Client 进行网络请求。
3.POST 过滤器:它是在请求己被路由到微服务后执行的。一般情况下,用作收集统计信息、指标,以及将响应传输到客户端。
4.ERROR 过滤器:它是在其他过滤器发生错误时执行的。
Zuul 采取了动态读取、编译和运行这些过滤器。过滤器之间不能直接相互通信,而是通过RequestContext 对象来共享数据, 每个请求都会创建一个RequestContext 对象。Zuul 过滤器具有以下关键特性。
1) Type (类型) : Zuul 过滤器的类型,这个类型决定了过滤器在请求的哪个阶段起作用,例如Pre 、Post 阶段等。
2) Execution Order (执行顺序) :规定了过滤器的执行顺序, Order 的值越小,越先执行。
3) Criteria (标准) : Filter 执行所需的条件。
4) Action (行动〉: 如果符合执行条件,则执行Action (即逻辑代码)。
当一个客户端Request 请求进入Zuul 网关服务时,网关先进入“pre filter”飞进行一系列的验证、操作或者判断。然后交给“routing filter”进行路由转发,转发到具体的服务实例进行逻辑处理、返回数据。当具体的服务处理完后,最后由“post filter”进行处理,该类型的处理器处理完之后,将Response 信息返回给客户端。
根据用户名来过滤代码:
package com.test.springcloud.zuul;
import javax.servlet.http.HttpServletRequest;
import com.netflix.zuul.ZuulFilter;
import com.netflix.zuul.context.RequestContext;
public class AccessUserNameFilter extends ZuulFilter {
@Override
public Object run() {
RequestContext ctx = RequestContext.getCurrentContext();
HttpServletRequest request = ctx.getRequest();
System.out.println(String.format("%s AccessUserNameFilter request to %s", request.getMethod(), request.getRequestURL().toString()));
String username = request.getParameter("username");// 获取请求的参数
if(null != username && username.equals("tester")) {// 如果请求的参数不为空,且值为chhliu时,则通过
ctx.setSendZuulResponse(true);// 对该请求进行路由
ctx.setResponseStatusCode(200);
ctx.set("isSuccess", true);// 设值,让下一个Filter看到上一个Filter的状态
return null;
}else{
ctx.setSendZuulResponse(false);// 过滤该请求,不对其进行路由
ctx.setResponseStatusCode(401);// 返回错误码
ctx.setResponseBody("{\"result\":\"username is not correct!\"}");// 返回错误内容
ctx.set("isSuccess", false);
return null;
}
}
@Override
public boolean shouldFilter() {
return true;// 是否执行该过滤器,此处为true,说明需要过滤
}
@Override
public int filterOrder() {
return 0;// 优先级为0,数字越大,优先级越低
}
@Override
public String filterType() {
return "pre";// 前置过滤器
}
}
@SpringBootApplication
@EnableDiscoveryClient
@EnableZuulProxy
public class ZuulServerApplication {
public static void main(String[] args) {
SpringApplication.run(ZuulServerApplication.class, args);
}
@Bean
public AccessUserNameFilter accessUserNameFilter(){
return new AccessUserNameFilter();
}
}
filterType:返回一个字符串代表过滤器的类型,在zuul中定义了四种不同生命周期的过滤器类型,具体如下:
pre:可以在请求被路由之前调用
route:在路由请求时候被调用
post:在route和error过滤器之后被调用
error:处理请求时发生错误时被调用
Zuul的主要请求生命周期包括“pre”,“route”和“post”等阶段。对于每个请求,都会运行具有这些类型的所有过滤器。
filterOrder:通过int值来定义过滤器的执行顺序
shouldFilter:返回一个boolean类型来判断该过滤器是否要执行,所以通过此函数可实现过滤器的开关。在上例中,我们直接返回true,所以该过滤器总是生效
run:过滤器的具体逻辑。需要注意,这里我们通过ctx.setSendZuulResponse(false)令zuul过滤该请求,不对其进行路由,然后通过ctx.setResponseStatusCode(401)设置了其返回的错误码
过滤器间的协调:过滤器没有直接的方式来访问对方。 在 Filter 之间,通过com.netflix.zuul.context.RequestContext 类来进行通信,内部采用 ThreadLocal 保存每个请求的一些信息,包括请求路由、错误信息、HttpServletRequest、HttpServletResponse,这使得一些操作是十分可靠的,它还扩展了 ConcurrentHashMap,目的是为了在处理过程中保存任何形式的信息。
请求上下文RequestContext通过ThreadLocal存储,需要在请求完成后删除该对象。RequestContext提供了执行filter Pipeline所需要的Context,因为Servlet是单例多线程,这就要求RequestContext即要线程安全又要Request安全。context使用ThreadLocal保存,这样每个worker线程都有一个与其绑定的RequestContext,因为worker仅能同时处理一个Request,这就保证了Request Context 即是线程安全的由是Request安全的。
通过 ctx.setSendZuulResponse(false) 设置了不路由到服务,并且返回 null,那只是当前的过滤器执行完成了,后面所有的过滤器都会执行,解决这个问题的办法就是通过shouldFilter 来处理,即在拦截之后通过数据传递的方式告诉下一个过滤器是否要执行。
增加一行数据传递的代码:
ctx.set("isSuccess", false);
在 RequestContext 中设置一个值来标识是否成功,当为 true 的时候,后续的过滤器才执行,若为 false 则不执行。
利用这种方法,在后面的过滤器就需要用到这个值来决定自己此时是否需要执行,此时只需要在 shouldFilter 方法中加上如下所示的代码即可:
public boolean shouldFilter() {
RequestContext ctx = RequestContext.getCurrentContext();
Object success = ctx.get("isSuccess");
return success == null ? true : Boolean.parseBoolean(success.toString());
}
ZuulServlet 是Zuul 的核心Servlet。ZuulServlet 的作用是初始化ZuulFilter , 并编排这些ZuulFilter 的执行顺序。该类中有一个 service()方法,执行了过滤器执行的逻辑。
@Override
public void service ( ) throws ServletException , IOException {
try {
try {
preRoute() ;
} catch (ZuulException e) {
error (e) ;
postRoute ();
return ;
}
try {
route () ;
} catch (ZuulException e) {
error (e) ;
postRoute() ;
return ;
}
try {
postRoute() ;
) catch ( ZuulException e) {
error(e) ;
return ;
) catch (Throwable e) {
error(new ZuulException(e, 500,”UNHANDLED EXCEPTION_" + e . getClass () . getNarne () ) ) ;
) finally {
RequestContext . getCurrentContext() .unset();
}
}
Zuul 是采用了类似于Spring MVC 的DispatchServlet 来实现的,采用的是异步阻塞模型,所以性能比 Ngnix 差。由于Zuul 和其他 Netflix 组件可以相互配合、无缝集成, Zuul 很容易就能实现负载均衡、智能路由和熔断器等功能。在大多数情况下, Zuul 都是以集群的形式存在的。由于Zuul 的横向扩展能力非常好,所以当负载过高时,可以通过添加实例来解决性能瓶颈。
一种常见的使用方式是对不同的渠道使用不同的Zuul 来进行路由,例如移动端共用一个Zuul 网关实例, Web 端用另一个Zuul 网关实例,其他的客户端用另外一个Zuul 实例进行路由。
另外一种常见的集群是通过 Ngnix 和 Zuul 相互结合来做负载均衡。暴露在最外面的是Ngnix 主从双热备进行 Keepalive, Ngnix 经过某种路由策略,将请求路由转发到Zuul 集群上,Zuul 最终将请求分发到具体的服务上。
五、Spring Cloud Config
远程配置服务,这个还是静态的,得配合Spring Cloud Bus实现动态的配置更新。
远程配置是每个都必不可少的中间件,远程配置的特点一般需要:多节点主备、配置化、动态修改、配置本地化缓存、动态修改的实时推送等。
config允许配置文件放在git上或者svn上,和spring boot的集成非常容易。
使用Spring Cloud Bus 刷新配置:
Spring Cloud Bus 是用轻量的消息代理将分布式的节点连接起来,可以用于广播配置文件的更改或者服务的监控管理。一个关键的思想就是,消息总线可以为微服务做监控,也可以实
现应用程序之间相互通信。Spring Cloud Bus 可选的消息代理组建包括RabbitMQ 、AMQP 和
Kafka 等。
如果有几十个微服务,而每一个服务又是多实例,当更改配置时,需要重新启动多个微服务实例,会非常麻烦。Spring Cloud Bus 的一个功能就是让这个过程变得简单,当远程 Git 仓库的配置更改后,只需要向某一个微服务实例发送一个 Post 请求,通过消息组件通知其他微服务实例重新拉取配置文件。
六、总结
Eureka:各个服务启动时,Eureka Client都会将服务注册到Eureka Server,并且Eureka Client还可以反过来从Eureka Server拉取注册表,从而知道其他服务在哪里
Ribbon:服务间发起请求的时候,基于Ribbon做负载均衡,从一个服务的多台机器中选择一台
Hystrix:发起请求是通过Hystrix的线程池来走的,不同的服务走不同的线程池,实现了不同服务调用的隔离,避免了服务雪崩的问题
Zuul:如果前端、移动端要调用后端系统,统一从Zuul网关进入,由Zuul网关转发请求给对应的服务
Dubbo 和 SpringCloud 的区别(使用Dubbo的RPC来实现服务间调用的一些痛点):
1.服务提供方与调用方接口依赖方式太强:
我们为每个微服务定义了各自的service抽象接口,并通过持续集成发布到私有仓库中,调用方应用对微服务提供的抽象接口存在强依赖关系,因此不论开发、测试、集成环境都需要严格的管理版本依赖,才不会出现服务方与调用方的不一致导致应用无法编译成功等一系列问题,以及这也会直接影响本地开发的环境要求,往往一个依赖很多服务的上层应用,每天都要更新很多代码并install之后才能进行后续的开发。若没有严格的版本管理制度或开发一些自动化工具,这样的依赖关系会成为开发团队的一大噩梦。
而REST接口相比RPC更为轻量化,服务提供方和调用方的依赖只是依靠一纸契约,不存在代码级别的强依赖,当然REST接口也有痛点,因为接口定义过轻,很容易导致定义文档与实际实现不一致导致服务集成时的问题,但是该问题很好解决,只需要通过每个服务整合swagger,让每个服务的代码与文档一体化,就能解决。所以在分布式环境下,REST方式的服务依赖要比RPC方式的依赖更为灵活。
2.服务对平台敏感,难以简单复用:
通常我们在提供对外服务时,都会以REST的方式提供出去,这样可以实现跨平台的特点,任何一个语言的调用方都可以根据接口定义来实现。那么在Dubbo中我们要提供REST接口时,不得不实现一层代理,用来将RPC接口转换成REST接口进行对外发布。若我们每个服务本身就以REST接口方式存在,当要对外提供服务时,主要在API网关中配置映射关系和权限控制就可实现服务的复用了。
发表评论
文章已被作者锁定,不允许评论。
-
HTTPS的加密原理解读
2021-12-31 11:25 300一、为什么需要加密? 因为http的内容是明文传输的,明文数据 ... -
容器技术的基石: cgroup、namespace和联合文件系统
2021-12-09 10:47 729Docker 是基于 Linux Kernel 的 Names ... -
链路追踪skywalking安装部署
2021-10-21 12:06 816APM 安装部署: 一、下载 版本目录地址:http://a ... -
自动化运维 Ansible 安装部署
2021-08-20 19:06 844一、概述 Ansible 实现了批量系统配置、批量程序部署、 ... -
Linux 下 Kafka Cluster 搭建
2021-07-08 11:23 978概述 http://kafka.apachecn.org/q ... -
ELK RPM 安装配置
2021-06-22 18:59 621相关组件: 1)filebeat。用于收集日志组件,经测试其 ... -
在Kubernetes上部署 Redis 三主三从 集群
2021-03-10 16:25 670NFS搭建见: Linux NFS搭建与配置(https:// ... -
docker-compose 部署ELK(logstash->elasticsearch->kibana)
2020-11-11 18:02 1606概述: ELK是三个开源软件的缩写,分别表示:elastic ... -
Kubernetes1.16.3下部署node-exporter+alertmanager+prometheus+grafana 监控系统
2020-10-28 10:48 1074准备工作 建议将所有的yaml文件存在如下目录: # mkd ... -
Linux NFS 搭建与配置
2020-10-21 17:58 427一、NFS 介绍 NFS 是 Network FileSys ... -
K8S 备份及升级
2020-10-20 15:48 881一、准备工作 查看集群版本: # kubectl get no ... -
API 网关 kong 的 konga 配置使用
2020-09-23 10:46 4219一、Kong 概述: kong的 ... -
云原生技术 Docker、K8S
2020-09-02 16:53 558容器的三大好处 1.资源 ... -
Kubernetes 应用编排、管理与运维
2020-08-24 16:40 586一、kubectl 运维命令 kubectl control ... -
API 网关 kong/konga 安装部署
2020-08-25 17:34 603一、概述 Kong是Mashape开 ... -
Linux 下 Redis Cluster 搭建
2020-08-13 09:14 749Redis集群演变过程: 单 ... -
Kubernetes离线安装的本地yum源构建
2020-08-08 22:41 541一、需求场景 在K8S的使用过程中有时候会遇到在一些无法上网 ... -
Kubernetes 证书延期
2020-08-01 22:28 472一、概述 kubeadm 是 kubernetes 提供的一 ... -
kubeadm方式部署安装kubernetes
2020-07-29 08:01 2397一、前提准备: 0、升级更新系统(切记升级一下,曾被坑过) ... -
Kubernetes 部署 Nginx 集群
2020-07-20 09:32 878一.设置标签 为了保证nginx之能分配到nginx服务器需要 ...
相关推荐
微服务架构是一种将大型复杂应用程序分解为一组...Spring Cloud的这些组件相互协作,构建出强大的微服务生态系统,使得开发者能够轻松地实现服务治理、监控和服务间通信等功能,极大地简化了微服务架构的实现和运维。
相比之下,Spring Cloud是一整套微服务解决方案,包含多个子项目,如Spring Cloud Config(配置管理)、Spring Cloud Netflix(包含Eureka服务发现、Zuul智能路由等组件),提供了构建分布式系统所需的全套工具。...
文档中详细介绍了spring cloud的相关组件,包含了服务...的相关介绍,对于初学者或者 通过 PPT介绍springcloud 组件非常有帮助,文档中包含图形和文字,利于学习和讲解。本人也是需要讲解,找了很久的资源,与大家分享
SpringCloud基础概念与入门 SpringCloud核心组件Eureka服务注册与发现 SpringCloud核心组件Ribbon负载...SpringCloud微服务架构设计与实践 SpringCloud服务治理与最佳实践 SpringCloud与DockerKubernetes集成部署
这个面试专题系列涵盖了三个关键的技术框架:Dubbo、Spring Boot和Spring Cloud,这些都是构建微服务架构的核心组件。以下是对这些技术的详细解读: 1. **Dubbo**:由阿里巴巴开源的高性能Java RPC框架,它为服务...
总而言之,SpringCloud微服务分布式架构开发实战课程涵盖了微服务架构的关键技术,通过作业和参考答案,你可以加深对SpringCloud组件的理解,提升微服务应用的开发能力。在实践中不断学习,才能更好地应对复杂的...
本套笔记全面覆盖了SpringCloud微服务架构的关键知识点,从理论到实践,帮助读者深入了解并掌握微服务设计原则和SpringCloud的实现方式,对于想要在微服务领域深化学习的Java开发者来说是一份宝贵的资料。
SpringCloud微服务架构技术分享 四个方面: 一、单体应用架构存在的问题:主要介绍目前传统项目的单体应用架构的问题和局限性 二、微服务架构介绍:介绍微服务架构的来源和应用场景,以及传统项目往微服务架构的...
【SpringCloud微服务架构视频】是一系列视频教程,总计五十一课,专注于讲解SpringCloud的原理及实践操作,尤其适合初学者和希望深入理解微服务架构的开发者。本教程的前十二节已提供,后续内容可通过作者的博客获取...
毫无疑问,Spring Cloud是目前微服务架构领域的翘楚,无数的书籍博客都在讲解这个技术。不过大多数讲解还停留在对Spring Cloud功能使用的层面,其底层的很多原理,很多人可能并不知晓。因此本文将通过大量的手绘图,...
3. **Spring Cloud**:作为Spring Boot的延伸,Spring Cloud提供了实现微服务架构所需的各种工具和组件。其中包括Eureka(服务发现)、Zuul或Ribbon(负载均衡)、Hystrix(断路器)、Config Server(配置中心)等。...
而SpringCloud由众多子项目组成,提供了包括配置管理、服务发现、断路器、智能路由等在内的全套解决方案,大大简化了微服务架构的搭建。其中,SpringCloudConfig实现了统一配置中心,方便了配置的统一管理;...
上传的这个microservicecloud工程的主要使用了SpringCloud的5大技术栈做了一个微服务架构案例,涉及到Eureka集群的配置、Ribbon的自定义负载均衡、Feign的声明式接口调用、Hystrix的服务熔断和降级、Zuul的Api ...
Spring Cloud是实现微服务架构的一套框架,它提供了许多工具和服务,以简化微服务的开发和管理。Spring Cloud基于Spring Boot,使得创建和配置微服务变得简单易行。Spring Boot是一个快速开发框架,用于简化Spring...
总结而言,SpringCloud微服务架构升级总结不仅仅是对微服务和Spring Cloud框架的介绍,更是对如何将这些现代架构理念和技术应用于实际开发中的一次深刻思考。通过对微服务、Spring Boot和Spring Cloud的深入理解和...
在实际开发中,Spring Cloud提供的这些组件可以根据具体需求进行选择和组合,构建出符合业务场景的微服务架构。了解并熟练掌握这些组件的使用,将极大地提升开发效率和系统质量,使开发者能够更加专注于业务逻辑的...
总之,Spring Cloud Hystrix作为微服务架构中的关键组件,通过断路器机制提高了系统的稳定性和容错性。结合Eureka Server和Feign,我们可以构建出一个健壮且可靠的分布式系统。开发者应该深入理解Hystrix的工作原理...
微服务架构和SpringCloud专题培训课件 微服务架构是指将一个单体式的应用程序拆分成许多小的、独立的服务, 每个服务都能被独立地开发、测试、部署和扩展。这些服务之间通过轻量级的通信机制进行交互,实现业务功能...
综上所述,"微服务分布式 springcloud+seata+nacos+mysql8"的学习涉及了构建高可用、可扩展的微服务架构所需的核心技术,包括服务治理、分布式事务处理、配置中心和数据库管理。这些技术的掌握对于理解和实施微服务...