博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
基于spring4.0配置分布式ehcache,以及相关使用
阅读量:6308 次
发布时间:2019-06-22

本文共 21192 字,大约阅读时间需要 70 分钟。

说明:本文是基于RMI手动同步的方式,使用程序动态注入配置缓存,抛弃传统的ehcache.xml配置方式

 

1,注入cacheManager管理所有缓存,添加各个缓存名及相关参数配置:

思路大致是:

在项目的主配置类中,通过@Bean注入cacheManager,统一管理所有cache,传统的用xml方式配置太累赘,其实只需要更该chacheName,其他参数变化不是很大,考虑用程序封装做到最大程度的代码重用所以采取拼接JSON串的形式,配置缓存名,及所有相关需要设置好的参数。然后通过解析JSON,拼接成带主机IP的RmiUrl,以manual手动rmi同步的方式配置peerDiscovery成员发现,再通过配置cacheManagerPeerListenerFactory这个类启动本机监听程序,来发现其他主机发来的同步请求。最后再用@EnableCaching注解开启spring支持对ehcache注解使用

下面是项目配置好的详细代码:

1 package com.cshtong.tower.web.init;  2   3 import java.io.IOException;  4 import java.io.InputStream;  5 import java.io.UncheckedIOException;  6 import java.net.InetAddress;  7 import java.net.UnknownHostException;  8 import java.util.ArrayList;  9 import java.util.Iterator; 10 import java.util.List; 11 import java.util.Properties; 12  13 import javax.annotation.Resource; 14  15 import org.slf4j.Logger; 16 import org.slf4j.LoggerFactory; 17 import org.springframework.cache.annotation.EnableCaching; 18 import org.springframework.cache.ehcache.EhCacheCacheManager; 19 import org.springframework.cache.interceptor.KeyGenerator; 20 import org.springframework.cache.interceptor.SimpleKeyGenerator; 21 import org.springframework.context.annotation.Bean; 22 import org.springframework.context.annotation.Configuration; 23 import org.springframework.transaction.annotation.EnableTransactionManagement; 24  25 import com.alibaba.fastjson.JSONArray; 26 import com.alibaba.fastjson.JSONObject; 27 import com.cshtong.tower.service.UserService; 28 import com.cshtong.tower.util.StringUtil; 29  30 import net.sf.ehcache.Cache; 31 import net.sf.ehcache.CacheException; 32 import net.sf.ehcache.CacheManager; 33 import net.sf.ehcache.config.CacheConfiguration; 34 import net.sf.ehcache.config.DiskStoreConfiguration; 35 import net.sf.ehcache.config.FactoryConfiguration; 36 import net.sf.ehcache.distribution.RMICacheManagerPeerListenerFactory; 37 import net.sf.ehcache.distribution.RMICacheManagerPeerProviderFactory; 38 import net.sf.ehcache.distribution.RMICacheReplicatorFactory; 39 import net.sf.ehcache.store.MemoryStoreEvictionPolicy; 40  41 @EnableCaching 42 @Configuration 43 @EnableTransactionManagement 44 public class EhcacheConfig extends RMICacheReplicatorFactory{ 45      46     private static Logger logger = LoggerFactory.getLogger(EhcacheConfig.class); 47     private static final String EHCACHE_PROPERTIES = "ehcache.properties"; 48      49     /** 最大缓存数量 0 = no limit. **/ 50     private static final Integer MAX_CACHE = 0; 51     /** 缓存失效时间间隔/秒  **/ 52     private static final Integer TIME_TOLIVE_SECONDS = 24*60*60; 53     /** 缓存闲置多少秒后自动销毁  **/ 54     private static final Integer TIME_TOIDLE_SECONDS = 60*60; 55     /** 磁盘失效线程运行时间间隔/秒。 **/ 56     private static final Integer DISK_EXPIRY_Thread_INTERVAL_SENCONDS = 100; 57      58     @Resource 59     UserService userService; 60      61     /** 注入cacheManager **/ 62     @Bean 63     public org.springframework.cache.CacheManager cacheManager() { 64         org.springframework.cache.CacheManager cm = new EhCacheCacheManager(putCache()); 65         return cm; 66     } 67  68     @Bean 69     public KeyGenerator keyGenerator() { 70         return new SimpleKeyGenerator(); 71     } 72      73     @Bean 74     public CacheManager putCache(){ 75          76         String rmiUrls = initRmiURLs(); 77          78         net.sf.ehcache.config.Configuration cf = new net.sf.ehcache.config.Configuration(); 79         Properties pro = initRmiUrlsProperties(); 80         if (null!=rmiUrls) { 81             /** 临时文件目录 **/ 82             cf.diskStore(new DiskStoreConfiguration().path("java.io.tmpdir")) 83             /**成员发现 peerDiscovery 方式:manual:手动,rmiUrls:主机名+端口号+缓存名,用来接受或者发送信息的接口,不同的缓存或者机器用“|”隔开 **/   84             .cacheManagerPeerProviderFactory(new FactoryConfiguration
>() 85 .className(RMICacheManagerPeerProviderFactory.class.getName()) 86 .properties("peerDiscovery=manual,rmiUrls=" + rmiUrls) 87 ); 88 /** 本机监听程序,来发现其他主机发来的同步请求 hostName=192.168.1.112 这里默认是本机可以不配置 **/ 89 cf.cacheManagerPeerListenerFactory(new FactoryConfiguration
>() 90 .className(RMICacheManagerPeerListenerFactory.class.getName()) 91 .properties("port="+ pro.getProperty("rmiPortNumber") +",socketTimeoutMillis=2000") 92 ); 93 }else{ 94 logger.debug("The rmiUrls is null!!"); 95 } 96 97 CacheManager cm = null; 98 99 try {100 cm = CacheManager.create(cf);101 } catch (UncheckedIOException e) {102 logger.debug("Fail to load config. message:{}", e.getMessage());103 }104 105 List
array = null;106 try {107 array = cacheCollection();108 } catch (CacheException e) {109 logger.error("collect cache Failed");110 }111 112 for (Cache list:array) {113 cm.addCache(list);114 }115 116 return cm;117 }118 119 public static List
cacheCollection(){120 String listParameters = cacheParametersCollection();121 return cacheConf(listParameters);122 }123 124 /**125 * 添加缓存的参数126 * 相关属性: 127 name : "缓存名称,cache的唯一标识(ehcache会把这个cache放到HashMap里) 128 maxElementsInMemory : 缓存最大个数,0没有限制。129 eternal="false" : 对象是否永久有效,一但设置了,timeout将不起作用。 (必须设置)130 maxEntriesLocalHeap="1000" : 堆内存中最大缓存对象数,0没有限制(必须设置)131 maxEntriesLocalDisk= "1000" : 硬盘最大缓存个数。 132 overflowToDisk="false" : 当缓存达到maxElementsInMemory值是,是否允许溢出到磁盘(必须设设置)(内存不足时,是否启用磁盘缓存。)133 diskSpoolBufferSizeMB : 这个参数设置DiskStore(磁盘缓存)的缓存区大小。默认是30MB。每个Cache都应该有自己的一个缓冲区。 134 diskPersistent="false" : 磁盘缓存在JVM重新启动时是否保持(默认为false)硬盘持久化135 timeToIdleSeconds="0" : 导致元素过期的访问间隔(秒为单位),即当缓存闲置n秒后销毁。 当eternal为false时,这个属性才有效,0表示可以永远空闲,默认为0136 timeToLiveSeconds="0" : 元素在缓存里存在的时间(秒为单位),即当缓存存活n秒后销毁. 0 表示永远存在不过期137 memoryStoreEvictionPolicy="LFU" : 当达到maxElementsInMemory时,如何强制进行驱逐默认使用"最近使用(LRU)"策略,其它还有先入先出FIFO,最少使用LFU,较少使用LRU138 diskExpiryThreadIntervalSeconds :磁盘失效线程运行时间间隔,默认是120秒。139 clearOnFlush : 内存数量最大时是否清除。 140 141 cacheEventListenerFactory : 给缓存添加监听142 replicateAsynchronously=true : 异步的方式143 replicatePuts=true,replicateUpdates=true,replicateUpdatesViaCopy=true,replicateRemovals= true : 在put,update,copy,remove操作是否复制144 cationIntervalMillis=1000 : 同步时间1s145 bootstrapCacheLoaderFactory 启动是指一启动就同步数据146 * @return147 */148 public static String cacheParametersCollection(){149 150 String listParameters = "[" ;151 152 /** 排班:列表详情 **/153 listParameters += "{'cacheName':'schedule_listPatrol','maxEntriesLocalHeap':'0'}";154 155 /** APP:用户当月的排班 **/156 listParameters += "{'cacheName':'owner_schedule','maxEntriesLocalHeap':'0'}";157 158 /**考勤统计 :echarts图表相关数据**/159 listParameters += "{'cacheName':'attendance_findByOrgIds','maxEntriesLocalHeap':'0'}";160 /**考勤统计 :主界面表格相关的数据**/161 listParameters += "{'cacheName':'attendance_findByOrgIdsAndPage','maxEntriesLocalHeap':'0','timeToIdleSeconds':'0'}";162 163 /** 机构 **/164 listParameters += "{'cacheName':'org_findAll','maxEntriesLocalHeap':'0'}";165 166 /** 用户信息 key=userId **/167 listParameters += "{'cacheName':'list_userInfo','maxEntriesLocalHeap':'0'}";168 169 /** APP 勤务圈 **/170 listParameters += "{'cacheName':'app_message','maxElementsInMemory':'0','maxEntriesLocalHeap':'0'}";171 172 /** 报警,最近1km内的用户 **/173 listParameters += "{'cacheName':'rescue_users','maxElementsInMemory':'0','maxEntriesLocalHeap':'0'}";174 175 /** 报警,最近1km内的用户 **/176 listParameters += "{'cacheName':'app_message_typeName','maxElementsInMemory':'0','maxEntriesLocalHeap':'0'}";177 178 listParameters += "]";179 180 return listParameters;181 }182 183 /**184 * 添加成员发现: //主机ip+端口号/185 * @return186 */187 public static List
cacheManagerPeerProviderCollection(){188 Properties pro = initRmiUrlsProperties();189 190 List
ip = new ArrayList<>();191 try {192 ip.add(pro.get("machine1").toString());193 ip.add(pro.get("machine2").toString());194 } catch (Exception e) {195 logger.error("Fail to provider cacheManagerPeer. config file [{}], message:{}", EHCACHE_PROPERTIES, e.getMessage());196 }197 198 InetAddress ia;199 try {200 ia = InetAddress.getLocalHost();201 String localip = ia.getHostAddress();202 for (int i = 0; i < ip.size(); i++) {203 /** 过滤本机ip **/204 if (localip.equalsIgnoreCase(ip.get(i))) {205 ip.remove(i);206 }207 }208 } catch (UnknownHostException Host) {209 Host.printStackTrace();210 logger.error("Unknown to host Address. config file [{}], message:{}", EHCACHE_PROPERTIES, Host.getMessage());211 }212 213 List
peer = new ArrayList<>();214 for (int j = 0; j < ip.size(); j++) {215 peer.add("//" + ip.get(j) + ":" + pro.getProperty("rmiPortNumber").toString());216 }217 218 return peer;219 }220 221 public static String initRmiURLs(){222 String rmiUrls = "";223 String listParameters = cacheParametersCollection();224 JSONArray array = initCacheParameters(listParameters);225 for (Iterator
iterator = array.iterator(); iterator.hasNext();) {226 JSONObject obj = (JSONObject)iterator.next();227 String cacheName = obj.get("cacheName").toString();228 List
peer = cacheManagerPeerProviderCollection();229 for (String list:peer) {230 rmiUrls += list + cacheName + "|";231 }232 }233 if (!"".equals(rmiUrls)) {234 rmiUrls = rmiUrls.substring(0,rmiUrls.length()-1);235 }236 return rmiUrls;237 }238 239 public static JSONArray initCacheParameters(String listParameters){240 JSONArray array = null;241 try {242 array = JSONArray.parseArray(listParameters);243 } catch (Exception e) {244 logger.error("Fail to init The cache parameters. message:{}", e.getMessage());245 }246 return array;247 }248 249 public static Properties initRmiUrlsProperties(){250 InputStream resourcesStream = EhcacheConfig.class.getClassLoader().getResourceAsStream(EHCACHE_PROPERTIES);251 Properties pro = new Properties();252 try {253 pro.load(resourcesStream);254 } catch (IOException e) {255 logger.error("Fail to load config. config file [{}], message:{}", EHCACHE_PROPERTIES, e.getMessage());256 }257 return pro;258 }259 260 /**261 * @param listPatrolParameters 缓存参数JSON数组262 * @return 缓存的集合263 */264 @SuppressWarnings("deprecation")265 public static List
cacheConf(String listParameters){266 267 List
listCache = new ArrayList<>();268 JSONArray array = initCacheParameters(listParameters);269 for (Iterator
iterator = array.iterator(); iterator.hasNext();) {270 JSONObject obj = (JSONObject)iterator.next();271 272 String cacheName = obj.get("cacheName").toString();273 String maxElementsInMemory = obj.getString("maxElementsInMemory");274 String maxEntriesLocalHeap = obj.getString("maxEntriesLocalHeap");275 String timeToLiveSeconds = obj.getString("timeToLiveSeconds");276 String timeToIdleSeconds = obj.getString("timeToIdleSeconds");277 278 RMICacheReplicatorFactory rmi = new RMICacheReplicatorFactory();279 Properties pro = initRmiUrlsProperties();280 rmi.createCacheEventListener(pro);281 282 CacheConfiguration cacheConfiguration = new CacheConfiguration(cacheName,StringUtil.isNull(maxEntriesLocalHeap)?MAX_CACHE:Integer.parseInt(maxEntriesLocalHeap))283 .memoryStoreEvictionPolicy(MemoryStoreEvictionPolicy.LFU) 284 .maxElementsInMemory(StringUtil.isNull(maxElementsInMemory)?MAX_CACHE:Integer.parseInt(maxElementsInMemory))285 .overflowToDisk(true) 286 .eternal(false) 287 .timeToLiveSeconds(StringUtil.isNull(timeToLiveSeconds)?TIME_TOLIVE_SECONDS:Integer.parseInt(timeToLiveSeconds)) 288 .timeToIdleSeconds(StringUtil.isNull(timeToIdleSeconds)?TIME_TOIDLE_SECONDS:Integer.parseInt(timeToIdleSeconds)) 289 .diskPersistent(false) 290 .diskExpiryThreadIntervalSeconds(DISK_EXPIRY_Thread_INTERVAL_SENCONDS)291 .clearOnFlush(true)292 .cacheEventListenerFactory(new CacheConfiguration.CacheEventListenerFactoryConfiguration().className(RMICacheReplicatorFactory.class.getName()));293 Cache cache = new Cache(cacheConfiguration);294 295 listCache.add(cache);296 }297 298 return listCache;299 }300 }

 

