Apollo2.1.0+Springboot使用OpenApI

apollo2,springboot,使用,openapi · 浏览次数 : 36

小编点评

```java @RestController @RequestMapping("/apollo") @SwaggerScanClass public class ApolloOperatorController { /** 分页查询数据 namespaceName */ @PostMapping("/queryLimit") @ApiOperation("查询Apollo配置") public Resp> queryPage( @RequestParam String appId, @RequestParam String clusterName, @RequestParam String env, @RequestParam String namespaceName ) throws IOException { ApolloOpenApiOperator apolloOpenApiOperator = ApolloOpenApiOperator.getApolloOpenApiOperator(); ApolloOpenApiOperator.BaseApolloKey baseApolloKey = new ApolloOpenApiOperator.BaseApolloKey(); baseApolloKey.setAppId(appId); baseApolloKey.setClusterName(clusterName); baseApolloKey.setEnv(env); baseApolloKey.setNamespaceName(namespaceName); List openItemDTOS = apolloOpenApiOperator.queryApolloKey(baseApolloKey, 0, 10); return Resp.Ok(openItemDTOS); } /** 通过主键查询单条数据 */ @GetMapping("/save") public Resp insert(@RequestBody Map doIssueApolloKey) throws IOException { String json = JSON.toJSONString(doIssueApolloKey); ApolloOpenApiOperator.DoIssueApolloKey doIssueApolloData = JSONObject.parseObject(json, ApolloOpenApiOperator.DoIssueApolloKey.class); ApolloOpenApiOperator apolloOpenApiOperator = ApolloOpenApiOperator.getApolloOpenApiOperator(); doIssueApolloData.setModifiedOrInsert(false); apolloOpenApiOperator.doIssueApollo(doIssueApolloData); return Resp.Ok(doIssueApolloData.getKey()); } /** 修改数据 */ @PutMapping(\"/update") public Resp update(@RequestBody Map doIssueApolloKey) throws IOException { String json = JSON.toJSONString(doIssueApolloKey); ApolloOpenApiOperator.DoIssueApolloKey doIssueApolloData = JSONObject.parseObject(json, ApolloOpenApiOperator.DoIssueApolloKey.class); ApolloOpenApiOperator apolloOpenApiOperator = ApolloOpenApiOperator.getApolloOpenApiOperator(); doIssueApolloData.setModifiedOrInsert(true); apolloOpenApiOperator.doIssueApollo(doIssueApolloData); return Resp.Ok(doIssueApolloData.getKey()); } /** Description: 移出Apollo已下发数据 */ @GetMapping(\"/dels") public Resp delete(@RequestParam String key, @RequestParam String appId, @RequestParam String clusterName, @RequestParam String env, @RequestParam String namespaceName) throws IOException { ApolloOpenApiOperator apolloOpenApiOperator = ApolloOpenApiOperator.getApolloOpenApiOperator(); ApolloOpenApiOperator.DoIssueApolloKey removeKeys = new ApolloOpenApiOperator.DoIssueApolloKey(); removeKeys.setAppId(appId); removeKeys.setEnv(env); removeKeys.setNamespaceName(namespaceName); removeKeys.setClusterName(clusterName); removeKeys.setKey(key); apolloOpenApiOperator.executorRemove(removeKeys); return Resp.Ok(removeKeys.getKey()); } } ```

正文

依赖管理

  <!-- bootstrap最高级启动配置读取 -->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-bootstrap</artifactId>
            <version>3.1.3</version>
        </dependency>

        <!-- https://mvnrepository.com/artifact/com.ctrip.framework.apollo/apollo-client -->
        <dependency>
            <groupId>com.ctrip.framework.apollo</groupId>
            <artifactId>apollo-client</artifactId>
            <version>2.1.0</version>
        </dependency>
        <dependency>
            <groupId>com.ctrip.framework.apollo</groupId>
            <artifactId>apollo-openapi</artifactId>
            <version>2.1.0</version>
        </dependency>

application.properties

