欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

springcloud 学习笔记

程序员文章站 2022-03-05 10:26:11
...

1. Eureka 注册中心

Spring cloud 封装了Netflix开发的Eureka来实现服务的注册和发现

1.引入依赖

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
</dependency>

2.书写配置文件

server:
  port: 7001

eureka:
  instance:
    #hostname: eureka7001.com #eureka服务端的实例名称
    hostname: localhost #eureka服务端的实例名称
  client:
    register-with-eureka: false     #false表示不向注册中心注册自己。
    fetch-registry: false     #false表示自己端就是注册中心,我的职责就是维护服务实例,并不需要去检索服务
    service-url:
      #集群指向其它eureka
      #defaultZone: http://eureka7002.com:7002/eureka/
      #单机就是7001自己
      defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/
    #server:
    #关闭自我保护机制,保证不可用服务被及时踢除
    #enable-self-preservation: false
    #eviction-interval-timer-in-ms: 2000

3.书写启动类

Eureka 注册中心需要在启动类上添加@@EnableEurekaServer

在服务提供者和消费者的启动类上添加@EnableEurekaClient 同时在配置文件中添加上相关配置

package com.javacode;


import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer;

@SpringBootApplication
//标志为本类是eureka服务端
@EnableEurekaServer
public class EurekaApp {
    public static void main(String[] args) {
        SpringApplication.run(EurekaApp.class, args);
    }
}

4. 服务提供者和消费者配置

  1. 添加相关的maven依赖
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
  1. 修改yml配置文件
spring:
  application:
    #在eureka注册中心注册的服务的名字
    name: cloud-payment-service
eureka:
  client:
    #表示是否将自己注册进EurekaServer默认为true。
    register-with-eureka: true
    #是否从EurekaServer抓取已有的注册信息,默认为true。单节点无所谓,集群必须设置为true才能配合ribbon使用负载均衡
    fetchRegistry: true
    service-url:
      #单机版
      defaultZone: http://localhost:7001/eureka
      # 集群版
      #defaultZone: http://eureka7001.com:7001/eureka,http://eureka7002.com:7002/eureka
  #实例的展示名字    
  instance:
      instance-id: payment8001
      #访问路径可以显示IP地址
      prefer-ip-address: true
      #Eureka客户端向服务端发送心跳的时间间隔,单位为秒(默认是30秒)
      #lease-renewal-interval-in-seconds: 1
      #Eureka服务端在收到最后一次心跳后等待时间上限,单位为秒(默认是90秒),超时将剔除服务
      #lease-expiration-duration-in-seconds: 2
  1. 启动类上添加相应的注解

添加@@EnableEurekaClient

package com.javacode;

import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;

/**
 * @author eleven
 * @apiNote 启动类
 * @date 2020年10月24日14:48:09
 */
@SpringBootApplication
@EnableEurekaClient
@MapperScan("com.javacode.mapper")
public class PaymentApp {
    public static void main(String[] args) {
        SpringApplication.run(PaymentApp.class, args);
    }
}

  1. 启动

先启动EurekaServer、后启动EurekaClient

5. Eureka集群注册

单机模拟情况下不能都是用localhost,要修改hosts文件

C:\windows\system32\driver\etc\hosts 添加

127.0.0.1 eureka7001.com

127.0.0.1 eureka7002.com

eureka:
  instance:
    #hostname: eureka7001.com #eureka服务端的实例名称
    hostname: eureka7001.com #eureka服务端的实例名称
  client:
    register-with-eureka: false     #false表示不向注册中心注册自己。
    fetch-registry: false     #false表示自己端就是注册中心,我的职责就是维护服务实例,并不需要去检索服务
    service-url:
      #集群指向其它eureka
      #defaultZone: http://eureka7002.com:7002/eureka/
      #单机就是7001自己 多个就是用 , 隔开
      defaultZone: http://eureka7002.com:7002/eureka/
    #server:
    #关闭自我保护机制,保证不可用服务被及时踢除
    #enable-self-preservation: false
    #eviction-interval-timer-in-ms: 2000

提供服务的配置文件需要修改为集群版本的,(貌似不修改也能行)

eureka:
  client:
    fetch-registry: true
    service-url:
      defaultZone: http://erueka7001.com:7001/eureka,http://erueka7001.com:7002/eureka
    register-with-eureka: true

成功截图

成功之后微服务会注册到所有的配置中心

备用图片地址

![eureka.png](https://ae03.alicdn.com/kf/H61187cba7d8449c7ba4b185d005889b77.png)

![eureka2.png](https://ae02.alicdn.com/kf/H2d5ffaed0fa14078a4920d7d17654c235.png)

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-PlKkAh5L-1607163635929)(C:\Users\eleven\Desktop\笔记\img\eureka.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-84E3b1pd-1607163635935)(C:\Users\eleven\Desktop\笔记\img\eureka2.png)]

6. 使用RestTemplate 调用远程服务

1. 单机版

  1. 书写RestTemplate配置类
package com.javacode.config;

import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.client.RestTemplate;

/**
 * @author eleven
 */

@Configuration
public class ApplicationConfig {

    @Bean
    public RestTemplate getRestTemplate(){
        return new RestTemplate();
    }
}

  1. 直连服务

这里因为是单机版的所以可以直接写死URL 或者是用服务的名称(具体有没有用还没有做实验)

package com.javacode.controller;

import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.client.RestTemplate;
import com.javacode.util.R;
import com.javacode.entity.Payment;

@Slf4j
@RestController
@RequestMapping("/order")
public class OrderController {

    @Autowired
    private RestTemplate restTemplate;

    private static final String PAYMENT_URL = "http://localhost:8001";
    
    @PostMapping
    public R create(@RequestBody Payment payment){
        String url = PAYMENT_URL + "/payment";
        return new R<>(restTemplate.postForObject(url, payment, R.class));
    }

    @GetMapping("/{id}")
    public R getById(@PathVariable String id){
        String url = PAYMENT_URL + "/payment/" + id;
        Payment payment = restTemplate.getForObject(url, Payment.class);
        return new R<>(payment);
    }

}

