项目中需要记录接口调用的日志,由于目前数量不大,决定根据年进行动态创建表(invoke_interface_log_2021)。使用MyBatis实现(动态SQL)。
Dao.xml
<mapper namespace="com.xxx.xxx.dao.manager.system.InvokeInterfaceLogDao">
<!-- 根据表名统计已存在的数量 -->
<select id="countTables" parameterType="string" resultType="int" databaseId="mysql">
SELECT count(1)
FROM information_schema.TABLES
WHERE LCASE(table_name) = #{tableName}
</select>
<!-- 创建表SQL,参数为表名 -->
<update id="createTable" parameterType="string" databaseId="mysql">
CREATE TABLE ${tableName} (
`id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '主键',
`client_id` varchar(50) DEFAULT NULL COMMENT '客户端标识',
`interface_type` tinyint(4) unsigned DEFAULT NULL COMMENT '接口类型',
`interface_name` varchar(50) DEFAULT NULL COMMENT '接口名称',
`invoke_time` datetime DEFAULT NULL COMMENT '调用时间',
`is_success` tinyint(1) unsigned DEFAULT '0' COMMENT '是否成功',
`fail_type` tinyint(4) unsigned DEFAULT NULL COMMENT '失败类型',
`fail_msg` varchar(255) DEFAULT NULL COMMENT '失败信息',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='调用接口日志表';
</update>
<insert id="insert" parameterType="com.xxx.xxx.xxx.manager.system.InvokeInterfaceLogDO" databaseId="mysql">
INSERT INTO ${tableName} (
client_id,
interface_type,
interface_name,
invoke_time,
is_success,
fail_type,
fail_msg
)
VALUES
(
#{invokeInterfaceLog.clientId},
#{invokeInterfaceLog.interfaceType},
#{invokeInterfaceLog.interfaceName},
#{invokeInterfaceLog.invokeTime},
#{invokeInterfaceLog.success},
#{invokeInterfaceLog.failType},
#{invokeInterfaceLog.failMsg}
)
</insert>
</mapper>
复制代码
Dao.java
/**
* 接口调用日志数据访问层接口
*
* @author: yemingxiang
*/
public interface InvokeInterfaceLogDao {
/**
* 统计存在的表数量
*
* @param tableName 表名
* @return 存在的表数量
*/
int countTables(String tableName);
/**
* 创建表
*
* @param tableName 表名
*/
void createTable(@Param("tableName") String tableName);
/**
* 新增
*
* @param tableName 表名
* @param invokeInterfaceLogDO 调用接口日志数据对象
* @return 新增成功的记录数
*/
int insert(@Param("tableName") String tableName,
@Param("invokeInterfaceLog") InvokeInterfaceLogDO invokeInterfaceLogDO);
}
复制代码
业务层
实现如下:
/**
* 调用接口日志业务逻辑层实现
*
* @author yemingxiang
*/
@Service
public class InvokeInterfaceLogServiceImpl implements InvokeInterfaceLogService {
private static final Logger logger = LoggerFactory.getLogger(InvokeInterfaceLogServiceImpl.class);
@Autowired
private InvokeInterfaceLogDao invokeInterfaceLogDao;
@Autowired
private RedisService redisService;
@Autowired
private RedissonClient redissonClient;
@Override
public boolean save(InvokeInterfaceLogDO invokeInterfaceLogDO) {
int currentYear = LocalDate.now().getYear();
String key = String.format("system:invokeInterfaceLog:%s:string", currentYear);
logger.info("接口调用日志表Redis Key:" + key);
String tableName = redisService.getString(key);
logger.info("接口调用日志表表名:" + tableName);
if (tableName != null) {
return invokeInterfaceLogDao.insert(tableName, invokeInterfaceLogDO) == 1;
}
String newTableName = String.format("invoke_interface_log_%s", currentYear);
String lockKey = newTableName;
try {
RLock redissonLock = redissonClient.getLock(lockKey);
redissonLock.lock();
if (!Thread.currentThread().isInterrupted()) {
log.info("get redis lock, key: {}", lockKey);
try {
// 第二层判断,防范作用
tableName = redisService.getString(key);
logger.info("再次获取接口调用日志表表名:" + tableName);
if (tableName != null) {
return invokeInterfaceLogDao.insert(tableName, invokeInterfaceLogDO) == 1;
}
int count = invokeInterfaceLogDao.countTables(newTableName);
if (count == 0) {
logger.info("创建接口调用日志表,并将表名保存到Redis");
invokeInterfaceLogDao.createTable(newTableName);
redisService.set(key, newTableName);
}
return invokeInterfaceLogDao.insert(newTableName, invokeInterfaceLogDO) == 1;
} finally {
redissonLock.unlock();
log.info("release redis lock, key: {}", lockKey);
}
} else {
log.error("{} does not get lock.", Thread.currentThread().getName());
throw new ThirdPartyServiceException(ThirdPartyServiceErrorCodeEnum.CACHE_SERVICE_TIMEOUT);
}
} catch (Exception e) {
if (e instanceof ThirdPartyServiceException) {
throw e;
}
throw new BaseException("处理失败_" + e.getMessage(), e);
}
return false;
}
}
复制代码
大功告成^_^
© 版权声明
文章版权归作者所有,未经允许请勿转载。
THE END