SpringCloud 之 Zuul 基础使用与进阶
发布日期:2021-06-30 18:35:02 浏览次数:2 分类:技术文章

本文共 7827 字,大约阅读时间需要 26 分钟。

SpringCloud 之 Zuul 基础配置与进阶

简介

Alt

基础使用

PS:zuul 基本需要配合 Eureka 使用,就不多介绍了:

准备

服务A

服务名:service-a
端口号:8080

服务B

服务名:service-b
端口号:8081

zuul 服务

服务名:zuul
端口号:8084

加依赖

org.springframework.cloud
spring-cloud-starter-netflix-zuul

启动器加注释

@EnableZuulProxy

配置

PS:Eureka 配置就不写了,和上面 Eureka 里面客户端配置一样

#配置端口号server:  port: 8084spring:  application:    #配置服务名    name: zuul

日志查看

# netflix ⽇志logging:  level:    com.netflix: DEBUG

如无法掌握Zuul路由的规律,可将com.netflix包的日志级别设为DEBUG。这样,Zuul就会打印 转发的具体细节,从而帮助我们更好地理解Zuul的路由配置

**举个栗子**
意思:service-a返回服务器:172.0.0.1:8080 的请求是 /api/1

不加额外配置

在不加多余的配置文件配置时, zuul 就已经可以使用了,我们可以通过 IP + 服务名 的方式结合 Eureka 去访问,如图:

在这里插入图片描述 在这里插入图片描述

自定义服务访问以及服务忽略

虽然说不加配置就可以使用了,但是 Eureka 中所有的服务都能访问到这显然很不合理,也不安全

