Spring Cloud and microservices

Posted by ss32 on Thu, 09 Sep 2021 20:58:19 +0200


Spring Cloud is one of the projects under spring. It integrates some popular technologies and realizes functions such as configuration management, service discovery, intelligent routing, load balancing, fuse, control bus, cluster status and so on. The main components involved include:

  • Eureka: Registration Center
  • Zuul: service gateway (now commonly used gateway)
  • Ribbon: load balancing
  • Feign: service call
  • Hystrix: fuse

1. Eureka registry

Before introducing Eureka registry, we first establish two models: user service and consumer. The user operates the consumer module to call the provider module to access the data.

What is Eureka

Eureka is based on REST (representative state transfer) services, mainly supported by AWS cloud services, providing service discovery and realizing load balancing and failover. We call this service Eureka service. Eureka provides a Java client component, Eureka Client, to facilitate interaction with the server. The client has built-in simple load balancing based on round robin. In Netflix, Eureka is packaged with a more complex load balancing scheme to achieve high availability, including weighted load balancing based on traffic, resource utilization and request return status.

Infrastructure

Three core roles in Eureka architecture:

  • Service registry: Eureka's server-side application, which provides service registration and discovery functions.
  • Service provider: the application that provides services can be a Spring Boot application or any other technology implementation, as long as it provides REST style services.
  • Service consumer: the consumer application obtains the service list from the registry, so as to know the information of each service provider and where to call the service provider.

Eureka schematic diagram

  • Eureka: it's a service registry (which can be a cluster) that exposes its own address
  • Provider: register your information with Eureka after startup (address, what services to provide)
  • Consumer: subscribe to the service from Eureka. Eureka will send the address list of all providers of the corresponding service to consumers and update it regularly
  • Heartbeat (renewal): the provider periodically updates its status to Eureka through HTTP

Write EurekaServer

Add eureka dependency

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

Write startup class

//Eureka service when declaring the current application
@EnableEurekaServer
@SpringBootApplication
public class EurekaServerApplication {
public static void main(String[] args) {
SpringApplication.run(EurekaServerApplication.class, args);
}
}

Write configuration yml profile

server:
  #If the jvm parameter exists, use the parameter port, which is the same as ${port:10086}
  port: 10086
  ng:
  application:
    name: eureka-server
eureka:
  client:
    service-url:
      #The address of the Eureka service. For clustering, you need to specify other Eureka addresses ${defaultZone:http://127.0.0.1:10086/eureka }
      defaultZone: http://127.0.0.1:10086/eureka
      #Don't register yourself
    register-with-eureka: false
    #No pull service
    fetch-registry: false

Service registration

Add dependency on service provider

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

Enable Eureka client function on startup class

Enable Eureka client functionality by adding @ EnableDiscoveryClient

@SpringBootApplication
@MapperScan("com.lxs.user.mapper")
@EnableDiscoveryClient //Enable Eureka client discovery
public class UserApplication {
public static void main(String[] args) {
SpringApplication.run(UserApplication.class, args);
}
}

Write configuration

server:
  port: ${port:9999}
spring:
  datasource:
    driver-class-name: com.mysql.jdbc.Driver
    url: jdbc:mysql://127.0.0.1:3306/springcloud?useSSL=false
    username: root
    password: 123456
  application:
    name: user-service
mybatis:
  type-aliases-package: com.yh.user.pojo
eureka:
  client:
    service-url:
      defaultZone: http://127.0.0.1:10086/eureka
        #http://127.0.0.1:10086/eureka,http://127.0.0.1:10087/eureka

Do not specify register with Eureka and fetch registry, because the default is true

Service discovery

Add dependency on client

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

Add the annotation to start Eureka client discovery in the startup class

@SpringBootApplication
@EnableDiscoveryClient // Open Eureka client
public class UserConsumerDemoApplication {
@Bean
public RestTemplate restTemplate() {
return new RestTemplate(new OkHTTP3ClientHTTPRequestFactory());
}
public static void main(String[] args) {
SpringApplication.run(UserConsumerDemoApplication.class, args);
}
}

Write configuration

spring:
  application:
    name: consumer-demo
