SpringCloud (Chapter 10~16): Hystrix circuit breaker, zuul routing gateway, Gateway new generation gateway, Config distributed configuration center, Bus message bus, Stream message driver, Sleuth distributed link tracking

10. Hystrix Circuit Breaker

  • Although the Hystrix service degradation framework SpringCloud has officially stopped updating, its design concept is very excellent. Service degradation, service circuit breaker, service current limiting, etc. Its series of ideas are essential medicine for subsequent frameworks to learn from. So we need to take a deeper look at Hystrix.
  • Although the official website now recommends using resilience4j, it is more commonly used abroad.
  • In China, we mainly use Hystrix or sentienl (Alibaba’s)
  • It can be used on both the consumer side and the service side, and is generally used on the consumer side.

10.1 Overview

10.1.1 Problems faced by distributed systems

  • Problems faced by distributed systems

    • 复杂分布式体系结构中的应用程序有数十个依赖关系,每个依赖关系在某些时候将不可避免地失败。
      Insert image description here
  • Service avalanche

    • When calling between multiple microservices, suppose microservice A calls microservice B and microservice C, and microservice B and microservice C call other microservices. This is what is called “扇出”. If the call response time of a certain microservice on the fan-out link is too long or is unavailable, the call to microservice A will occupy more and more system resources, causing the system to crash, the so-called "avalanche effect".
    • For high-traffic applications, a single backend dependency can cause all resources on all servers to be saturated within seconds. Worse than failure, these applications can also cause increased latency between services, straining backup queues, threads, and other system resources, leading to more cascading failures throughout the system. These all represent the need for faults and delays to be isolated and managed so that the failure of a single dependency does not take down the entire application or system. Therefore, usually when you find that an instance under a module fails, the module will still receive traffic, and then the problematic module also calls other modules, so a cascading failure, or avalanche, will occur . .
  • eg: Service 80 calls 8001, 8001 calls 8002, 8002 calls 8004, and 8004 calls 8006. Calling each other one by one makes the link longer and longer. As long as one of them goes wrong, it will cause problems for all services.

10.1.2 What is

  • 延迟Hystrix is ​​an open source library used to handle distributed systems 容错. In distributed systems, many dependencies will inevitably fail to call, such as timeouts, exceptions, etc. Hystrix can ensure that when a dependency goes wrong 不会导致整体服务失败,避免级联故障,以提高分布式系统的弹性,
  • The "circuit breaker" itself is a switching device. When a service unit fails, through the fault monitoring of the circuit breaker (similar to blowing a fuse), 向调用方返回一个符合预期的、可处理的备选响应(FallBack),而不是长时间的等待或者抛出调用方无法处理的异常this ensures that the service caller's thread will not be blocked for a long time and unnecessarily. The land is occupied, thereby avoiding the spread of faults in the distributed system and even avalanches.

10.1.3 What can be done

  • Service downgrade
  • Service circuit breaker
  • Near real-time monitoring

10.1.4 Official website information

  • Official website information: https://github.com/Netflix/Hystrix/wiki/How-To-Use
    Insert image description here

10.1.5 Hystrix official announcement, suspension of updates for maintenance

  • https://github.com/Netflix/Hystrix
    Insert image description here
  • Passively fix bugs
  • Merge requests are no longer accepted
  • No more new releases

10.2 Important concepts of Hystrix

10.2.1 Service downgrade fallback

  • The server is busy, please try again later, without letting the client wait and immediately return a friendly prompt, fallback
    • That is: similar to the if-else structure, the other party's system is unavailable, and you need to give me a clear solution.
  • Under what circumstances will a downgrade occur?
    • Program runs abnormally
    • time out
    • Service circuit breaker triggers service degradation
    • When the thread pool/semaphore is full, it will also cause service degradation.

10.2.2 Service break

  • After the analog fuse reaches the maximum service access, access is directly denied, power is cut off, and then the service downgrade method is called and a friendly prompt is returned.
  • It’s the fuse: service degradation -> then fuse -> restore the call link
  • Service interruption can also be regarded as a type of downgrade.

10.2.3 Service flow limit flowlimit

  • For operations such as flash killing of high concurrency, crowding is strictly prohibited. Everyone queues up, N times per second, and proceeds in an orderly manner.

10.3 hystrix case

  • Because it is troublesome to start multiple services, we changed to a stand-alone version for testing.
    • 7001 restored to stand-alone version
      Insert image description here

10.3.1 Build

1) Create new cloud-provider-hystrix-payment8001

  • Create an 8001 with blown hystrix
    Insert image description here

2)POM

Insert image description here

<?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>cloud2020</artifactId>
        <groupId>com.angenin.springcloud</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>cloud-provider-hystrix-payment8001</artifactId>

    <properties>
        <maven.compiler.source>8</maven.compiler.source>
        <maven.compiler.target>8</maven.compiler.target>
    </properties>
    <dependencies>
        <!--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>
        <!--web-->
        <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><!-- 引入自己定义的api通用包,可以使用Payment支付Entity -->
            <groupId>com.angenin.springcloud</groupId>
            <artifactId>cloud-api-commons</artifactId>
            <version>${project.version}</version>
        </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>

3)YML

Insert image description here

server:
  port: 8001

spring:
  application:
    name: cloud-provider-hystrix-payment

eureka:
  client:
    register-with-eureka: true
    fetch-registry: true
    service-url:
      #集群版
      #defaultZone: http://eureka7001.com:7001/eureka,http://eureka7002.com:7002/eureka
      #单机版
      defaultZone: http://eureka7001.com:7001/eureka

4) Main startup

Insert image description here

package com.angenin.springcloud;

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

@EnableEurekaClient
@SpringBootApplication
public class PaymentHystrixMain8001 {
    
    

    public static void main(String[] args) {
    
    
        SpringApplication.run(PaymentHystrixMain8001.class, args);
    }

}

5) Business category

  • service : Under normal circumstances, you need to write an interface and interface implementation class. In order to save time, you write the implementation class directly.
    Insert image description here
package com.angenin.springcloud.service;

import org.springframework.stereotype.Service;

import java.util.concurrent.TimeUnit;

@Service
public class PaymentService {
    
    

    //正常访问方法
    public String paymentInfo_OK(Integer id){
    
    
        //如果正常访问则,返回当前线程池的名字、传入的id、表情包
        return "线程池:  "+Thread.currentThread().getName()+"  paymentInfo_OK,id:  "+id+"\t"+"O(∩_∩)O哈哈~";
    }


    //超时访问方法
    public String paymentInfo_TimeOut(Integer id){
    
    
        //前面学过时会导致服务降级,模拟错误
        int timeNumber = 3;
        try {
    
    
            TimeUnit.SECONDS.sleep(timeNumber);
        } catch (InterruptedException e) {
    
    
            e.printStackTrace();
        }
        //超时返回的提示信息
        return "线程池:" + Thread.currentThread().getName() +
                "paymentInfo_TimeOut,id:" +id+"\t"+"O(∩_∩)O哈哈~"+"  耗时(秒):"+timeNumber;
    }

}

  • controller
    Insert image description here
package com.angenin.springcloud.controller;

import com.angenin.springcloud.service.PaymentService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;

import javax.annotation.Resource;

@Slf4j
@RestController
public class PaymentController {
    
    

    @Resource
    PaymentService paymentService;

    @Value("${server.port}")    //spring的@Value注解
    private String ServerPort;

    //正常
    @GetMapping("/payment/hystrix/ok/{id}")
    public String paymentInfo_OK(@PathVariable("id") Integer id){
    
    
        String result = paymentService.paymentInfo_OK(id);
        log.info("******result:" + result);
        return result;
    }

    //超时
    @GetMapping("/payment/hystrix/timeout/{id}")
    public String paymentInfo_TimeOut(@PathVariable("id") Integer id){
    
    
        String result = paymentService.paymentInfo_TimeOut(id);
        log.info("******result:" + result);
        return result;
    }

}

6) Normal test

  • Start eureka7001

  • Start cloud-provider-hystrix-payment8001

  • Access: All can access and return results correctly.

    • Success method: http://localhost:8001/payment/hystrix/ok/4
      Insert image description here
      Insert image description here

    • Each call takes 3 seconds: http://localhost:8001/payment/hystrix/timeout/4
      Insert image description here
      Insert image description here

  • The above modules are all OK: use the above as the basic platform, go from correct -> error -> downgrade circuit breaker -> recovery

10.3.2 High concurrency testing

  • The above can still barely satisfy the requirements under non-high concurrency situations, but...

1) Jmeter stress test

  • Open Jmeter, 20,000 concurrent requests will crush 8001, and 20,000 requests will all access the paymentInfo_TimeOut service.
    Insert image description here
    Insert image description here
    Save:
    Insert image description here
    Insert image description here
    http://localhost:8001/payment/hystrix/timeout/4
    Insert image description here

  • Start eureka7001

  • Start cloud-provider-hystrix-payment8001

  • Access the 2 methods respectively and view the demonstration results (self-test)

    • Success method: http://localhost:8001/payment/hystrix/ok/4
    • Each call takes 3 seconds: http://localhost:8001/payment/hystrix/timeout/4
  • result:

    • Both are spinning in circles on their own
      • It turns out that the method that accesses successfully returns the result immediately, and the method that accesses it takes 3 seconds to return the result (the number of threads is small, only 2 threads)
      • Now: both methods will spin in circles, indicating that this successful method has also been slowed down (many threads, 20,000 + 2 threads)
    • Why is it stuck?
      • Tomcat's default number of working threads is full, and there are no extra threads to decompose the pressure and process. (Springboot integrates Tomact by default, which has a thread pool for the Tomact container)
      • There are two methods of accessing the same microservice. The microservice concentrates resources to process high-concurrency requests. Due to the resources being drained under heavy pressure, the successful access method will also suffer from lagging and delay effects.

2) Jmeter pressure test conclusion

  • The above is still a service 提供者8001自己测试. If external consumers 80 also come to visit at this time, 消费者they can only wait. In the end, the consumer 80 will be dissatisfied and the server 8001 will be directly dragged to death.

3) Watching the fun and not disdain the big deal, 80 new members joined

  • Create new cloud-consumer-feign-hystrix-order80
    Insert image description here

  • POM
    Insert image description here

<?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>cloud2020</artifactId>
        <groupId>com.angenin.springcloud</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>cloud-consumer-feign-hystrix-order80</artifactId>

    <properties>
        <maven.compiler.source>8</maven.compiler.source>
        <maven.compiler.target>8</maven.compiler.target>
    </properties>

    <dependencies>
        <!--openfeign-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-openfeign</artifactId>
        </dependency>
        <!--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>
        <!-- 引入自己定义的api通用包,可以使用Payment支付Entity -->
        <dependency>
            <groupId>com.angenin.springcloud</groupId>
            <artifactId>cloud-api-commons</artifactId>
            <version>${project.version}</version>
        </dependency>
        <!--web-->
        <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>
  • YML
    Insert image description here
server:
  port: 80

eureka:
  client:
    register-with-eureka: false
    service-url:
      defaultZone: http://eureka7001.com:7001/eureka/

  • main boot
    Insert image description here
package com.angenin.springcloud;

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

@SpringBootApplication
@EnableFeignClients  //激活feifn
public class OrderHystrixMain80
{
    
    
    public static void main(String[] args) {
    
    
        SpringApplication.run(OrderHystrixMain80.class,args);
    }
}

  • Business class (PaymentHystrixService, OrderHystirxController)
    Insert image description here
package com.angenin.springcloud.service;

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;

@Component
//调用的微服务名
@FeignClient(value = "CLOUD-PROVIDER-HYSTRIX-PAYMENT" )
public interface PaymentHystrixService
{
    
    
    @GetMapping("/payment/hystrix/ok/{id}")
    public String paymentInfo_OK(@PathVariable("id") Integer id);

    @GetMapping("/payment/hystrix/timeout/{id}")
    public String paymentInfo_TimeOut(@PathVariable("id") Integer id);
}

-----------------------------------------------------------

package com.angenin.springcloud.controller;


import com.angenin.springcloud.service.PaymentHystrixService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;

import javax.annotation.Resource;

@RestController
@Slf4j
public class OrderHystirxController {
    
    
    @Resource
    private PaymentHystrixService paymentHystrixService;

    //调用ok方法
    @GetMapping("/consumer/payment/hystrix/ok/{id}")
    public String paymentInfo_OK(@PathVariable("id") Integer id) {
    
    
        String result = paymentHystrixService.paymentInfo_OK(id);
        return result;
    }

    //调用超时方法
    @GetMapping("/consumer/payment/hystrix/timeout/{id}")
    public String paymentInfo_TimeOut(@PathVariable("id") Integer id) {
    
    
        String result = paymentHystrixService.paymentInfo_TimeOut(id);
        return result;
    }
}



  • normal test

    • Start 7001, 8001, 80
      Insert image description here

    • Visit ok: http://localhost/consumer/payment/hystrix/ok/4 (the speed is also very fast)
      Insert image description here

  • High concurrency testing

    • 2W threads press 8001
    • The consumer side 80 microservice then accesses the normal Ok microservice 8001 address
    • http://localhost/consumer/payment/hystrix/ok/32
    • Effect:
      • Or wait in circles, and it will take a while for the results to be returned.
      • Or the consumer reports a timeout error
        Insert image description here

10.3.3 Fault phenomena and causes

  • 8001 Other interface services at the same level are trapped because the working threads in the tomcat thread pool have been occupied.
  • 80 calls 8001 at this time, and the client access response is slow and spinning in circles.

10.3.4 Conclusion of appeal

  • It is precisely because of the above failures or poor performance that our downgrading/fault tolerance/current limiting and other technologies were born.

10.3.5 How to solve it? Resolved requirements

  • Timeout causes the server to slow down (spinning in circles): No more waiting after timeout
  • Error (downtime or program operation error): Make sure you have a clear understanding of the error
  • Solution :
    • The other party's service (8001) has timed out. The caller (80) cannot be stuck waiting forever. There must be a service downgrade.
    • The other party's service (8001) is down. The caller (80) cannot be stuck waiting forever. There must be a service downgrade.
    • The other party's service (8001) is OK. The caller (80) fails or has its own requirements (its own waiting time is less than the service provider), and handles the downgrade itself.

10.3.6 Service degradation

1) Downgrade configuration

  • @HystrixCommand
  • Nowadays, practical configuration is basically used instead of coding.

2) 8001 first looks for problems within itself

  • Set the peak value of your own call timeout. Normal operation can be performed within the peak value. If it exceeds the peak value, you need to have a back-up method to deal with it and perform service degradation fallback.

3)8001fallback

Producer downgrade: The default peak value of the timeout method is 3 seconds. Now it sleeps for 5 seconds, so it needs to be downgraded and a cover-up method is performed.

  • Business class enabled
    • How to deal with exceptions reported by @HystrixCommand
    • Once the call to the service method fails and an error message is thrown, the specified method in the @HystrixCommand-marked fallbackMethod calling class will be automatically called.
      Insert image description here
      Insert image description here
package com.angenin.springcloud.service;

import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand;
import com.netflix.hystrix.contrib.javanica.annotation.HystrixProperty;
import org.springframework.stereotype.Service;

import java.util.concurrent.TimeUnit;

@Service
public class PaymentService {
    
    

    //正常访问方法
    public String paymentInfo_OK(Integer id){
    
    
        //如果正常访问则,返回当前线程池的名字、传入的id、表情包
        return "线程池:  "+Thread.currentThread().getName()+"  paymentInfo_OK,id:  "+id+"\t"+"O(∩_∩)O哈哈~";
    }



