Springboot: 2.1.6. RELEASE

SpringCloud: Greenwich.SR1

Unless otherwise noted, the above versions are used for this series of tutorials

In the last article, we talked about the use of Zuul and the automatic forwarding mechanism, but there are many more uses of Zuul, such as authentication, traffic forwarding, request statistics, etc.

1. Zuul’s core

At Zuul’s core is the Filter, which enables control of external services. They are “PRE”, “ROUTING”, “POST”, and “ERROR”. The entire life cycle can be shown in the following figure.

Most of Zuul’s functionality is achieved through filters. There are four standard filter types defined in Zuul that correspond to the typical life cycle of a request.

  • PRE: This filter is invoked before the request is routed. We can use this filter to authenticate, select requested microservices in the cluster, log debugging information, and so on.

  • ROUTING: This filter routes the request to the microservice. This filter is used to build requests sent to microservices and request microservices using Apache HttpClient or Netfilx Ribbon.

  • OST: This filter is executed after routing to the microservice. Such filters can be used to add standard HTTP headers to responses, collect statistics and metrics, send responses from microservices to clients, and so on.

  • ERROR: This filter is executed when errors occur in other phases.

2. Filter implemented by default in Zuul

type The order The filter function
pre – 3 ServletDetectionFilter The tag handles the type of Servlet
pre 2 – Servlet30WrapperFilter Wrap the HttpServletRequest request
pre – 1 FormBodyWrapperFilter Wrapping request body
route 1 DebugFilter Debugging flag
route 5 PreDecorationFilter Process the request context for subsequent use
route 10 RibbonRoutingFilter ServiceId Forwards the request
route 100 SimpleHostRoutingFilter Url Request forwarding
route 500 SendForwardFilter Forward request forwarding
post 0 SendErrorFilter Handle request responses with errors
post 1000 SendResponseFilter Process normal request responses

2.1 Disabling the specified Filter

You can configure the filter to be disabled in application.yml in the following format:

zuul:
  FormBodyWrapperFilter:
    pre:
      disable: true
Copy the code

3. Customize Filter

To implement a custom Filter, you need to inherit ZuulFilter’s classes and override four of its methods.

package com.springcloud.zuulsimple.filter;

import com.netflix.zuul.ZuulFilter;
import com.netflix.zuul.exception.ZuulException;

/**
 * Created with IntelliJ IDEA.
 *
 * @User: weishiyao
 * @Date: 2019/7/6
 * @Time: 16:10
 * @email: [email protected]
 * Description:
 */
public class MyFilter extends ZuulFilter {
    @Override
    public String filterType() {
        return null;
    }

    @Override
    public int filterOrder() {
        return 0;
    }

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

    @Override
    public Object run() throws ZuulException {
        returnnull; }}Copy the code

4. Example for customizing Filter

We assume a scenario where the service gateway deals with all external requests. In order to avoid security risks, we need to make certain restrictions on requests. For example, if the request contains a Token, the request is allowed to continue, and if the request does not carry a Token, it is directly returned with a hint.

4.1 zuul – simple modification

First, put the zuul-Simple copy from the previous article into a new folder, define a Filter, and verify that the parameter contains the Token in the run() method.

package com.springcloud.zuulsimple.filter;

import com.netflix.zuul.ZuulFilter;
import com.netflix.zuul.context.RequestContext;
import com.netflix.zuul.exception.ZuulException;
import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.servlet.http.HttpServletRequest;

/**
 * Created with IntelliJ IDEA.
 *
 * @User: weishiyao
 * @Date: 2019/7/6
 * @Time: 16:11
 * @email: [email protected]
 * Description:
 */
public class TokenFilter extends ZuulFilter {

    private final Logger logger = LoggerFactory.getLogger(TokenFilter.class);

    @Override
    public String filterType() {
        return "pre"; } @override public int can be called before the request is routedfilterOrder() {
        return0; } @override public Boolean Specifies the filter execution order. The priority is 0. A larger number indicates a lower priorityshouldFilter() {
        return true; // Whether to execute the filter, istrueThat need to filter} @ Override public Object the run () throws ZuulException {RequestContext CTX. = the RequestContext getCurrentContext (); HttpServletRequest request = ctx.getRequest(); logger.info("--->>> TokenFilter {},{}", request.getMethod(), request.getRequestURL().toString());

        String token = request.getParameter("token"); // Get the request parametersif (StringUtils.isNotBlank(token)) {
            ctx.setSendZuulResponse(true); / / the request routing CTX. SetResponseStatusCode (200); ctx.set("isSuccess".true);
            return null;
        } else {
            ctx.setSendZuulResponse(false); / / not routed the CTX. SetResponseStatusCode (400); ctx.setResponseBody("token is empty");
            ctx.set("isSuccess".false);
            returnnull; }}}Copy the code

To add TokenFilter to the request interception queue, add the following code to the startup class:

@Bean
public TokenFilter tokenFilter() {
  return new TokenFilter();
}
Copy the code