eureka:
  client:
    service-url:
      defaultZone: http://127.0.0.1:10086/eureka

Modify the code and use the method of DiscoveryClient class to obtain the service instance according to the service name

@RestController
@RequestMapping("/consumer")
public class ConsumerController {
@Autowired
private RestTemplate restTemplate;
@GetMapping("{id}")
public String queryById(@PathVariable Long id) {
String url = "http://user-service/user/" + id;
return restTemplate.getForObject(url, String.class);
}

}

Failure elimination and self-protection

Service offline

When the service is shut down normally, it will trigger a REST request for service offline to Eureka Server and tell the service registry: "I want to offline
It's too late. After receiving the request, the service center will set the service to offline status

Failure elimination

Sometimes our service may not work normally due to memory overflow or network failure, and the service registry has not received the request for "service offline". Compared with the "service renewal" operation of the service provider, the service registry will create a scheduled task at startup. By default, every other period of time
(60 seconds by default) eliminate the services in the current list that have timed out (90 seconds by default) without renewal. This operation is called invalidation elimination. You can modify it through the eureka.server.eviction-interval-timer-in-ms parameter. The unit is milliseconds.

Self protection

When a service is shut down, you will see a warning on the Eureka panel:

This triggered Eureka's self-protection mechanism. When a service fails to renew its heartbeat on time, Eureka will count whether the proportion of service instances with heartbeat failure in the last 15 minutes exceeds 85%. When Eureka server node loses too many clients in a short time (network partition failure may occur). In the production environment, due to network delay and other reasons, the proportion of heartbeat failure instances is likely to exceed the standard, but it is not appropriate to remove the service from the list at this time, because the service may not be down. Eureka will protect the registration information of the current instance and will not eliminate it. This is very effective in the production environment and ensures that most
The data service is still available.

Turn off self-protection mode

eureka:
  server:
    enable-self-preservation: false # Turn off self-protection mode (on by default)
    eviction-interval-timer-in-ms: 1000 # Scan the interval between failed services (60*1000ms by default)

Load balancing Ribbon

What is Ribbon


Get the address list from Eureka server according to the server name, and Ribbon will get an address from the address list according to the load balancing algorithm for access. The algorithm is divided into polling and random. The default is polling

Turn on load balancing

Because Ribbon has been integrated in Eureka, we do not need to introduce new dependencies. Modify code directly:
Add @ LoadBalanced annotation on the configuration method of RestTemplate:

@Bean
@LoadBalanced
public RestTemplate restTemplate() {
return new RestTemplate();
}

Modify load balancing algorithm

user-service:
  ribbon:
    NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RandomRule

Hystrix

Hystix is an open source delay and fault tolerance Library of Netflix, which is used to isolate access to remote services and prevent cascading failures.

Avalanche problem

In microservices, the calling relationship between services is complex. A request may need to call multiple microservice interfaces to implement, which will form a very complex calling link. If an exception occurs in a microservice at this time, the request is blocked, and the user request will not be responded to, then the thread of tomcat will not be released. Therefore, more and more user requests will arrive, and more and more threads will be blocked. The number of threads and concurrency supported by the server is limited, and the requests are blocked all the time, which will lead to the depletion of server resources, resulting in the unavailability of all other services, forming an avalanche effect.

This is like an automobile production line that needs different parts to produce different cars. If a part cannot be used for various reasons, the whole car will not be able to assemble and fall into the state of waiting for parts. Assembly can not continue until the parts are in place. At this time, if there are many models that need this
The whole factory will be in a waiting state, resulting in the paralysis of all production. The scope of a part is expanding.

The methods used by Hystrix to solve the avalanche problem mainly include:
Thread isolation
service degradation

Thread isolation & service degradation

Thread isolation:
Hystrix allocates a small thread pool for each dependent service call. If the thread pool is full, the call will be rejected immediately. By default, queuing is not used to speed up the failure determination time.
The user's request will no longer directly access the service, but through the idle thread in the thread pool. If the thread pool is full or the request times out, it will be degraded.

