SpringCloud实战6-Zuul⽹关服务
为什么需要⽹关呢?
我们知道我们要进⼊⼀个服务本⾝,很明显我们没有特别好的办法,直接输⼊IP地址+端⼝号,我们知道这样的做法很糟糕的,这样的做法⼤有问题,⾸先暴露了我们实体机器的IP地址,别⼈⼀看你的IP地址就知道服务部署在哪⾥,让别⼈很⽅便的进⾏攻击操作。
第⼆,我们这么多服务,我们是不是要挨个调⽤它呀,我们这⾥假设做了个权限认证,我们每⼀个客户访问的都是跑在不同机器上的不同的JVM上的服务程序,我们每⼀个服务都需要⼀个服务认证,这样做烦不烦呀,明显是很烦的。
那么我们这时候⾯临着这两个极其重要的问题,这时我们就需要⼀个办法解决它们。⾸先,我们看IP地址的暴露和IP地址写死后带来的单点问题,我是不是对这么服务本⾝我也要动态的维护它服务的列表呀,我需要调⽤这服务本⾝,是不是也要⼀个负载均衡⼀样的玩意,
还有关于IP地址暴露的玩意,我是不是需要做⼀个代理呀,像Nginx的反向代理⼀样的东西,还有这玩意上部署公共的模块,⽐如所有⼊⼝的权限校验的东西。因此我们现在需要Zuul API⽹关。它就解决了上⾯的问题,你想调⽤某个服务,它会给你映射,把你服务的IP地址映射成
某个路径,你输⼊该路径,它匹配到了,它就去替你访问这个服务,它会有个请求转发的过程,像Nginx⼀样,服务机器的具体实例,它不会直接去访问IP,它会去Eureka注册中⼼拿到服务的实例ID,即服务的名字。我再次使⽤客户端的负载均衡ribbon访问其中服务实例中的⼀台。
API⽹关主要为了服务本⾝对外的调⽤该怎么调⽤来解决的,还有解决权限校验的问题,你可以在这⾥整合调⽤⼀系列过滤器的,例如整合shiro,springsecurity之类的东西。
Zuul可以通过加载动态过滤机制,从⽽实现以下各项功能:
1.验证与安全保障: 识别⾯向各类资源的验证要求并拒绝那些与要求不符的请求。
2.审查与监控: 在边缘位置追踪有意义数据及统计结果,从⽽为我们带来准确的⽣产状态结论。
3.动态路由: 以动态⽅式根据需要将请求路由⾄不同后端集处。
4.压⼒测试: 逐渐增加指向集的负载流量,从⽽计算性能⽔平。
5.负载分配: 为每⼀种负载类型分配对应容量,并弃⽤超出限定值的请求。
6.静态响应处理: 在边缘位置直接建⽴部分响应,从⽽避免其流⼊内部集。
7.多区域弹性: 跨越AWS区域进⾏请求路由,旨在实现ELB使⽤多样化并保证边缘位置与使⽤者尽可能接近。
接着下来进⾏实战⼩Demo
第⼀步,在原来的⼯程下,新建⼀个Zuul模块,引⼊依赖,代码如下:
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-eureka</artifactId>
<version>1.3.5.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-zuul</artifactId>
<version>1.3.5.RELEASE</version>
</dependency>
接着在启动类上打上@EnableZuulProxy注解,代码如下:
server:
port: 5000
spring:
application:
name: api-geteway
zuul:
routes:
#标识你服务的名字,这⾥可以⾃⼰定义,⼀般⽅便和规范来讲还是跟⾃⼰服务的名字⼀样
hello-service:
#服务映射的路径,通过这路径就可以从外部访问你的服务了,⽬的是为了不爆露你机器的IP,⾯向服务的路由了,给你选⼀个可⽤的出来,
#这⾥zuul是⾃动依赖hystrix,ribbon的,不是⾯向单机
path: /hello-service/**
#这⾥⼀定要是你Eureka注册中⼼的服务的名称,是所以这⾥配置serviceId因为跟eureka结合了,如果单独使⽤zuul,那么就必须写⾃⼰机器的IP了,
#如url:localhost:8080/ 这样的不好就是写死IP了,万⼀这IP挂了,这⾼可⽤性,服务注册那套东西就⽤不起来了
serviceId: hello-service
eureka:
#客户端
client:
#注册中⼼地址
service-url:
defaultZone: localhost:8888/eureka/,localhost:8889/eureka/
接着启动先前⽂章中的注册中⼼和两个hello-service服务提供者,接着我们运⾏,看⼀下它的请求转发
功能,看他有没有轮询进⼊两个服务,
输⼊localhost:5000/hello-service/hello,如下:
接着再刷新⼀遍:
可以看到zuul进⾏了请求分发了。它是根据你的服务名字hello-servie来映射到具体的机器上,这不就是⼀个反向代理的功能吗?
zuul还能进⾏请求过滤,那么我们进⾏⼀下token校验来演⽰⼀下,⾸先我们需要先新建⼀个TokenFilter类来继承ZuulFilter这个类,实现它的四个接⼝,代码如下:
package hjc.zuul;
import comflix.zuul.ZuulFilter;
import t.RequestContext;
import javax.servlet.http.HttpServletRequest;
/**
* Created by cong on 2018/5/18.
*/
public class TokenFilter extends ZuulFilter {
//四种类型:pre,routing,error,post
//pre:主要⽤在路由映射的阶段是寻路由映射表的
//routing:具体的路由转发过滤器是在routing路由器,具体的请求转发的时候会调⽤
//error:⼀旦前⾯的过滤器出错了,会调⽤error过滤器。
//post:当routing,error运⾏完后才会调⽤该过滤器,是在最后阶段的
@Override
public String filterType() {
return "pre";
}
//⾃定义过滤器执⾏的顺序,数值越⼤越靠后执⾏,越⼩就越先执⾏
@Override
public int filterOrder() {
return 0;
}
//控制过滤器⽣效不⽣效,可以在⾥⾯写⼀串逻辑来控制
@Override
public boolean shouldFilter() {
return true;
}
//执⾏过滤逻辑
@Override
public Object run() {
RequestContext context = CurrentContext();
HttpServletRequest request = Request();
String token = Parameter("token");
if (token == null){
context.setSendZuulResponse(false);
context.setResponseStatusCode(401);
context.setResponseBody("unAuthrized");
return null;
}
return null;
}
}
filterType:返回⼀个字符串代表过滤器的类型,在zuul中定义了四种不同⽣命周期的过滤器类型,具体如下:
1.pre:可以在请求被路由之前调⽤,⽤在路由映射的阶段是寻路由映射表的
2.route:在路由请求时候被调⽤,具体的路由转发过滤器是在routing路由器具体的请求转发的时候会调⽤
3.error:处理请求时发⽣错误时被调⽤
4.post:当routing,error运⾏完后才会调⽤该过滤器,是在最后阶段的
这⾥声明⼀下zuul过滤器执⾏⽹络请求发⽣的异常,过滤器⾥⾯是不能直接将try-catch捕捉的异常抛出给页⾯的。应⽤程序抛出的异常是可以返回出的需解决办法就是在catch⾥⾯⽤context.set()⽅法返回给页⾯。如下:try{
业务逻辑......
}catch(Exception e){
RequestContext context = CurrentContext();
context.set("error.status_code",401);
context.set("ption",e);
context.set("ssage","sfdfsdf");
}
接着,你还需要把这个过滤器加⼊spring中,让spring管理,代码如下:
package hjc;
import hjc.zuul.TokenFilter;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloudflix.zuul.EnableZuulProxy;
import t.annotation.Bean;
@SpringBootApplication
@EnableZuulProxy
public class ZuulApplication {
public static void main(String[] args) {
SpringApplication.run(ZuulApplication.class, args);
}
//将过滤器交给Spring管理
@Bean
public TokenFilter tokenFilter(){
return new TokenFilter();
}
}
接着,让我们启动启动类,先进⾏不带token的访问,如下:
可以看到,返回⼀个没权限的信息,这⾥要说⼀下,Token⼀般都是放在请求头中的,这⾥我们只是为了演⽰才没那么⼲,
接着将token带上再去访问,如下:
可以看到这是已经将我们的请求放过去了。
这⾥我还要讲⼀下什么是默认路由,将zuul的配置删除路由配置,如下:
server:
port: 5000
spring:
application:
name: api-geteway
eureka:
#客户端
client:
#注册中⼼地址
service-url:
defaultZone: localhost:8888/eureka/,localhost:8889/eureka/
接着,重启继续访问,如下:
、
可以看到,还是能继续访问,我们什么都没配,居然还能访问,那是因为,这⾥默认⽤你的服务名字hello-service⾃动声明了。
那么,如果说我不想让它帮我⾃动声明,我要我⾃⼰定义,那么可以在yml配置⽂件中使⽤zuu.ignored-services就可以把⾃⼰像过滤的过滤,如下:”zuul:
#如果ignored-services:* 表⽰所有的默认路由都失效了,要⾃⼰⼀个个配,没⼈会那么,除⾮遇到奇葩业务
ignored-services:
接着我们再说⼀下映射规则,⽐⽅说
zuul:
routes:
#标识你服务的名字,这⾥可以⾃⼰定义,⼀般⽅便和规范来讲还是跟⾃⼰服务的名字⼀样
hello-service:
#服务映射的路径,通过这路径就可以从外部访问你的服务了,⽬的是为了不爆露你机器的IP,⾯向服务的路由了,给你选⼀个可⽤的出来,
#这⾥zuul是⾃动依赖hystrix,ribbon的,不是⾯向单机
path: /hello-service/**
#这⾥⼀定要是你Eureka注册中⼼的服务的名称,是所以这⾥配置serviceId因为跟eureka结合了,如果单独使⽤zuul,那么就必须写⾃⼰机器的IP了,
#如url:localhost:8080/ 这样的不好就是写死IP了,万⼀这IP挂了,这⾼可⽤性,服务注册那套东西就⽤不起来了
serviceId: hello-service
zuul:
routes:
hello-service:
path: /hello-service/ext/**
serviceId: hello-service
这⾥的两个zuul配置映射路径都有/hello-service/,可以看到/hello-service/**是包括/hello-service/ext/**的,这两个路径进⾏匹配的时候是不是有冲突呀,怎么处理呢?谁先匹配呢?
这⾥是yml中定义的顺序来匹配的。如果是application.properties格式的配置⽂件,它这个顺序是不能保证的,yml格式的配置⽂件是有顺序的,可以保证,这⾥要注意下⼀下。如果我们想定义⼀下匹配规则怎么办呢?那么我们就需要在启动类中定义⼀个bean,这个类就是决定你的路由的,如下:
这⾥就不演⽰了,需要⽤到的时候⾃⼰再去慢慢查资料吧。
还有就是ignored-patterns:,如下:
zuul:
routes:
#标识你服务的名字,这⾥可以⾃⼰定义,⼀般⽅便和规范来讲还是跟⾃⼰服务的名字⼀样
hello-service:
#服务映射的路径,通过这路径就可以从外部访问你的服务了,⽬的是为了不爆露你机器的IP,⾯向服务的路由了,给你选⼀个可⽤的出来,
#这⾥zuul是⾃动依赖hystrix,ribbon的,不是⾯向单机
path: /hello-service/**
#这⾥⼀定要是你Eureka注册中⼼的服务的名称,是所以这⾥配置serviceId因为跟eureka结合了,如果单独使⽤zuul,那么就必须写⾃⼰机器的IP了,
#如url:localhost:8080/ 这样的不好就是写死IP了,万⼀这IP挂了,这⾼可⽤性,服务注册那套东西就⽤不起来了
serviceId: hello-service
ignored-patterns: /hello/**
ignored-patterns:表⽰屏蔽掉/hello/**的路径,就算你/hello-service/hello/**也不⾏,照样屏蔽。这个配置我们可以进⼀步细化,⽐如说我不想给/hello接⼝路由,那我们可以按照上⾯⽅式配置
如果我们还想配置⼀个服务的前缀该怎么办?代码如下:
zuul:
routes:
#标识你服务的名字,这⾥可以⾃⼰定义,⼀般⽅便和规范来讲还是跟⾃⼰服务的名字⼀样
hello-service:
#服务映射的路径,通过这路径就可以从外部访问你的服务了,⽬的是为了不爆露你机器的IP,⾯向服
务的路由了,给你选⼀个可⽤的出来,
nginx和网关怎么配合使用#这⾥zuul是⾃动依赖hystrix,ribbon的,不是⾯向单机
path: /hello-service/**
#这⾥⼀定要是你Eureka注册中⼼的服务的名称,是所以这⾥配置serviceId因为跟eureka结合了,如果单独使⽤zuul,那么就必须写⾃⼰机器的IP了,
#如url:localhost:8080/ 这样的不好就是写死IP了,万⼀这IP挂了,这⾼可⽤性,服务注册那套东西就⽤不起来了
serviceId: hello-service
prefix: /api/**
可以看到那么你访问的服务都必须要加/api/前缀,例如/api/hello-service/**
如果我们还想进⾏⼀个路径访问就跳转到我的本地,那该怎么办呢?
我希望⽤户在访问/local时能够⾃动跳转到这个⽅法上来处理,那么此时我们需要⽤到Zuul的本地跳转,
配置⽅式如下:
zuul:
prefix: /api
ignored-patterns: /**/hello/**
routes:
local:
path: /hello-service/**
url: forward:/local
我们常⽤的⼀些,对接springsecurity,或者是⼀些第三⽅组件,它们会获取你的⼀些cookie信息,那么Zuul⽹关为了安全起见,把你的cookie信息都给⼲掉了,这个是没办法去搞cookie的。它是默认⼲掉的。
这⾥Zuul提供了zuul.sensitive-headers来给你搞这些cookie,header,这些信息不要进⾏过滤。控制
你的敏感信息。
默认情况下,敏感的头信息⽆法经过API⽹关进⾏传递,我们可以通过如下配置使之可以传递:
zuul:
routes:
hello-service:
path: /hello-service/**
serviceId: hello-service
sensitive-headers: cookie,header之类额东西
还可以配合Hystrix的⼀些详细配置⼀起使⽤,前⾯也讲过了。这⾥就不说了
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论