2. 集群版

  1. 修改RestTemplate配置文件

使用@LoadBalanced 注解让RestTemplate拥有负载均衡的能力,即可通过微服务的名称调用集群中的微服务,默认的负载均衡模式为 轮询,即你一次我一次 。

假设有三个微服务的话 123 ,轮询模式就是 1 2 3 1 2 3这种模式调用

package com.javacode.config;

import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.client.RestTemplate;

/**
 * @author eleven
 */

@Configuration
public class ApplicationConfig {

    /**
     * 使用@LoadBalanced 让RestTemplate开启负载均衡
     * @return
     */
    @Bean
    @LoadBalanced
    public RestTemplate getRestTemplate(){
        return new RestTemplate();
    }
}
  1. 控制器

因为现在是eureka 集群,所以服务提供者不再是暴露的端口号,而是暴露的服务名称 也就是provider 中的spring.application.name ,也可以在eureka控制台找到

package com.javacode.controller;

import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.client.RestTemplate;
import com.javacode.util.R;
import com.javacode.entity.Payment;

@Slf4j
@RestController
@RequestMapping("/order")
public class OrderController {

    @Autowired
    private RestTemplate restTemplate;

    //private static final String PAYMENT_URL = "http://localhost:8001";
    private static final String PAYMENT_URL = "http://CLOUD-PAYMENT-SERVICE";

    @PostMapping
    public R create(@RequestBody Payment payment){
        String url = PAYMENT_URL + "/payment";
        return new R<>(restTemplate.postForObject(url, payment, R.class));
    }

    @GetMapping("/{id}")
    public R getById(@PathVariable String id){
        String url = PAYMENT_URL + "/payment/" + id;
        Payment payment = restTemplate.getForObject(url, Payment.class);
        return new R<>(payment);
    }

}

7. 服务发现 Discovery

对于注册进eureka里边的微服务,可以通过服务发现来获取该服务的信息

  1. 在controller中添加相关的代码
import org.springframework.cloud.client.discovery.DiscoveryClient;

public class Test{

    @Autowired
    private DiscoveryClient discoveryClient;

    @GetMapping("/discovery")
    public Object discovery(){
        //获取所有在eureka中的服务名称
        List<String> services = discoveryClient.getServices();
        services.forEach(System.out::println);
        List<ServiceInstance> instances = discoveryClient.getInstances("cloud-payment-service");
        for (ServiceInstance instance : instances) {
            log.info("serviceId:" + instance.getServiceId()+ "\tHost:" +
            instance.getHost() + "\tUri:" +instance.getUri());
        }
        return discoveryClient;
    }
}
  1. 在启动类上添加相应的注解

添加@EnableDiscoveryClient 注解

package com.javacode;

import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;

/**
 * @author eleven
 * @apiNote 启动类
 * @date 2020年10月24日14:48:09
 */
@SpringBootApplication
@EnableEurekaClient
@EnableDiscoveryClient
@MapperScan("com.javacode.mapper")
public class PaymentApp {
    public static void main(String[] args) {
        SpringApplication.run(PaymentApp.class, args);
    }
}

8. Eureka的自我保护机制

如果在Eureka Server的受压看到了以下提示则说明Eureka进入了保护模式

EMERGENCY! EUREKA MAY BE INCORRECTLY CLAIMING INSTANCES ARE UP WHEN THEY’RE NOT. RENEWALS ARE LESSER THAN THRESHOLD AND HENCE THE INSTANCES ARE NOT BEING EXPIRED JUST TO BE SAFE.

简单来说就是**某时刻某一个服务不可用了,Eureka不会立刻清理该服务,依旧会对改微服务的信息进行保存,(属于CAP里边的AP思想)**

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-1xjEaKv4-1607163635938)(C:\Users\eleven\Desktop\笔记\img\Eureka保护模式.png)]

1. 自我保护介绍

Q&A

为什么会产生Eureka自我保护机制

为了防止EurekaClient可以正常运行,但是与EurekaServer网络不通情况下,EurekaServer不会立刻将EurekaClient服务剔除

什么是自我保护机制

