SpringCloud详解 第五章 API网关服务zuul(五)
本章梳理动态加载
一、动态过滤器
既然通过 Zuul 构建的API网关服务能够轻松地实现动态路由的加载,那么对于API网关服务的另外 一 大重要功能 一— 请求过滤器的动态加载自然也不能放过, 只是对于请求过滤器的动态加载与请求路由的动态加载在实现机制上会有所不同。 这个不难理解,通过之前介绍的请求路由和请求过滤的示例, 我们可以看到请求路由通过配置文件就能实现,而请求过滤则都是通过编码实现。 所以,对于实现请求过滤器的动态加载, 我们需要借助基于NM实现的动态语言的帮助, 比如Groovy。
基于上文的演示 Demo中进行改造
1.添加依赖:版本在cloud内有定义
<dependency>
<groupId>org.codehaus.groovy</groupId>
<artifactId>groovy-all</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>
2.为了方便使用, 我们先自定义 一 些用来配置动态加载过滤器的参数, 并将它们的配置值加入到 application.properties 中, 比如:
zuul.filter.root=filter//指定动态加载的过滤器存储路径 zuul.filter.interval=5//配置动态加载的间隔时间, 以秒为单位。
创建用来加载自定义属性的配置类, 命名为 FilterConfiguration, 具体内容如下:
@ConfigurationProperties("zuul.fi1ter") public class FilterConfiguration { private String root; private Integer interval; //......getter/setter }
创建应用启动主类, 并在该类中引入上面定义的 FilterConfiguration 配置,并创建动态加载过滤器的实例。 具体内容如下:
@SpringBootApplication @EnableZuulProxy @EnableDiscoveryClient @EnableConfigurationProperties({FilterConfiguration.class}) public class ZuulApp { private final static Logger log = LoggerFactory.getLogger(ZuulApp.class); public static void main(String[] args) { SpringApplication.run(ZuulApp.class, args); log.info("服务启动成功"); } @Bean public FilterLoader filterLoader(FilterConfiguration filterConfiguration) { FilterLoader filterLoader = FilterLoader.getInstance(); filterLoader.setCompiler(new GroovyCompiler()); try { FilterFileManager.setFilenameFilter(new GroovyFileFilter()); FilterFileManager.init( filterConfiguration.getInterval(), filterConfiguration.getRoot() + "/pre", filterConfiguration.getRoot() + "/post"); } catch (Exception e) { throw new RuntimeException(e); } return filterLoader; } }
至此, 我们就已经完成了为基础的API网关服务增加动态加载过滤器的能力。 根据上面的定义, API 网关应用 会每隔 5 秒, 从 API 网关服务所在位置的 filter/pre 和filter/post目录下获取 Groovy 定义的过滤器,并对其进行编译和动态加载使用。 对于动态加载的时间间隔,可通过 zuul.filter.interval 参数来修改。而加载过滤器实现类的根目录可通过 zuul.filter.root 调整根目录的位置来修改, 但是对于根目录的子目录, 这里写死了读取 /pre 和 /post 目录
3.在 filter/pre 目录下创建 一 个 pre 类型的过滤器,命名为 PreFilter.groovy 。
public class PreFilter extends ZuulFilter { Logger log = LoggerFactory.getLogger(PreFilter.class); @Override public String filterType() { return "pre"; } @Override public int filterOrder() { return 1000; } @Override public boolean shouldFilter() { return true; } @Override public Object run() throws ZuulException { HttpServletRequest request = RequestContext.getCurrentContext().getRequest(); log.info("this is a pre filter: Send{ } request to{}", request.getMethod(), request.getRequestURL().toString()); return null; } }
4.在加入了该过滤器之后, 不需要重启 API 网关服务, 只需要稍等几秒就会生效。 我们可 以继续尝 试 向 API 网 关 服 务 发 起 请 求: http://localhost:9110/fegin/hello, 此时在控制台中可以看到 PreFilter.groovy 过滤器中定义的日志信息, 具体如下:
API网关服务的动态过滤器功能可以帮助我们增强API网关的待续服务能力, 对于网关中的处理逻辑维护也变得更为灵活, 不仅可以动态地实现请求校验, 还可以动态地实现对请求内容的干预。不过, 目前在实际使用过程中, 对于处理 一 些简单的常用过滤功能还是没有什么问题的, 只是需要注意 一 些已知的问题并避开这些情况来使用即可。 比如, 在使用Groovy定义动态过滤器的时候, 删除Groovy文件并不能从当前运行的API网关中移除这个过滤器, 所以如果要移除的话可以通过修改Groovy过滤器的shouldFilter返回false。 另外还需要注意 一 点, 目前的动态过滤器是无法直接注入API 网关服务的Spring容器中加载的实例来使用的, 比如, 我们是无法直接通过注入RestTemplate等实例, 在动态过滤器中对各个微服务发起请求的 。
二、动态路由
通过之前对请求路由的详细介绍, 我们可以发现对于路由规则的控制几乎都可以在配置文件application.properties或application.yaml中完成。 既然这样, 对于如何实现Zuul的动态路由,我们很自然地会将它与SpringCloud Config的动态刷新机制联系到 一 起。 只需将API网关服务的配置文件通过Spring Cloud Config连接的Git仓库存储和管理, 我们就能轻松实现动态刷新路由规则的功能。直接上硬货。
Config-Server搭建:
1.创建springboot项目,添加依赖:
<properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding> <java.version>1.8</java.version> </properties> <dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-config</artifactId> </dependency> <dependency><!--不注册到eureka上可以不要--> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-config-server</artifactId> </dependency> </dependencies>
2.配置 application.properties:
server.port = 8888 spring.application.name=config spring.profiles.active=git #github地址 spring.cloud.config.server.git.uri=https://github.com/wuzhenzhao/spring-cloud-config-repo.git #配置文件在哪个目录下开始寻找 spring.cloud.config.server.git.search-paths=properties # master spring.cloud.config.server.git.default-label=master #账号 spring.cloud.config.server.git.username=********* #密码 spring.cloud.config.server.git.password=********
3.启用config server
@SpringBootApplication @EnableConfigServer public class App { private final static Logger log = LoggerFactory.getLogger(App.class); public static void main(String[] args) { SpringApplication.run(App.class, args); log.info("服务启动成功"); } }
这样子 config-server就搭建好了。为了更清晰的配置动态路由,这里重新创建一个动态路由的zuul服务.
dynamic-zuul-server 搭建:
1.创建boot服务,添加依赖:
<properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding> <java.version>1.8</java.version> </properties> <dependencies> <!-- https://mvnrepository.com/artifact/org.springframework.cloud/spring-cloud-starter-zuul --> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-zuul</artifactId> <version>1.4.6.RELEASE</version> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-eureka-server</artifactId> </dependency> <dependency> <groupId>org.codehaus.groovy</groupId> <artifactId>groovy-all</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-configuration-processor</artifactId> <optional>true</optional> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-config</artifactId> </dependency> </dependencies>
2.在/resource文件夹下创建一个 bootstrap.properties 文件:
#应用名称,默认根据这个去获取配置文件,文件名称为dynamic-zuul-server.properties spring.application.name=dynamic-zuul-server server.port = 9111 spring.cloud.config.uri = http://localhost:8888/ #开启这个端点,便于等等我们查看配置 management.endpoints.web.exposure.include = routes #Eureka服务地址 eureka.client.serviceUrl.defaultZone = http://localhost:7001/eureka/,http://localhost:7002/eureka/
3.修改启动类,并且开启动态更新配置
@SpringBootApplication @EnableZuulProxy public class DynamicZuulApp { private final static Logger log = LoggerFactory.getLogger(DynamicZuulApp.class); public static void main(String[] args) { SpringApplication.run(DynamicZuulApp.class, args); log.info("服务启动成功"); } @RefreshScope @ConfigurationProperties("zuul") public ZuulProperties zuulProperties() { return new ZuulProperties(); } }
在完成了所有程序相关的编写之后, 我们还需要在 Git 仓库中增加网关的配置文件,取名为 dynamic-zuul-server.properties。 在配置文件中, 我们为 API 网关服务预定义以下路由规则, 比如:
zuul.routes.feign.serviceId = feign-server zuul.routes.feign.path = /fegin/**
在完成了上述内容之后我们启动服务,在 dynamic-zuul-server 服务启动后,我们可以看到 config-server 应用的控制台打印出下列信息,说明配置没问题:
可以通过对 API 网关服务调用/routes 接口来获取当前网关上的路由规则, 根据上述配置我们可以得到如下返回信息:其他两个是系统生成的。
这个时候我们通过 http://localhost:9111/feign/hello 访问也是没问题的。
修改 Git 仓库中的 dynamic-zuul-server.properties 配置文件,在修改完 配 置文件之后, 将修 改内容推送到远程仓 库。 然后, 通过向dynamic-zuul-server 的 /refresh 接口发送 POST 请求来刷新配置信息。这里需要打开refresh端点:
management.endpoints.web.exposure.include = routes,refresh
当配置文件有修改的时候,该接口会返回被修改的属性名称,根据上面的修改,我们会得到如下返回信息:
再访问 http://localhost:9111/actuator/routes ,就会发生信息变化。当然现在接口的转发路由就会变化。