    /**
     * @HystrixCommand:启用
     * 超时访问方法
     * fallbackMethod:此方法出现问题了,执行哪个兜底的方法
     * HystrixProperty:此方法线程的超时时间为3秒钟,我们现在睡眠5秒说明超时了,就走兜底的方法
     */
    @HystrixCommand(fallbackMethod = "paymentInfo_TimeOutHandler",commandProperties = {
    
    
            @HystrixProperty(name="execution.isolation.thread.timeoutInMilliseconds",value="3000")
    })
    public String paymentInfo_TimeOut(Integer id){
    
    
        //前面学过时会导致服务降级,模拟错误
        int timeNumber = 5;
        //int age = 10/0; //程序出现异常,同样会走兜底的方法
        try {
    
    
            TimeUnit.SECONDS.sleep(timeNumber);
        } catch (InterruptedException e) {
    
    
            e.printStackTrace();
        }
        //超时返回的提示信息
        return "线程池:" + Thread.currentThread().getName() +
                "paymentInfo_TimeOut,id:" +id+"\t"+"O(∩_∩)O哈哈~"+"  耗时(秒):"+timeNumber;
    }

    // 兜底方法
    public String paymentInfo_TimeOutHandler(Integer id){
    
     // 回调函数向调用方返回一个符合预期的、可处理的备选响应
        return "线程池:  "+Thread.currentThread().getName()+"  8001系统繁忙或者运行报错,请稍后再试,id:  "+id+"\t"+"o(╥﹏╥)o";
    }


}


  • Main startup class activation
    Insert image description here
package com.angenin.springcloud;

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

@EnableEurekaClient
@SpringBootApplication
@EnableCircuitBreaker //激活
public class PaymentHystrixMain8001 {
    
    

    public static void main(String[] args) {
    
    
        SpringApplication.run(PaymentHystrixMain8001.class, args);
    }

}

  • Self-test: Start 7001, 8001
    Insert image description here

  • http://localhost:8001/payment/hystrix/timeout/1
    Insert image description here

4)80fallback

Consumer downgrade: The producer's peak timeout is 5 seconds and it slept for 3 seconds. Now the consumer call requires 1.5 seconds and cannot wait for 3 seconds, so the service downgrade calls a cover-up method.

  • 80 order microservices can also better protect themselves, and they can also follow the same example to protect client downgrades.
  • As an aside, remember: the hot deployment method we have configured has obvious changes to the Java code, but it is recommended to restart the microservices when modifying @HystrixCommandinternal attributes.
  • YML
    Insert image description here
feign:
  hystrix:
    enabled: true
  • main boot
    Insert image description here
package com.angenin.springcloud;

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

@SpringBootApplication
@EnableFeignClients  //激活feifn
@EnableHystrix //激活
public class OrderHystrixMain80
{
    
    
    public static void main(String[] args) {
    
    
        SpringApplication.run(OrderHystrixMain80.class,args);
    }
}

  • Business class
    Insert image description here
package com.angenin.springcloud.controller;


import com.angenin.springcloud.service.PaymentHystrixService;
import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand;
import com.netflix.hystrix.contrib.javanica.annotation.HystrixProperty;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;

import javax.annotation.Resource;

@RestController
@Slf4j
public class OrderHystirxController {
    
    
    @Resource
    private PaymentHystrixService paymentHystrixService;

    //调用ok方法
    @GetMapping("/consumer/payment/hystrix/ok/{id}")
    public String paymentInfo_OK(@PathVariable("id") Integer id) {
    
    
        String result = paymentHystrixService.paymentInfo_OK(id);
        return result;
    }

    //调用超时方法
    @GetMapping("/consumer/payment/hystrix/timeout/{id}")
    @HystrixCommand(fallbackMethod = "paymentTimeOutFallbackMethod",commandProperties = {
    
    
            @HystrixProperty(name="execution.isolation.thread.timeoutInMilliseconds",value="1500")
    })
    public String paymentInfo_TimeOut(@PathVariable("id") Integer id) {
    
    
	    int age = 10/0; //测试出现异常同样要执行兜底的方法
        String result = paymentHystrixService.paymentInfo_TimeOut(id);
        return result;
    }

    public String paymentTimeOutFallbackMethod(@PathVariable("id") Integer id) {
    
    

        return "我是消费者80,对方支付系统繁忙请10秒钟后再试或者自己运行出错请检查自己,o(╥﹏╥)o";
    }
}


  • Modify the producer side to normal: the peak value of the producer side is 5 seconds and sleeps for 3 seconds. At this time, the peak value of the consumer side is 1.5 seconds and cannot wait for 3 seconds, so the back-up method is executed.
    Insert image description here

  • Test: start 7001, 8001, 80
    Insert image description here

  • http://localhost/consumer/payment/hystrix/timeout/1
    Insert image description here

5) Current issues

  • Problem 1: Each business method corresponds to a covert method, and the code is bloated.

  • Problem 2: Business logic methods and covert methods are mixed together, and the coupling degree of the code is extremely high.

  • Solution: Separate unified and customized

    • There are 100 business methods, of which 97 common methods use global configuration, and only 3 special ones are customized (write 3 covert methods), which can reduce the generation of fallback service degradation methods.

6) Problem solving demonstration

Solve problem 1: Configure one for each method? ? ? Expansion

  • feign interface series

  • @DefaultProperties(defaultFallback = “”)

    • 1:1 Each method configures a service downgrade method. Technically it is possible, but in fact it is silly.
    • 1:N Except for some important core businesses that are exclusive, other common ones can be uniformly jumped to the unified processing result page through @DefaultProperties(defaultFallback = “”)
    • 通用的和独享的各自分开,避免了代码膨胀,合理减少了代码量,O(∩_∩)O哈哈~
      Insert image description here
  • controller configuration
    Insert image description here
    Insert image description here

package com.angenin.springcloud.controller;


import com.angenin.springcloud.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.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;

import javax.annotation.Resource;

@RestController
@Slf4j
/**
 * 没有配置过定制的@HystrixCommand(fallbackMethod)方法就走这个全局定义的@DefaultProperties(defaultFallback)方法,
 * 如果配置了就走自定义的@HystrixCommand(fallbackMethod)兜底方法。
 */
@DefaultProperties(defaultFallback = "payment_Global_FallbackMethod")
public class OrderHystirxController {
    
    
    @Resource
    private PaymentHystrixService paymentHystrixService;

    //调用ok方法
    @GetMapping("/consumer/payment/hystrix/ok/{id}")
    public String paymentInfo_OK(@PathVariable("id") Integer id) {
    
    
        String result = paymentHystrixService.paymentInfo_OK(id);
        return result;
    }

    //调用超时方法
    @GetMapping("/consumer/payment/hystrix/timeout/{id}")
    /*@HystrixCommand(fallbackMethod = "paymentTimeOutFallbackMethod",commandProperties = {
            @HystrixProperty(name="execution.isolation.thread.timeoutInMilliseconds",value="1500")
    })*/
    /**
     * 注意:需要注释的是这个@HystrixCommand注解中的属性而不是整个注解
     *   注释掉注解:代表不使用服务降级,正确就正确,错误就错误
     *   使用@HystrixProperty注解:不指定fallbackMethod属性表示使用全局的,指定代表使用定制的。
     */
    @HystrixCommand
    public String paymentInfo_TimeOut(@PathVariable("id") Integer id) {
    
    
        int age = 10/0; //测试出现异常同样要执行兜底的方法
        String result = paymentHystrixService.paymentInfo_TimeOut(id);
        return result;
    }

    public String paymentTimeOutFallbackMethod(@PathVariable("id") Integer id) {
    
    

        return "我是消费者80,对方支付系统繁忙请10秒钟后再试或者自己运行出错请检查自己,o(╥﹏╥)o";
    }

    //下面是全局fallback兜底方法
    public String payment_Global_FallbackMethod() {
    
    
        return "Global异常处理信息,请稍后再试,/(ㄒoㄒ)/~~";
    }
}


  • Restart: 7001, 8001, 80 test
    Insert image description here
  • Effect:http://localhost/consumer/payment/hystrix/timeout/1
    Insert image description here

Solve problem 2: Mix it with business logic? ? ? confusion

  • Idea: As long as consumers use Feign to call, they must have a service interface. Then all methods in this interface can be defined and scheduled in a unified way, so as to achieve the purpose of decoupling.
  • The service is degraded. When the client calls the server, the server is down or shut down (timeouts and runtime exceptions have been tested before).
  • The service degradation processing in this case 在客户端80实现完成的has nothing to do with the server 8001. You only need to add an implementation class for service degradation processing to the interface defined by the Feign client to achieve decoupling.
  • The anomalies we will face in the future
    • run
    • time out
    • Downtime
  • Let’s look at our business class PaymentController again
    Insert image description here
  • Modify cloud-consumer-feign-hystrix-order80
  • According to the existing PaymentHystrixService interface of cloud-consumer-feign-hystrix-order80, create a new class (PaymentFallbackService) to implement the interface, and unify the methods in the interface for exception handling.
  • The PaymentFallbackService class implements the PaymentFeignClientService interface
    Insert image description here
package com.angenin.springcloud.service;


import org.springframework.stereotype.Component;

@Component
public class PaymentFallbackService implements PaymentHystrixService{
    
    
    @Override
    public String paymentInfo_OK(Integer id) {
    
    
        return "-----PaymentFallbackService fall back-paymentInfo_OK ,o(╥﹏╥)o";
    }

    @Override
    public String paymentInfo_TimeOut(Integer id) {
    
    
        return "-----PaymentFallbackService fall back-paymentInfo_TimeOut ,o(╥﹏╥)o";
    }
}

  • YML (already set up before)
    Insert image description here
  • PaymentFeignClientService interface
    Insert image description here
package com.angenin.springcloud.service;

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;

@Component
/**
 * value:调用的微服务名
 * 过程:fallback:相当于去找CLOUD-PROVIDER-HYSTRIX-PAYMENT这个微服务的名字,去调用下面已有的方法,
 *      假如出事了去调用PaymentFallbackService里面的方法
 */
@FeignClient(value = "CLOUD-PROVIDER-HYSTRIX-PAYMENT",fallback = PaymentFallbackService.class )
public interface PaymentHystrixService
{
    
    
    @GetMapping("/payment/hystrix/ok/{id}")
    public String paymentInfo_OK(@PathVariable("id") Integer id);

    @GetMapping("/payment/hystrix/timeout/{id}")
    public String paymentInfo_TimeOut(@PathVariable("id") Integer id);
}

  • test
    • A single eureka starts first 7001
    • PaymentHystrixMain8001 starts, OrderHystrixMain80
    • Normal access test:http://localhost/consumer/payment/hystrix/ok/31
      Insert image description here
    • Deliberately shutting down microservices 8001
      Insert image description here
    • The client calls the prompt: At this time, the server provider is down, but we have done service downgrade processing so that the client will get the prompt information when the server is unavailable without hanging up the server.

10.3.7 Service circuit breaker

1) Theory

  • Circuit breaker: In one word, it is the fuse of your home

  • Overview of the circuit breaker mechanism

    • The circuit breaker mechanism is a microservice link protection mechanism to deal with the avalanche effect. When a microservice on the fan-out link becomes unavailable due to an error or the response time is too long, the
      service will be degraded, thereby interrupting the call of the microservice on the node and quickly returning incorrect response information.
    • When it is detected that the node's microservice call response is normal, the call link is restored.
    • In the Spring Cloud framework, the circuit breaker mechanism is implemented through Hystrix. Hystrix will monitor the status of calls between microservices.
      When failed calls reach a certain threshold, the default is 20 failed calls within 5 seconds, the circuit breaker mechanism will be activated. The annotation of the circuit breaker mechanism is still @HystrixCommand.
    • Master's paper: https://martinfowler.com/bliki/CircuitBreaker.html
      Insert image description here

2) Practical operation

修改cloud-provider-hystrix-payment8001

  • PaymentService
    Insert image description here
    //=========服务熔断

    /**
     * commandProperties中配置的4个注解的含义:
     * true使用断路器,假设在时间窗口期10秒钟内,10次请求有
     * 超过60%都是失败的,那么这个断路器将起作用。
     *
     */
    @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(@PathVariable("id") Integer id) {
    
    
       /* 业务逻辑:
        *  如果输入的id大于等于0,则输出流水号,如果输入的id是个负数抛出异常,
        *  那么根据上面注解配置的熔断机制执行降级的兜底方法paymentCircuitBreaker_fallback
        * */
        if(id<0){
    
    
            throw new RuntimeException("******id 不能负数");
        }
        /**
         * IdUtil.simpleUUID()类似于 UUID.randomUUID().toString().replaceAll("-", "")
         * 它来自于之前在父项目中引入的hutool依赖
         * hutool是个功能强大的JAVA工具包(中国人编写的),官网:https://hutool.cn/
         */
        String serialNumber = IdUtil.simpleUUID();
        return Thread.currentThread().getName()+"\t"+"调用成功,流水号: " + serialNumber;
    }

    //降级的兜底方法
    public String paymentCircuitBreaker_fallback(@PathVariable("id") Integer id) {
    
    
        return "id 不能负数,请稍后再试,/(ㄒoㄒ)/~~   id: " +id;
    }

  • Why configure these parameters, check the official website :https://github.com/Netflix/Hystrix/wiki/How-it-Works#CircuitBreaker
    Insert image description here
    Insert image description here
  • PaymentController
    Insert image description here
    //=========服务熔断
    
    @GetMapping("/payment/circuit/{id}")
    public String paymentCircuitBreaker(@PathVariable("id") Integer id) {
    
    
        String result = paymentService.paymentCircuitBreaker(id);
        log.info("****result: "+result);
        return result;
    }

test:

  • 启动:cloud-eureka-server7001、cloud-provider-hystrix-payment8001

  • Self-test cloud-provider-hystrix-payment8001

  • 正确:http://localhost:8001/payment/circuit/31
    Insert image description here

  • 错误:http://localhost:8001/payment/circuit/-31
    Insert image description here

  • One right, one wrong trytry

  • Key tests: Many mistakes, and then gradually correct, and found that the conditions were not met at the beginning, and even the correct access address could not be used.

    • Explanation: Entering a negative ID multiple times returns an error page (the setting is that more than 60% will blow the circuit breaker). At this time, even if a positive ID is entered, the error page is still returned. After entering a positive ID multiple times, the accuracy rate increases. Slowly restore to the correct page.
    • Entering a negative ID multiple times returns an error page
      Insert image description here
    • At this time, even if you enter a positive ID, the error page is still returned.
      Insert image description here
    • After entering the positive ID multiple times, the accuracy rate increases and the error rate decreases, and the correct page is slowly restored.
      Insert image description here

