MicroServices architecture, usually a business system will have a lot of microservices, such as: OrderService, Productservice, UserService ..., in order to make the invocation more simple, usually in the front end of these services to encapsulate a layer, similar to the following:
In front of this layer is commonly known as "Gateway layer", its existence significance is that the "1 to N" problem into the "1 to 1" problem, while the request to the real micro-service, can do some preprocessing, such as: source legality detection, authorization check, anti-crawler and so on ...
In the traditional way, the most Earth-like approach, the gateway layer can be encapsulated in human flesh, similar to the following sample code:
Loginresult Login (...) {
TODO preprocessing ... return Userservice.login ();//Call the User Service's login method }product queryproduct (...) {
TODO preprocessing ... return productservice.queryproduct ();//Invoke the Query method of the product service }order submitorder (...) {
TODO preprocessing ... return Orderservice.submitorder ();//Call the Order service's Query method}
To do this, of course, can run, but the maintenance of a large amount, the subsequent micro-services to add new methods, all need to manually add the appropriate method package at the gateway layer, and Spring cloud Zuul is a good solution to this problem, as follows:
Zuul as gateway layer, itself is a micro-service, with other services service-1,service-2, ... Like Service-n, are registered on the Eureka server, you can discover each other, Zuul can perceive which services are online, and by configuring routing rules (shown later), you can automatically forward the request to the specified back-end microservices, for some common preprocessing (such as: Permission authentication , token legitimacy check, gray-level verification, some traffic guidance, etc.), can be placed in the so-called filter (zuulfilter) processing, so that the backend service after the new service, Zuul layer almost no modification.
Steps to use:
One, add Zuul-dependent jar packages
Compile ' Org.springframework.cloud:spring-cloud-starter-zuul '
Ii. Configuring routing in Application.yml
Zuul: routes: api-a: path:/api-user/** service-id:service-provider sensitive-headers: Api-b: path:/api-order/** Service-id:service-consumer
Explanation: The above configuration indicates that the URL request that/api-user/begins will be forwarded to the Service-provider,/api-order/URL request, will be forwarded to service-consumer this micro-service.
Third, the fuse treatment
If the microservices behind the gateway are hung, Zuul also allows you to define a fallback class for fusing, referring to the following code:
Package Com.cnblogs.yjmyzz.spring.cloud.study.gateway;import Org.springframework.cloud.netflix.zuul.filters.route.zuulfallbackprovider;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;import java.nio.charset.charset;/** * Created by yangjunming on 2017/7/14. */@Componentpublic class Serviceconsumerfallbackprovider implements Zuulfallbackprovider {@Override public String g Etroute () {return "Service-consumer"; } @Override Public Clienthttpresponse fallbackresponse () {return new Clienthttpresponse () {@Over Ride public Httpstatus Getstatuscode () throws IOException {return httpstatus.ok; } @Override public int GetrawstAtuscode () throws IOException {return This.getstatuscode (). value (); } @Override Public String Getstatustext () throws IOException {return THIS.GETSTATUSC Ode (). Getreasonphrase (); } @Override public void Close () {} @Override public InputStream ge TBody () throws IOException {return new Bytearrayinputstream ("Service-consumer not Available". GetBytes ()); } @Override Public Httpheaders getheaders () {httpheaders headers = new Httpheaders ( ); MEDIATYPE MT = new MediaType ("Application", "JSON", Charset.forname ("UTF-8")); Headers.setcontenttype (MT); return headers; } }; }}
Developers simply specify the microservices instance to be processed in the Getroute method and rewrite the fallbackresponse.
At this point, if you observe the/health endpoint, you can also see that the hystrix is in a melt-off state
Four, Zuulfilter filter
Filters are a very useful mechanism, following several classic scenarios under the demo:
4.1.Token verification/Safety Certification
When the gateway is exposed directly to the public network, the terminal will call a service, usually send the token after login, the gateway layer to verify token validity, if the token is invalid (or not token), prompt to re-login or direct rejection. In addition, the microservices behind the gateway, if you set up basic Auth in spring security (that is, do not allow anonymous access, you must provide a user name, password), or you can handle it in filter. Refer to the following code:
Package Com.cnblogs.yjmyzz.spring.cloud.study.gateway;import Com.netflix.zuul.zuulfilter;import Com.netflix.zuul.context.requestcontext;import Org.apache.commons.codec.binary.base64;import Org.slf4j.Logger; Import Org.slf4j.loggerfactory;import org.springframework.cloud.netflix.zuul.filters.support.FilterConstants; Import Org.springframework.stereotype.component;import javax.servlet.http.httpservletrequest;/** * Created by Yangjunming on 2017/7/13. */@Componentpublic class Accessfilter extends Zuulfilter {private static Logger Logger = Loggerfactory.getlogger (Acces Sfilter.class); @Override public String FilterType () {return filterconstants.pre_type; } @Override public int filterorder () {return 0; } @Override public Boolean shouldfilter () {return true; } @Override public Object run () {RequestContext CTX = Requestcontext.getcurrentcontext (); HttpServletRequest request = Ctx.getrequest (); Object token = RequeSt.getparameter ("token"); Check token if (token = = null) {Logger.info ("token is empty, no access allowed!"); Ctx.setsendzuulresponse (FALSE); Ctx.setresponsestatuscode (401); return null; } else {//todo obtains the corresponding login information according to token, verifies (slightly)}//Adds basic Auth authentication information Ctx.addzuulrequestheader ("Au Thorization "," Basic "+ getbase64credentials (" App01 "," * * * * ")); return null; } private string Getbase64credentials (string username, string password) {string plaincreds = Username + ":" + P Assword; byte[] plaincredsbytes = Plaincreds.getbytes (); byte[] base64credsbytes = base64.encodebase64 (plaincredsbytes); return new String (base64credsbytes); }}
There are 4 types of filter, whose constant values are defined in org.springframework.cloud.netflix.zuul.filters.support.FilterConstants
Zuul Filter Type Constants-----------------------------------/** * {@link zuulfilter#filtertype ()} error type. */string Error_type = "ERROR";/** * {@link zuulfilter#filtertype ()} post TYPE. */string Post_type = "POST";/** * {@link zuulfilter#filtertype ()} Pre TYPE. */string Pre_type = "PRE";/** * {@link zuulfilter#filtertype ()} Route TYPE. */string Route_type = "ROUTE";
Security checks are generally placed before the request is actually processed, so the above example FilterType is specified as the pre, and the rest is simply to rewrite their logic in the Shouldfilter (), run () method.
4.2 Dynamically modifying request parameters
Zuulfilter can intercept all request parameters, and modify it, such as: Terminal sent data, for security requirements, may be encrypted processing, the gateway layer to decrypt the parameters, and then pass to the back of the service, such as: the user passed the token value, need to convert to UserID /username this information and then pass it on to the micro service behind it. Refer to the following run method:
Public Object Run () {try {RequestContext context = Getcurrentcontext (); InputStream in = (InputStream) context.get ("requestentity"); if (in = = null) {in = Context.getrequest (). getInputStream (); The String BODY = streamutils.copytostring (in, Charset.forname ("UTF-8")); BODY = "Dynamically add a piece of content to the body:" + body; byte[] bytes = body.getbytes ("UTF-8"); Context.setrequest (New Httpservletrequestwrapper (Getcurrentcontext (). Getrequest ()) {@Override Public ServletInputStream getInputStream () throws IOException {return new Servletinputstreamwrapper (bytes); } @Override public int getcontentlength () {return bytes.length; } @Override public Long Getcontentlengthlong () {return Bytes.len Gth }}); } catch (IOException e) {rethrowruntimeexception (e); } return null; }
For more examples of filter, you can refer to the official website: https://github.com/spring-cloud-samples/sample-zuul-filters
4.3 grayscale release (Gated Launch/gray release)
In large-scale distributed system, gray-scale publishing is an important means to ensure the safety of the online system, the general practice is: from the cluster to designate a (or a few) machines, each time to do a new release before the release of these machines, first to see if it is normal, if stable operation, and then released to other machines. This strategy, which is equivalent to grayscale by a subset of nodes, can meet the requirements in most cases, but there are some specific scenarios that might not be appropriate.
For example: The author is located in the "Delicious do not Wait" company, the main B-end users for the food and beverage brands of businesses, most of the case, if the new on a function, hoping to find some small-scale restaurant to do the pilot, first look at the running after the line, if run well, and then promote to other businesses.
Another example: There are more than n versions of the backend service running concurrently, such as V1, V2, now added a V3 version (which is very common in mobile app), I hope that only partially upgraded the app's users access to the latest V3 version of the service, other users still access the old version, after the system stability, Further large-scale prompting users to upgrade.
The nature of these grayscale requirements, which appear to vary in demand, is essentially the same: turning the request (based on the parameter content + business rules) to a specific grayscale machine. Spring Cloud Microservice has a metadata-map ( metadata ) setting that can be used to meet such needs.
The first thing to do is introduce a jar package: (This is an open source project on GitHub Ribbon-discovery-filter-spring-cloud-starter)
Compile ' io.jmnarloch:ribbon-discovery-filter-spring-cloud-starter:2.1.0 '
Examples are as follows:
Set the following metadata-map in the application.yml of each service
Eureka: instance: metadata-map: gated-launch:false
That is, when all nodes are published, the default grayscale mode is False. The configuration on a particular grayscale machine is then changed to True (indicating that the machine is used for grayscale verification).
Then refer to the following code in Zuulfilter:
@Override public Object Run () {RequestContext CTX = Requestcontext.getcurrentcontext (); HttpServletRequest request = Ctx.getrequest (); Object token = request.getparameter ("token"); Check token if (token = = null) {Logger.info ("token is empty, no access allowed!"); Ctx.setsendzuulresponse (FALSE); Ctx.setresponsestatuscode (401); return null; } else {//todo Gets the appropriate login information based on token and checks (slightly)//Grayscale Example Ribbonfiltercontextholder.clearcurrentcon Text (); if (Token.equals ("1234567890")) {Ribbonfiltercontextholder.getcurrentcontext (). Add ("Gated-launch", "true") ; } else {Ribbonfiltercontextholder.getcurrentcontext (). Add ("Gated-launch", "false"); }}//Add basic AUTH Certification Information Ctx.addzuulrequestheader ("Authorization", "Basic" + getbase64credentials ("app 01 "," * * * * ")); return null; }
Note 18-23 lines, which demonstrate that the request is directed to the Gated-lanuch=true machine via a specific token parameter value. (Note: Refer to this principle, you can put the parameter value, change their version-version number, shopid-merchant ID, etc.). This request is forwarded to the grayscale node as long as the token=1234567890 in the request parameter.
If a friend is curious about how this can be done, look at the Io.jmnarloch.spring.cloud.ribbon.predicate.MetadataAwarePredicate class:
@Override protected Boolean apply (Discoveryenabledserver server) { final Ribbonfiltercontext context = Ribbonfiltercontextholder.getcurrentcontext (); Final set<map.entry<string, string>> attributes = Collections.unmodifiableset (Context.getattributes (). EntrySet ()); Final map<string, string> metadata = Server.getinstanceinfo (). GetMetaData (); Return Metadata.entryset (). Containsall (attributes); }
The general principle is that in context, the developer-set properties are compared to the Metadata-map in the service node, and if the METADATA-MAP includes developer-set properties, it returns success (that is, selecting this server)
Sample Source: Https://github.com/yjmyzz/spring-cloud-demo
Spring Cloud Learning (6)-Zuul micro-service Gateway