SpringCloud - learning and recording of Eureka feign ribbon, hystrix zuul and other key components

SpringCloud - learning and recording of Eureka feign ribbon, hystrix zuul and other key components

Foreword: This article is the feeling and summary after learning the crazy spring cloud tutorial recently. Since the understanding of the spring cloud system is still in the initial stage, it is inevitable to be biased in wording and understanding. Please criticize and correct it!

1, Overview

Spring cloud is a set of microservice solutions, which are available in spring cloud dependencies Hoxton SR8 version and spring boot dependencies 2.3.4 With the release version, this set of solutions provides Eureka registry, ribbon + resttemplet or Feign load balancing scheme, Hystrix fusing, Hystrix+Feign degradation, HystrixDashboard service monitoring and Zuul routing gateway.

However, in the subsequent versions of spring cloud dependencies (such as 2020.x.x Series), the services provided above have changed greatly, especially when Ribbon stops updating (IRule interface is removed) and Hystrix+Feign changes make it difficult for us to use custom load balancing algorithm and degrade services. I am still learning and exploring these parts, It will be recorded after basic understanding and mastery, so this article is still based on the earlier version of spring cloud dependencies.

(P.S. although the Hoxton version is still supported, according to the changes of NetFIix in the new version, maybe this solution will be abandoned sooner or later)

2, Composition of the project

1. Project integration method

The spring cloud project consists of a total POM XML is responsible for managing all dependencies that may be used in the project. In this way, the role of management is to facilitate the unification of versions. When the sub module uses the same dependency, it is not necessary to specify the dependent version, because these versions are already in the total POM XML.

Total POM in a project In XML, we can use tags to manage all dependencies. At this time, all dependencies will not be imported, but the sub module will inherit the total POM XML and related dependencies will be imported into the corresponding sub module.

For example, shown below is the total POM xml

<?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">
    <modelVersion>4.0.0</modelVersion>

    <groupId>org.example</groupId>
    <artifactId>springclouddemo</artifactId>
    <version>1.0-SNAPSHOT</version>
    <modules>
        <module>springcloud-api</module>
        <module>springcloud-provider-dept-8001</module>
        <module>springcloud-consumer-dept-80</module>
        <module>springcloud-eureka-7001</module>
        <module>springcloud-eureka-7002</module>
        <module>springcloud-eureka-7003</module>
        <module>springcloud-provider-dept-8002</module>
        <module>springcloud-provider-dept-8003</module>
        <module>springcloud-consumer-dept-feign</module>
        <module>springcloud-provider-dept-hystrix-8001</module>
        <module>springcloud-consumer-hystrix-dashboard</module>
        <module>springcloud-zuul</module>
        <module>springcloud-config-server-3344</module>
        <module>spingcloud-config-client-3355</module>
        <module>springcloud-config-eureka-7001</module>
        <module>springcloud-config-dept-8001</module>
    </modules>

    <properties>
        <maven.compiler.source>8</maven.compiler.source>
        <maven.compiler.target>8</maven.compiler.target>
        <junit.version>4.12</junit.version>
        <druid.version>1.1.10</druid.version>
        <lombok.version>1.18.22</lombok.version>
    </properties>

    <packaging>pom</packaging>

    <dependencyManagement>
        <dependencies>
            <!-- https://mvnrepository.com/artifact/org.springframework.cloud/spring-cloud-dependencies -->
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-dependencies</artifactId>
                <version>Hoxton.SR8</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>

            <!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-dependencies -->
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-dependencies</artifactId>
                <version>2.3.4.RELEASE</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>

            <dependency>
                <groupId>mysql</groupId>
                <artifactId>mysql-connector-java</artifactId>
                <version>8.0.23</version>
            </dependency>

            <!-- https://mvnrepository.com/artifact/com.alibaba/druid -->
            <dependency>
                <groupId>com.alibaba</groupId>
                <artifactId>druid</artifactId>
                <version>${druid.version}</version>
            </dependency>

            <dependency>
                <groupId>org.mybatis.spring.boot</groupId>
                <artifactId>mybatis-spring-boot-starter</artifactId>
                <version>2.2.1</version>
            </dependency>

            <!--Log test-->
            <dependency>
                <groupId>junit</groupId>
                <artifactId>junit</artifactId>
                <version>${junit.version}</version>
            </dependency>

            <dependency>
                <groupId>log4j</groupId>
                <artifactId>log4j</artifactId>
                <version>1.2.12</version>
            </dependency>

            <dependency>
                <groupId>ch.qos.logback</groupId>
                <artifactId>logback-core</artifactId>
                <version>1.2.7</version>
            </dependency>

            <dependency>
                <groupId>org.projectlombok</groupId>
                <artifactId>lombok</artifactId>
                <version>${lombok.version}</version>
            </dependency>
        </dependencies>
    </dependencyManagement>