3) Principle (short summary)

  • God's conclusion
    Insert image description here

  • fuse type

    • fuse open
      • The request no longer calls the current service. The internal set clock is generally MTTR (Mean Time to Failure). When the open time reaches the set clock, it enters the semi-fuse state.
    • circuit breaker closed
      • Turning off the circuit breaker will not circuit break the service.
    • fuse half open
      • Some requests call the current service according to the rules. If the request is successful and complies with the rules, the current service is considered to be back to normal and the circuit breaker is turned off.
  • Official website circuit breaker flow chart
    Insert image description here

    • Official website steps
      Insert image description here

    • Under what circumstances does the circuit breaker start to function?
      Insert image description here
      It involves three important parameters of the circuit breaker: 快照时间窗、请求总数阀值、错误百分比阀值.

      • 1): Snapshot time window: To determine whether to open the circuit breaker, you need to count some request and error data, and the statistical time range is the snapshot time window, which defaults to the latest 10 seconds .
      • 2): Threshold of the total number of requests: Within the snapshot time window, the threshold of the total number of requests must be met to be eligible for circuit breaker. The default is 20 , which means that if the hystrix command is called less than 20 times within 10 seconds, even if all requests time out or fail for other reasons, the circuit breaker will not be opened.
      • 3): Error percentage threshold: When the total number of requests exceeds the threshold within the snapshot time window, for example, 30 calls occur, if among these 30 calls, 15 timeout exceptions occur, that is, more than 50% Error percentage, under the default setting of 50% threshold , the circuit breaker will be opened at this time.
    • Conditions for opening or closing the circuit breaker

      • When a certain threshold is met (default exceeds 20 requests within 10 seconds)
      • When the failure rate reaches a certain level (by default, more than 50% of requests fail within 10 seconds)
      • When the above threshold is reached, the circuit breaker will open
      • When enabled, all requests will not be forwarded.
      • After a period of time (the default is 5 seconds), the circuit breaker is half-open and one of the requests will be forwarded. If successful, the circuit breaker will be closed, if failed, it will remain open. Repeat 4 and 5
    • After the circuit breaker is opened

      • When another request is called, the main logic will not be called, but the downgrade fallback will be called directly. Through the circuit breaker, errors are automatically discovered and the downgrade logic is switched to the main logic, thereby reducing the response delay.
      • How to restore the original main logic?
        For this problem, hystrix also implemented an automatic recovery function for us.
        When the circuit breaker is opened and the main logic is blown, hystrix will start a sleep time window. During this time window, the downgrade logic temporarily becomes the main logic. When the sleep time window expires, the circuit breaker will enter the half-open state. Release a request to the original main logic. If the request returns normally, the circuit breaker will continue to be closed and the main logic will be restored. If there is still a problem with this request, the circuit breaker will continue to enter the open state and the sleep time window will be re-timed.
    • All configuration: as follows

//========================All
@HystrixCommand(fallbackMethod = "str_fallbackMethod",
        groupKey = "strGroupCommand",
        commandKey = "strCommand",
        threadPoolKey = "strThreadPool",

        commandProperties = {
    
    
// 设置隔离策略,THREAD 表示线程池 SEMAPHORE:信号池隔离
@HystrixProperty(name = "execution.isolation.strategy", value = "THREAD"),
// 当隔离策略选择信号池隔离的时候,用来设置信号池的大小(最大并发数)
@HystrixProperty(name = "execution.isolation.semaphore.maxConcurrentRequests", value = "10"),
// 配置命令执行的超时时间
@HystrixProperty(name = "execution.isolation.thread.timeoutinMilliseconds", value = "10"),
// 是否启用超时时间
@HystrixProperty(name = "execution.timeout.enabled", value = "true"),
// 执行超时的时候是否中断
@HystrixProperty(name = "execution.isolation.thread.interruptOnTimeout", value = "true"),
// 执行被取消的时候是否中断
@HystrixProperty(name = "execution.isolation.thread.interruptOnCancel", value = "true"),
// 允许回调方法执行的最大并发数
@HystrixProperty(name = "fallback.isolation.semaphore.maxConcurrentRequests", value = "10"),
// 服务降级是否启用,是否执行回调函数
@HystrixProperty(name = "fallback.enabled", value = "true"),
// 是否启用断路器
@HystrixProperty(name = "circuitBreaker.enabled", value = "true"),
// 该属性用来设置在滚动时间窗中,断路器熔断的最小请求数。例如,默认该值为 20 的时候,
// 如果滚动时间窗(默认10秒)内仅收到了19个请求, 即使这19个请求都失败了,断路器也不会打开。
@HystrixProperty(name = "circuitBreaker.requestVolumeThreshold", value = "20"),
// 该属性用来设置在滚动时间窗中,表示在滚动时间窗中,在请求数量超过
// circuitBreaker.requestVolumeThreshold 的情况下,如果错误请求数的百分比超过50,
                // 就把断路器设置为 "打开" 状态,否则就设置为 "关闭" 状态。
@HystrixProperty(name = "circuitBreaker.errorThresholdPercentage", value = "50"),
// 该属性用来设置当断路器打开之后的休眠时间窗。 休眠时间窗结束之后,
// 会将断路器置为 "半开" 状态,尝试熔断的请求命令,如果依然失败就将断路器继续设置为 "打开" 状态,
// 如果成功就设置为 "关闭" 状态。
@HystrixProperty(name = "circuitBreaker.sleepWindowinMilliseconds", value = "5000"),
// 断路器强制打开
@HystrixProperty(name = "circuitBreaker.forceOpen", value = "false"),
// 断路器强制关闭
@HystrixProperty(name = "circuitBreaker.forceClosed", value = "false"),
// 滚动时间窗设置,该时间用于断路器判断健康度时需要收集信息的持续时间
@HystrixProperty(name = "metrics.rollingStats.timeinMilliseconds", value = "10000"),
// 该属性用来设置滚动时间窗统计指标信息时划分"桶"的数量,断路器在收集指标信息的时候会根据
// 设置的时间窗长度拆分成多个 "桶" 来累计各度量值,每个"桶"记录了一段时间内的采集指标。
// 比如 10 秒内拆分成 10 个"桶"收集这样,所以 timeinMilliseconds 必须能被 numBuckets 整除。否则会抛异常
@HystrixProperty(name = "metrics.rollingStats.numBuckets", value = "10"),
// 该属性用来设置对命令执行的延迟是否使用百分位数来跟踪和计算。如果设置为 false, 那么所有的概要统计都将返回 -1。
@HystrixProperty(name = "metrics.rollingPercentile.enabled", value = "false"),
// 该属性用来设置百分位统计的滚动窗口的持续时间,单位为毫秒。
@HystrixProperty(name = "metrics.rollingPercentile.timeInMilliseconds", value = "60000"),
// 该属性用来设置百分位统计滚动窗口中使用 “ 桶 ”的数量。
@HystrixProperty(name = "metrics.rollingPercentile.numBuckets", value = "60000"),
// 该属性用来设置在执行过程中每个 “桶” 中保留的最大执行次数。如果在滚动时间窗内发生超过该设定值的执行次数,
// 就从最初的位置开始重写。例如,将该值设置为100, 滚动窗口为10秒,若在10秒内一个 “桶 ”中发生了500次执行,
// 那么该 “桶” 中只保留 最后的100次执行的统计。另外,增加该值的大小将会增加内存量的消耗,并增加排序百分位数所需的计算时间。
@HystrixProperty(name = "metrics.rollingPercentile.bucketSize", value = "100"),
// 该属性用来设置采集影响断路器状态的健康快照(请求的成功、 错误百分比)的间隔等待时间。
@HystrixProperty(name = "metrics.healthSnapshot.intervalinMilliseconds", value = "500"),
// 是否开启请求缓存
@HystrixProperty(name = "requestCache.enabled", value = "true"),
// HystrixCommand的执行和事件是否打印日志到 HystrixRequestLog 中
@HystrixProperty(name = "requestLog.enabled", value = "true"),
        },
        threadPoolProperties = {
    
    
// 该参数用来设置执行命令线程池的核心线程数,该值也就是命令执行的最大并发量
@HystrixProperty(name = "coreSize", value = "10"),
// 该参数用来设置线程池的最大队列大小。当设置为 -1 时,线程池将使用 SynchronousQueue 实现的队列,
// 否则将使用 LinkedBlockingQueue 实现的队列。
@HystrixProperty(name = "maxQueueSize", value = "-1"),
// 该参数用来为队列设置拒绝阈值。 通过该参数, 即使队列没有达到最大值也能拒绝请求。
// 该参数主要是对 LinkedBlockingQueue 队列的补充,因为 LinkedBlockingQueue
                // 队列不能动态修改它的对象大小,而通过该属性就可以调整拒绝请求的队列大小了。
@HystrixProperty(name = "queueSizeRejectionThreshold", value = "5"),
        }
)
public String strConsumer() {
    
    
return "hello 2020";
}
public String str_fallbackMethod()
{
    
    
return "*****fall back str_fallbackMethod";
}

10.3.8 Service current limit

  • The advanced chapter below will explain Alibaba’s Sentinel instructions.

10.4 hystrix workflow

  • https://github.com/Netflix/Hystrix/wiki/How-it-Works
    Insert image description here

  • Hystrix workflow

    • Official website legend
      Insert image description here

    • Step instructions

      • 1) Create a HystrixCommand (used when the dependent service returns a single operation result) or HystrixObserableCommand (used when the dependent service returns multiple operation results) objects.

      • 2) Command execution. Among them, HystrixComand implements the first two execution methods below; and HystrixObservableCommand implements the latter two execution methods: execute(): synchronous execution, returning a single result object from the dependent service, or throwing an exception when an error occurs. queue(): Asynchronous execution, directly returns a Future object, which contains a single result object to be returned when the service execution ends. observe(): Returns an Observable object, which represents multiple results of the operation. It is a Hot Observable (regardless of whether the "event source" has "subscribers", the event will be published after creation, so for each Hot Observable A "subscriber" may start halfway through the "event source" and may only see part of the entire operation). toObservable(): It also returns an Observable object, which also represents multiple results of the operation, but it returns a Cold Observable (it does not publish events when there are no "subscribers", but waits until there is a "subscriber" "The event is published only after", so for Cold Observable's subscribers, it can guarantee to see the entire process of the entire operation from the beginning).

      • 3) If the request caching function of the current command is enabled and the command cache is hit, the cached result will be returned immediately in the form of an Observable object.

      • 4) Check whether the circuit breaker is open. If the circuit breaker is open, Hystrix will not execute the command and instead transfers to the fallback processing logic (step 8); if the circuit breaker is closed, check whether there are available resources to execute the command (step 5).

      • 5) Whether the thread pool/request queue/semaphore is full. If the command relies on the dedicated thread pool and request queue of the service, or the semaphore (when the thread pool is not used) is already full, then Hystrix will not execute the command, but will transfer to the fallback processing logic (step 8) .

      • 6) Hystrix will decide how to request dependent services based on the method we wrote. HystrixCommand.run(): Returns a single result, or throws an exception. HystrixObservableCommand.construct(): Returns an Observable object to emit multiple results, or send error notifications through onError.

      • 7) Hystrix will report "success", "failure", "rejection", "timeout" and other information to the circuit breaker, and the circuit breaker will maintain a set of counters to count these data. The circuit breaker will use these statistics to decide whether to open the circuit breaker to "blow/short-circuit" requests for a dependent service.

      • 8) When the command execution fails, Hystrix will enter fallback and try to roll back the process. We usually call this operation "service downgrade". The following situations can cause service degradation: Step 4: The current command is in the "fuse/short circuit" state and the circuit breaker is open. Step 5: When the thread pool, request queue or semaphore of the current command is full. Step 6: When HystrixObservableCommand.construct() or HystrixCommand.run() throws an exception.

      • 9) When the Hystrix command is successfully executed, it will return the processing result directly or in the form of an Observable.

      • Tips: If we do not implement degradation logic for the command or an exception is thrown in the degradation processing logic, Hystrix will still return an Observable object, but it will not emit any result data. Instead, it will notify the command to immediately interrupt the request through the onError method, and The exception that caused the command failure is sent to the caller through the onError() method.

10.5 Service monitoring hystrixDashboard

10.5.1 Overview

  • In addition to isolating calls to dependent services, Hystrix also provides that 准实时的调用监控(Hystrix Dashboard)Hystrix will continuously record the execution information of all requests initiated through Hystrix and display it to users in the form of statistical reports and graphics, including how many requests are executed per second, how many successes, and how many Failure etc. Netflix implements the monitoring of the above indicators through the hystrix-metrics-event-stream project. Spring Cloud also provides integration with Hystrix Dashboard to transform monitoring content into a visual interface.
    • Namely: graphical display of hystrix’s dashboard monitoring interface

10.5.2 Dashboard 9001

1) Create new cloud-consumer-hystrix-dashboard9001

Insert image description here

2)POM

Insert image description here

<?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>cloud2020</artifactId>
        <groupId>com.angenin.springcloud</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>cloud-consumer-hystrix-dashboard9001</artifactId>

    <properties>
        <maven.compiler.source>8</maven.compiler.source>
        <maven.compiler.target>8</maven.compiler.target>
    </properties>
    <dependencies>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-hystrix-dashboard</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>

3)YML

Insert image description here

server:
  port: 9001

4) Main startup class

Insert image description here

package com.angenin.springcloud;

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

@SpringBootApplication
@EnableHystrixDashboard //启用Hystrix仪表盘
public class HystrixDashboardMain9001
{
    
    
    public static void main(String[] args)
    {
    
    
        SpringApplication.run(HystrixDashboardMain9001.class,args);
    }
}

5) View all producer pom files

  • All Provider microservice classes (8001/8002/8003) need to monitor dependency configurations
    Insert image description here

6) Test

  • Start cloud-consumer-hystrix-dashboard9001. This microservice will subsequently monitor microservice 8001.
  • http://localhost:9001/hystrix. The appearance of this interface indicates that the hystrixDashboard monitoring platform has been established.
    Insert image description here

10.5.3 Circuit breaker demonstration (service monitoring hystrixDashboard)

  • Demonstration: Take the previous case of 8001 service circuit breaker as an example to see what icons will be generated by 9001 monitoring 8001.

  • It requires building a 9001 monitoring and testing platform yourself, which is troublesome. After Hystrix was replaced by sentienl of domestic Alibaba, you can directly log in to a website for monitoring.

1) Modify cloud-provider-hystrix-payment8001

Precautions:

  1. The monitored producer service generally needs to add two dependencies for graphical monitoring to take effect.
    Insert image description here
  2. The new version of Hystrix needs to specify the monitoring path in the main startup class MainAppHystrix8001, otherwise Unable to connect to Command Metric Stream 404 will be reported.
    Insert image description here
package com.angenin.springcloud;

import com.netflix.hystrix.contrib.metrics.eventstream.HystrixMetricsStreamServlet;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.web.servlet.ServletRegistrationBean;
import org.springframework.cloud.client.circuitbreaker.EnableCircuitBreaker;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
import org.springframework.context.annotation.Bean;

@EnableEurekaClient
@SpringBootApplication
@EnableCircuitBreaker //激活
public class PaymentHystrixMain8001 {
    
    

    public static void main(String[] args) {
    
    
        SpringApplication.run(PaymentHystrixMain8001.class, args);
    }

    /**
     *此配置是为了服务监控而配置,与服务容错本身无关,springcloud升级后的坑
     *ServletRegistrationBean因为springboot的默认路径不是"/hystrix.stream",
     *只要在自己的项目里配置上下面的servlet就可以了
     */
    @Bean
    public ServletRegistrationBean getServlet() {
    
    
        HystrixMetricsStreamServlet streamServlet = new HystrixMetricsStreamServlet();
        ServletRegistrationBean registrationBean = new ServletRegistrationBean(streamServlet);
        registrationBean.setLoadOnStartup(1);
        registrationBean.addUrlMappings("/hystrix.stream");
        registrationBean.setName("HystrixMetricsStreamServlet");
        return registrationBean;
    }

}

2) Monitoring test

You can start 1 eureka or 3 eureka clusters

  • 启动cloud-eureka-server7001,cloud-provider-hystrix-payment8001,cloud-consumer-hystrix-dashboard9001