# Apollo
app.id=MORE-USER-USE
apollo.cache-dir=./ManyUser/apollo/cache
apollo.bootstrap.enabled=true
#apollo.bootstrap.namespaces=application.properties
apollo.meta=http://localhost:8080
apollo.bootstrap.eagerLoad.enabled=true
apollo.config-service=http://localhost:8080
env=DEV
apollo.use.admin.token=809d168da6f3baa39c02c1d376a0cc9cec852fa0fa8c6e2cff9b33e276d3738a
apollo.use.portal.url=http://localhost:8070

apollo-env.properties

dev.meta=http://localhost:8080

启动类 加入 @EnableApolloConfig

/**
 * @description: 启动类
 * @author: GuoTong
 * @createTime: 2023-06-21 20:31
 * @since JDK 1.8 OR 11
 **/
@SpringBootApplication
@EnableApolloConfig
public class AppStartClass {

    public static void main(String[] args) {
        SpringApplication.run(AppStartClass.class, args);
    }
}

常规配置中心集成使用

package com.gton.user.config;

import com.ctrip.framework.apollo.model.ConfigChangeEvent;
import com.ctrip.framework.apollo.spring.annotation.ApolloConfigChangeListener;
import com.ctrip.framework.apollo.spring.annotation.ApolloJsonValue;
import com.gton.user.io.Resp;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

import java.util.ArrayList;
import java.util.List;

/**
 * @description: Apollo同时还增加了几个新的Annotation来简化在Spring环境中的使用。
 * @author: GuoTong
 * @createTime: 2023-06-24 12:30
 * @since JDK 1.8 OR 11
 **/
@Component
@Slf4j
public class MyApolloConfig {

    /**
     * ApolloJsonValue annotated on fields example, the default value is specified as empty list - []
     * <br />
     * jsonBeanProperty=[{"someString":"hello","someInt":100},{"someString":"world!","someInt":200}]
     */
    @ApolloJsonValue("${jsonBeanProperty:[]}")
    private List<Resp> anotherJsonBeans = new ArrayList<>();


    @Value("${batch:100}")
    private String batch;


    //config change listener for namespace application
    @ApolloConfigChangeListener(value = "application")
    private void apolloChange(ConfigChangeEvent changeEvent) {
        //update injected value of batch if it is changed in Apollo
        if (changeEvent.isChanged("batch")) {
            batch = changeEvent.getChange("batch").getNewValue();
        }
        log.info(changeEvent.getNamespace() + "变化的key=>" + changeEvent.changedKeys() + ";==1");
    }

    @ApolloConfigChangeListener(value = "order")
    private void someOnChange(ConfigChangeEvent changeEvent) {
        //update injected value of batch if it is changed in Apollo
        if (changeEvent.isChanged("anotherJsonBeans")) {
            log.info(changeEvent.getChange("anotherJsonBeans").getOldValue());
            log.error(changeEvent.getChange("anotherJsonBeans").getNewValue());
        }
        log.info(anotherJsonBeans.toString());
        log.info(changeEvent.getNamespace() + "变化的key=>" + changeEvent.changedKeys() + ";==1");
    }

}

Apollo 的开放平台使用

package com.gton.user.handler;

import com.ctrip.framework.apollo.openapi.client.ApolloOpenApiClient;
import com.ctrip.framework.apollo.openapi.dto.NamespaceReleaseDTO;
import com.ctrip.framework.apollo.openapi.dto.OpenAppNamespaceDTO;
import com.ctrip.framework.apollo.openapi.dto.OpenItemDTO;
import com.ctrip.framework.apollo.openapi.dto.OpenPageDTO;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.apache.commons.lang3.StringUtils;
import org.springframework.core.io.ClassPathResource;

import java.io.IOException;
import java.io.InputStream;
import java.util.List;
import java.util.Properties;

/**
 * @description: Apollo开放平台操作
 * @author: GuoTong
 * @createTime: 2023-06-24 14:10
 * @since JDK 1.8 OR 11
 **/
public class ApolloOpenApiOperator {

    private ApolloOpenApiClient apolloOpenApiClient;

    private static ApolloOpenApiOperator apolloOpenApiOperator = null;

    public static ApolloOpenApiOperator getApolloOpenApiOperator() throws IOException {

        if (apolloOpenApiOperator == null) {
            synchronized (ApolloOpenApiOperator.class) {
                if (apolloOpenApiOperator == null) {
                    return new ApolloOpenApiOperator();
                }
            }
        }
        return apolloOpenApiOperator;
    }

