There are a lot of resources on the web that explain exactly what an API gateway is and what it can do, but I don’t think it’s friendly for beginners, Zuul is the gateway in SpringCloud microservices.

For starters, Zuul is a unified management of the API as the number of services increases. A certain type of API will call a certain type of service. In addition, it can filter the incoming API requests. Further, Zuul has other functions, which are shown in the figure below:

This article focuses on routing and forwarding and filters.

1 How to introduce Zuul

Similarly, to create a Zuul module, in this case there is no consumer, so the method of creating an empty parent module and then a concrete child module is not adopted. Then add the dependency to the POM file in Zuul:

<dependencies>
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-netflix-zuul</artifactId>
    </dependency>
</dependencies>
Copy the code

The directory structure of the project so far looks like this:

2 Main startup class and configuration file

Because there is no service consumption involved, just API processing, so the main startup class is relatively simple

@SpringBootApplication
@EnableZuulProxy		/ / open Zuul
@EnableEurekaClient
public class ZuulMain9401 {
    public static void main(String[] args) { SpringApplication.run(ZuulMain9401.class, args); }}Copy the code

The configuration file words are the same as the regular Eureka client

spring:
  application:
    name: zuul9401
server:
  port: 9401

eureka:
  client:
    fetch-registry: true
    register-with-eureka: true
    service-url:
      defaultZone: http://localhost:8001/eureka/
  instance:
    instance-id: zuul9401
Copy the code

3 Route Forwarding

Route forwarding is modified by adding content to the configuration file. The following describes the three modes of route forwarding.

Set the service ID for registering Eureka

To add the first configuration file, add the following content to the original configuration file.

