🥳🥳Welcome Huihui's Code World ! !🥳🥳
接下来看看由辉辉所写的关于SpringCloud的相关操作吧
目录
🥳🥳Welcome Huihui's Code World ! !🥳🥳
一.网关组件是什么
二. 网关组件的详解
生活例子
例子剖析
三.代码演示【网关的三种使用方式】
0.配置
1.根据服务名访问
2.根据路径访问
3.动态路由
路由
断言
过滤器
配置类
路由处理类
注意点:
一.网关组件是什么
网关(Gateway)组件是指用于构建具有统一入口的微服务架构中的一个组件,它可以实现动态路由、负载均衡、熔断、安全控制等功能。在微服务架构中,每个服务都有自己的入口,客户端需要知道每个服务的地址和端口号才能访问它们。而网关组件可以为所有服务提供一个统一的入口,客户端只需要知道网关的地址和端口号,就可以通过网关访问所有的服务。
常见的网关组件包括Zuul、Spring Cloud Gateway、Kong、Nginx等。这些网关组件都具有一定的功能和特点,可以根据具体需求选择合适的网关组件。
Spring Cloud Gateway
三大核心概念
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可以对请求和响应进行处理
二. 网关组件的详解
这里举一个生活中的例子,来帮助大家理解一下网关这个组件
生活例子
假设你住在一个小区里,每个家庭都有自己的门禁系统和电话系统。
现在,你想要给小区外的朋友打电话
但是你不知道他们具体的电话号码,只知道他们的姓名。这时候,你需要通过小区的门禁系统(网关)来连接到外部电话网络,然后通过输入朋友的姓名(数据)来查找他们的电话号码(其他网络)并打通电话。
例子剖析
在这个例子中,小区的门禁系统就是一个网关,它连接了小区内部的电话系统和外部的电话网络,帮助你实现了与外部电话网络的通信。类似地,在计算机网络中,网关起到了连接不同网络或协议的桥梁作用,使得数据能够在它们之间进行传输和转换。
三.代码演示【网关的三种使用方式】
0.配置
在进行下面三种方式的讲解之前,咱先得将文件都给配置好
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-webflux</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-gateway</artifactId> </dependency>
然后需要新建一个项目,我这里建的是一个springboot的项目
1.根据服务名访问
这里只需要配置好就行了
discovery: #根据服务名 locator: #true代表开启基于服务发现的路由规则 enabled: true #配置之后访问时service-id无需大写 lower-case-service-id: true
然后就可以按照nacos中的服务器名中进行访问了
2.根据路径访问
其中我是没有使用负载均衡,如果使用了负载均衡,那么在那个uri那里写的就是负载均衡的格式
单体
routes: - id: user-consumer-api #目标服务地址(uri:地址,请求转发后的地址),会自动从注册中心获得服务的IP,不需要手动写死 uri: http://localhost:8081 #路由条件(predicates:断言) predicates: # 路径匹配, - Path=/aa/** filters: - StripPrefix=1
负载均衡
routes: - id: user-consumer-api #目标服务地址(uri:地址,请求转发后的地址),会自动从注册中心获得服务的IP,不需要手动写死 uri: lb//consumer #路由条件(predicates:断言) predicates: # 路径匹配, - Path=/aa/** filters: - StripPrefix=1
3.动态路由
这里就是自己定义了一个规则,然后需要编写路由,断言,过滤器,以及读取配置的文件的配置类,还有关于路由操作的处理类
#自定义配置 gateway: nacos: server-addr: ${spring.cloud.nacos.server-addr} # namespace: xxx-xx-xx-xx data-id: dynamic-routing.json group: DEFAULT_GROUP
路由
package gateway.demo.pojo; import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; import lombok.experimental.Accessors; import java.util.ArrayList; import java.util.List; /** * @author hgh */ @SuppressWarnings("all") @Data @NoArgsConstructor @AllArgsConstructor @Accessors(chain = true) public class RouteEntity { //路由id private String id; //路由断言集合 private List<PredicateEntity> predicates = new ArrayList<>(); //路由过滤器集合 private List<FilterEntity> filters = new ArrayList<>(); //路由转发的目标uri private String uri; //路由执行的顺序 private int order = 0; }
断言
package gateway.demo.pojo; import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; import lombok.experimental.Accessors; import java.util.LinkedHashMap; import java.util.Map; /** * @author hgh */ @SuppressWarnings("all") @Data @NoArgsConstructor @AllArgsConstructor @Accessors(chain = true) public class PredicateEntity { //断言对应的Name private String name; //断言规则 private Map<String, String> args = new LinkedHashMap<>(); }
过滤器
package gateway.demo.pojo; import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; import lombok.experimental.Accessors; import java.util.LinkedHashMap; import java.util.Map; /** * @author hgh */ @SuppressWarnings("all") @Data @NoArgsConstructor @AllArgsConstructor @Accessors(chain = true) public class FilterEntity { //过滤器对应的Name private String name; //路由规则 private Map<String, String> args = new LinkedHashMap<>(); }
配置类
package gateway.demo.pojo; import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; import lombok.experimental.Accessors; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.stereotype.Component; @SuppressWarnings("all") @Data @NoArgsConstructor @AllArgsConstructor @Accessors(chain = true) @ConfigurationProperties(prefix = "gateway.nacos") @Component public class GatewayNacosProperties { private String serverAddr; private String dataId; private String namespace; private String group; }
路由处理类
package gateway.demo; import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSONObject; import com.alibaba.nacos.api.NacosFactory; import com.alibaba.nacos.api.PropertyKeyConst; import com.alibaba.nacos.api.config.ConfigService; import com.alibaba.nacos.api.config.listener.Listener; import com.alibaba.nacos.api.exception.NacosException; import com.fasterxml.jackson.databind.ObjectMapper; import gateway.demo.pojo.FilterEntity; import gateway.demo.pojo.GatewayNacosProperties; import gateway.demo.pojo.PredicateEntity; import gateway.demo.pojo.RouteEntity; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.cloud.gateway.event.RefreshRoutesEvent; import org.springframework.cloud.gateway.filter.FilterDefinition; import org.springframework.cloud.gateway.handler.predicate.PredicateDefinition; import org.springframework.cloud.gateway.route.RouteDefinition; import org.springframework.cloud.gateway.route.RouteDefinitionWriter; import org.springframework.context.ApplicationEventPublisher; import org.springframework.context.ApplicationEventPublisherAware; import org.springframework.context.annotation.Bean; import org.springframework.stereotype.Component; import org.springframework.web.util.UriComponentsBuilder; import reactor.core.publisher.Mono; import java.net.URI; import java.util.ArrayList; import java.util.List; import java.util.Properties; import java.util.concurrent.Executor; /** * 此类实现了Spring Cloud Gateway + nacos 的动态路由, * 它实现一个Spring提供的事件推送接口ApplicationEventPublisherAware */ @SuppressWarnings("all") @Slf4j @Component public class DynamicRoutingConfig implements ApplicationEventPublisherAware { @Autowired private RouteDefinitionWriter routeDefinitionWriter; @Autowired private GatewayNacosProperties gatewayProperties; @Autowired private ObjectMapper mapper; private ApplicationEventPublisher applicationEventPublisher; @Override public void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) { this.applicationEventPublisher = applicationEventPublisher; } /** * 这个方法主要负责监听Nacos的配置变化,这里先使用参数构建一个ConfigService, * 再使用ConfigService开启一个监听, * 并且在监听的方法中刷新路由信息。 */ @Bean public void refreshRouting() throws NacosException { //创建Properties配置类 Properties properties = new Properties(); System.out.println(gatewayProperties); //设置nacos的服务器地址,从配置类GatewayProperties中获取 properties.put(PropertyKeyConst.SERVER_ADDR, gatewayProperties.getServerAddr()); //设置nacos的命名空间,表示从具体的命名空间中获取配置信息,不填代表默认从public获得 if (gatewayProperties.getNamespace() != null) { properties.put(PropertyKeyConst.NAMESPACE, gatewayProperties.getNamespace()); } //根据Properties配置创建ConfigService类 ConfigService configService = NacosFactory.createConfigService(properties); //获得nacos中已有的路由配置 String json = configService.getConfig(gatewayProperties.getDataId(), gatewayProperties.getGroup(), 5000); this.parseJson(json); //添加监听器,监听nacos中的数据修改事件 configService.addListener(gatewayProperties.getDataId(), gatewayProperties.getGroup(), new Listener() { @Override public Executor getExecutor() { return null; } /** * 用于接收远端nacos中数据修改后的回调方法 */ @Override public void receiveConfigInfo(String configInfo) { log.warn(configInfo); //获取nacos中修改的数据并进行转换 parseJson(configInfo); } }); } /** * 解析从nacos读取的路由配置信息(json格式) */ public void parseJson(String json) { log.warn("从Nacos返回的路由配置(JSON格式):" + json); boolean refreshGatewayRoute = JSONObject.parseObject(json).getBoolean("refreshGatewayRoute"); if (refreshGatewayRoute) { List<RouteEntity> list = JSON.parseArray(JSONObject.parseObject(json).getString("routeList")).toJavaList(RouteEntity.class); for (RouteEntity route : list) { update(assembleRouteDefinition(route)); } } else { log.warn("路由未发生变更"); } } /** * 路由更新 */ public void update(RouteDefinition routeDefinition) { try { this.routeDefinitionWriter.delete(Mono.just(routeDefinition.getId())); log.warn("路由删除成功:" + routeDefinition.getId()); } catch (Exception e) { log.error(e.getMessage(), e); } try { routeDefinitionWriter.save(Mono.just(routeDefinition)).subscribe(); this.applicationEventPublisher.publishEvent(new RefreshRoutesEvent(this)); log.warn("路由更新成功:" + routeDefinition.getId()); } catch (Exception e) { log.error(e.getMessage(), e); } } /** * 路由定义 */ public RouteDefinition assembleRouteDefinition(RouteEntity routeEntity) { RouteDefinition definition = new RouteDefinition(); // ID definition.setId(routeEntity.getId()); // Predicates List<PredicateDefinition> pdList = new ArrayList<>(); for (PredicateEntity predicateEntity : routeEntity.getPredicates()) { PredicateDefinition predicateDefinition = new PredicateDefinition(); predicateDefinition.setArgs(predicateEntity.getArgs()); predicateDefinition.setName(predicateEntity.getName()); pdList.add(predicateDefinition); } definition.setPredicates(pdList); // Filters List<FilterDefinition> fdList = new ArrayList<>(); for (FilterEntity filterEntity : routeEntity.getFilters()) { FilterDefinition filterDefinition = new FilterDefinition(); filterDefinition.setArgs(filterEntity.getArgs()); filterDefinition.setName(filterEntity.getName()); fdList.add(filterDefinition); } definition.setFilters(fdList); // URI URI uri = UriComponentsBuilder.fromUriString(routeEntity.getUri()).build().toUri(); definition.setUri(uri); return definition; } }
注意点:
还有就是这个文件中如果你使用的是负载均衡,那么uri也是需要写负载均衡的,如果不是的话,那就写的需要访问服务的路径【我的这个是单体的,不是负载均衡的】
{ "refreshGatewayRoute": true, "routeList": [ { "id": "consumer-api", "predicates": [ { "name": "Path", "args": { "_genkey_0": "/con/**" } } ], "filters": [ { "name": "StripPrefix", "args": { "_genkey_0": "1" } } ], "uri": "http://localhost:8081", "order": 0 }, { "id": "provider-api", "predicates": [ { "name": "Path", "args": { "_genkey_0": "/pro/**" } } ], "filters": [ { "name": "StripPrefix", "args": { "_genkey_0": "1" } } ], "uri": "http://localhost:8082", "order": 0 } ] }
当我们去更改nacos中的配置时,这个也是会实时加载的,这个都是写在那个路由处理类中
好啦,今天的分享就到这了,希望能够帮到你呢!😊😊