properties文件:

1 #RMICacheReplicatorFactory properties 2 replicateAsynchronously=true 3 replicatePuts=true 4 replicatePutsViaCopy=false 5 replicateUpdates=true 6 replicateUpdatesViaCopy=false 7 replicateRemovals=true 8 asynchronousReplicationIntervalMillis=1000 9 asynchronousReplicationMaximumBatchSize=100010 11 #RMI URLs 12 machine1=//主机ip+端口号/13 14 #RMI port 15 rmiPortNumber=8010

到这里就注入好了缓存名为listParameters里面cacheName的所有缓存,如果后续添加或修改缓存,只需更改listParameters的相关属性,如果在集群环境,也只需在porperties文件中添加machine..配置即可。

 

2,基于注解方式的缓存使用:

1,为方便重用所有缓存建议在service层使用,当方法第一次执行时将返回值以key-value对象写进缓存,之后在执行该方法时,如果缓存的condition满足则直接取缓存返回,实际上方法都不会进!

 

2,在修改或添加方法使用@CachePut,查询方法使用@Cacheable,删除方法使用@CacheEvict注意:缓存一定要配合使用,例如一个list方法将相应值缓存起来,如果有针对该值CUD操作时,一定要将新的返回值@CachePut,否则会出现数据脏读的情况!如果更新或修改方法的返回值与list不相同即该缓存@CacheEvict,否则会报数据映射错误!

 