默认情况下,如果EurekaServer在一定时间内没有接收到某个微服务实例的心跳,EurekaServer将会注销该实例((默认90秒)。但是当网络分区故障发生(延时、卡顿、拥挤)时,微服务与EurekaServer之间无法正常通信,以上行为可能变得非常危险了——因为微服务本身其实是健康的,此时本不应该注销这个微服务。Eureka通过“自我保护模式”来解决这个问题——当EurekaServer节点在短时间内丢失过多客户端时(可能发生了网络分区故障),那么这个节点就会进入自我保护模式。

在自我保护模式中,Eureka Server会保护服务注册表中的信息,不再注销任何服务实例。font>

它的设计哲学就是宁可保留错误的服务注册信息,也不盲目注销任何可能健康的服务实例。一句话讲解:好死不如赖活着
综上,自我保护模式是一种应对网络异常的安全保护措施。它的架构哲学是宁可同时保留所有微服务(健康的微服务和不健康的微服务都会保留)也不盲目注销任何健康的微服务。使用自我保护模式,可以让Eureka集群更加的健壮、稳定。

2. 如何关闭Eureka的自我保护

erueka:
  server:
      #关闭自我保护机制,保证不可用服务被及时踢除
      enable-self-preservation: false
      eviction-interval-timer-in-ms: 2000

2. zookeeper 注册中心

1. 引入依赖

<!-- SpringBoot整合zookeeper客户端 -->
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-zookeeper-discovery</artifactId>
    <!--先排除自带的zookeeper3.5.3,解决jar包冲突问题-->
    <exclusions>
        <exclusion>
            <groupId>org.apache.zookeeper</groupId>
            <artifactId>zookeeper</artifactId>
        </exclusion>
    </exclusions>
</dependency>
<!--添加zookeeper3.4.9版本-->
<dependency>
    <groupId>org.apache.zookeeper</groupId>
    <artifactId>zookeeper</artifactId>
    <version>3.4.9</version>
</dependency>

2. 书写配置文件

server:
  port: 8004


spring:
  application:
    name: cloud-provider-payment
  cloud:
    zookeeper:
      connect-string: localhost:2181

3. 书写启动类

package com.javacode;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;

/**
 * @author eleven
 *
 */
@SpringBootApplication
@EnableDiscoveryClient
public class PaymentApp4 {
    public static void main(String[] args) {
        SpringApplication.run(PaymentApp4.class,args);
    }
}

X. 杂七杂八

  1. 启动zookeeper

windows 启动 zkServer.cmd 再启动 zkCli.cmd

linux ./zkServer.sh start再启动zkCli.sh

  1. 查看zookeeper节点
ls / 根节点 
get /zookeeper
  1. zookeeper是临时节点一短时间没有心跳包直接剔除

3. Ribbon 负载均衡

Spring cloud ribbon 是基于Netflix Ribbon实现的一个负载均衡工具

简单的说,Ribbon是Netflix发布的开源项目,主要功能是提供客户端的软件负载均衡算法和服务调用。Ribbon客户端组件提供一系列完善的配置项如连接超时,重试等。简单的说,就是在配置文件中列出Load Balancer(简称LB)后面所有的机器,Ribbon会自动的帮助你基于某种规则(如简单轮询,随机连接等)去连接这些机器。我们很容易使用Ribbon实现自定义的负载均衡算法。

1. 介绍

LB负载均衡(Load Balance)是什么
就是将用户的请求平摊的分配到多个服务上,从而达到系统的HA(高可用)。常见的负载均衡有软件Nginx,LVS,硬件F5等。

Ribbon本地负载均衡客户端和Nginx服务端负载均衡区别
Nginx是服务器负载均衡,客户端所有请求都会交给nginx,然后由nginx实现转发请求。即负载均衡是由服务端实现的。
Ribbon本地负载均衡,在调用微服务接口时候,会在注册中心上获取注册信息服务列表之后缓存到VM本地,从而在本地实现RPC远程服务调用技术。

Q&A

Ribbon是一个软负载均衡的客户端组件

服务消费者向注册中心注册,在调用服务的时候插叙可用的服务列表,根据设置的负载均衡策略来调用相关的服务

2. 示例

  • 也可以不用加这个依赖 spring-cloud-starter-netflix-eureka-client 这个依赖中自己就继承了一个ribbon 2.2.1
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-netflix-ribbon</artifactId>
    <version>2.2.1.RELEASE</version>
    <scope>compile</scope>
</dependency>

getForEntity 返回一个 ResponseEntity 其中的 StatusCode 符合Http请求的状态码,根据状态码返回不同的信息即可

@GetMapping("/entity/{id}")
public R getEntityById(@PathVariable String id){
    String url = PAYMENT_URL + "/payment/" + id;
    ResponseEntity<Payment> entity = restTemplate.getForEntity(url, Payment.class);
    if(entity.getStatusCode().is2xxSuccessful()){
        return new R<>(entity);
    }
    return new R<>();
}

3. 负载均衡

  • ribbon就是 负载均衡 + restTemplate

  • IRule 接口中选个一个有效的服务无需调用服务

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-1phBu8Jl-1607163635944)(C:\Users\eleven\Desktop\笔记\img\Irule.png)]

类名 名字 解释
com.netflix.loadbalance.RoundRobbinRule 轮询 轮着来
com.netflix.loadbalance.RandomRule 随机
com.netflix.loadbalance.RetryRule 先按照轮询
WeightResponseTimeRule 对轮询的拓展,按照响应时间
BestAvaliableRule
AvaliabilityPilteringRule 先过滤掉故障实例
ZoneAvoidanceRule

1. 负载均衡规则替换

  • 配置规则不能放在@ComponentScan能扫描的下边,需要另起一个,否则会被所有的Ribbon客户端共享
  1. 书写负载均衡规则
package com.rules;

import com.netflix.loadbalancer.IRule;
import com.netflix.loadbalancer.RandomRule;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

/**
 * @author eleven
 */

@Configuration
public class MyRule {
	
    //有原来的轮询变为了随机
    @Bean
    public IRule myRule(){
        return new RandomRule();
    }
}

  1. 服务消费者主启动类添加相应注解
package com.javacode;

import com.rules.MyRule;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
import org.springframework.cloud.netflix.ribbon.RibbonClient;

@SpringBootApplication
@EnableEurekaClient
//建议此处大写,小写有时候不好用
@RibbonClient(name = "CLOUD-PAYMENT-SERVER",configuration = MyRule.class)
public class OrderApp {
    public static void main(String[] args) {
        SpringApplication.run(OrderApp.class, args);
    }
}

2. 负载均衡原理

  • 负载均衡算法: rest接口第几次请求数%服务器集群总数量=实际调用服务器位置下标,每次服务重启动后rest接口计数从1开始。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-dRTlXYFT-1607163635947)(C:\Users\eleven\Desktop\笔记\img\ribbon轮询.png)]

3. 手写负载均衡轮询算法(模仿)


4. Fegin 声明式客户端

  • Feign是一个声明式WebService客户端。使用Feign能让编写Web Service客户端更加简单。它的使用方法是定义一个服务接口然后在上面添加注解。Feign也支持可拔插式的编码器和解码器。Spring Cloud对Feign进行了封装,使其支持了Spring MVC标准注解和HttpMessageConverters。Feign可以与Eureka和Ribbon组合使用以支持负载均衡

1.Fegin 和OpenFegin的区别

fegin OpenFegin
Feign是Spring Cloud组件中的一个轻量级RESTful的HTTP服务客户端Feign内置了Ribbon,用来做客户端负载均衡,去调用服务注册中心的服务。Feign的使用方式是:使用Feign的注解定义接口,调用这个接口,就可以调用服务注册中心的服务 OpenFeign是Spring Cloud在Feign的基础上支持了SpringMVC的注解,如@RequesMapping等等。OpenFeign的@FeignClient可以解析SpringlMVc的@RequestMapping注解下的接口,并通过动态代理的方式产生实现类,实现类中做负载均衡并调用其他服务。

