介绍:服务网关是微服务架构中一个不可或缺的部分。通过服务网关统一向外系统提供REST API的过程中,除了具备服务路由、均衡负载功能之外,它还具备了权限控制等功能。
Zuul是Netflix开源的微服务网关,他可以和Eureka,Ribbon,Hystrix等组件配合使用。Zuul组件的核心是一系列的过滤器,这些过滤器可以完成以下功能:
#身份认证和安全: 识别每一个资源的验证要求,并拒绝那些不符的请求
#审查与监控:
#动态路由:动态将请求路由到不同后端集群
#压力测试:逐渐增加指向集群的流量,以了解性能
#负载分配:为每一种负载类型分配对应容量,并弃用超出限定值的请求
#静态响应处理:边缘位置进行响应,避免转发到内部集群
搭建一个Zuul服务网关
1.新建模块ZuulGateWay,增加所需依赖包
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-zuul</artifactId>
</dependency>
注意,eureka-client导包,是:spring-cloud-starter-netflix-eureka-client
不是:spring-cloud-netflix-eureka-client
2.在程序启动类增加注解@EnableZuulProxy开启Zuul
@SpringCloudApplication注解,通过源码我们看到,它整合了@SpringBootApplication、@EnableDiscoveryClient、@EnableCircuitBreaker,主要目的还是简化配置。
package com.offcn;
import com.offcn.filter.AccessFilter;
import org.springframework.boot.SpringApplication;
import org.springframework.cloud.client.SpringCloudApplication;
import org.springframework.cloud.netflix.zuul.EnableZuulProxy;
import org.springframework.context.annotation.Bean;
@SpringCloudApplication //组合注解
@EnableZuulProxy
public class ZuulGatwayStarter {
public static void main(String[] args) {
SpringApplication.run(ZuulGatwayStarter.class,args);
}
//初始化过滤器为bean
@Bean
public AccessFilter createAccessFilter(){
return new AccessFilter();
}
}
3.修改application.yml属性配置文件
spring:
application:
name: ZUULGATEWAY
server:
port: 80
完成上面的工作后,Zuul已经可以运行了,但是如何让它为我们的微服务集群服务,还需要我们另行配置,下面详细的介绍一些常用配置内容。
Zuul服务网关路由配置
方式一:通过url直接映射
1.修改项目ZuulGateWay的属性配置文件application.yml
zuul:
routes:
userprovider001:
path: /userprovider001/**
url: http://localhost:9003/
userprovider002:
path: /userprovider002/**
url: http://localhost:9004/
该配置,定义了,所有到Zuul中的规则为:/userprovider001/**的访问都映射到http://localhost:9003/上,也就是说当我们访问http://localhost/userprovider001/的时候,Zuul会将该请求路由到:http://localhost:9003/上
注意:配置属性zuul.routes.userprovider001.path中的userprovider001部分为路由的名字,可以任意定义,但是一组映射关系的path和url要相同
2测试url直接映射方式
(测试该服务的某个接口功能)
http://localhost/userprovider001/user/getAll
方式二:通过在Eureka服务注册的serviceId进行映射
通过url映射的方式对于Zuul来说,并不是特别友好,Zuul需要知道我们所有微服务的地址,才能完成所有的映射配置。而实际上,我们在实现微服务架构时,服务名与服务实例地址的关系在eureka server中已经存在了,所以只需要将Zuul注册到eureka server上去发现其他服务,我们就可以实现对serviceId的映射。
1.修改服务网关ZuulGateWay的属性配置文件application.yml
eureka:
client:
service-url:
defaultZone: http://localhost:10086/eureka,http://localhost:10087/eureka
zuul:
routes:
userprovider:
path: /service/** #自定义访问规则
service-id: USERPROVIDER #注册的提供服务名,就不需要再配置服务地址了
2.测试ServiceId映射方式
http://localhost/service/user/getAll
3.修改客户端调用Zuul网关地址
使用Fegin方式实现接口的
1.修改注解FeignClient
@FeignClient(value = “ZUULGATEWAY”,configuration = feignConfig.class,fallback = UserServiceImpl.class)
2.调用方法按照Zuul定义的规则修改即可
例:
@GetMapping("/service/user/getAll")
public List getAll(…);
Zuul服务网关过滤器使用
在服务网关中定义过滤器只需要继承ZuulFilter抽象类实现其定义的四个抽象函数就可对请求进行拦截与过滤。
比如下面的例子,定义了一个Zuul过滤器,实现了在请求被路由之前检查请求中是否有accessToken参数,若有就进行路由,若没有就拒绝访问,返回401 Unauthorized错误。
1.编写Zuul过滤器(在模块ZuulGateWay下)
package com.offcn.filter;
import com.netflix.zuul.ZuulFilter;
import com.netflix.zuul.context.RequestContext;
import com.netflix.zuul.exception.ZuulException;
import javax.servlet.http.HttpServletRequest;
public class AccessFilter extends ZuulFilter {
//设置过滤器的类型,决定了过滤器的执行时间
@Override
public String filterType() {
//常见过滤器类型pre 路由请求转发之前执行 routing 在路由转发同时执行 post 在routing和error过滤器之后被调用 error:处理请求时发生错误时被调用
return "pre";
}
@Override
public int filterOrder() {
return 0; //过滤器的执行顺序,数字越小越先执行
}
//开关
@Override
public boolean shouldFilter() {
return true; //true 表示丐萝氯气处于可运行状态 false表示该过滤器不可用
}
//该过滤器做身份验证
@Override
public Object run() throws ZuulException {
//获取到当前请求上下文环境
RequestContext context = RequestContext.getCurrentContext();
//从上下文环境获取当前请求对象
HttpServletRequest request = context.getRequest();
//从请求对象获取传递的凭证
String token = request.getParameter("token");
//判断凭证是否存在
if(token==null){
//凭证不存在,禁止路由转发
context.setSendZuulResponse(false);
//提示错误状态码 401 权限不足的意思
context.setResponseStatusCode(401);
}
return null;
}
}
2.实例化该过滤器
我们只需要在启动类中增加如下内容:
@Bean
public AccessFilter accessFilter() {
return new AccessFilter();
}
3.测试过滤器
http://localhost/service/user/getAll
发现:
因为没有传token的值,所以被过滤器拦截了
我们再测试:
http://localhost/service/user/getAll?token=token
可以发现能正常访问该接口了