Spring cloud learning - Zuul detailed explanation (with code package)

preface

Previous: detailed explanation of Hystrix
Zuul (Gateway) is very important and important in spring cloud. The role of zuul gateway is similar to that of nginx. Nginx can forward requests, and nginx manages the ip address of services. Clients only need to access the port of nginx, and which service to forward and access is forwarded by nginx. Zuul also has this function, and zuul can authenticate and authorize. If zuul thinks that a request has permission, Will be forwarded to the corresponding service. In addition, if the service has iterative changes, you only need to change the zuul configuration. The client does not need to change. It can also limit the flow. It has many advantages, but it is very simple to use.
advantage:
1. Request forwarding, the client does not need to maintain a large number of service ports
2. Authentication and authorization operations
3. Current limiting and safety
4. Project iteration leads to project splitting or change. You don't need to move the client, just modify Zuul configuration

1, Zuul quick start

Create a new module, maven project, named zuul_01
Common steps: import dependency, configure yml, configure startup class + start Zuul annotation, and finally test

Note: zuul also accesses other services by registering in Eureka

Step 1: import dependencies

<dependencies>
        <!--zuul Yes and through EurekaClient To register and access other services, you also need to import Eureka_Client-->
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-netflix-zuul</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
</dependencies>

Step 2: configure application.yml

# Specify which EurekaServer service to register with
eureka:
  client:
    serviceUrl:
      defaultZone: http://root:root@localhost:8761/eureka/,http://root:root@localhost:8762/eureka/

# Specify the service name to distinguish the created services
spring:
  application:
    name: zuul

server:
  port: 8001

Step 3: start the class and annotate @ EnableEurekaClient and @ EnableZuulProxy, because Zuul needs to be registered in Eureka

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

Start ZuulApplication and enter localhost:8761 to see the services that have been started. Here you should also see my previous cases and run the code. They are all related

Through Zuul's port 8001, you can access the corresponding service by entering the service name + interface. The process is Zuul - > Customer - > pay call

2, Zuul common configuration

2.1 monitoring interface and service configuration

The Zuul monitoring interface is not as good-looking as Hystrix. It is relatively simple. The effects are as follows. You can go to Zuul to see whether the configured service is effective. For example, if the address contains / customer / * *, you will find it in the customer service. If you encounter / eureka / * *, you will find it in the eureka service, which is similar to nginx

How do we configure it? A few old steps

Step 1: import dependencies
newly added

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

Step 2: configure yml
newly added

# zuul's monitoring interface is configured as * during development and does not need to be configured online
management:
  endpoints:
    web:
      exposure:
        include: "*"

Then restart and enter http://localhost:8001/actuator/routes that will do

What if you want to remove a service? Add the following configuration to application.yml

Restart the program and refresh the interface. You will find that the eureka service is directly removed and the customer service is still there, but you can't access it

