0


【SpringCloud】之网关应用(进阶使用)

🎉🎉欢迎来到我的CSDN主页!🎉🎉

🏅我是君易--鑨,一个在CSDN分享笔记的博主。📚📚

🌟推荐给大家我的博客专栏《SpringCloud开发之网关应用》。🎯🎯

🎁如果感觉还不错的话请给我关注加三连吧!🎁🎁


前言

  1. 在上一期的博客分享中我们一起了解到了SpringCloud的配置中心的相关知识的学习以及应用的方式,本期的博客分享给大家带来的是SpringCloud的网关应用。

一、什么是网关

1. 基本概述

  1. Spring Cloud GatewaySpring官方基于Spring5.0SpringBoot2.0Project Reactor等技术开发的网关旨在为微服务框架提供一种简单而有效的统一的API路由管理方式,统一访问接口。
  2. Spring Cloud Gateway作为Spring Cloud生态体系中的网关,目标是替代NetflixZuul,其不仅提供统 一的路由方式,并且基于Filter链的方式提供了网关基本的功能,例如:安全、监控/埋点和限流等等。 它是基于Netty的响应式开发模式。

2. 图解

3. 组成结构

1️⃣ 路由(route****):路由是网关最基础的部分,路由信息由一个ID,一个目的URL、一组断言工厂和一组Filter组成。如果断言为真,则说明请求URL和配置的路由匹配。

2️⃣ 断言(Predicate****):Java8中的断言函数,Spring Cloud Gateway中的断言函数输入类型是Spring5.0框架中的ServerWebExchange。Spring Cloud Gateway中的断言函数允许开发者去定义匹配来自http Request中的任何信息,比如请求头和参数等。

3️⃣过滤器(Filter****):一个标准的Spring WebFilter,Spring Cloud Gateway中的Filter分为两种类型:Gateway Filter和Global Filter。过滤器Filter可以对请求和响应进行处理。

4. 应用场景

二、网关的集成使用

1. 新建一个模块作为网关

  1. 我们在主项目下的创建一个SpringCloud的项目作为网关模块。

  1. 创建完成之后我们将网关模块的pom文件中自带的pom文件依赖给去除掉,包括跳过编译也去除掉。

2. pom文件设置

  1. 我们首先在创建好的pom文件中先集成我们主项目的pom文件,导入网关运行服务器的依赖和网关的依赖,但是注意主项目中有没有定义web模块,否则会发生冲突。
  1. <!-- 集成主项目-->
  2. <parent>
  3. <groupId>com.yx</groupId>
  4. <artifactId>cloud</artifactId>
  5. <version>1.0-SNAPSHOT</version>
  6. </parent>
  7. <dependencies>
  8. <!-- 导入网关服务器的依赖-->
  9. <dependency>
  10. <groupId>org.springframework.boot</groupId>
  11. <artifactId>spring-boot-starter-webflux</artifactId>
  12. </dependency>
  13. <!-- 导入网关的依赖-->
  14. <dependency>
  15. <groupId>org.springframework.cloud</groupId>
  16. <artifactId>spring-cloud-starter-gateway</artifactId>
  17. </dependency>
  18. </dependencies>

3. 新建配置文件进行配置

  1. 我们在网关的项目中在main的目录下新建一个resource文件夹,在其文件夹下新建一个application.yml文件进行相应的配置
  1. server:
  2. port: 8082
  3. spring:
  4. application:
  5. name: gateway
  6. cloud:
  7. nacos:
  8. discovery:
  9. server-addr: localhost:8848
  10. gateway:
  11. discovery:
  12. locator:
  13. enabled: true
  14. lower-case-service-id: true

4. 启动类标记使用nacos

  1. 我们记得在网关启动类上的标记启用nacos的注解,启用其nacos

5. 使用网关的方式

5.1 方式一:

1. 对其yml文件配置
  1. 在网关的yml文件中配置下述内容
  1. spring:
  2. application:
  3. # 名称
  4. name: gateway
  5. cloud:
  6. nacos:
  7. discovery:
  8. # 地址
  9. server-addr: localhost:8848
  10. gateway:
  11. discovery:
  12. locator:
  13. enabled: true
  14. lower-case-service-id: true

2. 测试演示
  1. 我们在我们要访问的请求方法中编写一个输入语句便于测试

  1. 我们在网页进行测试

  1. 有上述的动图所示,我们先是访问使用生成者的接口访问其请求方法;然后我们在使用网关的接口访问其生产者的请求接口,该生产者控制台输出对应的输出语句。

5.2 方式二:

