前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >spring/springboot的整合分布式配置中心(ACM diamond nacos Apollo)

spring/springboot的整合分布式配置中心(ACM diamond nacos Apollo)

作者头像
逍遥壮士
发布2020-11-24 10:28:57
1.2K0
发布2020-11-24 10:28:57
举报
文章被收录于专栏:技术趋势技术趋势

注:本文篇幅有点长,所以建议各位下载源码学习。(如需要请收藏!转载请声明来源,谢谢!)

代码下载:https://gitee.com/hong99/spring/issues/I1N1DF

分布式配置中心是什么?

配置中心指的是将传统的.propertis或.yaml以及项目中些临时需要修改的数据,通过一个配置中心来统一规范管理。

分布式配置中心解决了什么问题?

实时更新:线上某些业务需要立即启动,只需要修改下配置几秒钟就更新了;

高可用:有一套行之有效的保障方案;

配置与应用分离:解决了需要修改配置居然还要重启项目....;

统一管理统一标准:规范了各种DIY问题;

个人理解:比如你线上有500台服务器,现在要立刻将某个业务暂时或启动,要是传统要重新修改配置文件->再发版,这时候时间已过...但是有了配置中心,只需要修改一下配置,几秒钟后全部生效,马上开始进行。

有哪些分布式配置中心?

阿里的ACM(收费)

应用配置管理ACM(Application Configuration Management)是一款在分布式架构环境中对应用配置进行集中管理和推送的产品。凭借配置变更、配置推送、历史版本管理、灰度发布、配置变更审计等配置管理工具,ACM能帮助您集中管理所有应用环境中的配置,降低分布式系统中管理配置的成本,并降低因错误的配置变更造成可用性下降甚至发生故障的风险。

https://help.aliyun.com/learn/learningpath/acm.html?spm=5176.163362.847321.learning.3d7c2539omBLES

Diamond(淘宝钻石)

diamond是淘宝内部使用的一个管理持久配置的系统,它的特点是简单、可靠、易用,目前淘宝内部绝大多数系统的配置,由diamond来进行统一管理。

https://github.com/takeseem/diamond(已经不维护)

https://github.com/gzllol/diamond

Apollo(阿波罗):

Apollo(阿波罗)是携程框架部门研发的分布式配置中心,能够集中化管理应用不同环境、不同集群的配置,配置修改后能够实时推送到应用端,并且具备规范的权限、流程治理等特性,适用于微服务配置管理场景。

https://www.bookstack.cn/read/ctripcorp-apollo/66fd39d228fadcad.md

https://github.com/ctripcorp/apollo

Nacos(阿里巴巴)

Nacos 致力于帮助您发现、配置和管理微服务。Nacos 提供了一组简单易用的特性集,帮助您快速实现动态服务发现、服务配置、服务元数据及流量管理。

https://nacos.io/zh-cn/docs/quick-start.html

各个分布式配置中心对比:

https://juejin.im/entry/6844903846041387016

https://cloud.tencent.com/developer/article/1347918

http://blog.zollty.com/b/archive/config-center-selection.html

代码下载:https://gitee.com/hong99/spring/issues/I1N1DF

代码实现:

diamond使用:

先拉下代码:

https://github.com/gzllol/diamond

创建数据库权限和表

代码语言:javascript
复制
create database diamond; 
grant all on diamond.* to CK@'%' identified by 'abc'; 
  
