SpringCloud 总结

核心组件:

  1. Eureka 服务发现框架
  2. Ribbon 进程内负载均衡器
  3. Open Feign 服务调用映射
  4. Hystrix 服务降级熔断器
  5. Zuul 微服务网关
  6. Config 微服务统一配置中心
  7. Bus 消息总线
  8. Outh2(还需学习)
  9. Sleuth(还需学习)

Eureka 服务发现框架

在这里插入图片描述服务发现 :其实就是一个“中介”,整个过程中有三个角色:服务提供者(出租房子的)、服务消费者(租客)、服务中介(房屋中介) 。

服务提供者 :就是提供一些自己能够执行的一些服务给外界。

服务消费者 :就是需要使用一些服务的“用户”。

服务注册 Register:

官方解释:当 Eureka 客户端向 Eureka Server 注册时,它提供自身的元数据,比如IP地址、端口,运行状况指示符URL,主页等。

结合中介理解:房东 (提供者 Eureka Client Provider)在中介 (服务器 Eureka Server) 那里登记房屋的信息,比如面积,价格,地段等等(元数据 metaData)。

服务续约 Renew:

官方解释:Eureka 客户会每隔30秒(默认情况下)发送一次心跳来续约。通过续约来告知 Eureka Server 该 Eureka 客户仍然存在,没有出现问题。正常情况下,如果 Eureka Server 在90秒没有收到 Eureka 客户的续约,它会将实例从其注册表中删除

结合中介理解:房东 (提供者 Eureka Client Provider) 定期告诉中介 (服务器 Eureka Server) 我的房子还租(续约) ,中介 (服务器Eureka Server) 收到之后继续保留房屋的信息。

获取注册列表信息 Fetch Registries:

官方解释:Eureka 客户端从服务器获取注册表信息,并将其缓存在本地。客户端会使用该信息查找其他服务,从而进行远程调用。该注册列表信息定期(每30秒钟)更新一次。每次返回注册列表信息可能与 Eureka 客户端的缓存信息不同, Eureka 客户端自动处理。如果由于某种原因导致注册列表信息不能及时匹配,Eureka 客户端则会重新获取整个注册表信息。Eureka 服务器缓存注册列表信息,整个注册表以及每个应用程序的信息进行了压缩,压缩内容和没有压缩的内容完全相同。Eureka 客户端和 Eureka 服务器可以使用JSON / XML格式进行通讯。在默认的情况下 Eureka 客户端使用压缩 JSON 格式来获取注册列表的信息。

结合中介理解:租客(消费者 Eureka Client Consumer) 去中介 (服务器 Eureka Server) 那里获取所有的房屋信息列表 (客户端列表 Eureka Client List) ,而且租客为了获取最新的信息会定期向中介 (服务器 Eureka Server) 那里获取并更新本地列表。

服务下线 Cancel:

官方解释:Eureka客户端在程序关闭时向Eureka服务器发送取消请求。发送请求后,该客户端实例信息将从服务器的实例注册表中删除。该下线请求不会自动完成,它需要调用以下内容:DiscoveryManager.getInstance().shutdownComponent();

结合中介理解:房东 (提供者 Eureka Client Provider) 告诉中介 (服务器 Eureka Server) 我的房子不租了,中介之后就将注册的房屋信息从列表中剔除。

服务剔除 Eviction:

官方解释:在默认的情况下,当Eureka客户端连续90秒(3个续约周期)没有向Eureka服务器发送服务续约,即心跳,Eureka服务器会将该服务实例从服务注册列表删除,即服务剔除。

结合中介理解:房东(提供者 Eureka Client Provider) 会定期联系 中介 (服务器 Eureka Server) 告诉他我的房子还租(续约),如果中介 (服务器 Eureka Server) 长时间没收到提供者的信息,那么中介会将他的房屋信息给下架(服务剔除)。

官网NetFlix 给出的 Eureka 架构图
在这里插入图片描述

当然,可以充当服务发现的组件有很多:Zookeeper ,Consul , Eureka 等。

Eureka: 符合AP原则 为了保证了可用性,Eureka 不会等待集群所有节点都已同步信息完成,它会无时无刻提供服务。
Zookeeper: 符合CP原则 为了保证一致性,在所有节点同步完成之前是阻塞状态的