Observe monitoring window

  • 9001 monitoring 8001

    • Fill in the monitoring address: http://localhost:8001/hystrix.stream
      Insert image description here
  • Test address (these are the two addresses of the 8001 test service circuit breaker above, 10.3.7 directory)

    • http://localhost:8001/payment/circuit/31 (Enter a positive ID to return to the correct page)
      Insert image description here

    • http://localhost:8001/payment/circuit/-31 Enter a negative ID and return to the error page)
      Insert image description here

    • The above test passed

    • ok

    • Visit the correct address first, then the wrong address, and then the correct address. You will find that the circuit breaker in the picture is slowly released.

      • Monitoring results, successful
        Insert image description here

      • Monitoring results, failed
        Insert image description here

  • How to see it?

    • 7 colors
      Insert image description here

    • 1 lap

      • Solid circle: There are two meanings. It represents the health of the instance through the change of color. Its health changes from green to green. In addition to the change of color, its size will also change according to the request traffic of the instance. The greater the traffic, the larger the solid circle will be. . Therefore, through the display of the solid circle, you can quickly discover it in a large number of examples 故障实例和高压力实例.
        Insert image description here
    • 1 line

      • Curve: used to record the relative changes in flow within 2 minutes, through which the upward and downward trends in flow can be observed.
        Insert image description here
    • Whole picture description
      Insert image description here

    • Whole picture description 2
      Insert image description here

  • Only when you understand one can you understand the complex one
    Insert image description here

11. zuul routing gateway

  • Zuul is outdated, so I won’t explain it here. Let’s go directly to the new generation gateway Gateway. If the company’s old projects use zuul technology, take notes and check the brain map.
    Insert image description here

12. Gateway new generation gateway

12.1 Overview Introduction

  • Why do you need a gateway?
    Insert image description here

  • Technical implementation of gateway
    Insert image description here

  • Official website

    • Previous generation zuul 1.X: https://github.com/Netflix/zuul/wiki
      Insert image description here

    • 当前gateway:https://cloud.spring.io/spring-cloud-static/spring-cloud-gateway/2.2.1.RELEASE/reference/html/
      Insert image description here

  • what is
    Insert image description here

    • Overview
      Insert image description here

    • In one sentence:

      • The reactor-netty reactive programming component in Webflux used by SpringCloud Gateway uses the Netty communication framework at the bottom.
      • Source code architecture
        Insert image description here
  • What can you do

    • reverse proxy
    • Authentication
    • flow control
    • fuse
    • Log monitoring
  • Where is the gateway in microservice architecture?
    Insert image description here

  • Why did the gateway come out again when there was Zuul?

    • Why did we choose Gateway?

      • neflix is ​​not reliable, zuul2.0 has been delayed and has not been released.
        Insert image description here

      • SpringCloud Gateway has the following features:
        Insert image description here

      • The difference between SpringCloud Gateway and Zuul
        Insert image description here

    • Zuul1.x model
      Insert image description here

    • GateWay model

      • What is WebFlux
        Insert image description here

      • https://docs.spring.io/spring/docs/current/spring-framework-reference/web-reactive.html#webflux-new-framework
        Insert image description here

      • Note: Traditional Web frameworks, such as struts2, springmvc, etc., all run based on Servlet API and Servlet container. But 在Servlet3.1之后有了异步非阻塞的支持. WebFlux is a typical non-blocking asynchronous framework, and its core is implemented based on Reactor-related APIs. Compared with traditional web frameworks, it can run on containers such as Netty, Undertow and Servlet3.1. Non-blocking + functional programming (Spring5 must let you use java8) Spring WebFlux is a new reactive framework introduced by Spring 5.0. Different from Spring MVC, it does not need to rely on the Servlet API. It is completely asynchronous and non-blocking, and is based on Reactor to implement the reactive streaming specification.

12.2 Three core concepts

  • Route

    • Routing is the basic module for building a gateway. It consists of an ID, a target URI, a series of assertions and filters. If the assertion is true, the route is matched.
  • Predicate

    • The reference is Java8's java.util.function.Predicate
      developers can match everything in the HTTP request (such as request headers or request parameters),如果请求与断言相匹配则进行路由
  • Filter

    • Refers to the instance of GatewayFilter in the Spring framework. Using filters, requests can be modified before or after the request is routed.
  • overall:
    Insert image description here

    • The web request locates the real service node through some matching conditions. And perform some refined control before and after this forwarding process.
      predicate is our matching condition;
    • And filter can be understood as an omnipotent interceptor. With these two elements, plus the target uri, a specific route can be implemented.

12.3 Gateway workflow

  • Official website summary
    Insert image description here

  • Core logic:路由转发+执行过滤器链

12.4 Getting Started Configuration

12.4.1 Gateway9527 setup

1) Create a new Module

  • cloud-gateway-gateway9527
    Insert image description here

2)POM

Insert image description here

<?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>cloud2020</artifactId>
        <groupId>com.angenin.springcloud</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>cloud-gateway-gateway9527</artifactId>

    <properties>
        <maven.compiler.source>8</maven.compiler.source>
        <maven.compiler.target>8</maven.compiler.target>
    </properties>

    <dependencies>
        <!--gateway-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-gateway</artifactId>
        </dependency>
        <!--eureka-client-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
        </dependency>
        <!-- 引入自己定义的api通用包,可以使用Payment支付Entity -->
        <dependency>
            <groupId>com.angenin.springcloud</groupId>
            <artifactId>cloud-api-commons</artifactId>
            <version>${project.version}</version>
        </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>

3)YML

  • As a microservice, gateWay gateway also needs to be registered in the service center. Any registration center can be used, such as zk
    Insert image description here
server:
  port: 9527

spring:
  application:
    name: cloud-gateway

eureka:
  instance:
    hostname: cloud-gateway-service
  client: #服务提供者provider注册进eureka服务列表内
    service-url:
      register-with-eureka: true
      fetch-registry: true
      defaultZone: http://eureka7001.com:7001/eureka  #以单机版为例,不然集群启动太慢

4) No business category

  • The gateway does not have a business class. He is the one who watches the gate from the front.

5) Main startup class

Insert image description here

package com.angenin.springcloud;


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

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

6) How does the 9527 gateway do routing mapping? ? ?

  • Take cloud-provider-payment8001 as an example

  • cloud-provider-payment8001Look at the access address of the controller

    • get method: test query function
      Insert image description here

    • lib: Test custom load balancing rules
      Insert image description here

  • We don't want to expose port 8001 at the moment. We hope to put a layer of 9527 outside 8001. This is safer and prevents others from directly attacking 8001. There is a layer of gateway blocking it.

7) New gateway configuration in YML

Insert image description here

server:
  port: 9527

spring:
  application:
    name: cloud-gateway
  #网关配置:
  cloud:
    gateway:
      routes:  #routes表示可以路由多个,可以为某个controller里面的所有rest接口都可以做路由
        #第一个路由
        - id: payment_routh #payment_route    #路由的ID,没有固定规则但要求唯一,建议配合服务名
          uri: http://localhost:8001          #匹配后提供服务的路由地址    写死的
          predicates:
            - Path=/payment/get/**         # 断言,路径相匹配的进行路由

        #第二个路由
        - id: payment_routh2 #payment_route    #路由的ID,没有固定规则但要求唯一,建议配合服务名
          uri: http://localhost:8001          #匹配后提供服务的路由地址    写死的
          predicates:
            - Path=/payment/lb/**         # 断言,路径相匹配的进行路由


eureka:
  instance:
    hostname: cloud-gateway-service
  client: #服务提供者provider注册进eureka服务列表内
    service-url:
      register-with-eureka: true
      fetch-registry: true
      defaultZone: http://eureka7001.com:7001/eureka  #以单机版为例,不然集群启动太慢

8) Test

  • 启动cloud-eureka-server7001,cloud-provider-payment8001,cloud-gateway-gateway9527

  • A small bug in the introduction of jar packages. The gateway gateway does not need to introduce web-related dependencies. If it is introduced at startup, an error will be reported.
    Insert image description here

  • Access instructions
    Insert image description here

    • Before adding gateway: http://localhost:8001/payment/get/4
      Insert image description here

    • After adding the gateway: http://localhost:9527/payment/get/4, it can also be accessed correctly.
      Insert image description here

9) Two ways to configure routing in Gateway'

Method 1: Configure in the configuration file yml, see the previous steps

Method 2: Inject the RouteLocator Bean in the code

  • Official website case: https://cloud.spring.io/spring-cloud-static/spring-cloud-gateway/2.2.1.RELEASE/reference/html/#spring-cloud-circuitbreaker-filter-factory
    Insert image description here

Insert image description here

  • Baidu’s domestic news website requires an external network: http://news.baidu.com/guonei (it seems that this website no longer exists)
    Insert image description here

  • Write one yourself

    • Baidu News

    • Business requirements: Access the Baidu News website on the external network through 9527 gateway

    • coding

      • cloud-gateway-gateway9527
      • Business implementation, config, and configuration class content are detailed below.
        Insert image description here
    • test:

      • Restart project 9527
      • http://localhost:9527/guonei, returns the same news page
        Insert image description here

Configuration content:

package com.angenin.springcloud.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;

@Configuration
public class GateWayConfig {
    
    
    /**
     * 配置了一个id为route-name的路由规则,
     * 当访问地址 http://localhost:9527/guonei时会自动转发到地址:http://news.baidu.com/guonei
     * @param
     * @return
     */
    @Bean
    public RouteLocator customRouteLocator(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();
    }
}

12.5 Implement dynamic routing through microservice names

  • Current problems: The address is hard-coded, and there may be multiple service providers, so load balancing is required.
    Insert image description here

  • Architecture
    Insert image description here

  • By default, Gateway will be created based on the service list registered in the registration center with the path of the microservice name on the registration center.动态路由进行转发,从而实现动态路由的功能

  • Startup: one eureka7001 (cloud-eureka-server7001) + two service providers 8001/8002 (cloud-provider-payment8001, cloud-provider-payment8002)

  • POM: This dependency has been added before and the service is registered in the registration center
    Insert image description here

  • YML

    • It should be noted that the protocol of uri is lb, which means the load balancing function of Gateway is enabled.
    • lb://serviceName is the load balancing uri automatically created for us by spring cloud gateway in the microservice.
      Insert image description here
server:
  port: 9527