Service degradation:
Service degradation can give priority to ensuring core services. When the user's request fails, it will not be blocked, nor will it wait endlessly or see the system crash. At least one execution result can be seen (for example, return a friendly prompt). Although service degradation will lead to request failure, it will not lead to blocking. At most, it will affect the resources in the thread pool corresponding to the dependent service and will not respond to other services.

Conditions that trigger the degradation of the Hystrix service:

  • Thread pool full
  • request timeout

Write Hystrix service downgrade

  1. Introduce dependency
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
</dependency>

2. Open the fuse
Add a comment on the startup class consumer ConsumerApplication: @ enablercircuitbreaker

@SpringBootApplication
@EnableDiscoveryClient
@EnableCircuitBreaker
public class ConsumerApplication {
// ...
}

Note: @ SpringCloudApplication=
@SpringBootApplication
@EnableDiscoveryClient
@EnableCircuitBreaker

@SpringCloudApplication
public class ConsumerApplication {
// ...
}

3. Write degradation logic
When the call of the target service fails, use the hystrix command to complete a quick failure and give the user a friendly prompt. The degradation processing logic in case of failure needs to be written in advance.

@RestController
@RequestMapping("/consumer")
@Slf4j
public class ConsumerController {
@Autowired
private RestTemplate restTemplate;
@Autowired
private DiscoveryClient discoveryClient;
@GetMapping("{id}")
@HystrixCommand(fallbackMethod = "queryByIdFallback")
public String queryById(@PathVariable Long id) {
String url = "http://user-service/user/" + id;
return restTemplate.getForObject(url, String.class);
}
public String queryByIdFallback(Long id) {
log.error("Failed to query user information. id: {}", id);
return "Sorry, the Internet is too crowded!";
}
}

Note: the degraded logic method must guarantee the same parameter list and return value declaration as the normal logic method.
@HystrixCommand(fallbackMethod = "queryByIdFallBack"): a method used to declare a degraded logic
4. Timeout setting

The default timeout for Hystrix is 1s

hystrix:
  command:
    default:
      execution:
        isolation:
          thread:
            timeoutInMilliseconds=2000:

Service fuse

Fusing principle

  • In service fusing, the fuse used is also called Circuit Breaker, and its English word is: Circuit Breaker fusing mechanism is similar to the circuit fusing principle used at home; In case of short circuit, the circuit can be fused immediately to avoid disaster. After the application service is fused in the distributed system; The service caller can judge which services are slow or have a large number of timeouts, and can actively fuse these services to prevent the whole system from being dragged down.
  • The service fuse mechanism of Hystrix can realize elastic fault tolerance; When the service request situation improves, it can be reconnected automatically. The subsequent requests are directly rejected by means of circuit breaking. After a period of time (5 seconds by default), some requests are allowed to pass. If the call is successful, the circuit breaker will return to the closed state. Otherwise, it will continue to open and reject the requested service.

Fuse schematic diagram


The state machine has three states:

  • Closed: closed state (circuit breaker closed), all requests are accessed normally.
  • Open: open status (circuit breaker open), all requests will be degraded. Hystrix will count the request conditions. When the percentage of failed requests reaches the threshold within a certain time, it will trigger the fuse and the circuit breaker will be fully opened. The default threshold of failure ratio is 50%, and the number of requests is no less than 20.
  • HalfOpen: half open state, not permanent. After the circuit breaker is opened, it will enter sleep time (5S by default). Then the circuit breaker will automatically enter the half open state. At this time, some requests will be released to pass. If these requests are healthy, the circuit breaker will be closed, otherwise it will continue to be open and sleep timing will be carried out again

Change fusing strategy

hystrix:
  command:
    default:
      execution:
        isolation:
          thread:
            timeoutInMilliseconds= 5000:
      circuitBreaker:
        errorThresholdPercentage: 50 # Trigger fusing error proportional threshold, default 50%
        sleepWindowInMilliseconds: 10000 # Sleep duration after fusing, the default value is 5 seconds
        requestVolumeThreshold: 10 # The minimum number of fuse trigger requests. The default value is 20

Feign

Feign can hide the Rest request and disguise it as a Controller similar to spring MVC. You don't have to splice URLs, splice parameters and so on. Let feign do everything.

1. Feign dependency

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

2.Feign's client