    public ApolloOpenApiOperator(ApolloOpenApiClient apolloOpenApiClient) {
        this.apolloOpenApiClient = apolloOpenApiClient;
    }

    public ApolloOpenApiOperator() throws IOException {
        initApolloOpenApiOperator();
    }

    private void initApolloOpenApiOperator() throws IOException {
        // 获取配置文件
        ClassPathResource classPathResource = new ClassPathResource("application.properties");
        InputStream inputStream = classPathResource.getInputStream();
        // 使用properties加载
        Properties properties = new Properties();
        properties.load(inputStream);
        String token = properties.getProperty("apollo.use.admin.token");
        String portalUrl = properties.getProperty("apollo.use.portal.url");
        if (StringUtils.isNotEmpty(token)) {
            this.apolloOpenApiClient = ApolloOpenApiClient
                    .newBuilder().
                            withConnectTimeout(60000).
                            withToken(token).
                            withPortalUrl(portalUrl).
                            withReadTimeout(60000)
                    .build();
        }

    }

    public ApolloOpenApiClient getApolloOpenApiClient() {
        return this.apolloOpenApiClient;
    }

    /**
     * Description:  创建NamSpace
     *
     * @param openAppNamespaceKey
     * @author: GuoTong
     * @date: 2023-06-24 15:04:44
     * @return:void
     */
    public void crateNameSpace(OpenAppNamespaceKey openAppNamespaceKey) {

        OpenAppNamespaceDTO openAppNamespaceDTO = new OpenAppNamespaceDTO();
        openAppNamespaceDTO.setAppId(openAppNamespaceKey.getAppId());
        openAppNamespaceDTO.setName(openAppNamespaceKey.getSpaceName());
        openAppNamespaceDTO.setComment(openAppNamespaceKey.getSpaceDesc());
        openAppNamespaceDTO.setPublic(openAppNamespaceKey.spaceIsPub);
        apolloOpenApiClient.createAppNamespace(openAppNamespaceDTO);
    }

    /**
     * Description:  下发Apollo
     *
     * @param doIssueApolloKey
     * @author: GuoTong
     * @date: 2023-06-24 14:35:52
     * @return:void
     */
    public void doIssueApollo(DoIssueApolloKey doIssueApolloKey) {
        OpenItemDTO openItemDTO = new OpenItemDTO();
        openItemDTO.setKey(doIssueApolloKey.getKey());
        openItemDTO.setValue(doIssueApolloKey.getValue());
        openItemDTO.setComment(doIssueApolloKey.getDesc());
        openItemDTO.setType(doIssueApolloKey.handlerValueType(doIssueApolloKey.getValueType()));
        openItemDTO.setDataChangeCreatedBy(doIssueApolloKey.getOperator());
        if (doIssueApolloKey.isModifiedOrInsert()) {
            openItemDTO.setDataChangeLastModifiedBy(doIssueApolloKey.getOperator());
        }
        // 下发到Apollo
        apolloOpenApiClient.createOrUpdateItem(
                doIssueApolloKey.getAppId(),
                doIssueApolloKey.getEnv(),
                doIssueApolloKey.getClusterName(),
                doIssueApolloKey.getNamespaceName(),
                openItemDTO
        );

    }

    /**
     * Description:  发布整个NameSpace;推荐下发完成之后统一调一次,不要多次的下发操作,每一次下发都调用
     *
     * @param publishApolloKey
     * @author: GuoTong
     * @date: 2023-06-24 14:56:43
     * @return:void
     */
    public void executorPublish(PublishApolloKey publishApolloKey) {

        // apollo下发的数据执行发布
        NamespaceReleaseDTO namespaceReleaseDTO = new NamespaceReleaseDTO();
        //配置版本名称
        namespaceReleaseDTO.setReleaseTitle(publishApolloKey.getReleaseTitle());
        // 发布说明
        namespaceReleaseDTO.setReleaseComment(publishApolloKey.getReleaseDescription());
        // 操作员
        namespaceReleaseDTO.setReleasedBy(publishApolloKey.getReleaseOperator());
        apolloOpenApiClient.publishNamespace(publishApolloKey.getAppId(),
                publishApolloKey.getEnv(),
                publishApolloKey.getClusterName(),
                publishApolloKey.getNamespaceName(), namespaceReleaseDTO);

    }

