This article is excerpted from the book “Spring Cloud Microservice Introduction and Progress”. # 1. / routes endpoint

When @enableZuulProxy works with Spring Boot Actuator, Zuul exposes a ROUTING management endpoint /routes.

This endpoint makes it easy and intuitive to view and manage Zuul routes.

To expose all endpoints, add the following configuration:

management.endpoints.web.exposure.include=*
Copy the code

http://localhost:2103/actuator/routes can display all routing information:

{
  "/cxytiandi/**": "http://cxytiandi.com"."/hystrix-api/**": "hystrix-feign-demo"."/api/**": "forward:/local"."/hystrix-feign-demo/**": "hystrix-feign-demo"
}
Copy the code

2. / filters endpoint

The /fliters endpoint will return information about all filters in Zuul. You can clearly see which filters are available in Zuul and which are disabled.

http://localhost:2103/actuator/filters can display all filters information:

{
  "error": [{"class": "com.cxytiandi.zuul_demo.filter.ErrorFilter"."order": 100, 
      "disabled": false."static": true}]."post": [{"class": "org.springframework.cloud.netflix.zuul.filters.post.SendResponseFilter"."order": 1000, 
      "disabled": false."static": true}]."pre": [{"class": "com.cxytiandi.zuul_demo.filter.IpFilter"."order": 1, 
      "disabled": false."static": true}]."route": [{"class": "org.springframework.cloud.netflix.zuul.filters.route.RibbonRoutingFilter"."order": 10, 
      "disabled": false."static": true}}]Copy the code

3. Upload files

Create a new Maven project, Zuul-file-demo, and write a file upload interface, as shown in Listing 7-20.

Listing 7-20 File upload interface

@RestController
public class FileController {

  @PostMapping("/file/upload")
  public String fileUpload(@RequestParam(value = "file") MultipartFile file) throws IOException {
      byte[] bytes = file.getBytes();
      File fileToSave = new File(file.getOriginalFilename());
      FileCopyUtils.copy(bytes, fileToSave);
      returnfileToSave.getAbsolutePath(); }}Copy the code

Register the service with Eureka. The service name is zuul-file-demo. Use PostMan to upload files, as shown in Figure 7-4

As you can see, the interface returns the path of the uploaded file. Next, we will change to a larger file with a size of 1.7MB.

An error message is displayed (as shown in Figure 7-5). If the file is uploaded through Zuul, you need to configure the size of the uploaded file. Zuul and the uploaded service need to add the configuration:

spring.servlet.multipart.max-file-size=1000Mb
spring.servlet.multipart.max-request-size=1000Mb
Copy the code

After the configuration is added, upload it again, as shown in Figure 7-6.

The second solution is to prefix the gateway’s request address with /zuul to bypass the Spring DispatcherServlet for uploading large files.

# normal address
http://localhost:2103/zuul-file-demo/file/upload
# bypass address
http://localhost:2103/zuul/zuul-file-demo/file/upload
Copy the code

The zuul service does not need to configure the file upload size by prefixing it with /zuul, but the service that receives the file still needs to configure the file upload size, otherwise the file will fail to be uploaded.

It takes a long time to upload large files. In this case, you need to set a proper timeout period to avoid timeout.

ribbon.ConnectTimeout=3000
ribbon.ReadTimeout=60000
Copy the code

When the Hystrix isolation mode is thread zuul.ribbon- Isolation-strategy =thread, the Hystrix timeout period needs to be set.

hystrix.command.default.execution.isolation.thread.timeoutInMilliseconds=60000
Copy the code

4. Output request and response information

When a fault occurs in the production environment, the best way to troubleshoot the fault is to view logs. Log records are as detailed as possible, so that you can quickly locate the fault.

Let’s learn how to output request response information in Zuul to help us solve some problems.

Zuul has four types of filters, each of which has a specific usage scenario. To record response data, the request must be routed to a specific service and then returned. This requirement is suitable for post filters. This code is shown in Listing 7-21.

Listing 7-21 Zull gets the request information

