微服务架构设计模式之一谈到了设计可配置服务。将服务从单体架构细分为微服务后,所有配置属性都存储在一个地方,更易于管理。这个集中存储管理配置的地方就是配置中心。使用配置中心还有一个好处就是经常支持应用配置的热更新,这样就不需要像修改本地配置那样进行发布部署。可是这么好的东西就没有坏处吗?当然有,除非有基础设施支撑,否则就需要额外的人力来设计和运维。好在有SpringCloudConfig等各种开源框架,可以让服务无侵入地访问配置中心。至少表面上,没有任何变化。那么Go中有没有类似的解决方案呢?经过我这周的实验和摸索,真的发现这个方案的实现也很简单,所以今天就和大家简单说一下。更详细的,只有上手才能感受。该解决方案涉及的代码过多。可以给我公众号:网络管理员,发消息【go-config】获取项目下载地址。分享软件开发和系统架构设计的基础知识,Go语言和Kubernetes。可能有人会说,在远程配置中心,我把配置放在ETCD上,项目启动的时候拉下来?别着急,我们来看看隔壁的Spring是如何实现的,看看有没有可以借鉴的地方。Spring配置与配置中心用过Spring的同学都接触过。Java项目中有一个resources目录。一般在该目录下有一个名为application.properties的配置文件。配置文件也可能有.yaml的后缀,所以属性配置的格式为YAML格式。这些配置文件中设置的属性配置可以通过@Value注解注入到对象的属性中。例如,假设我们在配置文件中设置配置order:discount:95,其中订单折扣为95折。在代码中,我们可以将属性配置的值绑定到类实例的属性上,如下所示:…}后来因为微服务流行起来,大家又开始喜欢拆自己的服务了,所以后来SpringCloud也进入了Spring大家庭。众所周知,阿里巴巴、Netflix等知名厂商都按照这个标准开源了自己的内部组件,我们每天看到的各种宣传文章里都有SpringCloud-Netflix,SpringCloud-Alibaba。SpringCloud-Alibaba因为阿里的关系,在国内使用比较广泛。其中提供的配置中心解决方案是一个名为Nacos的组件。因为有SpringCloudConfig标准的存在,所以各个厂商的远程配置中心不管用到什么组件,都需要实现。SpringCloudConfig中的标准配置。最直观的好处就是,比如我把应用的属性配置放到了远程Nacos上,比如这样:远程配置中心Nacos但是在应用中,我们还是可以通过@Value注解来获取放置的属性值在远程配置中心。如果同时存在本地配置中心和远程配置中心,则本地磁盘中的配置优先。方便吗?这类似于在应用程序中使用门面模式,由下层加载组件提供的驱动来完成项目配置的加载。Go中有类似的解决方案吗?是的,虽然没有SpringCloud那么多组件,但是支持ETCD和Consul作为配置中心就足够了。Go项目配置与配置中心说起Go项目配置与配置中心,在我看到的几十个项目中,没错,这几年融资很多的两家初创公司里面就有很多项目.不断尝试各种方向的业务,否则投资者很难,咳咳。是的,不行我就放弃了T-_-T。说起来,我们配置的东西在这几十个项目中基本上分为两组,使用Viper或者其他Yaml开源库直接操作本地文件。另一个学派是直接读取ETCD,拿下来把字节流传给本地的配置对象。有没有兼容本地配置和远程配置中心两种模式的解决方案?我检查过Viper是否支持从远程ETCD或Consul获取配置。但是经过我的实验,发现官网给的例子有BUG,根本无法从ETCD读取配置,更谈不上热更新了。先按这个点,先给大家介绍下Viper的基本使用。.主要原因是我没有从头开始使用它。我之前用的项目架是别人建的,哈哈~,不过面试的时候别说实话。看了我今天的文章,至少配置了centerModelselection的结构,我可以夸一下,你呢?我不会谈论如何安装Viper包。都可以在官方网站上找到。文末会附上官网链接,下面直接贴代码。如果,不是如果,我真的在项目配置文件里写了一个数据库连接信息的YAML配置。database:type:mysqldsn:"user:pass@tcp(localhost:30306)/db_name?charset=utf8&parseTime=True&loc=Local"maxopen:100maxidle:10maxlifetime:300那怎么用Viper读取这个配置呢?这里直接在配置文件目录下使用一个Goinit函数,在函数中用Viper将配置反序列化成一个全局变量供项目使用。typedatabaseConfigstruct{//配置属性的名称与type字段不同,所以要加上下面的标签Typestring`mapstructure:"type"`DSNstring`mapstructure:"dsn"`MaxOpenConnint`mapstructure:"maxopen""`MaxIdleConnint`mapstructure:"maxidle"`MaxLifeTimetime.Duration`mapstructure:"maxlifetime"`}varDatabase*databaseConfigfuncinit(){//获取当前文件的路径_,filename,_,_:=runtime.Caller(0)//配置文件目录的路径configBaseDir:=path.Dir(filename)vp:=viper.New()vp.AddConfigPath(configBaseDir)vp.SetConfigType("yaml")err:=vp.ReadInConfig()iferr!=nil{panic(err)}vp.UnmarshalKey("database",&Database)Database.MaxLifeTime*=time.Second}除了将配置项反序列化为结构体类型,还可以读取以一种类似于Spring中@Value的方式单个配置项的值vp.Get("database.type")不过,我更喜欢反序列化到一个stru的方法结构,使用更方便,热更新配置也更方便。Project在实例化数据库连接时,我们可以这样使用我们的配置。//具体完整的例子代码太多了//麻烦给我公众号:网管,发消息[go-config]接收。db,err:=gorm.Open(config.Database.Type,config.Database.DSN)iferr!=nil{panic(err)}db.DB().SetMaxOpenConns(config.Database.MaxOpenConn)db.DB().SetMaxIdleConns(config.Database.MaxIdleConn)db.DB().SetConnMaxLifetime(config.Database.MaxLifeTime)如果错误=db.DB().Ping();err!=nil{panic(err)}接下来我们进行例如Viper使用远程配置中心。这里给大家介绍一下我的ETCD集群K8s搭建教程:使用Kubernetes搭建一个Etcd集群和WebUI,不然如果你本地没有ETCD,也不好实践,除非……你下载你们公司测试的ETCD环境,呵呵,保命要紧,自己电脑上搭建吧。另外安利一下我的K8s教程。上面用的Nacos也是我用K8s搭建的。它包含在教程中。在公众号网管回复k8s即可获取教程,绝对实用。接下来我们在项目redis中添加一个redis连接信息的配置:地址:"localhost:6579"密码:"DFgsdfhshf"dbnumber:0maxactive:100maxidle:20把这个配置放到远程ETCD配置中心:配置在ETCD后面,按照官网的例子,我读取不到我的key对应的配置。我在网上查过。原因是Viper依赖crypt库,而crypt目前还不支持新版ETCD的API。ETCD的KV可以存储加密数据,Viper在获取到数据后会自动通过crypt进行解密。这个初衷是好的,但是公司里面的配置中心基本都是从内网访问的。以上方式,直接在客户端进行KV编辑。我能做些什么?根据网上一些技术大佬的分析,可以重新实现remoteConfigFactory接口;typeremoteConfigFactoryinterface{Get(rpRemoteProvider)(io.Reader,error)Watch(rpRemoteProvider)(io.Reader,error)WatchChannel(rpRemoteProvider)(<-chan*RemoteResponse,chanbool)}我不会发布这个接口的具体实现,太多了,大家可以下载工程自己看,如何获取下载链接,发到我的公众号「网络管理」留言【go-config】获取工程下载链接。要使用Viper读取远程配置,还需要匿名导入它提供的一个库。_"github.com/spf13/viper/remote"下面演示使用Viper读取远程配置和热更新配置的代码。typeRedisConfigstruct{地址字符串`mapstructure:"address"`密码字符串`mapstructure:"password"`DbNumberint`mapstructure:"dbnumber"`MaxActiveint`mapstructure:"maxactive"`MaxIdleint`mapstructure:"maxidle"`}varRedis*RedisConfigfuncinit(){//按照上面的例子初始化Viper,此处省略...省略代码中的所有错误处理//告诉Viper在远程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.Secondvp.UnmarshalKey("redis",&Redis)//这里是简单的redis配置输出,没有其他演示v\n",Redis)//监听KV变化并进行热更新gowatchRemoteConfig(vp)}监听配置变化并进行热更新。我暂时实现了一个简单的点。我用的是nextpolling,以后有更好的方法再更新。funcwatchRemoteConfig(vp*viper.Viper){for{time.Sleep(5*time.Second)err:=vp.WatchRemoteConfigOnChannel()iferr!=nil{zlog.Error("读取配置服务器错误",zap.Error(err))return}//监听远程配置变化vp.UnmarshalKey("redis",&Redis)fmt.Printf("RedisConfig:%v\n",Redis)}}这里演示的配置热更新很简单输出Redis的配置到控制台。开始后,我尝试了一下。在ETCD中修改配置后,我可以直接在项目中更改。下面是将Redis配置的端口从6579修改为6580的演示。配置动态更新总结今天给大家讲讲微服务配置中心的实现方案。首先介绍下SpringCloudConfig标准下的使用方案,因为Spring生态比较完善,对这方面的支持比较好,比如ETCD、Consul甚至Git。两者都支持它作为配置中心。Go中的Viper库也很强大,但是我们在使用Etcd作为配置中心时需要做一些扩展。虽然不是那么开箱即用,但是亲手研究和解决问题的过程还是很有趣的。顺便说一下,Viper支持同时使用本地和远程配置。本地配置的优先级高于远程,所以不要搞混了。这里我再给出一个建议,比如服务器启动参数server.port、application.name等配置在项目创建后不会改变,放在本地配置文件中即可。