3,注解说明:

 

@Cacheable (value="缓存值/SpEL/不填默认返回值", key="缓存key/SpEL") : 应用到读取数据的方法上,即可缓存的方法,如查找方法:先从缓存中读取,如果没有再调用方法获取数据,然后把数据添加到缓存中。如图:

 

 

 

 

@CachePut 应用到写数据的方法上,如新增/修改方法,调用方法时会自动把相应的数据放入缓存: 如图:

 

 

 

 

@CacheEvict: 移除数据,如图:

 

 

 

 

@Caching 组合多个Cache注解使用,如图:

 

 

 

注解参数说明:

value:必须指定,其表示当前方法的返回值是会被缓存在哪个Cache上的,对应Cache的名称, listparamentes中的cacheName

key:通过SpringEL表达式来指定我们的key。这里的EL表达式可以使用方法参数及它们对应的属性。使用方法参数时我们可以直接使用“#参数名或者“#p参数index我们统一采用方法的参数做唯一key,没有参数不写!

condition:有的时候我们可能并不希望缓存一个方法所有的返回结果,condition属性默认为空,表示将缓存所有的调用情形。其值是通过SpringEL表达式来指定的,当为true时表示进行缓存处理;当为false时表示不进行缓存处理,即每次调用该方法时该方法都会执行一次。如下示例表示只有当userid为偶数时才会进行缓存

 

