Spring cloud: service gateway zuul API gateway

Posted by Iceman512 on Mon, 04 Nov 2019 10:43:58 +0100

What is an API gateway:

In a microservice architecture, there are usually multiple service providers. Imagine an e-commerce system, which may have multiple types of services, such as goods, orders, payments, users, etc., and the number of services of each type will also grow and change with the increase of the whole system volume. As the UI side, it may be necessary to aggregate data from multiple microservices when presenting the page, and the division structure of services may change. The gateway can expose the aggregation API to the outside, shield the small changes of the internal micro service, and maintain the stability of the whole system.

Zuul is the microservice API gateway in Spring Cloud's family bucket.

All requests from devices or websites go through zuul to the back-end Netflix applications. As a boundary application, zuul provides dynamic routing, monitoring, elastic load and security functions. Zuul bottom layer uses various filter s to realize the following functions:

  • Authentication and security identify each resource to be authenticated and reject requests that do not meet the requirements.
  • Performance monitoring tracks and counts data at service boundaries, providing accurate production views.
  • Dynamic routing dynamically routes requests to back-end clusters as needed.
  • Stress testing gradually increases the flow to the cluster to understand its performance.
  • Load unload pre allocates capacity for each type of request, and automatically discards when the request exceeds the capacity.
  • Static resource processing returns some responses directly at the boundary.

In the Spring Cloud microservice system, a common way of load balancing is that the client's request first goes through the load balancing (Ngnix), then to the service gateway (zuul cluster), and then to the specific service. Services are registered to the highly available service registry cluster (Eureka, consumer). All configuration files of services are managed by the configuration service. The configuration files of the configuration service are placed in the git warehouse, which is convenient for developers to change the configuration at any time.
--------

2. Dynamic routing

 

 

 pom.xml:

<dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-eureka</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-zuul</artifactId>
        </dependency>
    </dependencies>

application.properties:

spring.application.name=service-zuul
server.port=8061
## Configuration of registration service center
eureka.client.service-url.defaultZone=http://localhost:8001/eureka/
#zuul.routes.<route>.path Configure the path to intercept requests
#zuul.routes.<route>.serviceId Configure the specified eureka service
#In addition to combining eureka Services, specifying serviceId Using, you can also specify as a url Address, such as zuul.routes.hello-service.path=http://localhost:8011
zuul.routes.user-service.path=/user-service/**
zuul.routes.user-service.serviceId=USER-SERVICE
# Note above, change to user-service.url to visit Baidu directly
#zuul.routes.user-service.url=http://pay.weixin.qq.com/partner/public/home
#zuul.routes.user-service.url=http://localhost:8011

Zuul.routes. < route >. Path and zuul.routes. < route >. Serviceid respectively configure the path of zuul interception request and the specified eureka service to be routed after interception

In addition to combining eureka service and specifying serviceId, you can also specify a url address, such as zuul. Routes. Hello service. Path = http: / / localhost: 8011

application.yml:

 

##timeout config
hystrix:
  command:
    default:
      execution:
        timeout:
          enabled: true
        isolation:
          thread:
            timeoutInMilliseconds: 60000
ribbon:
  ReadTimeout: 60000
  ConnectTimeout: 60000
  MaxAutoRetries: 0
  MaxAutoRetriesNextServer: 1
  eureka:
    enabled: true

zuul:
  max:
    host:
      connections: 500
  host:
    socket-timeout-millis: 60000
    connect-timeout-millis: 60000

Start class Application.java:

package cn.demo;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
import org.springframework.cloud.netflix.zuul.EnableZuulProxy;

@SpringBootApplication
@EnableZuulProxy
@EnableEurekaClient
public class ServiceZuulApplication {

    public static void main(String[] args) {
        SpringApplication.run(ServiceZuulApplication.class, args);
    }
}

Here @ EnableZuulProxy is used to open zuul gateway.

@In order to integrate eureka, enable eureka client calls the services registered in eureka, so zuul is also the client of eureka. Of course, @ EnableDiscoveryClient can also be used here. It can be found that the @ EnableEurekaClient annotation implementation contains @ EnableDiscoveryClient. If it is only used to call the eureka service, both can be used. If you want to use other services, such as consumer, you can only use @ EnableDiscoveryClient.

Test:

Start Eureka: 8001, hello service: 80118012, zuul service: 8061

Visit: http: / / localhost: 8061 / user service / Hello? Name = zuul

 

 

Indicates that the route is successful. Moreover, repeated access can also find that ribbon load balancing is used by default.

Next, let's change it to:

zuul.routes.user-service.path=/user-service/**
zuul.routes.user-service.url=http://localhost:8011

Similarly, visit: http: / / localhost: 8061 / Hello service / Hello? Name = zuul

 

 

Of course, if we change the connection to Baidu website, then we will directly jump to Baidu.   

Since zuul is used in the spring cloud ecosystem, it is better to use it in combination with eureka ribbon.

III. gateway filtering - (login, permission verification)
If in the whole system, each microservice manages the user state by itself, it is obviously not desirable, so it is generally placed in the service gateway. Then we need to process the user login status and whether to release the user request in the service gateway.

Here, we implement zuul gateway filter to obtain access_token in each interface parameter, judge whether it is legal or not, if it is legal, it will release, if it is illegal, it will intercept and prompt error.

package cn.demo.filter;
import com.netflix.zuul.ZuulFilter;
import com.netflix.zuul.context.RequestContext;
import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;

import javax.servlet.http.HttpServletRequest;

/**
 * Service request filter
 */