</project>

The following shows a child module's response to the parent POM Inheritance and use of XML

<?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>springclouddemo</artifactId>
        <groupId>org.example</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>springcloud-provider-dept-hystrix-8001</artifactId>

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

        <!-- https://mvnrepository.com/artifact/org.springframework.cloud/spring-cloud-starter-netflix-hystrix -->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
            <version>2.2.9.RELEASE</version>
        </dependency>

        <!--EUREKA provide-->
        <!-- https://mvnrepository.com/artifact/org.springframework.cloud/spring-cloud-starter-netflix-eureka-client -->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
            <version>3.0.5</version>
        </dependency>
<!--        actuator Improve monitoring information-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-actuator</artifactId>
        </dependency>

<!--Used to get entity classes-->
        <dependency>
            <groupId>org.example</groupId>
            <artifactId>springcloud-api</artifactId>
            <version>1.0-SNAPSHOT</version>
        </dependency>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
        </dependency>
        <!-- https://mvnrepository.com/artifact/com.alibaba/druid -->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid</artifactId>
        </dependency>

        <dependency>
            <groupId>ch.qos.logback</groupId>
            <artifactId>logback-core</artifactId>
        </dependency>

        <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
        </dependency>

        <!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-test -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-test</artifactId>
        </dependency>

        <!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-web -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-jetty</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
        </dependency>
    </dependencies>

</project>

Of course, if we are not in the total POM If some dependency management is declared in XML, the version number of these dependencies should be indicated when importing in the sub module.

2. Various parts of the project

A spring cloud project consists of four parts: service registry, service API, service provider and service consumer.

  • The registration center is responsible for the registration and discovery of service providers, which is convenient for service consumers to use services, and can effectively manage the services provided. In short, a registry is a cluster. When one cluster fails, it can still ensure the operation of other clusters and prevent service collapse. For example, a registration center is like an e-commerce service platform. Service providers, businesses display goods on the platform, service consumers and buyers browse goods on the platform.
  • The service API provides the entity classes required by the service (and the parts usually corresponding to the database tables). At the same time, it also provides some general configurations. For example, when Feign is used for load balancing, consumers obtain the service by calling the API interface. This configuration method avoids repeatedly configuring the interface in the consumer module to achieve the purpose of unified use.
  • Service providers are part of traditional projects. For example, in SpringBoot, our projects generally include Dao layer, service layer and Controller layer, so as to carry out database interaction, service logic processing and service interface explicit work. In SpringCloud, service providers continue to undertake these work. The difference is, Service providers may have multiple sub modules that provide the same service, which is caused to facilitate load balancing.
  • The purpose of the service consumer is to hide the url of the service provider, and the explicit is the url and port of the consumer. When the current end calls the service, it only needs to call the consumer to get the background data. On the consumer side, different methods can be used to achieve load balancing, which will be carried out later.

The following section will introduce several core functions provided by spring cloud and show them in combination with the project source code.

3, Registration and discovery - Eureka

Eureka, as a registry, provides a platform to help service providers make services explicit.