1 @Cacheable(value="users",key="#user.id",condition="#user.id%2==0")2 public User find(User user) {3     System.out.println("find user by user " + user);4     return user;5 }

 

3,基于程序代码方式的缓存相关使用:

当注解不能完全满足需求,或需要在程序代码中实现动态操作时,就需要对ehcache实现相关封装,从而现对缓存手动进行增删改。可以考虑写成util,我这里是以service的形式现实的封装及相关调用,仅供参考。

 

1 package com.cshtong.tower.service;  2   3 import java.io.UncheckedIOException;  4 import java.util.ArrayList;  5 import java.util.List;  6   7 import javax.annotation.Resource;  8   9 import org.slf4j.Logger; 10 import org.slf4j.LoggerFactory; 11 import org.springframework.stereotype.Service; 12  13 import com.alibaba.fastjson.JSONObject; 14 import com.cshtong.tower.model.MessageType; 15 import com.cshtong.tower.model.User; 16 import com.cshtong.tower.repository.MessageTypeRepository; 17  18 import net.sf.ehcache.Cache; 19 import net.sf.ehcache.CacheManager; 20 import net.sf.ehcache.Ehcache; 21 import net.sf.ehcache.Element; 22  23 @Service 24 public class EhcacheServiceImpl implements EhcacheService{ 25      26     private static Logger logger = LoggerFactory.getLogger(EhcacheServiceImpl.class); 27      28     @Resource 29     private UserService userSerivce; 30      31     @Resource 32     private MessageTypeRepository messageTypeRepository; 33      34     public static CacheManager cacheManager(){ 35          36         CacheManager cm = null; 37         try { 38             cm = CacheManager.newInstance(); 39         } catch (UncheckedIOException e) { 40             logger.error("Fail to load config, message:{}", e.getMessage()); 41         } 42         return cm; 43     } 44      45     /** 46      * key可以为空 47      */ 48     @SuppressWarnings({"deprecation" }) 49     @Override 50     public com.cshtong.tower.model.Ehcache findCache(String cacheName, String key) { 51         com.cshtong.tower.model.Ehcache eh = null; 52         if (null == key) { 53             Ehcache cache = cacheManager().getEhcache(cacheName); 54             eh = new com.cshtong.tower.model.Ehcache(); 55             eh.setCacheName(cache.getName()); 56             List
listKey = new ArrayList<>(); 57 for (int j = 0; j < cache.getKeys().size(); j++) { 58 listKey.add(cache.getKeys().get(j).toString()); 59 } 60 eh.setKeys(listKey); 61 eh.setSize(cache.getSize()); 62 /*eh.setHitrate(cache.getStatistics().cacheHitRatio());*/ 63 eh.setDiskStoreSize(cache.getDiskStoreSize()); 64 eh.setMemoryStoreSize(cache.getMemoryStoreSize()); 65 eh.setStatus(cache.getStatus().toString()); 66 }else{ 67 Element el = cacheManager().getEhcache(cacheName).get(key); 68 if (el == null) { 69 el = cacheManager().getEhcache(cacheName).get(Integer.parseInt(key)); 70 } 71 eh = new com.cshtong.tower.model.Ehcache(); 72 eh.setKeyHit(el.getHitCount()); 73 eh.setLastUpdateTime(el.getLastUpdateTime()); 74 eh.setValues(el.getValue()); 75 } 76 return eh; 77 } 78 79 @SuppressWarnings("deprecation") 80 @Override 81 public List
listAllEhcahce() { 82 List
list = new ArrayList<>(); 83 String[] cache = cacheManager().getCacheNames(); 84 List
cachelist = new ArrayList<>(); 85 for (int i = 0; i < cache.length; i++) { 86 Ehcache c = cacheManager().getEhcache(cache[i]); 87 cachelist.add(c); 88 } 89 for (int i = 0; i < cachelist.size(); i++) { 90 com.cshtong.tower.model.Ehcache eh = new com.cshtong.tower.model.Ehcache(); 91 eh.setCacheName(cachelist.get(i).getName()); 92 List
listKey = new ArrayList<>(); 93 for (int j = 0; j < cachelist.get(i).getKeys().size(); j++) { 94 listKey.add(cachelist.get(i).getKeys().get(j).toString()); 95 } 96 eh.setKeys(listKey); 97 eh.setSize(cachelist.get(i).getSize()); 98 eh.setStatus(cachelist.get(i).getStatus().toString()); 99 eh.setMemoryStoreSize(cachelist.get(i).getMemoryStoreSize());100 eh.setDiskStoreSize(cachelist.get(i).getDiskStoreSize());101 list.add(eh);102 }103 return list;104 }105 106 /**107 * 获取缓存108 * @param cacheName109 * @param key110 * @return json字符串111 */112 @Override113 public String getCache(String cacheName, Object key) {114 logger.debug("Getting Cache that name is " + cacheName + "and key is" + key);115 Element el = cacheManager().getCache(cacheName).get(key);116 return JSONObject.toJSONString(el.getObjectValue());117 }118 119 @Override120 public Cache getCache(String name) {121 logger.debug("Getting Cache that name is " + name);122 return cacheManager().getCache(name);123 }124 125 /**126 * 获取所有的缓存127 * @param names128 * @return129 */130 @Override131 public String[] getCacheNames() {132 logger.debug("All of the Cache is " + cacheManager().getCacheNames());133 return cacheManager().getCacheNames();134 }135 136 @Override137 public void update(String cacheName, Object key, Object value) {138 try {139 remove(cacheName, key);140 put(cacheName, key, value);141 } catch (Exception e) {142 logger.debug("Fail to update the Cache,which is " + cacheManager().getCacheNames());143 }144 }145 146 @Override147 public void put(String cacheName, Object key, Object value) {148 logger.debug("add Cache is " + cacheManager().getCacheNames() + ",and key is " + key + ",and value is" + value);149 Element el = new Element(key, value);150 cacheManager().getCache(cacheName).put(el);151 }152 153 @Override154 public boolean ishasCache(String cacheName, Object key) {155 boolean rs = false;156 Element value = cacheManager().getCache(cacheName).get(key);157 if (null != value) {158 rs = true;159 }160 return rs;161 }162 163 /**164 * 根据缓存名清除缓存key value165 * @param name166 */167 @Override168 public void evictName(String name) {169 logger.debug("delete Cache that name is " + name);170 cacheManager().getCache(name).removeAll();171 }172 173 /**174 * 根据缓存名对应的key清除缓存175 */176 @Override177 public void remove(String cacheName, Object key) {178 logger.debug("Delete Cache that Name is "+ cacheName +"and key is " + key );179 Cache cache = cacheManager().getCache(cacheName); 180 cache.remove(key); 181 }182 183 /**184 * 清除当前cacheManager的所有缓存185 */186 @Override187 public void clear() {188 logger.debug("clear all cache!!");189 cacheManager().clearAll();190 }191 192 }

 

 

 

