自定义注解实现服务的动态开关

shigen日更文章的博客写手,擅长Java、python、vue、shell等编程语言和各种应用程序、脚本的开发。记录成长,分享认知,留住感动。

🧑‍💻🧑‍💻🧑‍💻Make things different and more efficient

接近凌晨了,今天的稿子还没来得及写,甚是焦虑,于是熬了一个夜也的给它写完。正如我的题目所说:《自定义注解实现服务动态开关》,接下来和shigen一起来揭秘吧。


前言

shigen实习的时候,遇到了业务场景:**实现服务的动态开关,避免redis的内存被打爆了。**当时的第一感受就是这个用nacos配置一下不就可以了,nacos不就是有一个注解refreshScope,配置中心的配置文件更新了,服务动态的更新。当时实现是这样的:

在我的nacos上这样配置的:

service:enable: true

那对应的java部分的代码就是这样的:

class Service {@Value("service.enable")private boolean serviceEnable;public void method() {if (!serviceEnable) {return;}// 业务逻辑}
}

貌似这样是可以的,因为我们只需要动态的观察数据的各项指标,遇到了快要打挂的情况,直接把布尔值换成false即可。


但是不优雅,我们来看看有什么不优雅的:

  1. 配置的动态刷新是有延迟的。nacos的延迟是依赖于网络的;
  2. 不亲民。万一哪个开发改坏了配置,服务就是彻底的玩坏了;而且,如果业务想做一个动态的配置,任何人都可以在系统上点击开关,类似于下边的操作:

服务开关操作

element-UI的动态开关

nacos配置的方式直接不可行了!

那给予以上的问题,相信部分的伙伴已经思考到了:那我把配置放在redis中呗,内存数据库,直接用外部接口控制数据。

很好,这种想法打开了今天的设计思路。我们先协一点伪代码:

@getMapping(value="switch") 
public Integer switch() {Integer status = redisTemplate.get("key");if (status == 1) {status = 0;} else {status = 1;}redisTemplate.set("key", status);return status;
}@getMapping(value= "pay")
public Result pay() {Integer status = redisTemplate.get("key");if (status ==0) {throw new Bizexception("服务不可用");} else {doSometing();}
}

貌似超级完美了,但是想过没有,业务的侵入很大呢。而且,万一我的业务拓展了,别的地方也需要这样的配置,岂不是直接复制粘贴?那就到此为止吧。


我觉得任何业务的设计都是需要去思考的,一味的写代码,做着CRUD的各种操作,简直是等着被AI取代吧。

那接下来分享shigen的设计,带着大家从我的视角分析我的思考和设计点、关注点。

代码设计

注解设计
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface ServiceSwitch {String switchKey();String message() default "当前业务已关闭,请稍后再试!";}

我在设计的时候,考虑到了不同的业务模块和失败的信息,这些都可以抽取出来,在使用的时候,直接加上注解即可。具体的方法和拦截,我们采用spring的AOP来做。

常量类
public class Constants {public static final String ON = "1";public static final String OFF = "0";public static class Service {public static final String ORDER = "service-order";public static final String PAY = "service-pay";}}

既然涉及到了业务模块和状态值,那配置一个常量类是再合适不过了。

业务代码
  @ServiceSwitch(switchKey = Constants.Service.PAY)public Result pay() {log.info("paying now");return Result.success();}

业务代码上,我们肯定喜欢这样的设计,直接加上一个注解标注我们想要控制的模块。

请注意,核心点来了,我们注解的AOP怎么设计?

AOP设计

老方式,我们先看一下代码:

@Aspect
@Component
@Slf4j
public class ServiceSwitchAOP {@Resourceprivate RedisTemplate<String, String> redisTemplate;/*** 定义切点,使用了@ServiceSwitch注解的类或方法都拦截 需要用注解的全路径*/@Pointcut("@annotation(main.java.com.shigen.redis.annotation.ServiceSwitch)")public void pointcut() {}@Around("pointcut()")public Object around(ProceedingJoinPoint point) {// 获取被代理的方法的参数Object[] args = point.getArgs();// 获取被代理的对象Object target = point.getTarget();// 获取通知签名MethodSignature signature = (MethodSignature) point.getSignature();try {// 获取被代理的方法Method method = target.getClass().getMethod(signature.getName(), signature.getParameterTypes());// 获取方法上的注解ServiceSwitch annotation = method.getAnnotation(ServiceSwitch.class);// 核心业务逻辑if (annotation != null) {String switchKey = annotation.switchKey();String message = annotation.message();/*** 配置项: 可以存储在mysql、redis 数据字典*/String configVal = redisTemplate.opsForValue().get(switchKey);if (Constants.OFF.equals(configVal)) {// 开关关闭,则返回提示。return new Result(HttpStatus.FORBIDDEN.value(), message);}}// 放行return point.proceed(args);} catch (Throwable e) {throw new RuntimeException(e.getMessage(), e);}}
}

拦截我的注解,实现一个切点,之后通知切面进行操作。在切面的操作上,我们读取注解的配置,然后从redis中拿取对应的服务状态。如果服务的状态是关闭的,直接返回我们自定义的异常类型;服务正常的话,继续进行操作。

接口测试

最后,我写了两个接口实现了服务的调用和服务模块状态值的切换。

@RestController
@RequestMapping(value = "serviceSwitch")
public class ServiceSwitchTestController {@Resourceprivate ServiceSwitchService serviceSwitchService;@GetMapping(value = "pay")public Result pay() {return serviceSwitchService.pay();}@GetMapping(value = "switch")public Result serviceSwitch(@RequestParam(value = "status", required = false) String status) {serviceSwitchService.switchService(status);return Result.success();}
}

代码测试

测试服务正常

服务状态正常情况下的测试

此时,redis中服务的状态值是1,服务也可以正常的调用。

测试服务不正常

我们先调用接口,改变服务的状态:

调用接口,切换服务的状态

再次调用服务:

服务模块关闭

发现服务403错误,已经不能调用了。我们改变一下状态,服务又可以用了,这里就不做展示了。


以上就是今天分享的全部内容了,觉得不错的话,记得点赞 在看 关注支持一下哈,您的鼓励和支持将是shigen坚持日更的动力。同时,shigen在多个平台都有文章的同步,也可以同步的浏览和订阅:

平台账号链接
CSDNshigen01shigen的CSDN主页
知乎gen-2019shigen的知乎主页
掘金shigen01shigen的掘金主页
腾讯云开发者社区shigenshigen的腾讯云开发者社区主页
微信公众平台shigen公众号名:shigen

shigen一起,每天不一样!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/176240.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

多级缓存之实现多级缓存

多级缓存的实现离不开Nginx编程&#xff0c;而Nginx编程又离不开OpenResty。 1. OpenResty快速入门 我们希望达到的多级缓存架构如图&#xff1a; 其中&#xff1a; windows上的nginx用来做反向代理服务&#xff0c;将前端的查询商品的ajax请求代理到OpenResty集群 OpenRest…

【教3妹学编程-算法题】K 个元素的最大和

3妹&#xff1a;“太阳当空照&#xff0c;花儿对我笑&#xff0c;小鸟说早早早&#xff0c;你为什么背上炸药包” 2哥 :3妹&#xff0c;什么事呀这么开发。 3妹&#xff1a;2哥你看今天的天气多好啊&#xff0c;阳光明媚、万里无云、秋高气爽&#xff0c;适合秋游。 2哥&#x…

2.5 Windows驱动开发:DRIVER_OBJECT对象结构

在Windows内核中&#xff0c;每个设备驱动程序都需要一个DRIVER_OBJECT对象&#xff0c;该对象由系统创建并传递给驱动程序的DriverEntry函数。驱动程序使用此对象来注册与设备对象和其他系统对象的交互&#xff0c;并在操作系统需要与驱动程序进行交互时使用此对象。DRIVER_OB…

半导体应用系统一些小知识收集(stripwafer mapping,EAPscada)

单一元件追踪Single Device Traceability ,指的是在制造封装流程中对任何一个点上的任何一台单一设备进行实时追踪&#xff0c;并将相关历史数据储存进数据库服务器&#xff0c;同时在需要的情况下能够查询这些历史数据的能力。 SDT系统的核心特性可以被概括为如下: Wafer Map …

【深度学习】吴恩达课程笔记(四)——优化算法

笔记为自我总结整理的学习笔记&#xff0c;若有错误欢迎指出哟~ 【吴恩达课程笔记专栏】 【深度学习】吴恩达课程笔记(一)——深度学习概论、神经网络基础 【深度学习】吴恩达课程笔记(二)——浅层神经网络、深层神经网络 【深度学习】吴恩达课程笔记(三)——参数VS超参数、深度…

2024 AIGC 规划:探索交互体验变革及 智能硬件基础设施篇

TL;DR Run LLM/Embedding on Android: https://github.com/unit-mesh/android-semantic-search-kitInference SDK&#xff1a;https://github.com/unit-mesh/inference 正文&#xff1a; 在过去的一年时间里&#xff0c;国内外大中型公司都在探索、引入了 GenAI / AIGC&#xf…

Seaborn数据可视化综合应用Basemap和Seaborn在线闯关_头歌实践教学平台

Seaborn数据可视化综合应用Basemap和Seaborn 第1关 Seaborn第2关 Seaborn图形介绍第3关 Basemap 第1关 Seaborn 任务描述 本关任务&#xff1a;编写一个绘制每个月销售总额的折线图。 编程要求 本关的编程任务是补全右侧上部代码编辑区内的相应代码&#xff0c;根据输入文件路…

优选算法精品解析

1.双指针(前后/左右双指针) 1.1 283.移动零 快排双指针的核心算法 左边所有数 < tmp,右边所有数 > tmp,以tmp这个数为标准 1.2 1089.复习零 如果一对双指针从左向右不行,那么就从右向左,换一个方向 1.3 202.快乐数 双指针中的快慢指针: slow1,fast2 1.4 11.最多盛水的…

使用Java实现一个简单的贪吃蛇小游戏

一. 准备工作 首先获取贪吃蛇小游戏所需要的头部、身体、食物以及贪吃蛇标题等图片。 然后&#xff0c;创建贪吃蛇游戏的Java项目命名为snake_game&#xff0c;并在这个项目里创建一个文件夹命名为images&#xff0c;将图片素材导入文件夹。 再在src文件下创建两个包&#xff0…

CCLink转Modbus TCP网关_MODBUS网口设置

兴达易控CCLink转Modbus TCP网关是一种用于连接CCLink网络和Modbus TCP网络的设备。它提供了简单易用的MODBUS网口设置&#xff0c;可以帮助用户轻松地配置和管理网络连接 1 、网关做为MODBUS主站 &#xff08;1&#xff09;将电脑用网线连接至网关的P3网口上。 &#xff08;…

Ubuntu22.04源码安装ROS-noetic(ROS1非ROS2),编译运行VINS-MONO

1. Ubuntu22.04源码编译安装ROS-noetic 由于22.04默认安装ROS2&#xff0c;但很多仓库都是基于ROS1的&#xff0c;不想重装系统&#xff0c;参考这两个博客安装了ROS-noetic&#xff1a; 博客1. https://blog.csdn.net/Drknown/article/details/128701624博客2. https://zhua…

飞天使-django创建一个初始项目过程

创建django项目 运行项目 运行命令 pyhont manage.py runserver 然后访问 http://127.0.0.1:8000/&#xff0c; 则可以打开本地新建的项目 虚拟环境的部署-mac 在一台计算机上可以通过虚拟环境实现多个版本Django的开发环境 安装虚拟环境工具&#xff1a;如果你的系统中没有安…