zuul:
  routes:
    user-a:
      path: /api-a/**
      serviceId: eureka-provide
Copy the code

User-a is arbitrary, path is the external access path, and serviceId is the value of spring.application.name in the microservice configuration file.

So the overall meaning of the configuration file added above is when external access/api-a/Is forwarded to a path namedeureka-providThe service provides services.

Open Eureka Service registry EurekaServer8001, service provider Eureka Provide701/2/3, API Gateway ZuulMain9401:

Then go to http://localhost:9401/api-a/eureka/provide, according to the analysis, should be forwarded to the ` eureka – dojo.provide ` had been in service/dojo.provide path.

To prevent confusion, post the code for the first project again, as detailed in the first article in this series.

@SpringBootApplication
@RestController
@EnableEurekaClient
public class EurekaProvide7001 {

    @Value("${server.port}")
    int port;

    @GetMapping("/eureka/provide")
    public String getInfo(a) {
        return "hello, i am eureka provide, the provide service. My port: " + port;
    }

    @GetMapping("/eureka/delayProvide")
    public String delayGetInfo(a) throws InterruptedException {
        Thread.sleep(3000);
        return "hello, delay to do something";
    }
    public static void main(String[] args) { SpringApplication.run(EurekaProvide7001.class, args); }}Copy the code

You can see that the route is successfully forwarded

Set the URL

Add a second wave profile

zuul:
  routes:
# user-a:
# path: /api-a/**
# serviceId: eureka-provide
    user-b:
      path: /api-b/**
      url: http://localhost:7101/
Copy the code

As mentioned above, which service should the URL be forwarded to

Emulating the new service by changing the port and service name through Edit Configurations is also illustrated in a clear GIF in the first article.

Other services don’t have to shut down, continue to open new Provide7101, restart ZuulMain9401 services, visit http://localhost:9401/api-b/eureka/provide, also can see enough forward to success

Set the service ID of the unregistered Eureka

As I mentioned earlier when I was learning about the Ribbon, we can access some services that are not registered with Eureka through the Ribbon Settings. The same effect can be achieved by setting the Ribbon through the configuration file in the API gateway.

Add a third wave profile

zuul:
  routes:
# user-a:
# path: /api-a/**
# serviceId: eureka-provide
# user-b:
# path: /api-b/**
# url: http://localhost:7101/
    user-c:
      path: /api-c/**
      serviceId: provide-without-eureka

This is definitely needed
ribbon:
  eureka:
    enabled: false
provide-without-eureka:
  ribbon:
    ServerListClassName: com.netflix.loadbalancer.ConfigurationBasedServerList
    listOfServers: localhost:7201, localhost:7202
    ConnectTimeout: 1000
    ReadTimeout: 3000
    MaxTotalHttpConnections: 500
    MaxConnectionsPerHost: 100
Copy the code

If you uncomment user-a, user A cannot be forwarded, and an error of 500 will be reported. I guess this is because ribbon. Eureka.

ServiceId is also the name of the microservice, and then set the microservice name to [provid-without-eureka]. Ribbon. The other properties are related, and the most important one is listOfServers. Indicates that this access to this service name will be assigned in the list of services.

For simple or with the same service, with the above method to modify the configuration files, modify the port number 7201, modified eureka. Client. Register. With. Eureka = false to simulate not registered into the service of eureka.

Then I copied the configuration again and changed the port number to 7202. In total, I created two new services with port 7201,7202.

Other services not shut, open ProvideWithoutEureka7201/2 services, restart ZuulMain9401 services, open at this time all of the services is as follows

Go to http://localhost:9401/api-c/eureka/provide, the service is still the same forward to success

4 Check the route status

By the way, to check the routing status, first of all, you need to add the configuration file, it must be added

management:
  endpoints:
    web:
      exposure:
        include: "*"
  endpoint:
    health:
      show-details: ALWAYS
Copy the code

Then visit http://localhost:9401/actuator/routes and normal circumstances will appear below

If you want a detailed information, then only need to visit http://localhost:9401/actuator/routes/details

5 Fallback for forwarding routes

And Hystrix, when the forwarding route finds that the service cannot provide services properly, it can fallback.

Create a class MyFallbackProvider to implement the FallbackProvider interface

@Component
public class MyFallbackProvider implements FallbackProvider {
    @Override
    public String getRoute(a) {
        // Rollback all routes
        return "*";	
    }

    @Override
    public ClientHttpResponse fallbackResponse(String route, Throwable throwable) {
        return new ClientHttpResponse() {
            @Override
            public HttpStatus getStatusCode(a) throws IOException {
                return HttpStatus.OK;
            }

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

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

            @Override
            public void close(a) {}@Override
            public InputStream getBody(a) throws IOException {
                // It is displayed after the rollback
                return new ByteArrayInputStream("something wrong, fallback now".getBytes());
            }

            @Override
            public HttpHeaders getHeaders(a) {
                HttpHeaders headers = new HttpHeaders();
                headers.setContentType(MediaType.APPLICATION_JSON);
                returnheaders; }}; }}Copy the code

Now to manually close ProvideWithoutEureka7201/2 service simulation service outage, to see if I can back

6 filters

Zuul is able to perform authentication, authorization, static resource processing, etc., thanks to the following filters, but I will focus on the most basic filters, and I will probably go into more depth later on.

Creating a filter

First create the Filter package, then create a filter class, MyPreFilter, that implements the ZuulFilter interface

public class MyPreFilter extends ZuulFilter {
    @Override
    public String filterType(a) {	// Filter type
        return FilterConstants.PRE_TYPE;	// Request pre-processing
    }

    @Override
    public int filterOrder(a) {		// Filter order, the smaller the priority
        return 0;
    }

    @Override
    public boolean shouldFilter(a) {	// Whether to enable filtering
        return true;
    }

    @Override
    public Object run(a) throws ZuulException {	// Execute logic
        RequestContext currentContext = RequestContext.getCurrentContext();
        HttpServletRequest request = currentContext.getRequest();
        System.out.println("[ PreFilter" + "]" + String.format("send %s request to %s",request.getMethod(),request.getRequestURL()));
        return null; }}Copy the code

There are a number of constants defined in the FilterConstants class, of which there are several for filters

public static final String ERROR_TYPE = "error";	// Execute if an error occurs
public static final String POST_TYPE = "post";		// Request after request
public static final String PRE_TYPE = "pre";		// Request before request
public static final String ROUTE_TYPE = "route";	// Process the target request
Copy the code

Create a post-request at the same time

public class MyPostFilter extends ZuulFilter {
    @Override
    public String filterType(a) {
        return FilterConstants.POST_TYPE;
    }

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

    @Override
    public boolean shouldFilter(a) {
        return true;
    }

    @Override
    public Object run(a) throws ZuulException {
        RequestContext currentContext = RequestContext.getCurrentContext();
        HttpServletRequest request = currentContext.getRequest();
        // Change PreFilter to PostFilter
        System.out.println("[ PostFilter" + "]" + String.format("send %s request to %s",request.getMethod(),request.getRequestURL()));	
        return null; }}Copy the code

Inject into a container

Create a new Config package and create a class ZuulConfiguration under the package

@Configuration
public class ZuulConfiguration {

    @Bean
    public MyPreFilter getZuulPreFilterBean(a) {
        return new MyPreFilter();
    }

    @Bean
    public MyPostFilter getZuulPostFilterBean(a) {
        return newMyPostFilter(); }}Copy the code

The directory structure of the Zuul module is as follows

Note that there is a pit where the fallback from the previous section fails when the filter is turned on.

Restart the ZuulMain9401 service and clear the IDEA output console

If it follows from the previous section, it should be forwarding routes that are not registered with the Eureka service

Visit http://localhost:9401/api-c/eureka/provide to view the console output

Creation is not easy, if it is helpful to you, welcome to like, collect and share!