    /**
     * Description: 移除Apollo的配置
     * String appId, String env, String clusterName, String namespaceName, String key,
     * String operator
     *
     * @param removeKeys
     * @author: GuoTong
     * @date: 2023-06-24 15:27:07
     * @return:void
     */
    public void executorRemove(DoIssueApolloKey removeKeys) {
        apolloOpenApiClient.removeItem(removeKeys.getAppId(),
                removeKeys.getEnv(),
                removeKeys.getClusterName(),
                removeKeys.getNamespaceName(), removeKeys.getKey(), removeKeys.getOperator());
    }

    /**
     * queryApolloKey  many
     *
     * @param baseApolloKey
     * @author: GuoTong
     * @date: 2023-06-24 14:56:43
     * @return:void
     */
    public List<OpenItemDTO> queryApolloKey(BaseApolloKey baseApolloKey, int page, int size) {

        if (page <= 1) {
            page = 0;
        }
        if (size <= 0) {
            size = 10;
        }
        OpenPageDTO<OpenItemDTO> itemsByNamespace = apolloOpenApiClient.findItemsByNamespace(baseApolloKey.getAppId(),
                baseApolloKey.getEnv(),
                baseApolloKey.getClusterName(),
                baseApolloKey.getNamespaceName(), page, size);
        List<OpenItemDTO> content = itemsByNamespace.getContent();
        return content;

    }


    /**
     * queryApolloKey one
     *
     * @param baseApolloKey
     * @author: GuoTong
     * @date: 2023-06-24 14:56:43
     * @return:void
     */
    public OpenItemDTO queryApolloKeyOne(DoIssueApolloKey baseApolloKey) {
        OpenItemDTO itemsByNamespace = apolloOpenApiClient.getItem(baseApolloKey.getAppId(),
                baseApolloKey.getEnv(),
                baseApolloKey.getClusterName(),
                baseApolloKey.getNamespaceName(), baseApolloKey.getKey());
        return itemsByNamespace;

    }


    @Data
    public static class OpenAppNamespaceKey {

        private String appId;

        private String spaceName;

        private String spaceDesc;

        private boolean spaceIsPub = false;

        // 默认值 DEV
        private String env = "DEV";

        private String clusterName;

        private String namespaceName;


    }

    @Data
    @NoArgsConstructor
    @AllArgsConstructor
    public static class DoIssueApolloKey extends BaseApolloKey {


        private String key;

        private String value;


        //  默认值 Apollo
        private String operator = "Apollo";

        private String desc;


        private String valueType;

        // 是否是修改
        private boolean modifiedOrInsert;


        /**
         * Description: 查询 Portal web的前端得出
         *
         * @author: GuoTong
         * @date: 2023-06-24 14:46:42
         * <select class="form-control ng-valid ng-valid-valdr ng-not-empty ng-dirty ng-touched ng-valid-parse" name="type" ng-model="item.type" ng-change="changeType()">
         * <option value="0" class="ng-binding">String</option>
         * <option value="1" class="ng-binding">Number</option>
         * <option value="2" class="ng-binding">Boolean</option>
         * <option value="3" class="ng-binding">JSON</option>
         * </select>
         */
        public int handlerValueType(String valueType) {
            switch (valueType) {
                case "String":
                    return 0;
                case "Number":
                    return 1;
                case "Boolean":
                    return 2;
                case "JSON":
                    return 3;

            }
            // 未匹配就默认使用String的值
            return 0;
        }

    }

    @Data
    @NoArgsConstructor
    @AllArgsConstructor
    public static class BaseApolloKey {

        protected String appId;

        // 默认值 DEV
        protected String env = "DEV";

        // 默认值 default
        protected String clusterName = "default";

        protected String namespaceName;

    }

    @Data
    public static class PublishApolloKey extends BaseApolloKey {

        // 发布时候的标题
        private String releaseTitle;

        // 发布时候的操作员
        private String releaseOperator;


        // 发布时候的DESC
        private String releaseDescription;

    }
}

配合Controller使用