use diamond 
-- 配置表
CREATE TABLE IF NOT EXISTS `config_info` (
  `id` bigint(64) unsigned NOT NULL AUTO_INCREMENT,
  `data_id` varchar(255) NOT NULL DEFAULT '',
  `group_id` varchar(128) NOT NULL DEFAULT '',
  `content` longtext NOT NULL,
  `md5` varchar(32) NOT NULL DEFAULT '',
  `src_ip` varchar(20) DEFAULT NULL,
  `src_user` varchar(20) DEFAULT NULL,
  `gmt_create` datetime NOT NULL DEFAULT now(),
  `gmt_modified` datetime NOT NULL DEFAULT now(),
  PRIMARY KEY (`id`),
  UNIQUE KEY `uk_config_datagroup` (`data_id`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;


-- 组表
CREATE TABLE IF NOT EXISTS `group_info` (
  `id` bigint(64) unsigned NOT NULL AUTO_INCREMENT,
  `address` varchar(70) NOT NULL DEFAULT '',
  `data_id` varchar(255) NOT NULL DEFAULT '',
  `group_id` varchar(128) NOT NULL DEFAULT '',
  `src_ip` varchar(20) DEFAULT NULL,
  `src_user` varchar(20) DEFAULT NULL,
  `gmt_create` datetime NOT NULL DEFAULT now(),
  `gmt_modified` datetime NOT NULL DEFAULT now(),
  PRIMARY KEY (`id`),
  UNIQUE KEY `uk_group_address` (`address`,`data_id`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;

登录diamond配置

添加配置

代码语言:javascript
复制
config: 123456

结果:发现这个diamond还是挺简单的。

接下来进行spring整合diamond。(发现idamond整合资料极少...)

注:先启动diamond-server

项目结构

diamond.properties

代码语言:javascript
复制
diamond.port=8090 
diamond.config.ip=127.0.0.1 
diamond.dataId=hong

com.hong.spring.config.diamond.ApplicationConfigurer

代码语言:javascript
复制
package com.hong.spring.config.diamond;
import com.taobao.diamond.manager.DiamondManager;
import com.taobao.diamond.manager.ManagerListener;
import com.taobao.diamond.manager.impl.DefaultDiamondManager;
import org.apache.commons.configuration.ConfigurationRuntimeException;
import org.apache.commons.configuration.PropertiesConfiguration;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.support.PropertySourcesPlaceholderConfigurer;
import org.springframework.util.StringUtils;

import javax.naming.ConfigurationException;
import java.io.*;
import java.net.URL;
import java.util.*;
import java.util.concurrent.Executor;

/**
 *
 * 功能描述: 动态拉取diamond!
 *
 * @param:
 * @return:
 * @auther: csh
 * @date: 2020/11/9 11:34
 */
@Configuration
public class ApplicationConfigurer {

    private static final Logger logger = LoggerFactory.getLogger(ApplicationConfigurer.class);

    //diamond系统配置的dataId
    String dataId="hong";

    @Bean
    public PropertySourcesPlaceholderConfigurer createPropertySourcesPlaceholderConfigurer() throws ConfigurationException, IOException {
        PropertySourcesPlaceholderConfigurer propertyPlaceholderConfigurer = new PropertySourcesPlaceholderConfigurer();

        //加载diamond配置信息
        loadDiamondConfig();

        if (!StringUtils.isEmpty(dataId)) {
            String[] dataIds = dataId.split(",");
            for (int i = 0; i < dataIds.length; i++) {
                //diamond客户端调用服务端获取配置信息
                DiamondManager manager = new DefaultDiamondManager(dataIds[i], new ManagerListener() {
                    @Override
                    public Executor getExecutor() {
                        return null;
                    }

                    @Override
                    public void receiveConfigInfo(String configInfo) {
                        //配置信息动态修改将触发此方法
                        System.out.println("receive config: " + configInfo);
                        propertyPlaceholderConfigurer.setProperties(getProperties(new StringReader(configInfo)));
                    }
                });
                /**
                 * 同步获取一份有效的配置信息,按照本地文件->diamond服务器->上一次正确配置的snapshot
                 * 的优先顺序获取, 如果这些途径都无效,则返回null
                 *
                 * @param timeout
                 * 从网络获取配置信息的超时,单位毫秒
                 * @return 配置信息
                 */
                String configInfo = manager.getAvailableConfigureInfomation(1000);
                System.out.println("获取的内容:"+configInfo);
                //关闭
                //manager.close();
                //解析Propertie配置信息
                propertyPlaceholderConfigurer.setProperties(getProperties(new StringReader(configInfo)));
            }
        }
        return propertyPlaceholderConfigurer;
    }


    /**
     *
     * 功能描述: 获取所有属性
     *
     * @param:
     * @return:
     * @auther: csh
     * @date: 2020/11/9 11:34
     */
    private Properties getProperties(StringReader in) {
        Map<String, String> map = getPropertisMap(in);
        Properties properties = new Properties();
        for (String key : map.keySet()) {
            properties.setProperty(key, map.get(key));
        }
        return properties;
    }

   /**
    *
    * 功能描述: 解析文件内容
    *
    * @param:
    * @return:
    * @auther: csh
    * @date: 2020/11/9 11:35
    */
    private Map getPropertisMap(StringReader in) {
        Map <String, String> map = new HashMap<String, String>();
        PropertiesConfiguration.PropertiesReader reader = new PropertiesConfiguration.PropertiesReader(in);
        try {
            while (reader.nextProperty()) {
                String key = reader.getPropertyName();
                String value = reader.getPropertyValue();
                map.put(key, value);
            }
        } catch (IOException ioex) {
            throw new ConfigurationRuntimeException(ioex);
        } finally {
            try {
                reader.close();
            } catch (IOException e) {
                logger.error("load config exception", e);
            } finally {
                return map;
            }
        }
    }

  /**
   *
   * 功能描述: 加载配置信息
   *
   * @param:
   * @return:
   * @auther: csh
   * @date: 2020/11/9 11:35
   */
    private void loadDiamondConfig() {
        URL url = this.getClass().getClassLoader().getResource("diamond.properties");
        if (url != null) {
            File file = new File(url.getFile());
            Properties prop = new Properties();
            try {
                prop.load(new FileInputStream(file));
                dataId = prop.getProperty("diamond.dataId");
            } catch (IOException e) {
            }
        }
    }
}

修改diamond-client中的loadConfig方法:com.taobao.diamond.client.DiamondConfigure#loadConfig

注意这里的端口配成跟server一样的

代码语言:javascript
复制
URL url = this.getClass().getClassLoader().getResource("diamond.properties");
if (url != null) {
    File file = new File(url.getFile());
    Properties prop = new Properties();
    try {
        prop.load(new FileInputStream(file));
        configServerAddress = prop.getProperty(Constants.CONF_KEY_CONFIG_IP, Constants.DEFAULT_DOMAINNAME);
        String portStr = prop.getProperty(Constants.CONF_KEY_PORT, String.valueOf(port));
        try {
            port = Integer.parseInt(portStr);
        } catch (NumberFormatException nfe) {
            port = 8090;
        }
    } catch (IOException e) {
    }
}

结果:

代码语言:javascript
复制
receive config: 777777777777777777777
receive config: 888888888888888888

https://blog.hufeifei.cn/2020/04/15/Alibaba/Diamond/

https://github.com/takeseem/diamond

https://my.oschina.net/piorcn/blog/340407

diamond总结:该组件算是国内第一个开源分布式配置中心,虽然说挺好用,但是文档极少并且在淘宝已不维护,所以不建议使用,维护成本挺高的!


Apollo(阿波罗)使用:

apollo-configservice:提供配置获取接口,提供配置更新推送接口,接口服务对象为Apollo客户端

apollo-adminservice:提供配置管理接口,提供配置修改、发布等接口,接口服务对象为Portal,以及Eureka

apollo-portal:提供Web界面供用户管理配置

apollo-client:Apollo提供的客户端程序,为应用提供配置获取、实时更新等功能

Quick-Start启动

参考:https://github.com/ctripcorp/apollo/wiki/Quick-Start

先下载网盘链接下载,提取码: 9wwe

下载到本地后,在本地解压apollo-quick-start.zip

创建数据库:执行以下两份

登陆账号密码:apollo/admin

到这里可以发现对比diamond这个apollo所支持的功能比diamond多得多,但是复杂程度也是多好几个量级,如果非中大型项目不太建议,的确非常复杂,我们继续...

相关配置说明:

app.id:在配置中心配置的应用身份信息。

apollo.bootstrap.enabled:在应用启动阶段是否向Spring容器注入被托管的properties文件配置信息。

apollo.bootstrap.eagerLoad.enabled:将Apollo配置加载提到初始化日志系统之前。

apollo.bootstrap.namespaces:配置的命名空间,多个逗号分隔,一个namespace相当于一个配置文件。

**apollo.meta:**当前环境服务配置地址,生产环境建议至少双节点,可以填写多个逗号分隔,使用一个单独的域,如 http://config.xxx.com(由nginx等软件负载平衡器支持),而不是多个IP地址,因为服务器可能会扩展或缩小。

新建项目

新增属性

运行测试客户端:com.ctrip.framework.apollo.demo.api.SimpleApolloConfigDemo

运行配置如下(不配会找不到服务):

代码语言:javascript
复制
-Dapollo.configService=http://localhost:8080

结果:

动态加载,修改为999:

参考文档:

https://github.com/ctripcorp/apollo/wiki/Apollo%E4%BD%BF%E7%94%A8%E6%8C%87%E5%8D%97

spring整合Apollo(阿波罗)

结果

代码语言:javascript
复制
18:40:41.371 [http-nio-8081-exec-4] INFO com.hong.spring.controller.IndexController - 获取配置信息{"val":"999"}

修改为:123

结果

代码语言:javascript
复制
10:08:01.675 [http-nio-8081-exec-1] INFO com.hong.spring.controller.IndexController - 获取配置信息{"val":"123"}

单个节点不算什么,再试试再开一个端口:8082

请求结果

10:11:54.076 [http-nio-8082-exec-6] INFO com.hong.spring.controller.IndexController - 获取配置信息{"val":"123"}

再试试修改为:123456789111

特别注意,每次修改后要发布:

结果

关于spring mvc整合完毕~

springboot整合Apollo(阿波罗)

pom.xml

代码语言:javascript
复制
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>com.hong.springboot</groupId>
        <artifactId>springboot_all</artifactId>
        <version>0.0.1-SNAPSHOT</version>
        <relativePath/>
    </parent>
    <groupId>com.hong.springboot</groupId>
    <artifactId>springboot_apollo_client</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>springboot_apollo_client</name>
    <description>springboot??apollo</description>

    <dependencies>
        <dependency>
            <groupId>com.ctrip.framework.apollo</groupId>
            <artifactId>apollo-client</artifactId>
            <version>1.1.0</version>
        </dependency>

    </dependencies>

</project>

META-INF/app.properties

代码语言:javascript
复制
#id
app.id=hong
#环境 dev(开发)FAT()
env=DEV
#运行配置
#-Dapollo.meta=http://localhost:8080
apollo.meta=http://localhost:8080
apollo.bootstrap.enabled=true
#spring.application.name = reservation-service
#server.port = 8080
#logging.level = ERROR
#eureka.client.serviceUrl.defaultZone = http://127.0.0.1:8761/eureka/
#eureka.client.healthcheck.enabled = true
#eureka.client.registerWithEureka = true
#eureka.client.fetchRegistry = true
#eureka.client.eurekaServiceUrlPollIntervalSeconds = 60
#eureka.instance.preferIpAddress = true

application.properties

代码语言:javascript
复制
# will inject 'application' namespace in bootstrap phase
apollo.bootstrap.enabled = true
# put apollo initialization before logging system initialization
apollo.bootstrap.eagerLoad.enabled=true
#端口
server.port=8083

Application

代码语言:javascript
复制
/**
 * @author: csh
 * @Date: 2020/11/21 11:37
 * @Description:
 */
@SpringBootApplication(scanBasePackages = "com.hong.springboot")
public class Application {
    public static void main(String[] args) {
        SpringApplication.run(Application.class);
    }
}

注:其他配置与以上一致!建议下载源码学习!

结果

再次修改为:111111

再次请求

springboot真的快了倍,并且简洁!

由于考虑篇幅长度,其它配置中心放到下篇文章统一输出!感谢阅读,如有疑问请私聊或下方留言,谢谢!

参考文章:

https://developer.aliyun.com/article/468274

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2020-11-21,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 技术趋势 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 注:本文篇幅有点长,所以建议各位下载源码学习。(如需要请收藏!转载请声明来源,谢谢!)
相关产品与服务
微服务引擎 TSE
微服务引擎(Tencent Cloud Service Engine)提供开箱即用的云上全场景微服务解决方案。支持开源增强的云原生注册配置中心(Zookeeper、Nacos 和 Apollo),北极星网格(腾讯自研并开源的 PolarisMesh)、云原生 API 网关(Kong)以及微服务应用托管的弹性微服务平台。微服务引擎完全兼容开源版本的使用方式,在功能、可用性和可运维性等多个方面进行增强。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档