Skip to content

Commit

Permalink
uc query cache support disk
Browse files Browse the repository at this point in the history
  • Loading branch information
YangSen-qn committed Nov 2, 2023
1 parent e95a91f commit 18ba83b
Show file tree
Hide file tree
Showing 5 changed files with 276 additions and 8 deletions.
22 changes: 22 additions & 0 deletions src/main/java/com/qiniu/common/Constants.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package com.qiniu.common;

import java.io.File;
import java.nio.charset.Charset;

/**
Expand Down Expand Up @@ -47,7 +48,28 @@ public final class Constants {
*/
public static final int CONNECTION_POOL_MAX_IDLE_MINUTES = 5;

public static final String CACHE_DIR = getCacheDir();

private Constants() {
}

private static String getCacheDir() {
String tmpDir = System.getProperty("java.io.tmpdir");
if (tmpDir == null || tmpDir.isEmpty()) {
return null;
}

String qiniuDir = tmpDir + "com.qiniu.java-sdk";
File dir = new File(qiniuDir);
if (!dir.exists()) {
return dir.mkdirs() ? qiniuDir : null;
}

if (dir.isDirectory()) {
return qiniuDir;
}

return null;
}
}

31 changes: 24 additions & 7 deletions src/main/java/com/qiniu/storage/AutoRegion.java
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,7 @@
import com.qiniu.common.QiniuException;
import com.qiniu.http.Client;
import com.qiniu.http.Response;
import com.qiniu.util.StringUtils;
import com.qiniu.util.UrlUtils;
import com.qiniu.util.*;

