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:
- It is the maintenance problem of routing rules and service instances mentioned above.
- 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:
- New maven project api-gateway
- 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>
- 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); } }
- 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
- 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:
- Modify the POM file and introduce Eureka dependencies
<!-- 引入eureka依赖 --> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-eureka</artifactId> </dependency>
- 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/eurekaNote: zuul.routes.api-a.url = hello-service can also achieve the function, but it cannot perform normal load balancing and fault tolerance protection.
- 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:
- 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; } }
- 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); } }
- test
- ) access http://localhost:5555/hello-service/hello, the access fails
-
- ) 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.