Spring cloud introductory series six: use Zuul to implement API gateway service

 

Through the previous sharing, we have learned about several core facilities of the microservice architecture, through which we can build a simple microservice architecture system. For example, using Spring Cloud Eureka to build a highly available service registry and realize service registration and discovery;

Load balancing through Spring Cloud Ribbon or Feign; service fault tolerance protection through Spring Cloud Hystrix to avoid failure spread. After the microservice is built, we will definitely provide some unified RESTFul API service interfaces to the external system for calling.

But when an external system calls our RESTful API, how do we determine which service provides the functionality it needs? This involves the maintenance of a routing rule and a list of service instances.

This introduces our protagonist today - Spring Cloud Zuul, which is an API gateway component based on Netflix Zuul. It can solve two big problems:

  1. It is the maintenance problem of routing rules and service instances mentioned above.
  2. For some verification (such as login verification, etc.) redundancy problems. According to our customary practice, these checks need to be added to each service, but this will lead to redundant code and more troublesome maintenance. With the gateway service of Spring Cloud Zuul, we can integrate these common checks. Check the gateway for unified maintenance.

Ok, let's take a look at how to implement this gateway service.

1. Build a gateway and configure routing

 Here we still need to use the previous hello-service and feign-consumer services. We used feign-consumer as a service consumer before, but don't forget that in the eureka system, each service is both a service provider and a service consumer, so feign-consumer is also a service provider, and http://localhost: Interfaces such as 9001/feign-consumer are the services it provides.

Next we build a gateway service, the code structure is as follows:

  