spring:
  application:
    name: cloud-gateway
  #网关配置:
  cloud:
    gateway:
      discovery:
        locator:
          enabled: true #开启从注册中心动态创建路由的功能,利用微服务名进行路由
      routes:  #routes表示可以路由多个,可以为某个controller里面的所有rest接口都可以做路由
        #第一个路由
        - id: payment_routh #payment_route    #路由的ID,没有固定规则但要求唯一,建议配合服务名
          #uri: http://localhost:8001          #匹配后提供服务的路由地址    写死的
          uri: lb://cloud-payment-service #匹配后提供服务的路由地址(8001 8002集群组成的微服务名称)    替换为动态的
          predicates:
            - Path=/payment/get/**         # 断言,路径相匹配的进行路由

        #第二个路由
        - id: payment_routh2 #payment_route    #路由的ID,没有固定规则但要求唯一,建议配合服务名
          #uri: http://localhost:8001          #匹配后提供服务的路由地址    写死的
          uri: lb://cloud-payment-service #匹配后提供服务的路由地址    替换为动态的
          predicates:
            - Path=/payment/lb/**         # 断言,路径相匹配的进行路由


eureka:
  instance:
    hostname: cloud-gateway-service
  client: #服务提供者provider注册进eureka服务列表内
    service-url:
      register-with-eureka: true
      fetch-registry: true
      defaultZone: http://eureka7001.com:7001/eureka  #以单机版为例,不然集群启动太慢

  • test
    • On startup 9527
    • http://localhost:9527/payment/lb
    • 8001/8002 two port switching
      Insert image description here
      Insert image description here

12.6 Use of Predicate

12.6.1 What is

  • Start our gateway9527
    Insert image description here
  • That is: multiple matching rules under predicates
    Insert image description here

12.6.2 What are Route Predicate Factories?

  • https://cloud.spring.io/spring-cloud-static/spring-cloud-gateway/2.2.1.RELEASE/reference/html/#gateway-request-predicates-factories
    Insert image description here

  • Spring Cloud Gateway implements route matching as part of the Spring WebFlux HandlerMapping infrastructure. Spring Cloud Gateway includes a number of built-in Route Predicate factories. All these Predicates match different properties of the HTTP request. Multiple Route Predicate factories can be combined

  • When Spring Cloud Gateway creates a Route object, it uses RoutePredicateFactory to create a Predicate object, and the Predicate object can be assigned to Route. Spring Cloud Gateway includes many built-in Route Predicate Factories.

  • All these predicates match different properties of the HTTP request. Various predicate factories can be combined and passed through logical and.

12.6.3 Commonly used Route Predicate

Insert image description here

  1. After Route Predicate
  2. Before Route Predicate
  3. Between Route Predicate
  4. Cookie Route Predicate
  5. Header Route Predicate
  6. Host Route Predicate
  7. Method Route Predicate
  8. Path Route Predicate
  9. Query Route Predicate

12.6.4 Demonstrate commonly used Route Predicate

1)After Route Predicate

Official website case:
Insert image description here

spring:
  cloud:
    gateway:
      routes:
      - id: after_route
        uri: https://example.org
        predicates:
        - After=2017-01-20T17:42:47.789-07:00[America/Denver]

Question: You can directly copy this line of configuration, but how do you write the time format you want, and how do you write its time zone? ? ?

Test it yourself:

  • Test class: The purpose is to obtain the date and time format and time zone written after After
    Insert image description here

import java.time.ZonedDateTime;

public class T2 {
    
    
    public static void main(String[] args) {
    
    

        //获取默认时区的当前时间
        ZonedDateTime zbj = ZonedDateTime.now(); // 默认时区
        //2023-08-27T19:32:02.975+08:00[Asia/Shanghai]
        System.out.println(zbj);

        // 用指定时区获取当前时间(美国/纽约)
        //ZonedDateTime zny = ZonedDateTime.now(ZoneId.of("America/New_York"));
        //System.out.println(zny);
    }
}



  • yml: Then copy this time format to the yml configuration file, which means that the lb address will not take effect until the specified time. The two conditions are used in combination.
    Insert image description here
server:
  port: 9527

spring:
  application:
    name: cloud-gateway
  #网关配置:
  cloud:
    gateway:
      discovery:
        locator:
          enabled: true #开启从注册中心动态创建路由的功能,利用微服务名进行路由
      routes:  #routes表示可以路由多个,可以为某个controller里面的所有rest接口都可以做路由
        #第一个路由
        - id: payment_routh #payment_route    #路由的ID,没有固定规则但要求唯一,建议配合服务名
          #uri: http://localhost:8001          #匹配后提供服务的路由地址    写死的
          uri: lb://cloud-payment-service #匹配后提供服务的路由地址(8001 8002集群组成的微服务名称)    替换为动态的
          predicates:
            - Path=/payment/get/**         # 断言,路径相匹配的进行路由

        #第二个路由
        - id: payment_routh2 #payment_route    #路由的ID,没有固定规则但要求唯一,建议配合服务名
          #uri: http://localhost:8001          #匹配后提供服务的路由地址    写死的
          uri: lb://cloud-payment-service #匹配后提供服务的路由地址    替换为动态的
          predicates:
            - Path=/payment/lb/**         # 断言,路径相匹配的进行路由
            - After=2023-08-27T19:32:02.975+08:00[Asia/Shanghai] #这个匹配的请求要在指定的时间之后,路由地址lb才生效(亚洲/上海)


eureka:
  instance:
    hostname: cloud-gateway-service
  client: #服务提供者provider注册进eureka服务列表内
    service-url:
      register-with-eureka: true
      fetch-registry: true
      defaultZone: http://eureka7001.com:7001/eureka  #以单机版为例,不然集群启动太慢


  • test:
    • If the current time is configured, then the current time must have passed during the test, so it will take effect. Restart 9527 and access successfully, and the two ports 8001/8002 are switched.
      Insert image description here
      http://localhost:9527/payment/lb
      Insert image description here
      Insert image description here
    • Modify this time to a time that has not yet arrived: for example, the current time is 2023.8 27.19:52, change it to 2023.8 27.20:52, the time has not arrived at this time, so it will not take effect, and access error http://localhost:9527/payment/
      Insert image description here
      lb
      Insert image description here

2)Before Route Predicate

  • The usage is the same as 1) After Route Predicate
  • There are two items written under predicates, which means that the lb address will not take effect until the specified time. They are used in combination.
    Insert image description here
# 在指定的时间之前生效
- Before=2020-02-05T15:10:03.685+08:00[Asia/Shanghai]         # 断言,路径相匹配的进行路由

3)Between Route Predicate

  • The usage is the same as 1) After Route Predicate
  • 2 items used together
    Insert image description here
# 在指定的2个时间内生效
- Between=2020-02-02T17:45:06.206+08:00[Asia/Shanghai],2020-03-25T18:59:06.206+08:00[Asia/Shanghai]

4)Cookie Route Predicate

  • Official website case:
    Insert image description here
    Cookie Route Predicate requires two parameters, one is Cookie name and the other is regular expression.
    The routing rules will be matched by obtaining the corresponding cookie name value and the regular expression. If there is a match, the routing will be executed. If there is no match, the routing will not be executed.
spring:
  cloud:
    gateway:
      routes:
      - id: cookie_route
        uri: https://example.org
        predicates:
        - Cookie=chocolate, ch.p

Test it yourself:

  • Simulate the way to send get and post requests? ? ?
    • jmeter tool
    • postman tool (graphical tool)
    • curl command (command used by the underlying graphical tool)
  • In this case, the curl command is used to debug microservices.
  • yml file: indicates that after the specified time, the lb path can take effect only if the cookie format is met.
    Insert image description here
        #第二个路由
        - id: payment_routh2 #payment_route    #路由的ID,没有固定规则但要求唯一,建议配合服务名
          #uri: http://localhost:8001          #匹配后提供服务的路由地址    写死的
          uri: lb://cloud-payment-service #匹配后提供服务的路由地址    替换为动态的
          predicates:
            - Path=/payment/lb/**         # 断言,路径相匹配的进行路由
            - After=2023-08-27T19:32:02.975+08:00[Asia/Shanghai] #这个匹配的请求要在指定的时间之后,路由地址lb才生效(亚洲/上海)
            #- Before=2020-02-05T15:10:03.685+08:00[Asia/Shanghai]   #在指定时间之前生效
            #- Between=2020-02-02T17:45:06.206+08:00[Asia/Shanghai],2020-03-25T18:59:06.206+08:00[Asia/Shanghai] #在指定的2个时间之内生效
            - Cookie=username,zzyy #满足cookie名和正则表达式格式

  • To access without cookies, send a get request:curl http://localhost:9527/payment/lb

    • At this time, cookies are not satisfied and therefore do not take effect.
      Insert image description here
  • Visit with cookies:curl http://localhost:9527/payment/lb --cookie "username=zzyy"

    • After the specified time is met, the cookie format is met, so the lb path takes effect, and the effects 8001 and 8002 appear alternately.
      Insert image description here
    • If adding curl returns Chinese garbled characters, check the blog at this address for a solution.
      https://blog.csdn.net/leedee/article/details/82685636
      Insert image description here

5)Header Route Predicate

  • Official website case
    Insert image description here
    Two parameters: one is the attribute name and a regular expression. If the attribute value matches the regular expression, it will be executed.
spring:
  cloud:
    gateway:
      routes:
      - id: header_route
        uri: https://example.org
        predicates:
        - Header=X-Request-Id, \d+

Test it yourself

  • yml file: lb will take effect only if the matching rules of the request header are met (see clearly those that are commented out and those that are not commented out)
    Insert image description here
        #第二个路由
        - id: payment_routh2 #payment_route    #路由的ID,没有固定规则但要求唯一,建议配合服务名
          #uri: http://localhost:8001          #匹配后提供服务的路由地址    写死的
          uri: lb://cloud-payment-service #匹配后提供服务的路由地址    替换为动态的
          predicates:
            - Path=/payment/lb/**         # 断言,路径相匹配的进行路由
            #- After=2023-08-27T19:32:02.975+08:00[Asia/Shanghai] #这个匹配的请求要在指定的时间之后,路由地址lb才生效(亚洲/上海)
            #- Before=2020-02-05T15:10:03.685+08:00[Asia/Shanghai]   #在指定时间之前生效
            #- Between=2020-02-02T17:45:06.206+08:00[Asia/Shanghai],2020-03-25T18:59:06.206+08:00[Asia/Shanghai] #在指定的2个时间之内生效
            #- Cookie=username,zzyy  #满足cookie名和正则表达式格式
            - Header=X-Request-Id, \d+  # 请求头要有X-Request-Id属性并且值为整数的正则表达式

  • curl test:curl http://localhost:9527/payment/lb -H "X-Request-Id:123"
    Insert image description here

6)Host Route Predicate

  • The official website document
    Insert image description here
    Host Route Predicate receives a set of parameters and a list of matching domain names. This template is an ant-delimited template, using . as the separator.
    It uses the host address in the parameter as a matching rule.

test

  • yml
    Insert image description here
- Host=**.atguigu.com #根据主机地址进行匹配

  • input the command
    • 正确:curl http://localhost:9527/payment/lb -H “Host: www.atguigu.com”
    • 错误:curl http://localhost:9527/payment/lb -H “Host: java.atguigu.net”

Insert image description here

7)Method Route Predicate

  • Official documentation
    Insert image description here

Test it yourself:

  • yml
    Insert image description here
- Method=GET   #根据请求方式进行匹配
  • Enter the address: write nothing, just get request
    Insert image description here

8)Path Route Predicate

  • Official documentation
    Insert image description here

test

  • It is the previous path matching. For details, see 12.4 Getting Started Configuration----7), 8)
    Insert image description here

9)Query Route Predicate

  • Official documentation
    Insert image description here

Test it yourself

  • yml
    Insert image description here
- Query=username, \d+  # 要有参数名username并且值还要是整数才能路由
  • 正确:http://localhost:9527/payment/lb?username=31
  • 错误:http://localhost:9527/payment/lb?username=-31

10) Summary

  • Total yml
    Insert image description here
server:
  port: 9527

spring:
  application:
    name: cloud-gateway
  #网关配置:
  cloud:
    gateway:
      discovery:
        locator:
          enabled: true #开启从注册中心动态创建路由的功能,利用微服务名进行路由
      routes:  #routes表示可以路由多个,可以为某个controller里面的所有rest接口都可以做路由
        #第一个路由
        - id: payment_routh #payment_route    #路由的ID,没有固定规则但要求唯一,建议配合服务名
          #uri: http://localhost:8001          #匹配后提供服务的路由地址    写死的
          uri: lb://cloud-payment-service #匹配后提供服务的路由地址(8001 8002集群组成的微服务名称)    替换为动态的
          predicates:
            - Path=/payment/get/**         # 断言,路径相匹配的进行路由

        #第二个路由
        - id: payment_routh2 #payment_route    #路由的ID,没有固定规则但要求唯一,建议配合服务名
          #uri: http://localhost:8001          #匹配后提供服务的路由地址    写死的
          uri: lb://cloud-payment-service #匹配后提供服务的路由地址    替换为动态的
          predicates:
            - Path=/payment/lb/**         # 断言,路径相匹配的进行路由
            #- After=2023-08-27T19:32:02.975+08:00[Asia/Shanghai] #这个匹配的请求要在指定的时间之后,路由地址lb才生效(亚洲/上海)
            #- Before=2020-02-05T15:10:03.685+08:00[Asia/Shanghai]   #在指定时间之前生效
            #- Between=2020-02-02T17:45:06.206+08:00[Asia/Shanghai],2020-03-25T18:59:06.206+08:00[Asia/Shanghai] #在指定的2个时间之内生效
            #- Cookie=username,zzyy  #满足cookie名和正则表达式格式
            #- Header=X-Request-Id, \d+  # 请求头要有X-Request-Id属性并且值为整数的正则表达式
            #- Host=**.atguigu.com
            #- Method=GET   #根据请求方式进行匹配
            - Query=username, \d+  # 要有参数名username并且值还要是整数才能路由



eureka:
  instance:
    hostname: cloud-gateway-service
  client: #服务提供者provider注册进eureka服务列表内
    service-url:
      register-with-eureka: true
      fetch-registry: true
      defaultZone: http://eureka7001.com:7001/eureka  #以单机版为例,不然集群启动太慢

  • To put it bluntly, Predicate is to implement a set of matching rules so that requests can find the corresponding Route for processing.

12.7 Use of Filter

  • GatewayFilter is a filter provided in the gateway, which can process requests entering the gateway and responses returned by microservices:
    Insert image description here

12.7.1 What is

Insert image description here

Routing filters can be used to modify incoming HTTP requests and returned HTTP responses. Routing filters can only specify routes for use.

Spring Cloud Gateway has a variety of built-in routing filters, all of which are generated by the factory class of GatewayFilter.

12.7.2 Spring Cloud Gateway的Filter

Life cycle, Only Two

  • pre: before business logic
  • post: after business logic

Type, Only Two:

  • GatewayFilter: single
    Insert image description here

  • GlobalFilter: global
    Insert image description here

  • Please check the official website for details: https://cloud.spring.io/spring-cloud-static/spring-cloud-gateway/2.2.1.RELEASE/reference/html/#gatewayfilter-factories
    Insert image description here

12.7.3 Commonly used GatewayFilter (single filter)

  • YML
    Insert image description here
          filters:
            - AddRequestParameter=X-Request-Id,1024 #过滤器工厂会在匹配的请求头加上一对请求头,名称为X-Request-Id值为1024
  • For the rest, check the official website

12.7.4 Customize the global GlobalFilter (global filter)

1) Introduction to two main interfaces

  • implements GlobalFilter,Ordered

2) What can you do?

  • Global logging
  • Unified gateway authentication

3) Case code

Insert image description here

package com.angenin.springcloud.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.http.HttpStatus;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

import java.util.Date;

@Component
@Slf4j
public class MyLogGateWayFilter implements GlobalFilter, Ordered {
    
    

    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
    
    
        log.info("**************come in MyLogGateWayFilter:" + new Date());
        //获取request中的uname参数
        String uname = exchange.getRequest().getQueryParams().getFirst("uname");

        if(uname == null){
    
    
            log.info("*******用户名为null,非法用户!!");
            //设置 response 状态码   因为在请求之前过滤的,so就算是返回NOT_FOUND 也不会返回错误页面
            exchange.getResponse().setStatusCode(HttpStatus.NOT_ACCEPTABLE);
            //完成请求调用
            return exchange.getResponse().setComplete();
        }

        //过滤链放行
        return chain.filter(exchange);

    }

    @Override
    public int getOrder() {
    
    
        //返回值是过滤器的优先级,越小优先级越高(最小-2147483648,最大2147483648)
        return 0;
    }
}


4) Test

  • start up
    Insert image description here

  • Correct: http://localhost:9527/payment/lb?uname=z3 (8001 8002 switches back and forth)
    Insert image description here

  • mistake

    • There is no parameter uname: http://localhost:9527/payment/lb, forwarding cannot be used normally.
      Insert image description here
  • 9527 console log
    Insert image description here

13 SpringCloud Config distributed configuration center

13.1 Overview

13.1.1 Configuration issues faced by distributed systems

  • Microservices means splitting the business in a single application into sub-services. The granularity of each service is relatively small, so there will be a large number of services in the system. Since each service requires necessary configuration information to run, a centralized, dynamic configuration management facility is essential.

  • SpringCloud provides ConfigServer to solve this problem. Each of our microservices has its own application.yml to manage hundreds of configuration files... /
    (ㄒoㄒ)/~~

13.1.2 What is

  • what is

    • SpringCloud Config provides centralized external configuration support for microservices in a microservice architecture, and a configuration server is 各个不同微服务应用provided for all environments 中心化的外部配置.
  • How to play

    • SpringCloud Config is divided into 服务端和客户端两部分.
    • The server is also called the server 分布式配置中心,它是一个独立的微服务应用, which is used to connect to the configuration server and provide access interfaces for clients to obtain configuration information, encrypt/decrypt information, etc.
    • The client manages application resources and business-related configuration content through the designated configuration center, and obtains and loads configuration information from the configuration center at startup. The configuration server uses git to store configuration information by default, which is helpful. Version management of environment configuration, and configuration content can be easily managed and accessed through git client tools.
  • Example:

    • Problem: The yml files of the three microservice projects are all connected to the same database. If the port number needs to be modified at this time, all three yml files need to be modified.
    • Solution: Extract public configurations to the configuration center, and write private configurations in their own yml files, which makes management easier.
    • As shown below: the operation and maintenance engineer modifies the configuration on GitHub, then synchronizes it locally, and then synchronizes it locally to the shared configuration center, and then lets ABC download it together, so that the operation and maintenance engineer can modify the configuration once and publish it everywhere.
      Insert image description here

13.1.3 What can be done

  • Centrally manage configuration files
  • Different configurations for different environments, dynamic configuration updates, and environment-specific deployment such as dev/test/prod/beta/release
  • Dynamically adjust the configuration during operation. There is no need to write configuration files on the machines where each service is deployed. The service will uniformly pull its own configuration information from the configuration center.
  • When the configuration changes, the service does not need to be restarted to detect the configuration changes and apply the new configuration.
  • Expose the configuration information in the form of REST interface: post and curl access and refresh can be used...

13.1.4 Integrate configuration with GitHub

  • Since SpringCloud Config uses Git to store configuration files by default (there are other ways, such as supporting SVN and local files), but the most recommended one is Git, and it uses http/https access.

13.1.5 Official website

  • https://cloud.spring.io/spring-cloud-static/spring-cloud-config/2.2.1.RELEASE/reference/html/
    Insert image description here

13.2 Config server configuration and testing

13.2.1 Set up GitHub environment

  • Create a new Repository remote warehouse named springcloud-config on GitHub using your own account
    Insert image description here

  • Obtain the newly created git address from the previous step:https://github.com/123shuai-aa/springcloud-config.git
    Insert image description here

  • Create a new git local warehouse in the local hard disk directory and clone the remote warehouse locally.

    • Local warehouse location: E:\Git-bendiku\SpringCloud2020

    • Open a command line terminal
      Insert image description here

    • Enter the cloning command in the command line terminal:git clone https://github.com/123shuai-aa/springcloud-config.git

      • Because there is no content in this newly created remote library, a warning will be reported.
        Insert image description here
    • Effect:
      Insert image description here
      Insert image description here

  • Create 3 yml configuration files in the springcloud-config directory: the saving format must be UTF-8 (the default is)

    • Development environment: config-dev.yml

    • Production environment: config-pro.yml

    • Test environment: config-test.yml
      Insert image description here

  • config-dev.yml:

config:
  info: "master branch,springcloud-config/config-dev.yml version=1"
  • config-pro.yml:
config:
  info: "master branch,springcloud-config/config-prod.yml version=1"
  • config-test.yml:
config:
  info: "master branch,springcloud-config/config-test.yml version=1" 

  • Use the command to push these 3 files to the remote warehouse: (provided that you enter the command line terminal)
    • Check the local library status: git status, you can see 3 yml files
      Insert image description here

    • Add to the staging area: git add file name

      • git add config-dev.yml
      • git add config-pro.yml
      • git add config-test.yml
        Insert image description here
    • Submit the local library: git commit -m "Log information" file name (note the English symbols)

      • git commit -m “init yml” config-dev.yml
      • git commit -m “init yml” config-pro.yml
      • git commit -m “init yml” config-test.yml
      • Check the status again. After submission, it shows that there are no files to submit.
        Insert image description here
    • The remote library link address is too long and difficult to remember, so I gave the link an alias to make it more convenient to push and pull code in the future ( 语法:git remote add 别名 远程库链接地址)

      • View all current remote address aliases: git remote -v
      • The default alias is: origin, so there is no need to create another alias.
        Insert image description here
    • Push the local library branch to the remote warehouse:

      • Syntax: git push alias branch (the default branch name is the main branch: main)

      • git push origin main
        Insert image description here

      • A window will pop up, select 浏览器账号登录.
        Insert image description here

      • Enter your GitHub account password
        Insert image description here

      • Add to credential manager
        Insert image description here

      • Command line effect:
        Insert image description here

    • Refresh the browser's git page and find that the content on our main branch has been pushed to the remote repository created by GitHub.
      Insert image description here

13.2.2 Create configuration center module Congig Server

  • Create a new Module module cloud-config-center-3344,
    which is Cloud’s configuration center module cloudConfig Center.
    Insert image description here

1) Module name: cloud-config-center-3344

Insert image description here

2)POM

Insert image description here

<?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>cloud2020</artifactId>
        <groupId>com.angenin.springcloud</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>cloud-config-center-3344</artifactId>

    <properties>
        <maven.compiler.source>8</maven.compiler.source>
        <maven.compiler.target>8</maven.compiler.target>
    </properties>
    <dependencies>
        <!--config server-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-config-server</artifactId>
        </dependency>
        <!--eureka client(通过微服务名实现动态路由)-->
        <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>

3)YML

  • The premise is that git, GitHub environment password account commands and integration with idea have been done.
    Insert image description here
server:
  port: 3344


spring:
  application:
    name: cloud-config-center #注册进Eureka服务器的微服务名
  cloud:
    config:
      server:
        git:
          uri: https://github.com/123shuai-aa/springcloud-config.git  #git的仓库地址
          search-paths:   #搜索目录
            - springcloud-config
      label: main   #读取的分支


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


  • Corresponding configuration
    Insert image description here

4) Main startup class

Insert image description here

package com.angenin.springcloud;

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

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

5) windows: modify hosts file

  • Modify the hosts file under windows and add mapping
  • 127.0.0.1 config-3344.com
    Insert image description here

6) Test

  • Test whether the configuration content can be obtained from GitHub through the Config microservice
    • Start microservice 7001, 3344
    • http://config-3344.com:3344/main/config-dev.yml (can access the configuration file on GitHub, and successfully used SpringCloud Config to obtain configuration information through GitHub )
      Insert image description here
  • This part is now complete:
    Insert image description here

13.2.3 Configuring read rules

Official website:https://cloud.spring.io/spring-cloud-static/spring-cloud-config/2.2.1.RELEASE/reference/html/#_quick_start

  • That is: what format of address can be entered into the browser to read the configuration file in the GitHub warehouse. (5 types)
    Insert image description here

Explain 3 of them:

  • The first one: /{label}/{application}-{profile}.yml (branch name + file name, with a spacer between the file names, recommended, whichever writing method was tested above)
    • master branch
      • http://config-3344.com:3344/main/config-dev.yml
      • http://config-3344.com:3344/main/config-test.yml
      • http://config-3344.com:3344/main/config-pro.yml
    • dev branch
      • http://config-3344.com:3344/dev/config-dev.yml
      • http://config-3344.com:3344/dev/config-test.yml
      • http://config-3344.com:3344/dev/config-pro.yml
  • The second type: /{application}-{profile}.yml (only write the file name, there is a space between the file names)
    • http://config-3344.com:3344/config-dev.yml
    • http://config-3344.com:3344/config-test.yml
    • http://config-3344.com:3344/config-pro.yml
    • http://config-3344.com:3344/config-xxxx.yml (non-existent configuration), the result is:{}
  • The third type: /{application}/{profile}[/{label}] (the reverse operation of the first type, the result is in json format)
    • http://config-3344.com:3344/config/dev/main
      Insert image description here

    • http://config-3344.com:3344/config/test/main

    • http://config-3344.com:3344/config/test/dev

Glossary:

  • /{name}-{profiles}.yml
  • /{label}-{name}-{profiles}.yml
  • label: branch
  • name: service name
  • profiles: environment (dev/test/prod)

13.3 Config client configuration and testing

13.3.1 Create new cloud-config-client-3355

Insert image description here

13.3.2 POM

Insert image description here

<?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>cloud2020</artifactId>
        <groupId>com.angenin.springcloud</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>cloud-config-client-3355</artifactId>

    <properties>
        <maven.compiler.source>8</maven.compiler.source>
        <maven.compiler.target>8</maven.compiler.target>
    </properties>
    <dependencies>
        <!--config server-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-config</artifactId>
        </dependency>
        <!--eureka client(通过微服务名实现动态路由)-->
        <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>

13.3.3 bootstrap.yml

  • What is:

    • applicaiton.yml is a user-level resource configuration item
    • bootstrap.yml is system-level,优先级更加高
    • Spring Cloud will create a "Bootstrap Context" as the parent contextApplication Context of the Spring application . During initialization, it is responsible for loading configuration properties from external sources and parsing configurations. The two contexts share one obtained from the outside .Bootstrap ContextEnvironment
    • BootstrapProperties have high priority and by default they are not overridden by local configuration. Bootstrap contextand Application Contexthave different conventions, so a new bootstrap.ymlfile was added to ensure separation Bootstrap Contextfrom Application Contextconfiguration.
    • It is critical to change the application.yml file under the Client module to bootstrap.yml, because bootstrap.yml is loaded before application.yml. bootstrap.yml has higher priority than application.yml
    • Summary : It can be understood that the client A service carries 2 configuration files, one is bootstrap.yml for communication with the configuration center 3344, and the other is application.yml for its own use. In this way, the public one is read from the 3344 configuration center first. Configure it into your own bootstrap.yml, and then load your own application.yml. Together, it is the most complete client file.
      Insert image description here
  • content:
    Insert image description here

server:
  port: 3355

spring:
  application:
    name: config-client
  cloud:
    #Config客户端配置
    config:
      label: main #分支名称
      name: config #配置文件名称
      profile: dev #读取后缀名称   上述3个综合:master分支上config-dev.yml的配置文件被读取http://config-3344.com:3344/main/config-dev.yml
      uri: http://localhost:3344 #配置中心地址
      #过程:表示3355通过3344间接读取到配置的主分支下面的config-dev配置文件。

#服务注册到eureka地址
eureka:
  client:
    service-url:
      defaultZone: http://localhost:7001/eureka
  • illustrate:
    Insert image description here

13.3.4 Main startup

Insert image description here

package com.angenin.springcloud;

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

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

13.3.5 Business class

As mentioned earlier in 13.1.3 What can be done:

  • The distributed configuration center can expose configuration information in the form of a REST interface: post and curl access and refresh can be used...
  • Explanation: Since the configuration information is exposed, 3355 can read the message and content configuration of the 3344 configuration center through REST style.
    Insert image description here
package com.angenin.springcloud.controller;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class ConfigClientController {
    
    

    @Value("${config.info}")
    private String configInfo;

    @GetMapping("/configInfo")
    public String getConfigInfo() {
    
    
        return configInfo;
    }
}
  • @Value("${config.info}")What is read is the prefix of the configuration file in GitHub
    Insert image description here

13.3.6 Testing

  • Start the 7001 service registration center

  • Start the Config configuration center 3344 microservice and self-test

    • http://config-3344.com:3344/main/config-pro.yml
      Insert image description here

    • http://config-3344.com:3344/main/config-dev.yml
      Insert image description here

  • Start 3355 as Client to prepare for access

    • http://localhost:3355/configInfo
      Insert image description here
  • Conclusion: Successfully implemented client 3355 access to SpringCloud Config3344 to obtain configuration information through GitHub

13.3.7 Problems come at any time, the problem of dynamic refresh of distributed configuration

  • Linux operation and maintenance modifies the content of the configuration file on GitHub to make adjustments

    • Modify the config-dev.yml configuration and submit it to GitHub, such as adding a variable age or version number.
      Insert image description here
  • Refresh the 3344 browser page and find that the ConfigServer configuration center responded immediately
    Insert image description here

  • Refreshed 3355 and found that the ConfigClient client did not respond.
    Insert image description here

  • 3355 There is no change unless you restart or reload
    Insert image description here

  • Is it so difficult that every time operation and maintenance modify the configuration file, the client needs to be restarted? ? nightmare

13.4 Dynamic refresh of Config client (manual)

  • Purpose: Avoid restarting the client microservice every time the configuration is updated 3355

13.4.1 Dynamic refresh-modify 3355 module

1) POM introduces actuator monitoring

  • The graphical monitoring dependency has been added above, and the meaning itself can be monitored by others after it changes.
    Insert image description here
        <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>

2) Modify YML to expose the monitoring port

Insert image description here

server:
  port: 3355

spring:
  application:
    name: config-client
  cloud:
    #Config客户端配置
    config:
      label: main #分支名称
      name: config #配置文件名称
      profile: dev #读取后缀名称   上述3个综合:master分支上config-dev.yml的配置文件被读取http://config-3344.com:3344/main/config-dev.yml
      uri: http://localhost:3344 #配置中心地址
      #过程:表示3355通过3344间接读取到配置的主分支下面的config-dev配置文件。

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


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

3) @RefreshScope business class Controller modification

Insert image description here

package com.angenin.springcloud.controller;

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;

@RestController
@RefreshScope //实现刷新功能
public class ConfigClientController {
    
    

    @Value("${config.info}")
    private String configInfo;

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

4) Test: 3355 does not take effect

  • Restart 3355 configuration to take effect
  • Modify the GitHub content version again to 3
    Insert image description here
  • Self-test access 3344 found that the content has changed----effective
    • http://config-3344.com:3344/main/config-dev.yml
      Insert image description here
  • Visit 3355----The configuration has been modified but it still does not take effect.
    Insert image description here

5) Solve the problem: 3355 does not take effect

  • Operation and maintenance personnel need to send a Post request to refresh 3355

    • Must be a POST request
    • curl -X POST "http://localhost:3355/actuator/refresh"(command line window)
      Insert image description here
  • Visit 3355 again. Although 3355 has not been restarted at this time, it is found that the version number has changed, indicating that the configuration has taken effect.

    • Benefit: Avoid service restart
    • http://localhost:3355/configInfo
      Insert image description here

13.4.2 Think about any other questions?

  • If there are multiple microservice clients 3355/3366/3377, each microservice must perform a post request and refresh manually?

    • Solution: for loop, write a batch script.
  • Is it possible to broadcast a notification once and have it take effect everywhere? We want to automatically refresh on a large scale, please find a way

    • Question 1: For 100 machines, you only need to broadcast the notification once for all to take effect, and there is no need to make 100 post requests.
    • Question 2: There are 100 machines, 98 of which need to take effect, and the remaining 2 do not need to take effect and maintain the original version. This kind of precise notification and precise clearing.
  • Solution: For the above series of problems, batch notification and precise notification can be solved by using the SpringCloud Bus message bus.

14 SpringCloud Bus message bus

14.1 Overview

  • The deepening and expansion of the previous explanation, in one word

    • Distributed automatic refresh configuration function
    • Spring Cloud Bus can be used with Spring Cloud Config to achieve dynamic refresh of configuration.
  • what is

    • Spring Cloud Bus is a framework used to link distributed system nodes with lightweight messaging systems.它整合了Java的事件处理机制和消息中间件的功能。
    • Spring Clud Bus supports two message brokers: RabbitMQ and Kafka
      Insert image description here
  • What can you do

    • Spring Cloud Bus can manage and propagate messages between distributed systems, just like a distributed executor, which can be used to broadcast state changes, event push, etc., and can also be used as a communication channel between microservices.
      Insert image description here
  • Why is it called a bus?

    • what is bus
      • In a system with a microservice architecture, it is usually used 轻量级的消息代理to build one 共用的消息主题and connect all microservice instances in the system. Because 该主题中产生的消息会被所有实例监听和消费,所以称它为消息总线. Each instance on the bus can easily broadcast some messages that need to be known to other instances connected to the topic.
    • Fundamental
      • ConfigClient instances all listen to the same topic in MQ (the default is springCloudBus). When a service refreshes data, it will put this information into the Topic so that other services listening to the same Topic can be notified and then update their own configurations.
    • If you don’t understand the meaning of nouns such as theme, check:
      • 杨哥B站ActiveMQ课程:https://www.bilibili.com/video/av55976700?from=search&seid=15010075915728605208

14.2 RabbitMQ environment configuration

  • The course installs RabbitMQ in the windows environment. Here I take the installation of RabbitMQ in the linux environment as an example.
  • View the installation steps: https://blog.csdn.net/aa35434/article/details/126634371
  • Log in to the RabbitMQ backend management interface:http://192.168.10.140:15672/
    • Username: admin
    • Password: 123456
      Insert image description here
  • Effect:
    Insert image description here

14.3 SpringCloud Bus dynamically refreshes global broadcast

  • You must first have a good RabbitMQ environment

14.3.1 Use 3355 as a template to make another 3366

  • Demonstrate the broadcast effect, increase the complexity, and then use 3355 as a template to create a 3366

1) Create new cloud-config-client-3366

Insert image description here

1)POM

Insert image description here

<?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>cloud2020</artifactId>
        <groupId>com.angenin.springcloud</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>cloud-config-client-3366</artifactId>

    <properties>
        <maven.compiler.source>8</maven.compiler.source>
        <maven.compiler.target>8</maven.compiler.target>
    </properties>
    <dependencies>
        <!--config server-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-config</artifactId>
        </dependency>
        <!--eureka client(通过微服务名实现动态路由)-->
        <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)YML

Insert image description here

server:
  port: 3366

spring:
  application:
    name: config-client
  cloud:
    #Config客户端配置
    config:
      label: main #分支名称
      name: config #配置文件名称
      profile: dev #读取后缀名称   上述3个综合:master分支上config-dev.yml的配置文件被读取http://config-3344.com:3344/main/config-dev.yml
      uri: http://localhost:3344 #配置中心地址
      #过程:表示3366通过3344间接读取到配置的主分支下面的config-dev配置文件。

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


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

3) Main startup

Insert image description here

package com.angenin.springcloud;

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

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

4)controller

Insert image description here

package com.angenin.springcloud.controller;

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;

@RestController
@RefreshScope
public class ConfigClientController {
    
    
    
    @Value("${server.port}")
    private String serverPort;

    @Value("${config.info}")
    private String configInfo;

    @GetMapping("/configInfo")
    public String configInfo() {
    
    
        
        return "serverPort: "+serverPort+"\t\n\n configInfo: "+configInfo;
    }

}

14.3.2 Design thinking & technology selection

  • Use the message bus to trigger a client/bus/refresh and refresh the configuration of all clients

    • That is: trigger one of the clients 3355, and 3355 broadcasts the notification to 3366
      Insert image description here
  • Use the message bus to trigger the /bus/refresh endpoint of a server ConfigServer and refresh the configuration of all clients.

    • That is: trigger to configuration center 3344, and 3344 broadcast notification to 3355 and 3366
      Insert image description here
  • The architecture in Figure 2 is obviously more suitable. The reasons why Figure 1 is not suitable are as follows:

    • It breaks the single responsibility of microservices, because microservices themselves are business modules and should not be responsible for configuration refresh.
    • Destroying the parity of each node of the microservice.
    • There are certain limitations. For example, when a microservice is migrated, its network address often changes. At this time, if you want to automatically refresh, you will add more modifications.

14.3.3 Modify cloud-config-center-3344

服务端Add message bus support to the cloud-config-center-3344 configuration center :

  • POM
    Insert image description here
        <!-- 添加rabbitMQ的消息总线支持包 -->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-bus-amqp</artifactId>
        </dependency>
        <!--凡事要暴露监控刷新的操作,3344的pom文件一定要引入actuator依赖,之前已经添加过了-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-actuator</artifactId>
        </dependency>
  • YML

Insert image description here

server:
  port: 3344

spring:
  application:
    name: cloud-config-center #注册进Eureka服务器的微服务名
  cloud:
    config:
      server:
        git:
          uri: https://github.com/123shuai-aa/springcloud-config.git  #git的仓库地址
          search-paths:   #搜索目录
            - springcloud-config
      label: main   #读取的分支
  #rabbitmq相关配置
  rabbitmq:
    host: 192.168.10.140  #linux上的主机ip
    port: 5672       #15672是图形管理界面的端口,5672是client访问端口
    username: admin
    password: 123456

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


#rabbitmq相关配置,暴露bus刷新配置的端点
#凡事要暴露监控刷新的操作,3344的pom文件一定要引入actuator依赖
management:
  endpoints: #暴露bus刷新配置的端点
    web:
      exposure:
        include: 'bus-refresh'

14.3.4 Modify cloud-config-client-3355

客户端Add message bus support to cloud-config-client-3355

  • POM
    Insert image description here
        <!-- 添加rabbitMQ的消息总线支持包 -->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-bus-amqp</artifactId>
        </dependency>
        <!--凡事要暴露监控刷新的操作,3344的pom文件一定要引入actuator依赖,之前已经添加过了-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-actuator</artifactId>
        </dependency>
  • YML
    Insert image description here
server:
  port: 3355

spring:
  application:
    name: config-client
  cloud:
    #Config客户端配置
    config:
      label: main #分支名称
      name: config #配置文件名称
      profile: dev #读取后缀名称   上述3个综合:master分支上config-dev.yml的配置文件被读取http://config-3344.com:3344/main/config-dev.yml
      uri: http://localhost:3344 #配置中心地址
      #过程:表示3355通过3344间接读取到配置的主分支下面的config-dev配置文件。
  #rabbitmq相关配置
  rabbitmq:
    host: 192.168.10.140  #linux上的主机ip
    port: 5672       #15672是图形管理界面的端口,5672是client访问端口
    username: admin
    password: 123456


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


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

14.3.5 Modify cloud-config-client-3366

客户端Add message bus support to cloud-config-client-3366

  • POM
    Insert image description here
        <!-- 添加rabbitMQ的消息总线支持包 -->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-bus-amqp</artifactId>
        </dependency>
        <!--凡事要暴露监控刷新的操作,3344的pom文件一定要引入actuator依赖,之前已经添加过了-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-actuator</artifactId>
        </dependency>
  • YML
    Insert image description here
server:
  port: 3366

spring:
  application:
    name: config-client
  cloud:
    #Config客户端配置
    config:
      label: main #分支名称
      name: config #配置文件名称
      profile: dev #读取后缀名称   上述3个综合:master分支上config-dev.yml的配置文件被读取http://config-3344.com:3344/main/config-dev.yml
      uri: http://localhost:3344 #配置中心地址
      #过程:表示3366通过3344间接读取到配置的主分支下面的config-dev配置文件。
  #rabbitmq相关配置
  rabbitmq:
    host: 192.168.10.140  #linux上的主机ip
    port: 5672       #15672是图形管理界面的端口,5672是client访问端口
    username: admin
    password: 123456

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

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

14.3.6 Testing

  • Start 7001, 3344, 3355, 3366
    Insert image description here

  • Operation and Maintenance Engineer

    • Modify the configuration file on Github to increase the version number
      Insert image description here

    • Send POST request ( 刷新的是3344配置中心)

      • curl -X POST “http://localhost:3344/actuator/bus-refresh”
        Insert image description here

      • Send once, effective everywhere

  • Configuration center

    • http://config-3344.com:3344/main/config-dev.yml
      Insert image description here
  • client

    • http://localhost:3355/configInfo
      Insert image description here

    • http://localhost:3366/configInfo
      Insert image description here

    • Without restarting the service, I obtained the configuration information again and found that it had been refreshed.

  • Modify once, broadcast notification, take effect everywhere

  • Log in to the RabbitMQ backend management interface:

    • Concepts discussed before:
      Insert image description here
      Insert image description here

14.4 SpringCloud Bus dynamically refreshes fixed-point notifications

14.1 Business requirements

  • I don’t want to notify everyone, I just want to notify specific points.
    • Notify only 3355
    • No notification 3366
  • A simple sentence
    • Specify a specific instance to take effect instead of all
    • official:http://localhost:配置中心的端口号/actuator/bus-refresh/{destination}
    • The /bus/refresh request is no longer sent to a specific service instance, but to the config server and specifies the service or instance that needs to update the configuration through the destination parameter class.

14.2 Test cases

Case

  • Here we take refreshing the config-client running on port 3355 as an example.

    • Notify only 3355
    • No notification 3366
  • Modify GitHub configuration file
    Insert image description here

  • refresh:

    • curl -X POST "http://localhost:3344/actuator/bus-refresh/config-client:3355"
      Insert image description here
      • That is: use refresh 3344 to notify the specified service 3355

      • bus-refresh
        Insert image description here

      • config-client:3355:
        Insert image description here

  • Effect: 3344, 3355 refreshed, 3366 not refreshed. (No restart)
    http://config-3344.com:3344/main/config-dev.yml
    Insert image description here
    http://localhost:3355/configInfo
    Insert image description here
    http://localhost:3366/configInfo
    Insert image description here

14.3 Summary

  • Notification SummaryAll
    Insert image description here

15 SpringCloud Stream message driver

15.1 Message-driven overview

15.1.1 Why Stream was introduced

  • Scenes:

    • The current system can be divided into three parts, the front end----"java backend----"big data platform. Now the java backend uses RabbitMQ and the big data platform uses Kafka. In this way, switching, maintenance and development are implemented. It's more troublesome to get up.
      Insert image description here
  • Purpose:

    • Is there a new technology that is born that allows us to no longer pay attention to the details of specific MQ? We only need to use an adaptation binding method to automatically switch between various MQs for us-----SpringCloudStream
  • Commonly used message middleware:

    • ActiveMQ
    • RabbitMQ
    • RocketMQ
    • Kafka (commonly used for big data)

15.1.1 What is

  • What is SpringCloudStream

    • The official definition of Spring Cloud Stream is a framework for building message-driven microservices.
    • Applications interact with binder objects in Spring Cloud Stream through inputs or outputs.
    • Binding is done through our configuration, and the binder object of Spring Cloud Stream is responsible for interacting with the message middleware.
    • Therefore, we only need to figure out how to interact with Spring Cloud Stream to use the message-driven approach conveniently.
    • By using Spring Integration to connect the message broker middleware to achieve message event driving.
    • Spring Cloud Stream provides personalized automated configuration implementation for some vendors' message middleware products, referencing the three core concepts of publish-subscribe, consumer group, and partition.
  • 目前仅支持RabbitMQ、Kafka。

  • In one sentence: shield the differences in underlying message middleware, reduce switching costs, and unify the message programming model

  • Official website:

    • https://spring.io/projects/spring-cloud-stream#overview
      Insert image description here

      • Spring Cloud Stream is a framework for building highly scalable event-driven microservices connected to a shared messaging system. The framework provides a flexible programming model that is built on already established and familiar Spring idioms and best practices, Including the three core concepts of publish/subscribe, consumer groups and message partitioning that support persistence
    • Official documentation: https://cloud.spring.io/spring-cloud-static/spring-cloud-stream/3.0.1.RELEASE/reference/html/
      Insert image description here

    • Spring Cloud Stream Chinese Instruction Manual

      • https://m.wang1314.com/doc/webapp/topic/20971999.html
        Insert image description here

15.1.2 Design thinking

  • Standard MQ
    Insert image description here

    • 消息Message: Information content is transmitted between producers/consumers through media
    • Message Channel MessageChannel: Messages must go through a specific通道
    • How are messages in the message channel consumed? Who is responsible for sending and receiving processing?
      • The sub-interface SubscribableChannel of the message channel MessageChannel, subscribed by the MessageHandler message processor
  • Why use Cloud Stream

    • For example, we use RabbitMQ and Kafka. Due to the differences in the architecture of these two message middlewares, RabbitMQ has exchange, and Kafka has Topic and Partitions partitions. The differences in these middlewares have caused certain problems in our actual project development. If we use one of the two message queues and want to migrate to another message queue for subsequent business needs, it will undoubtedly be a disaster, because it is coupled with our system 一大堆东西都要重新推倒重新做. , at this time springcloud Stream provides us with a way to decouple.
      Insert image description here
    • Why can stream unify the underlying differences?
      • In the absence of the concept of a binder, when our SpringBoot application directly interacts with message middleware, due to the different original intentions of each message middleware, their implementation details will be quite different. Defining the binder as the middle layer perfectly implements 应用程序与消息中间件细节之间的隔离。the unified Channel channel exposed to the application, so that the application no longer needs to consider various message middleware implementations.
      • 通过定义绑定器Binder作为中间层,实现了应用程序与消息中间件细节之间的隔离。
        Insert image description here
    • Binder
      • In the absence of the concept of a binder, when our SpringBoot application needs to directly interact with message middleware, due to the different original intentions of each message middleware, their implementation details will be quite different. By defining the binder as the middle layer, it is perfectly implemented 应用程序与消息中间件细节之间的隔离. Stream's further encapsulation of message middleware can make the code layer unaware of the middleware, and even dynamically switch middleware (rabbitmq switches to kafka), making microservice development highly decoupled, and services can pay more attention to themselves. business process

      • 通过定义绑定器Binder作为中间层,实现了应用程序与消息中间件细节之间的隔离。

      • Binder can generate Binding, which is used to bind the producers and consumers of the message container. It has two types, INPUT and OUTPUT. INPUT corresponds to the consumer, and OUTPUT corresponds to the producer.Insert image description here

      • INPUT corresponds to the consumer

      • OUTPUT corresponds to the producer

  • The message communication method in Stream follows the publish-subscribe model

    • Topic topic broadcast
      • RabbitMQ is the Exchange switch
      • In Kakfa it is Topic

15.1.3 Spring Cloud Stream standard process routine

Insert image description here
Insert image description here

  • Binder
    • Very convenient connection middleware, shielding differences
  • Channel
    • Channel is an abstraction of Queue. In the message communication system, it is the medium for storage and forwarding. The queue is configured through Channel.
  • Source and Sink
    • It can be simply understood that the reference object is Spring Cloud Stream itself, publishing messages from Stream is output (producer), and receiving messages is input (consumer).

15.1.4 Coding API and common annotations

Insert image description here

composition illustrate
Middleware Middleware, currently only supports FRabbitMQ and Kafka
Binder Binder is the encapsulation between the application and the message middleware. Currently, the Binder of KafKa and RabbitMQ is implemented. Through the Binder, the middleware can be easily connected and the message type can be dynamically changed (corresponding to the topic of Kafka and the exchange of RabbitMQ). These are all This can be achieved through configuration files
@Input The annotation identifies the input channel through which received messages enter the application.
@Output Annotation identifies the output channel through which published messages will leave the application
@StreamListener Listening queue, used to receive messages from consumer queues
@EnableBinding Refers to the channel channel and exchange being bound together.

15.2 Case description

  • The RabbitMQ environment is OK
  • Create three new submodules in the project
    • cloud-stream-rabbitmq-provider8801, a module for sending messages as a producer
    • cloud-stream-rabbitmq-consumer8802, as a message receiving module
    • cloud-stream-rabbitmq-consumer8803 as a message receiving module

15.3 Message-driven producer

15.3.1 New Module: cloud-stream-rabbitmq-provider8801

Insert image description here

15.3.2 POM

Insert image description here

<?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>cloud2020</artifactId>
        <groupId>com.angenin.springcloud</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>cloud-stream-rabbitmq-provider8801</artifactId>

    <properties>
        <maven.compiler.source>8</maven.compiler.source>
        <maven.compiler.target>8</maven.compiler.target>
    </properties>

    <dependencies>
        <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.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-stream-rabbit</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>

15.3.3 YML

  • Exploding popularity does not affect use
    Insert image description here
server:
  port: 8801

spring:
  application:
    name: cloud-stream-provider
  cloud:
    stream:
      binders: # 在此处配置要绑定的rabbitmq的服务信息;
        defaultRabbit: # 表示定义的名称,用于于binding整合
          type: rabbit # 消息组件类型
          environment: # 设置rabbitmq的相关的环境配置
            spring:
              rabbitmq:
                host: 192.168.10.140
                port: 5672
                username: admin
                password: 123456
      bindings: # 服务的整合处理
        output: # 这个名字是一个通道的名称 生产者使用output,消费者使用input
          destination: studyExchange # 表示要使用的Exchange名称定义
          content-type: application/json # 设置消息类型,本次为json,文本则设置“text/plain”
          binder: defaultRabbit # 设置要绑定的消息服务的具体设置

eureka:
  client: # 客户端进行Eureka注册的配置
    service-url:
      defaultZone: http://localhost:7001/eureka
  instance:
    lease-renewal-interval-in-seconds: 2 # 设置心跳的时间间隔(默认是30秒)
    lease-expiration-duration-in-seconds: 5 # 如果现在超过了5秒的间隔(默认是90秒)
    instance-id: send-8801.com  # 在信息列表时显示主机名称
    prefer-ip-address: true     # 访问的路径变为IP地址

15.3.4 Main startup class StreamMQMain8801

Insert image description here

package com.angenin.springcloud;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

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

15.3.5 Business class

1) Send message interface

Insert image description here

package com.angenin.springcloud.service;


public interface IMessageProvider {
    
    
     String send() ;
}

2) Send message interface implementation class

Insert image description here

package com.angenin.springcloud.service.impl;

import com.angenin.springcloud.service.IMessageProvider;
import org.springframework.cloud.stream.annotation.EnableBinding;
import org.springframework.cloud.stream.messaging.Source;
import org.springframework.integration.support.MessageBuilder;
import org.springframework.messaging.MessageChannel;

import javax.annotation.Resource;
import java.util.UUID;

/**
 * 不再是以前crud操作数据库了:业务类上使用@service了,类里面注入dao层对象
 * 现在操作的是消息中间件Rabbitmq:使用的是 SpringCloud Stream消息驱动中的注解
 */