First, we need to build a subproject, springcloud-eureka-7001, and build it in POM Import dependency in XML

    <dependencies>
        <!-- https://mvnrepository.com/artifact/org.springframework.cloud/spring-cloud-starter-eureka-server -->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-eureka-server</artifactId>
            <version>1.4.4.RELEASE</version>
        </dependency>


        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
        </dependency>

    </dependencies>

Note: spring boot devtools mainly provides hot reload services.

Next, we need to configure this sub module basically, and specify the port, eureka server instance name, whether to be discovered by the registry, whether we are the registry, and the address to be accessed by the registered component in the application

server:
  port: 7001

#Euraka configuration
eureka:
  instance:
#    hostname: localhost #Euraka server instance name
    hostname: eureka7001.com #Euraka server instance name cluster
  client:
    register-with-eureka: false #Indicates whether to register yourself with the eureka registry
    fetch-registry: false #If false, it means you are the registry
    service-url: #Indicates the address of the interaction with the registration center, that is, the monitoring page. The component to be registered accesses this address
#      defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/
      # If you access eureka alone, write in the browser http://localhost:7001/ Just
      defaultZone: http://eureka7002.com:7002/eureka/, http://eureka7003.com:7003/eureka/

Finally, we only need to indicate that this sub module is a eureka server in the startup class, and use the annotation @ EnableEurekaServer

@SpringBootApplication
@EnableEurekaServer //EnableEurekaServer server starts the class and accepts others to register
public class EurekaServer_7001 {
    public static void main(String[] args) {
        SpringApplication.run(EurekaServer_7001.class, args);
    }
}

Of course, as mentioned above, in order to ensure that the registry does not collapse unexpectedly, we can build multiple clusters to make multiple registries call each other to prevent the collapse of the registry from making the whole service unavailable.

Therefore, we create the subprojects springcloud-eureka-7002 and springcloud-eureka-7003, and configure the application yml

7002:

server:
  port: 7002

#Euraka configuration
eureka:
  instance:
    hostname: eureka7002.com #Euraka server instance name
  client:
    register-with-eureka: false #Indicates whether to register yourself with the eureka registry
    fetch-registry: false #If false, it means you are the registry
    service-url: #Indicates the address of the interaction with the registration center, that is, the monitoring page. The component to be registered accesses this address
#      defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/
      # If you access eureka alone, write in the browser http://localhost:7001/ Just
      defaultZone: http://eureka7001.com:7001/eureka/, http://eureka7003.com:7003/eureka/

7003:

server:
  port: 7003

#Euraka configuration
eureka:
  instance:
    hostname: eureka7003.com #Euraka server instance name
  client:
    register-with-eureka: false #Indicates whether to register yourself with the eureka registry
    fetch-registry: false #If false, it means you are the registry
    service-url: #Indicates the address of the interaction with the registration center, that is, the monitoring page. The component to be registered accesses this address
#      defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/
      # If you access eureka alone, write in the browser http://localhost:7001/ Just
      defaultZone: http://eureka7001.com:7001/eureka/, http://eureka7002.com:7002/eureka/

4, Load balancing

In spring cloud, the meaning of load balancing is from the perspective of the client. Select a Service on the server through the load balancing strategy, so as to avoid large congestion caused by access to a Service. Of course, the premise is that the Service provider provides several services with the same functions, as we said earlier.

The following will introduce load balancing from two different methods: Ribbon + resttemplet and Feign.

5, Load balancing - Ribbon + resttemplet

Spring Cloud Ribbon is a client load balancing tool based on HTTP and TCP, which is implemented based on Netflix Ribbon. Through the encapsulation of Spring Cloud, we can easily automatically convert service-oriented REST template requests into client-side load balanced service calls.

RestTemplate is a client provided by Spring to access Rest services. RestTemplate provides a variety of convenient methods to access remote Http services, which can greatly improve the writing efficiency of the client.

