Eureka用于服务的注册于发现,Feign支持服务的调用以及均衡负载,Hystrix处理服务的熔断防止故障扩散,Spring Cloud Config服务集群配置中心,似乎一个微服务框架已经完成了。
我们还是少考虑了一个问题,外部的应用如何来访问内部各种各样的微服务呢?在微服务架构中,后端服务往往不直接开放给调用端,而是通过一个API网关根据请求的url,路由到相应的服务。当添加API网关后,在第三方调用端和服务提供方之间就创建了一面墙,这面墙直接与调用方通信进行权限控制,后将请求均衡分发给后台服务端。
基本使用:
新建项目 api-gateway
application.properties配置
#应用名
spring.application.name=api-gateway
#这里是用了订单系统的配置中心 所以这样配置
spring.cloud.config.discovery.enabled=true
spring.cloud.config.discovery.service-id=CONFIG
spring.cloud.config.profile=dev
#注册到注册中心
eureka.client.service-url.defaultZone=http://localhost:8761/eureka/
#配置端口号
server.port=9000
启动类加上@EnableZuulProxy注解
启动priduct服务和api-gateway网关服务,通过网关服务访问地址http://localhost:9000/product/product/list,第一个product是eureka应用服务名,后面的是请求方法的路径。和直接访问product服务地址结果是一样的http://localhost:8080/product/list
自定义路由:
如上地址变成http://localhost:9000/myProduct/product/list
添加配置:-> 访问地址测试。
#自定义路由规则
#zuul.routes.myProduct.path=/myProduct/**
#需要自定义路由的应用
#zuul.routes.myProduct.serviceId=product
#简洁写法
zuul.routes.product=/myProduct/**
禁止路由访问:
添加配置:-> 访问地址测试。
#禁止路由访问
zuul.ignored-patterns=/**/product/**
Cookie
zuul默认是过滤cookie的,访问路由地址http://localhost:9000/product/product/list看看cookies有没传过去。
解决方法:
zuul.sensitive-headers=*
动态路由
增加zuul配置类
@Component
public class ZuulConfig {
@ConfigurationProperties("zuul")
@RefreshScope
@Bean
public ZuulProperties zuulProperties(){
return new ZuulProperties();
}
}
最后把上面所有的配置放到git上面去,实现动态路由。
路由和高可用
启动多个服务节点注册到注册中心就可以了,内部调用的时候a服务调用到某个zuul服务转发到b服务,外部调用的话可以用混搭的方式nginx+zuul,nginx对外暴露一个地址,nginx作负载均衡,把请求转发到zuul服务上,两个结合取长补短。
Zuul的核心
Filter是Zuul的核心,用来实现对外服务的控制。Filter的生命周期有4个,分别是“PRE”、“ROUTING”、“POST”、“ERROR”,整个生命周期可以用下图来表示。
Zuul大部分功能都是通过过滤器来实现的,这些过滤器类型对应于请求的典型生命周期。
- PRE: 这种过滤器在请求被路由之前调用。我们可利用这种过滤器实现身份验证、在集群中选择请求的微服务、记录调试信息等。
- ROUTING:这种过滤器将请求路由到微服务。这种过滤器用于构建发送给微服务的请求,并使用Apache HttpClient或Netfilx Ribbon请求微服务。
- POST:这种过滤器在路由到微服务以后执行。这种过滤器可用来为响应添加标准的HTTP Header、收集统计信息和指标、将响应从微服务发送给客户端等。
- ERROR:在其他阶段发生错误时执行该过滤器。 除了默认的过滤器类型,Zuul还允许我们创建自定义的过滤器类型。例如,我们可以定制一种STATIC类型的过滤器,直接在Zuul中生成响应,而不将请求转发到后端的微服务。
Zuul:Pre和Post过滤器
PRE:如何对请求作统一的校验:(所有经过zuul请求的都带上token参数,并且不能为空,不然权限校验不通过)
修改api-gateway服务
创建一个filter过滤器
@Component
public class TokenFilter extends ZuulFilter {
//FilterConstants常量类
@Override
public String filterType() { return PRE_TYPE; }
//过滤器顺序 看FilterConstants常量类里面有
// 这里放到PRE_DECORATION_FILTER_ORDER之前,越小越靠前
@Override
public int filterOrder() { return PRE_DECORATION_FILTER_ORDER - 1; }
@Override
public boolean shouldFilter() { return true; }
@Override
public Object run() throws ZuulException {
RequestContext requestContext = RequestContext.getCurrentContext();
HttpServletRequest request = requestContext.getRequest();
String token = request.getParameter("token");
if (StringUtils.isEmpty(token)){
requestContext.setSendZuulResponse(false);
requestContext.setResponseStatusCode(HttpStatus.UNAUTHORIZED.value());
}
return null;
}
}
添加Bean
@Bean
public TokenFilter zuulPreFilter() {
return new TokenFilter();
}
测试访问地址http://localhost:9000/myProduct/product/list?token=123444和http://localhost:9000/myProduct/product/list
POST过滤器:返回结果头写信息
创建一个filter过滤器
@Component
public class AddResponseHeaderFilter extends ZuulFilter{
@Override
public String filterType() {
return POST_TYPE;
}
@Override
public int filterOrder() {
return SEND_RESPONSE_FILTER_ORDER-1;
}
@Override
public boolean shouldFilter() {
return true;
}
@Override
public Object run() throws ZuulException {
RequestContext requestContext = RequestContext.getCurrentContext();
HttpServletResponse response = requestContext.getResponse();
response.setHeader("X-Foo", UUID.randomUUID().toString());
return null;
}
}
@Bean
public AddResponseHeaderFilter addResponseHeaderFilter() {
return new AddResponseHeaderFilter();
}
Zuul限流:
时机:请求转发之前调用
令牌桶算法:以固定速率放入,web请求先要获取令牌,成功才转发。
创建过滤器:
@Component
public class RateLimitFilter extends ZuulFilter{
//google令牌桶算法实现的组件
private static final RateLimiter RATE_LIMITER = RateLimiter.create(100);
@Override
public String filterType() { return PRE_TYPE; }
@Override
public int filterOrder() { return
//优先级最高
SERVLET_DETECTION_FILTER_ORDER -1; }
@Override
public boolean shouldFilter() { return true; }
@Override
public Object run() throws ZuulException {
RequestContext requestContext = RequestContext.getCurrentContext();
if (!RATE_LIMITER.tryAcquire()){
requestContext.setSendZuulResponse(false);
requestContext.setResponseStatusCode(HttpStatus.UNAUTHORIZED.value());
}
return null;
}
}
@Bean
public RateLimitFilter rateLimiter() {
return new RateLimitFilter();
}