Code implementation steps:

  1. New maven project api-gateway
  2. Modify the POM file
    <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
      <modelVersion>4.0.0</modelVersion>
      <groupId>com.sam</groupId>
      <artifactId>api-gateway</artifactId>
      <version>0.0.1-SNAPSHOT</version>
      
      <parent>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-parent</artifactId>
            <version>1.5.1.RELEASE</version>
        </parent>
    
        <properties>
            <javaVersion>1.8</javaVersion>
        </properties>
        <!-- 使用dependencyManagement进行版本管理 -->
        <dependencyManagement>
            <dependencies>
                <dependency>
                    <groupId>org.springframework.cloud</groupId>
                    <artifactId>spring-cloud-dependencies</artifactId>
                    <version>Camden.SR6</version>
                    <type>pom</type>
                    <scope>import</scope>
                </dependency>
            </dependencies>
    
        </dependencyManagement>
    
        <dependencies>
            <!-- 引入zuul依赖 , 它依赖了spring-boot-starter-actuator/spring-boot-starter-hystrix/spring-boot-starter-ribbon-->
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-starter-zuul</artifactId>
            </dependency>
    
        </dependencies>
        
    </project>
  3. New startup class
    /**
     * @EnableZuulProxy enables Zuul's API gateway service function
     *
     */
    @EnableZuulProxy
    @SpringCloudApplication
    public class GateWayApp {
    
        public static void main(String[] args) {
            SpringApplication.run(GateWayApp.class, args);
        }
    }
  4. New application.properties
    server.port=5555
    spring.application.name=api-gateway
    
    #Add the configuration of routing rules
    #Configure through zuul.routes. <route>.path and zuul.routes.<route> .url, <route> is the name of the route, which can be specified arbitrarily, but the route names of a group of paths and urls should be the same
     # as follows Example: All accesses that meet the /api-a/** rules will be routed to the address of //localhost:9001
    #That is to say, when we visit http://localhost:5555/api-a/hello, the API Gateway service will route the request to the microservice interface provided by http://localhost :9001/hello 
    zuul.routes.api-a.path=/api-a/**
    zuul.routes.api-a.url=http://localhost:9001
    
    
    zuul.routes.api-b.path=/api-b/**
    zuul.routes.api-b.url=http://localhost:9090

     

  5. Test, start eureka, hello-service, feign-consumer and the newly added api-gateway service, and then visit http://localhost:5555/api-a/feign-consumer

    Successfully accessed the service interface of feign-consumer -- feign-consonsumer.

The above steps realize the configuration of traditional routing . This configuration has a big disadvantage, that is, routing rules need to be manually configured in the application.properties file. When there are many services, the maintenance workload will be very large. In order to reduce maintenance costs, there is another kind of routing - service-oriented routing.

2. Service-Oriented Routing

Spring Cloud Zuul is integrated with Eureka. We can make the path of the route not map a specific url, but a specific service, and the url of the service is automatically maintained by the Eureka service discovery mechanism. This type of route is a service-oriented route. . The specific code configuration is as follows:

  1. Modify the POM file and introduce Eureka dependencies
    <!-- 引入eureka依赖 -->
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-starter-eureka</artifactId>
            </dependency>
  2. Modify the application.properties configuration file
    server.port=5555
    spring.application.name=api-gateway
    
    zuul.routes.api-a.path=/api-a/**
    
    #Here, use serviceId instead of url, and use service name instead of ip+port number
    zuul.routes.api-a.serviceId=hello-service

    eureka.client.service-url.defaultZone=http://localhost:1111/eureka

    Note: zuul.routes.api-a.url = hello-service can also achieve the function, but it cannot perform normal load balancing and fault tolerance protection.

  3. To test, visit http://localhost:5555/api-a/hello

    Access was successful.

 3. Default rules for service routing

In service-oriented routing, since the name of <route> is arbitrary, is it possible to:

zuul.routes.hello-service.path=/hello-service/**
zuul.routes.hello-service.serviceId=hello-service

The name of <route> is the service name. In fact, in practical applications, we often name it like this. If there is such a rule, then Zuul can help us implement such a function by default, further saving the trouble of configuration.

Let's do an experiment and change the configuration file to:

server.port=5555
spring.application.name=api-gateway

eureka.client.service-url.defaultZone=http://localhost:1111/eureka

Then page access verification

Access was successful.

 

However, by default, the services on Eureka will be routed by Zuul by creating a default mapping relationship, so that the services we do not want to open to the outside world can also be accessed externally. At this time, you can configure zuul.ignored-services for configuration. Rules for automatically creating routes. When zuul.ignored-services=*, all services will not automatically create routing rules. At this time, the relevant routing configuration needs to be performed through the previous configuration.

=================Gorgeous dividing line====================

What I said before is all about one problem: the maintenance of routing rules and service instances, so how to solve the second problem (check redundancy problem)?

 Fourth, request filtering

In order to verify the client request in the API gateway, we can intercept and filter the request through the filter. The implementation method is relatively simple, we only need to inherit the ZuulFilter abstract class and implement its four methods.

Modify api-gateway:

  1. Add filter class
    /**
     * Inherit ZuulFilter and implement its 4 interfaces
     *
     * Used for request filtering
     *
     */
    public class AccessFilter extends ZuulFilter {
        Logger logger = LoggerFactory.getLogger(AccessFilter.class);
        /* 
         * shouldFilter determines whether the filter needs to be executed
         *
         * This returns true directly, indicating that the filter will take effect on all requests.
         * In practice, we can use this function to specify the valid range of the filter
         */
        @Override
        public boolean shouldFilter() {
            return true;
        }
    
        /*
         * The specific logic of the filter
         *
         * Here we let zuul come to request through ctx.setSendZuulResponse(false) without routing it
         * Then set the returned error code through ctx.setResponseStatusCode(401)
         *
         */
        @Override
        public Object run() {
            RequestContext context = RequestContext.getCurrentContext();
            HttpServletRequest request = context.getRequest();
            Object accessToken = request.getParameter("accessToken");
            
            logger.info("send {} request to {}", request.getMethod(),request.getRequestURL().toString());
            if(accessToken == null) {
                context.setSendZuulResponse ( false );
                context.setResponseStatusCode(401);
            }
            
            return null;
        }
    
        /* filterType returns the filter type
         * He decides in which life cycle of the request the filter is executed. Defined as pre here, it means that the request will be executed before the request is routed.
         *
         * pre: filter before the request is executed
         * route: process the request and perform routing
         * post: filter executed after request processing is complete
         * error: the filter to execute when an error occurs
         */
        @Override
        public String filterType() {
            return "pre";
        }
        
        
        /* 
         * filterOrder returns the execution order of the filter
         *
         * When the request has multiple filters in one stage, it needs to be executed once according to the return value of the method
         *
         */
        @Override
        public int filterOrder() {
            return 0;
        }
    
    }
  2. Modify the startup class
    /**
     * @EnableZuulProxy enables Zuul's API gateway service function
     *
     */
    @EnableZuulProxy
    @SpringCloudApplication
    public class GateWayApp {
    
        //Append the bean is the implementation
        @Bean
        public AccessFilter accessFilter() {
            return new AccessFilter();
        }
        
        public static void main(String[] args) {
            SpringApplication.run(GateWayApp.class, args);
        }
    }
  3. test
    1. ) access http://localhost:5555/hello-service/hello, the access fails
      1.  

    2. ) access http://localhost:5555/hello-service/hello ?accessToken=token , normal access

       

 

Modified code structure:

 

 5. Expansion and extension

In fact, when the routing function is actually running, its route mapping and request forwarding are completed by several different filters.

Route mapping is mainly done through pre-type filters, which match the request path with the configured routing rules to find the destination address that needs to be forwarded.

The part of request forwarding is completed by the route type filter, which forwards the route address obtained by the pre type filter.

Therefore, the filter can be said to be the core component of Zuul's API gateway function. Each HTTP request entering Zuul will go through a series of filter processing chains to get the request response and return it to the client.

 

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=325903355&siteId=291194637