The first mock exam is to create a sub module called springcloud-consumer-dept-80, which will use 80 ports by default, in pom. Add dependencies to XML. The directory composition and code are as follows

    <dependencies>

        <!--ribbon-->
        <!-- https://mvnrepository.com/artifact/org.springframework.cloud/spring-cloud-starter-netflix-ribbon -->
<!--        <dependency>-->
<!--            <groupId>org.springframework.cloud</groupId>-->
<!--            <artifactId>spring-cloud-starter-netflix-ribbon</artifactId>-->
<!--            <version>2.2.9.RELEASE</version>-->
<!--        </dependency>-->

        <!--EUREKA Provide that it already contains ribbon,So you don't have to add it separately-->
        <!-- https://mvnrepository.com/artifact/org.springframework.cloud/spring-cloud-starter-netflix-eureka-client -->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-eureka</artifactId>
            <version>1.4.7.RELEASE</version>
        </dependency>

        <dependency>
            <groupId>org.example</groupId>
            <artifactId>springcloud-api</artifactId>
            <version>1.0-SNAPSHOT</version>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
        </dependency>
    </dependencies>

Next, we create a Config class to configure RestTemplate and the implementation of load balancing. The @ LoadBalanced annotation implements load balancing

@Configuration
public class ConfigBean {

    @Bean
    @LoadBalanced //Configure load balancing implementation
    public RestTemplate getRestTemplate() {
        return new RestTemplate();
    }

}

Then we implement the Controller layer. Here we use a constant to specify the required service. The link of the service does not need to specify the url address, but only the service name as a part of the link. For example:“ http://springcloud-provider-dept ”

For each service, we need to call the service given by the service provider using the getForObject or postForObject methods in the RestTemplate. for example

    @Autowired
    private RestTemplate restTemplate;
   
	//When using ribbon, the address here should be a variable and accessed through the service name
    private static final String REST_URL_PREFIX = "http://springcloud-provider-dept";

	@RequestMapping("/consumer/dept/add")
    public boolean add(Dept dept) {
        return restTemplate.postForObject(REST_URL_PREFIX+"/dept/add",dept,Boolean.class);
    }

REST_URL_PREFIX+"/dept/add" is the name of the service interface

dept is the incoming parameter

Boolean.class is the return value type

This also corresponds to public Boolean add (Dept dept Dept).

Give the complete code:

@RestController
public class DeptConsumerController {
    @Autowired
    private RestTemplate restTemplate;

//    private static final String REST_URL_PREFIX = "http://localhost:8001";

    //When using ribbon, the address here should be a variable and accessed through the service name
    private static final String REST_URL_PREFIX = "http://springcloud-provider-dept";

    @RequestMapping("/consumer/dept/add")
    public boolean add(Dept dept) {
        return restTemplate.postForObject(REST_URL_PREFIX+"/dept/add",dept,Boolean.class);
    }

    @RequestMapping("/consumer/dept/get/{id}")
    public Dept get(@PathVariable("id") Long id) {
        return restTemplate.getForObject(REST_URL_PREFIX+"/dept/get/"+id, Dept.class);
    }

    @RequestMapping("/consumer/dept/list")
    public List<Dept> list() {
        return restTemplate.getForObject(REST_URL_PREFIX+"/dept/list", List.class);
    }
}

Finally, implement the main startup class:

// After the integration of ribbon and Eureka, the client can call directly without caring about the IP address and port number
@SpringBootApplication
@EnableEurekaClient
public class DeptConsumer_80 {
    public static void main(String[] args) {
        SpringApplication.run(DeptConsumer_80.class, args);
    }
}

6, Load balancing - Feign

In fact, the essence of using Feign to realize load balancing is the same as using Ribbon + RestTemplate, because Feign itself also inherits Ribbon. The difference is that Feign is more in line with the idea of interface oriented programming. Through encapsulation, we can obtain services in the way of interface when calling, rather than through the constant in a RestTemplate.