import java.util.ArrayList;
import java.util.Arrays;
Expand All @@ -27,7 +26,9 @@ class AutoRegion extends Region {
/**
* 全局空间信息缓存,此缓存绑定了 token、bucket,全局有效。
*/
private static Map<String, UCRet> globalRegionCache = new ConcurrentHashMap<>();
private static final Cache<UCRet> globalRegionCache = new Cache.Builder<>(UCRet.class)
.setVersion("v1")
.builder();

/**
* 定义HTTP请求管理相关方法
Expand Down Expand Up @@ -75,8 +76,8 @@ private AutoRegion() {
* 通过 API 接口查询上传域名
*/
private UCRet queryRegionInfoFromServerIfNeeded(RegionIndex index) throws QiniuException {
String cacheKey = index.accessKey + index.bucket;
UCRet ret = globalRegionCache.get(cacheKey);
String cacheKey = getCacheId(index);
UCRet ret = globalRegionCache.cacheForKey(cacheKey);
if (ret != null && ret.isValid()) {
return ret;
}
Expand All @@ -94,7 +95,7 @@ private UCRet queryRegionInfoFromServerIfNeeded(RegionIndex index) throws QiniuE
ret = r.jsonToObject(UCRet.class);
if (ret != null) {
ret.setupDeadline();
globalRegionCache.put(cacheKey, ret);
globalRegionCache.cache(cacheKey, ret);
}
return ret;
}
Expand Down Expand Up @@ -122,7 +123,7 @@ static Region regionGroup(UCRet ret) {
*/
private Region queryRegionInfo(String accessKey, String bucket) throws QiniuException {
RegionIndex index = new RegionIndex(accessKey, bucket);
String cacheKey = index.accessKey + "::" + index.bucket;
String cacheKey = getCacheId(index);
Region region = regions.get(cacheKey);

Exception ex = null;
Expand Down Expand Up @@ -311,6 +312,22 @@ String[] getUcHostArray() throws QiniuException {
return getUcHosts(null).toArray(new String[0]);
}

private String getCacheId(RegionIndex index) {
StringBuilder builder = new StringBuilder()
.append(index.accessKey)
.append("-")
.append(index.bucket);

if (ucServers != null && !ucServers.isEmpty()) {
for (String host : ucServers) {
if (host != null && !host.isEmpty()) {
builder.append(host);
}
}
}

return UrlSafeBase64.encodeToString(builder.toString());
}

public Object clone() {
AutoRegion newRegion = new AutoRegion();
Expand Down
179 changes: 179 additions & 0 deletions src/main/java/com/qiniu/util/Cache.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,179 @@
package com.qiniu.util;

import com.qiniu.common.Constants;
import com.qiniu.storage.persistent.FileRecorder;

import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;


/**
* 包含内存缓存和磁盘缓存
* 磁盘缓存会被缓存在一个文件中
*/
public class Cache<T> {

// 当 cache 修改数量到达这个值时,就会 flush,默认是 1
private final int flushCount;

// 缓存被持久化为一个文件,此文件的文件名为 version,version 默认为:v1
private final String version;

// 存储对象的类型
private final Class<T> objectClass;

// 内部
private boolean isFlushing = false;
private int needFlushCount = 0;

private final ConcurrentHashMap<String, T> memCache = new ConcurrentHashMap<>();
private final FileRecorder diskCache;

private Cache(Class<T> objectClass, String cacheDir, int flushCount, String version) {
this.objectClass = objectClass;
this.flushCount = flushCount;
this.version = version;

FileRecorder fileRecorder = null;
try {
if (objectClass != null && cacheDir != null && !cacheDir.isEmpty()) {
fileRecorder = new FileRecorder(cacheDir + "/" + objectClass.getName());
}
} catch (Exception e) {
e.printStackTrace();
}
this.diskCache = fileRecorder;

this.load();
}

private void load() {
if (this.diskCache == null || objectClass == null) {
return;
}

byte[] cacheData = this.diskCache.get(this.version);
if (cacheData == null || cacheData.length == 0) {
return;
}

try {
HashMap<String, Object> cacheJson = Json.decode(new String(cacheData), HashMap.class);
for (String key : cacheJson.keySet()) {
try {
Object jsonMap = cacheJson.get(key);
String jsonString = Json.encode(jsonMap);
T object = Json.decode(jsonString, this.objectClass);
this.memCache.put(key, object);
} catch (Exception e) {
e.printStackTrace();
break;
}
}
} catch (Exception e) {
e.printStackTrace();
this.diskCache.del(this.version);
}
}

public T cacheForKey(String cacheKey) {
return this.memCache.get(cacheKey);
}

public void cache(String cacheKey, T object) {
if (StringUtils.isNullOrEmpty(cacheKey) || object == null) {
return;
}

synchronized (this) {
this.needFlushCount++;
this.memCache.put(cacheKey, object);

if (this.needFlushCount < this.flushCount) {
return;
}

this.needFlushCount = 0;
}

this.flush();
}

private void flush() {
if (this.diskCache == null) {
return;
}

Map<String, T> flushCache = null;
synchronized (this) {
if (this.isFlushing) {
return;
}

this.isFlushing = true;
flushCache = new HashMap<>(this.memCache);
}

if (flushCache.isEmpty()) {
return;
}

String jsonString = Json.encode(flushCache);
if (jsonString == null || jsonString.isEmpty()) {
return;
}

byte[] cacheData = jsonString.getBytes();
if (cacheData.length == 0) {
return;
}

this.diskCache.set(this.version, cacheData);

synchronized (this) {
isFlushing = false;
}
}

public void clearMemoryCache() {
this.memCache.clear();
}

public static class Builder<T> {
// 当 cache 修改数量到达这个值时,就会 flush,默认是 1
private int flushCount = 1;

// 缓存被持久化为一个文件,此文件的文件名为 version,version 默认为:v1
private String version = "v1";

// 存储路径
private String cacheDir = Constants.CACHE_DIR;

// 存储对象的类型
private final Class<T> objectClass;

public Builder(Class<T> objectClass) {
this.objectClass = objectClass;
}

public Builder<T> setFlushCount(int flushCount) {
this.flushCount = flushCount;
return this;
}

public Builder<T> setCacheDir(String cacheDir) {
this.cacheDir = cacheDir;
return this;
}

public Builder<T> setVersion(String version) {
this.version = version;
return this;
}

public Cache<T> builder() {
return new Cache<>(this.objectClass, cacheDir, this.flushCount, this.version);
}
}
}
2 changes: 1 addition & 1 deletion src/test/java/test/com/qiniu/storage/FormUploadTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ public void testSyncRetry() {
public void testHello2() {
TestConfig.TestFile[] files = TestConfig.getTestFileArray();
for (TestConfig.TestFile file : files) {
Configuration config = new Configuration(file.getRegion());
Configuration config = new Configuration();
config.useHttpsDomains = true;
UploadManager uploadManager = new UploadManager(config);
hello(uploadManager, file.getBucketName());
Expand Down
50 changes: 50 additions & 0 deletions src/test/java/test/com/qiniu/util/CacheTest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
package test.com.qiniu.util;

import com.qiniu.util.Cache;
import org.junit.jupiter.api.Test;

import static org.junit.jupiter.api.Assertions.assertEquals;

public class CacheTest {

@Test
public void testCache() {
Info info = new Info();
info.foo = "foo";
info.bar = 1;

String key = "info_key";
Cache cache = new Cache.Builder(Info.class)
.setVersion("v1")
.setFlushCount(1)
.builder();

cache.cache(key, info);


// 1. 测试内存缓存
Info memInfo = (Info) cache.cacheForKey(key);
assertEquals("foo", memInfo.foo);

// 2. 测试删除内存缓存
cache.clearMemoryCache();
memInfo = (Info) cache.cacheForKey(key);
assertEquals(null, memInfo);

// 3. 测试 load
cache = new Cache.Builder(Info.class)
.setVersion("v1")
.setFlushCount(1)
.builder();
memInfo = (Info) cache.cacheForKey(key);
assertEquals("foo", memInfo.foo);
}

static class Info {
String foo;
int bar;

Info(){
}
}
}

0 comments on commit 18ba83b

Please sign in to comment.