曾经写过两篇简单的关于多环境的配置方式,但是直到23年,从一个朋友的代码中获得一些灵感,促成了我想写下这过程中的一些思考
一、前提
以下思考也是基于我目前的状态,即大多数时候,应用的并发要求并不高,技术团队就2-3人,甚至后端有时候只有我1人,所以很多时候我基本上不使用云原生相关的技术,甚至连docker也不使用。docker解决的问题,我用AMI/镜像就可以解决,根据不同的中间件需求,配几个不同的AMI即可。docker还牵涉到多次网络上传,即本地到hub,服务器从hub获取的网络传输,在复杂和不稳定的国外网络环境下,我甚至还要考虑缩小上传包的大小。
再说单机和微服务的选择,在单机能完全承担流量和性能的要求时,我决不推荐优先选微服务,无论微服务的资源要求,还是分布式事务的难点(很多时候小团队甚至不解决分布式事务的问题),以及还牵涉到分布式事务的业务代码改造和引入更多复杂中间件的问题上,微服务都不是一个解决所有问题的银弹。更多时候,我会先对源码预留好方便微服务拆分的包结构,这样即使后边需要进行微服务拆分,也只需要把整个包迁移到相应微服务的项目里即可。
二、几个痛点
问题1 多环境需要打多次包
你肯定大概率有碰到过自己或同事因为遗漏修改某个生产环境参数的配置,导致上线出错的问题,所以越多环境需要指明环境,越容易出错。大部分时候,大部分人,开发已经消耗了他们的大部分精力和关注力,他们不会关心一个配置的上线,也不会提醒说注意某个配置,只会在失败时提出原因和解决方案。而且在沟通成本比较大的场景下,减少错误的出现,才是个人认为比较经济的解决方案。
问题2 交接时的配置遗漏或完全缺失
因为经常需要交接,源码中的开发和测试配置,常有与生产配置项不全或遗漏的情况。我甚至碰到过,有些人application.yml只配置了nacos的信息,交接时,他没有备份nacos的配置,导致全部配置项丢失的情况,碰到这种有时候真的很抓狂。
问题3 ……
想到再补充
三、理想中的
发布时最好不要每次打不同环境的包(即源码包最好任何环境都可以适用)
不同环境使用同一份源码包,我可以打一个包分别上传到test,prelease,prod等不同的环境,而且同一份源码,即表示不需要在编译时区分环境,减少配置,减少构建环境的差异,减少部署时的注意项,防止关注点因为人为遗漏引起的诸多问题
配置项最好跟着源码,配置值不要跟着源码
配置跟着代码,就不容易丢失,交接时,仍然可以知道所有的配置项,不会出现上面所说的问题2。配置值不提交到源码,是因为我们源码可能位于任何地方,公开的(github),私有的(公司内网,自建库等),说是私有其实属于公有的(三方免费或收费的服务)。虽然spring对于配置值加密已经有很多解决方案,但是实习生把源码发到私有博客/git的事件也实有发生,而且把加密后配置值暴露始终也是有隐患的。
四、目前的解决方案
在applicatin.yml里配置全局的所有配置,配置值用占位符代替
application.yml
spring:
datasource:
url: ${mysql.url}
username: ${mysql.user}
password: ${mysql.pwd}
在不同环境的配置里,配置占位符变量的值
application-local.yml
mysql:
ur: xxxx
user: xxx
pwd: xxx
这样在不同环境的配置文件里,我们就可以只关注重点的配置信息,而且可以不用按spring冗长的配置信息名,配置结构去配置。也可以免除之前的,application.yml只配公共,application-{环境}.yml里配环境变化的值,导致的同一类的配置信息分到不同文件的割裂问题了。
并且这样配置,即使源码中不包含配置值,从全量的配置文件中找出所有要配置的信息,也不难。
五、不完美
对于数组/集合的支持不完美
application.yml
arrays1:
- {a.0}
- {a[1]}
- {a.a2}
arrays2: {a}
application-local.yml
a:
- v1
- v2
- v3
碰到原始配置是数组或集合,甚至集合/集合+对象的配置,想要通过引入的配置的方法配置值,以上的方式都是不行的(可能我没有去读源码导致没找到最终的解决方案,如果有人知道或找到了,请一定给我留言)
六、未做的尝试
nacos下,可否使用此配置?
例如源码中只配置全量的配置和占位符,在nacos你本地配置一样,配置不同环境的占位符关键配置项的值即可,这些推断,因为各种原因,截止发稿时还未做尝试
微服务下是否有效(如nacos)
因为微服务下大部分应用都会有刷新环境变量配置的要求,如果上面的成立,我们刷新application-local.yml里的配置项,是否会导致application.yml里的配置不会刷新?
即,如下:
application.yml
spring:
datasource:
url: ${mysql.url}
application-local.yml
mysql:
url: xxx
系统检测到刷新的是mysql.url,而不是spring.datasource.url,导致我们想要实现的spring.datasource.url的刷新,不能实现。
七、遗留的思考
很多微服务项目使用maven变量区分环境,因为他们默认技术团队会有CI自动化,jks流水线(pipline)等各种自动化流程,所以即使不同的环境需要打不同的包,从部署环境时一次修改,永久有效的收益上来说,问题也不大。
不过个人认为以上如果有效,仍然可以让编译构建发布流程公共化和无环境化,对整个流程上又未尝不是一个正面的影响