First, we create the project and import POM Dependencies in XML:

    <dependencies>

        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-feign</artifactId>
            <version>1.4.7.RELEASE</version>
        </dependency>

        <!--ribbon You don't have to add it here-->
        <!-- https://mvnrepository.com/artifact/org.springframework.cloud/spring-cloud-starter-ribbon -->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-ribbon</artifactId>
            <version>1.4.7.RELEASE</version>
        </dependency>


        <!--EUREKA It has been provided ribbon,So you don't have to add it separately-->
        <!-- https://mvnrepository.com/artifact/org.springframework.cloud/spring-cloud-starter-netflix-eureka-client -->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-eureka</artifactId>
            <version>1.4.7.RELEASE</version>
        </dependency>

        <dependency>
            <groupId>org.example</groupId>
            <artifactId>springcloud-api</artifactId>
            <version>1.0-SNAPSHOT</version>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
        </dependency>
    </dependencies>

As mentioned earlier, load balancing is that the client selects the service interface of the server according to a certain strategy, so the load balancing interface must be implemented on the client. Therefore, we need to implement an interface in the springcloud API sub module, which implements a unified load balancing scheme and helps clients call the services of service providers. We create a service layer in the springcloud API sub module and create a ClientService interface:

@FeignClient(value = "springcloud-provider-dept")
public interface DeptClientService {
    @GetMapping("/dept/get/{id}")
    Dept queryById(@PathVariable("id") Long id);

    @GetMapping("/dept/list")
    List<Dept> queryAll();

    @PostMapping("/dept/add")
    boolean addDept(Dept dept);
}

In the interface, @ FeignClient(value = "springcloud provider dept") annotation describes the service name we want to access, springcloud provider dept, and provides the load balancing algorithm by default. So far, our load balancing scheme has been realized.

Some friends may wonder why the Ribbon+RestTemplate scheme does not create an interface in the springcloud API sub module. In my personal opinion, one reason is that the Ribbon+RestTemplate scheme is not interface oriented, but uses a constant as the service name to directly call the methods in RestTemplate to search for services, There is no problem of using a unified user-defined public interface; From another perspective, the second reason is that RestTemplate itself is an implementation class, just like a public interface DeptClientService implemented in the springcloud API sub module. The rest of our work is just calling, so we don't need to implement it alone.

Then, we go back to the springcloud consumer dept feign sub module and create a Controller to call the interface we just wrote

@RestController
public class DeptConsumerController {

    @Autowired
    private DeptClientService service = null;

    @RequestMapping("/consumer/dept/add")
    public boolean add(Dept dept) {
        return this.service.addDept(dept);
    }

    @RequestMapping("/consumer/dept/get/{id}")
    public Dept get(@PathVariable("id") Long id) {
        return this.service.queryById(id);
    }

    @RequestMapping("/consumer/dept/list")
    public List<Dept> list() {
        return this.service.queryAll();
    }
}

Finally, the annotation @ EnableFeignClients(basePackages = {"com.demo.springcloud"}) is introduced into the main startup class to enable it to use Feign and scan the interfaces in the package

@SpringBootApplication
@EnableEurekaClient
@EnableFeignClients(basePackages = {"com.demo.springcloud"})
public class DeptConsumer_feign {
    public static void main(String[] args) {
        SpringApplication.run(DeptConsumer_feign.class, args);
    }
}

7, Fuse - Hystrix

The main function of fusing is to adopt a protective measure to prevent the whole system from failure due to overload of service due to some reasons.

For example, if we request data that does not exist in the database, if it is not processed, the server will make an error in this place. With the increasing number of visitors, all visitors will block here because of requesting the wrong service, resulting in the collapse of the service. When this service collapses, the associated preamble service will also collapse due to congestion, resulting in the overall collapse of the whole service. This phenomenon is called avalanche.

In order to eliminate this phenomenon, we introduce the fuse mechanism. When a service error occurs, we try to use an interception method to return the wrong request to a response that can make it end normally, so as to prevent the occurrence of errors and avoid congestion.

