JUC 之 ThreadLocal 的运用
2023-10-23 本文已影响0人
Tinyspot
1. 会话管理器
/**
* 会话管理器
*/
@Slf4j
public class SessionFactory {
private static ThreadLocal<SessionContext> threadLocal = new ThreadLocal<>();
public static SessionContext getSession() {
return threadLocal.get();
}
public static SessionContext createSession(String entryName, String id) {
if(threadLocal.get() == null){
try {
threadLocal.set(new SessionContext(entryName, id));
} catch (Exception e) {
}
}
log.info("threadLocal.get(): " + threadLocal.get());
return threadLocal.get();
}
/**
* 谁创建谁销毁
*/
public static void destroySession(String entryName) {
if (threadLocal.get() == null) {
return;
}
if(entryName != null && entryName.equals(threadLocal.get().getEntryName())) {
threadLocal.set(null);
} else if (threadLocal.get().getEntryName() == null) {
threadLocal.set(null);
}
}
}
/**
* 会话上下文
*/
@Data
public class SessionContext implements Serializable {
private static final long serialVersionUID = -5118360132451003980L;
/**
* 会话Id
*/
private String id;
/**
* 会话入口方法
*/
private Method entrydMethod;
/**
* 会话入口名称
*/
private String entryName;
private String identityCode;
private String sceneCode;
/**
* 会话属性
*/
private Map<String, Object> attributeMap = new HashMap<>();
public SessionContext() {
this.id = UUID.randomUUID().toString().replaceAll("-", "");
}
public SessionContext(String entryName, String id) {
if (StringUtils.isNotBlank(id)) {
this.id = id;
} else {
this.id = UUID.randomUUID().toString().replaceAll("-", "");
}
this.entryName = entryName;
}
public void setAttribute(String name, Object value) {
attributeMap.put(name, value);
}
public void removeAttribute(String name) {
attributeMap.remove(name);
}
}
2. 工具类
2.1 SessionUtil
public class SessionUtil {
public static void setIdentityCode(String identityCode) {
SessionContext session = SessionFactory.getSession();
if(session != null) {
session.setIdentityCode(identityCode);
}
}
public static void setSceneCode(String sceneCode) {
SessionContext session = SessionFactory.getSession();
if(session != null) {
session.setSceneCode(sceneCode);
}
}
/**
* session属性值
*/
public static void setAttributes(OrderDTO orderDTO) {
if (orderDTO == null) {
return;
}
setTradeId(orderDTO.getTradeId());
// other...
}
public static void setTradeId(String tradeId) {
setAttribute("tradeId", tradeId);
}
public static void setAttribute(String key, Object value) {
if(key != null && value != null) {
SessionContext session = SessionFactory.getSession();
if(session != null) {
session.setAttribute(key, value);
}
}
}
}
2.2 LocalSessionUtil
public class LocalSessionUtil {
/**
* 内部通用会话id
*/
public static String getInnerSessionId() {
String remoteSessionId = LocalSessionUtil.getRemoteSessionId();
if (StringUtils.isNotBlank(remoteSessionId)) {
return remoteSessionId;
}
return initLocalSessionId();
}
/**
* 获取 RPC 的 session id
*/
public static String getRemoteSessionId() {
// todo
return "";
}
/**
* 生成随机 sessionId
*
* ip + 毫秒数 + 随机UUID
* 8位(16进制) 取6位 取10位
*/
public static String initLocalSessionId() {
return IpUtils.getHexIp()
+ getMilliseconds()
+ generatorId(10);
}
public static String getGeneratorId() {
return UUID.randomUUID().toString().replaceAll("-", "");
}
private static String generatorId(int length) {
return getGeneratorId().substring(0, length);
}
private static String getMilliseconds() {
long nowTime = System.currentTimeMillis();
return String.valueOf(nowTime % 1000000L);
}
}
2.3 IpUtils
import java.net.InetAddress;
public class IpUtils {
private static final String IP_SPLIT_TAG = "\\.";
private static String cacheIp = null;
public static String getLocalIp() {
try {
if(cacheIp == null) {
cacheIp = InetAddress.getLocalHost().getHostAddress();
}
} catch (Throwable t) {
cacheIp = "unknown_ip";
}
return cacheIp;
}
/**
* 返回十六进制的IP
* @return
*/
public static String getHexIp() {
StringBuilder builder = new StringBuilder();
String[] ipSplits = IpUtils.getLocalIp().split(IP_SPLIT_TAG);
for (String segment : ipSplits) {
String hex = Integer.toHexString(Integer.parseInt(segment));
// ip为个位数时,手动补0
if (hex.length() < 2) {
builder.append("0");
}
builder.append(hex);
}
return builder.toString();
}
}
3. 调用示例
3.1 ThreadLocalController
调用地址 http://localhost:8080/thread/threadLocal
@RestController
@RequestMapping("/thread")
public class ThreadLocalController {
private static Logger logger = LoggerFactory.getLogger(ThreadLocalController.class);
@Resource
private SceneTemplate sceneTemplate;
@RequestMapping("/threadLocal")
public String threadLocal() {
SingleResultDTO<OrderDTO> result = sceneTemplate.execute(new SceneCallback<OrderDTO>() {
@Override
public String getIdentityCode() {
return "local";
}
@Override
public String getSceneCode() {
return "threadLocal";
}
@Override
public SingleResultDTO<OrderDTO> executeScene(String identityCode, String sceneCode) {
SingleResultDTO<OrderDTO> resultDTO = SingleResultDTO.successResult(new OrderDTO("1001", "20201010"));
return resultDTO;
}
}, logger);
return JSON.toJSONString(result);
}
}
3.2 模板
public interface SceneTemplate<R> {
SingleResultDTO<R> execute(SceneCallback<R> action, Logger logger);
}
public interface SceneCallback<R> {
String getIdentityCode();
String getSceneCode();
SingleResultDTO<R> executeScene(String identityCode, String sceneCode);
}
@Data
public class SingleResultDTO<T> implements Serializable {
private static final long serialVersionUID = -1599951090724597173L;
private T result;
private String errorCode;
private String errorDesc;
private Boolean success;
public static <T> SingleResultDTO<T> successResult(T data) {
SingleResultDTO<T> result = new SingleResultDTO<T>();
result.setResult(data);
result.setSuccess(true);
return result;
}
}
3.3 SceneTemplateImpl
@Component
@Slf4j
public class SceneTemplateImpl<R> implements SceneTemplate<R> {
@Override
public SingleResultDTO<R> execute(SceneCallback<R> action, Logger logger) {
SingleResultDTO<R> result = new SingleResultDTO<>();
StringBuilder builder = new StringBuilder();
long startTime = System.currentTimeMillis();
String identityCode = null;
String sceneCode = null;
String sessionEntryName = null;
try {
// do something...
identityCode = action.getIdentityCode();
sceneCode = action.getSceneCode();
// 初始化session
if (Objects.isNull(SessionFactory.getSession())) {
// 暂定用 UUID 作为会话名称
// sessionEntryName = Thread.currentThread().getName() + "@" + LocalSessionUtil.getGeneratorId();
sessionEntryName = LocalSessionUtil.getGeneratorId();
SessionFactory.createSession(sessionEntryName, LocalSessionUtil.getInnerSessionId());
}
SessionUtil.setIdentityCode(identityCode);
SessionUtil.setSceneCode(sceneCode);
// 正式执行
result = action.executeScene(identityCode, sceneCode);
builder.append(identityCode).append("#").append(sceneCode).append("#");
long duration = System.currentTimeMillis() - startTime;
builder.append(duration).append("ms");
logger.info(builder.toString());
} catch (Throwable e) {
long duration = System.currentTimeMillis() - startTime;
builder.append(duration).append("ms");
logger.error(builder.toString());
} finally {
// do something...
if (StringUtils.isNotBlank(sessionEntryName)) {
SessionFactory.destroySession(sessionEntryName);
}
}
return result;
}
}