refactor: remove production in-memory session store
This commit is contained in:
@@ -28,10 +28,6 @@
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-data-redis</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.github.ben-manes.caffeine</groupId>
|
||||
<artifactId>caffeine</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.junit.jupiter</groupId>
|
||||
<artifactId>junit-jupiter</artifactId>
|
||||
|
||||
@@ -1,84 +0,0 @@
|
||||
package com.lingniu.ingest.session;
|
||||
|
||||
import com.github.benmanes.caffeine.cache.Cache;
|
||||
import com.github.benmanes.caffeine.cache.Caffeine;
|
||||
|
||||
import java.time.Duration;
|
||||
import java.util.Optional;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.ConcurrentMap;
|
||||
import java.util.function.UnaryOperator;
|
||||
|
||||
/**
|
||||
* 测试/本地用纯内存会话存储。支持按 sessionId / vin / phone 三种 key 查询。
|
||||
*
|
||||
* <p>失活清理:基于 {@link Caffeine} 的 {@code expireAfterAccess 30 分钟}。
|
||||
* 生产自动配置拒绝 memory 模式,应使用 Redis 会话索引。
|
||||
*/
|
||||
public final class InMemorySessionStore implements SessionStore {
|
||||
|
||||
private final Cache<String, DeviceSession> byId;
|
||||
private final ConcurrentMap<String, String> vinToId = new ConcurrentHashMap<>();
|
||||
private final ConcurrentMap<String, String> phoneToId = new ConcurrentHashMap<>();
|
||||
|
||||
public InMemorySessionStore() {
|
||||
this.byId = Caffeine.newBuilder()
|
||||
.expireAfterAccess(Duration.ofMinutes(30))
|
||||
.removalListener((String sid, DeviceSession s, com.github.benmanes.caffeine.cache.RemovalCause c) -> {
|
||||
if (s == null) return;
|
||||
if (s.vin() != null) vinToId.remove(s.vin(), sid);
|
||||
if (s.phone() != null) phoneToId.remove(s.phone(), sid);
|
||||
})
|
||||
.build();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void put(DeviceSession session) {
|
||||
byId.put(session.sessionId(), session);
|
||||
// 三索引用同一个 sessionId 汇聚;HTTP 下行可以拿 sessionId/VIN/phone 任意一种查在线连接。
|
||||
if (session.vin() != null) vinToId.put(session.vin(), session.sessionId());
|
||||
if (session.phone() != null) phoneToId.put(session.phone(), session.sessionId());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<DeviceSession> findBySessionId(String sessionId) {
|
||||
return Optional.ofNullable(byId.getIfPresent(sessionId));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<DeviceSession> findByVin(String vin) {
|
||||
String sid = vinToId.get(vin);
|
||||
return sid == null ? Optional.empty() : findBySessionId(sid);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<DeviceSession> findByPhone(String phone) {
|
||||
String sid = phoneToId.get(phone);
|
||||
return sid == null ? Optional.empty() : findBySessionId(sid);
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized Optional<DeviceSession> update(String sessionId, UnaryOperator<DeviceSession> updater) {
|
||||
DeviceSession old = byId.getIfPresent(sessionId);
|
||||
if (old == null) return Optional.empty();
|
||||
DeviceSession next = updater.apply(old);
|
||||
// update 复用 put,保证 VIN/phone 二级索引和主会话对象一起刷新。
|
||||
put(next);
|
||||
return Optional.of(next);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void remove(String sessionId) {
|
||||
DeviceSession s = byId.getIfPresent(sessionId);
|
||||
if (s != null) {
|
||||
byId.invalidate(sessionId);
|
||||
if (s.vin() != null) vinToId.remove(s.vin(), sessionId);
|
||||
if (s.phone() != null) phoneToId.remove(s.phone(), sessionId);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int size() {
|
||||
return (int) byId.estimatedSize();
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,5 @@
|
||||
package com.lingniu.ingest.session.config;
|
||||
|
||||
import com.lingniu.ingest.session.InMemorySessionStore;
|
||||
import com.lingniu.ingest.session.RedisSessionStore;
|
||||
import com.lingniu.ingest.session.SessionStore;
|
||||
import org.junit.jupiter.api.Test;
|
||||
@@ -9,6 +8,7 @@ import org.springframework.boot.test.context.runner.ApplicationContextRunner;
|
||||
import org.springframework.data.redis.core.StringRedisTemplate;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.assertThatThrownBy;
|
||||
import static org.mockito.Mockito.mock;
|
||||
|
||||
class SessionCoreAutoConfigurationTest {
|
||||
@@ -20,10 +20,15 @@ class SessionCoreAutoConfigurationTest {
|
||||
void doesNotCreateImplicitInMemorySessionStoreByDefault() {
|
||||
contextRunner.run(context -> {
|
||||
assertThat(context).doesNotHaveBean(SessionStore.class);
|
||||
assertThat(context).doesNotHaveBean(InMemorySessionStore.class);
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void doesNotPublishInMemorySessionStoreImplementation() {
|
||||
assertThatThrownBy(() -> Class.forName("com.lingniu.ingest.session.InMemorySessionStore"))
|
||||
.isInstanceOf(ClassNotFoundException.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
void usesRedisSessionStoreWhenEnabledAndRedisTemplateExists() {
|
||||
contextRunner
|
||||
|
||||
Reference in New Issue
Block a user