微服务配置中心, 这个方案 Go 里用起来不输SpringCloud

news2024/12/28 19:21:22

微服务架构设计模式里有一条讲到,要设计可配置的服务。把服务从单体架构细分成微服务后,所有配置属性都集中存储在一个位置,更易于管理。这个集中存储管理配置的地方叫,就是配置中心。

使用配置中心还有一个好处就是,往往都支持应用配置的热更新,这样就不需要像修改本地配置那样进行发版部署了。

但是这么好的事儿就没有缺点了吗?当然有,除非有基础设施支持,否则它需要额外的人力进行设计和运维。不过好在有各种开源框架比如 Spring Cloud Config,能使服务接入配置中心,没有什么侵入性。至少在表面使用上感觉不到有变化。

那么在 Go 里有没有类似的方案呢?经过我这周的试验探索,还真发现了,这个方案落地也很简单,今天就跟大家简单说说。更详细的还得是大家上手操作起来才能感受到。

有人可能会说远程配置中心,我就把配置放在 ETCD 上,项目启动的时候拉下来不就行了?先别着急,咱先看看隔壁家 Spring 是怎么实现这个事儿,有没有我们可以学习的地方。

Spring 的配置和配置中心

用过 Spring 的同学都接触过,在 Java 的项目里都有一个resources目录,这个目录里一般都会有类似名字叫application.properties 的配置文件。

也有可能配置文件的后缀名是.yaml,那么属性配置的格式就是YAML格式的。

在这些配置文件里设置的属性配置,都可以通过可以通过@Value注解注入到对象的属性上,比如假设我们在配置文件里设定了订单的折扣为95折的配置

order:
  discount: 95

那在代码里,我们就能像下面这样,把属性配置的值绑定到类实例的属性上:

public class CoffeeOrderController {

    @Value("${order.discount}")
    private Integer discount;

  ......
}

后来,因为微服务流行起来了,大家又对自己的服务拆得乐此不疲,所以 Spring 家族里后来又有了 SpringCloud,像我们知道的知名厂商 Alibaba、Netflix 都按它这个标准开源自己内部使用的组件,就有了我们天天看到的各种资料推广文里面的 SpringCloud-Netflix,SpringCloud- Alibaba这些。

SpringCloud- Alibaba在国内因为阿里的关系使用更广泛一些,它里面提供的配置中心方案是一个叫 Nacos 的组件,因为有 SpringCloudConfig 这个标准存在,不管各个厂商的远程配置中心是用什么组件,都需要实现 SpringCloudConfig 里的标准。

最直观的好处就是,比如说我把应用的属性配置放到了远程的 Nacos 上,比如这样:

但是在应用程序我们仍然可以继续使用 @Value注解拿到放在远程配置中心的属性值。如果本地和远程配置中心都有的话,以本地磁盘里的配置优先。

是不是很方便?这就类似应用里使用的是一个门面模式,下层加载使用的组件提供的driver来完成项目配置的载入。

那在 Go 里面有没有类似的方案呢?有,虽然没有 SpringCloud 这个支持的组件那么全,但是支持 ETCD 和 Consul 做配置中心,也够用了。

Go 项目的配置和配置中心

聊到 Go 项目的配置和配置中心,我见过的几十个项目里,是的,前几年待过的两个拿融资多的创业公司里,项目就是多,不停地尝试,不然投资人那不好说啊,咳咳。有的,做做没有效果就放弃了 T—_—T。

说回来,咱们配置的事儿,在这几十个项目里基本上分成两大派,有用 Viper 或者另一个Yaml开源库直接操作本地文件的。还有一派是直接读 ETCD ,拿下来把字节流转到本地配置对象的。

那有没有一种方案能兼容本地配置和远程配置中心两种模式的?

我看了一下 Viper 是支持从远程 ETCD 或者 Consul 取配置的。

但是呢,经过我的试验,发现官网的给的例子有BUG,从 ETCD 上根本读不了配置,更别提热更新了,这点我们先按下不表,我先给大家介绍下 Viper 的基本使用。

主要是我也没从头用过,以前用的项目架子里是别人搭好的,哈哈~,不过你们面试的时候可别这么说大实话,今天看完我的文章,至少配置中心这块的架构选型,我是可以吹吹的,你们呢?

怎么安装 Viper 包什么的,我就不说了,官网上都有,文末会附上官网的链接,下面直接上代码。假如,不是假如,我真在项目配置文件里写了个数据库连接信息的YAML配置。

database:
  type: mysql
  dsn: "user:pass@tcp(localhost:30306)/db_name?charset=utf8&parseTime=True&loc=Local"
  maxopen: 100
  maxidle: 10
  maxlifetime: 300


