SpringCloud version Hoxton SR5 - Lecture 5: zuul routing, filtering, fault tolerance and fallback, clustering, high availability

Port: SpringCloud version Hoxton SR5 - Lesson 1: Understanding First look at what Zuul can do, or his positioning and role in the project.

The last article mainly talks about: Functions and functions are in colloquial, mainly easy to understand. It is recommended to have a look first.

 

As mentioned in the previous article, routing, filtering, fault tolerance and fallback are three functions.

There are few explanations here; almost every configuration is commented on.

Zuul's cluster description follows.

 

To put it simply: What is zuul?

Zuul contains the two most important functions of routing and filtering requests:

The routing function is responsible for forwarding external requests to specific micro-service instances, which is the basis for achieving unified access to external services, while the filter function is responsible for intervening in the processing of requests, which is the basis for implementing such functions as request verification, service aggregation and so on.

Zuul integrates with Eureka, registering Zuul as an application under Eureka Service Governance, and getting messages from other microservices from Eureka, i.e. subsequent access to microservices is acquired through a Jump from Zuul.

Note: The Zuul service will eventually be registered with Eureka.

 

Single machine version:

Dependency: springboot: 2.2.8.BUILD-SNAPSHOT

    <properties>
        <java.version>1.8</java.version>
        <spring-cloud.version>Hoxton.SR5</spring-cloud.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-zuul</artifactId>
        </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>
To configure:

#################################################### Basic Public Configuration #######################################################
server:
  port: 7004  # Configure Project Access Port
  tomcat:
    uri-encoding: UTF-8   # Configure tomcat startup encoding
    max-threads: 1000     # Configure tomcat maximum access default 200
    min-spare-threads: 30 # Configure tomcat minimum access default 10


#################################################### eureka Client Configuration ####################################################
eureka: # Note that the following time settings take effect when the network is unstable, but if you manually close the client instance of eureka, you will send information directly to the Eureka server and close the service registered in Eureka
  client:
    serviceUrl:
      defaultZone: http://localhost:6004/eureka/#Register address provided by the Eureka server for single machine configuration, referring to defaultZone property value configured by the server (that is, register the Eureka client with the Eureka server)
      #      defaultZone: http://wangqinmin.com:6001/eureka#Cluster configuration I recommend this, but self-registration needs to be turned on in the server-side configuration, which is also turned on by default.
      #      defaultZone: http://wangqinmin.com:6001/eureka,http://127.0.0.1:6002/eureka,http://localhost:6003/eureka #cluster configuration
      # Register the client side of this project into all the servers, in order to prevent the server from not registering itself
      #(Normally, this is not a problem, even if some people don't know eureka and randomly configure cluster servers, so this is the safest way to do it. If you follow the configuration of my servers, this will not happen.)
      # The reason for the problem is that the self-registration configuration is turned off manually.
      # Another disadvantage of this configuration is that services that are not connected will continuously fail in the log
  instance:
    instance-id: client-zuul-1 #This instance is registered with the unique instance ID of the eureka server (that is, it assigns an ID to the current project, which is registered with the eureka server when the current project is running)
    prefer-ip-address: true #Whether to display IP address
    leaseRenewalIntervalInSeconds: 30 #How long does a eureka client need to send a heartbeat to the Eureka server to indicate that it is still alive, defaulting to 30 seconds (in seconds as configured below)
    leaseExpirationDurationInSeconds: 90 #How long does the Eureka server wait to delete an instance after receiving its last heartbeat, defaulting to 90 seconds

spring:
  application:
    name: zuul #The instance is registered with the name of the eureka server (in cluster mode: microservices with the same functionality must have the same name, since all microservices are invoked directly with this name when load balancing is invoked)


#################################################### zuul To configure ####################################################
zuul:
  prefix: /api # Set an access address prefix.
