基于BIT数组实现全局功能开关
前提
某一天巧合打开了sofa-bolt项目,查找部分源码,看到了项目中使用bit数组实现功能开关的特性,感觉这种方式可以借鉴,于是写下这篇文章。
原理
bit数组的布局如下:

由于每个bit都可以表示1或者0,刚好对应于开关的ON和OFF。只需要定义好每个开关所在的bit数组下标和开关的状态(ON = 1或者OFF = 0),通过判断不同开关下标所在的bit即可判断开关的状态:

- 优点:节省空间,理论上只需要占用最多
2n位的内存(n为开关的数量,这里考虑扩容,扩容让bit数组长度为原来的2倍) - 缺点:暂时没发现
实现
JDK中的bit数组可以直接使用BitSet。首先定义开关定义SwitchDef:
public class SwitchConst {
public static final boolean ON = true;
public static final boolean OFF = false;
}
@RequiredArgsConstructor
@Getter
public enum SwitchDef {
/**
* 启用HTTPS
*/
ENABLE_HTTPS(0, SwitchConst.ON, "启用HTTPS"),
/**
* 启用异步
*/
ENABLE_ASYNC(1, SwitchConst.OFF, "启用异步"),
;
/**
* 下标
*/
private final int index;
/**
* 默认状态
*/
private final boolean defaultStatus;
/**
* 描述
*/
private final String description;
@Override
public String toString() {
return String.format("SwitchDef(name=%s,description=%s)", name(), description);
}
}
接着定义开关接口Switch:
public interface Switch {
/**
* 启用
*
* @param switchDef switchDef
*/
void turnOn(SwitchDef switchDef);
/**
* 关闭
*
* @param switchDef switchDef
*/
void turnOff(SwitchDef switchDef);
/**
* 判断状态
*
* @param switchDef switchDef
* @return boolean
*/
boolean status(SwitchDef switchDef);
}
最后编写开关实现BitSetSwitch和客户端代码:
public enum BitSetSwitch implements Switch {
/**
* 单例
*/
X;
BitSetSwitch() {
init();
}
private final BitSet switches = new BitSet();
@Override
public void turnOn(SwitchDef switchDef) {
switches.set(switchDef.getIndex(), SwitchConst.ON);
}
@Override
public void turnOff(SwitchDef switchDef) {
switches.clear(switchDef.getIndex());
}
@Override
public boolean status(SwitchDef switchDef) {
return switches.get(switchDef.getIndex());
}
private void init() {
Stream.of(SwitchDef.values()).forEach(item -> switches.set(item.getIndex(), item.isDefaultStatus()));
}
public static void main(String[] args) {
Switch s = BitSetSwitch.X;
s.turnOn(SwitchDef.ENABLE_HTTPS);
s.turnOff(SwitchDef.ENABLE_ASYNC);
System.out.printf("开关[%s],状态:%s%n", SwitchDef.ENABLE_HTTPS, s.status(SwitchDef.ENABLE_HTTPS));
System.out.printf("开关[%s],状态:%s%n", SwitchDef.ENABLE_ASYNC, s.status(SwitchDef.ENABLE_ASYNC));
}
}
执行该main方法后控制台输出如下:
开关[SwitchDef(name=ENABLE_HTTPS,description=启用HTTPS)],状态:true
开关[SwitchDef(name=ENABLE_ASYNC,description=启用异步)],状态:false
仿真场景(伪代码)如下:
Switch s = BitSetSwitch.X;
String uri = "www.throwx.cn";
String schema = "http";
if (s.turnOn(SwitchDef.ENABLE_HTTPS)){
schema = "https";
}
HttpClint ch = new DefaultHttpClient();
if (s.turnOn(SwitchDef.ENABLE_ASYNC)){
ch = new AsyncHttpClient();
}
Result r = ch.executeRequest(schema + uri);
......
小结
在阅读一些主流框架源码的时候,可以借鉴一些设计合理的方案应用到自身的日常开发中。
参考资料:
(e-a-20210724 c-2-d)
基于BIT数组实现全局功能开关的更多相关文章
- 基于STC12C5A的MINI3216多功能点阵时钟
代码地址如下:http://www.demodashi.com/demo/12862.html 基于STC12C5A的MINI3216多功能点阵时钟 硬件详解 PCB 硬件原理图 主控模块 max72 ...
- 微软Azure配置中心 App Configuration (二):Feature Flag 功能开关特性
写在前面 Web服务开发过程中我们经常有这样的需求: 某些功能我必须我修改了配置才启用,比如新用户注册送券等: 某个功能需到特定的时间才启用,过后就失效,比如春节活动等: 某些功能,我想先对10%的用 ...
- 基于90nm CMOS技术的功能齐全的64Mb DDR3 STT-MRAM
自旋转矩磁阻随机存取存储器(ST-MRAM)有望成为一种快速,高密度的非易失性存储器,可以增强各种应用程序的性能,特别是在用作数据存储中的非易失性缓冲器时设备和系统.为此,everspin开发了基于9 ...
- [python] A*算法基于栅格地图的全局路径规划
# 所有节点的g值并没有初始化为无穷大 # 当两个子节点的f值一样时,程序选择最先搜索到的一个作为父节点加入closed # 对相同数值的不同对待,导致不同版本的A*算法找到等长的不同路径 # 最后c ...
- 科学计算三维可视化---Mlab基础(基于Numpy数组的绘图函数)
Mlab了解 Mlab是Mayavi提供的面向脚本的api,他可以实现快速的三维可视化,Mayavi可以通过Mlab的绘图函数对Numpy数组建立可视化. 过程为: .建立数据源 .使用Filter( ...
- 从头认识java-14.4 Java提供的数组的有用功能(2)
接着上一章节,我们继续介绍Java提供的数组的有用功能. 3.元素的对照Comparator package com.ray.ch14; import java.util.Arrays; import ...
- php基于SQLite实现的分页功能示例
php基于SQLite实现的分页功能. 这里操作数据库文件使用的是前面文章<PHP基于PDO实现的SQLite操作类>中的SQLite数据库操作类. 代码: <?php class ...
- 如何在 Blazor WebAssembly中 使用 功能开关
微软Azure 团队开发的 功能管理 (Feature Management) 包 Microsoft.FeatureManagement可用于实现 功能开关,可以通过 功能开关 特性动态的改变应用程 ...
- HMS Core积极探索基于硬件耳返的功能,帮助唱吧整体唱歌延迟率降低60%
唱吧的使命是让唱歌更简单.让生活更美好,其布局的K歌业务专注于让曲库更全.音质更好,开创了同框合唱.弹唱等有意思的游戏类K歌玩法.为了让用户拥有更加沉浸的娱乐体验,唱吧与HMS Core积极探索基于硬 ...
随机推荐
- java并发编程JUC第十一篇:如何在线程之间进行对等数据交换
java.util.concurrent.Exchanger可以用来进行数据交换,或者被称为"数据交换器".两个线程可以使用Exchanger交换数据,下图用来说明Exchange ...
- UNREFERENCED_PARAMETER的用处
UNREFERENCED_PARAMETER的用处 作用:告诉编译器,已经使用了该变量,不必检测警告! 在VC编译器下,如果您用最高级别进行编译,编译器就会很苛刻地指出您的非常细小的警告.当你生命了一 ...
- python django中restful框架的使用
在使用django进行前后台分离开发时通常会搭配django-rest-framework框架创建RESTful风格的接口API.框架介绍及版本要求可参考官方地址:https://www.django ...
- Golang的一致性哈希实现
Golang的一致性哈希实现 一致性哈希的具体介绍,可以参考:http://www.cnblogs.com/haippy/archive/2011/12/10/2282943.html 1 imp ...
- 『心善渊』Selenium3.0基础 — 16、Selenium对iframe表单的操作
目录 1.什么是iframe表单 2.iframe表单操作流程 3.iframe表单操作常用方法 (1)进入表单 (2)多表单切换 4.表单操作示例 1.什么是iframe表单 实际上就是HTML页面 ...
- CRM系统选型时的参考哪些方面
企业不论在制定营销策略或是在进行CRM系统选型时,首先都是要了解自身的需求.每一家企业的情况和需求都有很大差异,CRM系统的功能也都各有偏重.有些CRM偏重销售管理.有些注重于营销自动化.有些则侧重于 ...
- 面试题三:MySQL
MySQL有哪些存储引擎? MyISAM.InnoDB.CSV.Memory等 MyISAM和InnoDB比较: InnoDB MyISAM 事务 支持 不支持 存储限制 64TB 无 锁粒度 行锁 ...
- Linux守护进程列表/守护进程
在linux或者unix操作系统中在系统引导的时候会开启很多服务,这些服务就叫做守护进程.为了增加灵活性,root可以选择系统开启的模式,这些模式叫做运行级别,每一种运行级别以一定的方式配置系统. ...
- linux cut的用法
p.p1 { margin: 0; font: 12px ".PingFang SC Semibold"; color: rgba(53, 53, 53, 1) } p.p2 { ...
- 转:nginx服务器配置
1. user www-data说明的是使用的用户,至于www-data这个用户是系统自带的,我们不用说系统里没有这个账户的,虽然这个账户具体是做什么的,我也不太清楚2.worker_processe ...