org.springframework.cloud
spring-cloud-starter-feignk/artifactId>

org.springframework.cloud
spring-cloud-starter-openfeign

2. 引入依赖

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-openfeign</artifactId>
    <version>${fegin.version}</version>
</dependency>

3. 书写启动类

package com.javacode;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.openfeign.EnableFeignClients;

/**
 * @author eleven
 */
@SpringBootApplication
//开启Feign
@EnableFeignClients
public class OrderFeignApp {
    public static void main(String[] args) {
        SpringApplication.run(OrderFeignApp.class, args);
    }
}

4. 书写服务接口

此处@GetMapping() 处理器映射器中要填写的名字是实际的访问地址,即调用的其他服务的路径

package com.javacode.service;

import com.javacode.entity.Payment;
import com.javacode.util.R;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;

/**
 * @author eleven
 */
@Component
@FeignClient(value = "CLOUD-PAYMENT-SERVICE")
public interface PaymentFeignService {

    @PostMapping
    public R createPayment(@RequestBody Payment payment);

    /**
     *
     * @param id 要查询的id
     * @return
     */
    @GetMapping("/payment/{id}")
    public R getPaymentById(@PathVariable("id") String id);
}

5. OpenFeign超时控制

  • OpenFeign默认等待1秒钟,超时报错
ribbon:
  #指的是建立连接后从服务器读取到可用资源所用的时间
  ReadTimeout: 5000
  #指的是建立连接所用的时间,适用于网络状况正常的情况下,两端连接所用的时间
  ConnectTimeout: 5000

6. OpenFeign自带的日志

package com.javacode.config;

import feign.Logger;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

/**
 * @author eleven
 * @apiNote Feign自带的打印日志的功能
 */
@Configuration
public class FeignConfig {

    @Bean
    Logger.Level returnLevel(){
        //开启全部日志
        return Logger.Level.FULL;
    }
}

yml文件添加

logging:
  level:
    # feign日志以什么级别监控哪个接口
    com.javacode.config.FeignConfig: debug

X. 错误集锦

  1. Caused by: java.lang.IllegalStateException: PathVariable annotation was empty on param 0.

引发原因:@PathVaable 没写括号里边的值

@GetMapping("/{id}")
public R getPaymentById(@PathVariable("id") String id);

5. Hystrix 断路器(服务降级)

1. 概述

Hystrix是一个用于处理分布式系统的延迟和容错的开源库,在分布式系统里,许多依赖不可避免的会调用失败,比如超时、异常等,Hystrix能够保证在一个依赖出问题的情况下,不会导致整体服务失败,避免级联故障,以提高分布式系统的弹性。
"断路器”本身是一种开关装置,当某个服务单元发生故障之后,通过断路器的故障监控(类似熔断保险丝),向调用方返回一个符合预期的、可处理的备选响应(FallBack),而不是长时间的等待或者抛出调用方无法处理的异常,这样就保证了服务调用方的线程不会被长时间、不必要地占用,从而避免了故障在分布式系统中的蔓延,乃至雪崩。

服务雪崩

多个微服务之间调用的时候,假设微服务A调用微服务B和微服务C,微服务B和微服务C又调用其它的微服务,这就是所谓的T扇出”。如果扇出的链路上某个微服务的调用响应时间过长或者不可用,对微服务A的调用就会占用越来越多的系统资源,进而引起系统崩溃,所谓的“雪崩效应”.
对于高流量的应用来说,单一的后端依赖可能会导致所有服务器上的所有资源都在几秒钟内饱和。比失败更糟糕的是,这些应用程序还可能导致服务之间的延迟增加,备份队列,线程和其他系统资源紧张,导致整个系统发生更多的级联故障。这些都表示需要对故障和延迟进行隔离和管理,以便单个依赖关系的失败,不能取消整个应用程序或系统。

2. Hystrix中的重要概念

1. 服务降级

  • 服务器繁忙不让客户端等待立即返回一个提示 fallback

什么情况会导致服务降级?

  1. 程序运行异常
  2. 超时
  3. 服务熔断触发服务降级
  4. 线程池/信号量打满 也会导致服务降级

2. 服务熔断

  • 类似保险丝 达到最大访问的时候直接拒绝访问没然后调用服务降级的方法并返回友好提示

3. 服务限流

  • 同一时间访问量过大的话,防止拥挤,将请求按照队列的方式进行,将超出最大访问量的放在队列中等待执行

3. 相关配置

1. 引入pom依赖

<!--hystrix-->
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
</dependency>
<!--eureka client-->
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>

2. 书写启动类

package com.javacode;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.hystrix.EnableHystrix;

/**
 * @author eleven
 * @apiNote 带熔断的启动类
 */

@SpringBootApplication
@EnableHystrix
public class PaymentHystrixApp {
    public static void main(String[] args) {
        SpringApplication.run(PaymentHystrixApp.class, args);
    }
}

3. 书写测试类模拟一个正常一个超时的

@RestController
@Slf4j
public class PaymentController {

    @Autowired
    private PaymentService paymentService;

    @GetMapping("/ok/{id}")
    public String getOkStr(@PathVariable String id){	
       
        return paymentService.paymentInfo(id);
    }

    @GetMapping("/timeout/{id}")
    public String getTimeoutStr(@PathVariable String id){
        //sleep(3)
        return paymentService.paymentTimeout(id);
    }
}

4. 服务降级

1. 服务超时控制

在可能会出现错误的方法上可以加上@HystrixCommand注解,设置出错的处理方法

  1. 添加fallback处理方法
@HystrixCommand(fallbackMethod = "paymentTimeoutHandler",commandProperties = {
    @HystrixProperty(name="execution.isolation.thread.timeoutInMilliseconds",value = "3000")
})
public String paymentTimeout(String id) {
    try{
        TimeUnit.SECONDS.sleep(5);
    }catch(Exception e){
        e.printStackTrace();
    }
    return "线程池" + Thread.currentThread().getName() + "payment_Timeout:" + id;
}

