路由是微服务架构中必须的一部分,比如,“/” 可能映射到你的WEB程序上,”/api/users
“可能映射到你的用户服务上,“/api/shop”可能映射到你的商品服务商。(注解:我理解这里的这几个映射就是说通过Zuul这个网关把服务映射到不同的服务商去处理,从而变成了微服务!)
这里只列举zuul一些配置,headers,client等等不做考虑。
通过Zuul我们可以完成以下功能:
- 动态路由
- 监控与审查
- 身份认证与安全
- 压力测试: 逐渐增加某一个服务集群的流量,以了解服务性能;
- 金丝雀测试
- 服务迁移
- 负载剪裁: 为每一个负载类型分配对应的容量,对超过限定值的请求弃用;
- 静态应答处理
构建网关
在Idea里,新建项目,选择Spring initializer.
下面的pom
org.springframework.cloud
spring-cloud-starter-netflix-eureka-client
org.springframework.cloud
spring-cloud-starter-netflix-zuul
配置properties文件参数;
server.port=8887
spring.application.name=zuul-proxy
eureka.client.service-url.defaultZone=http://localhost:8882/eureka
启动类如下:
@EnableZuulProxy // 启动Zuul的路由服务
@SpringBootApplication
public class SpringCloundZuulDemoApplication {
public static void main(String[] args) {
SpringApplication.run(SpringCloundZuulDemoApplication.class, args);
}
}
代码基本编写完成,下面我们来启动项目;Eureka,service-hello(两个 + 一个实例),最后启动zuul-proxy;
浏览器输入:(可见,zuul也实现了负载均衡)
可见,zuul-proxy已经帮我们路由到相应的微服务。
自定义微服务访问路径
zuul.routes.service-hello = /hello/**
zuul.routes.service-hello-2 = /hello2/**
所要配置的路径可以指定一个正则表达式来匹配路径,因此,/
hello/*
只能匹配一级路径,但是通过/hello/**
可以匹配所有以/hello/
开头的路径。
忽略指定微服务
配置格式为: zuul.ignored-services=微服务Id1,微服务Id2...,多个微服务之间使用逗号分隔。
zuul.ignored-services = service-hello-2
可见,自定义路由依旧可以访问。
还有其他形式的路由配置,不在此一一列举。
spring-cloud-starter-zuul
本身已经集成了hystrix和ribbon,所以Zuul天生就拥有线程隔离和断路器的自我保护能力,以及对服务调用的客户端负载均衡功能。但是,我们需要注意,当使用path与url的映射关系来配置路由规则时,对于路由转发的请求则不会采用HystrixCommand
来包装,所以这类路由请求就没有线程隔离和断路器保护功能,并且也不会有负载均衡的能力。因此,我们在使用Zuul的时候尽量使用path和serviceId的组合进行配置,这样不仅可以保证API网关的健壮和稳定,也能用到Ribbon的客户端负载均衡功能。
我们来看看zuul是否集成hystrix,在之前feign已经集成了hystrix,这里不多做说明。
请注意,Zuul的Hystrix监控的粒度是微服务,而不是某个API,也就是所有经过Zuul的请求都会被Hystrix保护起来。假如,我们现在把service-hello-2服务关闭,再来访问会出现什么结果呢?结果可能不是我们所想那样,如下:
那么如何为Zuul实现容错与回退呢?
Zuul提供了一个ZuulFallbackProvider(新版:
FallbackProvider)
接口,通过实现该接口就可以为Zuul实现回退功能。那么让我们改造之前的Zuul-proxy
。
package com.example.zuul.fallback;
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;
/**
* @author [email protected]
* @version 1.0
* @date 2018-12-28
*/
@Component
public class ServiceFallbackProvider implements FallbackProvider {
@Override
public String getRoute() {
// 表明是为哪个微服务提供回退,*表示为所有微服务提供回退
return "*";
}
@Override
public ClientHttpResponse fallbackResponse(String route, Throwable cause) {
return new ClientHttpResponse() {
@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() {
}
@Override
public InputStream getBody() throws IOException {
return new ByteArrayInputStream((route + "服务暂不可用,请稍后重试!" + cause.getMessage()).getBytes());
}
@Override
public HttpHeaders getHeaders() {
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON_UTF8);
return headers;
}
};
}
}
再打开service-hello-2服务接口