然后用 Viper 怎么读这个配置呢?这里直接在配置文件目录下用一个 Go 的 init 函数,在函数里把配置用 Viper 反序列化到一个全局变量里,供项目使用。

type databaseConfig struct {// 配置属性跟类型字段不同名是要加下面这个tag
    Type        string        `mapstructure:"type"`
    DSN         string        `mapstructure:"dsn"`
    MaxOpenConn int           `mapstructure:"maxopen""`
    MaxIdleConn int           `mapstructure:"maxidle"`
    MaxLifeTime time.Duration `mapstructure:"maxlifetime"`
}
var Database *databaseConfig

func init() {
    // 获取当前文件的路径
    _, filename, _, _ := runtime.Caller(0)
    // 配置文件目录的路径
    configBaseDir := path.Dir(filename)
    vp := viper.New()
    vp.AddConfigPath(configBaseDir)
    vp.SetConfigType("yaml")
    err := vp.ReadInConfig()
    if err != nil {
        panic(err)
    }
    vp.UnmarshalKey("database", &Database)
    Database.MaxLifeTime *= time.Second
}

除了把配置项反序列化到结构体类型里,还能通过类似 Spring 里的@Value那种方式读单个配置项的值。

vp.Get("database.type")

不过我更倾向于反序列化到结构体这种方式,使用起来更方便,同时热更新配置时这种方式也更方便些。

项目里实例化数据库连接的时候,就可以像这样,用上我们的配置啦。

  db, err := gorm.Open(config.Database.Type, config.Database.DSN)
    if err != nil {
        panic(err)
    }
    db.DB().SetMaxOpenConns(config.Database.MaxOpenConn)
    db.DB().SetMaxIdleConns(config.Database.MaxIdleConn)
    db.DB().SetConnMaxLifetime(config.Database.MaxLifeTime)
    if err = db.DB().Ping(); err != nil {
        panic(err)
    }

下面我们给项目加一个 redis 连接信息的配置

redis:
  address: "localhost:6579"
  password: "DFgsdfhshf"
  dbnumber: 0
  maxactive: 100
  maxidle: 20

把这个配置放到远程的ETCD 配置中心里:

后来我按照官网的例子,搞了一下死活读不到我这个key 对应的配置,在网上查了一下,究其原因,是因为 Viper 依赖 crypt 库,而 crypt 截至目前还不支持新版 ETCD 的 API。

ETCD 的 KV 中可以存储加密的数据,Viper 在获取的时候通过 crypt 自动解密,这个初衷是好的,但是公司里的配置中心基本上都是内网访问,再则加密存储的话,我就不能像上面这样直接在客户端里进行KV编辑了,有什么办法呢?

看网上有技术大佬分析,可以通过重新实现remoteConfigFactory接口

type remoteConfigFactory interface {
    Get(rp RemoteProvider) (io.Reader, error)
    Watch(rp RemoteProvider) (io.Reader, error)
    WatchChannel(rp RemoteProvider) (<-chan *RemoteResponse, chan bool)
}

把加解密的部分去掉。你觉得这个是哪种工厂呢?

使用 Viper 读取远程配置,还需要匿名导入它提供的一个库。

_ "github.com/spf13/viper/remote"

下面演示一下使用 Viper 读取远程配置和热更新配置的代码。

type RedisConfig struct {
    Address   string `mapstructure:"address"`
    Password  string `mapstructure:"password"`
    DbNumber  int    `mapstructure:"dbnumber"`
    MaxActive int    `mapstructure:"maxactive"`
    MaxIdle   int    `mapstructure:"maxidle"`
}
var Redis *RedisConfig

func init() {
    // 初始化 Viper 和上面例子里的一样,这里省略
  ...
  代码里省略一切error处理
  // 读取远程ETCD里的KV
  err := vp.AddRemoteProvider("etcd", "http://127.0.0.1:32379", "root/config/viper-test/config")
    vp.SetConfigType("yaml")
  // 读本地配置
    err = vp.ReadInConfig()
    err = vp.ReadRemoteConfig()
    vp.UnmarshalKey("database", &Database)
    Database.MaxLifeTime *= time.Second
    vp.UnmarshalKey("redis", &Redis)
  // 这里简单输出一下 redis 的配置,就不做其他演示了
  fmt.Printf("Redis Config: %v\n", Redis)
  // 监听KV变更,进行热更新
    go watchRemoteConfig(vp)
}

监听配置变更,进行热更新这块,我暂时实现的简单点,用了下轮询,后面有好的方法了再更新。

func watchRemoteConfig(vp *viper.Viper) {
    for {
        time.Sleep(5 * time.Second)
        err := vp.WatchRemoteConfigOnChannel()
        if err != nil {
            zlog.Error("Read Config Server Error", zap.Error(err))
            return
        }
        // 监控远程配置的变更
        vp.UnmarshalKey("redis", &Redis)
    fmt.Printf("Redis Config: %v\n", Redis)
    }
}