@Component
public class AccessFilter extends ZuulFilter {

    private static Logger log = LoggerFactory.getLogger(AccessFilter.class);

    //Before routing
    @Override
    public String filterType() {
        return "pre";
    }

    //Order of filtering
    @Override
    public int filterOrder() {
        return 0;
    }

    //Here you can write logic judgment, whether to filter, this article true,Filtering forever
    @Override
    public boolean shouldFilter() {
        return true;
    }

    @Override
    public Object run() {
        RequestContext requestContext = RequestContext.getCurrentContext();
        HttpServletRequest request = requestContext.getRequest();

        log.info("{} >>> {}", request.getMethod(), request.getRequestURL().toString());

        String access_token = request.getParameter("access_token");
        if(StringUtils.isBlank(access_token) || !"test".equals(access_token)){
            // zuul Filter the request
            requestContext.setSendZuulResponse(false);//Indicates that the request will not be forwarded.
            requestContext.setResponseStatusCode(401);//Status code returned, 401 here
            requestContext.setResponseBody("token invide invalid");//The returned content can be specified as a string json
            log.info("the request {} is fail, the token is invide invalid", request.getRequestURL().toString());
        } else {
            log.info("the request {} is ok", request.getRequestURL().toString());
        }
        return null;
    }
}

filterType: returns a string representing the filter type. In zuul, four filter types with different life cycles are defined, as follows:

pre: before routing
Routing: routing time
post: after routing
Error: send error call
filterOrder: order of filtering

shouldFilter: Here you can write logic judgment to determine whether you want to filter. This article is true. Filter forever.

run: the specific logic of the filter. It can be very complex, including querying sql and nosql to determine whether the request has permission or not.

The filterType: pre specified above indicates that the request is intercepted before routing. The shouldFilter is always true, indicating that the request is always filtered, and the run method is executed.

Requestcontext.setsendzuullresponse (false); indicates that the request will not be forwarded.
requestContext.setResponseStatusCode(401); the returned status code, here is 401
requestContext.setResponseBody("token is invalid"); the returned content can be specified as a string of json

test
Restart zuul service: 8061

Visit: http: / / localhost: 8061 / Hello service / Hello? Name = zuul

Browser back to 401

 

 

Console console log output:

 

 

Next, we visit: http: / / localhost: 8061 / Hello service / Hello? Name = zuul & access_token = Test

 

 

Indicates verification filtering and release request.

IV. request life cycle

From the figure, we can see that when an external HTTP request arrives at the API gateway service, it will first enter the first stage of pre, where it will be processed by a filter of pre type. The main purpose of this type of filter is to do some pre processing before the request routing, such as request verification. After completing the pre type filter processing, the request enters the second stage of routing, that is, the routing request forwarding stage, where the request will be processed by the routing type filter. The specific processing content here is the process of forwarding the external request to the specific service instance. When the service instance returns the request results, the routing stage is completed, and the request is processed In the third stage, post, the request will be processed by the filter of post type. These filters can not only obtain the request information, but also the return information of the service instance. Therefore, in the filter of post type, we can process or transform the processing results. In addition, there is a special stage error, which is triggered only when there is an exception in the above three stages, but its final flow is still a post type filter, because it needs to return the final result to the requesting client through the post filter

 

 

 

Topics: Java Spring JSON git xml