#zuul configuration
zuul:
  ignored-services: eureka  #Ignore based on the service name. If you want to ignore all services, use*
  ignored-patterns: /**/search/** #The monitoring interface is still visible, but it cannot be accessed. 404

Custom configuration method: if you want to change the following path to another name, you can use custom configuration

#zuul configuration
zuul:
  ignored-services: eureka  #Ignore based on the service name. If you want to ignore all services, use*
  ignored-patterns: /**/pay/** #The monitoring interface is still visible, but it cannot be accessed. 404
  routes:
    payClient:  /pay/** #Service Name: new path
    customer: /user/**

This creates a new Accessible path. Of course, the old can also be used. The two are equivalent

If you don't want to use the old ones, you can ignore them. For example, we write ignored services: "*" here to try, restart and refresh. The previous access methods are ignored, so you can change the access name according to your needs

Enter as follows http://localhost:8001/user/customer/2 Get the following results, which used to be http://localhost:8001/customer/customer/2 , I just changed my name now

We don't need this for the time being. Ignore it and comment first

2.2 gray Publishing

What is grayscale publishing?
Grayscale publishing is in the version iteration process. For example, when a new version of app is released, the updated user calls the function carefully, while the old user still calls the function with the old interface. If grayscale publishing is adopted, the new and old versions can be balanced. What should we do? Just two steps

Step 1: add @ Bean code block to zuul startup class. Zuul uses version. The new version of zuul will let the corresponding service use the new interface, and the old version will use the old interface

@Bean
    public PatternServiceRouteMapper serviceRouteMapper(){
        return new PatternServiceRouteMapper(
                "(?<name>^.+)-(?<version>v.+$)",
                "${version}/${name}"
        		);
    }

Step 2: prepare a service and make two versions

Do the following first


Restart both old and new customers, ZuulApplication, and changes. Then check zuul's monitoring interface, and the new one will appear as / v1/customer. If you add another customer, it will become / v2/customer, and zuul will automatically perform

If you don't get V1 and v2, there is only one v1. If you don't have v2, it proves that you have a problem. Generally, it is a registration problem. Finally, I think all services should be registered. Then Eureka has all services, and Zuul can get all services by re registering


It's OK to start in this order

So it can be accessed according to different versions

3, Zuul filter

Filter is the core component of Zuul. Let's take a look at its execution process, as shown in the following figure

The client requests are sent to the Zuul service. First, through the PreFilter filter chain, this filter can perform operations such as flow restriction and permission verification. If the request is correct, it will be released normally, and the request will be forwarded to RoutingFilter again. RouteFilter will forward the request to the specified service to get the response result, and then pass the result back to RoutingFilter, RouteFilter then passes the result to the PostFilter filter filter chain, and finally gives the response information to the client.

Four filters are available

3.1 Zuul filter quick start

Create two custom filter classes in zuul module, inherit ZuulFilter and override four methods

@Component
public class TestZuulFilter extends ZuulFilter {
    @Override
    public String filterType() {
        return FilterConstants.PRE_TYPE;//Set filter type, prefilter
    }

    @Override
    public int filterOrder() {
        return FilterConstants.PRE_DECORATION_FILTER_ORDER - 1; //Set the filter execution order. The smaller the number, the first execution will be performed. If it is smaller than the pre filter, it will be performed before the pre filter
    }

    @Override
    public boolean shouldFilter() {
        return true;   //Open current filter
    }

    @Override
    public Object run() throws ZuulException {
        System.out.println("prefix The filter has been executed");
        return null;
    }
}

import com.netflix.zuul.ZuulFilter;
import com.netflix.zuul.exception.ZuulException;
import org.springframework.cloud.netflix.zuul.filters.support.FilterConstants;
import org.springframework.stereotype.Component;

@Component
public class TestZuulFilter2 extends ZuulFilter {
    @Override
    public String filterType() {
        return FilterConstants.PRE_TYPE;//Set filter type, prefilter
    }

    @Override
    public int filterOrder() {
        return FilterConstants.PRE_DECORATION_FILTER_ORDER + 1; //Set the filter execution order. The smaller the number, the first execution will be performed. If it is smaller than the pre filter, it will be performed before the pre filter
    }

    @Override
    public boolean shouldFilter() {
        return true;   //Open current filter
    }

    @Override
    public Object run() throws ZuulException {
        System.out.println("prefix Filter 222 has been executed");
        return null;
    }
}

Restart ZuulApplication, and then randomly access an address, such as this one http://localhost:8001/v1/customer/version The console will print the following results

FilterConstants.PRE_ DECORATION_ FILTER_ The TestZuulFilter of order - 1 is executed first, because the smaller the number is, it will be executed first. The default value in the source code is 5

3.2 PreFilter implements token verification

Create an AuthenticFilter and inherit ZuulFilter

import com.netflix.zuul.ZuulFilter;
import com.netflix.zuul.context.RequestContext;
import com.netflix.zuul.exception.ZuulException;
import org.apache.http.HttpStatus;
import org.springframework.cloud.netflix.zuul.filters.support.FilterConstants;

import javax.servlet.http.HttpServletRequest;

@Component	//Remember to add component s to SPring for management
public class AuthenticFilter extends ZuulFilter {
    @Override
    public String filterType() {
        return FilterConstants.PRE_TYPE;
    }

    @Override
    public int filterOrder() {
        return FilterConstants.PRE_DECORATION_FILTER_ORDER - 2;//Check before
    }

    @Override
    public boolean shouldFilter() {
        return true;
    }

    @Override
    public Object run() throws ZuulException {
        //1. Get request object
        RequestContext currentContext = RequestContext.getCurrentContext();
        HttpServletRequest request = currentContext.getRequest();

        //2. Get token parameter
        String token = request.getParameter("token");
        //3. Comparison token
        if(token == null || !("123".equals(token))){//Suppose the token is 123
            //4. token verification failed. The data is directly responded to
            currentContext.setSendZuulResponse(false);//No response
            currentContext.setResponseStatusCode(HttpStatus.SC_UNAUTHORIZED);//401

        }
        return null;
    }
}

The above tokens are actually found through Redis or other tokens. Note that the Filter defined by yourself should use @ Component and be managed by Spring

Restart ZuulApplication, refresh the browser, and return 401 inaccessible HttpStatus.SC_UNAUTHORIZED is the 401 status code, indicating unauthorized and verification failure

If the token is correct, it will be released

3.3 degradation of zuul

By default, Zuul will include Hystrix dependencies, so there is no need to import Hystrix dependencies on degradation. The essence is that Zuul integrates Hystrix to achieve degradation and return the underlying data

Create ZuulFallBack for degradation

import org.springframework.cloud.netflix.zuul.filters.route.FallbackProvider;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.client.ClientHttpResponse;
import org.springframework.stereotype.Component;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;

@Component
public class ZuulFallBack implements FallbackProvider {
    @Override
    public String getRoute() {
        return "*"; //Specify this degradation method for all problematic services
    }

    @Override
    public ClientHttpResponse fallbackResponse(String route, Throwable cause) {//route represents the service name
        System.out.println("Degraded services:"+route);
        cause.printStackTrace();//Problem printing
        return new ClientHttpResponse() {
            @Override
            public HttpStatus getStatusCode() throws IOException {
                return HttpStatus.INTERNAL_SERVER_ERROR;    //INTERNAL_SERVER_ERROR(500, "Internal Server Error"),
            }

            @Override
            public int getRawStatusCode() throws IOException {
                return HttpStatus.INTERNAL_SERVER_ERROR.value(); //500 status code
            }

            @Override
            public String getStatusText() throws IOException {
                //Specify error message
                return HttpStatus.INTERNAL_SERVER_ERROR.getReasonPhrase();
            }

            @Override
            public void close() {

            }

            @Override
            public InputStream getBody() throws IOException {
                String msg = "Current service:"+ route + "´╝îSomething went wrong";
                return new ByteArrayInputStream(msg.getBytes());
            }

            @Override
            public HttpHeaders getHeaders() {
                //Specify response header information
                HttpHeaders headers = new HttpHeaders();
                headers.setContentType(MediaType.APPLICATION_JSON);
                return headers;
            }
        };
    }
}

Read the code above with comments
New sleep

Restart Customer related services (two), restart ZuulApplication service, and enter the address http://localhost:8001/v2/customer/version?token=123

The console will print the reason, Read timed out, because sleep

code:
Link: https://pan.baidu.com/s/1U3ebAqfIS-s_7gqqyuOSGQ
Extraction code: 233x

Keywords: Java Nginx Spring Boot Spring Cloud

Added by ixos on Wed, 06 Oct 2021 16:40:53 +0300