这里演示的配置热更新我就是简单向控制台输出了一下 Redis 的配置,启动后我试了一下,在ETCD里把配置修改后能直接在项目里变更过来,下面是我把Redis配置的端口从 6579 改成 6580 的一个演示。

总结

今天给大家讲了微服务配置中心的实现方案,先介绍了下 SpringCloudConfig 标准下的使用方案,因为Spring生态比较完整,对这方面支持的比较好,像ETCD、Consul甚至Git什么的都支持拿来做配置中心。

Go 里边的 Viper 库也很强大,只是用 Etcd 当配置中心的时候需要我们自己做些扩展,虽然没有那么开箱即用,但是研究问题动手解决的过程还是很有意思的。

对了,Viper 支持同时使用本地和远程配置,本地配置优先级高于远程,大家不要弄混了。

这里我再给个建议像是服务器启动参数 server.portapplication.name这类几乎是项目创建完后就不会再改的配置,放在本地配置文件就好。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/194517.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

java基础面试题 一

一、面向对象五大基本原则是什么 1.单一职责原则SRP(Single Responsibility Principle) 类的功能要单一&#xff0c;不能包罗万象&#xff0c;跟杂货铺似的。 2.开放封闭原则OCP(Open&#xff0d;Close Principle) 一个模块对于扩展是开放的&#xff0c;对于修改是封闭的 …

Jenkins基于docker cloud动态增减节点

jenkins可以按照jenkins容器启动去部署 Jenkins管理动态节点 动态节点可以在有job运行时&#xff0c;临时加入一个agent到jenkins master&#xff0c;然后等job执行完毕之后&#xff0c;所加入的agent再自动删除掉&#xff0c;达到一个动态的增删节点效果&#xff0c;使所有的…

引用第三方插件到分包中即如何把uni_modules文件夹中的插件放入分包中

网上搜索了很多&#xff0c;但都没有直接说明如何把node_modules中的第三方插件如何引入到分包中&#xff0c;首先为什么要在分包引入&#xff0c;原因就是小程序有包大小的限制&#xff0c;不能超过2M&#xff0c;超过2M的话&#xff0c;则不能发布或预览&#xff0c;于是不能…

SCT81620QSTER,升降压电源芯片

P2P替代 LM3478和LM3481SCT81620QSTER设备是一个宽输入、非同步升压控制器。该设备可用于增强、间隔和反馈转换器和拓扑结构。SCT81620Q设备的开关频率可以通过将SCT81620Q设备的开关频率调整到100 kHz到2.2MHz之间的任何值。电流模式控制提供了优越的带宽和瞬态响应&#xff0…

IDEA设置注释模板(详细版)

IDEA设置注释模板 类注释模板方法注释模板效果展示 1. 类注释模板 类注释模板是IDEA创建类时生成的注释 第一步、File -> Settings 第二步、Editor -> File and Code Templates -> Includes -> File Header 点apply&#xff0c;再点ok即可 模板参考如下&#…

拉格朗日插值原理及其Julia实现

文章目录数学原理算法化测试设函数yf(x)yf(x)yf(x)在区间[a,b][a,b][a,b]上有定义&#xff0c;且在点a⩽x0⩽x1…⩽xn⩽ba\leqslant x_0\leqslant x_1\ldots\leqslant x_n\leqslant ba⩽x0​⩽x1​…⩽xn​⩽b上的值y0,y1…yny_0, y_1 \ldots y_ny0​,y1​…yn​之间存在一个函…

规则引擎-drools-3.2-drl文件构成-rule部分-属性Attribute

文章目录drl文件构成-rule部分rule示例rule nameAttribute全部属性说明no-loop 和 lock-on-activeactivation-group 和 agenda-groupdrl文件构成-rule部分 drl文件构成&#xff0c;位于官网的第5章位置&#xff0c;也是drools作为规则引擎应用的最核心部分。 其中rule模块&…

谷歌浏览器无法翻译此网页,解决方法?(谷歌浏览器无法翻译成中文,谷歌翻译,最新方法)

谷歌浏览器自带的翻译功能,对我们来说用处很大,但有的时候突然就会变成“无法翻译此网页”,针对此问题这里提供几种解决方案(翻译插件),如下: 方法1: 蓝奏云文件https://wwot.lanzouw.com/iFc7d0hmrtpg 访问密码:slee 方法2: 脚本之家

分布式事务的4种模式

分布式事务理论基础 解决分布式事务&#xff0c;也有相应的规范和协议。分布式事务相关的协议有2PC、3PC。 由于三阶段提交协议3PC非常难实现&#xff0c;目前市面主流的分布式事务解决方案都是2PC协议。 有些文章分析2PC时&#xff0c;几乎都会用TCC两阶段的例子&#xff0…

