介绍:服务网关是微服务架构中一个不可或缺的部分。通过服务网关统一向外系统提供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.flix.zuul.EnableZuulProxy;import org.springframework.context.annotation.Bean;@SpringCloudApplication //组合注解@EnableZuulProxypublic class ZuulGatwayStarter {public static void main(String[] args) {SpringApplication.run(ZuulGatwayStarter.class,args);}//初始化过滤器为bean@Beanpublic AccessFilter createAccessFilter(){return new AccessFilter();}}
3.修改application.yml属性配置文件
spring:application:name: ZUULGATEWAYserver: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/eurekazuul: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 flix.zuul.ZuulFilter;import flix.zuul.context.RequestContext;import flix.zuul.exception.ZuulException;import javax.servlet.http.HttpServletRequest;public class AccessFilter extends ZuulFilter {//设置过滤器的类型,决定了过滤器的执行时间@Overridepublic String filterType() {//常见过滤器类型pre 路由请求转发之前执行 routing 在路由转发同时执行 post 在routing和error过滤器之后被调用 error:处理请求时发生错误时被调用return "pre";}@Overridepublic int filterOrder() {return 0; //过滤器的执行顺序,数字越小越先执行}//开关@Overridepublic boolean shouldFilter() {return true; //true 表示丐萝氯气处于可运行状态 false表示该过滤器不可用}//该过滤器做身份验证@Overridepublic 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.实例化该过滤器
我们只需要在启动类中增加如下内容:
@Beanpublic AccessFilter accessFilter() {return new AccessFilter();}
3.测试过滤器
http://localhost/service/user/getAll
发现:
因为没有传token的值,所以被过滤器拦截了
我们再测试:
http://localhost/service/user/getAll?token=token
可以发现能正常访问该接口了