HttpServletRequest req = (HttpServletRequest)RequestContext.getCurrentContext().getRequest();
System.err.println("REQUEST:: " + req.getScheme() + "" + req.getRemoteAddr() + ":" + req.getRemotePort());
StringBuilder params = new StringBuilder("?"); // Get the URL parameter Enumeration<String> names = req.getparameterNames ();if( req.getMethod().equals("GET")) {while (names.hasMoreElements()) {
         String name = (String) names.nextElement();
         params.append(name);
         params.append("=");
         params.append(req.getParameter(name));
         params.append("&"); }}if (params.length() > 0) {
    params.delete(params.length()-1, params.length());
}
        
System.err.println("REQUEST:: > " + req.getMethod() + "" + req.getRequestURI() + params + "" + req.getProtocol());
Enumeration<String> headers = req.getHeaderNames();
while (headers.hasMoreElements()) {
      String name = (String) headers.nextElement();
      String value = req.getHeader(name);
      System.err.println("REQUEST:: > " + name + ":"+ value); } final RequestContext ctx = RequestContext.getCurrentContext(); // Get the request body parametersif(! ctx.isChunkedRequestBody()) { ServletInputStream inp = null; try { inp = ctx.getRequest().getInputStream(); String body = null;if(inp ! = null) { body = IOUtils.toString(inp); System.err.println("REQUEST:: > "+ body); } catch (IOException e) { e.printStackTrace(); }}}Copy the code

The output is as follows:

The first way to get the response content is shown in Listing 7-22.

Listing 7-22 Getting the response content (1)

try {
     Object zuulResponse = RequestContext.getCurrentContext().get("zuulResponse");
     if(zuulResponse ! = null) { RibbonHttpResponse resp = (RibbonHttpResponse) zuulResponse; String body = IOUtils.toString(resp.getBody()); System.err.println("RESPONSE:: > " + body);
          resp.close();
          RequestContext.getCurrentContext().setResponseBody(body);
     }
} catch (IOException e) {
     e.printStackTrace();
}
Copy the code

The second way to get the response content is shown in Listing 7-23.

Listing 7-23 Getting the response content (2)

InputStream stream = RequestContext.getCurrentContext().getResponseDataStream();
try {
      if(stream ! = null) { String body = IOUtils.toString(stream); System.err.println("RESPONSE:: > " + body);
          RequestContext.getCurrentContext().setResponseBody(body);
      }    
} catch (IOException e) {
      e.printStackTrace();
}
Copy the code

Why can the above two methods get the response content?

The following code can be seen in The RibbonRoutingFilter or SimpleHostRoutingFilter, as shown in Listing 7-24.

Listing 7-24 gets the source code for the response content

public Object run() {
    RequestContext context = RequestContext.getCurrentContext();
    this.helper.addIgnoredHeaders();
    try {
        RibbonCommandContext commandContext = buildCommandContext(context);
        ClientHttpResponse response = forward(commandContext);
        setResponse(response);
        returnresponse; } catch (ZuulException ex) { throw new ZuulRuntimeException(ex); } catch (Exception ex) { throw new ZuulRuntimeException(ex); }}Copy the code

The forward() method calls the service, gets the response, and sets up the response through the setResponse() method, as shown in Listing 7-25.

Listing 7-25 setResponse(a)

protected void setResponse(ClientHttpResponse resp) throws ClientException, IOException {
    RequestContext.getCurrentContext().set("zuulResponse", resp);
    this.helper.setResponse(resp.getStatusCode().value(),
    resp.getBody() == null ? null : resp.getBody(), resp.getHeaders());
}
Copy the code

The first line of code above explains our first fetch method, which adds the response content directly to the RequestContext.

The second explanation is in the helper.setresponse logic, as shown in Listing 7-26.

Listing 7-26 setResponse(2)

 public void setResponse(int status, InputStream entity,
            MultiValueMap<String, String> headers) throws IOException {
    RequestContext context = RequestContext.getCurrentContext();
    context.setResponseStatusCode(status);
    if(entity ! = null) { context.setResponseDataStream(entity); } / /... }Copy the code

5. Zuul’s built-in Debug function

Zuul comes with a DebugFilter, at first I did not understand the use of the DebugFilter, see the name is easy to understand, used for debugging, but you can see its source code almost no logic, just set two values, as shown in Listing 7-27.