Eureka Server 自我保护˙机制
当 Eureka Server 在某种特定情况下 Eureka Server 不会剔除其注册列表中的实例,那就是 Eureka 的自我保护时期。
何为自我保护? 假想一下,当一个 server 节点出现了网络分区等不可抗力原因,那么它会因此收不到 client 的续约心跳,如果网络波动比较大,也就可能导致 server 因为一次网络波动剔除了所有或者绝大部分 Client 。这种情况是我们不想看见的
所以 Eureka 会有一种自我保护机制,**默认是15分钟内收到的续约低于原来的85%(**这是上面的续约配置比例)那么就会开启 自我保护 。这阶段 Eureka Server 不会剔除其列表中的实例即使过了 90秒 也不会

负载均衡之 Ribbon

RestTemplate是Spring提供的一个访问Http服务的客户端类 ,怎么说呢?就是微服务之间的调用是使用的 RestTemplate 。比如这个时候我们 消费者B 需要调用 提供者A 所提供的服务我们就需要这么写。如我下面的伪代码

@Autowired
private RestTemplate restTemplate;
// 这里是提供者A的ip地址,但是如果使用了 Eureka 那么就应该是提供者A的名称
private static final String SERVICE_PROVIDER_A = "http://localhost:8081";

@PostMapping("/judge")
public boolean judge(@RequestBody Request request) {
    String url = SERVICE_PROVIDER_A + "/service1";
    return restTemplate.postForObject(url, request, Boolean.class);
}

Eureka 框架中的 注册 、续约 等,底层都是使用的 RestTemplate

为什么需要 Ribbon?

是一个客户端/进程内负载均衡器,运行在消费者端

Nginx 和 Ribbon 的对比

Nignx 和 Ribbon 不同的是,它是一种集中式 的负载均衡器。
何为集中式呢?简单理解就是 将所有请求都集中起来,然后再进行负载均衡 。如下图。
在这里插入图片描述
我们可以看到 Nginx 是接收了所有的请求进行负载均衡的,而对于 Ribbon 来说它是在消费者端进行的负载均衡。如下图。
在这里插入图片描述
请注意
Request 的位置,
Nginx 中请求是先进入负载均衡器,而在 Ribbon 中是先在客户端进行负载均衡才进行请求的。

Ribbon的几个负载均衡算法:
负载均衡,不管 Nginx 还是 Ribbon 都需要其算法的支持,如果我没记错的话 Nginx 使用的是 轮询和加权轮询算法。而在 Ribbon 中有更多的负载均衡调度算法,其默认是使用的 RoundRobinRule 轮询策略。

RoundRobinRule :轮询策略。Ribbon 默认采用的策略。若经过一轮轮询没有找到可用的 provider,其最多轮询 10 轮。若最终还没有找到,则返回 null。

RandomRule : 随机策略,从所有可用的 provider 中随机选择一个。

RetryRule : 重试策略。先按照 RoundRobinRule 策略获取 provider,若获取失败,则在指定的时限内重试。默认的时限为 500 毫秒。

只需要配置即可

providerName:
  ribbon:
    NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RandomRule

当然,在 Ribbon 中你还可以自定义负载均衡算法 ,你只需要实现 IRule 接口,然后修改配置文件或者自定义 Java Config 类。

OpenFeign

有了 Eureka,RestTemplate,Ribbon 我们就可以😃愉快地进行服务间的调用了,但是使用 RestTemplate 还是不方便,我们每次都要进行这样的调用。

@Autowired
private RestTemplate restTemplate;
// 这里是提供者A的ip地址,但是如果使用了 Eureka 那么就应该是提供者A的名称
private static final String SERVICE_PROVIDER_A = "http://localhost:8081";

@PostMapping("/judge")
public boolean judge(@RequestBody Request request) {
    String url = SERVICE_PROVIDER_A + "/service1";
    // 是不是太麻烦了???每次都要 url、请求、返回类型的
    return restTemplate.postForObject(url, request, Boolean.class);
}

这样每次都调用 RestRemplate 的 API 是否太麻烦,我能不能像调用原来代码一样进行各个服务间的调用呢?
那就用 映射 呀,就像域名和IP地址的映射。

OpenFeign 也是运行在消费者端的,使用 Ribbon 进行负载均衡,所以 OpenFeign 直接内置了 Ribbon。

// 使用 @FeignClient 注解来指定提供者的名字
@FeignClient(value = "eureka-client-provider")
public interface TestClient {
    // 这里一定要注意需要使用的是提供者那端的请求相对路径,这里就相当于映射了
    @RequestMapping(value = "/provider/xxx",
    method = RequestMethod.POST)
    CommonResponse<List<Plan>> getPlans(@RequestBody planGetRequest request);
}

Hystrix