#  strip-prefix: false # (Default true) When prefix is configured to define a prefix, it is prefixed with / api for each access, but there is a problem: the / api prefix appears on the prefix of the microservice call interface.If set here to true, the prefix on the microservice call interface will be cleared, so false is configured here, not cleared
  ignored-services: "*"  # The use of service names to invoke microservices is prohibited.You can also configure a single microservice name: server-order
  routes:
    myitem:  # A custom name for a microservice that needs routing
      serviceId: server-item  # Configure a service ID to route and get it in the Eureka registry.
      path: /item/**  # /** indicates that access can succeed regardless of the hierarchy behind it.
    myorder:   # A custom name for a microservice that needs routing
      serviceId: server-order  # Configure a service ID to route and get it in the Eureka registry.
      path: /order/**
    mylogin:
      serviceId: server-login
      path: /login/**

Code startup:

package com.springcloud;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.zuul.EnableZuulProxy;

/**
 * @author: wangqinmin
 * @date : 2020/6/14
 * <p>
 *
 * There is no need to annotate @EnableEurekaClient here because zuul integrates itself.
 * zuul You will also integrate Hystrix and Ribbon integration yourself.
 *
 * @EnableZuulProxy Simply understood as an enhanced version of @EnableZuulServer,
 * When Zuul is used with components like Eureka and Ribbon, we use @EnableZuulProxy
 */
@SpringBootApplication
@EnableZuulProxy
//@EnableZuulServer
public class ZuulServerApplication {


    /**
     * When only eureka and port number are configured for zuul's yml, zuul can use:
     * yml Other configurations in are also commented on.
     * The main functions are routing.
     */
    public static void main(String[] args) {
        SpringApplication.run(ZuulServerApplication.class);
    }
}

Configure zuul custom filters (see requirements)

package com.springcloud.config;

import com.netflix.zuul.ZuulFilter;
import com.netflix.zuul.context.RequestContext;
import com.netflix.zuul.exception.ZuulException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.cloud.netflix.zuul.filters.support.FilterConstants;
import org.springframework.stereotype.Component;

/**
 * @author: wangqinmin
 * @date : 2020/6/14
 * @description: When I go out laughing to heaven, am I a Artemisia annua
 * <p>
 * <p>
 * Filter is the core component of zuul. Most of the functions of zuul are implemented through filters.
 * <p>
 * zuul Four standard filter types are defined in, which correspond to the typical life cycle of a request as follows:
 * PRE: This filter is called before the request is routed.This filter can be used to authenticate, select the requested microservice in the cluster, record debugging information, and so on.
 * ROUTING: This filter routes requests to the microservice.This filter is used to construct requests to microservices and request microservices using Apache HttpCIient or Netfilx Ribbon
 * POST:This filter is executed after routing to the microservice.This filter can be used to add standard HTTP headers for responses, collect statistics and metrics, send responses from microservices to clients, and so on.
 * ERROR: Execute this filter when errors occur at other stages.
 */
@Component
public class LoggerFilter extends ZuulFilter {

    Logger logger = LoggerFactory.getLogger(LoggerFilter.class);

    /**
     * Here is the type of filter to select:
     * Whoever you use depends on which lifecycle you need to modify your code.
     *
     * @return
     */
    @Override
    public String filterType() {
        return FilterConstants.PRE_TYPE;
    }

    /**
     * Priority of the filter: The smaller the number, the higher the priority.
     * For example, if I write two filters of type PRE, this specifies who to execute first.
     * Of course, if the filter types are different, you should follow the four life cycle sequences in the zuul filter.
     *
     * @return
     */
    @Override
    public int filterOrder() {
        // return 0;
        /**
         * The default pre level is 5:
         * What does that mean?
         * If you want to add new functionality, consider whether to execute the zuul default filter before or after it is executed.
         *
         * I write code here myself as a test, so I get the ip address and print the log.
         * ip The address was acquired by zuul himself, which happened to be saved when zuul's filter was executed.
         * So my priority is + 1 by default.
         */
        return FilterConstants.PRE_DECORATION_FILTER_ORDER + 1;
    }

    /**
     * Is life enabled for the current filter (default false)
     *
     * @return
     */
    @Override
    public boolean shouldFilter() {
        return true;
    }

    /**
     * Here is the specific filtering logic.
     * Did you find that no request was passed in here?So how do we write filtering logic?
     * <p>
     * In the source code, there are: RequestContext currentContext =RequestContext.getCurrentContext() this code,
     * In the thread, this exists, all can be taken directly.There's almost everything we need.
     * The filter was written in the source code, but he put all the information into the RequestContext.
     *
     * @return
     * @throws ZuulException
     */
    @Override
    public Object run() throws ZuulException {
        RequestContext currentContext = RequestContext.getCurrentContext();
        String requestURI = currentContext.getRequest().getRequestURI();
        String remoteAddr = currentContext.getRequest().getRemoteAddr();
        logger.warn("request URI Address:[{}],Remote Access Address:{}", requestURI, remoteAddr);
// 2020-06-14 23:36:34.506 WARN 53856 --- [nio-7004-exec-1]Com.springcloud.config.LoggerFilter: Request URI address: [/api/login/getFeignOrderInfo], Remote Access address: 0:0:0:0:0:0:0:1
        return null;
    }
}

