前言
背景:本司 Oss 的服务迭代了不下 5 个了!!!!由于本司部分文件是加密存储的,且有对应的配置文件权限的面板,当切换到不同的 Oss 地址需要修改文件权限时,需要切换到对应 Oss 的 ak、sk 才能进行配置。老代码一堆的 if、else 还有各个不同的 OssUtil 着实看着难受,时不时这个 Oss 地址修改不了权限了,但是我明明已经改过了 OssUtil 啊,怎么没生效?搜一下全局变量,哦,原来 OssUtil 有不下 5 个了,我正好又是这个项目的负责人,真的操蛋,出于空闲时间,决定出手优化一下这段代码
问题背景
例如:http://oss-a、http://oss-b 是之前已经被迭代过的地址(Oss 容量已经满了),对其的修改权限、删除文件、获取加密访问地址都需用老的 Oss 的 ak、sk才能进行操作。因此老代码直接将 OssUtil 复制了 5 份出来,将里面的配置信息做了一个替换。使用的时候也很分散,有些地方用 OssUtil1、有些地方用 OssUtil2.没有做一个全局的配置,一旦涉及到切换 Oss 地址时,你就要改动多处代码,十分操蛋。
优化效果
当现有的 Oss 满了之后,迁移 Oss 地址时只需新增新的 Oss配置类即可,接口后续1亿年都不用改!!!!!做到零成本配置接入!全局统一替换的效果,后续也可以将配置放到 Nacos…中管理
例如:现在我有一个 beijing 的 Oss 还有一个 shangquan 的 Oss,后续如果要做替换的话。接口还是用上图那四个,我们只需新增一个策略配置类即可。
策略类的扩展
当换 Oss 存储的时候,新增如下配置类,继承我封装好的 AbstractOssStrategy 即可,然后把对应的 ak、sk、bucketName 啥的换一换,并且指定一下 Order 的优先级是第一即可。
- 指定优先级为第一目的:优先级第一的策略 Oss 容量没有满,负责存储解析,最新的上传文件
- 非优先级第一的策略类(Oss容量):负责处理解析历史文件
然后你就完成了领导布置的任务:小张,原先的 Oss 服务满了,我给你一个新的 Oss 地址,你把项目中所有用到了 Oss 的地方都给替换一下,之前的同事在这方面,可是经常犯错,一直说他改好了,可是后来 Bug 频出,我都搞不懂了,换个 Oss 地址有这么费劲吗,不过他是他,你是你,我对你还是很欣赏滴,你是个人才,勤劳肯干,并且愿意花大把的时间在检查配置文件上,好了等下我有个会要开,就先不和你说了 。我真的是欲哭无泪。
考虑到后续文件地址存储表过大,可能要做冷热表分离,例如:之前的用的是 OssA 服务,由于 OssA 服务满了,需要将流量迁移到 OssB 服务,但是在用 OssA 服务期间,所有的操作日志是用 OssALog 表存储的,现在 OssALog 表数据量太大,需要用 OssBLog 表存储,在配置 OssB 的策略的时候,重写 saveLog 方法即可,而 OssA 策略的 saveLog 方法里面仍然是对 OssALog 表进行操作,策略类间的逻辑互相隔离,因此提供扩展方法 saveLog 用来记录操作日志,例如:记录下载量、上传量、上传记录。但是有一个前提就是是 OssBLog 与 OssALog 表结构一样。我感觉这个功能有点鸡肋,因为限制比较多属于我自己发散出来的扩展点了。可能我自己用来了会比较顺手,开源出来就够呛了。
策略类使用
就拿 获取私有文件的访问地址 接口来说,Controller 层就几行代码(根据 url 匹配对应的策略,然后执行),看着简洁明了。线上的代码我还用了下 ApplicationListener 接口用来统一注册、管理所有的策略类。这里就不贴出来了。
//策略工厂
@Autowired
private OssStrategyFactory ossStrategyFactory;
@ApiOperation("私有文件获取加密路径")
@PostMapping("csdnGetAclPath")
public Result csdnGetAclPath(@RequestParam("url") String url) throws Throwable {
//从策略工厂中根据url匹配到对应的策略,然后进行执行
return Result.success(ossStrategyFactory
.getSuitAbleStrategy(url)
.getAclPath(url, 60000));
}
当然你也可以用这种方式调用策略类,通过依赖注入的方式,将所有的实现了 OssStrategy 接口的 Oss 策略注入到一个 List 中,然后循环遍历到合适的策略执行。
@Autowired
private List<OssStrategy> ossStrategies;
/**
* 根据 url 匹配具体的策略执行
* @param url
* @return
*/
public OssStrategy getSuitAbleStrategy(String url) {
if (StringUtils.isEmpty(url)) return ossStrategies.get(0);
try {
return ossStrategies
.stream()
.filter(ossStrategy -> {return ossStrategy.support(url);})
.findFirst().orElseThrow(new Supplier<Throwable>() {
@Override
public Throwable get() {
return new RuntimeException("系统未找到合适的策略执行!");
}
});
} catch (Throwable e) {
e.printStackTrace();
throw new RuntimeException("系统未找到合适的策略执行!");
}
}
总结
本文以真实业务场景为例:以如下几点展开诉说,策略模式是如何使用的
1: 编写顶级策略接口(OssStrategy)
2: 编写具体的策略实现类(OssStrategyImpl)
3: 通过 @Autowired 注入所有的策略实现类到一个 List 中,然后挨个遍历所有策略,直到找出合适的策略,然后执行
4: 后续策略类的扩展的话,新增一个顶级策略接口(OssStrategy)的实现类即可
更多关于实战中设计模式的落地应用可以扫描下方二维码,不定期更新更多干货