在分布式环境中,不可避免地会有许多服务依赖项中的某些失败。Hystrix是一个库,可通过添加等待时间容限和容错逻辑来帮助您控制这些分布式服务之间的交互。Hystrix通过隔离服务之间的访问点,停止服务之间的级联故障提供后备选项来实现此目的,所有这些都可以提高系统的整体弹性。

总体来说 Hystrix 就是一个能进行 熔断 和 降级 的库,通过使用它能提高整个系统的弹性。

那么什么是 熔断和降级 呢?
此时我们整个微服务系统是这样的。服务A调用了服务B,服务B再调用了服务C,但是因为某些原因,服务C顶不住了,这个时候大量请求会在服务C阻塞。
服务C阻塞了还好,毕竟只是一个系统崩溃了。但是请注意这个时候因为服务C不能返回响应,那么服务B调用服务C的的请求就会阻塞,同理服务B阻塞了,那么服务A也会阻塞崩溃。
请注意,为什么阻塞会崩溃。因为这些请求会消耗占用系统的线程、IO 等资源,消耗完你这个系统服务器不就崩了么。
在这里插入图片描述
这个就是服务雪崩!
所谓 熔断 就是服务雪崩的一种有效解决方案。当指定时间窗内的请求失败率达到设定阈值时,系统将通过 断路器 直接将此请求链路断开。
也就是我们上面服务B调用服务C在指定时间窗内,调用的失败率到达了一定的值,那么 Hystrix 则会自动将 服务B与C 之间的请求都断了,以免导致服务雪崩现象。

Hystrix两种处理模式:
1 断路器模式
2 后备处理模式
3 舱壁模式

其实这里所讲的 熔断 就是指的 Hystrix 中的 断路器模式 ,你可以使用简单的 @HystrixCommand 注解来标注某个方法,这样 Hystrix 就会使用 断路器 来“包装”这个方法,每当调用时间超过指定时间时(默认为1000ms),断路器将会中断对这个方法的调用。
当然你可以对这个注解的很多属性进行设置,比如设置超时时间,像这样。