因此可以通过自定义服务与服务忽略结合,来提高可用性
ignored-services: '*' 表示忽略所有服务的前提下,只对配置的服务进行路由
ignored-services: service-a,service-b 当然也可以指定服务,逗号隔开
ignoredPatterns: /**/admin/** 如果想使用通配符进行忽略,那就要换 ignoredPatterns 才行
这个是忽略所有包含 /admin/ 的路径

# zuul 配置zuul:  # 使⽤'*'可忽略所有微服务  # 通过此配置使得只有通过 routes 配置的路由才生效,更安全  ignored-services: '*'  routes:    # 配置 service-a 服务的 访问路径为 /service-a/**    service-a: /service-a/**

现在来访问下 service-a

在这里插入图片描述
再来访问下 service-b
在这里插入图片描述
可以看到,由于我们忽略了所有的服务后,只配置了service-a的配置,因此service-a能请求到,但是service-b就不行了

自定义路由名配置

这种配置效果和上面横写是一样的,访问路径同样是 http://127.0.0.1:8080/service-a/xxx

# zuul 配置zuul:  ignored-services: '*'  routes:    # 给路由⼀个名称,可以任意起名    # 通过服务名配置路由    service-a:      service-id: service-a # 指定服务名      path: /service-a/** # 服务名对应的路径

直接通过 URL 配置(有缺陷)

我们也可以直接通过 URL 进行配置,以 service-b 为例

# zuul 配置zuul:  ignored-services: '*'  routes:    # 通过 URL 配置路由    service-b:      url: http://127.0.0.1:8081 # 指定的url      path: /service-b/** # url对应的路径

在这里插入图片描述

PS:使用这种方式配置的路由不会作为 HystrixCommand 执行,同时也不能使用 Ribbon 来负载均衡多个URL

直接通过 URL 配置(无缺陷)

# zuul 配置zuul:  ignored-services: '*'  routes:    # 改为通过服务名去匹配    service-b:      service-id: service-b # 指定的服务名      path: /service-b/** # url对应的路径# 禁⽤掉ribbon的eureka使⽤ribbon:  eureka:    # 详⻅:http://cloud.spring.io/spring-cloud-static/Camden.SR4/#_example_disable_eureka_use_in_ribbon    enabled: false# 把 service-b 这个服务名与 IP 进行映射service-b:  ribbon:    listOfServers: localhost:8081,localhost:8082

路由前缀

请求路径:http://127.0.0.1:8080/api/service-a/1

实际请求路径:http://127.0.0.1:8080/service-a/api/1

这种配置估计应用场景应该是不同版本的接口切换吧,切换版本的时候,前端直接在统一配置里把api换掉,所有请求接口就切到新的版本上

# Zuul 配置zuul:  ignored-services: '*'  # 路由前缀,把 /api/service-a/1 映射到 /service-a/api/1  prefix: /api  strip-prefix: false  routes:  	service-a:      service-id: service-a # 指定服务名      path: /service-a/** # 服务名对应的路径

可以看到请求正常

在这里插入图片描述
再来看下日志输出
在这里插入图片描述
可以看到实际路径是http://127.0.0.1:8080/service-a/api/1

进阶配置

正则表达式指定Zuul的路由匹配规则

借助PatternServiceRouteMapper,实现从微服务到映射路由的正则配置

servicePattern 指定微服务的正则
routePattern 指定路由正则
正则怎么写,不会。。。

// zuul@EnableZuulProxy// Eureka 客户端@EnableDiscoveryClient// 由于没数据库,排除配置@SpringBootApplication(exclude={
DataSourceAutoConfiguration.class})public class ZuulApplication {
public static void main(String[] args) {
SpringApplication.run(ZuulApplication.class, args); } // 正则表达式指定Zuul的路由匹配规则 @Bean public PatternServiceRouteMapper serviceRouteMapper() {
// 调⽤构造函数PatternServiceRouteMapper(String servicePattern, String routePattern) // servicePattern指定微服务的正则 // routePattern指定路由正则 // 最后一个 "-" 后面以 v 打头的为 version,之前的都是 name return new PatternServiceRouteMapper("(?
^.+)-(?
v.+$)", "${version}/${name}"); }}

自定义 Zuul 拦截器

zuul 除了帮助我们进行路由转发的功能外,还给我们提供了一个过滤器,我们可以通过继承 ZuulFilter 来实现 zuul 过滤器

我们可以通过这个过滤器来实现授权拦截,限流,日志记录等功能
filterType 指定过滤器的调用时机,方便我们在需要的时间段调用过滤器
filterOrder 指定过滤器执行的先后顺便,比如有两个 pre 的过滤器,就可以通过这个返回值控制两个过滤器的先后顺序
shouldFilter 指定过滤器是否使用
run 具体的过滤逻辑
这里我以一个简单的授权过滤器为例子

/** * Zuul 过滤器 * 授权验证示例 * @author: linjinp * @create: 2019-09-12 09:42 **/@Componentpublic class AuthFilter extends ZuulFilter {
private static final Logger LOGGER = LoggerFactory.getLogger(AuthFilter. class); /** * 是否开启验证 * 正常项目里,这种属性应该放配置文件里 */ private static final Boolean AUTH = Boolean.TRUE; /** * 指定过滤器的调用时机 * pre: 路由之前,如实现认证,记录调试信息等 * routing: 路由时 * post: 路由后,比如添加HTTP header * error: 发生错误时调用 * * @return */ @Override public String filterType() {
return FilterConstants.PRE_TYPE; } /** * 过滤器顺序 * 比如有两个 pre 的过滤器,可以通过设置数字大小,控制两个过滤器执行先后 * * @return */ @Override public int filterOrder() {
return 1; } /** * 判断是否启用该过滤 * * @return */ @Override public boolean shouldFilter() {
return true; } /** * 过滤的具体逻辑 * * @return * @throws ZuulException */ @Override public Object run() {
RequestContext ctx = RequestContext.getCurrentContext(); HttpServletRequest request = ctx.getRequest(); // 启动验证 if (AUTH) {
String token = request.getHeader("Authorization"); if (token == null) {
LOGGER.info("该访问未进行授权"); // 路由失败 ctx.setSendZuulResponse(false); // 返回错误码 ctx.setResponseStatusCode(401); } else {
LOGGER.info("访问已授权"); // 验证成功 ctx.setSendZuulResponse(true); ctx.setResponseStatusCode(200); } } else {
LOGGER.info("访问成功"); // 没启用就直接成功 ctx.setSendZuulResponse(true); ctx.setResponseStatusCode(200); } return null; }}

看看日志打印

在这里插入图片描述

禁用自定义拦截器

除了在拦截器里直接关闭外,我们也可以通过在配置文件里配置关闭拦截器

zuul.<SimpleClassName>.<filterType>.disable=true

比如我要关闭上面的 AuthFilter 拦截器

zuul:  # AuthFilter 拦截器开关  AuthFilter:    pre:      disable: true

容错与回退

当我们使用 zuul 时,难免会出现接口因为各种原因请求不通的情况,比如服务宕机了之类的

为了保证系统的健壮性,我们可以通过实现 FallbackProvider 方法,来自定义返回内容

  • Edgware版本写法:实现 FallbackProvider
  • Edgware之前版本:实现 ZuulFallbackProvider
/** * 容错与回退 * @author: linjinp * @create: 2019-09-12 11:27 **/@Componentpublic class CommonFallbackProvider implements FallbackProvider {
private static final Logger LOGGER = LoggerFactory.getLogger(AuthFilter. class); /** * 需要回退的微服务,"*" 表示所有 * * @return */ @Override public String getRoute() {
return "*"; } /** * 回退逻辑 * @param route * @param cause * @return */ @Override public ClientHttpResponse fallbackResponse(String route, Throwable cause) {
if (cause instanceof HystrixTimeoutException) {
return response(HttpStatus.GATEWAY_TIMEOUT); } else {
return response(HttpStatus.INTERNAL_SERVER_ERROR); } } /** * 自定义返回值内容 * @param status * @return */ private ClientHttpResponse response(final HttpStatus status) {
return new ClientHttpResponse() {
@Override public HttpStatus getStatusCode() throws IOException {
return status; } @Override public int getRawStatusCode() throws IOException {
return status.value(); } @Override public String getStatusText() throws IOException {
return status.getReasonPhrase(); } @Override public void close() {
} @Override public InputStream getBody() throws IOException {
LOGGER.info("服务不可⽤,请稍后再试"); return new ByteArrayInputStream("服务不可⽤,请稍后再试。".getBytes()); } @Override public HttpHeaders getHeaders() {
// headers设定 HttpHeaders headers = new HttpHeaders(); MediaType mt = new MediaType("application", "json", Charset.forName( "UTF-8")); headers.setContentType(mt); return headers; } }; }}

现在我们把 service-a 停掉,然后请求试试,已模拟服务宕机

在这里插入图片描述

转载地址:https://linjinp.blog.csdn.net/article/details/100736962 如侵犯您的版权,请留言回复原文章的地址,我们会给您删除此文章,给您带来不便请您谅解!

上一篇:Navicat 连接 sqlserver 带端口号配置
下一篇:Tomcat 内存优化

发表评论

最新留言

初次前来,多多关照!
[***.217.46.12]2024年04月07日 14时26分45秒

关于作者

    喝酒易醉,品茶养心,人生如梦,品茶悟道,何以解忧?唯有杜康!
-- 愿君每日到此一游!

推荐文章