As far as my personal understanding is concerned, this is like an official error handling of an interface from the perspective of the server. We only need to implement an error handling method and use @ HystrixCommand annotation to let the system call back when an error occurs.

The following is the implementation scheme of fusing:

First, create a project and import POM XML dependency, which is an improvement based on service providers, is similar to service providers. Only more reliance on Hystrix.

    <dependencies>

        <!-- https://mvnrepository.com/artifact/org.springframework.cloud/spring-cloud-starter-netflix-hystrix -->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
            <version>2.2.9.RELEASE</version>
        </dependency>

        <!--EUREKA provide-->
        <!-- https://mvnrepository.com/artifact/org.springframework.cloud/spring-cloud-starter-netflix-eureka-client -->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
            <version>3.0.5</version>
        </dependency>
<!--        actuator Improve monitoring information-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-actuator</artifactId>
        </dependency>

<!--Used to get entity classes-->
        <dependency>
            <groupId>org.example</groupId>
            <artifactId>springcloud-api</artifactId>
            <version>1.0-SNAPSHOT</version>
        </dependency>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
        </dependency>
        <!-- https://mvnrepository.com/artifact/com.alibaba/druid -->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid</artifactId>
        </dependency>

        <dependency>
            <groupId>ch.qos.logback</groupId>
            <artifactId>logback-core</artifactId>
        </dependency>

        <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
        </dependency>

        <!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-test -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-test</artifactId>
        </dependency>

        <!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-web -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-jetty</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
        </dependency>
    </dependencies>

Then, the Controller layer is modified and error handling functions and annotations are added:

@RestController
public class DeptController {
    @Autowired
    private DeptService deptService;


    @GetMapping("/dept/get/{id}")
    @HystrixCommand(fallbackMethod = "hystrixGet")
    public Dept get(@PathVariable("id") Long id) {
        Dept dept = deptService.queryById(id);
        return dept;
    }

    //alternative
    public Dept hystrixGet(@PathVariable("id") Long id) {
        return new Dept().setDeptno(id)
                .setDname("id=>"+id+",The user does not exist and the information cannot be found")
                .setDb_source("no database");
    }
}

Finally, the @ enablercircuitbreaker annotation is added to the main startup class to support circuit breaker

@SpringBootApplication
@EnableEurekaClient //After the service is started, the provider's client content is registered in the registry eureka
@EnableDiscoveryClient //Service discovery
@EnableCircuitBreaker //Add support for fusing
public class DeptProviderHystrix_8001 {
    public static void main(String[] args) {
        SpringApplication.run(DeptProviderHystrix_8001.class, args);
    }

    //Add a Servlet
    @Bean
    public ServletRegistrationBean hystrixMetricsStreamServlet() {
        ServletRegistrationBean registrationBean = new ServletRegistrationBean(new HystrixMetricsStreamServlet());
        registrationBean.addUrlMappings("/actuator/hystrix.stream");
        return registrationBean;
    }
}

8, Downgrade - Hystrix+Feign

The degraded application scenario is that when one service is accessed too much and the other service is rarely accessed, if the other service can be closed, more space can be made for the server to carry the service with high access. However, at this time, shutting down another service rashly will lead to sudden errors by users using this service. In order to prevent this from happening, an overall error handling scheme should be added to this service. When the service suddenly shuts down, it returns an error message to the user instead of causing the server to crash.

The implementation steps are shown below:

First of all, we need a service provider that implements what Hystrix relies on, so we continue to use the project springcloud-provider-dept-hystrix-8001 in the fuse

Then, since the downgrade is from the perspective of the client, we also need a consumer sub module to access the service provider. Here, we use the client springcloud consumer dept Feign that Feign depends on

Finally, we implement an error handling scheme fallbackfactory in the api sub module, which is the bridge between consumers and providers. It can handle the errors of the whole class (i.e. service). However, it should be noted that the interface of the service provider must have @ HystrixCommand annotation to work.

