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
复杂分布式体系结构中的应用程序有数十个依赖关系,每个依赖关系在某些时候将不可避免地失败。
-
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 . .
- 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
-
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
10.1.5 Hystrix official announcement, suspension of updates for maintenance
- https://github.com/Netflix/Hystrix
- 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
- 7001 restored to stand-alone version
10.3.1 Build
1) Create new cloud-provider-hystrix-payment8001
- Create an 8001 with blown hystrix
2)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-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
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
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.
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
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
-
Each call takes 3 seconds: http://localhost:8001/payment/hystrix/timeout/4
-
-
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.
Save:
http://localhost:8001/payment/hystrix/timeout/4
-
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.
- Both are spinning in circles on their own
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
-
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-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
server:
port: 80
eureka:
client:
register-with-eureka: false
service-url:
defaultZone: http://eureka7001.com:7001/eureka/
- main boot
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)
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
-
Visit ok: http://localhost/consumer/payment/hystrix/ok/4 (the speed is also very fast)
-
-
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
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.
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
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
-
http://localhost:8001/payment/hystrix/timeout/1
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
@HystrixCommand
internal attributes. - YML
feign:
hystrix:
enabled: true
- main boot
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
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.
-
Test: start 7001, 8001, 80
-
http://localhost/consumer/payment/hystrix/timeout/1
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哈哈~
-
controller configuration
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
- Effect:
http://localhost/consumer/payment/hystrix/timeout/1
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
- 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
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)
- PaymentFeignClientService interface
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
- Deliberately shutting down microservices 8001
- 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
- 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
2) Practical operation
修改
cloud-provider-hystrix-payment8001
- PaymentService
//=========服务熔断
/**
* 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
- PaymentController
//=========服务熔断
@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
-
错误:http://localhost:8001/payment/circuit/-31
-
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
- At this time, even if you enter a positive ID, the error page is still returned.
- After entering the positive ID multiple times, the accuracy rate increases and the error rate decreases, and the correct page is slowly restored.
3) Principle (short summary)
-
God's conclusion
-
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.
- fuse open
-
Official website circuit breaker flow chart
-
Official website steps
-
Under what circumstances does the circuit breaker start to function?
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
-
Hystrix workflow
-
Official website legend
-
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
2)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-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
server:
port: 9001
4) Main startup class
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
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.
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:
- The monitored producer service generally needs to add two dependencies for graphical monitoring to take effect.
- 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.
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
- Fill in the monitoring address: http://localhost:8001/hystrix.stream
-
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)
-
http://localhost:8001/payment/circuit/-31 Enter a negative ID and return to the error page)
-
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
-
Monitoring results, failed
-
-
-
How to see it?
-
7 colors
-
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
故障实例和高压力实例
.
- 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
-
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.
- Curve: used to record the relative changes in flow within 2 minutes, through which the upward and downward trends in flow can be observed.
-
Whole picture description
-
Whole picture description 2
-
-
Only when you understand one can you understand the complex one
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.
12. Gateway new generation gateway
12.1 Overview Introduction
-
Why do you need a gateway?
-
Technical implementation of gateway
-
Official website
-
Previous generation zuul 1.X: https://github.com/Netflix/zuul/wiki
-
当前gateway:https://cloud.spring.io/spring-cloud-static/spring-cloud-gateway/2.2.1.RELEASE/reference/html/
-
-
what is
-
Overview
-
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
-
-
What can you do
- reverse proxy
- Authentication
- flow control
- fuse
- Log monitoring
- …
-
Where is the gateway in microservice architecture?
-
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.
-
SpringCloud Gateway has the following features:
-
The difference between SpringCloud Gateway and Zuul
-
-
Zuul1.x model
-
GateWay model
-
What is WebFlux
-
https://docs.spring.io/spring/docs/current/spring-framework-reference/web-reactive.html#webflux-new-framework
-
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),如果请求与断言相匹配则进行路由
- The reference is Java8's java.util.function.Predicate
-
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:
- 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.
- The web request locates the real service node through some matching conditions. And perform some refined control before and after this forwarding process.
12.3 Gateway workflow
-
Official website summary
-
Core logic:
路由转发+执行过滤器链
12.4 Getting Started Configuration
12.4.1 Gateway9527 setup
1) Create a new Module
- cloud-gateway-gateway9527
2)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-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
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
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
-
lib: Test custom load balancing rules
-
-
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
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.
-
Access instructions
-
Before adding gateway: http://localhost:8001/payment/get/4
-
After adding the gateway: http://localhost:9527/payment/get/4, it can also be accessed correctly.
-
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
-
Baidu’s domestic news website requires an external network: http://news.baidu.com/guonei (it seems that this website no longer exists)
-
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.
-
test:
- Restart project 9527
- http://localhost:9527/guonei, returns the same news page
-
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.
-
Architecture
-
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
-
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.
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
12.6 Use of Predicate
12.6.1 What is
- Start our gateway9527
- That is: multiple matching rules under predicates
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
-
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
- After Route Predicate
- Before Route Predicate
- Between Route Predicate
- Cookie Route Predicate
- Header Route Predicate
- Host Route Predicate
- Method Route Predicate
- Path Route Predicate
- Query Route Predicate
12.6.4 Demonstrate commonly used Route Predicate
1)After Route Predicate
Official website case:
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
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.
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.
http://localhost:9527/payment/lb
- 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/
lb
- 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.
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.
# 在指定的时间之前生效
- 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
# 在指定的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:
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.
#第二个路由
- 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.
- At this time, cookies are not satisfied and therefore do not take effect.
-
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.
- If adding curl returns Chinese garbled characters, check the blog at this address for a solution.
https://blog.csdn.net/leedee/article/details/82685636
- 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.
5)Header Route Predicate
- Official website case
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)
#第二个路由
- 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"
6)Host Route Predicate
- The official website document
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
- 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”
7)Method Route Predicate
- Official documentation
Test it yourself:
- yml
- Method=GET #根据请求方式进行匹配
- Enter the address: write nothing, just get request
8)Path Route Predicate
- Official documentation
test
- It is the previous path matching. For details, see 12.4 Getting Started Configuration----7), 8)
9)Query Route Predicate
- Official documentation
Test it yourself
- yml
- Query=username, \d+ # 要有参数名username并且值还要是整数才能路由
- 正确:http://localhost:9527/payment/lb?username=31
- 错误:http://localhost:9527/payment/lb?username=-31
10) Summary
- Total yml
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:
12.7.1 What is
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
-
GlobalFilter: global
-
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
12.7.3 Commonly used GatewayFilter (single filter)
- YML
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
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
-
Correct: http://localhost:9527/payment/lb?uname=z3 (8001 8002 switches back and forth)
-
mistake
- There is no parameter uname: http://localhost:9527/payment/lb, forwarding cannot be used normally.
- There is no parameter uname: http://localhost:9527/payment/lb, forwarding cannot be used normally.
-
9527 console log
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中心化的外部配置
.
- SpringCloud Config provides centralized external configuration support for microservices in a microservice architecture, and a configuration server is
-
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.
- SpringCloud Config is divided into
-
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.
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/
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
-
Obtain the newly created git address from the previous step:
https://github.com/123shuai-aa/springcloud-config.git
-
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
-
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.
- Because there is no content in this newly created remote library, a warning will be reported.
-
Effect:
-
-
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
-
-
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
-
Add to the staging area: git add file name
- git add config-dev.yml
- git add config-pro.yml
- git add config-test.yml
-
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.
-
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.
-
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
-
A window will pop up, select
浏览器账号登录
.
-
Enter your GitHub account password
-
Add to credential manager
-
Command line effect:
-
-
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.
-
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.
1) Module name: cloud-config-center-3344
2)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-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.
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
4) Main startup class
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
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 )
- This part is now complete:
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)
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
- master branch
- 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
-
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
13.3.2 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-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 context
Application 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 Context
Environment
Bootstrap
Properties have high priority and by default they are not overridden by local configuration.Bootstrap context
andApplication Context
have different conventions, so a newbootstrap.yml
file was added to ensure separationBootstrap Context
fromApplication Context
configuration.- 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.
-
content:
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:
13.3.4 Main startup
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.
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
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
-
http://config-3344.com:3344/main/config-dev.yml
-
-
Start 3355 as Client to prepare for access
- http://localhost:3355/configInfo
- http://localhost:3355/configInfo
-
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.
- Modify the config-dev.yml configuration and submit it to GitHub, such as adding a variable age or version number.
-
Refresh the 3344 browser page and find that the ConfigServer configuration center responded immediately
-
Refreshed 3355 and found that the ConfigClient client did not respond.
-
3355 There is no change unless you restart or reload
-
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.
<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
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
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
- Self-test access 3344 found that the content has changed----effective
- http://config-3344.com:3344/main/config-dev.yml
- http://config-3344.com:3344/main/config-dev.yml
- Visit 3355----The configuration has been modified but it still does not take effect.
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)
-
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
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
- Spring Cloud Bus is a framework used to link distributed system nodes with lightweight messaging systems.
-
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.
- 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.
-
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.
- In a system with a microservice architecture, it is usually used
- 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
- what is bus
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
- Effect:
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
1)POM
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>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
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
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
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
- That is: trigger one of the clients 3355, and 3355 broadcasts the notification to 3366
-
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
- That is: trigger to configuration center 3344, and 3344 broadcast notification to 3355 and 3366
-
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
<!-- 添加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
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
<!-- 添加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
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
<!-- 添加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
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
-
Operation and Maintenance Engineer
-
Modify the configuration file on Github to increase the version number
-
Send POST request (
刷新的是3344配置中心
)-
curl -X POST “http://localhost:3344/actuator/bus-refresh”
-
Send once, effective everywhere
-
-
-
Configuration center
- http://config-3344.com:3344/main/config-dev.yml
- http://config-3344.com:3344/main/config-dev.yml
-
client
-
http://localhost:3355/configInfo
-
http://localhost:3366/configInfo
-
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:
- Concepts discussed before:
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
-
refresh:
curl -X POST "http://localhost:3344/actuator/bus-refresh/config-client:3355"
-
That is: use refresh 3344 to notify the specified service 3355
-
bus-refresh
-
config-client:3355:
-
-
Effect: 3344, 3355 refreshed, 3366 not refreshed. (No restart)
http://config-3344.com:3344/main/config-dev.yml
http://localhost:3355/configInfo
http://localhost:3366/configInfo
14.3 Summary
- Notification SummaryAll
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.
- 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.
-
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
- 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/
-
Spring Cloud Stream Chinese Instruction Manual
- https://m.wang1314.com/doc/webapp/topic/20971999.html
- https://m.wang1314.com/doc/webapp/topic/20971999.html
-
15.1.2 Design thinking
-
Standard MQ
消息
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.
- 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作为中间层,实现了应用程序与消息中间件细节之间的隔离。
- 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
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.
-
INPUT corresponds to the consumer
-
OUTPUT corresponds to the producer
-
- 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
-
The message communication method in Stream follows the publish-subscribe model
- Topic topic broadcast
- RabbitMQ is the Exchange switch
- In Kakfa it is Topic
- Topic topic broadcast
15.1.3 Spring Cloud Stream standard process routine
- 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
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
15.3.2 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-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
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
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
package com.angenin.springcloud.service;
public interface IMessageProvider {
String send() ;
}
2) Send message interface implementation class
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
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
- Corresponding configuration:
- Visit Rabbitmq management interface: http://192.168.10.140:15672
- access
- http://localhost:8801/sendMessage
- The request occurred multiple times:
- System.out... output information of the background log:
- You can see the peak value of messages sent in the Rabbitmq management interface, indicating that the above integration configuration is successful.
15.4 Message-driven consumers
15.4.1 New Module: cloud-stream-rabbitmq-consumer8802
15.4.2 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-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.
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
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
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:
- Message producer sends message
- The message consumer receives the message
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
-
Directory Structure:
-
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.
-
How to solve
分组和持久化属性group
-
Production actual cases
-
By default, 8802 and 8803 are different groups and can be consumed repeatedly.
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
-
8803 Modify YML:
group: atguiguB
-
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
消费组
.
- 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
-
Conclusion: Or repeat consumption
-
Producer sends message, clicks 3 times
-
Producer Console 8801
-
Consumer Console 8802
-
Consumer Console 8803
-
8802/8803 become the same group, the two groups are the same
-
group: atguiguA
-
8802 Modify YML: atguiguA
-
8803 Modify YML: atguiguA
-
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.
-
Click on atguiguA: you can see that there are 2 consumers inside
-
Click atguiguB: The number of consumers is 0
-
-
Conclusion: Among multiple microservice instances in the same group, only one will get it at a time
-
Producer sends message, clicks 2 times
-
Producer Console 8801
-
Consumer Console 8802
-
Consumer Console 8803
-
-
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
- The group group of 8803: atguiguA has not been removed.
- The group group of 8803: atguiguA has not been removed.
-
8801 first sends 4 messages to rabbitmq:
http://localhost:8801/sendMessage
-
Start 8802 first, there is no group attribute configuration, and no message is printed in the background.
-
Start 8803 again, configure group attributes, and messages on MQ are printed in the background.
-
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.
15.2 What is
-
https://github.com/spring-cloud/spring-cloud-sleuth
-
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.
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/
-
zipkin-server-2.24.3-exec.jar
2) Run jar
- cmd to enter
- Input: java -jar zipkin-server-2.24.3-exec.jar
3) Run the console
-
http://localhost:9411/zipkin/
-
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.
- 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.
- 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.
- The dependencies of the entire link are as follows
- A link is uniquely identified by Trace Id, Span identifies the request information initiated, and each span is associated by parent id.
- 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.
- Complete calling link
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
<!--包含了sleuth+zipkin-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-zipkin</artifactId>
</dependency>
3)YML
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
@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
<!-- 引入sleuth + zipkin -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-zipkin</artifactId>
</dependency>
3)YML
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
// ====================> 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
-
Open the browser and visit: http://localhost:9411
-
The following interface will appear
-
Find traces
-
View dependencies
-