1. 对其yml文件进行配置
  1. 我们将之前方式一的yml文件注释掉,配置方式二所需的yml文件
  1. 方式二:
  2. # 设置路由 规则
  3. routes:
  4. # -路由标识
  5. - id: user-provider-api
  6. #目标服务地址(uri:地址,请求转发后的地址),会自动从注册中心获得服务的IP,不需要手动写死
  7. uri: lb://provider
  8. predicates:
  9. - # 路径匹配,
  10. - Path=/prov/**
  11. filters:
  12. #路径前缀删除示例:请求/name/bar/foo,StripPrefix=2,去除掉前面两个前缀之后,最后转
  13. # 发到目标服务的路径为/foo
  14. #前缀过滤,请求地址:http://localhost:8084/usr/hello
  15. #此处配置去掉1个路径前缀,再配置上面的 Path=/usr/**,就将**转发到指定的微服务
  16. #因为这个api相当于是服务名,只是为了方便以后nginx的代码加上去的,对于服务提供者
  17. # service-client来说,不需要这段地址,所以需要去掉
  18. - StripPrefix=1

2. 重启项目测试
  1. 我们先继续访问之前的方式一的请求路径,结果页面上会显示报错404找不到该接口路径,当我们换成方式二的请求路径进行访问就可以访问成功。

  1. 这就是方式二的测试结果显示

5.3 方式三:

1. 编写配置类
  1. 我们先将其方式二的yml文件内注释掉,进行编写方式三的yml文件内容。

  1. 这是属于我们自定义的配置,所以还需要我们自定义的配置类,写在我们的网关服务中
FilterEntity.java
  1. package com.yx.gateway.pojo;
  2. import lombok.AllArgsConstructor;
  3. import lombok.Data;
  4. import lombok.NoArgsConstructor;
  5. import lombok.experimental.Accessors;
  6. import java.util.LinkedHashMap;
  7. import java.util.Map;
  8. /**
  9. * @author hgh
  10. */
  11. @SuppressWarnings("all")
  12. @Data
  13. @NoArgsConstructor
  14. @AllArgsConstructor
  15. @Accessors(chain = true)
  16. public class FilterEntity {
  17. //过滤器对应的Name
  18. private String name;
  19. //路由规则
  20. private Map<String, String> args = new LinkedHashMap<>();
  21. }
GatewayNacosProperties.java
  1. package com.yx.gateway.pojo;
  2. import lombok.AllArgsConstructor;
  3. import lombok.Data;
  4. import lombok.NoArgsConstructor;
  5. import lombok.experimental.Accessors;
  6. import org.springframework.boot.context.properties.ConfigurationProperties;
  7. import org.springframework.stereotype.Component;
  8. @SuppressWarnings("all")
  9. @Data
  10. @NoArgsConstructor
  11. @AllArgsConstructor
  12. @Accessors(chain = true)
  13. @ConfigurationProperties(prefix = "gateway.nacos")
  14. @Component
  15. public class GatewayNacosProperties {
  16. private String serverAddr;
  17. private String dataId;
  18. private String namespace;
  19. private String group;
  20. }
PredicateEntity.java
  1. package com.yx.gateway.pojo;
  2. import lombok.AllArgsConstructor;
  3. import lombok.Data;
  4. import lombok.NoArgsConstructor;
  5. import lombok.experimental.Accessors;
  6. import java.util.LinkedHashMap;
  7. import java.util.Map;
  8. /**
  9. * @author hgh
  10. */
  11. @SuppressWarnings("all")
  12. @Data
  13. @NoArgsConstructor
  14. @AllArgsConstructor
  15. @Accessors(chain = true)
  16. public class PredicateEntity {
  17. //断言对应的Name
  18. private String name;
  19. //断言规则
  20. private Map<String, String> args = new LinkedHashMap<>();
  21. }
RouteEntity.java
  1. package com.yx.gateway.pojo;
  2. import lombok.AllArgsConstructor;
  3. import lombok.Data;
  4. import lombok.NoArgsConstructor;
  5. import lombok.experimental.Accessors;
  6. import java.util.ArrayList;
  7. import java.util.List;
  8. /**
  9. * @author hgh
  10. */
  11. @SuppressWarnings("all")
  12. @Data
  13. @NoArgsConstructor
  14. @AllArgsConstructor
  15. @Accessors(chain = true)
  16. public class RouteEntity {
  17. //路由id
  18. private String id;
  19. //路由断言集合
  20. private List<PredicateEntity> predicates = new ArrayList<>();
  21. //路由过滤器集合
  22. private List<FilterEntity> filters = new ArrayList<>();
  23. //路由转发的目标uri
  24. private String uri;
  25. //路由执行的顺序
  26. private int order = 0;
  27. }