public String paymentTimeoutHandler(String id){
    return "线程池" + Thread.currentThread().getName() + "timeoutHandler,id:" + id + "\tO(∩_∩)O哈哈~";
}
  1. 主启动类添加相应的注解

@EnableCircuitBreaker

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.circuitbreaker.EnableCircuitBreaker;
import org.springframework.cloud.netflix.hystrix.EnableHystrix;

/**
 * @author eleven
 * @apiNote 带熔断的启动类
 */

@SpringBootApplication
@EnableHystrix
@EnableCircuitBreaker
public class PaymentHystrixApp {
    public static void main(String[] args) {
        SpringApplication.run(PaymentHystrixApp.class, args);
    }
}

客户端超时处理

  1. 配置配置文件
feign:
  hystrix:
    enabled: true
  1. 设置超时的处理方法
    @GetMapping("/timeout/{id}")
    @HystrixCommand(fallbackMethod = "timeoutHandler",commandProperties = {
            @HystrixProperty(name="execution.isolation.thread.timeoutInMilliseconds",value = "1500")
    })
    public String getTimeout(@PathVariable String id ){
        return paymentHystrixService.getTimeoutStr(id);
    }

    public String timeoutHandler( String id){
        return "支付系统繁忙,请稍后再试";
    }
  1. 客户端启动类需要添加注解 @EnableHystrix
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.hystrix.EnableHystrix;
import org.springframework.cloud.openfeign.EnableFeignClients;

/**
 * @author zhaojinhui
 * @date 2020/11/5 23:16
 * @apiNote
 */
@SpringBootApplication
@EnableFeignClients
@EnableHystrix
public class FeignHystrixOrderApp {
    public static void main(String[] args) {
        SpringApplication.run(FeignHystrixOrderApp.class,args );
    }
}

问题

此时出错的话都会有fallback方法进行兜底,但是解决方案和业务逻辑混合在了一起,耦合度较高

2. 解决耦合,设置全局通用的fallback

  • @DefaultProperties(defaultFallback="")

在类上直接添加上这个标签会后,配置好相应的方法,如果出现错误的方法没有明确指明fallbak的话就会调用通用的fallback方法,否则就会调用配置的fallback,可以将该方法的返回类型设置为Object,防止报错。

想要使用默认的话就可以在方法时直接加上 @HystrixCommand注解即可

package com.javacode.controller;

import com.javacode.service.PaymentHystrixService;
import com.netflix.hystrix.contrib.javanica.annotation.DefaultProperties;
import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand;
import com.netflix.hystrix.contrib.javanica.annotation.HystrixProperty;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

/**
 * @author zhaojinhui
 * @date 2020/11/5 23:21
 * @apiNote
 */
@RestController
@Slf4j
@RequestMapping("/order")
@DefaultProperties(defaultFallback = "paymentTimeoutHandler")
public class OrderHystrixController {

    @Autowired
    private PaymentHystrixService paymentHystrixService;

    @GetMapping("/ok/{id}")
    public String getOk(@PathVariable String id ){
        return paymentHystrixService.getOkStr(id);
    }

    @GetMapping("/timeout/{id}")
//    @HystrixCommand(fallbackMethod = "timeoutHandler",commandProperties = {
//            @HystrixProperty(name="execution.isolation.thread.timeoutInMilliseconds",value = "1500")
//    })
    @HystrixCommand
    public String getTimeout(@PathVariable String id ){
        return paymentHystrixService.getTimeoutStr(id);
    }

    public String paymentTimeoutHandler(){
        return "全局默认配置fallback,支付系统超时";
    }

    public String timeoutHandler( String id){
        return "支付系统繁忙,请稍后再试";
    }
}

3. 针对Feign接口做统一的降级处理

  1. 新创建一个类实现Feign服务接口

实现Feign的接口类,在这个类中统一的做服务调用失败的处理,如果服务正常了就走Feign的服务,异常了就调用本类的服务了 注意,一定要 添加@Component 注解,否则spring扫描不到

package com.javacode.service.impl;

import com.javacode.service.PaymentHystrixService;
import org.springframework.stereotype.Component;

/**
 * @author zhaojinhui
 * @date 2020/11/8 18:03
 * @apiNote
 */
@Component
public class PaymentFallBackService implements PaymentHystrixService {
    @Override
    public String getOkStr(String id) {
        return "-------PaymentFallBackService处理正常----";
    }

    @Override
    public String getTimeoutStr(String id) {
        return "-------PaymentFallBackService处理超时错误----";
    }
}

  1. 在Feign接口上添加fallBack,配置出错去找上边写的那个类
import com.javacode.service.impl.PaymentFallBackService;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;

/**
 * @author zhaojinhui
 * @date 2020/11/5 23:18
 * @apiNote
 */
@Component
@FeignClient(value = "CLOUD-PAYMENT-SERVICE",fallback = PaymentFallBackService.class)
public interface PaymentHystrixService {
    @GetMapping("/ok/{id}")
    public String getOkStr(@PathVariable("id") String id);

    @GetMapping("/timeout/{id}")
    public String getTimeoutStr(@PathVariable("id") String id);
}

5. 服务熔断

一段时间内,请求失败率达到一定次数之后,就会触发熔断,

