spring cloud zuul实践
一、 描述
Spring Cloud Zuul是基于Netflix开源的Zuul项目构建而成,它作为微服务架构中的网关服务,主要用于实现动态路由、负载均衡和请求过滤等功能。
-
动态路由:Zuul根据预设的路由规则将进来的请求路由到相应的服务实例上。路由规则可以通过配置文件或代码进行定义,接收到请求后,Zuul会解析请求的URL,并根据配置的路由规则找到对应的服务地址,将请求转发到目标服务。
-
负载均衡:Zuul内置了Ribbon负载均衡器,可以对请求进行负载均衡。当一个服务有多个实例时,Zuul可以使用负载均衡算法将请求均匀地分发到不同的实例上,以提高系统的性能和可扩展性。
-
请求过滤:Zuul支持自定义过滤器,在请求被路由之前或之后对请求进行处理。过滤器可以用于在路由前进行身份验证、请求统计、参数校验等操作,也可以在路由后处理响应结果,比如修改返回数据、添加自定义的响应头等。开发者可以根据需求创建不同类型的过滤器,并定义过滤器的执行顺序。
-
整合服务注册中心:Zuul可以与服务注册中心(如Zookeeper、Eureka)进行整合,实现动态路由和负载均衡。通过与服务注册中心交互,Zuul能够动态地获取服务实例的信息,并根据需要进行路由和负载均衡。
-
高可用和容错:Zuul支持配置多个实例运行在不同的机器上,以实现高可用性。当一个Zuul实例发生故障时,其他实例可以接管请求处理,确保系统的稳定性和容错性。
Spring Cloud Zuul通过动态路由、负载均衡和请求过滤等机制,提供了一个强大且灵活的网关服务。它能够统一管理微服务的入口,实现请求的转发和过滤,简化了微服务架构中的通信和调用方式,提高了系统的可伸缩性和可维护性。
下面是使用Spring Cloud Zuul的一般实践步骤
二、添加依赖
引入zuul的依赖spring-cloud-starter-netflix-zuul
1 <dependency> 2 <groupId>org.springframework.cloud</groupId> 3 <artifactId>spring-cloud-starter-netflix-zuul</artifactId> 4 </dependency>
三、创建启动类:
创建一个启动类,并使用@EnableZuulProxy
注解开启Zuul代理功能。
1 @SpringBootApplication 2 @EnableZuulProxy 3 public class GatewayApplication { 4 5 public static void main(String[] args) { 6 SpringApplication.run(GatewayApplication.class, args); 7 } 8 9 }
四、 配置
在配置文件(如application.yml
)中定义Zuul的路由规则。可以使用Zuul的zuul.routes
前缀来配置不同的路由规则。
1 server: 2 port: 9000 3 management: 4 endpoints: 5 web: 6 exposure: 7 include: '*' 8 server: 9 port: 12345 10 zuul: 11 routes: 12 configserver: 13 path: /myConfig/** 14 serviceId: config 15 user: 16 path: /myUser/** 17 serviceId: provider 18 ignored-patterns: #忽略指定的路由 19 - /config/** 20 - /gateway/** 21 sensitive-headers: 22 # ignore-local-service: true # 忽略原来自身的路由。比如,true: 原来的config/xxx/xxx不可再访问
上述配置将/myConfig/**的请求转发到config服务,将/myUser/**的请求转发到provider服务。
还可以将网关注册到注册中心实现高可用。创建bootstrap.yml文件
1 spring: 2 application: 3 name: gateway 4 cloud: 5 zookeeper: 6 discovery: 7 register: true 8 enabled: true 9 connect-string: 192.168.3.100:2181 10 config: 11 discovery: 12 service-id: config 13 enabled: true
五、配置过滤器
通过zuul添加过滤器,对请求进行拦截或校验等等
1 package com.mike.study.gateway.fiter; 2 3 import com.netflix.zuul.ZuulFilter; 4 import com.netflix.zuul.context.RequestContext; 5 import com.netflix.zuul.exception.ZuulException; 6 import org.apache.commons.lang.StringUtils; 7 import org.springframework.http.HttpStatus; 8 import org.springframework.stereotype.Component; 9 10 import javax.servlet.http.HttpServletRequest; 11 12 import static org.springframework.cloud.netflix.zuul.filters.support.FilterConstants.PRE_DECORATION_FILTER_ORDER; 13 import static org.springframework.cloud.netflix.zuul.filters.support.FilterConstants.PRE_TYPE; 14 15 /** 16 * @Classname ParameterFilter 17 * @Created by Michael 18 * @Date 2023/7/12 19 * @Description 自定义过滤器 20 */ 21 @Component 22 public class ParameterFilter extends ZuulFilter { 23 /** 24 * 前置过滤 25 * @return 26 */ 27 @Override 28 public String filterType() { 29 return PRE_TYPE; 30 } 31 32 /** 33 * 数值越小,优先级越高 34 * @return 35 */ 36 @Override 37 public int filterOrder() { 38 return PRE_DECORATION_FILTER_ORDER - 1; 39 } 40 41 @Override 42 public boolean shouldFilter() { 43 return true; 44 } 45 46 @Override 47 public Object run() throws ZuulException { 48 49 // 获取请求对象 50 RequestContext context = RequestContext.getCurrentContext(); 51 HttpServletRequest request = context.getRequest(); 52 // 获取地址栏传入的参数 53 String token = request.getParameter("token"); 54 System.out.println("token ===>"+token); 55 //模拟校验token,没有token则终止转发 56 if (StringUtils.isEmpty(token)) { 57 context.setSendZuulResponse(false); 58 context.setResponseStatusCode(HttpStatus.UNAUTHORIZED.value()); 59 } 60 return null; 61 } 62 }
以上代码时在转发请求服务前,对参数进行校验,如果没有token参数,则停止转发,并且返回401错误
六、测试
测试网关转发主要需要请求发起方,网关和请求处理方。这里在原来demo的基础上,增加网关,所以看到有配置中心,以往的文章有项目的搭建介绍。如果不想那么复杂,可以去掉配置中心,将配置放置各个module即可,项目组如下
准备请求发起方,即consumer,controller通过feign框架请求网关,网关转发请求provider。consumer的controller如下
1 import com.mike.study.orderconsumer.client.UserClient; 2 import com.mike.study.orderconsumer.vo.UserVo; 3 import org.springframework.web.bind.annotation.GetMapping; 4 import org.springframework.web.bind.annotation.PathVariable; 5 import org.springframework.web.bind.annotation.RequestParam; 6 import org.springframework.web.bind.annotation.RestController; 7 8 import javax.annotation.Resource; 9 10 /** 11 * @Classname UserRuttingController 12 * @Created by Michael 13 * @Date 2023/7/9 14 * @Description feign框架调用provider api 15 */ 16 @RestController 17 public class UserRuttingController { 18 @Resource 19 private UserClient userClient; 20 21 @GetMapping("warp/user/2.1/{id}") 22 public UserVo getUser(@PathVariable("id") Integer userId, @RequestParam("token") String token) { 23 System.out.println("使用feign框架调用provide的api"); 24 return userClient.getUser(userId, token); 25 } 26 }
Feign接口
1 import com.mike.study.orderconsumer.vo.UserVo; 2 import org.springframework.cloud.openfeign.FeignClient; 3 import org.springframework.web.bind.annotation.GetMapping; 4 import org.springframework.web.bind.annotation.PathVariable; 5 import org.springframework.web.bind.annotation.RequestParam; 6 7 /** 8 * @Classname UserFeigClient 9 * @Created by Michael 10 * @Date 2023/7/9 11 * @Description feign client 12 */ 13 14 @FeignClient("gateway") 15 public interface UserClient { 16 17 @GetMapping("myUser/user/{id}") 18 public UserVo getUser(@PathVariable("id") Integer userId,@RequestParam("token") String token); 19 }
以上Feign接口配合注册中心zookeeper使用,访问的网关地址,而不是直接请求服务提供者的api。
provider module简单提供一个api查询用户信息。
六、测试结果
positive case:token参数有值
negative case:token参数i为空
总结,就是consumer不再直接请求provider server,而是通过zuul作为中转站,这样做的好处很多,比如,对consumer的请求进行拦截过滤,验证,降流等等。