@EnableBinding(Source.class) // 可以理解为定义消息的推送管道
public class MessageProviderImpl implements IMessageProvider {
    
    

    @Resource
    private MessageChannel output; // 消息的发送管道

    @Override
    public String send() {
    
    

        //定义发送的消息
        String serial = UUID.randomUUID().toString();
        //使用绑定器将消息绑定起来
        output.send(MessageBuilder.withPayload(serial).build());
        System.out.println("***serial: "+serial);

        return null;
    }
}

3)Controller

Insert image description here

package com.angenin.springcloud.controller;

import com.angenin.springcloud.service.IMessageProvider;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.annotation.Resource;

@RestController
public class SendMessageController {
    
    
    @Resource
    private IMessageProvider messageProvider;

    //业务:每调用一次就发送一次流水号到Rabbitmq
    @GetMapping(value = "/sendMessage")
    public String sendMessage() {
    
    
        return messageProvider.send();
    }

}

15.3.6 Testing

  • Start 7001eureka
  • Start rabbitmq
  • Start 8801
    • Visit Rabbitmq management interface: http://192.168.10.140:15672
      Insert image description here
    • Corresponding configuration:
      Insert image description here
  • access
    • http://localhost:8801/sendMessage
    • The request occurred multiple times:
      Insert image description here
    • System.out... output information of the background log:
      Insert image description here
    • You can see the peak value of messages sent in the Rabbitmq management interface, indicating that the above integration configuration is successful.
      Insert image description here

