引言
在现代软件开发和部署中,零停机部署技术是实现高可用性和无缝用户体验的关键。本文将讨论功能开发开关(Feature Toggles)的类型并分析它们的优缺点,同时提供相关的例子和演示。
PS: https://github.com/WeiXiao-Hyy/blog整理了后端开发的知识网络,欢迎Star!
功能开发(Feature Toggles)
功能开关是一种在运行时控制软件功能是否可见或可用的技术。它允许开发团队随时启用或禁用特定功能,从而实现零停机部署。
常见示例
function reticulateSplines() {
var useNewAlgorithm = false;
// useNewAlgorithm = true; // UNCOMMENT IF YOU ARE WORKING ON THE NEW SR ALGORITHM
if (useNewAlgorithm) {
return enhancedSplineReticulation();
} else {
return oldFashionedSplineReticulation();
}
}
function oldFashionedSplineReticulation() {
// current implementation lives here
}
function enhancedSplineReticulation() {
// TODO: implement better SR algorithm
}
功能开关的种类
Release Toggles
- 用于为实践持续交付的团队启用基于主干的开发。允许将正在进行的功能潜入共享集成分支(例如master分支)。
- 将[功能]发布与[代码]部署分离的持续交付原则的最常见方法。
Experiment Toggles
用于测试多个A/B实验时,将根据用户信息切换路由,将给定用户路由到一个代码路径或另一个代码路径中。通常用于电商的购买流程或号召性用语等内容进行数据驱动性的优化。
Ops Toggles
称为降级开关,当推出一项对性能影响不明确的新功能时,可能会引入Ops Toggle,以便系统操作员在生产环境中快速禁用或降级该功能。
大多数特征开发在新功能稳定之后,该标志就应该退役了。然而,系统拥有少量长寿命“特征开关”的情况并不常见,这些开关允许生产环境的操作员在系统承受异常高负载时可以优雅降级非重要的系统功能。
Permission Toggles
该开关和金丝雀发布有点类似,指的是新功能可以只发布给内部指定用户使用(内测)但是金丝雀发布是随机选择一小部分用户,而该开关是指定一小部分用户集合。
如何实现特征开关
解耦决策逻辑点
const features = fetchFeatureTogglesFromSomewhere();
function generateInvoiceEmail() {
const baseEmail = buildEmailForInvoice(this.invoice);
if (features.isEnabled("next-gen-ecomm")) {
return addOrderCancellationContentToEmail(baseEmail);
} else {
return baseEmail;
}
}
上述代码虽然看起来是一个合理的方法,但是却非常脆弱。
- 引入魔法值next-gen-ecomm;
- 为什么发票电子邮件需要知道订单取消内容是下一代功能集的一部分?
- 随着功能的开发,这种“切换范围”的变化是很常见,如果只向某些用户推出订单取消功能怎么办?
- …
// featureDecision.js
function createFeatureDecisions(features) {
return {
includeOrderCancellationInEmail() {
return features.isEnabled("next-gen-ecomm");
}
// ... additional decision functions also live here ...
};
}
// invoiceEmailer.js
const features = fetchFeatureTogglesFromSomewhere();
const featureDecisions = createFeatureDecisions(features);
function generateInvoiceEmail(){
const baseEmail = buildEmailForInvoice(this.invoice);
if( featureDecisions.includeOrderCancellationInEmail() ){
return addOrderCancellationContentToEmail(baseEmail);
}else{
return baseEmail;
}
}
将决策逻辑点和业务逻辑解耦,需要添加新的功能时只需要添加featureDecision中的方法,在invoiceEmailer中调用featureDecision中的方法。
避免(if/else)
如果Toggle Point是使用if语句来实现的。这对于简单,短暂的切换是有效的。但是,如果某个功能需要多个切换点。,或者希望切换点长期存在,则不建议使用if/else来实现。可以使用策略模式来优化代码中的if/else。
如何使用特征开关
动态路由或动态配置
真正需要在系统运行时动态切换开关存在以下两种场景:
- Ops Toggles 需要降级某个板块;
- Permission Toggles和Experiment Toggles需要动态路由用户的请求;
前者通过在运行期间修改开关的值是动态的,而后者切换路由本质上是动态的。实际上仍然有相当多的静态的配置,也许只能通过重新部署来更改。
如果需要在生产环境中使用更加通用的切换控制机制,最好使用真正的分布式配置系统来构建,并及时清理过时的feature toggle。
参考文献
- https://martinfowler.com/articles/feature-toggles.html
- https://book.douban.com/subject/36457109/
- https://juejin.cn/post/7368777511952924698