单机想突破性能瓶颈,即使用上Redis,ES这些缓存神器,但是也无法突破网络操作的耗时瓶颈,项目中的基础数据可考虑堆外缓存,整体提升系统吞吐,同时又不用担心GC导致JVM响应变慢,系统接口也跟着变慢。
package com.iamdog.rest;
import com.alibaba.fastjson.JSON;
import javafx.util.Pair;
import org.caffinitas.ohc.OHCache;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.List;
/**
* OHC堆外内存管理接口
*
* @author iamdog
*/
@RestController
@RequestMapping(value = "/iamdog/ohc")
public class IamDogOHCManageController {
@Resource(name="dogOHCache")
private OHCache ohCache;
@RequestMapping(value = "/get.app.do")
public String get(@RequestBody String key) {
return (String)ohCache.get(key);
}
@RequestMapping(value = "/batchGet.app.do")
public String batchGet(@RequestBody List<String> keys) {
List<Pair> results=new ArrayList<>();
keys.stream().forEach(key->{
String value=(String)ohCache.get(key);
Pair p=new Pair(key,value);
results.add(p);
});
return JSON.toJSONString(results);
}
@RequestMapping(value = "/set.app.do")
public String set(@RequestBody Pair pair) {
ohCache.put(pair.getKey(),pair.getValue());
return "OK";
}
@RequestMapping(value = "/batchSet.app.do")
public String batchSet(@RequestBody List<Pair> pairs) {
pairs.stream().forEach(pair->{
ohCache.put(pair.getKey(),pair.getValue());
});
return "OK";
}
@RequestMapping(value = "/remove.app.do")
public String remove(@RequestBody String key) {
ohCache.remove(key);
return "OK";
}
@RequestMapping(value = "/batchRemove.app.do")
public String batchRemove(@RequestBody List<String> keys) {
keys.stream().forEach(key->{
ohCache.remove(key);
});
return "OK";
}
@RequestMapping(value = "/clear.app.do")
public String clear() {
ohCache.clear();
return "OK";
}
@RequestMapping(value = "/status.app.do")
public String status() {
ohCache.clear();
return "Capacity:"+ohCache.capacity()/1024/1024+"MB,"
+ "freeCapacity:"+ohCache.freeCapacity()/1024/1024+"MB";
}
}
堆外内存配置类:
package com.iamdog.config;
import com.iamdog.cache.MyOHCStringSerializer;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.caffinitas.ohc.Eviction;
import org.caffinitas.ohc.HashAlgorithm;
import org.caffinitas.ohc.OHCache;
import org.caffinitas.ohc.OHCacheBuilder;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.core.StringRedisTemplate;
import javax.annotation.Resource;
import java.util.concurrent.TimeUnit;
/**
* @Author: iamdog
* @Description: 堆外内存OHC配置
* @Date: Created At 2021-10-18 11:16
* @Modified By:
*/
@Slf4j
@Configuration
public class MyOHCConfig {
@Value("${iamdog.ohc.capacity}")
private int capacity;
@Value("${iamdog.ohc.ttl}")
private int ttl;
/**
* @Author: iamdog
* @Description:
* OHC介绍文档https://github.com/snazy/ohc
* @Date: Created At 2021-10-18 14:51
*/
@Bean("dogOHCache")
public OHCache getOHCache(){
return OHCacheBuilder.<String, String>newBuilder()
.keySerializer(new MyOHCStringSerializer())
.valueSerializer(new MyOHCStringSerializer())
.hashMode(HashAlgorithm.CRC32C)
//单位是字节,默认2GB空间
.capacity(capacity*1024*1024*1024L)
.timeouts(true)
.defaultTTLmillis(ttl*1000)
//"LRU" 最近最少使用的, "W_TINY_LFU" 使用频率较低的条目被逐出
.eviction(Eviction.W_TINY_LFU)
.build();
}
}
序列化类:
package com.iamdog.cache;
import org.caffinitas.ohc.CacheSerializer;
import java.nio.ByteBuffer;
import java.nio.charset.StandardCharsets;
/**
* @Author: iamdog
* @Description:
* @Date: Created At 2021-10-18 16:43
* @Modified By:
*/
public class MyOHCStringSerializer implements CacheSerializer<String> {
/**
* 计算字符串序列化后占用的空间
*
* @param value 需要序列化存储的字符串
* @return 序列化后的字节数
*/
@Override
public int serializedSize(String value) {
byte[] bytes = value.getBytes(StandardCharsets.UTF_8);
// 设置字符串长度限制,2^16 = 65536 8KB
if (bytes.length > 65536) {
throw new RuntimeException("encoded string too long: " + bytes.length + " bytes");
}
// 设置字符串长度限制,2^16 = 65536
return bytes.length + 2;
}
/**
* 将字符串对象序列化到 ByteBuffer 中,ByteBuffer是OHC管理的堆外内存区域的映射。
*
* @param value 需要序列化的对象
* @param buf 序列化后的存储空间
*/
@Override
public void serialize(String value, ByteBuffer buf) {
// 得到字符串对象UTF-8编码的字节数组
byte[] bytes = value.getBytes(StandardCharsets.UTF_8);
// 用前16位记录数组长度
buf.put((byte) ((bytes.length >>> 8) & 0xFF));
buf.put((byte) ((bytes.length) & 0xFF));
buf.put(bytes);
}
/**
* 对堆外缓存的字符串进行反序列化
*
* @param buf 字节数组所在的 ByteBuffer
* @return 字符串对象.
*/
@Override
public String deserialize(ByteBuffer buf) {
// 判断字节数组的长度
int length = (((buf.get() & 0xff) << 8) + ((buf.get() & 0xff)));
byte[] bytes = new byte[length];
// 读取字节数组
buf.get(bytes);
// 返回字符串对象
return new String(bytes, StandardCharsets.UTF_8);
}
}