Spring cloud quick start, how do Java developers become architects step by step

Feign is a declarative and templated HTTP client developed by Netfix. It can help us call HTTP API more quickly. It supports its own annotations, Jax RS annotations and spring MVC annotations, and also integrates Ribbon and Eureka. By using feign, the Rest request can be hidden and disguised as a Controller similar to spring MVC. You don't have to splice URLs and parameters by yourself. It's very convenient for feign to do everything.

Feign project address: https://github.com/OpenFeign/feign

In the previous study, we used Ribbon's load balancing function to greatly simplify the code when calling remotely:

String user = this.restTemplate.getForObject("http://service-provider/user/" + id, String.class); 

Quick start:

Transformation of springboot service consumer project:

Import dependency:

<dependency>

    <groupId>org.springframework.cloud</groupId>

    <artifactId>spring-cloud-starter-openfeign</artifactId>

</dependency> 

Enable Feign function: add comments on the startup class and @ EnableFeignClients enable Feign function

@SpringCloudApplication //Combined annotation, equivalent to @ springbootapplication + @ enablediscoveryclient + @ enablercircuitbreaker

@EnableFeignClients     //Enable feign component

public class SpringbootServiceConsumerApplication {



    /*@Bean

    @LoadBalanced  //Turn on load balancing

    public RestTemplate restTemplate() {

        return new RestTemplate();

    }*/



    public static void main(String[] args) {

        SpringApplication.run(SpringbootServiceConsumerApplication.class, args);

    }



} 

Delete RestTemplate: feign has automatically integrated the RestTemplate of Ribbon load balancing. Therefore, there is no need to register RestTemplate here.

Configure Feign's client:

In the springboot service consumer project, add the UserClient interface:

//@Feignclient (value = "service provider") / / mark that this class is a feign interface

@FeignClient(value = "service-provider",fallback = UserClientFallBack.class)

public interface UserClient {



    @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 mapper

  • @FeignClient, declare that this is a Feign client, similar to the @ Mapper annotation. At the same time, 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

Modify the original calling logic and call the UserClient interface:

@RestController

@RequestMapping("consumer/user")

public class UserController {



    @Autowired

    private UserClient userClient;



    @GetMapping

    public String queryUserById(@RequestParam("id") Long id){

        User user = this.userClient.queryUserById(id);

        return user.toString();

    }



} 

Start test:

Access interface:

The results were obtained normally.

Load balancing and Hystrix support:

Ribbon dependency and automatic configuration have been integrated in Feign itself, so we don't need to introduce additional dependencies or register RestTemplate objects.

Feign also integrates with Hystrix by default, but it is turned off by default. We need to start it through the following parameters: (add the configuration content in the springboot service consumer project):

feign:

  hystrix:

    enabled: true # Turn on the fusing function of Feign 

However, the fallback configuration in Feign is not as simple as in hystrix. First, we need to define a class UserClientFallback to implement the UserClient just written as the fallback processing class

@Component

public class UserClientFallBack implements UserClient {

    @Override

    public User queryUserById(Long id) {

        User user = new User();

        user.setUsername("The server is busy, please try again later!!!");

        return user;

    }

} 

Then, in UserFeignClient, specify the implementation class just written

@FeignClient(value = "service-provider", fallback = UserClientFallback.class) // Note that this class is a feign interface

public interface UserClient {



    @GetMapping("user/{id}")

    User queryUserById(@PathVariable("id") Long id);

} 

Restart test:

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:

feign:

  compression:

    request:

      enabled: true # Turn on request compression

    response:

      enabled: true # Turn on response compression 

At the same time, we can also set the requested data type and the lower limit of the 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 

Note: the above data type and the lower limit of compression size are the default values.

Zuul gateway

===================================================================

Through the previous study, the architecture of microservices using Spring Cloud is basically formed, which is roughly as follows:

  • Eureka in Spring Cloud Netflix is used to realize service registry and service registration and discovery;

  • Service consumption and load balancing between services are realized through Ribbon or Feign.