@FeignClient("user-service")
public interface UserClient {
//http://user-service/user/123
@GetMapping("/user/{id}")
User queryById(@PathVariable("id") Long id);
}
  • First, this is an interface. Feign will help us generate implementation classes through dynamic proxies. This is very similar to Mybatis's mapper

  • @FeignClient, declare that this is a Feign client, and specify the service name through the value attribute

  • The methods defined in the interface completely adopt the annotations of spring MVC. Feign will help us generate URL s according to the annotations and access the results

  • @/ user in GetMapping, please don't forget; Because Feign needs to splice accessible addresses

Write a new controller class ConsumerFeignController and use UserClient to access:

@RestController
@RequestMapping("/cf")
public class ConsumerFeignController {
@Autowired
private UserClient userClient;
@GetMapping("/{id}")
public User queryById(@PathVariable Long id){
return userClient.queryById(id);
}
}

3. Enable Feign function

On the ConsumerApplication startup class, add an annotation to enable the Feign function

@SpringCloudApplication
@EnableFeignClients //Enable feign function
public class ConsumerApplication {
public static void main(String[] args) {
SpringApplication.run(ConsumerApplication.class, args);
}
@Bean
@LoadBalanced
public RestTemplate restTemplate() {
return new RestTemplate();
}
}

load balancing

Ribbon dependency and automatic configuration have been integrated in Feign itself, so there is no need to introduce additional dependencies or register RestTemplate objects. The ribbon built in Fegin sets the request timeout length by default, which is 1000ms. You can modify the timeout length through manual configuration:

ribbon:
  ReadTimeout: 2000 # Read timeout length
  ConnectTimeout: 1000 # Timeout for establishing link

Or specify for a specific service

user-service
  ribbon:
    ReadTimeout: 2000 # Read timeout length
    ConnectTimeout: 1000 # Timeout for establishing link

Because the ribbon has an internal retry mechanism, once it times out, it will automatically re initiate the request. Requests can be organized through configuration

ribbon:
  ConnectTimeout: 1000 # Connection timeout duration
  ReadTimeout: 2000 # Data communication timeout duration
  MaxAutoRetries: 0 # Number of retries for the current server
  MaxAutoRetriesNextServer: 0 # How many service retries
  OkToRetryOnAllOperations: false # Do you want to retry all request methods

Hystrix support

Feign integrates with Hystrix by default and is off by default. It needs to be enabled through the following parameters:

feign:
  hystrix:
    enabled: true # Turn on the fusing function of Feign

First, define a class to implement the UserFeignClient just written as the fallback processing class

@FeignClient("user-service")
public interface UserClient {
//http://user-service/user/123
@GetMapping("/user/{id}")
User queryById(@PathVariable("id") Long id);
}
@Component
public class UserClientFallback implements UserClient {
@Override
public User queryById(Long id) {
User user = new User();
user.setId(id);
user.setName("User exception");
return user;
}
}

Then, in UserClient, specify the implementation class just written

@FeignClient(value = "user-service", fallback = UserFeignClientFallback.class)
public interface UserFeignClient {
@GetMapping("/user/{id}")
User queryUserById(@PathVariable("id") Long id);
}

Request compression

Spring Cloud Feign supports GZIP compression of requests and responses to reduce performance loss during communication. The compression function of request and response can be enabled through the following parameters. You can also set the requested data type and the minimum size to trigger compression:

feign:
  compression:
    request:
      enabled: true # Turn on request compression
      mime-types: text/html,application/xml,application/json # Set compressed data type
      min-request-size: 2048 # Sets the lower limit of the size that triggers compression
    response:
      enabled: true # Turn on response compression

log level

You can set the log level through logging.level.lxs.xx=debug. However, this will not work for the Fegin client. Because a new instance of Fegin.Logger will be created when the client modified by the @ FeignClient annotation is proxy. We need to specify the level of this log.

1. Set the log level under com.xxx package in the configuration file of consumer demo to add the following configuration for debug:

logging:
  level:
    com.xxx: debug

2. Write a configuration class and define the log level

@Configuration
public class FeignConfig {
@Bean
Logger.Level feignLoggerLevel(){
//Record the details of all requests and responses, including header information, request body and metadata
return Logger.Level.FULL;
}
}