15.4 Message-driven consumers

15.4.1 New Module: cloud-stream-rabbitmq-consumer8802

Insert image description here

15.4.2 POM

Insert image description here

<?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>cloud2020</artifactId>
        <groupId>com.angenin.springcloud</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>cloud-stream-rabbitmq-consumer8802</artifactId>

    <properties>
        <maven.compiler.source>8</maven.compiler.source>
        <maven.compiler.target>8</maven.compiler.target>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-stream-rabbit</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>

15.4.3 YML

  • The only difference from producer 8801 is that producer 8801 uses output and consumer 8802 uses input. Of course the names are different.
    Insert image description here
server:
  port: 8802

spring:
  application:
    name: cloud-stream-consumer
  cloud:
    stream:
      binders: # 在此处配置要绑定的rabbitmq的服务信息;
        defaultRabbit: # 表示定义的名称,用于于binding整合
          type: rabbit # 消息组件类型
          environment: # 设置rabbitmq的相关的环境配置
            spring:
              rabbitmq:
                host: 192.168.10.140
                port: 5672
                username: admin
                password: 123456
      bindings: # 服务的整合处理
        input: # 这个名字是一个通道的名称     生产者使用output,消费者使用input
          destination: studyExchange # 表示要使用的Exchange名称定义
          content-type: application/json # 设置消息类型,本次为对象json,如果是文本则设置“text/plain”
          binder: defaultRabbit # 设置要绑定的消息服务的具体设置

