【ShenYu系列】ShenYu Dubbo插件全流程源码解析
网关启动
在ShenyuConfiguration注入ShenyuWebHandler。
@Bean("webHandler")public ShenyuWebHandler shenyuWebHandler(final ObjectProvider<List<ShenyuPlugin>> plugins, final ShenyuConfig config, @Lazy final ShenyuLoaderService shenyuLoaderService) {List<ShenyuPlugin> pluginList = plugins.getIfAvailable(Collections::emptyList);List<ShenyuPlugin> shenyuPlugins = pluginList.stream().sorted(Comparator.comparingInt(ShenyuPlugin::getOrder)).collect(Collectors.toList());shenyuPlugins.forEach(shenyuPlugin -> LOG.info("load plugin:[{}] [{}]", shenyuPlugin.named(), shenyuPlugin.getClass().getName()));return new ShenyuWebHandler(shenyuPlugins, shenyuLoaderService, config);}
在NacosSyncDataConfiguration注入用nacos的数据同步服务。
@Beanpublic SyncDataService nacosSyncDataService(final ObjectProvider<ConfigService> configService, final ObjectProvider<PluginDataSubscriber> pluginSubscriber,final ObjectProvider<List<MetaDataSubscriber>> metaSubscribers, final ObjectProvider<List<AuthDataSubscriber>> authSubscribers) {LOGGER.info("you use nacos sync shenyu data.......");return new NacosSyncDataService(configService.getIfAvailable(), pluginSubscriber.getIfAvailable(),metaSubscribers.getIfAvailable(Collections::emptyList), authSubscribers.getIfAvailable(Collections::emptyList));}
该实例的构造方法中会执行NacosSyncDataService#start
public void start() {watcherData(NacosPathConstants.PLUGIN_DATA_ID, this::updatePluginMap);watcherData(NacosPathConstants.SELECTOR_DATA_ID, this::updateSelectorMap);watcherData(NacosPathConstants.RULE_DATA_ID, this::updateRuleMap);watcherData(NacosPathConstants.META_DATA_ID, this::updateMetaDataMap);watcherData(NacosPathConstants.AUTH_DATA_ID, this::updateAuthMap);}
NacosCacheHandler#updatePluginMap,监听到数据,执行
protected void updatePluginMap(final String configInfo) {try {// Fix bug #656(https://github.com/apache/shenyu/issues/656)List<PluginData> pluginDataList = new ArrayList<>(GsonUtils.getInstance().toObjectMap(configInfo, PluginData.class).values());pluginDataList.forEach(pluginData -> Optional.ofNullable(pluginDataSubscriber).ifPresent(subscriber -> {subscriber.unSubscribe(pluginData);subscriber.onSubscribe(pluginData);}));} catch (JsonParseException e) {LOG.error("sync plugin data have error:", e);}}
AbstractDubboPluginDataHandler#handlerPlugin,处理器,把数据存储到ApacheDubboConfigCache
public abstract class AbstractDubboPluginDataHandler implements PluginDataHandler {public static final Supplier<CommonHandleCache<String, DubboRuleHandle>> RULE_CACHED_HANDLE = new BeanHolder<>(CommonHandleCache::new);public static final Supplier<CommonHandleCache<String, List<DubboUpstream>>> SELECTOR_CACHED_HANDLE = new BeanHolder<>(CommonHandleCache::new);protected abstract void initConfigCache(DubboRegisterConfig dubboRegisterConfig);@Overridepublic void handlerPlugin(final PluginData pluginData) {if (Objects.nonNull(pluginData) && Boolean.TRUE.equals(pluginData.getEnabled())) {DubboRegisterConfig dubboRegisterConfig = GsonUtils.getInstance().fromJson(pluginData.getConfig(), DubboRegisterConfig.class);DubboRegisterConfig exist = Singleton.INST.get(DubboRegisterConfig.class);if (Objects.isNull(dubboRegisterConfig)) {return;}if (Objects.isNull(exist) || !dubboRegisterConfig.equals(exist)) {// If it is null, initialize itthis.initConfigCache(dubboRegisterConfig);}Singleton.INST.single(DubboRegisterConfig.class, dubboRegisterConfig);}}
}
ApacheDubboPluginDataHandler
public class ApacheDubboPluginDataHandler extends AbstractDubboPluginDataHandler {@Overrideprotected void initConfigCache(final DubboRegisterConfig dubboRegisterConfig) {ApacheDubboConfigCache.getInstance().init(dubboRegisterConfig);ApacheDubboConfigCache.getInstance().invalidateAll();}
}
服务调用
ShenyuWebHandler.DefaultShenyuPluginChain#execute,执行每个插件.。
@Overridepublic Mono<Void> execute(final ServerWebExchange exchange) {return Mono.defer(() -> {if (this.index < plugins.size()) {ShenyuPlugin plugin = plugins.get(this.index++);boolean skip = plugin.skip(exchange);if (skip) {return this.execute(exchange);}return plugin.execute(exchange, this);}return Mono.empty();});}
AbstractShenyuPlugin#execute,匹配选择器和插件。
public Mono<Void> execute(final ServerWebExchange exchange, final ShenyuPluginChain chain) {initCacheConfig();final String pluginName = named();PluginData pluginData = BaseDataCache.getInstance().obtainPluginData(pluginName);// early exitif (Objects.isNull(pluginData) || !pluginData.getEnabled()) {return chain.execute(exchange);}final String path = exchange.getRequest().getURI().getPath();List<SelectorData> selectors = BaseDataCache.getInstance().obtainSelectorData(pluginName);SelectorData selectorData = obtainSelectorDataCacheIfEnabled(path);// handle Selectorif (Objects.nonNull(selectorData) && StringUtils.isBlank(selectorData.getId())) {return handleSelectorIfNull(pluginName, exchange, chain);}if (Objects.isNull(selectorData)) {if (CollectionUtils.isEmpty(selectors)) {return handleSelectorIfNull(pluginName, exchange, chain);}Pair<Boolean, SelectorData> matchSelectorData = matchSelector(exchange, selectors);selectorData = matchSelectorData.getRight();if (Objects.isNull(selectorData)) {if (matchCacheConfig.getSelector().getSelectorEnabled() && matchSelectorData.getLeft()) {selectorData = new SelectorData();selectorData.setPluginName(pluginName);cacheSelectorData(path, selectorData);}return handleSelectorIfNull(pluginName, exchange, chain);} else {if (matchCacheConfig.getSelector().getSelectorEnabled() && matchSelectorData.getLeft()) {cacheSelectorData(path, selectorData);}}}printLog(selectorData, pluginName);if (Objects.nonNull(selectorData.getContinued()) && !selectorData.getContinued()) {// if continued, not match rulesreturn doExecute(exchange, chain, selectorData, defaultRuleData(selectorData));}List<RuleData> rules = BaseDataCache.getInstance().obtainRuleData(selectorData.getId());if (CollectionUtils.isEmpty(rules)) {return handleRuleIfNull(pluginName, exchange, chain);}RuleData ruleData = obtainRuleDataCache(path);if (Objects.nonNull(ruleData) && Objects.isNull(ruleData.getId())) {return handleRuleIfNull(pluginName, exchange, chain);}if (selectorData.getType() == SelectorTypeEnum.FULL_FLOW.getCode()) {//get lastRuleData rule = rules.get(rules.size() - 1);printLog(rule, pluginName);return doExecute(exchange, chain, selectorData, rule);} else {// lru map as L1 cache,the cache is enabled by default.// if the L1 cache fails to hit, using L2 cache based on trie cache.// if the L2 cache fails to hit, execute default strategy.if (Objects.isNull(ruleData)) {// L1 cache not exist data, try to get data through trie cacheruleData = trieMatchRule(exchange, selectorData, path);// trie cache fails to hit, execute default strategyif (Objects.isNull(ruleData)) {LOG.info("{} rule match path from default strategy", named());Pair<Boolean, RuleData> matchRuleData = matchRule(exchange, rules);ruleData = matchRuleData.getRight();if (matchRuleData.getLeft()) {ruleData = Optional.ofNullable(ruleData).orElse(RuleData.builder().pluginName(pluginName).matchRestful(false).build());cacheRuleData(path, ruleData);}}}}if (Objects.isNull(ruleData) || Objects.isNull(ruleData.getId())) {return handleRuleIfNull(pluginName, exchange, chain);}printLog(ruleData, pluginName);return doExecute(exchange, chain, selectorData, ruleData);}
GlobalPlugin构建上下文信息
public class GlobalPlugin implements ShenyuPlugin {private final ShenyuContextBuilder builder;/*** Instantiates a new Global plugin.** @param builder the builder*/public GlobalPlugin(final ShenyuContextBuilder builder) {this.builder = builder;}@Overridepublic Mono<Void> execute(final ServerWebExchange exchange, final ShenyuPluginChain chain) {ShenyuContext shenyuContext = builder.build(exchange);exchange.getAttributes().put(Constants.CONTEXT, shenyuContext);return chain.execute(exchange);}
}
DefaultShenyuContextBuilder#build,构建上下文
@Overridepublic ShenyuContext build(final ServerWebExchange exchange) {Pair<String, MetaData> buildData = buildData(exchange);return decoratorMap.get(buildData.getLeft()).decorator(buildDefaultContext(exchange.getRequest()), buildData.getRight());}private Pair<String, MetaData> buildData(final ServerWebExchange exchange) {ServerHttpRequest request = exchange.getRequest();HttpHeaders headers = request.getHeaders();String rpcType = headers.getFirst(RPC_TYPE);if (StringUtils.isNotEmpty(rpcType)) {return Pair.of(rpcType, new MetaData());}String upgrade = headers.getFirst(UPGRADE);if (StringUtils.isNotEmpty(upgrade) && RpcTypeEnum.WEB_SOCKET.getName().equals(upgrade)) {return Pair.of(RpcTypeEnum.WEB_SOCKET.getName(), new MetaData());}MetaData metaData = MetaDataCache.getInstance().obtain(request.getURI().getPath());if (Objects.nonNull(metaData) && Boolean.TRUE.equals(metaData.getEnabled())) {exchange.getAttributes().put(Constants.META_DATA, metaData);return Pair.of(metaData.getRpcType(), metaData);} else {return Pair.of(RpcTypeEnum.HTTP.getName(), new MetaData());}}
RpcParamTransformPlugin用于获取参数信息
public class RpcParamTransformPlugin implements ShenyuPlugin {@Overridepublic Mono<Void> execute(final ServerWebExchange exchange, final ShenyuPluginChain chain) {ServerHttpRequest request = exchange.getRequest();ShenyuContext shenyuContext = exchange.getAttribute(Constants.CONTEXT);if (Objects.nonNull(shenyuContext)) {MediaType mediaType = request.getHeaders().getContentType();if (MediaType.APPLICATION_JSON.isCompatibleWith(mediaType)) {return body(exchange, request, chain);}if (MediaType.APPLICATION_FORM_URLENCODED.isCompatibleWith(mediaType)) {return formData(exchange, request, chain);}return query(exchange, request, chain);}return chain.execute(exchange);}
}
AbstractDubboPlugin#doExecute,检查元数据和参数,进行方法调用
@Overridepublic Mono<Void> doExecute(final ServerWebExchange exchange,final ShenyuPluginChain chain,final SelectorData selector,final RuleData rule) {String param = exchange.getAttribute(Constants.PARAM_TRANSFORM);ShenyuContext shenyuContext = exchange.getAttribute(Constants.CONTEXT);assert shenyuContext != null;MetaData metaData = exchange.getAttribute(Constants.META_DATA);if (!checkMetaData(metaData)) {LOG.error(" path is : {}, meta data have error : {}", shenyuContext.getPath(), metaData);exchange.getResponse().setStatusCode(HttpStatus.INTERNAL_SERVER_ERROR);Object error = ShenyuResultWrap.error(exchange, ShenyuResultEnum.META_DATA_ERROR);return WebFluxResultUtils.result(exchange, error);}if (Objects.nonNull(metaData) && StringUtils.isNoneBlank(metaData.getParameterTypes()) && StringUtils.isBlank(param)) {exchange.getResponse().setStatusCode(HttpStatus.INTERNAL_SERVER_ERROR);Object error = ShenyuResultWrap.error(exchange, ShenyuResultEnum.DUBBO_HAVE_BODY_PARAM);return WebFluxResultUtils.result(exchange, error);}this.rpcContext(exchange);return this.doDubboInvoker(exchange, chain, selector, rule, metaData, param);}
ApacheDubboPlugin#doDubboInvoker,开始dubbo的泛化调用。
@Overrideprotected Mono<Void> doDubboInvoker(final ServerWebExchange exchange,final ShenyuPluginChain chain,final SelectorData selector,final RuleData rule,final MetaData metaData,final String param) {RpcContext.getContext().setAttachment(Constants.DUBBO_SELECTOR_ID, selector.getId());RpcContext.getContext().setAttachment(Constants.DUBBO_RULE_ID, rule.getId());RpcContext.getContext().setAttachment(Constants.DUBBO_REMOTE_ADDRESS, Objects.requireNonNull(exchange.getRequest().getRemoteAddress()).getAddress().getHostAddress());final Mono<Object> result = dubboProxyService.genericInvoker(param, metaData, exchange);return result.then(chain.execute(exchange));}
ApacheDubboProxyService#genericInvoker
- 获取
ReferenceConfig对象; - 获取泛化服务
GenericService对象; - 构造请求参数
pair对象; - 发起异步的泛化调用。
public Mono<Object> genericInvoker(final String body, final MetaData metaData, final ServerWebExchange exchange) throws ShenyuException {String referenceKey = metaData.getPath();String namespace = "";if (CollectionUtils.isNotEmpty(exchange.getRequest().getHeaders().get(Constants.NAMESPACE))) {namespace = exchange.getRequest().getHeaders().get(Constants.NAMESPACE).get(0);referenceKey = namespace + ":" + referenceKey;}ReferenceConfig<GenericService> reference = ApacheDubboConfigCache.getInstance().get(referenceKey);if (StringUtils.isEmpty(reference.getInterface())) {ApacheDubboConfigCache.getInstance().invalidate(referenceKey);reference = ApacheDubboConfigCache.getInstance().initRefN(metaData, namespace);}GenericService genericService = reference.get();Pair<String[], Object[]> pair;if (StringUtils.isBlank(metaData.getParameterTypes()) || ParamCheckUtils.bodyIsEmpty(body)) {pair = new ImmutablePair<>(new String[]{}, new Object[]{});} else {pair = dubboParamResolveService.buildParameter(body, metaData.getParameterTypes());}return Mono.fromFuture(invokeAsync(genericService, metaData.getMethodName(), pair.getLeft(), pair.getRight()).thenApply(ret -> {if (Objects.isNull(ret)) {ret = Constants.DUBBO_RPC_RESULT_EMPTY;}exchange.getAttributes().put(Constants.RPC_RESULT, ret);exchange.getAttributes().put(Constants.CLIENT_RESPONSE_RESULT_TYPE, ResultEnum.SUCCESS.getName());return ret;})).onErrorMap(exception -> exception instanceof GenericException ? new ShenyuException(((GenericException) exception).getExceptionMessage()) : new ShenyuException(exception));}@SuppressWarnings("unchecked")private CompletableFuture<Object> invokeAsync(final GenericService genericService, final String method, final String[] parameterTypes, final Object[] args) throws GenericException {//Compatible with asynchronous calls of lower Dubbo versionsgenericService.$invoke(method, parameterTypes, args);Object resultFromFuture = RpcContext.getContext().getFuture();return resultFromFuture instanceof CompletableFuture ? (CompletableFuture<Object>) resultFromFuture : CompletableFuture.completedFuture(resultFromFuture);}
ResponsePlugin#execute,处理响应结果
@Overridepublic Mono<Void> execute(final ServerWebExchange exchange, final ShenyuPluginChain chain) {ShenyuContext shenyuContext = exchange.getAttribute(Constants.CONTEXT);assert shenyuContext != null;return writerMap.get(shenyuContext.getRpcType()).writeWith(exchange, chain);}

服务注册
启动shenyu-examples里面的shenyu-examples-apache-dubbo-service-annotation。
AbstractContextRefreshedEventListener
- 读取属性配置
- 开启线程池
- 启动注册中心,用于向
shenyu-admin注册
public AbstractContextRefreshedEventListener(final PropertiesConfig clientConfig,final ShenyuClientRegisterRepository shenyuClientRegisterRepository) {Properties props = clientConfig.getProps();this.appName = props.getProperty(ShenyuClientConstants.APP_NAME);this.contextPath = Optional.ofNullable(props.getProperty(ShenyuClientConstants.CONTEXT_PATH)).map(UriUtils::repairData).orElse("");if (StringUtils.isBlank(appName) && StringUtils.isBlank(contextPath)) {String errorMsg = "client register param must config the appName or contextPath";LOG.error(errorMsg);throw new ShenyuClientIllegalArgumentException(errorMsg);}this.ipAndPort = props.getProperty(ShenyuClientConstants.IP_PORT);this.host = props.getProperty(ShenyuClientConstants.HOST);this.port = props.getProperty(ShenyuClientConstants.PORT);publisher.start(shenyuClientRegisterRepository);}
AbstractContextRefreshedEventListener#onApplicationEvent,获取所有的ServiceBean的Bean,处理元数据对象和处理URI对象。
@Overridepublic void onApplicationEvent(@NonNull final ContextRefreshedEvent event) {final ApplicationContext context = event.getApplicationContext();Map<String, T> beans = getBeans(context);if (MapUtils.isEmpty(beans)) {return;}if (!registered.compareAndSet(false, true)) {return;}publisher.publishEvent(buildURIRegisterDTO(context, beans));beans.forEach(this::handle);Map<String, Object> apiModules = context.getBeansWithAnnotation(ApiModule.class);apiModules.forEach((k, v) -> handleApiDoc(v, beans));}
ShenyuClientRegisterEventPublisher#publishEvent,使用disruptor来处理事件。
public void publishEvent(final DataTypeParent data) {DisruptorProvider<DataTypeParent> provider = providerManage.getProvider();provider.onData(data);}
在RegisterCenterConfiguration注入ShenyuClientServerRegisterRepository
@Bean(destroyMethod = "close")public ShenyuClientServerRegisterRepository shenyuClientServerRegisterRepository(final ShenyuRegisterCenterConfig shenyuRegisterCenterConfig,final List<ShenyuClientRegisterService> shenyuClientRegisterService) {String registerType = shenyuRegisterCenterConfig.getRegisterType();ShenyuClientServerRegisterRepository registerRepository = ExtensionLoader.getExtensionLoader(ShenyuClientServerRegisterRepository.class).getJoin(registerType);RegisterClientServerDisruptorPublisher publisher = RegisterClientServerDisruptorPublisher.getInstance();Map<String, ShenyuClientRegisterService> registerServiceMap = shenyuClientRegisterService.stream().collect(Collectors.toMap(ShenyuClientRegisterService::rpcType, Function.identity()));publisher.start(registerServiceMap);registerRepository.init(publisher, shenyuRegisterCenterConfig);return registerRepository;}
DisruptorProviderManage#startup(boolean),启动DisruptorProviderManage会创建Disruptor
public void startup(final boolean isOrderly) {OrderlyExecutor executor = new OrderlyExecutor(isOrderly, consumerSize, consumerSize, 0, TimeUnit.MILLISECONDS,new LinkedBlockingQueue<>(),DisruptorThreadFactory.create("shenyu_disruptor_consumer_", false), new ThreadPoolExecutor.AbortPolicy());int newConsumerSize = this.consumerSize;EventFactory<DataEvent<T>> eventFactory;if (isOrderly) {newConsumerSize = 1;eventFactory = new OrderlyDisruptorEventFactory<>();} else {eventFactory = new DisruptorEventFactory<>();}Disruptor<DataEvent<T>> disruptor = new Disruptor<>(eventFactory,size,DisruptorThreadFactory.create("shenyu_disruptor_provider_" + consumerFactory.fixName(), false),ProducerType.MULTI,new BlockingWaitStrategy());@SuppressWarnings("all")QueueConsumer<T>[] consumers = new QueueConsumer[newConsumerSize];for (int i = 0; i < newConsumerSize; i++) {consumers[i] = new QueueConsumer<>(executor, consumerFactory);}disruptor.handleEventsWithWorkerPool(consumers);disruptor.setDefaultExceptionHandler(new IgnoreExceptionHandler());disruptor.start();RingBuffer<DataEvent<T>> ringBuffer = disruptor.getRingBuffer();provider = new DisruptorProvider<>(ringBuffer, disruptor, isOrderly);}
RegisterServerConsumerExecutor用来处理Disruptor中的数据,选择执行器
@Overridepublic void run() {Collection<DataTypeParent> results = getData().stream().filter(this::isValidData).collect(Collectors.toList());if (CollectionUtils.isEmpty(results)) {return;}selectExecutor(results).executor(results);}
MetadataExecutorSubscriber#executor,根据rpcType获取shenyuClientRegisterService。
@Overridepublic void executor(final Collection<MetaDataRegisterDTO> metaDataRegisterDTOList) {metaDataRegisterDTOList.forEach(meta -> {Optional.ofNullable(this.shenyuClientRegisterService.get(meta.getRpcType())).ifPresent(shenyuClientRegisterService -> {synchronized (shenyuClientRegisterService) {shenyuClientRegisterService.register(meta);}});});}
AbstractShenyuClientRegisterServiceImpl#register,注册selector,ruleHandler,metadata和contextPath
@Overridepublic String register(final MetaDataRegisterDTO dto) {//handler plugin selectorString selectorHandler = selectorHandler(dto);String selectorId = selectorService.registerDefault(dto, PluginNameAdapter.rpcTypeAdapter(rpcType()), selectorHandler);//handler selector ruleString ruleHandler = ruleHandler();RuleDTO ruleDTO = buildRpcDefaultRuleDTO(selectorId, dto, ruleHandler);ruleService.registerDefault(ruleDTO);//handler register metadataregisterMetadata(dto);//handler context pathString contextPath = dto.getContextPath();if (StringUtils.isNotEmpty(contextPath)) {registerContextPath(dto);}return ShenyuResultMessage.SUCCESS;}
RuleServiceImpl#registerDefault,判断数据库中是否存在
@Overridepublic String registerDefault(final RuleDTO ruleDTO) {if (Objects.nonNull(ruleMapper.findBySelectorIdAndName(ruleDTO.getSelectorId(), ruleDTO.getName()))) {return "";}RuleDO ruleDO = RuleDO.buildRuleDO(ruleDTO);if (StringUtils.isEmpty(ruleDTO.getId())) {ruleMapper.insertSelective(ruleDO);addCondition(ruleDO, ruleDTO.getRuleConditions());}ruleEventPublisher.onRegister(ruleDO, ruleDTO.getRuleConditions());return ruleDO.getId();}
DataChangedEventDispatcher用于监听DataChangedEvent
public void onApplicationEvent(final DataChangedEvent event) {for (DataChangedListener listener : listeners) {switch (event.getGroupKey()) {case APP_AUTH:listener.onAppAuthChanged((List<AppAuthData>) event.getSource(), event.getEventType());break;case PLUGIN:listener.onPluginChanged((List<PluginData>) event.getSource(), event.getEventType());break;case RULE:listener.onRuleChanged((List<RuleData>) event.getSource(), event.getEventType());break;case SELECTOR:listener.onSelectorChanged((List<SelectorData>) event.getSource(), event.getEventType());applicationContext.getBean(LoadServiceDocEntry.class).loadDocOnSelectorChanged((List<SelectorData>) event.getSource(), event.getEventType());break;case META_DATA:listener.onMetaDataChanged((List<MetaData>) event.getSource(), event.getEventType());break;default:throw new IllegalStateException("Unexpected value: " + event.getGroupKey());}}}
AbstractListDataChangedListener#onRuleChanged,规则改变执行该方法。
@Overridepublic void onRuleChanged(final List<RuleData> changed, final DataEventTypeEnum eventType) {updateRuleMap(getConfig(changeData.getRuleDataId()));switch (eventType) {case DELETE:changed.forEach(rule -> {List<RuleData> ls = RULE_MAP.getOrDefault(rule.getSelectorId(), new ArrayList<>()).stream().filter(s -> !s.getId().equals(rule.getId())).sorted(RULE_DATA_COMPARATOR).collect(Collectors.toList());RULE_MAP.put(rule.getSelectorId(), ls);});break;case REFRESH:case MYSELF:Set<String> selectIdSet = changed.stream().map(RuleData::getSelectorId).collect(Collectors.toSet());RULE_MAP.keySet().removeAll(selectIdSet);changed.forEach(rule -> {List<RuleData> ls = new ArrayList<>(RULE_MAP.getOrDefault(rule.getSelectorId(),new ArrayList<>()));ls.add(rule);ls.sort(RULE_DATA_COMPARATOR);RULE_MAP.put(rule.getSelectorId(), ls);});break;default:changed.forEach(rule -> {List<RuleData> ls = RULE_MAP.getOrDefault(rule.getSelectorId(), new ArrayList<>()).stream().filter(s -> !s.getId().equals(rule.getId())).collect(Collectors.toList());ls.add(rule);ls.sort(RULE_DATA_COMPARATOR);RULE_MAP.put(rule.getSelectorId(), ls);});break;}publishConfig(changeData.getRuleDataId(), RULE_MAP);LOG.debug("[DataChangedListener] RuleChanged {}", changeData.getRuleDataId());}
NacosDataChangedListener#publishConfig,使用nacos发布配置。
@Overridepublic void publishConfig(final String dataId, final Object data) {try {configService.publishConfig(dataId, NacosPathConstants.GROUP, GsonUtils.getInstance().toJson(data),ConfigType.JSON.getType());} catch (NacosException e) {LOG.error("Publish data to nacos error.", e);throw new ShenyuException(e.getMessage());}}
总结一下
- 应用服务往注册中心发布信息,网关服务从注册中心上拉取信息。
- 网关服务根据请求匹配选择器和规则,经过一系列的执行链,调用应用服务。相比于gateway,省去了配置服务的工作。

相关文章:
【ShenYu系列】ShenYu Dubbo插件全流程源码解析
网关启动 在ShenyuConfiguration注入ShenyuWebHandler。 Bean("webHandler")public ShenyuWebHandler shenyuWebHandler(final ObjectProvider<List<ShenyuPlugin>> plugins, final ShenyuConfig config, Lazy final ShenyuLoaderService shenyuLoaderS…...
spring解决循环依赖的三级缓存
一、Spring在创建Bean的过程中分三步 实例化,对应方法:AbstractAutowireCapableBeanFactory中的createBeanInstance方法,简单理解就是new了一个对象。属性注入,对应方法:AbstractAutowireCapableBeanFactory的populat…...
C++ - 标准库(STL)
目录 一、简介 二、什么时候使用STL 2.1、 vector 和 deque 的使用场景 2.2、 vector 和 deque 的比较 2.3、 list的使用场景 一、简介 C标准库是C编程语言的标准程式库,它提供了一个通用的容器类、算法和函数模板库。 其中包括了多种容器类型,例…...
Java使用 Scanner连续输入int, String 异常错误输出原因分析
目录 一、Scanner常用语法 1、sc.nextInt()介绍 2、sc.next()介绍 3、sc.nextLine()介绍 4、sc.hasNext()介绍 二、报错案例 1、使用next()来接收带有空格的字符串会输出异常 2、先输入数字再输入字符串的输出异常 一、Scanner常用语法 Scanner sc new Scanner(System.…...
pt13网络编程
网络编程 OSI 7层模型 建立了统一的通信标准 降低开发难度,每层功能明确,各司其职 七层模型实际规定了每一层的任务,该完成什么事情 TCP/IP模型 七层模型过于理想,结构细节太复杂在工程中应用实践难度大实际工作中以TCP/IP模型…...
华为云 绑定/更换证书
操作场景 为了支持HTTPS数据传输加密认证,在创建HTTPS协议监听的时候需绑定证书,您可以参考本章节绑定证书。如果弹性负载均衡实例使用的证书过期或者其它原因需要更换,您可以参考本章节更换证书。如果还有其他的服务也使用了待更换的证书&a…...
重大问题,Windows11出现重大BUG
重大问题,Windows11出现重大BUG 这种Windows11操作系统出现BUG已经可以说是非常常见的,但是,今天我将代表所有微软用户,解决一个关于UI设计非常不舒服的功能 关闭多平面覆盖 事情叙述问题 微软社区解决方案自己发现的解决方案解决…...
傅里叶变换解析
p.s.本文无论是cos还是sin,都统一用“正弦波”(Sine Wave)一词来代表简谐波。 一、什么是频域 从我们出生,我们看到的世界都以时间贯穿,股票的走势、人的身高、汽车的轨迹都会随着时间发生改变。这种以时间作为参照来观察动态世界的方法我们称…...
你的登录接口真的安全吗?
1.前言 大家学写程序时,第一行代码都是hello world。但是当你开始学习WEB后台技术时,很多人的第一个功能就是写的登录 (小声:别人我不知道,反正我是)。但是我在和很多工作经验较短的同学面试或沟通的时候&…...
ChatGPT情商很高,但并不适合当搜索引擎
微软和谷歌正急于使用大型语言模型技术来强化搜索引擎。但有充分的理由认为,相比于提供事实性信息,这项技术更适合作为人们情感上的伴侣。 美媒评论称,目前基于大型语言模型的人工智能工具,例如ChatGPT,更擅长共情而不…...
Mac 地址与 IP 地址有什么区别?
Mac 地址和 IP 地址是两个不同的概念,它们分别代表了计算机网络中的不同层次和地址。Mac 地址是物理地址,是在计算机硬件中存储的地址,通常是以特定的六进制格式表示。每个设备都有一个唯一的 MAC 地址,它可以用来在计算机之间进行…...
bootloaders
什么是BootLoader? 一般来说,bootloader是一种软件/固件,它在SoC上电后立即运行。bootloader的主要职责是启动软件的后续部分,例如操作系统、baremetal应用程序或在某些情况下另一个bootloader。当涉及到嵌入式时,bootloader通常…...
PC或服务器装双系统
1. 准备工作 1.1U盘启动盘的制作 ①准备一个 4G 以上的 U 盘,备份好U盘资料,后面会对 U 盘进行格式化。 ②去CentOS官网下载你想要安装的 ISO 格式镜像文件,现在通常是CentOS6、7或者8。如果你英文不太好,可以选择使用edge浏览…...
嵌入式代码查看分析利器---Understand
平时在开发嵌入式程序的时候大多数使用的都是keil软件,一般小的工程使用keil没感觉到有什么问题,但是当工程比较大的时候,比如移植了FreeRTOS系统或者LWIP网络系统时,代码全部编译一次就要花费很长世间,特别是开启了点…...
人群计数经典方法Density Map Estimation,密度图估计
(3)Density Map Estimation(主流) 这是crowd counting的主流方法 传统方法不好在哪里?object detection-based method和regression-based method无法从图像中提取更抽象的有助于完成人群计数任务的语义特征 概况&…...
【华为】Smart-Link基础知识
Smark-Link技术 Smark-Link(灵活链路or备份链路,华为/华三 私有用) Smark-Link定义 Smark-Link,又叫备份链路。一个Smark Link由两个接口组组成,其中一个接口作为另一个的备份。Smark-Link常用于双上行组网,提供可靠高效的备份与…...
分享24个强大的HTML属性 —— 建议每位前端工程师都应该掌握
前期回顾 是不是在为 API 烦恼 ?好用免费的api接口大全呼之欲出_0.活在风浪里的博客-CSDN博客APi、常用框架、UI、文档—— 整理合并https://blog.csdn.net/m0_57904695/article/details/130459417?spm1001.2014.3001.5501 👍 本文专栏:…...
NIO基础 - 网络编程
non-blocking io 非阻塞 IO 1. 三大组件 1.1 Channel & Buffer channel 有一点类似于 stream,它就是读写数据的双向通道,可以从 channel 将数据读入 buffer,也可以将 buffer 的数据写入 channel,而之前的 stream 要么是输入…...
06.toRef 和 toRefs
学习要点: 1.toRef 和 toRefs 本节课我们来要了解一下 Vue3.x 中的 ref 两个周边 API 的用法; 一.toRef 和 toRefs 1. toRef 可以将源响应式对象上的 property 创建一个 ref 对象; const obj reactive({ name : Mr.Lee, age : 10…...
RabbitMq、Kafka、RocketMq整理
MQ的主要作用:异步提高性能、解耦提高扩展性、削峰。 一、常见中间件对比 Kafka、RocketMq和RabbitMq最大的区别就是:前两个是分布式存储。 1.1、ActiveMq 优点:1)完全支持jms规范的消息中间件 ,2)提供丰富的api, 3)多种集群构建模式。 缺点:)在高并发的场景下,性能可…...
LBE-LEX系列工业语音播放器|预警播报器|喇叭蜂鸣器的上位机配置操作说明
LBE-LEX系列工业语音播放器|预警播报器|喇叭蜂鸣器专为工业环境精心打造,完美适配AGV和无人叉车。同时,集成以太网与语音合成技术,为各类高级系统(如MES、调度系统、库位管理、立库等)提供高效便捷的语音交互体验。 L…...
rknn优化教程(二)
文章目录 1. 前述2. 三方库的封装2.1 xrepo中的库2.2 xrepo之外的库2.2.1 opencv2.2.2 rknnrt2.2.3 spdlog 3. rknn_engine库 1. 前述 OK,开始写第二篇的内容了。这篇博客主要能写一下: 如何给一些三方库按照xmake方式进行封装,供调用如何按…...
Debian系统简介
目录 Debian系统介绍 Debian版本介绍 Debian软件源介绍 软件包管理工具dpkg dpkg核心指令详解 安装软件包 卸载软件包 查询软件包状态 验证软件包完整性 手动处理依赖关系 dpkg vs apt Debian系统介绍 Debian 和 Ubuntu 都是基于 Debian内核 的 Linux 发行版ÿ…...
如何在看板中体现优先级变化
在看板中有效体现优先级变化的关键措施包括:采用颜色或标签标识优先级、设置任务排序规则、使用独立的优先级列或泳道、结合自动化规则同步优先级变化、建立定期的优先级审查流程。其中,设置任务排序规则尤其重要,因为它让看板视觉上直观地体…...
Redis数据倾斜问题解决
Redis 数据倾斜问题解析与解决方案 什么是 Redis 数据倾斜 Redis 数据倾斜指的是在 Redis 集群中,部分节点存储的数据量或访问量远高于其他节点,导致这些节点负载过高,影响整体性能。 数据倾斜的主要表现 部分节点内存使用率远高于其他节…...
USB Over IP专用硬件的5个特点
USB over IP技术通过将USB协议数据封装在标准TCP/IP网络数据包中,从根本上改变了USB连接。这允许客户端通过局域网或广域网远程访问和控制物理连接到服务器的USB设备(如专用硬件设备),从而消除了直接物理连接的需要。USB over IP的…...
uniapp 开发ios, xcode 提交app store connect 和 testflight内测
uniapp 中配置 配置manifest 文档:manifest.json 应用配置 | uni-app官网 hbuilderx中本地打包 下载IOS最新SDK 开发环境 | uni小程序SDK hbulderx 版本号:4.66 对应的sdk版本 4.66 两者必须一致 本地打包的资源导入到SDK 导入资源 | uni小程序SDK …...
深度剖析 DeepSeek 开源模型部署与应用:策略、权衡与未来走向
在人工智能技术呈指数级发展的当下,大模型已然成为推动各行业变革的核心驱动力。DeepSeek 开源模型以其卓越的性能和灵活的开源特性,吸引了众多企业与开发者的目光。如何高效且合理地部署与运用 DeepSeek 模型,成为释放其巨大潜力的关键所在&…...
0x-3-Oracle 23 ai-sqlcl 25.1 集成安装-配置和优化
是不是受够了安装了oracle database之后sqlplus的简陋,无法删除无法上下翻页的苦恼。 可以安装readline和rlwrap插件的话,配置.bahs_profile后也能解决上下翻页这些,但是很多生产环境无法安装rpm包。 oracle提供了sqlcl免费许可,…...
嵌入式学习之系统编程(九)OSI模型、TCP/IP模型、UDP协议网络相关编程(6.3)
目录 一、网络编程--OSI模型 二、网络编程--TCP/IP模型 三、网络接口 四、UDP网络相关编程及主要函数 编辑编辑 UDP的特征 socke函数 bind函数 recvfrom函数(接收函数) sendto函数(发送函数) 五、网络编程之 UDP 用…...
