Spring Cloud实现微服务的架构基本成型:
使用Spring Cloud Netflix中的Eureka实现了服务注册中心以及服务注册与发现;而服务间通过Ribbon或Feign实现服务的消费以及均衡负载。
为了使得服务集群更为健壮,使用Hystrix的融断机制来避免在微服务架构中个别服务出现异常时引起的故障蔓延。
Zuul是Netflix开源的服务网关,可以和Eureka、Ribbon、Hystrix等组件配合使用
Zuul的核心是一系列的过滤器,完成包括:
- 认证和安全:识别每个需要认证的资源,拒绝不符合要求的请求。
- 性能监测:在服务边界追踪并统计数据,提供精确的生产视图。
- 动态路由:根据需要将请求动态路由到后端集群。
- 压力测试:逐渐增加对集群的流量以了解其性能。
- 负载分配:预先为每种类型的请求分配容量,当请求超过容量时自动丢弃。
- 静态资源处理:直接在边界返回某些响应,从而避免其转发到内部集群。
不管是自于客户端(PC或移动端)的请求,还是服务内部调用。一切对服务的请求都会经过Zuul这个网关,然后再由网关来实现 鉴权、动态路由等等操作。
Zuul就是服务的统一入口。
1.工程入门
1.1工程基本信息:
1.2 添加Zuul依赖:
1.3编写application.yml配置信息
server:
port: 8090 #服务端口
spring:
application:
name: api-gateway #指定服务名
1.4编写引导类
使用@EnableZuulProxy注解开启Zuul功能
@SpringBootApplication
@EnableZuulProxy // 开启网关功能
public class ItestZuulApplication {
public static void main(String[] args) {
SpringApplication.run(ItestZuulApplication.class, args);
}
}
1.5编写路由规则
使用Zuul来代理test-provider服务
server:
port: 8090 #服务端口
spring:
application:
name: api-gateway #指定服务名
zuul:
routes:
test-provider: # 这里是路由id,随意写
path: /test-provider/** # 这里是映射路径
url: http://127.0.0.1:8081 # 映射路径对应的实际url地址
将符合 path
规则的一切请求,都代理到 url
参数指定的地址
测试
2面向服务的路由
对itest-zuul工程修改优化
2.1.添加Eureka客户端依赖
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starter-netflix-eureka-clientartifactId>
dependency>
2.2添加Eureka配置,获取服务信息
eureka:
client:
registry-fetch-interval-seconds: 5 # 获取服务列表的周期:5s
service-url:
defaultZone: http://127.0.0.1:8086/eureka
2.3在启动类中添加Eureka客户端发现功能
@SpringBootApplication
@EnableZuulProxy // 开启Zuul的网关功能
@EnableDiscoveryClient
public class TestZuulApplication {
public static void main(String[] args) {
SpringApplication.run(TestZuulApplication.class, args);
}
}
2.4修改映射配置,通过服务名称获取
因为已经有了Eureka客户端,直接可以从Eureka获取服务的地址信息,因此映射时无需指定IP地址,而是通过服务名称来访问,而且Zuul已经集成了Ribbon的负载均衡功能。
zuul:
routes:
test-provider: # 这里是路由id,随意写
path: /test-provider/** # 这里是映射路径
serviceId: test-provider # 指定服务名称
简化配置
大多数情况下
路由名称和服务名会写成一样的。
因此Zuul就提供了一种简化的配置语法:
zuul.routes.=
zuul:
routes:
test-provider: /test-provider/** # 这里是映射路径
2.5 Zuul指定了默认的路由规则:
默认情况下,一切服务的映射路径就是服务名本身。例如服务名为:test-provider
,则默认的映射路径就 是:/test-provider/**
zuul:
routes:
service-provider: /service-provider/**
service-consumer: /service-consumer/**
prefix: /api # 添加路由前缀
通过zuul.prefix=/api
来指定了路由的前缀,这样在发起请求时,路径就要以/api开头。
3过滤器:
Zuul作为网关的其中一个重要功能,就是实现请求的鉴权。而这个动作我们往往是通过Zuul提供的过滤器来实现的。
3.1ZuulFilter
ZuulFilter是过滤器的顶级父类。其中定义的4个最重要的方法:
public abstract ZuulFilter implements IZuulFilter{
abstract public String filterType();
abstract public int filterOrder();
boolean shouldFilter();// 来自IZuulFilter
Object run() throws ZuulException;// IZuulFilter
}
shouldFilter
:返回一个Boolean
值,判断该过滤器是否需要执行。返回true执行,返回false不执行。
run
:过滤器的具体业务逻辑。
filterType
:返回字符串,代表过滤器的类型。包含以下4种:
pre
:请求在被路由之前执行,一般用于请求认证、负载均衡和日志记录。
route
:在路由请求时调用。这里使用Apache HttpClient或Netflix Ribbon构造对目标的HTTP请求。
post
:在route和errror过滤器之后调用,般会在此步骤添加响应头、收集统计和性能数据等。
error
:处理请求时发生错误调用。
filterOrder
:通过返回的int值来定义过滤器的执行顺序,数字越小优先级越高。
Zuul Filter有以下几个特征:
- Type:用以表示路由过程中的阶段(内置包含PRE、ROUTING、POST和ERROR)
- Execution Order:表示相同Type的Filter的执行顺序
- Criteria:执行条件
- Action:执行体
Zuul提供了动态读取、编译和执行Filter的框架。各个Filter间没有直接联系,但是都通过RequestContext
共享一些状态数据。
3.2过滤器的生命周期
3.1正常流程:
3.2异常流程:
整个过程中,pre或者route过滤器出现异常,都会直接进入error过滤器,在error处理完毕后,会将请求交给POST过滤器,最后返回给用户。
如果是error过滤器自己出现异常,最终也会进入POST过滤器,将最终结果返回给请求客户端。
如果是POST过滤器出现异常,会跳转到error过滤器,但是与pre和route不同的是,请求不会再到达POST过滤器了。
3.3过滤器使用场景:
4.自定义过滤器
模拟一个登录的校验。
基本逻辑:如果请求中有access-token参数,则认为请求有效,放行。
4.1定义过滤器类package:filter.LoginFilter
@Component
public class LoginFilter extends ZuulFilter {
/**
* 过滤器类型,前置过滤器
*/
@Override
public String filterType() {
return "pre";
}
/**
* 过滤器的执行顺序
*/
@Override
public int filterOrder() {
return 1;
}
/**
* 该过滤器是否生效
*/
@Override
public boolean shouldFilter() {
return true;
}
/**
* 登陆校验逻辑*/
@Override
public Object run() throws ZuulException {
// 获取zuul提供的上下文对象
RequestContext cOntext= RequestContext.getCurrentContext();
// 从上下文对象中获取请求对象
HttpServletRequest request = context.getRequest();
// 获取token信息
String token = request.getParameter("access-token");
// 判断
if (StringUtils.isBlank(token)) {
// 过滤该请求,不对其进行路由
context.setSendZuulResponse(false);
// 设置响应状态码,401
context.setResponseStatusCode(HttpStatus.SC_UNAUTHORIZED);
// 设置响应信息
context.setResponseBody("{\\"status\\":\\"401\\", \\"text\\":\\"request error!\\"}");
}
// 校验通过,把登陆信息放入上下文信息,继续向后执行
context.set("token", token);
return null;
}
}
5.Zuul中的负载均衡和熔断
Zuul中默认就已经集成了Ribbon负载均衡和Hystix熔断机制。
但是所有的超时策略都是走的默认值,如熔断超时时间只有1S,很容易就触发了。
建议我们手动进行配置:
hystrix:
command:
default:
execution:
isolation:
thread:
timeoutInMilliseconds: 2000 # 设置hystrix的超时时间为6000ms
相关结束。