优化TTFB 至500ms内
继续上一篇《优化vue+springboot项目页面响应时间:waiting(TTFB) 及content Download》 优化TTFB 至500ms内
目前TTFB 接近1秒。再想优化,需要用到缓存技术,memcached或redis。
暂选择memcached,将查询数据写进内存,从内存中读取。
XMemcachedConfig.java
1 package com.ruoyi.framework.config; 2 3 import com.ruoyi.framework.config.properties.XMemcachedProperties; 4 import net.rubyeye.xmemcached.MemcachedClient; 5 import net.rubyeye.xmemcached.MemcachedClientBuilder; 6 import net.rubyeye.xmemcached.XMemcachedClientBuilder; 7 import net.rubyeye.xmemcached.command.BinaryCommandFactory; 8 import net.rubyeye.xmemcached.impl.KetamaMemcachedSessionLocator; 9 import org.springframework.beans.factory.annotation.Autowired; 10 import org.springframework.context.annotation.Bean; 11 import org.springframework.context.annotation.Configuration; 12 13 @Configuration 14 public class XMemcachedConfig { 15 @Autowired 16 private XMemcachedProperties xMemcachedProperties; 17 18 // 构建builder 19 @Bean 20 public MemcachedClientBuilder getXMemcachedBuilder() throws Exception{ 21 MemcachedClientBuilder memcachedClientBuilder = null; 22 try{ 23 24 if ( !xMemcachedProperties.isOpenCache()) { 25 return null; 26 } 27 String servers = xMemcachedProperties.getServers(); 28 memcachedClientBuilder = new XMemcachedClientBuilder(servers); 29 // 开启/关闭failure模式 30 memcachedClientBuilder.setFailureMode(xMemcachedProperties.isFailureMode()); 31 memcachedClientBuilder.setSanitizeKeys(xMemcachedProperties.isSanitizeKeys()); 32 memcachedClientBuilder.setConnectionPoolSize(xMemcachedProperties.getPoolSize()); 33 memcachedClientBuilder.setCommandFactory(new BinaryCommandFactory()); 34 memcachedClientBuilder.setOpTimeout(xMemcachedProperties.getOperationTimeout()); 35 memcachedClientBuilder.setSessionLocator(new KetamaMemcachedSessionLocator()); 36 37 return memcachedClientBuilder; 38 }catch(Exception e){ 39 e.printStackTrace(); 40 } 41 return null; 42 } 43 44 // client 45 @Bean 46 public MemcachedClient getXMemcachedClient(MemcachedClientBuilder memcachedClientBuilder) throws Exception{ 47 MemcachedClient memcachedClient = null; 48 try{ 49 memcachedClient = memcachedClientBuilder.build(); 50 return memcachedClient; 51 }catch(Exception e){ 52 e.printStackTrace(); 53 } 54 return null; 55 } 56 }
XMemcachedProperties.java
1 package com.ruoyi.framework.config.properties; 2 3 import org.springframework.boot.context.properties.ConfigurationProperties; 4 import org.springframework.stereotype.Component; 5 6 @Component 7 @ConfigurationProperties(prefix = "memcached") 8 public class XMemcachedProperties { 9 10 private String servers; 11 private int poolSize; 12 private boolean isSanitizeKeys; 13 private boolean isOpenCache; 14 private boolean isFailureMode; 15 private long operationTimeout; 16 17 public String getServers() { 18 return servers; 19 } 20 21 public void setServers(String servers) { 22 this.servers = servers; 23 } 24 25 public int getPoolSize() { 26 return poolSize; 27 } 28 29 public void setPoolSize(int poolSize) { 30 this.poolSize = poolSize; 31 } 32 33 public boolean isSanitizeKeys() { 34 return isSanitizeKeys; 35 } 36 37 public void setSanitizeKeys(boolean isSanitizeKeys) { 38 this.isSanitizeKeys = isSanitizeKeys; 39 } 40 41 public boolean isOpenCache() { 42 return isOpenCache; 43 } 44 45 public void setIsOpenCache(boolean isOpenCache) { 46 this.isOpenCache = isOpenCache; 47 } 48 49 public boolean isFailureMode() { 50 return isFailureMode; 51 } 52 53 public void setIsFailureMode(boolean isFailureMode) { 54 this.isFailureMode = isFailureMode; 55 } 56 57 public long getOperationTimeout() { 58 return operationTimeout; 59 } 60 61 public void setOperationTimeout(long operationTimeout) { 62 this.operationTimeout = operationTimeout; 63 } 64 }
XMemcachedCache.java
1 package com.ruoyi.common.core.xmemcached; 2 3 import net.rubyeye.xmemcached.Counter; 4 import net.rubyeye.xmemcached.MemcachedClient; 5 import net.rubyeye.xmemcached.exception.MemcachedException; 6 import org.springframework.beans.factory.annotation.Autowired; 7 import org.springframework.stereotype.Component; 8 9 import java.io.IOException; 10 import java.util.concurrent.TimeoutException; 11 12 /** 13 * @author x8023z 14 */ 15 @Component 16 public class XMemcachedCache { 17 18 @Autowired 19 private MemcachedClient memcachedClient; 20 private static boolean isStatus; 21 22 @Override 23 protected void finalize() 24 { 25 if (isStatus) 26 { 27 try { 28 memcachedClient.shutdown(); 29 } catch (IOException e) { 30 e.printStackTrace(); 31 } 32 } 33 } 34 /** 35 * 永久缓存基本的对象,Integer、String、实体类等 36 * 37 * @param key 缓存的键值 38 * @param value 缓存的值 39 */ 40 public <T> void setCacheObject(final String key, final T value) throws InterruptedException, TimeoutException, MemcachedException { 41 if (memcachedClient != null) { 42 isStatus = memcachedClient.set(key,0, value); 43 } 44 } 45 46 /** 47 * 缓存基本的对象,Integer、String、实体类等 48 * 49 * @param key 缓存的键值 50 * @param value 缓存的值 51 * @param timeout(单位毫秒),设置过期时间,如果expiredTime还未到期,timeout到期,则该memcached过期 52 */ 53 public <T> void setCacheObject(final String key, final T value, final long timeout) throws InterruptedException, TimeoutException, MemcachedException { 54 if (memcachedClient != null) { 55 isStatus = memcachedClient.add(key, 0, value,timeout); 56 } 57 } 58 59 /** 60 * 添加缓存基本的对象,Integer、String、实体类等 61 * 62 * @param key 缓存的键值 63 * @param expiredTime 时间 (单位秒),超过这个时间,memcached将这个数据替换出去,0表示永久存储(默认是一个月) 64 * @param value 缓存的值 65 * @param timeout(单位毫秒),设置过期时间,如果expiredTime还未到期,timeout到期,则该memcached过期 66 */ 67 public <T> void setCacheObject(final String key, final Integer expiredTime, final T value, final long timeout) throws InterruptedException, TimeoutException, MemcachedException { 68 if (memcachedClient != null ) { 69 isStatus = memcachedClient.add(key, expiredTime, value,timeout); 70 } 71 } 72 73 74 /** 75 * 替换缓存基本的对象,Integer、String、实体类等,注意类型是Object(统计切勿使用replace) 76 * 77 * @param key 缓存的键值 78 * @param expiredTime 时间 (单位秒),超过这个时间,memcached将这个数据替换出去,0表示永久存储(默认是一个月) 79 * @param value 缓存的值 80 */ 81 public <T> void replaceCacheObject(final String key, final Integer expiredTime, final T value) throws InterruptedException, TimeoutException, MemcachedException { 82 if (memcachedClient != null) { 83 isStatus = memcachedClient.replace(key, expiredTime, value); 84 } 85 } 86 87 /** 88 * 替换缓存基本的对象,Integer、String、实体类等 89 * 90 * @param key 缓存的键值 91 * @param expiredTime 时间 (单位秒),超过这个时间,memcached将这个数据替换出去,0表示永久存储(默认是一个月) 92 * @param value 缓存的值 93 * @param timeout(单位毫秒),设置过期时间,如果expiredTime还未到期,timeout到期,则该memcached过期 94 */ 95 public <T> void replaceCacheObject(final String key, final Integer expiredTime, final T value, final long timeout) throws InterruptedException, TimeoutException, MemcachedException { 96 if (memcachedClient != null) { 97 isStatus = memcachedClient.replace(key, expiredTime, value,timeout); 98 } 99 } 100 101 /** 102 * 永久替换缓存基本的对象,Integer、String、实体类等 103 * 104 * @param key 缓存的键值 105 * @param value 缓存的值 106 */ 107 public <T> void replaceCacheObject(final String key, final T value) throws InterruptedException, TimeoutException, MemcachedException { 108 if (memcachedClient != null) { 109 isStatus = memcachedClient.replace(key,0, value); 110 } 111 } 112 113 /** 114 * 获得缓存的基本对象。 115 * 116 * @param key 缓存键值 117 * @return 缓存键值对应的数据 118 */ 119 public <T> T getCacheObject(final String key) throws InterruptedException, TimeoutException, MemcachedException { 120 if (memcachedClient != null) { 121 return memcachedClient.get(key); 122 } else { 123 return null; 124 } 125 } 126 127 /** 128 * 删除缓存的基本对象。 129 * 130 * @param key 缓存键值 131 */ 132 public <T> void deleteCacheObject(final String key) throws InterruptedException, TimeoutException, MemcachedException { 133 if (memcachedClient != null) { 134 isStatus = memcachedClient.delete(key); 135 } 136 } 137 138 139 public <T> long getStats(final String key) throws InterruptedException, MemcachedException, TimeoutException { 140 long reCount = -1; 141 Counter counter = null; 142 //第二个参数是计数器的初始值 143 if (memcachedClient != null) { 144 counter = memcachedClient.getCounter(key,-1); 145 } 146 reCount = counter.get(); 147 //使用count时实质是在创建一个key,因此需要将这个key清除掉 148 if(reCount == -1){ 149 deleteCacheObject(key); 150 } 151 return reCount; 152 } 153 154 155 /** 156 * 计数器累加extentOfIncrement 157 * @param key 键 158 * @param extentOfIncrement 递增的幅度大小 159 * @param original key不存在的情况下的初始值 160 * @return 计数 161 */ 162 public <T> long addStats(String key, long extentOfIncrement, long original) throws InterruptedException, MemcachedException, TimeoutException { 163 long reCount = -1; 164 Counter counter = null; 165 if (memcachedClient != null) { 166 counter = memcachedClient.getCounter(key, original); 167 } 168 counter.set(extentOfIncrement+original); 169 reCount = counter.incrementAndGet(); 170 return reCount; 171 } 172 173 public <T> boolean getStatus() 174 { 175 return isStatus; 176 } 177 178 179 180 }
application.yml
# memcached配置 memcached: servers: 127.0.0.1:11211 poolSize: 10 #是否启用url encode 机制 sanitizeKeys: false # true为启动缓存 false为标准实现 isOpenCache: true #是否开启失败模式,默认为false isFailureMode: false #接口操作的默认超时时间,可以被接口覆盖 operationTimeout: 3000
修改优化之处
1 @Autowired 2 private XMemcachedCache memcachedcache; 3 4 /** 5 * 项目启动时,初始化参数到缓存,定时任务,每隔一分钟刷新缓存 6 */ 7 @PostConstruct 8 public void init() throws InterruptedException, TimeoutException, MemcachedException { 9 loadingSysStreetCache(); 10 } 11 12 /** 13 * 设置cache key 14 * 15 * @param sysStreetKey 参数键 16 * @return 缓存键key 17 */ 18 private String getSysStreetKey(String sysStreetKey) 19 { 20 return Constants.SYS_STREET_KEY + sysStreetKey; 21 } 22 23 /** 24 * 加载所有省市区镇级缓存数据 25 */ 26 27 28 public List<TreeNodeDict> loadingSysStreetCache() throws InterruptedException, TimeoutException, MemcachedException { 29 List<SysStreet> list = streetService.selectStreetListView(new SysStreet()); 30 TreeDataConvertUtils tree = new TreeDataConvertUtils(); 31 List<TreeNodeDict> collect = list.stream() 32 .map(a-> new TreeNodeDict(a.getCode(), a.getName(), a.getProvinceCode())) 33 .collect(Collectors.toList()); 34 35 // 后台返回非树结构的list,前端处理慢 36 // return AjaxResult.success(list); 37 // 后台返回树结构list,前端处理快 38 // return AjaxResult.success(tree.convertToTreeUseMap(collect,"1")); 39 40 List<TreeNodeDict> treeNodes =tree.convertToTreeUseMap(collect,"1"); 41 // 序列化 42 if (memcachedcache != null) { 43 memcachedcache.setCacheObject(getSysStreetKey(""), JSON.toJSONString(treeNodes)); 44 } 45 return treeNodes; 46 }
// 注意使用之时,返回前端需要反序列化
public AjaxResult listview(SysStreet street) throws InterruptedException, TimeoutException, MemcachedException {
List<TreeNodeDict> listTreeNodeDict = null;
if (memcachedcache != null && memcachedcache.getStatus() ) {
// 反序列化
listTreeNodeDict = JSON.parseArray(memcachedcache.getCacheObject(getSysStreetKey("")), TreeNodeDict.class);
}
if (listTreeNodeDict == null || listTreeNodeDict.size() ==0 )
{
listTreeNodeDict = loadingSysStreetCache();
}
return AjaxResult.success(listTreeNodeDict);
}
优化结果: