1: Configurar la información de nacos relacionada
Se ha omitido otra información
Gateway.yml en el proyecto
nacos:
server-addr:xx.xx.xx.xx:8848
namespace: dev
#省略其他代码。。。。。。。。。
#动态路由相关配置
dynamic:
route:
data-id: routes-${spring.application.name}.yaml
group: ROUTE_GROUP
server-addr: xx.xx.xx.xx:8848
namespace: dev
2: crear clases de entidad relacionadas
filtrar
package com.carry.www.entity;
/**
* 过滤器实体类
*
*/
import lombok.Data;
import java.util.LinkedHashMap;
import java.util.Map;
@Data
public class FilterEntity {
// 过滤器对应的Name
private String name;
// 路由规则
private Map<String, String> args = new LinkedHashMap<>();
}
Afirmación:
package com.carry.www.entity;
import lombok.Data;
import java.util.LinkedHashMap;
import java.util.Map;
/**
* 路由断言实体类
*
*/
@Data
public class PredicateEntity {
// 断言对应的Name
private String name;
// 断言规则
private Map<String, String> args = new LinkedHashMap<>();
}
Clase de enrutamiento
package com.carry.www.entity;
import lombok.Data;
import java.util.ArrayList;
import java.util.List;
/**
* 路由实体类
*
*/
@Data
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;
}
3: Agregar monitor
Monitorear la información de configuración de nacos
package com.carry.www.config;
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.carry.www.entity.FilterEntity;
import com.carry.www.entity.PredicateEntity;
import com.carry.www.entity.RouteEntity;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
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.context.annotation.Configuration;
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;
/**
* 从Nacos获取动态路由
* 实现ApplicationEventPublisherAware发布接口来发布路由更新事件
*/
@Configuration
@Slf4j
public class DynamicRoutingConfig implements ApplicationEventPublisherAware {
@Value("${dynamic.route.server-addr}")
private String serverAddr;
@Value("${nacos.namespace}")
private String namespace;
@Value("${dynamic.route.data-id}")
private String dataId;
@Value("${dynamic.route.group}")
private String groupId;
// 保存、删除路由服务
@Autowired
private RouteDefinitionWriter routeDefinitionWriter;
private ApplicationEventPublisher applicationEventPublisher;
@Override
public void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) {
this.applicationEventPublisher = applicationEventPublisher;
}
/**
* @return
* @Author carryer
* @Description 获取nacos配置服务
* @Date
* @Param
**/
public ConfigService getNacosConfigInfo() throws Exception {
Properties properties = new Properties();
properties.setProperty(PropertyKeyConst.SERVER_ADDR, serverAddr);
// properties.setProperty(PropertyKeyConst.USERNAME, username);
// properties.setProperty(PropertyKeyConst.PASSWORD, password);
properties.setProperty(PropertyKeyConst.NAMESPACE, namespace);
ConfigService configService = NacosFactory.createConfigService(properties);
return configService;
}
/**
* @return
* @Author carryer
* @Description 初始化路由
* @Date
* @Param
**/
@Bean
public void initRouting() {
try {
ConfigService configService = this.getNacosConfigInfo();
String configInfo = configService.getConfig(dataId, groupId, 5000);
if (null != configInfo) {
List<RouteEntity> list = JSONObject.parseArray(configInfo).toJavaList(RouteEntity.class);
for (RouteEntity route : list) {
update(assembleRouteDefinition(route));
}
}
} catch (Exception e) {
log.error(e.getMessage(), e);
e.printStackTrace();
}
}
/**
* @return
* @Author carryer
* @Description 刷新路由
* @Date
* @Param
**/
@Bean
public void refreshRouting() {
try {
ConfigService configService = this.getNacosConfigInfo();
//监听路由变化
configService.addListener(
dataId,
groupId,
new Listener() {
@Override
public Executor getExecutor() {
return null;
}
@Override
public void receiveConfigInfo(String configInfo) {
try {
log.info(configInfo);
if (null != configInfo) {
List<RouteEntity> list =
JSONObject.parseArray(configInfo).toJavaList(RouteEntity.class);
//更新路由表
for (RouteEntity route : list) {
update(assembleRouteDefinition(route));
}
}
} catch (Exception e) {
log.error(e.getMessage(), e);
e.printStackTrace();
}
}
});
} catch (Exception e) {
log.error(e.getMessage(), e);
e.printStackTrace();
}
}
/**
* @return
* @Author carryer
* @Description 路由更新
* @Date
* @Param
**/
private void update(RouteDefinition routeDefinition) throws Exception {
//先删除路由
routeDefinitionWriter.delete(Mono.just(routeDefinition.getId()));
//再保存路由
routeDefinitionWriter.save(Mono.just(routeDefinition)).subscribe();
//发布事件 发布者是RefreshRoutesEvent 事件是刷新路由
applicationEventPublisher.publishEvent(new RefreshRoutesEvent(this));
}
/**
* @return
* @Author carryer
* @Description 实体信息解析
* @Date
* @Param
**/
private 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;
}
}
El código anterior nacos puede agregar enrutamiento dinámicamente, y los amigos pueden usarlo directamente
============================= línea divisoria =================== ==========================
4: Análisis de código fuente relevante
En la configuración anterior, primero cargue la información en api-gatway.yaml y luego cargue la información de configuración en route-api-gatway.yaml. El principio es el mismo, pero el oyente es route-api-gatway.yaml (hay es formato json)
ApplicationEventPublisherAware es una interfaz de publicación de eventos, que incluye eventos, editores y oyentes. El editor del evento aquí es la clase que viene con la puerta de enlace, RefreshRoutesEvent, y el evento es para actualizar la ruta.
Independientemente de inicializar el enrutamiento o actualizar el enrutamiento, el primer paso es obtener la información relevante de nacos. ConfigService se obtiene mediante la clase de fábrica estática NacosFactory
Ingrese a ConfigFactory y encuentre que ConfigService es NacosConfigService obtenido por mecanismo de reflexión, NacosConfigService implementa ConfigService
Ingrese a NacosConfigService, el método de construcción interno realiza las asignaciones relevantes de nacos, incluidos los espacios de nombres, etc. Hasta ahora, se puede ver que nacos se ha conectado correctamente y se ha devuelto la clase de configuración de nacos.
Veamos la segunda oración del código String configInfo = configService.getConfig (dataId, groupId, 5000) Esta oración en realidad significa que la clase de configuración de nacos obtiene información del archivo de configuración de acuerdo con dataId y groupId, que es el contenido de yaml que configura en nacos interfaz de visualización.
Mira el código a continuación
Mire directamente el método getConfigInner, este método primero obtiene la información de configuración local de Nacos, si no, luego obtiene la información de configuración en el servidor
String content = LocalConfigInfoProcessor.getFailover(agent.getName(), dataId, group, tenant);
Obtenga la información de configuración en el servidor nacos.
String[] ct = worker.getServerConfig(dataId, group, tenant, timeoutMs);
Nacos的ClientWorker类进行赋值操作,调用getServerConfig方法获取配置返回配置数组
Ingrese getServerConfig de ClientWorker
El método principal en ClientWorker agent.httpGet (Constants.CONFIG_CONTROLLER_PATH, null, params, agent.getEncode (), readTimeout), haga clic para ingresar ServerHttpAgent
ServerHttpAgent clase para llamada http
La clase HttpSimpleClient se llama en ServerHttpAgent para llamar a Nacos Open API y obtener la configuración del servidor Nacos. La
segunda vez aquí es la URL: http: //xx.xx.xx.xx: 8848 / nacos / v1 / cs / configs? DataId = route-api-gatway & group = ROUTE_GROUP & tenant = dev , también puede usar postman para llamar, de hecho, lo que se devuelve es su archivo de configuración de api-gatway.yaml o route-api-gatway.yaml en nacos
El archivo de configuración de routes-api-gatway.yaml en nacos
En este punto, se han ejecutado las dos primeras líneas de código. Lo siguiente es analizar y luego publicar el evento.
String configInfo = configService.getConfig (dataId, groupId, 5000) La cadena de ruta devuelta por el método se analiza en una colección de clases de entidad de ruta, y la ruta se analiza y se asigna a la clase RouteDefinition en un bucle,
Ruta, afirmación, URL y otras configuraciones en RouteDefinition
La siguiente verificación, agrega una nueva ruta con id de servicio4 y publícala, encontrarás que el código lee automáticamente la última configuración y realiza análisis de ruta para actualizar la ruta