SpringCloud - Learning and Recording of Key Components such as Eureka Feign Ribbon Hystrix Zuul

SpringCloud - Learning and Recording of Key Components such as Eureka Feign Ribbon Hystrix Zuul

Preface: This is the thoughts and summary of the recent learning of the mad SpringCloud tutorial. Since the understanding of the SpringCloud system is still in its early stages, there will inevitably be biases in terms of wording and understanding. Please also criticize and correct them!

1. Overview

SpringCloud is a set of micro-service solutions in the spring-cloud-dependencies Hoxton.SR8 Version and spring-boot-dependencies 2.3.4. In conjunction with the RELEASE version, this set of solutions provides the Eureka Registry, the Ribbon+RestTemplete or Feign load balancing scheme, Hystrix melting, Hystrix+Feign demotion, HystrixDashboard service monitoring, and the Zuul routing gateway.

However, in subsequent versions of spring-cloud-dependencies (such as the 2020.x.x series), the services offered above have changed considerably, especially since Ribbon stopped updating (IRule interface removed), Hystrix+Feign changes have made it difficult for us to use custom load balancing algorithms and downgrade services, which I am still learning and exploring. Recording will take place after you have a basic understanding and mastery, so this article is still based on an earlier version of spring-cloud-dependencies.

(P.S. Although the Hoxton version is still supported, it may be sooner or later that NetFIix has changed in the new version.)

2. Composition of the project

1. Project integration approach

The SpringCloud project consists of a total pom. The XML is responsible for managing all the dependencies that may be used in the project, so that the management function is to facilitate the unification of versions. Submodules do not need to specify dependent versions when using the same dependencies, as these versions are already in the total pom. Specified in xml.

Total POM in a project. In xml, we can use the <dependency Management></dependency Management> tag to manage all dependencies, in which case all <dependency></dependency> will not be imported but will be inherited from the total POM by the sub-module. The XML is imported into the appropriate sub-module only after the dependent dependencies are used.

For example, the total POM is shown below. 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 to a 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 Perfect 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 don't have a total pom. Some dependency management is declared in the xml, and the dependencies are imported in the sub-module with the version number.

2. Parts of the project

A SpringCloud project consists of four components: the service registry, the service API, the service provider, and the service consumer.

  • The registry is responsible for the registration and discovery of service providers, facilitating the use of services by service consumers, and effectively managing the services provided. Simply put, a registry is a multi-cluster. When one cluster fails, it can still keep other clusters running and prevent service crashes. For example, a registration center is like an e-commerce service platform, where service provider businesses display their goods and service consumer buyers browse their goods.
  • The service API provides the entity classes (and usually the parts that correspond to the database tables) required by the service, as well as some common configurations, such as Feign's load balancing use, in which consumers access the service by invoking the API's interface, which avoids duplicating the configuration of the interface in the consumer module for uniform use.
  • Service providers are common parts of traditional projects, such as SpringBoot, where our projects typically include the Dao, Service, and Controller layers for database interaction, service logic processing, and service interface exposing. In SpringCloud, service providers continue to do this, unlike Service providers may have multiple sub-modules that provide the same service for ease of load balancing.
  • The purpose of service consumers is to conceal the url of service providers. What is obvious is the url and port of consumers. When the current end invokes the service, only the consumer needs to be invoked to get background data. On the consumer side, there are different ways to achieve load balancing, which will unfold later.

The following section describes several core features SpringCloud provides and presents them in conjunction with project source code.

3. Registration and Discovery - Eureka

Eureka acts as a registry and provides a platform to help service providers expose their services.

First, we need to set up a sub-project springcloud-eureka-7001, and in pom. Import dependencies 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 overload services.

Next, we need to make a basic configuration of this sub-module, specifying the port in the application, the eureka server instance name, whether to be discovered by the registry, whether we are the registry, and the address accessed by the component to be registered

server:
  port: 7001

#Euraka Configuration
eureka:
  instance:
#    hostname: localhost #Euraka server side instance name
    hostname: eureka7001.com #In the case of Euraka server-side 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 a registry
    service-url: #Represents the address at which the registry interacts, that is, the monitoring page, accessed by the component to be registered
#      defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/
      # If you access eureka separately, write in the browser http://localhost:7001/ that will do
      defaultZone: http://eureka7002.com:7002/eureka/, http://eureka7003.com:7003/eureka/

Finally, we just need to indicate in the startup class that this sub-module is a eureka server, using the comment @EnableEurekaServer

@SpringBootApplication
@EnableEurekaServer //EnableEurekaServer service-side startup class that accepts registration
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 crash unexpectedly, we can set up multiple clusters so that multiple registries can call each other to prevent the entire service from being unavailable due to a registry crash.

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

7002:

server:
  port: 7002

