1.简介
Zuul包含了对请求的路由和过滤两个最主要的功能:
其中路由功能负责将外部请求转发到具体的微服务实例上,是实现外部访问统一入口的基础,类似于保安的职能,而过滤器功能则负责对请求的处理过程进行干预,是实现请求校验,服务聚合等功能的基础,Zuul和Eureka进行整合,将Zuul自身注册为Eureka服务治理下的应用,同时从Eureka中获取其他的微服务消息,也即以后访问微服务是通过Zuul跳转后获得,最终Zuul服务还是会注册进Eureka。提供 服务代理 ,路由,过滤三大功能。
从以上这张架构图中,我们可以看到所有的请求都必须通过API GateWay服务才能到达后面的服务,这就是Zuul所需要承担起来的责任。可见他的存在是很重要的。
为什么需要zuul或者说zuul承担的功能是什么?
zuul作为路由网关组件,在微服务架构中有着非常重要的作用,主要体现在如下六个方面:
1)zuul,ribbon以及eureka相结合,可以实现智能路由及负载均衡的作用,zuul可以将请求流量按某种策略分发到集群状态的多个服务实例。
2)网关将所有服务的API接口统一聚合,并且统一对外暴露。外界系统调用API接口时候,都是由网关对外暴露的API接口,外界系统不需要知道微服务系统中各个服务湘湖调用的复杂性。微服务系统也保护了其内部微服务单元的API接口,防止被外界直接调用,导致服务的敏感信息对外暴露。
3)网关服务可以做用户身份认证和权限认证,房子非法请求操作API接口,对服务器气到保护作用
4)网关可以实现监控功能,实时日志输出,对请求进行记录
5)网关可以用来实现流量监控,在高流量情况下,对服务进行降级
6)API接口从内部服务分离出来,方便做测试
2. 实例
2.1 快速入门
新建一个名为eureka-zuul-client的项目
引入pom
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<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>
</dependencies>
在程序的启动类中打上@EnableZuulProxy注解
@EnableZuulProxy
@EnableEurekaClient
@SpringBootApplication
public class EurekaZuulClientApplication {
public static void main(String[] args) {
SpringApplication.run(EurekaZuulClientApplication.class, args);
}
}
新增application.yml配置文件
eureka:
client:
serviceUrl:
defaultZone: http://localhost:8761/eureka/
server:
port: 8766
spring:
application:
name: eureka-zuul-client
zuul:
routes:
hiapi:
path: /hiapi/**
serviceId: eureka-client
ribbonapi:
path: /ribbonapi/**
serviceId: eureka-ribbon-client
feignapi:
path: /feignapi/**
serviceId: eureka-feign-client
这边注意看下配置
依次启动 server client ribbon fegin 及zuul
localhost:8766/hiapi/hello?name=llf
localhost:8766/ribbonapi/hi?name=llf
localhost:8766/feignapi/hi?name=llf
即可调用成功。
此时如果想使用指定服务的url可以修改配置文件application.yml指定zuul调用的服务
zuul:
routes:
hiapi:
path: /hiapi/**
url: http://localhost:8762
这样再请求的话就只会请求到8762的服务了
如果又想指定url又想搞负载均衡的话,那么就需要自己维护负载均衡的服务注册列表。
首先把ribbon.eureka.enabled改为false。即ribbon负载均衡客户端不想eurekaclient获取服务注册列表。然后我们自己维护一份注册列表,改注册列表对应服务名为hiapi-vi(可自定义),通过配置hiapi-v1.ribbon.listOfServers来配置多个负载均衡的URL。
zuul:
routes:
hiapi:
path: /hiapi/**
serviceId: hiapi-v1
ribbon:
eureka:
enabled: false
hiapi-v1:
ribbon:
listOfServers: http://localhost:8762,http://localhost:8763
2.2 在Zuul上配置API接口版本号
如果想给每个接口加前缀 比如:
http://localhost:8766/v1/hiapi/hello?name=llf
即在所有的接口前加一个v1的版本号,所以此时需要用到zuul.prefix配置
示例如下:
eureka:
client:
serviceUrl:
defaultZone: http://localhost:8761/eureka/
server:
port: 8766
spring:
application:
name: eureka-zuul-client
zuul:
routes:
hiapi:
path: /hiapi/**
serviceId: eureka-client
ribbonapi:
path: /ribbonapi/**
serviceId: eureka-ribbon-client
feignapi:
path: /feignapi/**
serviceId: eureka-feign-client
prefix: /v1
重启zuul即可实现。
2.3 在Zuul上配置熔断器
实现一个针对eureka-client的熔断器,当eureka-client服务出现故障时候,进入熔断器逻辑,向浏览器输入一句错误提示
最主要是继承实现FallbackProvider 接口
@Component
public class MyFallbackProvider implements FallbackProvider {
@Override
public String getRoute() {
return "eureka-client";
}
@Override
public ClientHttpResponse fallbackResponse(String route, Throwable cause) {
return new ClientHttpResponse() {
@Override
public HttpHeaders getHeaders() {
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
return headers;
}
@Override
public InputStream getBody() throws IOException {
return new ByteArrayInputStream("error,im the fallback".getBytes());
}
@Override
public HttpStatus getStatusCode() throws IOException {
return HttpStatus.OK;
}
@Override
public int getRawStatusCode() throws IOException {
return 200;
}
@Override
public String getStatusText() throws IOException {
return "OK";
}
@Override
public void close() {
}
};
}
}
然后关闭eureka-client实例,再次调用即进入fallback逻辑,如果需要所有的路由服务都加熔断功能,只需要在getRoute方法返回通配符*即可
如下:
@Override
public String getRoute() {
return "*";
}
2.4 在Zuul上使用过滤器
采取自定义过滤器,只需要继承ZuulFilter,并且实现ZuulFilter中的抽象方法,包括filterType和filterOrder以及shouldFilter和Object的run方法。
其中filterType即过滤器的类型,分别有四种类型 pre post routing和error,
filterOrder是过滤顺序,它作为int类型的值,值越小,越早执行这个过滤器。
shoulderFilter表示该过滤器是否过滤逻辑,如果是true,则执行run方法,如果false,则不执行run方法。run方法写具体的过滤逻辑,我们这边实例是检查请求参数中是否传了token这个参数,如果没有传,则请求不被路由到具体的服务实例,直接返回响应状态码。
@Component
public class MyFilter extends ZuulFilter {
@Override
public String filterType() {
return PRE_TYPE;
}
@Override
public int filterOrder() {
return 0;
}
@Override
public boolean shouldFilter() {
return true;
}
@Override
public Object run() throws ZuulException {
RequestContext ctx = RequestContext.getCurrentContext();
HttpServletRequest request = ctx.getRequest();
Object accessToken = request.getParameter("token");
if (accessToken == null) {
ctx.setSendZuulResponse(false);
ctx.setResponseStatusCode(401);
try {
ctx.getResponse().getWriter().write("token is empty");
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
return null;
}
}
启动服务
http://localhost:8766/v1/hiapi/hello?name=llf
浏览器显示token is empty
再次输入
http://localhost:8766/v1/hiapi/hello?name=llf&token=xsddd
就可以正确调用到eureka-client了