Configuring zuul's fault tolerance and fallback (which is almost a downgrade function) is just a separate class that overrides the downgrade method.

package com.springcloud.config;

import com.netflix.hystrix.exception.HystrixTimeoutException;
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;

/**
 * @author: wangqinmin
 * @date : 2020/6/15
 * @description: When I go out laughing to heaven, am I a Artemisia annua
 */
@Component
public class ZuulFallbackProvide implements FallbackProvider {
    @Override
    public String getRoute() {
        //Decide which microservice to provide a fallback for (write the microservice name here * on behalf of all microservices)
        return "*";
    }

    /**
     * This method needs to return a ClientHttpResponse object, ClientHttpResponse is an interface to which the specific fallback logic implements
     *
     * @param route Microservice name in error
     * @param cause Error Exception
     * @return
     */
    @Override
    public ClientHttpResponse fallbackResponse(String route, final Throwable cause) {
        //Here you can decide whether to handle different anomalies or not
        //When finished, call the response method and pass in HttpStatus based on the exception type
        if (cause instanceof HystrixTimeoutException) {
            return response(HttpStatus.GATEWAY_TIMEOUT); // 504
        } else {
            return response(HttpStatus.INTERNAL_SERVER_ERROR); // 500
        }
    }


    /**
     * This is written by you and can be defined by you.
     *
     * @param status
     * @return
     */
    private ClientHttpResponse response(final HttpStatus status) {
        //This returns a ClientHttpResponse object and implements its methods. Detailed fallback logic is described in the following methods
        return new ClientHttpResponse() {
            @Override
            public HttpStatus getStatusCode() throws IOException {
                //Returns an HttpStatus object. This object is an enumeration object and contains a status code and reasonPhrase information
                return status;
            }

            @Override
            public int getRawStatusCode() throws IOException {
                //Return status code s such as 404, 500, etc.
                return status.value();
            }

            @Override
            public String getStatusText() throws IOException {
                //Returns reasonPhrase information for an HttpStatus object
                return status.getReasonPhrase();
            }

            @Override
            public void close() {
                //The method invoked when close, which is simply the method invoked when the downgrade information has all responded
            }

            /**
             * When testing here, I did not open the item's microservice and called the item's address directly:
             * http://localhost:7004/api/item/query
             *
             * The page then returns the downgrade information configured here.
             * @return
             * @throws IOException
             */
            @Override
            public InputStream getBody() throws IOException {
                //Respond demotion information back to front
                return new ByteArrayInputStream("Downgrade information, system busy, try again later".getBytes());
            }

            @Override
            public HttpHeaders getHeaders() {
                //You can set it here if you need to set the response header
                HttpHeaders headers = new HttpHeaders();
                headers.setContentType(MediaType.APPLICATION_JSON);
                return headers;
            }
        };
    }
}

 

When you use SpringCloud, you must have a question.

How do we call microservices when there was no Zuul before?

For instance:

User Micro Services Configuration of 3 Machines

Order Micro Services Configuration of 3 Machines

For example, our users must log in first. After successful login, they can query my user's order information.

Think about registering User, Order when using Eureka.

There is a Ribbon load balancing configuration in User, at which time the User micro-service calls the Order micro-service internally, and the Order micro-service is called, using load balancing.

But User is not load balanced, right?User has three services. How do I call three Users'IPS with one address?

User has three machines, such as 192.168.2.11, 192.168.2.12, 192.168.2.13, respectively.

So the front-end call has a problem, I can call with all three ip s.But there is no load balancing function.

 

There will be a problem, for example, if I don't use nginx as the reverse proxy, I use zuul as the proxy.

This solves the above problem, you can call zuul directly, then route zuul, but then the problem comes again.

 

For example, there are many services, one zuul is not enough. For high availability, I want to make a zuul cluster.

