【Soul源码阅读】14.soul-admin 与 soul-bootstrap 同步机制之 nacos 解析(上)

Soul 网关(一)---- 架构设计简介、soul-admin、soul-bootstrap Soul 网关(一) Soul 是一个异步的,高性能的,跨语言的,响应式的API网关。今天先来整体了解一下 Soul 的结构。 Soul 架构图 两大部分:Soul-Admin(Soul 网关管理系统) 和 Soul-Cluster(网关Soul 集群) soul-admin (soul 网关的管理系统) soal-admin 依赖 MySQL,首先我们创建 soul 数据库,创建所需要的表,一共 12 个。 然后启动 soul-admin soul-admin 默认端口 9095 ,访问 http 阅读详情

目录

1.准备配套设施

2.配置

2.1 soul-admin 配置

2.2 soul-bootstrap 配置

3.启动服务

4.源码分析


1.准备配套设施

启动 MySQL、Nacos。

使用 docker 启动 Nacos(详情见官网github https://github.com/nacos-group/nacos-docker)。

Clone project

git clone --depth 1 https://github.com/nacos-group/nacos-docker.git
cd nacos-docker

Standalone Derby

docker-compose -f example/standalone-derby.yaml up

启动后,访问 http://localhost:8848/nacos/

用户名/密码 nacos/nacos

依次点击【命名空间】【新建命名空间】,手动创建一个 namespace,后面在配置文件中也要使用的,配置如下图所示:

这里要强调一下,同步时使用的是 ID,所以这个 ID 在下面配置文件中也需要配置一致(1c10d748-af86-43b9-8265-75f487d20c6c)。

未启动服务时,全都是空的,如下图所示:

 

2.配置

2.1 soul-admin 配置

soul:
  sync:
      nacos:
        url: localhost:8848
        namespace: 1c10d748-af86-43b9-8265-75f487d20c6c
  #      acm:
  #        enabled: false
  #        endpoint: acm.aliyun.com
  #        namespace:
  #        accessKey:
  #        secretKey:

这里使用的是源码自带的,如果没有使用ACM,注释可以不打开,只打开 url 和 namespace 即可,其中 namespace 要跟 nacos 中新建的保持一致。

2.2 soul-bootstrap 配置

soul:
    sync:
        nacos:
            url: localhost:8848
            namespace: 1c10d748-af86-43b9-8265-75f487d20c6c
#url: 配置成你的nacos地址,集群环境请使用(,)分隔。
# 其他参数配置,请参考naocs官网。

这里要注意的一点是,namespace 一定要跟 soul-admin 中的 namespace 保持一致。

还需要在 pom 文件中引入依赖:

        <!--soul data sync start use nacos-->
        <dependency>
            <groupId>org.dromara</groupId>
            <artifactId>soul-spring-boot-starter-sync-data-nacos</artifactId>
            <version>${project.version}</version>
        </dependency>

3.启动服务

在 soul-bootstrap 日志中,可以看到如下一条日志,就证明使用了 nacos 同步策略:

2021-01-29 02:22:28.839  INFO 22496 --- [           main] d.s.s.s.s.d.n.NacosSyncDataConfiguration : you use nacos sync soul data.......

那成功没成功呢?看下 nacos 管理页面【配置管理】【配置列表】

 

4.源码分析

既然看到上面的日志了,直接搜一下,就找到了 NacosSyncDataConfiguration。

看所在目录结构,就知道这是 Spring start 机制加载的配置类,并且对应到我们上面 2.2 小节的配置文件和引入的依赖包。

好的,那我们就以这里为起点,顺藤摸瓜,看看能摸到什么好东西。

// NacosSyncDataConfiguration.java
/**
 * Nacos sync data configuration for spring boot.
 *
 * @author xiaoyu(Myth)
 */
@Configuration
@ConditionalOnClass(NacosSyncDataService.class)
@ConditionalOnProperty(prefix = "soul.sync.nacos", name = "url")
@Slf4j
public class NacosSyncDataConfiguration {

    /**
     * Nacos sync data service.
     *
     * @param configService     the config service
     * @param pluginSubscriber the plugin subscriber
     * @param metaSubscribers   the meta subscribers
     * @param authSubscribers   the auth subscribers
     * @return the sync data service
     */
    @Bean
    public SyncDataService nacosSyncDataService(final ObjectProvider<ConfigService> configService, final ObjectProvider<PluginDataSubscriber> pluginSubscriber,
                                           final ObjectProvider<List<MetaDataSubscriber>> metaSubscribers, final ObjectProvider<List<AuthDataSubscriber>> authSubscribers) {
        log.info("you use nacos sync soul data.......");
        return new NacosSyncDataService(configService.getIfAvailable(), pluginSubscriber.getIfAvailable(),
                metaSubscribers.getIfAvailable(Collections::emptyList), authSubscribers.getIfAvailable(Collections::emptyList));
    }

    /**
     * Nacos config service config service.
     *
     * @param nacosConfig the nacos config
     * @return the config service
     * @throws Exception the exception
     */
    @Bean
    public ConfigService nacosConfigService(final NacosConfig nacosConfig) throws Exception {
        Properties properties = new Properties();
        if (nacosConfig.getAcm() != null && nacosConfig.getAcm().isEnabled()) {
            properties.put(PropertyKeyConst.ENDPOINT, nacosConfig.getAcm().getEndpoint());
            properties.put(PropertyKeyConst.NAMESPACE, nacosConfig.getAcm().getNamespace());
            properties.put(PropertyKeyConst.ACCESS_KEY, nacosConfig.getAcm().getAccessKey());
            properties.put(PropertyKeyConst.SECRET_KEY, nacosConfig.getAcm().getSecretKey());
        } else {
            properties.put(PropertyKeyConst.SERVER_ADDR, nacosConfig.getUrl());
            properties.put(PropertyKeyConst.NAMESPACE, nacosConfig.getNamespace());
        }
        return NacosFactory.createConfigService(properties);
    }

    /**
     * Http config http config.
     *
     * @return the http config
     */
    @Bean
    @ConfigurationProperties(prefix = "soul.sync.nacos")
    public NacosConfig nacosConfig() {
        return new NacosConfig();
    }
}

关键方法 nacosSyncDataService 调用了 NacosSyncDataService 的构造方法:

// NacosSyncDataService.java
    /**
     * Instantiates a new Nacos sync data service.
     *
     * @param configService         the config service
     * @param pluginDataSubscriber the plugin data subscriber
     * @param metaDataSubscribers   the meta data subscribers
     * @param authDataSubscribers   the auth data subscribers
     */
    public NacosSyncDataService(final ConfigService configService, final PluginDataSubscriber pluginDataSubscriber,
                                final List<MetaDataSubscriber> metaDataSubscribers, final List<AuthDataSubscriber> authDataSubscribers) {

        super(configService, pluginDataSubscriber, metaDataSubscribers, authDataSubscribers);
        start();
    }

    /**
     * Start.
     */
    public void start() {
        watcherData(PLUGIN_DATA_ID, this::updatePluginMap);
        watcherData(SELECTOR_DATA_ID, this::updateSelectorMap);
        watcherData(RULE_DATA_ID, this::updateRuleMap);
        watcherData(META_DATA_ID, this::updateMetaDataMap);
        watcherData(AUTH_DATA_ID, this::updateAuthMap);
    }

在 start 方法中调用的 watcherData 是其父类 NacosCacheHandler 的方法

// NacosCacheHandler.java
    protected void watcherData(final String dataId, final OnChange oc) {
        Listener listener = new Listener() {
            @Override
            public void receiveConfigInfo(final String configInfo) {
                oc.change(configInfo);
            }

            @Override
            public Executor getExecutor() {
                return null;
            }
        };
        oc.change(getConfigAndSignListener(dataId, listener));
        LISTENERS.computeIfAbsent(dataId, key -> new ArrayList<>()).add(listener);
    }

    @SneakyThrows
    private String getConfigAndSignListener(final String dataId, final Listener listener) {
        // 这里根据 dataId 获取配置,并且注册监听器
        String config = configService.getConfigAndSignListener(dataId, GROUP, 6000, listener);
        if (config == null) {
            config = "{}";
        }
        return config;
    }

这里第2个入参使用了 OnChange 接口,而且里面只有一个方法 change,符合函数式接口的定义,我理解这里之所以这么设计,是为了方便前面在调用时可以使用方法引用。

// NacosCacheHandler.java
    protected interface OnChange {
        void change(String changeData);
    }

 这里的 oc.change 方法会分别根据 dataId 调用对应的方法,对应关系就是调用 watcherData 方法时的传参,即

// NacosCacheHandler.java
    protected void updatePluginMap(final String configInfo) {
        try {
            // Fix bug #656(https://github.com/dromara/soul/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);
        }
    }

    protected void updateSelectorMap(final String configInfo) {
        try {
            List<SelectorData> selectorDataList = GsonUtils.getInstance().toObjectMapList(configInfo, SelectorData.class).values().stream().flatMap(Collection::stream).collect(Collectors.toList());
            selectorDataList.forEach(selectorData -> Optional.ofNullable(pluginDataSubscriber).ifPresent(subscriber -> {
                subscriber.unSelectorSubscribe(selectorData);
                subscriber.onSelectorSubscribe(selectorData);
            }));
        } catch (JsonParseException e) {
            log.error("sync selector data have error:", e);
        }
    }

    protected void updateRuleMap(final String configInfo) {
        try {
            List<RuleData> ruleDataList = GsonUtils.getInstance().toObjectMapList(configInfo, RuleData.class).values()
                    .stream().flatMap(Collection::stream)
                    .collect(Collectors.toList());
            ruleDataList.forEach(ruleData -> Optional.ofNullable(pluginDataSubscriber).ifPresent(subscriber -> {
                subscriber.unRuleSubscribe(ruleData);
                subscriber.onRuleSubscribe(ruleData);
            }));
        } catch (JsonParseException e) {
            log.error("sync rule data have error:", e);
        }
    }

    protected void updateMetaDataMap(final String configInfo) {
        try {
            List<MetaData> metaDataList = new ArrayList<>(GsonUtils.getInstance().toObjectMap(configInfo, MetaData.class).values());
            metaDataList.forEach(metaData -> metaDataSubscribers.forEach(subscriber -> {
                subscriber.unSubscribe(metaData);
                subscriber.onSubscribe(metaData);
            }));
        } catch (JsonParseException e) {
            log.error("sync meta data have error:", e);
        }
    }

    protected void updateAuthMap(final String configInfo) {
        try {
            List<AppAuthData> appAuthDataList = new ArrayList<>(GsonUtils.getInstance().toObjectMap(configInfo, AppAuthData.class).values());
            appAuthDataList.forEach(appAuthData -> authDataSubscribers.forEach(subscriber -> {
                subscriber.unSubscribe(appAuthData);
                subscriber.onSubscribe(appAuthData);
            }));
        } catch (JsonParseException e) {
            log.error("sync auth data have error:", e);
        }
    }

这5个方法的逻辑一致,只是需要处理的类型不同而已。

里面最核心的方法

subscriber.unSubscribe(pluginData);
subscriber.onSubscribe(pluginData);
subscriber.unSelectorSubscribe(selectorData);
subscriber.onSelectorSubscribe(selectorData);
subscriber.unRuleSubscribe(ruleData);
subscriber.onRuleSubscribe(ruleData);
subscriber.unSubscribe(metaData);
subscriber.onSubscribe(metaData);
subscriber.unSubscribe(appAuthData);
subscriber.onSubscribe(appAuthData);

其中 pluginData、selectorData、ruleData 最终都会调用 CommonPluginDataSubscriber 的 subscribeDataHandler 方法,其中,解除订阅就是 DELETE,订阅就是 UPDATE:

// CommonPluginDataSubscriber.java
    private <T> void subscribeDataHandler(final T classData, final DataEventTypeEnum dataType) {
        Optional.ofNullable(classData).ifPresent(data -> {
            if (data instanceof PluginData) {
                PluginData pluginData = (PluginData) data;
                if (dataType == DataEventTypeEnum.UPDATE) {
                    BaseDataCache.getInstance().cachePluginData(pluginData);
                    Optional.ofNullable(handlerMap.get(pluginData.getName())).ifPresent(handler -> handler.handlerPlugin(pluginData));
                } else if (dataType == DataEventTypeEnum.DELETE) {
                    BaseDataCache.getInstance().removePluginData(pluginData);
                    Optional.ofNullable(handlerMap.get(pluginData.getName())).ifPresent(handler -> handler.removePlugin(pluginData));
                }
            } else if (data instanceof SelectorData) {
                SelectorData selectorData = (SelectorData) data;
                if (dataType == DataEventTypeEnum.UPDATE) {
                    BaseDataCache.getInstance().cacheSelectData(selectorData);
                    Optional.ofNullable(handlerMap.get(selectorData.getPluginName())).ifPresent(handler -> handler.handlerSelector(selectorData));
                } else if (dataType == DataEventTypeEnum.DELETE) {
                    BaseDataCache.getInstance().removeSelectData(selectorData);
                    Optional.ofNullable(handlerMap.get(selectorData.getPluginName())).ifPresent(handler -> handler.removeSelector(selectorData));
                }
            } else if (data instanceof RuleData) {
                RuleData ruleData = (RuleData) data;
                if (dataType == DataEventTypeEnum.UPDATE) {
                    BaseDataCache.getInstance().cacheRuleData(ruleData);
                    Optional.ofNullable(handlerMap.get(ruleData.getPluginName())).ifPresent(handler -> handler.handlerRule(ruleData));
                } else if (dataType == DataEventTypeEnum.DELETE) {
                    BaseDataCache.getInstance().removeRuleData(ruleData);
                    Optional.ofNullable(handlerMap.get(ruleData.getPluginName())).ifPresent(handler -> handler.removeRule(ruleData));
                }
            }
        });
    }

这里之前也分析到了,但是对于不同类型的还需要再整体梳理一下,这个后面再梳理。

剩下的 metaData 会有5种不同的子类实现,分别是:

AlibabaDubboMetaDataSubscriber
ApacheDubboMetaDataSubscriber
MetaDataAllSubscriber
SofaMetaDataSubscriber
TarsMetaDataSubscriber

appAuthData:

/**
 * The type Sign auth data subscriber.
 *
 * @author xiaoyu
 */
public class SignAuthDataSubscriber implements AuthDataSubscriber {
    
    @Override
    public void onSubscribe(final AppAuthData appAuthData) {
        SignAuthDataCache.getInstance().cacheAuthData(appAuthData);
    }
    
    @Override
    public void unSubscribe(final AppAuthData appAuthData) {
        SignAuthDataCache.getInstance().removeAuthData(appAuthData);
    }
}

把内存中的数据重新处理后,把数据缓存在了 LISTENERS 中。

OK,到这里,启动时处理逻辑就分析完了。

那如果我在 soul-admin web 页面修改 divide 插件内的 selector 或 rule 时,是如何同步的呢?我们明天再来分析,敬请期待。

 

 

Soul源码分析–soul-admin入门 Soul源码分析–soul-admin运行使用原理介绍 运行 上一篇介绍过 soul-admin 启动有两种数据库选择,一种是MySQL, 一种是内存H2数据库。如果本地有MySQL,建议还是使用MySQL数据库,毕竟等下我们还是需要看看 soul-admin 启动时创建的表结构。 启动后我们选择"插件列表"->“divide”,如果没有启动别的服务,这个菜单里面应该是空的。 这里我们还要启动 Soul 网关主要的服务 soul-bootstrap,如果不想自己写业务服务来测试,需要把 soul-e 阅读详情

相关推荐

Soul源码分析–soul-admin源码分析

Soul源码分析–soul-admin源码分析前言源码分析总结 前言 在上一篇文章中,介绍了 soul-admin 怎么管理和配置网关的请求,其核心原理是基于各种插件来实现对应的功能。现在我们来看看后台是怎么来管理和实现动态配置。 源码分析 在分析源码之前,先看下图,页面显示加载的插件列表会对应后端的请求,根据后端请求,找到对应的controller类 然后找到对应的方法,在上图可以看到这里是访问 plugin 中默认为空的 mapping,传入到分页相关的参数,然后去查询数据库中对应的插件记录 数据库

yutanbo123的博客 513

Soul 极简入门(国产微服务网关)

1. 概述 Soul是基于WebFlux实现的响应式的 API 网关,具有异步、高性能、跨语言等特点。 作者:我希望能够有一样东西像灵魂一样,保护您的微服务。在参考了 Kong、Spring Cloud Gateway等优秀的网关后,站在巨人的肩膀上,Soul 由此诞生! 作者是艿艿的大表弟,胖友信么?! 目前 Soul 功能列表如下: 支持各种语言,无缝集成到 Dubbo、Spring Cloud、Spring Boot 中。 Soul 是极其少支持 Dubbo 的 AP...

weixin_42073629的博客 4935

鸿蒙OS开机动画背后的秘密:从代码到视觉的完整解析(附源码分析)

本文深入解析了鸿蒙OS开机动画的系统级实现原理。从bootanimation服务的启动配置、与Render Service的协同,到基于VSync信号的帧率同步与绘制流水线,结合OpenHarmony源码进行了逐行解读。文章揭示了其事件驱动的优雅退出机制,并探讨了视频支持、多屏适配等高级特性与定制化可能,为开发者理解鸿蒙图形子系统提供了硬核技术视角。

sony5的博客 969

google的api key调用次数是多少_并发、分布式、SQL编程中你遇到多少锁?距离高级工程师只差一步...

什么是锁,锁有哪些锁是java并发编程中最重要的同步机制。它可使临界区互斥执行,部分锁可以通过一定的手段进行消息通讯。Java中有哪些锁?分布式锁实现分布式锁常用实现有1. 数据库乐观锁;2. 基于Redis的分布式锁;3. 基于ZooKeeper的分布式锁,此章节着重介绍数据库乐观锁和基于redis的分布式锁的实现。乐观锁、悲观锁乐观锁:总是认为不会产生并发问题,每次去取数据的时候总认为不会有其...

weixin_39631951的博客 185

QMT Python API 实战指南:从安装到策略开发的完整流程

本文提供了一份详尽的QMT Python API实战指南,涵盖从环境配置到策略开发的完整流程。文章深入解析了xtdata行情模块与xttrader交易模块的核心架构与协作方式,并通过一个双均线策略实例,手把手教你如何获取数据、计算信号并执行交易,是量化交易开发者快速上手QMT Python接口文档的实用教程。

butter的博客 1413

华为AR3260路由器NAT回流配置实战:从WEB界面到命令行的完整避坑指南

本文详细解析了华为AR3260路由器NAT回流(Hairpin NAT)的配置原理与实战方法。针对内网用户无法通过公网地址访问内部服务器的常见问题,文章不仅剖析了WEB界面配置的常见报错原因,更提供了清晰、可靠且一劳永逸的命令行(CLI)配置方案与排错指南,帮助企业网络工程师彻底解决内外网访问不一致的难题。

u6v7w8x的博客 872

Soul网关(十)---- 多soul-admin与多soul-bootstrap的数据同步

演示多个 soul-admin 和 多个 soul-bootstrap 数据同步 分别在 9095 端口、9096 端口启动 soul-admin; 分别在 9197 端口、9198端口启动 soul-bootstrap; 在其中一个 soul-admin 改动divide选择器其中的一个rule: 将精确匹配URL 由 /http/order/findById 改为 /http/order/findById111 改完之后可以看到另外一个 soul-admin 中此条规则也同步更新了。 ========

Loving you 785

树莓派Bookworm系统避坑指南:3种方法解决pip install报错externally-managed-environment

本文详细解析树莓派Bookworm系统中pip install报错externally-managed-environment的成因,并提供三种实用解决方案:使用虚拟环境(venv)隔离项目依赖、通过pipx管理全局Python工具,以及理解风险后的系统安装方法。帮助开发者安全高效地管理Python环境,避免系统冲突。

android的专栏 763

soul网关系列(八):soul-admin与网关数据同步之zookeeper

本篇文章主要介绍soul网关admin与bootstrap数据同步的另外三种方式(zookeeper/http长连接/nacos)

坚持、死磕、重复 602

【异常】Cursor IDE 扩展主机超时错误处理文档The agent execution provider did not respond within 4 seconds.

该错误通常是 Cursor IDE 的临时性问题,不影响项目代码。大多数情况下,重新加载窗口或重启 IDE 即可解决。如果问题持续,可按上述进阶方案排查。重新加载窗口(最快)重启 IDE检查系统资源清理缓存检查扩展更多关于 AI+教育领域的干货,可以关注我的微信公众号第一时间获取哦~

奶爸的编程之路,也就一周冷个三天 2697

ethernet eth0: Could not attach to PHY

整个流程看下来就是MAC connect PHY的过程失败了,失败的具体原因根据config_init,上图(bcm54xx_config_init) ,从此方向入手去定位原因;我这里的问题是has_gmac4没有置1,根据 has_gmac4: uses GMAC4 core.按照上面的调用逻辑,添加了部分打印,定位到__phy_read接口返回失败。stmmac_mdio_read 大部分都是linux的标准接口。最终取决于平台用的是哪家的phy,调用config_init。

无线互联&网络介绍 2700

【Soul源码阅读】15.soul-admin 与 soul-bootstrap 同步机制之 nacos 解析(下)

目录 1.准备 2.页面操作 3.跟踪源码 3.1 soul-admin 端 3.2 soul-bootstrap 端 上一篇讲到使用 nacos 同步策略,在项目启动时的同步机制,今天来看下在页面操作时,是怎么同步的。 1.准备 按照昨天的帖子(【Soul源码阅读】14.soul-admin 与 soul-bootstrap 同步机制之 nacos 解析(上)),把项目启动起来,soul-admin、soul-bootstrap 和 soul-examples-http 三个项目,当然还有

hellboy0621的博客 1130

杰理AC696N使用ADC检测电位器调音量教程

本教程详细介绍了在杰理AC696N芯片上使用ADC功能检测电位器电压值的方法。主要内容包括:硬件连接方式(电位器VCC/OUT/GND分别接3.3V、PA3引脚和地线),10位ADC配置(0-1023对应0-3.3V),以及核心代码实现。关键步骤涉及ADC采样通道初始化、GPIO引脚配置(关闭数字输入和上下拉电阻),以及通过定时器实现50Hz采样频率。教程还详解了ADC值到系统音量的线性映射算法,并提供了完整的代码示例和调试方法,帮助开发者实现电位器实时控制音量的功能。

qq_42014563的博客 1245

Soul网关源码阅读(十)- Soul 网关 Nacos 数据同步源码解析

学习目标: 学习Soul 网关 Nacos 数据同步源码解析 学习内容: Soul 网关 Nacos 数据同步原理 学习时间:2020年1月25号 提示:这里可以添加计划学习的时间 例如: 1、 周一至周五晚上 7 点—晚上9点 2、 周六上午 9 点-上午 11 点 3、 周日下午 3 点-下午 6 点 学习产出: 提示:这里统计学习计划的总量 例如: 1、 技术笔记 2 遍 2、优快云 技术博客 3 篇 3、 学习的 vlog 视频 1 个 ...

koutann2015的博客 173

Python全栈项目--手机语音远程控制智能家居系统(含嵌入式控制)

摘要:本项目实现了一个基于Python全栈的智能家居语音控制系统,支持手机端语音指令控制家居设备。系统采用FastAPI后端+Vue3前端的架构,集成Web Speech API实现语音识别,通过嵌入式设备(如ESP32)执行硬件控制。功能包括用户认证、设备管理、场景联动、指令解析和日志记录,支持HTTP轮询和直推两种命令下发模式。项目涵盖物联网全链路开发,适用于课程设计或毕业设计原型开发。

exlink2012的专栏 877

ET2012新版免狗免安装绿色软件

ET2012新版免狗免安祖昂绿色软件。,新界面,新视觉,

Soul-源码阅读14-Nacos同步数据分析2

问题回顾解决 继续分析昨天boot-strap启动报错NPE的问题,提示: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'nacosSyncDataService' defined in class path resource [org/dromara/soul/springboot/starter/sync/data/nacos/NacosSyncDataConfigurati

tbmingzhao的博客 602

jdk-8u144-linux-x64.tar.gz.

该资源为搭建Linux系统Java环境用,资源的后缀为tar.gz格式的压缩包

【Soul源码阅读】16.启动多台admin,多个网关,形成一个集群

目录 1.启动多台 soul-admin 2.启动多台 soul-bootstrap 3.启动多台业务系统 4.验证同步 4.1 负载均衡 4.2 数据同步 1.启动多台 soul-admin 修改配置文件,把可以开启允许并行执行的复选框。 application.yml server: port: 9095 server: port: 9096 启动后状态: 2.启动多台 soul-bootstrap 跟上面的一样,修改配置文件,打开并行执行的开关。.

hellboy0621的博客 583

老版QQ原生表情gif动态

这是老版的QQ表情,不过全是动态的的,Windows照片查看器是不会动的,可以右击用浏览器打开查看,再说一遍是老版的,只是分享给宁愿用老版也要动态的人。

大中型超市收银系统(可直接运行)

光步前台收银系统系统功能:商品销售、商品退货、挂单、收银员权限、会员销售、单品取消、整单取消、商品查询、脱机销售、脱机数据上传,基本资料下载,开钱箱、小票打印、收款结算,支持多币种付款方式(现金、储值卡、礼券、微信等),支持多种促销。运行环境:.net 4.0登陆信息用户名:1001 密码:123456内置商品信息,商品编码可输入00001000 - 00010000,测试效果相关功能键F5 结算 F9挂单 F11商品查询 F8退货 Insert单品取消 Del整单取消 F12开钱箱F2 会员 Ctrl+Z退出系统 Home 锁屏 相关源码待公开

Soul高性能服务网关搭建指南

<?xml version="1.0" encoding="UTF-8"?> <!-- ~ Licensed to the Apache Software Foundation (ASF) under one or more ~ contributor license agreements. See the NOTICE file distributed with ~ this work for additional information regarding copyri.

一个天秤座的架构师 2万+

原始的接收前端数据方法

@PostMapping("test") @ApiOperation("测试") public String Test(HttpServletRequest request){ //数据在reqest中 String str= testService.test(retquest); return str; } public String test(HttpServletRequest request) { net.sf.json.JSONObjec

等哈哆 267

soul网关系列(九):soul-admin与网关数据同步之http长轮询-admin端

soul-admin与网关数据同步之http长轮询-admin端

坚持、死磕、重复 403

翻牌源码之Nacos--配置中心实现 一

Nacos怎么入门?当然是从看官方文档开始。这篇博客就把怎么安装Nacos,怎么进行服务的注册进行一个梳理吧。

YuGuHan的博客 930

Soul网关源码解析(番外2):soul-bootstrap启动流程

Soul网关源码解析(番外2):soul-bootstrap启动流程 Soul网关源码解析(番外2):soul-bootstrap启动流程Debug Bootstrap的方法Soul-Bootstrap启动流程WebHandler的初始化Plugins的初始化SyncDataService初始化小结参考 Debug Bootstrap的方法 对于Bootstrap启动过程中,具体哪些类参与,分别干了什么,在刚开始我们是不知道如何入手的,这里介绍一种方式,能让我们较快的找到突破口,当然这种方式对阅读其他

S_powerStone的博客 383

Nacos配置中心交互模型原理

对于Nacos大家应该都不太陌生,出身阿里名声在外,能做动态服务发现、配置管理,非常好用的一个工具。然而这样的技术用的人越多面试被问的概率也就越大,如果只停留在使用层面,那面试可能要吃大亏。 比如我们今天要讨论的话题,Nacos在做配置中心的时候,配置数据的交互模式是服务端推过来还是客户端主动拉的? 这里我先抛出答案:客户端主动拉的! 接下来咱们扒一扒Nacos的源码,来看看它具体是如何实现的? 配置中心 聊Nacos之前简单回顾下配置中心的由来。 简单理解配置中心的作用就是对配置统一管理,

qq_38730338的博客 1219

【Nacos源码分析】- 01 ConfigService创建流程

基本API/** *测试从NacosServer拉取配置内容 *@throwsNacosException */ @Test publicvoidgetConfig...

god_86的专栏 3231

【Soul源码阅读】9.soul-admin 与 soul-bootstrap 同步机制之 websocket 解析

1.前情回顾 在第4篇中,我们分析了 HTTP 用户业务系统接入 Soul 网关后,会调用 soul-admin 的注册接口,把需要网关代理的接口信息全部注册到 soul-admin 上,在最后,会通过 websocket 长连接,将soul-admin 接收到的接口信息同步给 Soul 网关(即 soul-bootstrap),今天就来接着继续分析,数据是怎么同步到 soul-bootstrap 的。 不清楚流程的可以出门左转看下第4篇文章【Soul源码阅读】4.HTTP 用户接入 Soul 调用 .

hellboy0621的博客 1080

微服务网关soul搭建

1.git clonehttps://github.com/Dromara/soul.git源码 2.安装zk 3.刷入 源码目录scripts/下soul.sql到数据库 4.修改soul-admin目录下相关参数 5.启动SoulAdminApplication ,访问http://localhost:8888/index.html输入 admin /123456 6...

工匠精神coding,终生学习 6090
上一篇: 【Soul源码阅读】13.soul-admin 与 soul-bootstrap 同步机制之 http 长轮询解析(下)
下一篇: 【Soul源码阅读】15.soul-admin 与 soul-bootstrap 同步机制之 nacos 解析(下)
hellboy0621
博客等级 码龄9年 61粉丝 147原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值