Listing 7-27 DebugFilter Run method

@Override
public Object run() {
    RequestContext ctx = RequestContext.getCurrentContext();
    ctx.setDebugRouting(true);
    ctx.setDebugRequest(true);
    return null;
}
Copy the code

To get this filter to execute, look at its shouldFilter() method, as shown in Listing 7-28. Listing 7-28 DebugFilter shouldFilter method

@Override
public boolean shouldFilter() {
    HttpServletRequest request = RequestContext.getCurrentContext().getRequest();
    if ("true".equals(request.getParameter(DEBUG_PARAMETER.get()))) {
      return true;
    }
    return ROUTING_DEBUG.get();
}
Copy the code

The filter can be turned on if either of two conditions are met, the first being if the request parameter has a parameter =true, which is obtained in the code shown in Listing 7-29.

Listing 7-29 DebugFilter enable parameter (1)

private static final DynamicStringProperty DEBUG_PARAMETER = DynamicPropertyFactory
      .getInstance().getStringProperty(ZuulConstants.ZUUL_DEBUG_PARAMETER, "debug");
Copy the code

DynamicStringProperty is an API provided by Archaius, the configuration management framework of Netflix. The configuration can be obtained from the configuration center. Since Netflix has no open source Archaius server, Therefore, the default value debug is used here. If you want to obtain this value dynamically, you can use Ctrip’s open source Apollo to interconnect with Archaius, which will be explained to you in the following chapters.

This filter can be enabled by appending debug=true to the request address. The parameter name debug can also be overridden in the configuration file, specified as zuul.debug.parameter, otherwise obtained from Archaius, which is the default if Archaius is not connected.

The second conditional code is shown in Listing 7-30.

Listing 7-30 DebugFilter enable parameters (2)

private static final DynamicBooleanProperty ROUTING_DEBUG = DynamicPropertyFactory
      .getInstance().getBooleanProperty(ZuulConstants.ZUUL_DEBUG_REQUEST, false);
  
Copy the code

Zuul.debug. request=true can be set in the configuration file to enable the DebugFilter.

After the DebugFilter was started, nothing happened. In the run method, debuacrylate and DebugRequest were just set to true. Then I continued to look at the source code and found that there was a code called Acrylate in many places. Such as com.net flix. Zuul. FilterProcessor. RunFilters (String), as shown in listing 7-31.

Listing 7-31 Adding debugging information

if (RequestContext.getCurrentContext().debugRouting()) {
    Debug.addRoutingDebug("Invoking {" + sType + "} type filters");
}
Copy the code

When the Debuacrylate is true some Debug information is added to the RequestContext. Now it is clear why debuacrylate and DebugRequest are set to true in the DebugFilter.

The log level is Debug, but the Debug information is only accumulated and stored in RequestContext, not shown to the user.

Continue to look at the code, everything comes to him who waits, in org.springframework.cloud.net flix. Zuul. Filters. Post. SendResponseFilter. AddResponseHeaders () this code saw hope. The code is shown in Listing 7-32.

Listing 7-32 Debug message Setting the response

private void addResponseHeaders() {
     RequestContext context = RequestContext.getCurrentContext();
     HttpServletResponse servletResponse = context.getResponse();
     if (this.zuulProperties.isIncludeDebugHeader()) {
         @SuppressWarnings("unchecked")
         List<String> rd = (List<String>) context.get(ROUTING_DEBUG_KEY);
         if(rd ! = null) { StringBuilder debugHeader = new StringBuilder();for (String it : rd) {
               debugHeader.append("The [[[" + it + "]]]"); } servletResponse.addHeader(X_ZUUL_DEBUG_HEADER, debugHeader.toString()); }}}Copy the code

The core code is this. ZuulProperties. IsIncludeDebugHeader (), only meet the conditions of the RequestContext debugging information as output response headers, in the configuration file configuration can increase the following:

zuul.include-debug-header=true
Copy the code

Finally, you can see the debugging content in the response header of the request, as shown in Figure 7-7.

This article is excerpted from the book “Spring Cloud Microservice Introduction and Progress”.