@Component
public class DeptClientServiceFallBackFactory implements FallbackFactory {
    @Override
    public DeptClientService create(Throwable cause) {
        return new DeptClientService() {
            @Override
            public Dept queryById(Long id) {
                return new Dept()
                        .setDeptno(id)
                        .setDname("id=>"+id+"There is no corresponding information. The client provides degradation information. The current service has been shut down")
                        .setDb_source("No database corresponding information");
            }

            @Override
            public List<Dept> queryAll() {
                return null;
            }

            @Override
            public boolean addDept(Dept dept) {
                return false;
            }
        };
    }
}

This implementation class should implement the FallbackFactory interface. Meanwhile, the FeignClient annotation should be modified in the user-defined interface using Feign

@FeignClient(value = "springcloud-provider-dept", fallbackFactory = DeptClientServiceFallBackFactory.class)
//@FeignClient(value = "springcloud-provider-dept")
public interface DeptClientService {
    @GetMapping("/dept/get/{id}")
    Dept queryById(@PathVariable("id") Long id);

    @GetMapping("/dept/list")
    List<Dept> queryAll();

    @PostMapping("/dept/add")
    boolean addDept(Dept dept);
}

At this time, when we start the service, we suddenly shut down the process of the service provider. If we request from the client again, there will be no corresponding information. The client provides degradation information and the error message that the current service has been shut down.

Difference between fusing and degradation:

1. Fusing refers to the error handling of an interface. Degradation can handle the error of a class

2. From the perspective of service providers, and from the perspective of consumers

9, Service monitoring - HystrixDashBoard

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 are successful, how many are failed, etc.

On the client side, we need to create a separate monitoring sub module and add the @ EnableHystrixDashboard annotation to the main startup class.

In the service provider's POM Add the following dependencies to XML

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-actuator</artifactId>
</dependency>

Add the following code to the startup class of the service provider:

    //Add a Servlet
    @Bean
    public ServletRegistrationBean hystrixMetricsStreamServlet() {
        ServletRegistrationBean registrationBean = new ServletRegistrationBean(new HystrixMetricsStreamServlet());
        registrationBean.addUrlMappings("/actuator/hystrix.stream");
        return registrationBean;
    }

10, Routing gateway - Zuul

The routing gateway mainly provides a unified system entrance from which access services are conducted, which can effectively shield the port information of each micro service and improve security.

First, create a submodule of zuul and create it in application Add zuul configuration information to YML