The Level specified here is FULL. Feign supports four levels:

  • NONE: no log information is recorded, which is the default value.
  • BASIC: only record the requested method, URL, response status code and execution time
  • HEADERS: on the basis of BASIC, additional header information of request and response is recorded
  • FULL: records the details of all requests and responses, including header information, request body and metadata

3. Specify the configuration class in the @ FeignClient annotation on the UserClient interface class

@FeignClient(value = "user-service", fallback = UserClientFallback.class, configuration =
FeignConfig.class)

Spring Cloud Gateway

  • Spring Cloud Gateway is a gateway service developed on the spring official website based on Spring 5.0, Spring Boot 2.0, Project Reactor and other technologies.
  • Spring Cloud Gateway provides the basic functions of the gateway based on the Filter chain: security, monitoring / embedding point, current limiting, etc.
  • Spring Cloud Gateway provides a simple, effective and unified API routing management method for microservice architecture.
  • Spring Cloud Gateway is a solution to replace Netflix Zuul.

The core of the Spring Cloud Gateway component is a series of filters that can forward (route) requests sent by clients to corresponding microservices. Spring Cloud Gateway is a firewall and agent added at the forefront of the whole microservice to hide the IP port information of microservice nodes, so as to strengthen security protection. Spring Cloud Gateway itself is also a micro service, which needs to be registered with Eureka service registry.

The core functions of the gateway are: filtering and routing

Architecture after Gateway is added


Unlike Feign, Feign is a mutual call request between different microservices. Whether the Gateway is a request from the client (PC or mobile terminal) or an internal call of the service. All requests for services can pass through the Gateway, and then the Gateway can realize authentication, dynamic routing and so on. The Gateway is the unified entrance to our services.

spring cloud config

Create a warehouse in the code cloud and add the yml configuration file

Back to Idea, create a config module

Add config dependency

<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-config-server</artifactId>
</dependency>
</dependencies>
@SpringBootApplication
@EnableConfigServer //Open configuration service
public class ConfigServerApplication {
public static void main(String[] args) {
SpringApplication.run(ConfigServerApplication.class, args);
}
}

Modify application.yml

server:
  port: 12000
spring:
  application:
    name: config-server
  cloud:
    config:
      server:
        git:
          #Code cloud warehouse address
          uri: https://gitee.com/yin-hang1531/my-config

eureka:
  client:
    service-url:
      defaultZone: http://127.0.0.1:10086/eureka

Successfully accessed configuration information

Deleting the configuration in the service module application.yml, creating bootstrap.yml will add configuration information to call the public configuration information from the remote warehouse (later configuration information to nacos Management).

spring:
  cloud:
    config:
# Be consistent with the application of the configuration file in the warehouse
    name: user
# Keep consistent with the profile of the configuration file in the warehouse
    profile: dev
# It should be the same as the version (Branch) to which the configuration file in the warehouse belongs
    label: master
    discovery:
# Use configuration center
      enabled: true
# Configuration center service name
      service-id: config-server
eureka:
  client:
    service-url:
      defaultZone: http://127.0.0.1:10086/eureka

The bootstrap.yml file is also the default configuration file for Spring Boot, and its loading time is earlier than that of application.yml.
Although application.yml and bootstrap.yml are the default configuration files of Spring Boot, their positioning is different. bootstrap.yml
It can be understood as some parameter configurations at the system level. These parameters generally will not change. application.yml can be used to define the application level
If used in conjunction with spring cloud config, the files defined in application.yml can be dynamically replaced.
To sum up, the bootstrap.yml file is equivalent to the boot file when the project is started, and the content is relatively fixed. application.yml file is some general configuration parameters of microservices, which change frequently.

spring cloud bus

****Spring Cloud Bus connects distributed nodes with lightweight message broker, which can be used to change broadcast configuration files or monitor and manage services. That is, the message bus can monitor microservices and realize the communication between applications. The optional message brokers for Spring Cloud Bus are RabbitMQ and Kafka. (after the configuration information changes, it will be automatically refreshed and handed over to nacos)
After using Bus:

Topics: Spring Cloud gateway