#Euraka Configuration
eureka:
  instance:
    hostname: eureka7002.com #Euraka server side 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 a registry
    service-url: #Represents the address at which the registry interacts, that is, the monitoring page, accessed by the component to be registered
#      defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/
      # If you access eureka separately, write in the browser http://localhost:7001/ that will do
      defaultZone: http://eureka7001.com:7001/eureka/, http://eureka7003.com:7003/eureka/

7003:

server:
  port: 7003

#Euraka Configuration
eureka:
  instance:
    hostname: eureka7003.com #Euraka server side 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 a registry
    service-url: #Represents the address at which the registry interacts, that is, the monitoring page, accessed by the component to be registered
#      defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/
      # If you access eureka separately, write in the browser http://localhost:7001/ that will do
      defaultZone: http://eureka7001.com:7001/eureka/, http://eureka7002.com:7002/eureka/

4. Load Balancing

In SpringCloud, the meaning of load balancing is to select a Service from the client's point of view through a load balancing strategy to avoid large congestion of access to a Service, provided, of course, that the Service provider provides several services with the same functionality, as we mentioned earlier.

Load balancing is described below in two different ways, Ribbon+RestTemplete and Feign.

5. Load Balancing - Ribbon+RestTemplete

Spring Cloud Ribbon is an HTTP and TCP based client load balancing tool based on Netflix Ribbon implementation. Encapsulating Spring Cloud allows us to easily automate 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 efficiency of client writing.

First, we create a sub-module called springcloud-consumer-dept-80, which will use port 80 by default in pom. Add dependencies to xml, directory composition and code 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 don't add it alone-->
        <!-- 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, with the @LoadBalanced annotation implementing load balancing

@Configuration
public class ConfigBean {

    @Bean
    @LoadBalanced //Configure Load Balancing Implementation
    public RestTemplate getRestTemplate() {
        return new RestTemplate();
    }

}

Then we implement the Controller layer, where we use a constant to specify the services we need. The link to the service does not need to specify the url address, it just needs the service name as part of the link, for example: http://springcloud-provider-dept "

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

    @Autowired
    private RestTemplate restTemplate;
   
    //When ribbon is used, the address should be a variable, accessed by 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 service interface name

dept is an incoming parameter

Boolean.class is the return value type

This corresponds to the public boolean add(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 ribbon is used, the address should be a variable, accessed by 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, the main startup class is implemented:

// When ribbon and Eureka are integrated, clients can call them directly, regardless of 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

Load balancing using Feign is essentially the same as using Ribbon+RestTemple, because Feign inherits Ribbon itself, but Feign is more in line with the idea of Interface-oriented programming, enabling us to access services interactively when invoked through encapsulation rather than through a constant in RestTemplate.

First we create the project and import the 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 really need 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 Provide that it already contains ribbon,So don't add it alone-->
        <!-- 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 the choice of service interfaces on the server side by the client according to a certain policy, so the interface for load balancing is guaranteed to be implemented on the client side. So we want to implement an interface in the springcloud-api sub-module, which implements a unified load balancing scheme and helps clients invoke services from service providers. We create a service layer in the springcloud-api sub-module and create a new 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, the @FeignClient(value = "springcloud-provider-dept") comment states the service name springcloud-provider-dept that we want to access, and the load balancing algorithm is provided by default, so our load balancing scheme is implemented.

Some friends may wonder why in the Ribbon+RestTemplate scenario, no interfaces are created in the springcloud-api sub-module. In my own opinion, one reason is that the Ribbon+RestTemplate scenario is not interface-oriented, but uses a constant as the service name to call the methods in RestTemplate directly to search for services. There is no problem with using a single, customized public interface; In another way, the second reason is that RestTemplate itself is an implementation class, just like DeptClientService, a public interface we implemented in the springcloud-api submodule, the rest of our work is just a call, so we don't need to implement it on our own.

Then we go back to the springcloud-consumer-dept-feign submodule 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"} was introduced in 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 the fuse is to overload the service for some reason, which is a protective measure to prevent the whole system from failing.

For example, if we request data that does not exist in a database, if we do not process it, the server will make an error in this place, and as more and more visitors grow, all visitors will be blocked here by requesting the wrong service, causing the service to crash. When the service crashes, the associated prefix service crashes due to congestion, causing the entire service to crash, a phenomenon known as an avalanche.

To eliminate this phenomenon, we introduce a fused mechanism. When a service error occurs, we try to use an intercept method to return the wrong request to a response that will allow it to end normally, thus preventing the error from occurring and avoiding congestion.

In my own opinion, this is like an officially provided error handling of an interface from a server-side perspective. We just need to implement one error handling method and use the @HystrixCommand annotation to have the system callback when an error occurs.

Here is the implementation of the fuse:

First, create a project and import the pom.xml dependencies are similar to service providers because they are improvements based on service providers. Just more Hystrix dependencies.

    <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 Perfect 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>

Next, transform the Controller layer to include error handling functions and annotations:

@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 information cannot be found")
                .setDb_source("no database");
    }
}