@HystrixCommand(
    commandProperties = {@HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds",value = "1200")}
)
public List<Xxx> getXxxx() {
    // ...省略代码逻辑
}

熔断 和 降级 的概念混淆了
以我的理解,降级是为了更好的用户体验当一个方法调用异常时,通过执行另一种代码逻辑来给用户友好的回复 。这也就对应着Hystrix 的 后备处理 模式。你可以通过设置 fallbackMethod 来给一个方法设置备用的代码逻辑。比如这个时候有一个热点新闻出现了,我们会推荐给用户查看详情,然后用户会通过id去查询新闻的详情,但是因为这条新闻太火了(比如最近什么*易对吧),大量用户同时访问可能会导致系统崩溃,那么我们就进行 服务降级 ,一些请求会做一些降级处理比如当前人数太多请稍后查看等等。

// 指定了后备方法调用
@HystrixCommand(fallbackMethod = "getHystrixNews")
@GetMapping("/get/news")
public News getNews(@PathVariable("id") int id) {
    // 调用新闻系统的获取新闻api 代码逻辑省略
}
//
public News getHystrixNews(@PathVariable("id") int id) {
    // 做服务降级
    // 返回当前人数太多,请稍后查看
}

强调 舱壁模式
在不使用舱壁模式的情况下,服务A调用服务B,这种调用默认的是使用同一批线程来执行 的,而在一个服务出现性能问题的时候,就会出现所有线程被刷爆并等待处理工作,同时阻塞新请求,最终导致程序崩溃。而舱壁模式会将远程资源调用隔离在他们自己的线程池中,以便可以控制单个表现不佳的服务,而不会使该程序崩溃。

Hystrix 仪表盘 ,它是用来实时监控Hystrix 的各项指标信息的(这块需要再去看)

微服务网关——Zuul

简单来讲网关是系统唯一对外的入口,介于客户端与服务器端之间,用于对请求进行鉴权 、限流 、 路由 、监控 等功能

最重要的 功能 就是 路由 和过滤器了

Zuul 的路由功能
比如这个时候我们已经向 Eureka Server 注册了两个 Consumer 、三个 Provicer ,这个时候我们再加个 Zuul 网关应该变成这样子了。
在这里插入图片描述
Consumer 都向 Eureka Server 进行注册了,我网关是不是只要注册就能拿到所有 Consumer 的信息了?
拿到信息有什么好处呢?

我拿到信息我是不是可以获取所有的 Consumer 的元数据(名称,ip,端口)?

拿到这些元数据有什么好处呢?
拿到了我们是不是直接可以做路由映射 ?比如原来用户调用 Consumer1 的接口 localhost:8001/studentInfo/update 这个请求,我们是不是可以这样进行调用了呢?localhost:9000/consumer1/studentInfo/update 呢?你这样是不是恍然大悟了?

上面的你理解了,那么就能理解关于 Zuul 最基本的配置了,看下面。

server:
  port: 9000
eureka:
  client:
    service-url:
      # 这里只要注册 Eureka 就行了
      defaultZone: http://localhost:9997/eureka

然后在启动类上加入 @EnableZuulProxy 注解就行了。没错,就是那么简单

这么做 有什么好处?
统一前缀

这个很简单,就是我们可以在前面加一个统一的前缀,比如我们刚刚调用的是 localhost:9000/consumer1/studentInfo/update,这个时候我们在 yaml 配置文件中添加如下。

zuul:
  prefix: /zuul

这样我们就需要通过 localhost:9000/zuul/consumer1/studentInfo/update 来进行访问了。

路由策略配置

你会发现前面的访问方式(直接使用服务名),需要将微服务名称暴露给用户,会存在安全性问题。所以,可以自定义路径来替代微服务名称,即自定义路由策略。

zuul:
  routes:
    consumer1: /FrancisQ1/**
    consumer2: /FrancisQ2/**

这个时候你就可以使用 localhost:9000/zuul/FrancisQ1/studentInfo/update 进行访问了。

服务名屏蔽

这个时候你别以为你好了,你可以试试,在你配置完路由策略之后使用微服务名称还是可以访问的,这个时候你需要将服务名屏蔽。

zuul:
  ignore-services: "*"

路径屏蔽

Zuul 还可以指定屏蔽掉的路径 URI,即只要用户请求中包含指定的 URI 路径,那么该请求将无法访问到指定的服务。通过该方式可以限制用户的权限。

zuul:
  ignore-patterns: **/auto/**

这样关于 auto 的请求我们就可以过滤掉了。

** 代表匹配多级任意路径
*代表匹配一级任意路径
敏感请求头屏蔽

默认情况下,像 Cookie、Set-Cookie 等敏感请求头信息会被 zuul 屏蔽掉,我们可以将这些默认屏蔽去掉,当然,也可以添加要屏蔽的请求头。

Zuul 的过滤功能
所有请求都经过网关(Zuul),那么我们可以进行各种过滤,这样我们就能实现 限流 ,灰度发布 ,权限控制 等等。

在这里插入图片描述
滤器类型:Pre、Routing、Post。前置Pre就是在请求之前进行过滤,Routing路由过滤器就是我们上面所讲的路由策略,而Post后置过滤器就是在 Response 之前进行过滤的过滤器。你可以观察上图结合着理解,并且下面我会给出相应的注释。

典型用法:
1 实现一个请求时间打印日志。
2 令牌桶限流

Zuul 的过滤器的功能肯定不止上面我所实现的两种,它还可以实现 权限校验 ,包括我上面提到的 灰度发布 等等。
当然,Zuul 作为网关肯定也存在 单点问题 ,如果我们要保证 Zuul 的高可用,我们就需要进行 Zuul 的集群配置,这个时候可以借助额外的一些负载均衡器比如 Nginx

Spring Cloud配置管理——Config

如果我们不进行配置的统一管理,我们只能去每个应用下一个一个寻找配置文件然后修改配置文件再重启应用 。
首先对于分布式系统而言我们就不应该去每个应用下去分别修改配置文件,再者对于重启应用来说,服务无法访问所以直接抛弃了可用性,这是我们更不愿见到的。

Spring Cloud Config 为分布式系统中的外部化配置提供服务器和客户端支持。使用 Config 服务器,可以在中心位置管理所有环境中应用程序的外部属性。

我们的应用是不是只有启动的时候才会进行配置文件的加载,那么我们的 Spring Cloud Config 就暴露出一个接口给启动应用来获取它所想要的配置文件,应用获取到配置文件然后再进行它的初始化工作
在这里插入图片描述

这张图 一定好好理解 配置是通过 config server 从git 上拿去的

而一般我们会使用 Bus 消息总线 + Spring Cloud Config 进行配置的动态刷新

SpringCloud Bus

你可以简单理解为 Spring Cloud Bus 的作用就是管理和广播分布式系统中的消息 ,也就是消息引擎系统中的广播模式。当然作为 消息总线 的 Spring Cloud Bus 可以做很多事而不仅仅是客户端的配置刷新功能。

而拥有了 Spring Cloud Bus 之后,我们只需要创建一个简单的请求,并且加上 @ResfreshScope 注解就能进行配置的动态修改了,下面我画了张图供你理解。
在这里插入图片描述

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值