Java开发基础入门之Java基础中的Stack类及其常用方法

一、Stack类 1.Stack是Vector的一个子类&#xff0c;它实现标准的后进先出堆栈。Stack只定义了创建空堆栈的默认构造方法。 Stack() 2.Stack类里面主要实现的有以下的几个方法&#xff1a; (1)boolean empty( )方法是判断堆栈是否为空。 (2)Object peek( )方法是返回栈顶端…

一夜爆火的现象级产品ChatGPT,是AI突破还是昙花乍现?

导语 | 编写代码、翻译小说、参加考试……2022 年末&#xff0c;人工智能聊天机器人 ChatGPT 风靡全网。自 2016 年 AlphaGo 击败围棋世界冠军李世石后&#xff0c;ChatGPT 再次掀起了人工智能发展应用的高潮。它将会给我们带来哪些影响&#xff1f;人工智能的颠覆性应用是否即…

MyBatis 二级缓存整合Redis【学习记录】

二级缓存整合Redis 上篇文章介绍了MyBatis自带的二级缓存&#xff0c;但是这个缓存是单服务器工作&#xff0c;无法实现分布式缓存。那么什么是分布式缓存呢&#xff1f;假设现在有两个服务器1和2&#xff0c;用户访问的时候访问了服务器1&#xff0c;查询后的缓存就会放在服务…

酒店预订订单的分析与建模【决策树、xgboost】

酒店预订订单的分析与建模【决策树、xgboost】 本项目包含 1.数据处理 2.数据探索性分析 3.网格搜索对决策树、xgboost进行模型参数调优 4.基于五折交叉验证的决策树、xgboost模型预测 专栏和往期项目 &#x1f449;往期文章可以关注我的专栏 下巴同学的数据加油小站 会不…

《神经网络与深度学习》 邱希鹏 学习笔记(二)

正则化 所有损害优化的方法都是正则化。增加优化约束&#xff0c;干扰优化过程 优化约束包括 L1/L2约束&#xff0c;数据增强 干扰优化包括 随机梯度下降 权重衰减 提前停止 在上式中 y(n)为样本n&#xff0c;其展开形式为y^{(n)}为样本n&#xff0c;其展开形式为y(n)为样本…

【Axure教程】低代码可视化编辑器

低代码是一组数字技术工具平台&#xff0c;基于图形化拖拽、参数化配置等更为高效的方式&#xff0c;通过少量代码或不用代码实现数字化转型中的场景应用创新。例如在业务系统中&#xff0c;如果企业新增了一项业务&#xff0c;以往往往需要对系统继续开发和升级&#xff0c;但…

【Python学习笔记】9. Python3 解释器

前言 Linux/Unix的系统上&#xff0c;一般默认的 python 版本为 2.x&#xff0c;我们可以将 python3.x 安装在 /usr/local/python3 目录中。 Python3 解释器 Linux/Unix的系统上&#xff0c;一般默认的 python 版本为 2.x&#xff0c;我们可以将 python3.x 安装在 /usr/loca…

IDEA 常用插件跟配置提升开发效率

工欲善其事必先利其器 安装好 IntelliJ IDEA 后&#xff0c;进行如下的初始化操作&#xff0c;工作效率提升50倍。 一、插件 1. Codota 代码智能提示插件 只要打出首字母就能联想出一整条语句&#xff0c;这也太智能了&#xff0c;还显示了每条语句使用频率。原因是它学习了…

最全的视频转换器工具清单,这18款免费视频格式转换器记得收藏

审查和比较具有功能和定价的最佳视频转换器软件。从这个顶级付费和免费在线视频转换器工具列表中选择&#xff0c;以快速轻松地转换任何视频&#xff1a; 什么是视频转换器&#xff1f; 视频转换工具允许您将视频从一种格式转换为另一种格式。第一个商业上成功的视频格式是 Q…

11.1-股票基金历年收益率计算

文章目录1. 计算目标2. 关键问题3. 获取交易日历4. 逻辑编写1. 计算目标 我们想知道&#xff0c;一只股票标的&#xff0c;在之前的几年中&#xff0c;每一年的年化收益率是多少&#xff1f; 如果将每年的年化收益率进行求和汇总&#xff0c;截止到今年&#xff0c;总共年化收…

五、Mybatis详细教程

Mybatis概述 1 Mybatis概念 MyBatis 是一款优秀的持久层框架&#xff0c;用于简化 JDBC 开发 MyBatis 本是 Apache 的一个开源项目iBatis, 2010年这个项目由apache software foundation 迁移到了google code&#xff0c;并且改名为MyBatis 。2013年11月迁移到Github 官网&am…