Finally, add the @EnableCircuitBreaker annotation to the main startup class to support fusing

@SpringBootApplication
@EnableEurekaClient //After the service starts, register the provider's client content with 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. Demotion - Hystrix+Feign

A downgraded scenario is that when one service is accessed too high and the other is accessed by very few people, you can make more room for the server to host that highly accessed service if you can shut down the other service. However, at this time, the sudden shutdown of another service will cause users of this service to make sudden errors. In order to prevent this from happening, a holistic error handling scheme should be added to this service. When the service suddenly shuts down, an error message is returned to the user instead of causing the server to crash.

The following shows the implementation steps:

First, we need a service provider that implements the Hystrix dependency, so continue using the fused item springcloud-provider-dept-hystrix-8001

Next, since the downgrade is from a client perspective, a consumer sub-module is required to access the service provider, where we use the client springcloud-consumer-dept-feign that implements the Feign dependency

Finally, we implement an error handling scheme fallbackfactory in the api sub-module, a bridge between consumers and providers, which can handle errors for the entire class (i.e. services), but it is important to note that the @HystrixCommand annotation must be present on the interface of the service provider to make a difference.

@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+"No corresponding information, the client provided demotion information, the current service is off")
                        .setDb_source("No database information");
            }

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

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

This implementation class implements the interface FallbackFactory and modifies the FeignClient annotation in the custom interface that uses 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 point, when we start the service, we suddenly shut down the process of the service provider. When we request from the client again, there will be no corresponding information. The client provides the downgrade information, and the error message that the current service has been shut down.

Fusing is different from demotion:

1. A fuse is an error handling of an interface, and a downgrade can handle a class

2. Fusing from the service provider's perspective, downgrading from the consumer's Perspective

9. Service Monitoring - HystrixDashBoard

Hystrix keeps track of the execution of all requests initiated through Hystrix and presents them to users in statistical reports and graphics, including how many requests are executed per second, how many successes, how many failures, and so on.

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

At the service provider's pom. Add the following dependencies to the XML

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

Add the following code to the service provider's startup class:

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

10. Routing Gateway - Zuul

Routing gateways mainly provide a unified system entry from which access services are provided, which can effectively shield the port information of each micro-service itself and improve security.

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

zuul:
  routes:
    mydept.serviceId: springcloud-provider-dept # Original project name
    mydept.path: /mydept/**
    # This is used to hide the microservice name springcloud-provider-dept, where the springcloud-provider-dept on the url is replaced with mydept
  ignored-services: springcloud-provider-dept #No longer use this path access to hide all paths as: "*"
  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 a project, each sub-module contains its own application.yml configuration information, when a product changes from dev to test, we must modify the application in the project. yml. The remote yml configuration scheme allows you to modify the configuration information in the remote warehouse instead of modifying the yml in the project. The configuration file in the project only needs to simply invoke the configuration information of the remote warehouse to decouple the project from the configuration. It also facilitates the debugging of the environment before the project goes online by operation and testers.

First, we need a sub-module to get the remote warehouse configuration file, create springcloud-config-server-3344, and in pom. Add 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 remote warehouse
  cloud:
    config:
      server:
        git:
          uri: https://gitee.com/xxx/springcloud-demo.git #https

Add the comment @EnableConfigServer to 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 config dependencies in the sub-modules that require 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 profile, which is used to invoke a sub-module to obtain a remote warehouse profile and thus indirectly invoke the remote warehouse profile

spring:
  cloud:
    config:
      name: config-dept # Resource name to read from git, no suffix required
      label: master
      profile: dev
      uri: http://localhost:3344
      # The above equivalent: http://localhost:3344/master/config-dept-dev.yml

Modify the application.yml, delete all configurations, just keep the project name

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

Because bootstrap.yml is better than application.yml has a higher priority profile, so the project must be configured according to the remote warehouse's online profile, even in application.yml writes different port information and it doesn't work either.

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

#Configuration of eureka, where services are registered, and provider to find a registry
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 scenario
  instance:
    instance-id: springcloud-provider-dept8001 #Modify the default description information on eureka

#info Configuration Write Freely
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

#Configuration of eureka, where services are registered, and provider to find a registry
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 scenario
  instance:
    instance-id: springcloud-provider-dept8001 #Modify the default description information on eureka

#info Configuration Write Freely
info:
  app.name: demo-yjn
  company.name: yangejining.springcloud.com

appendix

Reference material:

https://www.bilibili.com/vide...

Introduction and use of resttemplate_ weixin_41261521 Blog-CSDN Blog_ resttemplate

Ribbon Details - Brief Book (jianshu.com)

Please indicate the source #for reprinting

Keywords: Java Spring Cloud

Added by loony383 on Sun, 06 Feb 2022 19:48:13 +0200