package com.gton.user.controller;


import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONObject;
import com.ctrip.framework.apollo.openapi.dto.OpenItemDTO;
import com.gton.user.annotation.SwaggerScanClass;
import com.gton.user.handler.ApolloOpenApiOperator;
import com.gton.user.io.Resp;
import io.swagger.annotations.ApiOperation;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

import java.io.IOException;
import java.util.List;
import java.util.Map;

/**
 * 多租户表(apollo)表控制层
 *
 * @author 郭童
 * @since 2023-06-22 16:28:10
 */
@RestController
@RequestMapping("apollo")
@SwaggerScanClass
public class ApolloOperatorController {


    /**
     * 分页查询数据 namespaceName
     *
     * @param
     * @return 所有数据
     */
    @PostMapping("/queryLimit")
    @ApiOperation("查询Apollo配置")
    public Resp<List<OpenItemDTO>> queryPage(
            @RequestParam("appId") String appId,
            @RequestParam("clusterName") String clusterName,
            @RequestParam("env") String env,
            @RequestParam("namespaceName") String namespaceName
    ) throws IOException {
        ApolloOpenApiOperator apolloOpenApiOperator = ApolloOpenApiOperator.getApolloOpenApiOperator();
        ApolloOpenApiOperator.BaseApolloKey baseApolloKey = new ApolloOpenApiOperator.BaseApolloKey();
        baseApolloKey.setAppId(appId);
        baseApolloKey.setClusterName(clusterName);
        baseApolloKey.setEnv(env);
        baseApolloKey.setNamespaceName(namespaceName);
        List<OpenItemDTO> openItemDTOS = apolloOpenApiOperator.queryApolloKey(baseApolloKey, 0, 10);
        return Resp.Ok(openItemDTOS);
    }

    /**
     * 通过主键查询单条数据
     *
     * @param key apollo-key
     * @return 单条数据
     */
    @GetMapping("/queryOne/{key}")
    public Resp<OpenItemDTO> selectOne(@PathVariable("key") String key,
                                       @RequestParam("appId") String appId,
                                       @RequestParam("clusterName") String clusterName,
                                       @RequestParam("env") String env,
                                       @RequestParam("namespaceName") String namespaceName) throws IOException {
        ApolloOpenApiOperator apolloOpenApiOperator = ApolloOpenApiOperator.getApolloOpenApiOperator();
        ApolloOpenApiOperator.DoIssueApolloKey baseApolloKey = new ApolloOpenApiOperator.DoIssueApolloKey();
        baseApolloKey.setAppId(appId);
        baseApolloKey.setEnv(env);
        baseApolloKey.setNamespaceName(namespaceName);
        baseApolloKey.setClusterName(clusterName);
        baseApolloKey.setKey(key);
        OpenItemDTO openItemDTO = apolloOpenApiOperator.queryApolloKeyOne(baseApolloKey);
        return Resp.Ok(openItemDTO);
    }

    /**
     * 新增数据 到Apollo
     *
     * @param doIssueApolloKey 实体对象
     * @return 新增结果
     */
    @PostMapping("/save")
    public Resp<String> insert(@RequestBody Map<String, String> doIssueApolloKey) throws IOException {
        String json = JSON.toJSONString(doIssueApolloKey);
        ApolloOpenApiOperator.DoIssueApolloKey doIssueApolloData = JSONObject.parseObject(json, ApolloOpenApiOperator.DoIssueApolloKey.class);
        ApolloOpenApiOperator apolloOpenApiOperator = ApolloOpenApiOperator.getApolloOpenApiOperator();
        doIssueApolloData.setModifiedOrInsert(false);
        apolloOpenApiOperator.doIssueApollo(doIssueApolloData);
        return Resp.Ok(doIssueApolloData.getKey());
    }


    /**
     * 修改数据
     *
     * @param doIssueApolloKey 实体对象
     * @return 修改结果
     */
    @PutMapping("/update")
    public Resp<String> update(@RequestBody Map<String, String> doIssueApolloKey) throws IOException {
        String json = JSON.toJSONString(doIssueApolloKey);
        ApolloOpenApiOperator.DoIssueApolloKey doIssueApolloData = JSONObject.parseObject(json, ApolloOpenApiOperator.DoIssueApolloKey.class);
        ApolloOpenApiOperator apolloOpenApiOperator = ApolloOpenApiOperator.getApolloOpenApiOperator();
        doIssueApolloData.setModifiedOrInsert(true);
        apolloOpenApiOperator.doIssueApollo(doIssueApolloData);
        return Resp.Ok(doIssueApolloData.getKey());
    }