服务降级
熔断
恢复调用链路
//=====服务熔断
@HystrixCommand(fallbackMethod = "paymentCircuitBreaker_fallback",commandProperties = {
    @HystrixProperty(name = "circuitBreaker.enabled",value = "true"),// 是否开启断路器
    @HystrixProperty(name = "circuitBreaker.requestVolumeThreshold",value = "10"),// 请求次数
    @HystrixProperty(name = "circuitBreaker.sleepWindowInMilliseconds",value = "10000"), // 时间窗口期
    @HystrixProperty(name = "circuitBreaker.errorThresholdPercentage",value = "60"),// 失败率达到多少后跳闸
})
public String paymentCircuitBreaker(Integer id)
{
    if(id < 0)
    {
        throw new RuntimeException("******id 不能负数");
    }
    String serialNumber = UUID.randomUUID().toString().replaceAll("-", "");

    return Thread.currentThread().getName()+"\t"+"调用成功,流水号: " + serialNumber;
}
public String paymentCircuitBreaker_fallback(Integer id)
{
    return "id 不能负数,请稍后再试,/(ㄒoㄒ)/~~   id: " +id;
}
  1. 再有请求调用的时候,将不会调用主逻辑,而是直接调用降级fallback。通过断路器,实现了自动地发现错误并将降级逻辑切换为主逻辑,减少响应延迟的效果。

  2. 原来的主逻辑要如何恢复呢?
    对于这一问题,hystrix也为我们实现了自动恢复功能。
    当断路器打开,对主逻辑进行熔断之后,hystrix会启动一个休眠时间窗,在这个时间窗内,降级逻辑是临时的成为主逻辑,
    当休眠时间窗到期,断路器将进入半开状态,释放一次请求到原来的主逻辑上,如果此次请求正常返回,那么断路器将继续闭合,主逻辑恢复,如果这次请求依然有问题,断路器继续进入打开状态,休眠时间窗重新计时。

6. 服务限流

7. 图形化页面 dashboard

  1. 引入相关的pom依赖
<dependencies>
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-netflix-hystrix-dashboard</artifactId>
    </dependency>

</dependencies>

  1. 配置文件
server:
  port: 9001
  1. 启动类
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.hystrix.dashboard.EnableHystrixDashboard;

/**
 * @author zhaojinhui
 * @date 2020/11/8 21:10
 * @apiNote
 */
@SpringBootApplication
@EnableHystrixDashboard
public class DashBoardApp {
    public static void main(String[] args) {
        SpringApplication.run(DashBoardApp.class, args);
    }
}
  1. 访问地址
localhost:9001/hystrix

6. Gateway 网关

1. Zuul

在SpringCloud Finchley正式版之前,Spring Cloud 推荐的网关是 Netflix提供的Zuul:
1、Zuul 1.x,是一个基于阻塞 I/O 的API Gateway
2、Zuul 1.x基于Servlet 2.5使用阻塞架构它不支持任何长连接(如WebSocket)Zuul的设计模式和Nginx较像,每次V/О操作都是从工作线程中选择一个执行,请求线程被阻塞到工作线程完成,但是差别是Nginx用C++实现,Zuul用Java 实现,而M本身会有第—次加载较慢的情况,使得Zuul的性能相对较差。
3、Zuul 2.x理念更先进,想基于Netty非阻塞和支持长连接,但SpringCloud目前还没有整合。Zuul 2.x的性能较Zuul 1.x有较大提升。在性能方面,根据官方提供的基准测试,Spring Cloud Gateway的RPS(每秒请求数)是Zuul的1.6倍。
4、Spring Cloud Gateway建立在Spring Framework 5、Project Reactor和Spring Boot 2,之上,使用非阻塞API。5、Spring Cloud Gateway还支持WebSocket,并且与Spring紧密集成拥有更好的开发体验

为什么使用Gateway

Zuul底层是阻塞式的,传统的Web框架,比如说: struts2,springmvc等都是基于Servlet API与Servlet容器基础之上运行的。但是
在Servlet3.1之后有了异步非阻塞的支持。而WebFlux是一个典型非阻塞异步的框架,它的核心是基于Reactor的相关API实现的。相对于传统的web框架来说,它可以运行在诸如Netty,Undertow及支持Servlet3.1的容器上。非阻塞式+函数式编程(Spring5必须让你使用java8)
Spring WebFlux是Spring 5.0 引入的新的响应式框架,区别于Spring MVC,它不需要依赖Servlet API,它是完全异步非阻塞的,并且基于Reactor来实现啊应式流规范。