zuul:
  routes:
    mydept.serviceId: springcloud-provider-dept # Original project name
    mydept.path: /mydept/**
    # The above operation is used to hide the microservice name springcloud provider dept, that is, the springcloud provider dept on the url will be replaced with mydept
  ignored-services: springcloud-provider-dept #No longer use this path to access hidden paths. All paths are: "*"
  prefix: /demodemo #Set uniform prefix

Then add the @ EnableZuulProxy annotation to the main startup class

@SpringBootApplication
@EnableZuulProxy //General selection agent
public class ZuulApplication_9527 {
    public static void main(String[] args) {
        SpringApplication.run(ZuulApplication_9527.class, args);
    }
}

11, Remote yml configuration - Config

In the development of the project, each sub module contains its own application YML configuration information. When a product changes from dev phase to test phase, we must modify the application yml. The remote YML configuration scheme allows you to modify the configuration information in the remote warehouse without modifying the YML in the project. The configuration file in the project only needs to simply call the configuration information of the remote warehouse, so as to achieve the purpose of decoupling the project from the configuration, and it is also convenient for operators and testers to debug the environment before the project goes online.

First, we need a sub module to get the configuration file of the remote warehouse, create springcloud-config-server-3344 and create it in POM Adding dependencies to XML

    <dependencies>
        <!-- https://mvnrepository.com/artifact/org.springframework.cloud/spring-cloud-config-server -->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-config-server</artifactId>
            <version>2.0.5.RELEASE</version>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

    </dependencies>

In application Configure port and remote warehouse connection information in YML

server:
  port: 3344

spring:
  application:
    name: springcloud-config-server #Name of the application
  #Connect to remote warehouse
  cloud:
    config:
      server:
        git:
          uri: https://gitee.com/xxx/springcloud-demo.git #https

Add the annotation @ EnableConfigServer in the main startup class

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

Next, we introduce the config dependency into the sub modules that need remote configuration

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-config</artifactId>
    <version>2.2.1.RELEASE</version>
</dependency>

Create a new bootstrap YML configuration file, which is used to call the sub module to obtain the configuration file of the remote warehouse, so as to indirectly call the configuration file of the remote warehouse

spring:
  cloud:
    config:
      name: config-dept # The resource name that needs to be read from git does not need a suffix
      label: master
      profile: dev
      uri: http://localhost:3344
      # The above is equivalent to: http://localhost:3344/master/config-dept-dev.yml

Modify application YML, delete all configurations and keep only the project name

spring:
  application:
    name: spring-config-client-3355

Due to bootstrap YML is better than application YML is a configuration file with higher priority, so the project will be configured according to the online configuration file of the remote warehouse, so even in application YML writes different port information, which doesn't work.

The yml file in the remote warehouse is as follows:

spring:
  profiles:
    active: dev
 
---
server:
  port: 8001
management:
  endpoints:
    web:
      exposure:
        include: "*"
mybatis:
  type-aliases-package: com.demo.springcloud.pojo
  config-location: classpath:mybatis/mybatis-config.xml
  mapper-locations: classpath:mybatis/mapper/*.xml

spring:
  profiles: dev
  application:
    name: springcloud-config-dept #Name of the application
  datasource:
    type: com.alibaba.druid.pool.DruidDataSource
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://localhost:3306/eis?useUnicode=true&characterEncoding-utf-8
    username: root
    password: 123456

#The configuration of eureka, where the service is registered, and the provider should find the registration center
eureka:
  client:
    service-url:
#      defaultZone: http://localhost:7001/eureka/
      defaultZone: http://eureka7001.com:7001/eureka/,http://eureka7002.com:7002/eureka/, http://eureka7003.com:7003/eureka/ #Provider publishing configuration URL in cluster
  instance:
    instance-id: springcloud-provider-dept8001 #Modify the default description information on eureka

#info configuration is written casually
info:
  app.name: demo-yjn
  company.name: yangejining.springcloud.com

---
server:
  port: 8001
management:
  endpoints:
    web:
      exposure:
        include: "*"
mybatis:
  type-aliases-package: com.demo.springcloud.pojo
  config-location: classpath:mybatis/mybatis-config.xml
  mapper-locations: classpath:mybatis/mapper/*.xml

spring:
  profiles: test
  application:
    name: springcloud-config-dept #Name of the application
  datasource:
    type: com.alibaba.druid.pool.DruidDataSource
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://localhost:3306/eis1?useUnicode=true&characterEncoding-utf-8
    username: root
    password: 123456

#The configuration of eureka, where the service is registered, and the provider should find the registration center
eureka:
  client:
    service-url:
#      defaultZone: http://localhost:7001/eureka/
      defaultZone: http://eureka7001.com:7001/eureka/,http://eureka7002.com:7002/eureka/, http://eureka7003.com:7003/eureka/ #Provider publishing configuration URL in cluster
  instance:
    instance-id: springcloud-provider-dept8001 #Modify the default description information on eureka

#info configuration is written casually
info:
  app.name: demo-yjn
  company.name: yangejining.springcloud.com

appendix

reference material:

https://www.bilibili.com/video/BV1jJ411S7xr

Introduction and use of resttemplate_ weixin_41261521 blog - CSDN blog_ resttemplate

Detailed explanation of Ribbon - Jianshu (jianshu.com)

#Please indicate the source for reprint#

Keywords: Spring Cloud eureka ribbon

Added by nerya on Fri, 04 Feb 2022 08:31:25 +0200