    /**
     * Description: 移出Apollo已下发数据
     *
     * @param key
     * @param appId
     * @param clusterName
     * @param env
     * @param namespaceName
     * @author: GuoTong
     * @date: 2023-06-24 15:42:24
     * @return:com.gton.user.io.Resp<java.lang.String>
     */
    @GetMapping("/dels")
    public Resp<String> delete(@RequestParam("key") String key,
                               @RequestParam("appId") String appId,
                               @RequestParam("clusterName") String clusterName,
                               @RequestParam("env") String env,
                               @RequestParam("namespaceName") String namespaceName) throws IOException {
        ApolloOpenApiOperator apolloOpenApiOperator = ApolloOpenApiOperator.getApolloOpenApiOperator();
        ApolloOpenApiOperator.DoIssueApolloKey removeKeys = new ApolloOpenApiOperator.DoIssueApolloKey();
        removeKeys.setAppId(appId);
        removeKeys.setEnv(env);
        removeKeys.setNamespaceName(namespaceName);
        removeKeys.setClusterName(clusterName);
        removeKeys.setKey(key);
        apolloOpenApiOperator.executorRemove(removeKeys);

        return Resp.Ok(removeKeys.getKey());
    }
}


与Apollo2.1.0+Springboot使用OpenApI相似的内容:

Apollo2.1.0+Springboot使用OpenApI

## 依赖管理 ```xml org.springframework.cloud spring-cloud-starter-bootstrap 3.1.3 com.ctrip.framework.apollo apollo-client 2.1.0 com.ctrip.framework.apoll

一文读懂Apollo客户端配置加载流程

SpringBoot集成Apollo源码分析 本文基于 apollo-client 2.1.0 版本源码进行分析 Apollo 是携程开源的配置中心,能够集中化管理应用不同环境、不同集群的配置,配置修改后能够实时推送到应用端,并且具备规范的权限、流程治理等特性。 Apollo支持4个维度管理Key-

Springboot整合Apollo

一、Apollo作用 随着程序功能的日益复杂,程序的配置日益增多:各种功能的开关、参数的配置、服务器的地址…… 对程序配置的期望值也越来越高:配置修改后实时生效,灰度发布,分环境、分集群管理配置,完善的权限、审核机制…… 在这样的大环境下,传统的通过配置文件、数据库等方式已经越来越无法满足开发人员对

2023年最新版Apollo保姆级使用手册(超级详尽版本)

目录Apollo操作说明前言Apollo环境部署一、环境构建二、官方地址三、数据库脚本使用四、配置Apollo文件五、启动Apollo六、访问ApolloApollo产品使用一、修改部门二、应用操作三、用户操作四、系统权限管理1、创建应用权限配置2、创建应用权限配置3、与旧版比对五、系统参数1、Po

如何在 Windows Server 2022 阿里云服务器上搭建自己的 MQTT 服务器之一Apache-Apollo服务器。

一、简介 最近,在做一个项目的时候,需要在线管理网络继电器,以前也做过硬件的项目,但是这样的项目不多。现在我想实现一个在线可以接受网络继电器发送的信号,也可以向网络继电器发送命令,控制其的运行。这个功能的实现需要物联网的支持,我在咨询了网络继电器卖家,想要在线实现网络硬件的管理有三种解决方案。 第一

如何在 Windows Server 2022 阿里云服务器上搭建自己的 MQTT 服务器之二Mosquitto服务器

一、介绍 最近几天都在搭建MQTT服务器,几天前搭建好了一个 Apache-Apollo的 MQTT 服务器,当我们在管理我们的主题的时候,发现主题的名称的斜杠(/)变成了点号(.),正好我在调试程序,在调用的时候出现了一些问题,各种解决办法都想了,还是没有解决,于是就向重新搭建一个 MQTT 服务