SpringCloud Zuul学习
作者:互联网
文章目录
SpringCloud Zuul学习
SpringCloud API网关Zuul
技术说明
-
在SpringCloud2020.0.1发布之后,由于Netflix的技术在2019年就开始停更维护,于是SpirngCloud决定对于Netflix的技术全部被抛弃,其中包括zuul组件,官方推荐使用
Spring Gateway
代替Zuul
,并且国内大部分公司也开始替换API网关技术,综上,Zuul
开始过时,可以将更多的精力转向SpringGateway
上,Zuul
只用来学习,了解。 -
由于版本迭代更新和弃用,学习成本高,如果要完全实现Zuul,则需要将
SpringCloud
版本降至2020.0之前,SpringBoot
版本降至2.2~2.3
SpringCloud Zuul
- Spring Cloud 这个一站式的微服务开发框架基于 Netflix Zuul 实现了 Spring Cloud Zuul,采用 Spring Cloud Zuul 即可实现一套 API 网关服务。
- zuul 是netflix开源的一个API Gateway 服务器, 本质上是一个web servlet应用
上图中,有feign声明式服务消费,SpringCloud LoadBalance实现负载均衡,Hystrix实现服务熔断
zuul的API网关服务在该图中可以提供:
- 管理对外,对内接口调用,即请求路由
- 权限管理
搭建SpringCloud Zuul服务
导入依赖
<!--导入Eureka客户端依赖-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<!--springcloud-zuul依赖-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-zuul</artifactId>
</dependency>
依赖说明
由于2020年后Netflix组件被SpringCloud抛弃,如果要真正实现Zuul,SpringCloud版本有要求在2020.0.1之前
-
SpringCloud版本
2020.0.1之前
-
SpringBoot版本
由于需要对应SpringCloud版本,所以需要在2.2~2.3之间
如果不如此选择,许多功能会受限制
比如:
- 如果SpringBoot版本在2.5.0及以上,Zuul服务调用时,会造成
HTTP Status 500 – Internal Server Error
异常错误 - 如果SpringCloud版本在2020.0.1之上(对应SpringBoot版本在2.3之上),会造成
500
转发异常,无法在注册中心发现其他微服务,但实际已经注册在注册中心了 - 不能使用微服务名称来转发,但是能使用url来转发(SpringCloud 2020.0.4,SpringBoot 2.4.8)
启动类开启ZuulAPI网关
// 开启Zuul的API网关服务
@EnableZuulProxy
//@EnableEurekaClient
@SpringBootApplication
public class SpringCloudZuulApplication {
public static void main(String[] args) {
SpringApplication.run(SpringCloudZuulApplication.class, args);
}
}
其中@EnableEurekaClient可以不使用,也会注册在eureka注册中心
Zuul配置类
server:
port: 9100
spring:
application:
# 微服务名称
name: springcloud-zuul
eureka:
client:
service-url:
# 指定Eureka注册中心地址,用于将微服务注册进Eureka服务端
defaultZone: http://eureka-cluster-a:8201/eureka/, http://eureka-cluster-b:8202/eureka/, http://eureka-cluster-c:8203/eureka/
instance:
# 将IP注册到Eureka Server上,而如果不配置ipAddress就是机器的主机名hostname
prefer-ip-address: true
# 使得服务实例在eureka界面增加显示版本号,格式:地址:微服务名:端口号:版本号
instance-id: ${spring.cloud.client.ip-address}:${spring.application.name}:${server.port}:@project.version@
# 每间隔 2s,向服务端发送一次心跳,证明自己依然"存活"
lease-renewal-interval-in-seconds: 2
# 告诉服务端,如果我 10s 之内没有给你发心跳,就代表我故障了,将我踢出掉
lease-expiration-duration-in-seconds: 10
# zuul配置路由规则
zuul:
prefix: /api
routes:
# 自定义的名字,只要保证子属性path和serviceId对应即可
route-feign:
# 访问API网关的时候,输入地址栏的路径uri
path: /route-feign/**
# # 路由转发 对应的微服务名称
# serviceId: springcloud-feign-consumer
# 映射路径对应的url,通过url来路由,耦合度较高
url: http://localhost:9000/
# # 简化方式,服务id和路由名称一致
# springcloud-feign-consumer: /route-feign/**
# 忽略相关接口
ignored-patterns: /**/hello/**
此处配置完后,通过zuul服务,即可访问其他的微服务了
Zuul请求过滤
Spring cloud Zuul 就像一个安检站,所有请求都会经过这个安检站, 所以我们可以在该安检站内实现对请求的过滤
/**
* @author slipperySoap
* @version 1.0
* ClassName: AuthFilter
* Description:
* Date: 2021/12/20 13:16
*/
@Component
public class AuthFilter extends ZuulFilter {
@Resource
ObjectMapper objectMapper;
/**
* 过滤器的类型
* @return pre代表在路由之前执行过滤器
*/
@Override
public String filterType() {
return "pre";
}
/**
* 过滤器的执行顺序,从小到达排序
* @return 当前排序下下标为0
*/
@Override
public int filterOrder() {
return 0;
}
/**
* 过滤器是否执行
* @return true代表当前过滤器打开
*/
@Override
public boolean shouldFilter() {
return true;
}
/**
* 具体过滤逻辑
*
* @return 返回值暂时没有任何意义,可以返回为null
* @throws ZuulException
*/
@SneakyThrows
@Override
public Object run() throws ZuulException {
// 获取请求体
RequestContext ctx = RequestContext.getCurrentContext();
HttpServletRequest request = ctx.getRequest();
// 简单的从请求参数中拿token令牌
String token = request.getParameter("token");
// 简单的过滤逻辑,如果没有令牌就不放行
if (token == null) {
// 设置setSendZuulResponse为false代表非法请求不放行
ctx.setSendZuulResponse(false);
// 设置响应码
ctx.setResponseStatusCode(ResultCode.UNAUTHENTICATED.code());
// 设置请求头中 媒体类型 信息
ctx.addZuulResponseHeader("content-type","application/json;charset=utf-8");
// 设置请求体
Result result = new Result(ResultCode.UNAUTHENTICATED);
String resultJson = objectMapper.writeValueAsString(result);
ctx.setResponseBody(resultJson);
}
return null;
}
}
-
filterType()
方法的返回值为过滤器的类型,过滤器的类型决定了过滤器在哪个生命周期执行
- pre 表示在路由之前执行过滤器
- route 表示在路由之时执行过滤器
- post 表示在路由之后执行过滤器
- error表示路由在发生错误后的过滤器
-
filterOrder()
表示过滤器的执行顺序,当过滤器很多时,我们可以通过 该方法的返回值来指定过滤器的执行顺序
-
shouldFilter()
用来判断过滤器是否执行,true 表示执行,false 表示不 执行
-
run()
表示过滤的具体逻辑
Zuul路由规则
服务名间接/直接指定路由
#配置路由规则
#属性api-wkcto为自定的值,而/api-wkcto/**为指定的请求url
zuul.routes.api-wkcto.path=/api-wkcto/**
zuul.routes.api-wkcto.serviceId=05-springcloud-service-feign
当访问地址符合/api-wkcto/**规则的时候,会被自动定位到
05-springcloud-service-feign
服务上,不过两行代码有点麻烦,还可以简化为
zuul.routes.05-springcloud-service-feign=/api-wkcto/**
zuul.routes 后面跟着的是服务名,服务名后面跟着的是路径规则,这种配置方 式更简单
默认路由
如果映射规则配置我们什么都不写,系统也给我们提供了一套默认的配置规则
等价于这样配置:
#默认的规则等价于
zuul.routes.05-springcloud-service-feign.path=/05-springcloud-service-feign/**
zuul.routes.05-springcloud-service-feign.serviceId=05-springcloud-service-feign
忽略服务的默认路由
不配置映射规则,Eureka上的所有服务会被Zuul默认进行映射规则
而对于部分微服务,我们不想提供对外的服务,可以进行忽略默认路由配置
-
忽略服务
#忽略掉服务提供者的默认规则 zuul.ignored-services=01-springcloud-service-provider
-
忽略指定请求
#忽略掉某一些接口路径 zuul.ignored-patterns=/**/hello/**
路由规则通配符
通配符 | 含义 | 举例 | 说明 |
---|---|---|---|
? | 匹配任意单个字符 | /05-springcloud-service-feign/? | 匹配 /05-springcloud-service-feign/a, /05-springcloud-service-feign/b, /05-springcloud-service-feign/c 等 |
* | 匹配任意数量的字符 | /05-springcloud-service-feign/* | 匹配 /05-springcloud-service-feign/aaa, /05-springcloud-service-feign/bbb, /05-springcloud-service-feign/ccc 等, 无法匹配 /05-springcloud-service-feign/a/b/c |
** | 匹配任意数量的字符 | /05-springcloud-service-feign/** | 匹配 /05-springcloud-service-feign/aaa, /05-springcloud-service-feign/bbb, /05-springcloud-service-feign/ccc 等, 也可以匹配 /05-springcloud-service-feign/a/b/c |
自定义路由业务
一般情况下 API 网关只是作为各个微服务的统一入口,但是有时候我们可能 也需要在 API 网关服务上做一些特殊的业务逻辑处理,那么我们可以让请求到 达 API 网关后,再转发给自己本身,由 API 网关自己来处理,那么我们可以进 行如下的操作
在 06-springcloud-api-gateway 项目中新建如下 Controller:
@RestController
public class GateWayController {
@RequestMapping("/api/local")
// 一些路由业务处理
public String hello() {
return "exec the api gateway.";
}
}
然后在 application.properties 文件中配置:
# 指定路由
zuul.routes.gateway.path=/gateway/**
# 使用url指定转发的路径
zuul.routes.gateway.url=forward:/api/local
Zuul异常处理
Spring Cloud Zuul 对异常的处理是非常方便的,但是由于 Spring Cloud 处于 迅速发展中,各个版本之间有所差异,本案例是以 Finchley.RELEASE 版本为例, 来说明 Spring Cloud Zuul 中的异常处理问题。
首先我们来看一张官方给出的 Zuul 请求的生命周期图:
- 正常情况下所有的请求都是按照 pre、route、post 的顺序来执行,然后由 post 返回 response
- 在 pre 阶段,如果有自定义的过滤器则执行自定义的过滤器
- pre、routing、post 的任意一个阶段如果抛异常了,则执行 error 过滤器
我们可以有两种方式统一处理异常:
1、禁用 zuul 默认的异常处理 SendErrorFilter 过滤器,然后自定义我们自己的
Errorfilter 过滤器
zuul.SendErrorFilter.error.disable=true
@Component
public class ErrorFilter extends ZuulFilter {
private static final Logger logger =
LoggerFactory.getLogger(ErrorFilter.class);
@Override
public String filterType() {
return "error";
}
@Override
public int filterOrder() {
return 1;
}
@Override
public boolean shouldFilter() {
return true;
}
@Override
public Object run() throws ZuulException {
try {
RequestContext context = RequestContext.getCurrentContext();
ZuulException exception = (ZuulException)context.getThrowable();
logger.error("进入系统异常拦截", exception);
HttpServletResponse response = context.getResponse();
response.setContentType("application/json; charset=utf8");
response.setStatus(exception.nStatusCode);
PrintWriter writer = null;
try {
writer = response.getWriter();
writer.print("{code:"+ exception.nStatusCode +",message:\""+
exception.getMessage() +"\"}");
} catch (IOException e) {
e.printStackTrace();
} finally {
if(writer!=null){
writer.close();
}
}
} catch (Exception var5) {
ReflectionUtils.rethrowRuntimeException(var5);
}
return null;
}
}
2、自定义全局 error 错误页面
@RestController
public class ErrorHandlerController implements ErrorController {
/**
* 出异常后进入该方法,交由下面的方法处理
*/
@Override
public String getErrorPath() {
return "/error";
}
@RequestMapping("/error")
public Object error(){
RequestContext ctx = RequestContext.getCurrentContext();
ZuulException exception = (ZuulException)ctx.getThrowable();
return exception.nStatusCode + "--" + exception.getMessage();
}
}
标签:feign,Zuul,service,SpringCloud,学习,springcloud,zuul,路由 来源: https://blog.csdn.net/slipperySoap/article/details/123573633