SpringCloud系列八:Zuul 路由访问(Zuul 的基本使用、Zuul 路由功能、zuul 过滤访问、Zuul 服务降级)
声明:本文来源于MLDN培训视频的课堂笔记,写在这里只是为了方便查阅。
1、概念:Zuul 路由访问
2、具体内容
在现在为止所有的微服务都是通过 Eureka 找到的,但是在很多的开发之中为了规范微服务的使用,提供有一个路由的处理控制组件:Zuul,也就是说 Zuul 就作为中间的一个代理层出现。
2.1、Zuul 的基本使用
本次使用 Zuul 将访问无安全认证的微服务信息,例如:公司信息就属于无安全认证的微服务;
1、 为了突出 zuul 的功能,建立一个新的主机映射:
127.0.0.1 gateway-9501.com
以后所有的微服务的访问不再直接进行处理了,而是通过 Zuul 进行跳转后获得
2、 建立一个新的模块:microcloud-zuul-gateway-9501;
3、 【microcloud-zuul-gateway-9501】修改 pom.xml 文件,追加 zuul 相关依赖包:
· 注意:Zuul 服务最终还是会注册到 Eureka 之中,那么千万要将其配置好;
<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-zuul</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-eureka</artifactId> </dependency>
4、 【microcloud-zuul-gateway-9501】修改 application.yml 配置文件:
server:
port: 9501
eureka:
client: # 客户端进行Eureka注册的配置
service-url:
defaultZone: http://edmin:studyjava@eureka-7001.com:7001/eureka,http://edmin:studyjava@eureka-7002.com:7002/eureka,http://edmin:studyjava@eureka-7003.com:7003/eureka
instance:
lease-renewal-interval-in-seconds: 2 # 设置心跳的时间间隔(默认是30秒)
lease-expiration-duration-in-seconds: 5 # 如果现在超过了5秒的间隔(默认是90秒)
instance-id: gateway-9501.com # 在信息列表时显示主机名称
prefer-ip-address: true # 访问的路径变为IP地址
info:
app.name: study-microcloud
company.name: www.study.cn
build.artifactId: $project.artifactId$
build.version: $project.verson$
spring:
application:
name: microcloud-zuul-gateway
5、 【microcloud-zuul-gateway-9501】创建程序启动的主类:
package cn.study.microcloud; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.netflix.zuul.EnableZuulProxy; @SpringBootApplication @EnableZuulProxy public class Zuul_9501_StartSpringCloudApplication { public static void main(String[] args) { SpringApplication.run(Zuul_9501_StartSpringCloudApplication.class, args); } }
可以看到【microcloud-zuul-gateway-9501】已经注册到eureka中了
6、 访问公司微服务信息:
· 原始访问路径:http://company-8101.com:8101/company/get/hello;
· Zuul 代理访问:http://gateway-9501.com:9501/microcloud-provider-company/company/get/hello;
现在使用 Zuul 代理的只有 company,因为 dept 上有安全认证不能访问。
2.2、Zuul 路由功能
整体的 Zuul 运行之后你会发现,zuul 所实现就是一个代理功能,那么现在就会出现一个问题,例如:以之前访问的路径为例:
http://gateway-9501.com:9501/microcloud-provider-company/company/get/hello
此时必须要知道应用程序的名称,但是如果不知道这个名称肯定无法访问,可是如果让用户知道这个名称,那么使用 zuul 就 没有任何的实际意义,直接调用即可。而 zuul 的主要功能是代理,那么代理的功能就是不让用户看见真实的操作,所以在实际的使用之中就需要为 zuul 设置一些路由规则。
1、 【microcloud-zuul-gateway-9501】为指定的应用设置路径,修改 application.yml 配置文件:
zuul: routes: microcloud-provider-company: /company-proxy/**
那么此时就可以通过“/company-proxy”来访问“microcloud-provider-company”名称。
http://gateway-9501.com:9501/company-proxy/company/get/hello
但是现在还会存在有一个实际的问题,虽然现在开启了路由访问支持,但是依然支持通过应用名称进行访问:
http://gateway-9501.com:9501/microcloud-provider-company/company/get/hello
2、 【microcloud-zuul-gateway-9501】修改 application.yml 配置文件忽略掉应用名称访问:
· 忽略掉“microcloud-provider-company”应用名称;
zuul: ignored-services: microcloud-provider-company routes: microcloud-provider-company: /company-proxy/**
这个时候就可以进行代理的安全使用,但是如果你一个系统之中存在有几百个微服务,如果按照如上的方式进行配置就会非 常的麻烦,所以最简单的做法是可以采用一个通配符“*”的模式来完成:
zuul: ignored-services: "*" routes: microcloud-provider-company: /company-proxy/**
现在表示所有的 Eureka 中的服务名称的信息访问都要忽略掉,所有的访问都需要配置一个映射路径的模式来完成。
3、 【microcloud-zuul-gateway-9501】除了以上的模式进行服务定义之外,在 zuul 之中也可以采用如下的方式进行处理:
zuul: ignored-services: "*" routes: mycompany.path: /company-proxy/** mycompany.serviceId: microcloud-provider-company
其中在代码里面出现的“mycompany”是一个逻辑名称,该名称的主要作用是将 path 与 serviceId 绑定在一起。
4、 【microcloud-zuul-gateway-9501】如果说现在不想通过 Eureka 进行访问,则也可以直接连接到 company 微服务的地址
zuul: ignored-services: "*" routes: company.path: /company-proxy/** company.url: http://company-8101.com:8101/company
此时的地址上由于已经存在有了“company”前缀,所以访问地址为:
http://gateway-9501.com:9501/company-proxy/get/hello
但是从实际的开发来讲不建议采用此类模式处理,因为所有的服务如果直接绑定了指定的服务提供者的地址,那么将不方便 进行负载均衡的配置处理,而且没有 Eureka 所有微服务的管理也非常不方便。
5、 【microcloud-zuul-gateway-9501】设置公共前缀:
zuul: prefix: /study-proxy ignored-services: "*" routes: microcloud-provider-company: /company-proxy/**
一旦存在有前缀定义之后所有微服务的访问上就必须追加有前缀名称:
http://gateway-9501.com:9501/study-proxy/company-proxy/company/get/hello
以上的地址:
· “/study-proxy”:整个 zuul 的前缀;
· “/company-proxy”:是在 zuul 中定义的映射路径;
· “/company/get/hello”:是微服务提供者提供的操作路径。
2.3、zuul 过滤访问
对于 zuul 的功能本质上就属于一个代理操作,但是在实际的使用之中,所有的微服务一定都要有自己的认证信息,那么在这 样的状态下,如果你当前所代理的微服务具有认证信息,那么就必须在其访问前追加认证的头部操作,这样的功能就需要通过 zuul的过滤操作完成。
1、 【microcloud-zuul-gateway-9501】修改 application.yml 配置,这个配置之中追加 dept 微服务的代理;
zuul: prefix: /study-proxy ignored-services: "*" routes: microcloud-provider-company: /company-proxy/** microcloud-provider-dept: /dept-proxy/**
此时的访问路径:http://studyjava:hello@gateway-9501.com:9501/study-proxy/dept-proxy/dept/get/1;现在的密码只是设置给了 zuul,而 zuul 并不能够将认证的信息传递到部门微服务之中。
2、 【microcloud-zuul-gateway-9501】追加过滤处理操作:
package cn.study.microcloud.filter; import java.nio.charset.Charset; import java.util.Base64; import com.netflix.zuul.ZuulFilter; import com.netflix.zuul.context.RequestContext; public class AuthorizedRequestFilter extends ZuulFilter { // 进行授权访问处理 @Override public Object run() { // 表示具体的过滤执行操作 RequestContext currentContext = RequestContext.getCurrentContext() ; // 获取当前请求的上下文 String auth = "studyjava:hello"; // 认证的原始信息 byte[] encodedAuth = Base64.getEncoder() .encode(auth.getBytes(Charset.forName("US-ASCII"))); // 进行一个加密的处理 // 在进行授权的头信息内容配置的时候加密的信息一定要与“Basic”之间有一个空格 String authHeader = "Basic " + new String(encodedAuth); currentContext.addZuulRequestHeader("Authorization", authHeader); return null; } @Override public boolean shouldFilter() { // 该Filter是否要执行 return true ; } @Override public int filterOrder() { return 0; // 设置优先级,数字越大优先级越低 } @Override public String filterType() { // 在进行Zuul过滤的时候可以设置其过滤执行的位置,那么此时有如下几种类型: // 1、pre:在请求发出之前执行过滤,如果要进行访问,肯定在请求前设置头信息 // 2、route:在进行路由请求的时候被调用; // 3、post:在路由之后发送请求信息的时候被调用; // 4、error:出现错误之后进行调用 return "pre"; } }
3、 【microcloud-zuul-gateway-9501】建立一个配置程序类作为认证请求的配置 Bean。
package cn.study.microcloud.config; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import cn.study.microcloud.filter.AuthorizedRequestFilter; @Configuration public class ZuulConfig { @Bean public AuthorizedRequestFilter getAuthorizedRequestFilter() { return new AuthorizedRequestFilter() ; } }
那么这个时候就意味着你现在的程序可以直接利用 zuul 的代理访问所有加密的微服务。
4、 【microcloud-zuul-gateway-9501】考虑到 zuul 也需要进行安全访问,所以应该修改项目中的 pom.xml 配置文件,追加 Spring 安全访 问配置处理操作:
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-security</artifactId> </dependency>
5、 【microcloud-zuul-gateway-9501】修改 application.yml 配置文件,最佳有用户信息配置:
security:
basic:
enabled: true
user:
name: zdmin
password: studyjava
那么此时现在表示 zuul 的代理上有了认证信息,则访问的地址上必须加上 zuul 的认证操作:
http://zdmin:studyjava@gateway-9501.com:9501/study-proxy/dept-proxy/dept/get/1
6、 【microcloud-service】现在所有的服务要通过 zuul 的代理来进行操作对于代理的配置如果要通过 feign 进行访问,那么在编写 feign 的时候就必须设置代理的服务名称;
package cn.study.service; import java.util.List; import org.springframework.cloud.netflix.feign.FeignClient; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import cn.study.commons.config.FeignClientConfig; import cn.study.service.fallback.IDeptClientServiceFallbackFactory; import cn.study.vo.Dept; @FeignClient(value = "MICROCLOUD-ZUUL-GATEWAY", configuration = FeignClientConfig.class, fallbackFactory = IDeptClientServiceFallbackFactory.class) public interface IDeptClientService { @RequestMapping(method = RequestMethod.GET, value = "/study-proxy/dept-proxy/dept/get/{id}") public Dept get(@PathVariable("id") long id); @RequestMapping(method = RequestMethod.GET, value = "/study-proxy/dept-proxy/dept/list") public List<Dept> list(); @RequestMapping(method = RequestMethod.POST, value = "/study-proxy/dept-proxy/dept/add") public boolean add(Dept dept); }
7、 【microcloud-service】修改服务的配置类,测试访问的应该是 zuul 的地址:
package cn.study.commons.config; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import feign.Logger; import feign.auth.BasicAuthRequestInterceptor; @Configuration public class FeignClientConfig { @Bean public Logger.Level getFeignLoggerLevel() { return feign.Logger.Level.FULL ; } @Bean public BasicAuthRequestInterceptor getBasicAuthRequestInterceptor() { return new BasicAuthRequestInterceptor("zdmin", "studyjava"); } }
8、 【microcloud-zuul-gateway-9501】默认情况下只要配置了过滤器,就可以进行一个正常的启动,如果现在有些过滤器突然不想让它 用了,则也可以通过修改 application.yml 配置文件让其禁用:
zuul: AuthorizedRequestFilter: pre: disable: true
此时表示“AuthorizedRequestFilter”过滤器将被禁止使用。如果以后有多个过滤服务出现的话,可以通过配置文件实现过滤 的启用与禁用控制。
2.4、Zuul 服务降级
Zuul 是一个代理服务,那么如果被代理的服务突然断掉了,那么这个时候 zuul 上面一定会显示出错误信息。例如:现在停止 掉“dept-8001:8001”端口上的微服务。
如果现在程序无法使用,则 zuul 的代理执行时就会出现有 timeout 的信息。但是千万要记住,由于现在的客户端已经提供有了 feign 中的服务降级的配置支持,所以客户端没有任何的问题,问题只出现在代理端。但是对于一个完善的 zuul 的代理应该更好的 实现服务降级的处理操作,所以如果有需要也可以在 zuul 中进行服务降级配置。
1、 【microcloud-zuul-gateway-9501】建立一个 Fallback 的回退处理类。
package cn.study.microcloud.fallback; import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.InputStream; import org.springframework.cloud.netflix.zuul.filters.route.ZuulFallbackProvider; import org.springframework.http.HttpHeaders; import org.springframework.http.HttpStatus; import org.springframework.http.client.ClientHttpResponse; import org.springframework.stereotype.Component; @Component public class DeptProviderFallback implements ZuulFallbackProvider { @Override public String getRoute() { return "microcloud-provider-dept"; // 设置好处理的失败路由 } @Override public ClientHttpResponse fallbackResponse() { return new ClientHttpResponse() { @Override public InputStream getBody() throws IOException { return new ByteArrayInputStream( "{\"deptno\":777777,\"dname\":\"【ERROR】Zuul-Fallback\",\"loc\":\"Gateway客户端提供\"}" .getBytes()); // 当出现服务调用错误之后返回的数据内容 } @Override public HttpHeaders getHeaders() { HttpHeaders headers = new HttpHeaders() ; headers.set("Content-Type", "text/html; charset=UTF-8"); return headers; } @Override public HttpStatus getStatusCode() throws IOException { return HttpStatus.BAD_REQUEST; } @Override public int getRawStatusCode() throws IOException { return HttpStatus.BAD_REQUEST.value(); } @Override public String getStatusText() throws IOException { return HttpStatus.BAD_REQUEST.getReasonPhrase(); } @Override public void close() { }}; } }
2、 【microcloud-zuul-gateway-9501】直接访问地址:
http://zdmin:studyjava@gateway-9501.com:9501/study-proxy/dept-proxy/dept/get/1
由于此时你返回的都是错误的代码,则客户端接收到此代码之后会认为服务器端已经死掉了。