1.1 Overview
In the microservice architecture, several basic service governance components are required, including service registration and discovery, service consumption, load balancing, circuit breakers, intelligent routing, configuration management , etc. These basic components cooperate with each other to form a Simple microservice system. A simple microservice system is shown below:
Note: A
Services and B
services can call each other, and I forgot when drawing
In Spring Cloud
a microservice system, a common implementation of load balancing is that the client's request first goes through load balancing (zuul、Ngnix)
, then reaches the service gateway ( zuul
cluster), and then to the specific service. Services are uniformly registered to the high-availability service registry cluster. All configuration files of the service are managed by the configuration service (explained in the next article). The configuration files of the configuration service are placed in the git
warehouse, which is convenient for developers to change the configuration at any time.
1.2 Zuul
Detailed Gateway
1.2.1 What is the significance of gateway
1.2.1.1 Two questions
1. We know that we are going to enter a service itself. Obviously, we have no particularly good way to directly enter the IP
address + port number . We know that this approach is very bad. This approach is very problematic . IP
AddressIP
, others will know where the service is deployed by looking at your address, making it easy for others to attack .
2. Second, we have so many services , do we want to call them one by one? We assume that we have done a permission authenticationJVM
here. Each of our customers accesses different service programs running on different machines. We Each service requires a service authentication . Is it annoying to do this? Obviously, it is very annoying.
1.2.1.2 Solutions
Then we are faced with these two extremely important problems at this time, and then we need a way to solve them. First of all, let's look at IP
the exposure of the address and IP
the single-point problem caused by the address writing . Do I have to dynamically maintain the list of its services for the service itself? I need to call the service itself, do I also need a load? Balance the same thing.
There is also the thing about IP address exposure , do I need to do a proxy , something like Nginx
a reverse proxy, and deploy public modules on this thing, such as the permission verification of all entrances.
So we now need a Zuul API
gateway. It solves the above problem. If you want to call a service, it will map it to you, map the IP
address of your service to a path, you enter the path, it matches, and it will access the service for you. It will have a request forwarding process , like Nginx
the specific instance of the service machine, it will not access it directly IP
, it will go to Eureka
the registry to get the instance ID
of the service, that is, the name of the service. Again, I use client-side load balancing Ribbon
to access one of the service instances .
API
The gateway is mainly to solve the problem of how to call the external call of the service itself, and to solve the problem of permission verification . You can integrate and call a series of filters here , such as integration shiro,springsecurity
and the like.
Zuul
By loading the dynamic filtering mechanism , the following functions can be achieved:
1. Authentication and security: Identify authentication requirements for various resources and reject those that do not meet the requirements
2. Review and monitoring: Track meaningful data and statistical results at the edge to bring us accurate production status conclusions
3. Dynamic routing: dynamically route requests to different backend clusters as needed
4. Stress test: Gradually increase the load traffic to the cluster to calculate the performance level
5. Load distribution: Allocate the corresponding capacity for each load type, and discard requests that exceed the limit
6. Static response processing: Partial responses are established directly at the edge location to prevent them from flowing into the internal cluster
AWS
7. Multi-region resiliency: request routing across regions, aiming to ELB
diversify usage and ensure that edge locations are as close as possible to users
1.3 Zuul
Code Examples
The first step is to create a new module under the original project Zuul
and introduce dependencies. The code is as follows:
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-eureka</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-zuul</artifactId>
</dependency>
Then @EnableZuulProxy
annotate the startup class with the following code:
server:
port: 5000
spring:
application:
name: api-geteway
zuul:
routes:
#标识你服务的名字,这里可以自己定义,一般方便和规范来讲还是跟自己服务的名字一样
hello-service:
#服务映射的路径,通过这路径就可以从外部访问你的服务了,目的是为了不爆露你机器的IP,面向服务的路由了,给你选一个可用的出来,
#这里zuul是自动依赖hystrix,ribbon的,不是面向单机
path: /hello-service/**
#这里一定要是你Eureka注册中心的服务的名称,所以这里配置serviceId因为跟eureka结合了,如果单独使用zuul,那么就必须写自己机器的IP了,
#如url:http://localhost:8080/ 这样的不好就是写死IP了,万一这IP挂了,这高可用性,服务注册那套东西就用不起来了
serviceId: hello-service
api-a:
path: /api-a/**
serviceId: service-ribbon
api-b:
path: /api-b/**
serviceId: service-feign
eureka:
#客户端
client:
#注册中心地址
service-url:
defaultZone: http://localhost:8888/eureka/,http://localhost:8889/eureka/
Description Tip
: /hello-service/
Requests starting with are forwarded to the hello-service
service; /api-a/
requests starting with are forwarded to the service-ribbon
service; /api-b/
requests starting with are forwarded to the service-feign
service;
Then start the registry and two service providers in the previous article, hello-service
and then we run to see if its request forwarding function is polled to enter the two services.
Enter localhost:5000/hello-service/hello
as follows:
Then refresh it again:
It can be seen zuul
that the request distribution has been carried out. hello-servie
It is mapped to a specific machine according to your service name . Isn't this the function of a reverse proxy ?
zuul
Request filtering can also be performed , so let's perform token
verification to demonstrate. First, we need to create a new TokenFilter
class to inherit ZuulFilter
this class and implement its four interfaces. The code is as follows:
package hjc.zuul;
import com.netflix.zuul.ZuulFilter;
import com.netflix.zuul.context.RequestContext;
import javax.servlet.http.HttpServletRequest;
public class TokenFilter extends ZuulFilter {
//四种类型:pre,routing,error,post
//pre:主要用在路由映射的阶段是寻找路由映射表的
//routing:具体的路由转发过滤器是在routing路由器,具体的请求转发的时候会调用
//error:一旦前面的过滤器出错了,会调用error过滤器。
//post:当routing,error运行完后才会调用该过滤器,是在最后阶段的
@Override
public String filterType() {
return "pre";
}
//自定义过滤器执行的顺序,数值越大越靠后执行,越小就越先执行
@Override
public int filterOrder() {
return 0;
}
//控制过滤器生效不生效,可以在里面写一串逻辑来控制
@Override
public boolean shouldFilter() {
return true;
}
//执行过滤逻辑
@Override
public Object run() {
RequestContext context = RequestContext.getCurrentContext();
HttpServletRequest request = context.getRequest();
String token = request.getParameter("token");
if (token == null){
context.setSendZuulResponse(false);
context.setResponseStatusCode(401);
context.setResponseBody("unAuthrized");
return null;
}
return null;
}
}
filterType
: Returns a string representing the type of filter ,
in zuul
which four filter types with different life cycles are defined, as follows:
1. pre
: It can be called before the request is routed. It is used in the routing mapping stage to find the routing mapping table.
2. route
: is called when the routing request is made, and the specific routing forwarding filter is called when the routing
specific request of the router is forwarded.
3. error
: Called when an error occurs while processing the request
4. post
: When routing
, error
the filter will be called after running, which is in the final stage
Here is a statement about zuul
the exception that occurs when the filter executes the network request. The filter cannot directly throw the try-catch
captured exception to the page. The exception thrown by the application can be returned. The solution is to catch
use the context.set()
method to return it to the page. as follows:
try{
//业务逻辑......
}catch(Exception e){
RequestContext context = RequestContext.getCurrentContext();
context.set("error.status_code",401);
context.set("error.exception",e);
context.set("error.message","sfdfsdf");
}
Next, you also need to add this filter to spring中,让
spring` management, the code is as follows:
package hjc;
import hjc.zuul.TokenFilter;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.zuul.EnableZuulProxy;
import org.springframework.context.annotation.Bean;
@SpringBootApplication
@EnableZuulProxy
public class ZuulApplication {
public static void main(String[] args) {
SpringApplication.run(ZuulApplication.class, args);
}
//将过滤器交给Spring管理
@Bean
public TokenFilter tokenFilter(){
return new TokenFilter();
}
}
Next, let's start the startup class, first without token
access, as follows:
As you can see, returning a message without permission, I want to talk about it here, it Token
is usually placed in the request header, here we just don’t do that for demonstration. Then it will be token
brought back to visit, as follows:
It can be seen that this has already put our request in the past.
Here I also want to talk about what is the default route, zuul
delete the configuration of the routing configuration, as follows:
server:
port: 5000
spring:
application:
name: api-geteway
eureka:
#客户端
client:
#注册中心地址
service-url:
defaultZone: http://localhost:8888/eureka/,http://localhost:8889/eureka/
Then, restart to continue accessing, as follows:
It can be seen that we can still continue to access. We have nothing to match, but we can still access. That is because, by default, your service name is hello-service
automatically declared here.
Then, if I don't want it to automatically declare it for me, I want to define it myself, then I can yml
use it in the configuration file to zuu.ignored-services
filter myself like a filter, as follows:
zuul:
#如果ignored-services:* 表示所有的默认路由都失效了,要自己一个个配,没人会那么操蛋,除非遇到奇葩业务
ignored-services:
Then let's talk about the mapping rules, for example:
zuul:
routes:
#标识你服务的名字,这里可以自己定义,一般方便和规范来讲还是跟自己服务的名字一样
hello-service:
#服务映射的路径,通过这路径就可以从外部访问你的服务了,目的是为了不爆露你机器的IP,面向服务的路由了,给你选一个可用的出来,
#这里zuul是自动依赖hystrix,ribbon的,不是面向单机
path: /hello-service/**
#这里一定要是你Eureka注册中心的服务的名称,是所以这里配置serviceId因为跟eureka结合了,如果单独使用zuul,那么就必须写自己机器的IP了,
#如url:http://localhost:8080/ 这样的不好就是写死IP了,万一这IP挂了,这高可用性,服务注册那套东西就用不起来了
serviceId: hello-service
zuul:
routes:
hello-service:
path: /hello-service/ext/**
serviceId: hello-service
There are two zuul
configuration mapping paths here /hello-service/
. You can see that they /hello-service/**
are included /hello-service/ext/**
. Is there a conflict when these two paths are matched? How to deal with it? Who will match first?
Here is yml
the order defined in to match. If it is a application.properties
format configuration file, its order cannot be guaranteed. The yml
format configuration file is in order and can be guaranteed. Pay attention here.
What if we want to define a matching rule? Then we need to define one in the startup class, bean
which determines your routing, as follows:
I won't demonstrate it here, and you can find the information slowly when you need it.
There is also ignored-patterns
:, as follows:
zuul:
routes:
#标识你服务的名字,这里可以自己定义,一般方便和规范来讲还是跟自己服务的名字一样
hello-service:
#服务映射的路径,通过这路径就可以从外部访问你的服务了,目的是为了不爆露你机器的IP,面向服务的路由了,给你选一个可用的出来,
#这里zuul是自动依赖hystrix,ribbon的,不是面向单机
path: /hello-service/**
#这里一定要是你Eureka注册中心的服务的名称,是所以这里配置serviceId因为跟eureka结合了,如果单独使用zuul,那么就必须写自己机器的IP了,
#如url:http://localhost:8080/ 这样的不好就是写死IP了,万一这IP挂了,这高可用性,服务注册那套东西就用不起来了
serviceId: hello-service
ignored-patterns: /hello/**
ignored-patterns
: Indicates the shielded /hello/**
path , even if you /hello-service/hello/**
can't do it, still shield it. We can further refine this configuration. For example, if I don't want to /hello
route the interface, then we can configure it as above.
What if we also want to configure a prefix for a service ? code show as below:
zuul:
routes:
#标识你服务的名字,这里可以自己定义,一般方便和规范来讲还是跟自己服务的名字一样
hello-service:
#服务映射的路径,通过这路径就可以从外部访问你的服务了,目的是为了不爆露你机器的IP,面向服务的路由了,给你选一个可用的出来,
#这里zuul是自动依赖hystrix,ribbon的,不是面向单机
path: /hello-service/**
#这里一定要是你Eureka注册中心的服务的名称,是所以这里配置serviceId因为跟eureka结合了,如果单独使用zuul,那么就必须写自己机器的IP了,
#如url:http://localhost:8080/ 这样的不好就是写死IP了,万一这IP挂了,这高可用性,服务注册那套东西就用不起来了
serviceId: hello-service
prefix: /api/**
You can see that the services you access must be /api/
prefixed, for example/api/hello-service/**
If we still want to make a path access and jump to my local, what should we do?
I hope that users /local
can automatically jump to this method to process when accessing, then Zuul
the local jump we need to use at this time is configured as follows:
zuul:
prefix: /api
ignored-patterns: /**/hello/**
routes:
local:
path: /hello-service/**
url: forward:/local
Some of our commonly used, docking springsecurity
, or some third-party components, they will obtain some of your cookie
information, then the Zuul
gateway will kill all your information for the sake of security cookie
, this is no way to do cookie
it, it is killed by default .
Here is Zuul
provided zuul.sensitive-headers
to do this for you cookie
, header
do not filter this information, control your sensitive information.
By default, sensitive header information cannot be passed through the API
gateway, we can make it pass through the following configuration:
zuul:
routes:
hello-service:
path: /hello-service/**
serviceId: hello-service
sensitive-headers: cookie,header之类额东西
It can also be used with Hystrix
some detailed configurations, which have been mentioned earlier. I won't say it here