2. gateway

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-irROjF53-1607163635949)(https://s3.ax1x.com/2020/11/13/D9B9OO.png)]

1. Route 路由

路由是构成网关的基本模块,它由ID,目标URI,一系列断言和过滤器组成,如果断言为true则匹配该路由

2. Predicate 断言

参考java8中的 java.util.function.Predicate,可以匹配HTTP请求中的所有内容(例如请求头或请求参数),如果请求与断言相匹配则进行路由

3. Filter 过滤器

指的是Spring框架中的GatewayFilter的实例,使用过滤器,可以在请求呗路由之前或之后对请求做出修改

springcloud 学习笔记

客户端向Spring Cloud Gateway发出请求。然后在Gateway Handler Mapping 中找到与请求相匹配的路由,将其发送到GatewayWeb Handler。
Handler再通过指定的过滤器链来将请求发送到我们实际的服务执行业务逻辑,然后返回。
过滤器之间用虚线分开是因为过滤器可能会在发送代理请求之前(“pre”)或之后(“post”)执行业务逻辑。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-n5LaVcub-1607163635952)(https://s3.ax1x.com/2020/11/13/D9DuCR.png)]

3. 搭建gateway网关

1. 引入相关依赖

<!--gateway-->
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>

2. 书写配置文件

#启动端口9527
server:
  port: 9527
#设置服务名称
spring:
  application:
    name: cloud-gateway
  cloud:
    gateway:
      discovery:
        locator:
          enabled: true #开启从注册中心动态创建路由的功能,利用微服务名进行路由
      routes:
        - id: payment_routh #payment_route    #路由的ID,没有固定规则但要求唯一,建议配合服务名
          #uri: http://localhost:8001          #匹配后提供服务的路由地址
          uri: lb://cloud-payment-service #匹配后提供服务的路由地址
          predicates:
            - Path=/payment/get/**         # 断言,路径相匹配的进行路由

        - id: payment_routh2 #payment_route    #路由的ID,没有固定规则但要求唯一,建议配合服务名
          #uri: http://localhost:8001          #匹配后提供服务的路由地址
          uri: lb://cloud-payment-service #匹配后提供服务的路由地址
          predicates:
            - Path=/payment/lb/**         # 断言,路径相匹配的进行路由
            #- After=2020-02-21T15:51:37.485+08:00[Asia/Shanghai]
            #- Cookie=username,zzyy
            #- Header=X-Request-Id, \d+  # 请求头要有X-Request-Id属性并且值为整数的正则表达式  
#注册进eureka
eureka:
  instance:
    hostname: cloud-gateway-service
  client:
    #服务提供者provider注册进eureka服务列表内
    service-url:
      register-with-eureka: true
      fetch-registry: true
      defaultZone: http://eureka7001.com:7001/eureka

GateWay中的路由配置有两种配置方式

  1. 书写在配置文件中的
cloud:
    gateway:
      discovery:
        locator:
          enabled: true #开启从注册中心动态创建路由的功能,利用微服务名进行路由
      routes:
        - id: payment_routh #payment_route    #路由的ID,没有固定规则但要求唯一,建议配合服务名
          uri: http://localhost:8001          #匹配后提供服务的路由地址
          #uri: lb://cloud-payment-service #匹配后提供服务的路由地址
          predicates:
            - Path=/payment/get/**         # 断言,路径相匹配的进行路由
  1. 代码中注入RouteLocator的Bean
package com.javacode.config;

import org.springframework.cloud.gateway.route.RouteLocator;
import org.springframework.cloud.gateway.route.builder.RouteLocatorBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

/**
 * @author zhaojinhui
 * @date 2020/11/22 21:39
 * @apiNote
 */
@Configuration
public class GateWayConfig {
    @Bean
    public RouteLocator buildRoute(RouteLocatorBuilder routeLocatorBuilder){
        RouteLocatorBuilder.Builder routes = routeLocatorBuilder.routes();
        routes.route("path_route_atguigu",
                r -> r.path("/guonei")
                        .uri("http://news.baidu.com/guonei")).build();
        return routes.build();
    }
}

3. 启动类

package com.javacode;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;

/**
 * @author zhaojinhui
 * @date 2020/11/22 21:15
 * @apiNote
 */
@SpringBootApplication
@EnableEurekaClient
public class Gateway9527App {
    public static void main(String[] args) {
        SpringApplication.run(Gateway9527App.class, args);
    }
}

4. 通过微服务名实现动态路由

  • 默认情况下Gateway会根据注册中心注册的服务列表,
    以注册中心上微服巳名为路径创建动态路由进行转发,从而实现动态路由的功能
  • lb 的含义是LoadBlance

修改配置文件

#启动端口9527
server:
  port: 9527
#设置服务名称
spring:
  application:
    name: cloud-gateway
  cloud:
    gateway:
      discovery:
        locator:
          enabled: true #开启从注册中心动态创建路由的功能,利用微服务名进行路由
      routes:
        - id: api-budget #payment_route    #路由的ID,没有固定规则但要求唯一,建议配合服务名
          #uri: http://localhost:8001          #匹配后提供服务的路由地址
          uri: lb://BUDGET-SERVICE-API-ZJH #匹配后提供服务的路由地址
          predicates:
            - Path=/api-budget/**        # 断言,路径相匹配的进行路由
        - id: api-reimbursement #payment_route    #路由的ID,没有固定规则但要求唯一,建议配合服务名
          #uri: http://localhost:8001          #匹配后提供服务的路由地址
          uri: lb://FK-REIMBURSEMENT #匹配后提供服务的路由地址
          predicates:
            - Path=/api-reimbursement/**         # 断言,路径相匹配的进行路由
            #- After=2020-02-21T15:51:37.485+08:00[Asia/Shanghai]
            #- Cookie=username,zzyy
            #- Header=X-Request-Id, \d+  # 请求头要有X-Request-Id属性并且值为整数的正则表达式
        - id: api-ear #payment_route    #路由的ID,没有固定规则但要求唯一,建议配合服务名
          uri: lb://ZHZ-WEB-API #匹配后提供服务的路由地址
          predicates:
            - Path=/api-ear/**         # 断言,路径相匹配的进行路由
            #- After=2020-02-21T15:51:37.485+08:00[Asia/Shanghai]
            #- Cookie=username,zzyy
            #- Header=X-Request-Id, \d+  # 请求头要有X-Request-Id属性并且值为整数的正则表达式
#注册进eureka
eureka:
  instance:
    hostname: cloud-gateway-service
  client:
    #服务提供者provider注册进eureka服务列表内
    service-url:
      register-with-eureka: true
      fetch-registry: true
      defaultZone: http://10.138.228.199:30771/eureka/


5. Predicate的使用

6. Filter使用

1. 自定义全局过滤器GlobalFilter

  • 主要的两个接口 implements GlobalFilter,Ordered
  • 可以使用@Order注解代替
  • 可以用来做全局日志处理和鉴权
package com.javacode.filter;

import lombok.extern.slf4j.Slf4j;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

/**
 * @author zhaojinhui
 * @date 2020/11/22 22:30
 * @apiNote
 */
@Component
@Order(0)
@Slf4j
public class MyGateWayFilter implements GlobalFilter {

    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        log.info("***********come in MyGlobalFilter");
        String uname = exchange.getRequest().getQueryParams().getFirst("uname");
        if(uname == null){
            log.info("用户名未空");
            exchange.getResponse().setStatusCode(HttpStatus.NOT_ACCEPTABLE);
            return exchange.getResponse().setComplete();
        }
        return chain.filter(exchange);
    }

}

7. Config

1. 简介

微服务意味着要将单体应用中的业务拆分成一个个子服务,每个服务的粒度相对较小,因此系统中会出现大量的服务。由于每个服务都需要必要的配置信息才能运行,所以一套集中式的、动态的配置管理设施是必不可少的。
SpringCloud提供了ConfigServer来解决这个问题,我们每一个微服务自己带着一个application.yml,上百个配置文件的管理./(ToT)/~~

  • Spring Cloud COnfig为我服务架构中的微服务提供种种花的外部配置支持,配置服务器为各个不同的微服务应用的所有换种提供了一个中心化的外部配置