以上愚见,只是个人的理解,仅供参考。如有不对的地方,欢迎指正批评....

转载于:https://www.cnblogs.com/liliangel/p/5279456.html

你可能感兴趣的文章
参与博客编辑器改版,我的礼物 感谢51cto
查看>>
JavaWeb笔记——JSTL标签
查看>>
Eclipse插件大全 挑选最牛的TOP30
查看>>
一些实用性的总结与纠正
查看>>
Kubernetes概念
查看>>
逻辑卷管理器(LVM)
查看>>
一个小代码,欢迎大佬的意见,求指正
查看>>
搭建LAMP架构
查看>>
神经网络注意力机制--Attention in Neural Networks
查看>>
Spring.Net+WCF实现分布式事务
查看>>
在Linux上高效开发的7个建议
查看>>
java数据结构 - 数组使用的代码
查看>>
个人简历-项目经验
查看>>
swoole异步任务task处理慢请求简单实例
查看>>
oracle数据泵导入分区表统计信息报错(四)
查看>>
spring技术内幕读书笔记之IoC容器的学习
查看>>
细说多线程(五) —— CLR线程池的I/O线程
查看>>
JavaScript instanceof和typeof的区别
查看>>
Hadoop文件系统详解-----(一)
查看>>
《面向模式的软件体系结构2-用于并发和网络化对象模式》读书笔记(8)--- 主动器...
查看>>