赞
踩
本文章基于spring-boot-starter-parent 2.0.6RELEASE,spring-cloud-dependencies Finchley.SR2。
Spring Cloud Zuul是基于Netflix Zuul实现的API网关组件,包含了对请求的路由和过滤两个做最主要的功能。其中路由功能负责将请求转发到具体的微服务实例上,是实现外部访问统一入口的基础,而过滤器功能则负责对请求的处理过程进行干预,是实现请求校验、服务聚合等动能的基础。
Zuul通过和Eureka进行整合,将Zuul自身注册为Eureka服务治理下的应用功能,同时从Eureka中获去其它微服务的实例信息。这样的设计将服务实例的维护工作交给了服务治理框架自动完成,而对于路由规则的维护,Zuul默认会将通过以服务名为ContextPath的方式来创建路由映射,大大减少了运维工作。
创建一个Spring Boot工程,命名为zuul
第一步:引入zuul和eureka依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
<version>2.0.2.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-zuul</artifactId>
<version>2.0.2.RELEASE</version>
</dependency>
第二步:创建启动类,并使用@EnableZuulProxy
注解开启Zuul的API网关服务功能
@SpringBootApplication
@EnableZuulProxy
public class AppZuul {
public static void main(String[] args) {
SpringApplication.run(AppZuul.class);
}
}
第三步:application.yml中配置Zuul应用的相关信息
server: port: 9000 eureka: client: serviceUrl: defaultZone: http://localhost:8081/eureka instance: instance-id: zuul-1 #此实例注册到eureka服务端的唯一的实例ID prefer-ip-address: true #是否显示IP地址 spring: application: name: zuul #此实例注册到eureka服务端的name zuul: routes: myorder: serviceId: order-micro path: /order/**
完成上面的工作后,通过Zuul+eureka实现的网关服务就构建完成了。为了演示请求路由功能,我们借用之前文章中搭建起来的Eurek和Order工程。
启动Eureka、Order以及Zuul工程,进入Eurek信息面板中可以看到Zuul作为一个服务注册进来了。
在上面的application.yml配置文件中,我们看到了关于zuul路由的配置
zuul:
routes:
myorder:
serviceId: order-micro
path: /order/**
其中myorder
是我们定义的路由规则的名称,path
指定路由路径,所有请求url满足/order/**
规则的都会被路由到serviceId
指定的服务上。order-micro
是Eureka中注册的服务名。
注意:路由规则中,/ **代表是所有层级, / * 是代表一层,?匹配任意单个字符
浏览器访问http://localhost:9000/order/getOrder.do
(通过网关访问路径,Order服务真实的访问路径为http://localhost:7000/getOrder.do
),会发现请求被正确路由了。
禁用微服务名调用
浏览器访问:http://localhost:9000/order-micro/getOrder.do
,我们发现通过服务名也能够访问服务,把微服务名称暴露出去,这是不合理也不安全的,所以我们一般会禁用微服务名方式的调用。
applicaion.yml中加入配置ignored-services: user-micro
即可禁用某个通过微服务名调用的方式。如果配置成ignored-services: "*"
,代表禁用所有。
配置样式如下:
zuul:
ignored-services: "*"
增加路由前缀
定义接口名称一般都需要一定的规范,譬如调用微服务的API URl前缀需要加上"/api",对于这种情况只需要增加一条配置zuul.pefix=/api
。如果"/api"会出现在路由后的IP地址中怎么办呢?只需要在增加一条配置zuul.strip-prefix=false
即可。
配置样式如下:
zuul:
pefix: /api
strip-prefix: false
过滤器(Filter)可以说是Zuul实现API网关功能最为核心的组件,Zuul中的大部分功能都是通过过滤器来实现的。 每一个进入Zuul的HTTP请求都会经过一系列的过滤器处理链得到请求相应并返回给客户端。
Zuul重定义了4中标准过滤器类型,这些过滤器类型对应于请求的典型生命周期。
下面我们通过编写一个日志过滤器LogFilter,在请求被路由之前记录下请求的相关信息。实现方法很简单,只需继承ZuulFilter抽象类并实现其中的方法:
@Component public class LogFilter extends ZuulFilter { @Override public String filterType() { // pre return FilterConstants.ROUTE_TYPE; } @Override public int filterOrder() { // PreDecorationFilter,执行顺序是5,是pre阶段最后被执行的过滤器 return FilterConstants.PRE_DECORATION_FILTER_ORDER; } @Override public boolean shouldFilter() { return true; } @Override public Object run() throws ZuulException { RequestContext currentContext = RequestContext.getCurrentContext(); HttpServletRequest request = currentContext.getRequest(); String remoteAddr = request.getRemoteAddr(); String routeAddr = currentContext.get(FilterConstants.REQUEST_URI_KEY).toString(); System.out.println("访问地址:" + request.getRequestURI() + "路由后的地址:" + routeAddr); return null; } }
重新运行Zuul工程,访问http://localhost:9000//getOrder.do
,查看下控制台,可看到打印信息:
在上面的代码中,我们通过集成ZuulFilter抽象类并重写了下面4个方法,这四个方法分别定义了如下内容:
Spring Cloud Zuul默认为Zuul编写了一些过滤器,这些过滤器默认都是启用状态的(包括我们自己编写的),如果不想使用了,如何禁用呢?
配置格式:zuul.<SimpleClassName>.<filterType>.disable=true
,比如禁用我们上面写的过滤器zuul.LogFilter.pre.disable=true
。
Zuul默认是整合了Hystrix和Ribbon的,所以Zuul天生就拥有线程隔离和断路器的自我保护功能,以及对服调用的客户端负载均衡功能。我们可以通过Hystrix和Ribbon的参数来调整路由请求的各种超时时间配置。
启动Eureka、zuul以及客户端负载均衡Ribbon文章中使用的两个Order工程,可以发现Zuul默认开启了Ribbon负载均衡。
下面来看下Zuul如何使用功能Hystrix中的降级回退:
停掉上面启动的两个Order工程,只启动Zuul和Eureka,浏览器访问http://localhost:9000/order/getOrder.do
会发现返回报错页面,这肯定是不友好的。
现在我们来实现Hystrix中的降级回退,继承FallbackProvider类然后重写里面的方法:
@Component public class MyFallbackProvider implements FallbackProvider { @Override public String getRoute() { // 指定为哪个微服务提供回退(微服务名 写*代表所有微服务) return "order-micro"; } @Override public ClientHttpResponse fallbackResponse(String route, Throwable cause) { if (cause instanceof HystrixTimeoutException) { return response(HttpStatus.GATEWAY_TIMEOUT); } else { return response(HttpStatus.INTERNAL_SERVER_ERROR); } } private ClientHttpResponse response(final HttpStatus status) { // 这里返回一个ClientHttpResponse对象并实现其中的方法 return new ClientHttpResponse() { @Override public HttpStatus getStatusCode() throws IOException { return status; } @Override public int getRawStatusCode() throws IOException { // 返回status的code 比如 404,500等 return status.value(); } @Override public String getStatusText() throws IOException { // 返回一个HttpStatus对象的reasonPhrase信息 return status.getReasonPhrase(); } @Override public void close() { //降级信息全部响应完了之后调用的方法 } @Override public InputStream getBody() throws IOException { // 把降级信息响应回前端 return new ByteArrayInputStream("Zuul降级信息".getBytes()); } @Override public HttpHeaders getHeaders() { // 需要对响应报头设置的话可以在此设置 HttpHeaders headers = new HttpHeaders(); headers.setContentType(MediaType.APPLICATION_JSON); return headers; } }; } }
再次访问http://localhost:9000/order/getOrder.do
发现服务被正确降级了
参考资料
Providing Hystrix Fallbacks For Routes
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。