Sentinel整合Springboot并将规则持久化到Oracle数据库
前言
公司一个老项目使用的是springboot,然后需要在这个项目上加一个sentinel流量哨兵,这一步不难,需要注意的是sentinel的依赖实在是太复杂了,而且很多老版本的已经不兼容了,所以建议大家新建一个springboot项目,脚手架选择阿里的springcloud alibaba框架,可以一键下载安装sentinel的相关依赖,不用担心版本兼容问题。(此方法来源于CSDN某网友,一时找不到原文章原作者了,在此再次表示感谢和歉意)
安装好sentinel后,会发现sentinel的规则是存储在内存当中的,也就是说一旦服务器重启,那么里面的规则将全部丢失。所以我们需要将sentinel的规则进行持久化。最常见的持久化方式当然是使用nacos,但是这个项目是一个springboot项目,并没有配置nacos,所以也没必要仅为了一个sentinel再去配置一套nacos。于是将规则持久化到数据库中也是个不错的选择。
在网上查阅资料后发现,网上已经有很多sentinel+MySQL的案例了,却没有oracle的案例,于是自己动手将网友大佬们的MySQL改造了一下,改成了oracle版本,MySQL原文链接在此,表示感谢。
Sentinel使用mysql进行规则持久化实践_sentinel-mysql规则持久化-CSDN博客
下载上面的资源后,解压后用idea打开下载依赖即可,前置工作准备完成后,开始改造。
首先我们要明确,从MySQL改造为oracle,我们需要改什么。
1.JDBC信息,2.SQL语句,3.数据库表的唯一约束与触发器
jdbc修改的东西不多,只需要在配置文件里把MySQL的驱动改为oracle即可,记得引入你jdk对应版本的oracle依赖包,此处需要注意的地方:
1.ORA-12514:TNS:listener does not currently know of service requested in connect descriptor
检查你的oracle版本,跟你的OCI版本是否兼容。以navicat为例,如下图所示。具体版本兼容性百度一搜就有。
2.ORA-12505, TNS:listener does not currently know of SID given in connect descriptor
驱动URL写错了,监听sid时应该为 jdbc:oracle:thin:@127.0.0.1:port:orcl。监听服务名时应该为jdbc:oracle:thin:@127.0.0.1:port/orcl。(区别在于一个端口号后是冒号一个是斜线)。
ok,到此为止已经完成了前置工作,接下来看代码。
代码改造
1.将SqlHelper中的MySQL驱动改为Oracle。
2.更改SQL语句
DefaultTableModel resultSet = SqlHelper.getResultSet("SELECT \"content\" FROM \"SENTINEL_RULES\" WHERE \"app_name\"='"+appName+"' AND \"rule_type\"="+dataId.getCode()+";");
注意,修改的这段SQL在MySQL里直接使用的replace into来进行自动判断是新增还是修改,但oracle里没有replace into这个语法,于是使用merge into来进行替代。由于此SQL较长,我先将SQL写成string字符串,再调用ExecSql执行,当然,也可以直接调用ExecSql,将字符串直接写到方法的第一个参数里。还有一点需要注意,oracle里的主键并不会自增,所以需要设置一个自增序列来让主键自增,或者使用触发器皆可,具体方法可自行百度。这里我使用的方法是自增序列。然后第二点需要注意的是,一旦把id设置为主键后,id这个字段是不允许传入null值的,所以我直接将SQL语句中传入的五个参数改为了四个,删掉了rule_id这个字段。使id靠自增序列进行自增长即可。
String mergeSql = "MERGE INTO \"SENTINEL_RULES\" t " +
"USING dual " +
"ON (t.\"rule_name\" = ? AND t.\"rule_type\" = ? AND t.\"app_name\" = ?) " +
"WHEN MATCHED THEN " +
"UPDATE SET t.\"content\" = ? " +
"WHEN NOT MATCHED THEN " +
"INSERT (\"rule_name\",\"rule_type\",\"app_name\",\"content\") " +
"VALUES(?,?,?,?)";
System.out.println(mergeSql);
SqlHelper.ExecSql(mergeSql,
dataId.getName(), dataId.getCode(), appName,
JSON.toJSONString(rules),
dataId.getName(), dataId.getCode(), appName, JSON.toJSONString(rules));
做到这里,那么应该就能新增数据了,我们在控制台新增的数据会插入到数据库里,但是目前有一个问题,当我们修改规则时,merge into 语句仍然会判断为新增而不是修改,原因是我们传入的4个参数中不包含主键(也就是rule_id),所以merge会永远认为我们在新增数据而不是修改,除非我们在SQL中加上where条件,很显然这做不到,因为我们新增和修改的SQL用的是同一条语句(也就是merge into那条),所以我们这条语句是没法改的,如果加了where条件,那新增的时候就有问题了。所以我们需要再加一个唯一约束,约束条件为规则名称和项目名称,这样当一条数据的规则名称和项目名称都一样是,merge就会认为这条语句是在修改而不是新增。
3.修改数据库表
alter table 表名
add constraint 约束名
unique (app_name,rule_type);
在MySQL原文中,原作者为我们写好了MySQL的一键建表语句,但oracle和MySQL建表语句不一样,所以我是手动对照着原作者的SQL语句手动建的表,所以是没有唯一性约束的,需要后期再执行上面的SQL来添加唯一性约束,其作用等同于下图中标记的语句。
重点需要改造的方法只有这两个,然后再把表的created_time的默认值设为当前时间即可,这样新数据插入的时候会自动将created_time字段设置为插入时间,然后在创建一个触发器,当数据更新时,更新update_time这个字段即可,这一步操作可以不做,也不会报错,只是这两个字段会为空,但不影响规则的持久化。
到此为止就改造完成,希望能够帮到大家。