eureka:
  client: # 客户端进行Eureka注册的配置
    service-url:
      defaultZone: http://localhost:7001/eureka
  instance:
    lease-renewal-interval-in-seconds: 2 # 设置心跳的时间间隔(默认是30秒)
    lease-expiration-duration-in-seconds: 5 # 如果现在超过了5秒的间隔(默认是90秒)
    instance-id: receive-8802.com  # 在信息列表时显示主机名称
    prefer-ip-address: true     # 访问的路径变为IP地址


15.4.4 Main startup class StreamMQMain8802

Insert image description here

package com.angenin.springcloud;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

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

15.4.5 Business class

Insert image description here

package com.angenin.springcloud.controller;


import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.stream.annotation.EnableBinding;
import org.springframework.cloud.stream.annotation.StreamListener;
import org.springframework.cloud.stream.messaging.Sink;
import org.springframework.messaging.Message;
import org.springframework.stereotype.Component;

@Component
@EnableBinding(Sink.class) //指信道channel和exchange绑定在一起
public class ReceiveMessageListener {
    
    

    @Value("${server.port}")
    private String serverPort;


    @StreamListener(Sink.INPUT) //监听队列,用于消费者的队列的消息接收
    public void input(Message<String> message) {
    
    
        System.out.println("消费者1号,----->接受到的消息: "+message.getPayload()+"\t  port: "+serverPort);
    }


}

15.4.6 Test 8801 to send and 8802 to receive messages

  • Start 7001, Rabbitmq, 8801, 8802
  • http://localhost:8801/sendMessage
  • View the Rabbitmq management interface:
    Insert image description here
    Insert image description here
  • Message producer sends message
    Insert image description here
  • The message consumer receives the message
    Insert image description here

15.5 Group consumption and persistence

15.1 According to the 8802 consumer, clone a copy and run the 8803 consumer

  • cloud-stream-rabbitmq-consumer8803
    Insert image description here

  • Directory Structure:
    Insert image description here

  • 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>cloud2020</artifactId>
        <groupId>com.angenin.springcloud</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>cloud-stream-rabbitmq-consumer8803</artifactId>

    <properties>
        <maven.compiler.source>8</maven.compiler.source>
        <maven.compiler.target>8</maven.compiler.target>
    </properties>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-stream-rabbit</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>
  • YML
server:
  port: 8803

spring:
  application:
    name: cloud-stream-consumer
  cloud:
    stream:
      binders: # 在此处配置要绑定的rabbitmq的服务信息;
        defaultRabbit: # 表示定义的名称,用于于binding整合
          type: rabbit # 消息组件类型
          environment: # 设置rabbitmq的相关的环境配置
            spring:
              rabbitmq:
                host: 192.168.10.140
                port: 5672
                username: admin
                password: 123456
      bindings: # 服务的整合处理
        input: # 这个名字是一个通道的名称     生产者使用output,消费者使用input
          destination: studyExchange # 表示要使用的Exchange名称定义
          content-type: application/json # 设置消息类型,本次为对象json,如果是文本则设置“text/plain”
          binder: defaultRabbit # 设置要绑定的消息服务的具体设置

eureka:
  client: # 客户端进行Eureka注册的配置
    service-url:
      defaultZone: http://localhost:7001/eureka
  instance:
    lease-renewal-interval-in-seconds: 2 # 设置心跳的时间间隔(默认是30秒)
    lease-expiration-duration-in-seconds: 5 # 如果现在超过了5秒的间隔(默认是90秒)
    instance-id: receive-8803.com  # 在信息列表时显示主机名称
    prefer-ip-address: true     # 访问的路径变为IP地址


  • Main startup class
package com.angenin.springcloud;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

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

  • Business class
package com.angenin.springcloud.controller;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.stream.annotation.EnableBinding;
import org.springframework.cloud.stream.annotation.StreamListener;
import org.springframework.cloud.stream.messaging.Sink;
import org.springframework.messaging.Message;
import org.springframework.stereotype.Component;

@Component
@EnableBinding(Sink.class)
public class ReceiveMessageListener
{
    
    
    @Value("${server.port}")
    private String serverPort;

    @StreamListener(Sink.INPUT)
    public void input(Message message)
    {
    
    
        System.out.println("消费者2号,------->接收到的消息:" + message.getPayload()+"\t port: "+serverPort);
    }
}

15.2 Startup

  • Start RabbitMQ
  • Start 7001 service registration
  • Start 8801 message production
  • Start 8802 message consumption
  • Start 8803 message consumption

15.3 There are two problems after running

  • There is a problem of repeated consumption
  • Message persistence issues

15.4 Consumption

Currently, I have received both 8802/8803 at the same time. There is a problem of repeated consumption.

  • http://localhost:8801/sendMessage: Producer 8801 sent a message and found that both consumers 8802 and 8803 received the message.
    Insert image description here

  • How to solve

    • 分组和持久化属性group
  • Production actual cases
    Insert image description here

  • By default, 8802 and 8803 are different groups and can be consumed repeatedly.
    Insert image description here
    Insert image description here

15.5 Grouping

principle

  • Placing microservice applications in the same group can ensure that messages will only be consumed once by one of the applications.
  • 不同的组是可以消费的,同一个组内会发生竞争关系,只有其中一个可以消费。

8802/8803 have become different groups, and the two groups are different.

  • group: atguiguA、atguiguB

  • 8802 Modify YML:group: atguiguA
    Insert image description here

  • 8803 Modify YML:group: atguiguB
    Insert image description here

  • Restart 8802, 8803 takes effect

  • We configure it ourselves

    • In order to achieve high availability and load balancing, distributed microservice applications actually deploy multiple instances. In this example, Yang Ge started two consumer microservices (8802/8803). In most cases, when the producer sends a message to a specific microservice We only hope to be consumed once. According to the example above where we start two applications, although they belong to the same application, the message is consumed twice. To solve this problem, the concept of is provided in Spring Cloud Stream 消费组.
      Insert image description here
      Insert image description here
  • Conclusion: Or repeat consumption

    • Producer sends message, clicks 3 times
      Insert image description here

    • Producer Console 8801
      Insert image description here

    • Consumer Console 8802
      Insert image description here

    • Consumer Console 8803
      Insert image description here

8802/8803 become the same group, the two groups are the same

  • group: atguiguA

  • 8802 Modify YML: atguiguA
    Insert image description here

  • 8803 Modify YML: atguiguA
    Insert image description here

  • Restart takes effect:

  • We configure it ourselves:

    • Looking at the Rabbitmq management interface, we found two groups: atguiguA and atguiguB. atguiguB is the previous history record and has nothing to do with it.

    • There are two consumers 8802 and 8803 in atguigu group A. At this time, a message is sent to group A and there is competition every time. Only one instance in the same group is allowed to get it, thus avoiding repeated consumption.
      Insert image description here
      Insert image description here

    • Click on atguiguA: you can see that there are 2 consumers inside
      Insert image description here
      Insert image description here

    • Click atguiguB: The number of consumers is 0
      Insert image description here
      Insert image description here

  • Conclusion: Among multiple microservice instances in the same group, only one will get it at a time

    • Producer sends message, clicks 2 times
      Insert image description here

    • Producer Console 8801
      Insert image description here

    • Consumer Console 8802
      Insert image description here

    • Consumer Console 8803
      Insert image description here

  • 8802/8803 implements polling grouping. Each time there is only one consumer,
    the message sent by the 8801 module can only be received by one of 8802 or 8803, thus avoiding repeated consumption.

15.6 Endurance

  • Through the above, the problem of repeated consumption is solved, and then look at persistence

  • Stop 8802/8803 and remove the group of 8802: atguiguA
    Insert image description here

    • The group group of 8803: atguiguA has not been removed.
      Insert image description here
  • 8801 first sends 4 messages to rabbitmq:http://localhost:8801/sendMessage
    Insert image description here
    Insert image description here

  • Start 8802 first, there is no group attribute configuration, and no message is printed in the background.
    Insert image description here

  • Start 8803 again, configure group attributes, and messages on MQ are printed in the background.
    Insert image description here

  • Summary: The consumer is down due to various problems. At this time, the producer continues to send messages, and then restarts the consumer. If the consumer 配置了分组consumes messages that have not been consumed in Rabbitmq, if the consumer 没有配置分组consumes 不会messages that have not been consumed due to fault problems, message, which results in message loss.

16 SpringCloud Sleuth distributed request link tracking

16.1 Overview

16.1 Why does this technology appear?

  • Why did this technology appear? What problems need to be solved?
    • In the microservice framework, a request initiated by the client will be called by multiple different service nodes in the back-end system to collaboratively produce the final request result. Each previous request will form a complex distributed service call chain. A high delay or error in any link in the link will cause the entire request to eventually fail.
    • So in our microservice architecture, as more and more links are called, we need to know: how many steps we have taken from service A, how much time each step takes, and how many steps we have taken after completing a link call. How many microservices have been used and which nodes have been passed.
    • Distributed request link tracking for very large systems. It needs to be used when there are extremely many microservice modules, such as 80 calling 8001, and 8001 calling 8002, thus forming a link. If a link in the link fails, we can use Sleuth to track the link. Find the faulty link.
      Insert image description here
      Insert image description here

15.2 What is

  • https://github.com/spring-cloud/spring-cloud-sleuth
    Insert image description here

  • Spring Cloud Sleuth provides a complete service tracking solution

  • Provides tracking solutions in distributed systems and is compatible with zipkin

  • sleuth is responsible for tracking, while zipkin is responsible for display

16.3 Resolution

  • After sending the link data, it will display the calling effect in the form of a web page.
    Insert image description here

16.2 Steps to set up link monitoring

16.2.1 zipkin

1) Download

  • Starting from version F, SpringCloud no longer needs to build the Zipkin Server server by itself. You only need to call the jar package.

  • https://repo1.maven.org/maven2/io/zipkin/zipkin-server/
    Insert image description here

  • zipkin-server-2.24.3-exec.jar
    Insert image description here

2) Run jar

  • cmd to enter
  • Input: java -jar zipkin-server-2.24.3-exec.jar
    Insert image description here

3) Run the console

  • http://localhost:9411/zipkin/
    Insert image description here

  • the term

    • Complete calling link
      • Represents a request link. A link is uniquely identified by Trace Id, and Span identifies the request information initiated. Each span is associated by parent id.
        Insert image description here
    • What is the picture above
      • A link is uniquely identified by Trace Id, Span identifies the request information initiated, and each span is associated by parent id.
        Insert image description here
      • The dependencies of the entire link are as follows
        Insert image description here
    • Glossary
      • Trace: A Span collection similar to a tree structure, representing a call link with a unique identifier
      • span: indicates the source of the calling link. The popular understanding of span is a request for information.

16.2.2 Service Provider

1)cloud-provider-payment8001

  • Taking into account the trouble of creating a new project, we will no longer create a new project here and use the previous one directly.

2)POM

  • Add dependencies
    Insert image description here
   <!--包含了sleuth+zipkin-->
   <dependency>
       <groupId>org.springframework.cloud</groupId>
       <artifactId>spring-cloud-starter-zipkin</artifactId>
   </dependency>

3)YML

Insert image description here

spring:
  application:
    #微服务名称,将此服务项目入住到注册中心,那么就需要给此项目取个名字
    name: cloud-payment-service
  zipkin:
    base-url: http://localhost:9411  # zipkin 地址
  sleuth:
    sampler:
      probability: 1  #采样率值介于0到1之间,1则表示全部采集(一般不为1,不然高并发性能会有影响)

4) Business class PaymentController

  • Add a test method to the control layer written before
    Insert image description here
    @GetMapping("/payment/zipkin")
    public String paymentZipkin() {
    
    
        
        return "hi ,i'am paymentzipkin server fall back,welcome to atguigu,O(∩_∩)O哈哈~";
    }

16.2.3 Service consumer (caller)

1)cloud-consumer-order80

  • Taking into account the trouble of creating a new project, we will no longer create a new project here and use the previous one directly.

2)POM

  • Also introduce this dependency
    Insert image description here
	<!-- 引入sleuth + zipkin -->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-zipkin</artifactId>
        </dependency>

3)YML

Insert image description here

spring:
  application:
    name: cloud-order-service
  zipkin:
    base-url: http://localhost:9411  # zipkin 地址
  sleuth:
    sampler:
      # 采样率值 介于0-1之间 ,1表示全部采集
      probability: 1  

4) Business class OrderController

  • Add a test method to the control layer written before
    Insert image description here
    // ====================> zipkin+sleuth
    @GetMapping("/consumer/payment/zipkin")
    public String paymentZipkin() {
    
    

        String result = restTemplate.getForObject("http://localhost:8001"+"/payment/zipkin/", String.class);
        return result;
    }

16.2.4 Testing

  • Start eureka7001/8001/80 in sequence

  • 80 calls 8001 multiple times for testing:http://localhost/consumer/payment/zipkin
    Insert image description here

  • Open the browser and visit: http://localhost:9411

    • The following interface will appear
      Insert image description here

    • Find traces
      Insert image description here

    • View dependencies
      Insert image description here

Guess you like

Origin blog.csdn.net/aa35434/article/details/132467982