config服务端和客户端介绍

  • 服务端也称为分布式配置中心,它是一个独立的微服务应用,用来连接配置服务器并为客户端提供获取配置信息,加密/解密信息等访问接口
  • 客户端则是通过指定的配置中心来管理应用资源,以及与业务相关的配置内容,并在启动的时候从配置中心获取和加载配置信息配置服务器默认采用git来存储配置信息,这样就有助于对环境配置进行版本管理,并且可以通过git客户端工具来方便的管理和访问配置内容

2. 引入依赖

<!--添加消息总线RabbitMQ支持-->
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-bus-amqp</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-config-server</artifactId>
</dependency>

3. 书写配置文件

server:
  port: 3344

spring:
  application:
    name:  cloud-config-center #注册进Eureka服务器的微服务名
  cloud:
    config:
      server:
        git:
          uri: aaa@qq.com:2496290990/springcloud-config.git #GitHub上面的git仓库名字
          ####搜索目录
          search-paths:
            - springcloud-config
      ####读取分支
      label: master
#rabbitmq相关配置
rabbitmq:
  host: localhost
  port: 5672
  username: guest
  password: guest

#服务注册到eureka地址
eureka:
  client:
    service-url:
      defaultZone: http://localhost:7001/eureka

##rabbitmq相关配置,暴露bus刷新配置的端点
management:
  endpoints: #暴露bus刷新配置的端点
    web:
      exposure:
        include: 'bus-refresh'

错误集锦

  1. org.eclipse.jgit.api.errors.TransportException: aaa@qq.com:xxxxxxxx.git:

请尝试以下的配置文件

将ssh方式改为http

spring:
application:
 name:  cloud-config-center #注册进Eureka服务器的微服务名
cloud:
 config:
   server:
     git:
       uri: https://github.com/2496290990/springcloud-config.git #GitHub上面的git仓库名字
       username: aaa@qq.com
       ####搜索目录
       search-paths:
            - springcloud-config
          password: 
          force-pull: true #重要
      ####读取分支
      label: master

4. 书写启动类

package com.javacode;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.config.server.EnableConfigServer;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;

/**
 * @author zhaojinhui
 * @date 2020/11/22 23:00
 * @apiNote
 */
@SpringBootApplication
@EnableConfigServer
@EnableEurekaClient
public class Config3344 {
    public static void main(String[] args) {
        SpringApplication.run(Config3344.class, args);
    }
}

5. 书写config客户端3355

1, pom依赖

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <artifactId>cloud</artifactId>
        <groupId>com.java-code</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>cloud-config-server3355</artifactId>

    <dependencies>
        <!--添加消息总线RabbitMQ支持-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-bus-amqp</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-config</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-actuator</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
            <scope>runtime</scope>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>


</project>

2. 书写bootstrap.yml

server:
  port: 3355

spring:
  application:
    name: config-client
  cloud:
    #Config客户端配置
    config:
      label: master #分支名称
      name: config #配置文件名称
      profile: dev #读取后缀名称   上述3个综合:master分支上config-dev.yml的配置文件被读取http://config-3344.com:3344/master/config-dev.yml
      uri: http://localhost:3344 #配置中心地址k

  #rabbitmq相关配置 15672是Web管理界面的端口;5672是MQ访问的端口
  rabbitmq:
    host: localhost
    port: 5672
    username: guest
    password: guest

#服务注册到eureka地址
eureka:
  client:
    service-url:
      defaultZone: http://localhost:7001/eureka

# 暴露监控端点
management:
  endpoints:
    web:
      exposure:
        include: "*"

6. 分布式配置中心的动态刷新问题

  • 当github上的配置文件内容调整时,刷新3344(服务端) 会立即得到相应,但是3355(客户端)并不会得到任何响应,除非3355重启或者重新加载

1. 修改客户端3355

  1. pom添加actuator模块
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
  1. 暴露监控端点
# 暴露监控端点
management:
  endpoints:
    web:
      exposure:
        include: "*"
  1. controller层添加注解@RefreshScope
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

/**
 * @auther zzyy
 * @create 2020-02-21 18:08
 */
@RestController
@RefreshScope
public class ConfigClientController
{
    @Value("${config.info}")
    private String configInfo;

    @GetMapping("/configInfo")
    public String getConfigInfo()
    {
        return configInfo;
    }
}

此时配置完成之后github修改 3355客户端还是不会自己修改的,必须手动的发post请求请求刷新

发送post请求到3355

Post http://localhost:3355/actuator/refresh

弊端

如果有多个服务的话,就会导致每个服务都需要去手动刷新,每个微服务都需有一个请求,因此引出BUS消息总线的概念,广播,一次通知处处生效

8. Bus

9. spring cloud stream

1. 引入相关的依赖

需要使用什么的消息中间件只需要把第二个依赖换成相关的消息中间件即可

<dependency>
	<groupId>org.springframework.cloud</groupId>
	<artifactId>spring-cloud-stream</artifactId>
</dependency>
<dependency>
	<groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-starter-activemq</artifactId>
</dependency>

2. 需要一个注解

package com.javacode.activeMq.impl;

import com.javacode.activeMq.mq.ActiveMqMessage;
import org.springframework.cloud.stream.annotation.EnableBinding;
import org.springframework.cloud.stream.messaging.Source;


/**
 * @author zhaojinhui
 * @date 2020/12/5 17:40
 * @apiNote
 */
@EnableBinding(Source.class)
public class MessageProvider implements ActiveMqMessage {
    @Override
    public String send() {
        return null;
    }
}

10. spring cloud sleuth

X. 杂七杂八

  1. linux解压
#解压 tar.gz压缩包
tar -zxvf xxx.tar.gz
#解压 tar 压缩包
tar -xvf xxx.tar
  1. 关闭centos7防火墙
systemctl stop firewalld