preface
Previous: detailed explanation of Hystrix
Zuul (Gateway) is very important and important in spring cloud. The role of zuul gateway is similar to that of nginx. Nginx can forward requests, and nginx manages the ip address of services. Clients only need to access the port of nginx, and which service to forward and access is forwarded by nginx. Zuul also has this function, and zuul can authenticate and authorize. If zuul thinks that a request has permission, Will be forwarded to the corresponding service. In addition, if the service has iterative changes, you only need to change the zuul configuration. The client does not need to change. It can also limit the flow. It has many advantages, but it is very simple to use.
advantage:
1. Request forwarding, the client does not need to maintain a large number of service ports
2. Authentication and authorization operations
3. Current limiting and safety
4. Project iteration leads to project splitting or change. You don't need to move the client, just modify Zuul configuration
1, Zuul quick start
Create a new module, maven project, named zuul_01
Common steps: import dependency, configure yml, configure startup class + start Zuul annotation, and finally test
Note: zuul also accesses other services by registering in Eureka
Step 1: import dependencies
<dependencies> <!--zuul Yes and through EurekaClient To register and access other services, you also need to import Eureka_Client--> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-zuul</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> </dependencies>
Step 2: configure application.yml
# Specify which EurekaServer service to register with eureka: client: serviceUrl: defaultZone: http://root:root@localhost:8761/eureka/,http://root:root@localhost:8762/eureka/ # Specify the service name to distinguish the created services spring: application: name: zuul server: port: 8001
Step 3: start the class and annotate @ EnableEurekaClient and @ EnableZuulProxy, because Zuul needs to be registered in Eureka
@SpringBootApplication @EnableEurekaClient @EnableZuulProxy public class ZuulApplication { public static void main(String[] args) { SpringApplication.run(ZuulApplication.class,args); } }
Start ZuulApplication and enter localhost:8761 to see the services that have been started. Here you should also see my previous cases and run the code. They are all related
Through Zuul's port 8001, you can access the corresponding service by entering the service name + interface. The process is Zuul - > Customer - > pay call
2, Zuul common configuration
2.1 monitoring interface and service configuration
The Zuul monitoring interface is not as good-looking as Hystrix. It is relatively simple. The effects are as follows. You can go to Zuul to see whether the configured service is effective. For example, if the address contains / customer / * *, you will find it in the customer service. If you encounter / eureka / * *, you will find it in the eureka service, which is similar to nginx
How do we configure it? A few old steps
Step 1: import dependencies
newly added
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-actuator</artifactId> </dependency>
Step 2: configure yml
newly added
# zuul's monitoring interface is configured as * during development and does not need to be configured online management: endpoints: web: exposure: include: "*"
Then restart and enter http://localhost:8001/actuator/routes that will do
What if you want to remove a service? Add the following configuration to application.yml
Restart the program and refresh the interface. You will find that the eureka service is directly removed and the customer service is still there, but you can't access it
#zuul configuration zuul: ignored-services: eureka #Ignore based on the service name. If you want to ignore all services, use* ignored-patterns: /**/search/** #The monitoring interface is still visible, but it cannot be accessed. 404
Custom configuration method: if you want to change the following path to another name, you can use custom configuration
#zuul configuration zuul: ignored-services: eureka #Ignore based on the service name. If you want to ignore all services, use* ignored-patterns: /**/pay/** #The monitoring interface is still visible, but it cannot be accessed. 404 routes: payClient: /pay/** #Service Name: new path customer: /user/**
This creates a new Accessible path. Of course, the old can also be used. The two are equivalent
If you don't want to use the old ones, you can ignore them. For example, we write ignored services: "*" here to try, restart and refresh. The previous access methods are ignored, so you can change the access name according to your needs
Enter as follows http://localhost:8001/user/customer/2 Get the following results, which used to be http://localhost:8001/customer/customer/2 , I just changed my name now
We don't need this for the time being. Ignore it and comment first
2.2 gray Publishing
What is grayscale publishing?
Grayscale publishing is in the version iteration process. For example, when a new version of app is released, the updated user calls the function carefully, while the old user still calls the function with the old interface. If grayscale publishing is adopted, the new and old versions can be balanced. What should we do? Just two steps
Step 1: add @ Bean code block to zuul startup class. Zuul uses version. The new version of zuul will let the corresponding service use the new interface, and the old version will use the old interface
@Bean public PatternServiceRouteMapper serviceRouteMapper(){ return new PatternServiceRouteMapper( "(?<name>^.+)-(?<version>v.+$)", "${version}/${name}" ); }
Step 2: prepare a service and make two versions
Do the following first
Restart both old and new customers, ZuulApplication, and changes. Then check zuul's monitoring interface, and the new one will appear as / v1/customer. If you add another customer, it will become / v2/customer, and zuul will automatically perform
If you don't get V1 and v2, there is only one v1. If you don't have v2, it proves that you have a problem. Generally, it is a registration problem. Finally, I think all services should be registered. Then Eureka has all services, and Zuul can get all services by re registering
It's OK to start in this order
So it can be accessed according to different versions
3, Zuul filter
Filter is the core component of Zuul. Let's take a look at its execution process, as shown in the following figure
The client requests are sent to the Zuul service. First, through the PreFilter filter chain, this filter can perform operations such as flow restriction and permission verification. If the request is correct, it will be released normally, and the request will be forwarded to RoutingFilter again. RouteFilter will forward the request to the specified service to get the response result, and then pass the result back to RoutingFilter, RouteFilter then passes the result to the PostFilter filter filter chain, and finally gives the response information to the client.
Four filters are available
3.1 Zuul filter quick start
Create two custom filter classes in zuul module, inherit ZuulFilter and override four methods
@Component public class TestZuulFilter extends ZuulFilter { @Override public String filterType() { return FilterConstants.PRE_TYPE;//Set filter type, prefilter } @Override public int filterOrder() { return FilterConstants.PRE_DECORATION_FILTER_ORDER - 1; //Set the filter execution order. The smaller the number, the first execution will be performed. If it is smaller than the pre filter, it will be performed before the pre filter } @Override public boolean shouldFilter() { return true; //Open current filter } @Override public Object run() throws ZuulException { System.out.println("prefix The filter has been executed"); return null; } }
import com.netflix.zuul.ZuulFilter; import com.netflix.zuul.exception.ZuulException; import org.springframework.cloud.netflix.zuul.filters.support.FilterConstants; import org.springframework.stereotype.Component; @Component public class TestZuulFilter2 extends ZuulFilter { @Override public String filterType() { return FilterConstants.PRE_TYPE;//Set filter type, prefilter } @Override public int filterOrder() { return FilterConstants.PRE_DECORATION_FILTER_ORDER + 1; //Set the filter execution order. The smaller the number, the first execution will be performed. If it is smaller than the pre filter, it will be performed before the pre filter } @Override public boolean shouldFilter() { return true; //Open current filter } @Override public Object run() throws ZuulException { System.out.println("prefix Filter 222 has been executed"); return null; } }
Restart ZuulApplication, and then randomly access an address, such as this one http://localhost:8001/v1/customer/version The console will print the following results
FilterConstants.PRE_ DECORATION_ FILTER_ The TestZuulFilter of order - 1 is executed first, because the smaller the number is, it will be executed first. The default value in the source code is 5
3.2 PreFilter implements token verification
Create an AuthenticFilter and inherit ZuulFilter
import com.netflix.zuul.ZuulFilter; import com.netflix.zuul.context.RequestContext; import com.netflix.zuul.exception.ZuulException; import org.apache.http.HttpStatus; import org.springframework.cloud.netflix.zuul.filters.support.FilterConstants; import javax.servlet.http.HttpServletRequest; @Component //Remember to add component s to SPring for management public class AuthenticFilter extends ZuulFilter { @Override public String filterType() { return FilterConstants.PRE_TYPE; } @Override public int filterOrder() { return FilterConstants.PRE_DECORATION_FILTER_ORDER - 2;//Check before } @Override public boolean shouldFilter() { return true; } @Override public Object run() throws ZuulException { //1. Get request object RequestContext currentContext = RequestContext.getCurrentContext(); HttpServletRequest request = currentContext.getRequest(); //2. Get token parameter String token = request.getParameter("token"); //3. Comparison token if(token == null || !("123".equals(token))){//Suppose the token is 123 //4. token verification failed. The data is directly responded to currentContext.setSendZuulResponse(false);//No response currentContext.setResponseStatusCode(HttpStatus.SC_UNAUTHORIZED);//401 } return null; } }
The above tokens are actually found through Redis or other tokens. Note that the Filter defined by yourself should use @ Component and be managed by Spring
Restart ZuulApplication, refresh the browser, and return 401 inaccessible HttpStatus.SC_UNAUTHORIZED is the 401 status code, indicating unauthorized and verification failure
If the token is correct, it will be released
3.3 degradation of zuul
By default, Zuul will include Hystrix dependencies, so there is no need to import Hystrix dependencies on degradation. The essence is that Zuul integrates Hystrix to achieve degradation and return the underlying data
Create ZuulFallBack for degradation
import org.springframework.cloud.netflix.zuul.filters.route.FallbackProvider; import org.springframework.http.HttpHeaders; import org.springframework.http.HttpStatus; import org.springframework.http.MediaType; import org.springframework.http.client.ClientHttpResponse; import org.springframework.stereotype.Component; import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.InputStream; @Component public class ZuulFallBack implements FallbackProvider { @Override public String getRoute() { return "*"; //Specify this degradation method for all problematic services } @Override public ClientHttpResponse fallbackResponse(String route, Throwable cause) {//route represents the service name System.out.println("Degraded services:"+route); cause.printStackTrace();//Problem printing return new ClientHttpResponse() { @Override public HttpStatus getStatusCode() throws IOException { return HttpStatus.INTERNAL_SERVER_ERROR; //INTERNAL_SERVER_ERROR(500, "Internal Server Error"), } @Override public int getRawStatusCode() throws IOException { return HttpStatus.INTERNAL_SERVER_ERROR.value(); //500 status code } @Override public String getStatusText() throws IOException { //Specify error message return HttpStatus.INTERNAL_SERVER_ERROR.getReasonPhrase(); } @Override public void close() { } @Override public InputStream getBody() throws IOException { String msg = "Current service:"+ route + ",Something went wrong"; return new ByteArrayInputStream(msg.getBytes()); } @Override public HttpHeaders getHeaders() { //Specify response header information HttpHeaders headers = new HttpHeaders(); headers.setContentType(MediaType.APPLICATION_JSON); return headers; } }; } }
Read the code above with comments
New sleep
Restart Customer related services (two), restart ZuulApplication service, and enter the address http://localhost:8001/v2/customer/version?token=123
The console will print the reason, Read timed out, because sleep
code:
Link: https://pan.baidu.com/s/1U3ebAqfIS-s_7gqqyuOSGQ
Extraction code: 233x