DynamicRoutingConfig.java
  1. package com.yx.gateway;
  2. import com.alibaba.fastjson.JSON;
  3. import com.alibaba.fastjson.JSONObject;
  4. import com.alibaba.nacos.api.NacosFactory;
  5. import com.alibaba.nacos.api.PropertyKeyConst;
  6. import com.alibaba.nacos.api.config.ConfigService;
  7. import com.alibaba.nacos.api.config.listener.Listener;
  8. import com.alibaba.nacos.api.exception.NacosException;
  9. import com.fasterxml.jackson.databind.ObjectMapper;
  10. import com.yx.gateway.pojo.FilterEntity;
  11. import com.yx.gateway.pojo.GatewayNacosProperties;
  12. import com.yx.gateway.pojo.PredicateEntity;
  13. import com.yx.gateway.pojo.RouteEntity;
  14. import lombok.extern.slf4j.Slf4j;
  15. import org.springframework.beans.factory.annotation.Autowired;
  16. import org.springframework.cloud.gateway.event.RefreshRoutesEvent;
  17. import org.springframework.cloud.gateway.filter.FilterDefinition;
  18. import org.springframework.cloud.gateway.handler.predicate.PredicateDefinition;
  19. import org.springframework.cloud.gateway.route.RouteDefinition;
  20. import org.springframework.cloud.gateway.route.RouteDefinitionWriter;
  21. import org.springframework.context.ApplicationEventPublisher;
  22. import org.springframework.context.ApplicationEventPublisherAware;
  23. import org.springframework.context.annotation.Bean;
  24. import org.springframework.stereotype.Component;
  25. import org.springframework.web.util.UriComponentsBuilder;
  26. import reactor.core.publisher.Mono;
  27. import java.net.URI;
  28. import java.util.ArrayList;
  29. import java.util.List;
  30. import java.util.Properties;
  31. import java.util.concurrent.Executor;
  32. /**
  33. * 此类实现了Spring Cloud Gateway + nacos 的动态路由,
  34. * 它实现一个Spring提供的事件推送接口ApplicationEventPublisherAware
  35. */
  36. @SuppressWarnings("all")
  37. @Slf4j
  38. @Component
  39. public class DynamicRoutingConfig implements ApplicationEventPublisherAware {
  40. @Autowired
  41. private RouteDefinitionWriter routeDefinitionWriter;
  42. @Autowired
  43. private GatewayNacosProperties gatewayProperties;
  44. @Autowired
  45. private ObjectMapper mapper;
  46. private ApplicationEventPublisher applicationEventPublisher;
  47. @Override
  48. public void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) {
  49. this.applicationEventPublisher = applicationEventPublisher;
  50. }
  51. /**
  52. * 这个方法主要负责监听Nacos的配置变化,这里先使用参数构建一个ConfigService,
  53. * 再使用ConfigService开启一个监听,
  54. * 并且在监听的方法中刷新路由信息。
  55. */
  56. @Bean
  57. public void refreshRouting() throws NacosException {
  58. //创建Properties配置类
  59. Properties properties = new Properties();
  60. System.out.println(gatewayProperties);
  61. //设置nacos的服务器地址,从配置类GatewayProperties中获取
  62. properties.put(PropertyKeyConst.SERVER_ADDR, gatewayProperties.getServerAddr());
  63. //设置nacos的命名空间,表示从具体的命名空间中获取配置信息,不填代表默认从public获得
  64. if (gatewayProperties.getNamespace() != null) {
  65. properties.put(PropertyKeyConst.NAMESPACE, gatewayProperties.getNamespace());
  66. }
  67. //根据Properties配置创建ConfigService类
  68. ConfigService configService = NacosFactory.createConfigService(properties);
  69. //获得nacos中已有的路由配置
  70. String json = configService.getConfig(gatewayProperties.getDataId(), gatewayProperties.getGroup(), 5000);
  71. this.parseJson(json);
  72. //添加监听器,监听nacos中的数据修改事件
  73. configService.addListener(gatewayProperties.getDataId(), gatewayProperties.getGroup(), new Listener() {
  74. @Override
  75. public Executor getExecutor() {
  76. return null;
  77. }
  78. /**
  79. * 用于接收远端nacos中数据修改后的回调方法
  80. */
  81. @Override
  82. public void receiveConfigInfo(String configInfo) {
  83. log.warn(configInfo);
  84. //获取nacos中修改的数据并进行转换
  85. parseJson(configInfo);
  86. }
  87. });
  88. }
  89. /**
  90. * 解析从nacos读取的路由配置信息(json格式)
  91. */
  92. public void parseJson(String json) {
  93. log.warn("从Nacos返回的路由配置(JSON格式):" + json);
  94. boolean refreshGatewayRoute = JSONObject.parseObject(json).getBoolean("refreshGatewayRoute");
  95. if (refreshGatewayRoute) {
  96. List<RouteEntity> list = JSON.parseArray(JSONObject.parseObject(json).getString("routeList")).toJavaList(RouteEntity.class);
  97. for (RouteEntity route : list) {
  98. update(assembleRouteDefinition(route));
  99. }
  100. } else {
  101. log.warn("路由未发生变更");
  102. }
  103. }
  104. /**
  105. * 路由更新
  106. */
  107. public void update(RouteDefinition routeDefinition) {
  108. try {
  109. this.routeDefinitionWriter.delete(Mono.just(routeDefinition.getId()));
  110. log.warn("路由删除成功:" + routeDefinition.getId());
  111. } catch (Exception e) {
  112. log.error(e.getMessage(), e);
  113. }
  114. try {
  115. routeDefinitionWriter.save(Mono.just(routeDefinition)).subscribe();
  116. this.applicationEventPublisher.publishEvent(new RefreshRoutesEvent(this));
  117. log.warn("路由更新成功:" + routeDefinition.getId());
  118. } catch (Exception e) {
  119. log.error(e.getMessage(), e);
  120. }
  121. }
  122. /**
  123. * 路由定义
  124. */
  125. public RouteDefinition assembleRouteDefinition(RouteEntity routeEntity) {
  126. RouteDefinition definition = new RouteDefinition();
  127. // ID
  128. definition.setId(routeEntity.getId());
  129. // Predicates
  130. List<PredicateDefinition> pdList = new ArrayList<>();
  131. for (PredicateEntity predicateEntity : routeEntity.getPredicates()) {
  132. PredicateDefinition predicateDefinition = new PredicateDefinition();
  133. predicateDefinition.setArgs(predicateEntity.getArgs());
  134. predicateDefinition.setName(predicateEntity.getName());
  135. pdList.add(predicateDefinition);
  136. }
  137. definition.setPredicates(pdList);
  138. // Filters
  139. List<FilterDefinition> fdList = new ArrayList<>();
  140. for (FilterEntity filterEntity : routeEntity.getFilters()) {
  141. FilterDefinition filterDefinition = new FilterDefinition();
  142. filterDefinition.setArgs(filterEntity.getArgs());
  143. filterDefinition.setName(filterEntity.getName());
  144. fdList.add(filterDefinition);
  145. }
  146. definition.setFilters(fdList);
  147. // URI
  148. URI uri = UriComponentsBuilder.fromUriString(routeEntity.getUri()).build().toUri();
  149. definition.setUri(uri);
  150. return definition;
  151. }
  152. }
2. 导入所需依赖
  1. 导入阿里的阿里的fastjson的依赖,因为配置类中需要
  1. <!-- 导入阿里的fastjson的依赖-->
  2. <dependency>
  3. <groupId>com.alibaba</groupId>
  4. <artifactId>fastjson</artifactId>
  5. <version>1.2.35</version>
  6. </dependency>

  1. 导入依赖之后记得更新刷新配置类防止报错。
3. 在nacos中创建一个json文件
  1. 我们进入nacos的官网中,在配置中心中创建一个json的配置文件,配置文件的名称要与yml文件中的id一致

json内容
  1. {
  2. "refreshGatewayRoute": true,
  3. "routeList": [
  4. {
  5. "id": "consumer-api",
  6. "predicates": [
  7. {
  8. "name": "Path",
  9. "args": {
  10. "_genkey_0": "/cum/**"
  11. }
  12. }
  13. ],
  14. "filters": [
  15. {
  16. "name": "StripPrefix",
  17. "args": {
  18. "_genkey_0": "1"
  19. }
  20. }
  21. ],
  22. "uri": "lb://consumer",
  23. "order": 0
  24. },
  25. {
  26. "id": "provider-api",
  27. "predicates": [
  28. {
  29. "name": "Path",
  30. "args": {
  31. "_genkey_0": "/pvr/**"
  32. }
  33. }
  34. ],
  35. "filters": [
  36. {
  37. "name": "StripPrefix",
  38. "args": {
  39. "_genkey_0": "1"
  40. }
  41. }
  42. ],
  43. "uri": "lb://provider",
  44. "order": 0
  45. }
  46. ]
  47. }
4. 测试效果
  1. 我们重新启动项目,在网页中进行测试效果
  1. 首先我们访问指定的请求路径测试,测试结果是能够成功访问

  1. 当我们前去把在nacos官网将该json文件中生产者的访问路径进行该功重新访问

  1. 我们更新之后先访问没更改前的请求路径,在访问更改后的请求路径。

  1. 这就是我们的动态路由的实现方式

🎉🎉本期的博客分享到此结束🎉🎉

📚📚各位老铁慢慢消化📚📚

🎯🎯下期博客博主会带来新货🎯🎯

🎁三连加关注,阅读不迷路 !🎁


本文转载自: https://blog.csdn.net/weixin_74352229/article/details/135502447
版权归原作者 君易--鑨 所有, 如有侵权,请联系我们删除。

“【SpringCloud】之网关应用(进阶使用)”的评论:

还没有评论