我的开发日记(十五)
报告一个好消息:测试至今,依然没有BUG。
这两天又遇到了两个坑:一个host缓存,一个多节点用户锁,解决方案比较现成,但是实施起来还是费了大劲。
host缓存
运行用例获取host,之前的方案是程勋启动把所有的host全部放在JVM
里面,这样直接从一个map
中获取host信息,会非常方便。后来在调试过程中发现,这样有一个弊端:无法感知到数据库数据的变化,本来想着可以做一个订阅或者通知功能,由于实施方案比较麻烦,改成了全部查库,每次查询的耗时在20-50ms,因为多线程运行,总体耗时增加是可以接受的。不过还是觉得host信息更改的机会太少了,缓存下来比较方便。现在的方案就是把查询过的,通过验证的host缓存在JVM
里面,设置一个有效期。这里没有用到线程锁,因为重复设置host不会导致程序出BUG
。
代码如下:
- service层实现
/**
* 获取host,缓存
*
* @param envId
* @param service_id
* @return
*/
@Override
public String getHost(int envId, int service_id) {
String host = ServerHost.getHost(envId, service_id);
if (StringUtils.isBlank(host)) {
host = commonMapper.getHost(envId, service_id);
if (StringUtils.isBlank(host) || !host.startsWith("http")) CommonException.fail("服务ID:{},环境ID:{}域名配置错误");
ServerHost.putHost(envId, service_id, host);
}
return host;
}
- 静态类
package com.okay.family.common.basedata
import com.okay.family.fun.frame.SourceCode
import org.slf4j.Logger
import org.slf4j.LoggerFactory
import java.util.concurrent.ConcurrentHashMap
class ServerHost extends SourceCode {
private static Logger logger = LoggerFactory.getLogger(ServerHost.class)
static Map<Integer, String> hosts = new ConcurrentHashMap<>()
static Map<Integer, Integer> timeout = new ConcurrentHashMap<>()
public static String getHost(int id) {
if ((getMark() - timeout.getOrDefault(id,0) > OkayConstant.HOST_TIMEOUT) || !hosts.containsKey(id)) null
else hosts.get(id)
}
static String getHost(int envId, int serviceId) {
getHost(serviceId * 10 + envId)
}
static void putHost(int envId, int serviceId, String host) {
int key = serviceId * 10 + envId
timeout.put(key, getMark())
hosts.put(key, host)
}
}
分布式锁
经过同事建议,决定采用MySQL
数据库实现,新建一张表,用表的主键ID作为锁的key
,根据插入id=key
这条数据的存在与否作为获取锁的成功状态,删除表示释放该锁。基本方案比较现成,代码如下:
-
service实现
-
下面是获取用户凭据的方法(非缓存)
@Override
@Transactional(isolation = Isolation.REPEATABLE_READ, propagation = Propagation.REQUIRES_NEW)
public TestUserCheckBean getCertificate(int id) {
Object o = UserLock.get(id);
synchronized (o) {
TestUserCheckBean user = testUserMapper.findUser(id);
if (user == null) UserStatusException.fail("用户不存在,ID:" + id);
String create_time = user.getCreate_time();
long create = Time.getTimestamp(create_time);
long now = Time.getTimeStamp();
if (now - create < OkayConstant.CERTIFICATE_TIMEOUT && user.getStatus() == UserState.OK.getCode())
return user;
boolean b = UserUtil.checkUserLoginStatus(user);
if (!b) {
updateUserStatus(user);
} else {
testUserMapper.updateUserStatus(user);
}
return user;
}
}
- 下面是通过登录获取用户凭据的方法
@Override
@Transactional(isolation = Isolation.REPEATABLE_READ, propagation = Propagation.REQUIRES_NEW)
public int updateUserStatus(TestUserCheckBean bean) {
Object o = UserLock.get(bean.getId());
int userLock = NodeLock.getUserLock(bean.getId());
synchronized (o) {
int lock = commonService.lock(userLock);
if (lock == 0) {
int i = 0;
while (true) {
SourceCode.sleep(OkayConstant.WAIT_INTERVAL);
TestUserCheckBean user = testUserMapper.findUser(bean.getId());
String create_time = user.getCreate_time();
long create = Time.getTimestamp(create_time);
long now = Time.getTimeStamp();
if (now - create < OkayConstant.CERTIFICATE_TIMEOUT && user.getStatus() == UserState.OK.getCode())
return 1;
i++;
if (i > OkayConstant.WAIT_MAX_TIME) {
UserStatusException.fail("获取分布式锁超时,导致无法更新用户凭据:id:" + bean.getId());
}
}
} else {
try {
UserUtil.updateUserStatus(bean);
int i = testUserMapper.updateUserStatus(bean);
return i;
} finally {
commonService.unlock(userLock);
}
}
}
}
- 下面是数据库
CREATE TABLE `qa_lock` (
`id` bigint(20) unsigned NOT NULL COMMENT '锁key',
`create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '创建时间',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='分布式锁表-QA-FunTester-20200715';
- 公众号FunTester首发,更多原创文章:FunTester420+原创文章,欢迎关注、交流,禁止第三方擅自转载。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南