  • In order to make the service cluster more robust, Hystrix's breaking mechanism is used to avoid the fault spread caused by the exception of individual services in the microservice architecture.

In this architecture, our service cluster includes internal services Service A and Service B, both of which register and subscribe to Eureka Server, while Open Service is an external service, which is exposed to the service caller through load balancing. We focus on external services and directly expose our service address. Is this implementation reasonable, or is there a better implementation?

We can pull such things as permission control out of our service unit, and the most suitable place for these logic is at the front end of external access. We need a more powerful service gateway of load balancer.

Service gateway is an indispensable part of microservice architecture. In the process of uniformly providing rest APIs to the external system through the service gateway, it not only has the functions of service routing and load balancing, but also has the functions of permission control. Zuul in Spring Cloud Netflix plays such a role, providing front door protection for the micro service architecture. At the same time, it migrates the heavy non business logic content of permission control to the service routing level, so that the service cluster subjects can have higher reusability and testability.

Zuul gateway official website: https://github.com/Netflix/zuul

Architecture after Zuul's addition

Whether it is a request from the client (PC or mobile terminal) or an internal call of the service. All requests for services will pass through zuul gateway, and then the gateway will realize authentication, dynamic routing and other operations. Zuul is the unified entrance to our services.

quick get start

New project:

Add Zuul dependency:

Project catalogue:

pom.xml file

<?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 https://maven.apache.org/xsd/maven-4.0.0.xsd">

    <modelVersion>4.0.0</modelVersion>

    <parent>

        <groupId>org.springframework.boot</groupId>

        <artifactId>spring-boot-starter-parent</artifactId>

        <version>2.2.11.RELEASE</version>

        <relativePath/> <!-- lookup parent from repository -->

    </parent>

    <groupId>com.ly</groupId>

    <artifactId>ly-zuul</artifactId>

    <version>0.0.1-SNAPSHOT</version>

    <name>ly-zuul</name>

    <description>Demo project for Spring Boot</description>



    <properties>

        <java.version>1.8</java.version>

        <spring-cloud.version>Hoxton.SR9</spring-cloud.version>

    </properties>



    <dependencies>

        <dependency>

            <groupId>org.springframework.cloud</groupId>

            <artifactId>spring-cloud-starter-netflix-zuul</artifactId>

        </dependency>

        <dependency>

            <groupId>org.springframework.cloud</groupId>

            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>

            <version>2.0.0.RELEASE</version>

        </dependency>



        <dependency>

            <groupId>org.springframework.boot</groupId>

            <artifactId>spring-boot-starter-test</artifactId>

            <scope>test</scope>

            <exclusions>

                <exclusion>

                    <groupId>org.junit.vintage</groupId>

                    <artifactId>junit-vintage-engine</artifactId>

                </exclusion>

            </exclusions>

        </dependency>



    </dependencies>



    <dependencyManagement>

        <dependencies>

            <dependency>

                <groupId>org.springframework.cloud</groupId>

                <artifactId>spring-cloud-dependencies</artifactId>

                <version>${spring-cloud.version}</version>

                <type>pom</type>

                <scope>import</scope>

            </dependency>

        </dependencies>

    </dependencyManagement>



    <build>

        <plugins>

            <plugin>

                <groupId>org.springframework.boot</groupId>

                <artifactId>spring-boot-maven-plugin</artifactId>

            </plugin>

        </plugins>

    </build>



</project> 

Create a new application.yml file and write the configuration

server:

  port: 10010 #Service port

spring:

  application:

    name: ly-zuul  #Specify the service name 

Write boot class:

Enable the function of Zuul through the @ EnableZuulProxy annotation:

@SpringBootApplication

@EnableZuulProxy // Enable gateway function

public class ItcastZuulApplication {



    public static void main(String[] args) {

        SpringApplication.run(ItcastZuulApplication.class, args);

    }

} 

Write routing rules:

We need to use Zuul to proxy the service provider service. First look at the service status in the control panel:

Mapping rules:

server:

  port: 10010

spring:

  application:

    name: ly-zuul



zuul:

  routes:

    service-provider: /service-provider/** #The route name can be used arbitrarily. It is customarily a service name

      path: /service-provider/**   # Here is the mapping path

      url: http://localhost:8081 # mapping the actual url address corresponding to the path 

We proxy all requests that comply with the path rule to the address specified by the url parameter

In this example, we proxy the request starting with / service provider / * * to http://localhost:8081

Start test:

The mapping path of the configuration rule needs to be added to the access path. We access: http://localhost:10010/service-provider/user/2

Service oriented routing

In the routing rule just now, we wrote the service address corresponding to the path to death! This is obviously unreasonable if there are multiple instances of the same service. According to the service name, we should go to Eureka registry to find the list of all instances corresponding to the service, and then conduct dynamic routing!

Modify and optimize the ly zuul project and add Eureka client dependency:

<dependency>

    <groupId>org.springframework.cloud</groupId>

    <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>

</dependency> 

Add Eureka configuration to obtain service information:

eureka:

  client:

    registry-fetch-interval-seconds: 5 # Cycle for obtaining service list: 5s

    service-url:

      defaultZone: http://localhost:10086/eureka 

Enable Eureka client discovery:

@SpringBootApplication

@EnableZuulProxy  //Enable zuul gateway component

@EnableDiscoveryClient  //Enable client

public class LyZuulApplication {



    public static void main(String[] args) {

        SpringApplication.run(LyZuulApplication.class, args);

    }



} 

Modify the mapping configuration and obtain the following information through the service name:

Because we already have Eureka client, we can obtain the address information of the service from Eureka. Therefore, during mapping, we do not need to specify the IP address, but access it through the service name. Moreover, Zuul has integrated the load balancing function of Ribbon.

zuul:

  routes:

    service-provider: # Here is the routing id. write it at will

      path: /service-provider/** # Here is the mapping path

      serviceId: service-provider # Specify the service name 

Start test:

Start again. This time, Zuul will use the Ribbon for load balancing access when acting as an agent:

Four methods of routing configuration: (the third is generally used)

zuul:

  routes:

    service-provider: 

      path: /service-provider/** #The route name can be used arbitrarily. It is customarily a service name

      url: http://localhost:8081

    

zuul:

  routes:

    service-provider:

      path: /service-provider/**   #The route name can be used arbitrarily. It is customarily a service name

      serviceId: service-provider  #Specify the service name

      

#In most cases, the route name and service name are written the same, so Zuul provides a simplified configuration syntax:

zuul:

  routes:

    service-provider: /provider/** #The route name can be used arbitrarily. It is customarily a service name

 

-No configuration, the default is service id Start path 

-Zuul The default routing rule is specified: by default,The mapping path of all services is the service name itself 

Test the third method: use prefix: / * to configure the virtual path

zuul:

  routes:

    service-provider: /provider/** #The route name can be used arbitrarily. It is customarily a service name

  prefix: /api  #Virtual path 

filter

As one of the important functions of Zuul gateway, it is to realize the authentication of requests. We often implement this action through the filter provided by Zuul.

ZuulFilter:

ZuulFilter is the top-level parent of the filter. Here we take a look at the four most important methods defined:

public abstract ZuulFilter implements IZuulFilter{



    abstract public String filterType();



    abstract public int filterOrder();

    

    boolean shouldFilter();// From IZuulFilter



    Object run() throws ZuulException;// IZuulFilter

} 

  • shouldFilter: returns a Boolean value to judge whether the filter needs to be executed. Return true to execute and false to not execute.

  • run: the specific business logic of the filter.

  • filterType: returns a string representing the type of filter. It includes the following four types:

    • pre: the request is executed before being routed

    • route: called when routing a request

    • post: call after route and errror filter.

    • Error: an error occurred while processing the request

  • filterOrder: defines the execution order of the filter by the returned int value. The smaller the number, the higher the priority.

Filter execution lifecycle:

This is the request life cycle diagram provided by Zuul's official website, which clearly shows the execution sequence of a request in each filter.

Normal process:

  • The request will first pass through the pre type filter and then reach the route type for routing. The request will reach the real service provider. After executing the request and returning the results, it will reach the post filter. Then return the response.

Abnormal process:

  • In the whole process, if the pre or route filter is abnormal, it will directly enter the error filter. After the error is processed, the request will be handed over to the POST filter and finally returned to the user.

  • If the error filter itself has an exception, it will eventually enter the POST filter and return the final result to the requesting client.

  • If the POST filter has an exception, it will jump to the error filter, but unlike pre and route, the request will not reach the POST filter again.

List of all built-in filters:

Usage scenario:

  • Request authentication: it is generally placed in the pre type. If it is found that there is no access permission, it will be intercepted directly

  • Exception handling: it is generally handled in combination with error type and post type filters.

  • Statistics of service call duration: pre and post are used together.

Custom filter:

Next, we customize a filter to simulate the verification of a login. Basic logic: if there is an access token parameter in the request, the request is considered valid and released.

Define filter class:

@Component

public class LoginFilter extends ZuulFilter {



    @Override

    public String filterType() {

        return "pre";

    }


Keywords: Java Spring Back-end Programmer RESTful

Added by jayskates on Mon, 06 Sep 2021 05:22:46 +0300