This adds our custom Filter to the request interception.

4.2 test

CV Eureka and Producer into a new folder and start them one by one.

Open a browser, we visit: http://localhost:8080/spring-cloud-producer/hello? Name =spring, return: token is empty, request intercepted return.

Access the address: http://localhost:8080/spring-cloud-producer/hello? Name = Spring&Token =123, Hello Spring, producer is ready, indicating that the request is properly responded.

From the above example, we can see that we can use the Filter of “PRE” type to do a lot of verification work. In actual use, we can combine shiro, OAuth2.0 and other technologies to do authentication and verification.

5. The route is fused

When an exception occurs in our back-end service, we do not want to throw the exception to the outermost layer, expecting the service to be degraded automatically. Zuul provides us with such support. When a service fails, the default information is returned.

We use a custom fallback method and assign it to a route to implement the circuit breaker handling for the route access failure. Main inheritance FallbackProvider interface to achieve, FallbackProvider default has two methods, one used to specify which service to intercept, a custom return content.

/* * Licensed under the Apache License, Version 2.0 (the"License");
 * you may not use this file except inThe compliance with the License. * You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 * * * * Unless required by applicable law or agreed toin writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.springframework.cloud.netflix.zuul.filters.route;

import org.springframework.http.client.ClientHttpResponse;

/**
 * Provides fallback when a failure occurs on a route.
 *
 * @author Ryan Baxter
 * @author Dominik Mostek
 */
public interface FallbackProvider {

	/**
	 * The route this fallback will be used for.
	 * @return The route the fallback will be used for.
	 */
	String getRoute();

	/**
	 * Provides a fallback response based on the cause of the failed execution.
	 * @param route The route the fallback is for
	 * @param cause cause of the main method failure, may be <code>null</code>
	 * @return the fallback response
	 */
	ClientHttpResponse fallbackResponse(String route, Throwable cause);

}

Copy the code

The implementation class tells Zuul which route definition it is responsible for by implementing the getRoute method. The fallbackResponse method tells Zuul what return value it will provide to process the request when a break occurs.

Let’s take the spring-Cloud-Producer service above as an example and customize its circuit breaker return content.

package com.springcloud.zuulsimple.component; import org.slf4j.Logger; import org.slf4j.LoggerFactory; 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 java.io.ByteArrayInputStream; import java.io.IOException; import java.io.InputStream; /** * Created with IntelliJ IDEA. * * @User: weishiyao * @Date: 2019/7/6 * @Time: 16:25 * @email: [email protected] * Description: */ @Component public class ProducerFallback implements FallbackProvider { private final Logger logger = LoggerFactory.getLogger(FallbackProvider.class); // Specify the service to process. @Override public StringgetRoute() {
        return "spring-cloud-producer";
    }

    public ClientHttpResponse fallbackResponse() {
        return new ClientHttpResponse() {
            @Override
            public HttpStatus getStatusCode() throws IOException {
                return HttpStatus.OK;
            }

            @Override
            public int getRawStatusCode() throws IOException {
                return 200;
            }

            @Override
            public String getStatusText() throws IOException {
                return "OK";
            }

            @Override
            public void close() {

            }

            @Override
            public InputStream getBody() throws IOException {
                return new ByteArrayInputStream("The service is unavailable.".getBytes());
            }

            @Override
            public HttpHeaders getHeaders() {
                HttpHeaders headers = new HttpHeaders();
                headers.setContentType(MediaType.APPLICATION_JSON);
                returnheaders; }}; } @Override public ClientHttpResponse fallbackResponse(String route, Throwable cause) {if(cause ! = null && cause.getCause() ! = null) { String reason = cause.getCause().getMessage(); logger.info("Excption {}",reason);
        }
        returnfallbackResponse(); }}Copy the code

If The service is abnormal, The system displays The exception information and The message The Service is unavailable.

Note that we need to modify the Eureka configuration file:

server:
  port: 8761
spring:
  application:
    name: eureka-serve
eureka:
# server:
# enable-self-preservation: false
  client:
    register-with-eureka: false
    service-url:
      defaultZone: http://localhost:8761/eureka/
Copy the code

Turn on the self-protection mode of Eureka. If the self-protection mode is not enabled, as soon as the producer stops serving Eureka, the service will be directly offline. Zuul will report an error that the corresponding producer service cannot be found.

We start all three services in turn.

Now open a browser and access the link: http://localhost:8080/spring-cloud-producer/hello? Name =spring&token=123 hello spring, producer is ready. The service is unavailable. In this way, our fusing test was successful.

6. Zuul is highly available

The way we actually use Zuul is shown in the figure above. Different clients use different loads to distribute requests to Zuul on the back end, and Zuul calls the back end service through Eureka, and finally outputs. Therefore, in order to ensure high availability of Zuul, the front-end can start multiple Zuul instances at the same time for load, and use Nginx or F5 for load forwarding on Zuul’s front-end to achieve high availability.

Example code -Github

Reference: www.ityouknow.com/springcloud…