Next, you configure three zuul servers.Three zuul at this time?Which zuul do I call to be truly highly available and load balanced?

 

So in this case, you need another zuul, which routes the three zuuls.Another solution is to use nginx as a reverse proxy for the three zuul.

I haven't tested it either, zuul or nginx, who does the gateway routing and who does the best.

However, there are many people on the network doing this kind of test, and the result is:

Actually, they are almost the same, basically, if the server performance is good, java will run fast, and zuul will visit faster after preheating.

So the choice between nginx and zuul depends on which one you like. After all, there is no clear boundary between them.

 

Next, take the cluster: Route two zuuls with zuul (for a long time to route the cluster zuul with zuul, I don't know why it is not possible).I succeeded in autism.)

But it's certainly possible.I'll find a solution again.

Or use nginx as the reverse proxy.Nginx is still simple to use.No more configuration files will be pasted.

The zuul configuration of the cluster is the same, the only difference is:

Access port numbers are different.

instance-Id: Configure your own name, but not the same.

Below Spring.application.name Must be the same.All other configurations are the same, just start.

#################################################### Basic Public Configuration #######################################################
server:
  port: 7002  # Configure Project Access Port


#################################################### eureka Client Configuration ####################################################
eureka: # Note that the following time settings take effect when the network is unstable, but if you manually close the client instance of eureka, you will send information directly to the Eureka server and close the service registered in Eureka
  client:
    serviceUrl:
      defaultZone: http://localhost:6004/eureka/#Register address provided by the Eureka server for single machine configuration, referring to defaultZone property value configured by the server (that is, register the Eureka client with the Eureka server)
      #      defaultZone: http://wangqinmin.com:6001/eureka#Cluster configuration I recommend this, but self-registration needs to be turned on in the server-side configuration, which is also turned on by default.
      #      defaultZone: http://wangqinmin.com:6001/eureka,http://127.0.0.1:6002/eureka,http://localhost:6003/eureka #cluster configuration
      # Register the client side of this project into all the servers, in order to prevent the server from not registering itself
      #(Normally, this is not a problem, even if some people don't know eureka and randomly configure cluster servers, so this is the safest way to do it. If you follow the configuration of my servers, this will not happen.)
      # The reason for the problem is that the self-registration configuration is turned off manually.
      # Another disadvantage of this configuration is that services that are not connected will continuously fail in the log
  instance:
    instance-id: client-zuul-2 #This instance is registered with the unique instance ID of the eureka server (that is, it assigns an ID to the current project, which is registered with the eureka server when the current project is running)
    prefer-ip-address: true #Whether to display IP address
    leaseRenewalIntervalInSeconds: 30 #How long does a eureka client need to send a heartbeat to the Eureka server to indicate that it is still alive, defaulting to 30 seconds (in seconds as configured below)
    leaseExpirationDurationInSeconds: 90 #How long does the Eureka server wait to delete an instance after receiving its last heartbeat, defaulting to 90 seconds

spring:
  application:
    name: server-zuul #The instance is registered with the name of the eureka server (in cluster mode: microservices with the same functionality must have the same name, since all microservices are invoked directly with this name when load balancing is invoked)


#################################################### zuul To configure ####################################################
zuul:
#  prefix: /api # Set an access address prefix.
#  strip-prefix: false # (Default true) When prefix is configured to define a prefix, it is prefixed with / api for each access, but there is a problem: / api prefix appears on the prefix of the microservice invocation interface. If set to true here, the prefix on the microservice invocation interface will be cleared, so configure false here, not clear
  ignored-services: '*'  # The use of service names to invoke microservices is prohibited.You can also configure a single microservice name: server-order
  routes:
    mylogin:
      serviceId: server-login
      path: /login/**
    myitem:  # A custom name for a microservice that needs routing
      serviceId: server-item  # Configure a service ID to route and get it in the Eureka registry.
      path: /item/**  # /** indicates that access can succeed regardless of the hierarchy behind it.
    myorder:   # A custom name for a microservice that needs routing
      serviceId: server-order  # Configure a service ID to route and get it in the Eureka registry.
      path: /order/**

 

Authentic.

 

 

Tags: Spring Java Nginx Tomcat

Posted on Thu, 18 Jun 2020 21:06:10 -0400 by PromaneX