spring(三、spring中的eheche缓存、redis使用)

本文主要介绍为什么要构建ehcache+redis两级缓存?以及在实战中如何实现?思考如何配置缓存策略更合适?这样的方案可能遗留什么问题?JUST DO IT! GO!


问题描述

场景:我们的应用系统是分布式集群的,可横向扩展的。应用中某个接口操作满足以下一个或多个条件: 
1. 接口运行复杂代价大, 
2. 接口返回数据量大, 
3. 接口的数据基本不会更改, 
4. 接口数据一致性要求不高(只需满足最终一致)。

此时,我们会考虑将这个接口的返回值做缓存。考虑到上述条件,我们需要一套高可用分布式的缓存集群,并具备持久化功能,备选的有ehcache集群或redis主备(sentinel)。

  • ehcache集群因为节点之间数据同步通过组播的方式,可能带来的问题:节点间大量的数据复制带来额外的开销,在节点多的情况下此问题越发严重,N个节点会出现N-1次网络传输数据进行同步。(见下图,缓存集群中有三台机器,其中一台机器接收到数据,需要拷贝到其他机器,一次input后需要copy两次,两次copy是需要网络传输消耗的) 
  • redis主备由于作为中心节点提供缓存,其他节点都向redis中心节点取数据,所以,一次网络传输即可。(当然此处的一次网络代价跟组播的代价是不一样的)但是,随着访问量增大,大量的缓存数据访问使得应用服务器和缓存服务器之间的网络I/O消耗越大。(见下图,同样三台应用服务器,redis sentinel作为中心节点缓存。所谓中心,即所有应用服务器以redis为缓存中心,不再像ehcache集群,缓存是分散存放在应用服务器中,需要互相同步的,任何一台应用服务器的input,都会经过一次copy网络传输到redis,由于redis是中心共享的,那么就可以不用同步的步骤,其他应用服务器需要只需去get取即可。但是,我们会发现多了N台服务器的get的网络开销。)

提出方案

那么要怎么处理呢?所以两级缓存的思想诞生了,在redis的方案上做一步优化,在缓存到远程redis的同时,缓存一份到本地进程ehcache(此处的ehcache不用做集群,避免组播带来的开销),取缓存的时候会先取本地,没有会向redis请求,这样会减少应用服务器<–>缓存服务器redis之间的网络开销。(见下图,为了减少get这几条网络传输,我们会在每个应用服务器上增加本地的ehcache缓存作为二级缓存,即第一次get到的数据存入ehcache,后面output输出即可从本地ehcache中获取,不用再访问redis了,所以就减少了以后get的网络开销。get开销只要一次,后续不需要了,除非本地缓存过期需要再get。) 
 
如果用过j2cache的都应该知道,oschina用j2cache这种两级缓存,实践证明了该方案是可行的。该篇使用spring+ehcache+redis实现更加简洁。


方案实施

1、 spring和ehcache集成

主要获取ehcache作为操作ehcache的对象。

ehcache.xml 代码如下:


  1. <ehcache updateCheck="false" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  2. xsi:noNamespaceSchemaLocation="http://ehcache.sf.net/ehcache.xsd">
  3. <diskStore path="java.io.tmpdir/ehcache"/>
  4. <!-- 默认的管理策略
  5. maxElementsOnDisk: 在磁盘上缓存的element的最大数目,默认值为0,表示不限制。
  6. eternal:设定缓存的elements是否永远不过期。如果为true,则缓存的数据始终有效,如果为false那么还要根据timeToIdleSeconds,timeToLiveSeconds判断。
  7. diskPersistent: 是否在磁盘上持久化。指重启jvm后,数据是否有效。默认为false。
  8. diskExpiryThreadIntervalSeconds:对象检测线程运行时间间隔。标识对象状态(过期/持久化)的线程多长时间运行一次。
  9. -->
  10. <defaultCache maxElementsInMemory="10000"
  11. eternal="false"
  12. timeToIdleSeconds="3600"
  13. timeToLiveSeconds="3600"
  14. overflowToDisk="true"
  15. diskPersistent="false"
  16. diskExpiryThreadIntervalSeconds="120"
  17. memoryStoreEvictionPolicy="LRU"/>
  18. <!-- 对象无过期,一个1000长度的队列,最近最少使用的对象被删除 -->
  19. <cache name="userCache"
  20. maxElementsInMemory="1000"
  21. eternal="true"
  22. overflowToDisk="false"
  23. timeToIdleSeconds="0"
  24. timeToLiveSeconds="0"
  25. memoryStoreEvictionPolicy="LFU">
  26. </cache>
  27. <!-- 组播方式:multicastGroupPort需要保证与其他系统不重复,进行端口注册 -->
  28. <!-- 若因未注册,配置了重复端口,造成权限缓存数据异常,请自行解决 -->
  29. <cacheManagerPeerProviderFactory
  30. class="net.sf.ehcache.distribution.RMICacheManagerPeerProviderFactory"
  31. properties="peerDiscovery=automatic,
  32. multicastGroupAddress=230.0.0.1,
  33. multicastGroupPort=4546, timeToLive=1"/>
  34. <!-- replicatePuts=true | false – 当一个新元素增加到缓存中的时候是否要复制到其他的peers. 默认是true。 -->
  35. <!-- replicateUpdates=true | false – 当一个已经在缓存中存在的元素被覆盖时是否要进行复制。默认是true。 -->
  36. <!-- replicateRemovals= true | false – 当元素移除的时候是否进行复制。默认是true。 -->
  37. <!-- replicateAsynchronously=true | false – 复制方式是异步的(指定为true时)还是同步的(指定为false时)。默认是true。 -->
  38. <!-- replicatePutsViaCopy=true | false – 当一个新增元素被拷贝到其他的cache中时是否进行复制指定为true时为复制,默认是true。 -->
  39. <!-- replicateUpdatesViaCopy=true | false – 当一个元素被拷贝到其他的cache中时是否进行复制(指定为true时为复制),默认是true。 -->
  40. <cache name="webCache_LT"
  41. maxElementsInMemory="10000"
  42. eternal="false"
  43. overflowToDisk="false"
  44. timeToIdleSeconds="3600"
  45. timeToLiveSeconds="3600"
  46. memoryStoreEvictionPolicy="LRU">
  47. <cacheEventListenerFactory
  48. class="net.sf.ehcache.distribution.RMICacheReplicatorFactory"
  49. properties="replicateRemovals=true"/>
  50. <bootstrapCacheLoaderFactory
  51. class="net.sf.ehcache.distribution.RMIBootstrapCacheLoaderFactory"/>
  52. </cache>
  53. <cache name="webCache_ST"
  54. maxElementsInMemory="1000"
  55. eternal="false"
  56. overflowToDisk="false"
  57. timeToIdleSeconds="300"
  58. timeToLiveSeconds="300"
  59. memoryStoreEvictionPolicy="LRU">
  60. <cacheEventListenerFactory
  61. class="net.sf.ehcache.distribution.RMICacheReplicatorFactory"
  62. properties="replicateRemovals=true"/>
  63. <bootstrapCacheLoaderFactory
  64. class="net.sf.ehcache.distribution.RMIBootstrapCacheLoaderFactory"/>
  65. </cache>
  66. </ehcache>

spring注入jedisPool、redisConnFactory、redisTemplate对象


  1. <!-- 加载redis.propertis -->
  2. <bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
  3. <property name="locations" value="classpath:redis.properties"/>
  4. </bean>
  5. <!-- Redis 连接池 -->
  6. <bean id="jedisPool" class="redis.clients.jedis.JedisPoolConfig">
  7. <property name="maxTotal" value="${redis.pool.maxActive}" />
  8. <property name="maxIdle" value="${redis.pool.maxIdle}" />
  9. <property name="testOnBorrow" value="${redis.pool.testOnBorrow}" />
  10. <property name="testOnReturn" value="${redis.pool.testOnReturn}" />
  11. <property name="maxWaitMillis" value="${redis.pool.maxWait}" />
  12. </bean>
  13. <!-- Redis 连接工厂 -->
  14. <bean id="redisConnFactory"
  15. class="org.springframework.data.redis.connection.jedis.JedisConnectionFactory">
  16. <property name="hostName" value="${redis.ip}" />
  17. <property name="port" value="${redis.port}" />
  18. <!-- property name="password" value="${redis.password}" -->
  19. <property name="timeout" value="${redis.timeout}" />
  20. <property name="poolConfig" ref="jedisPool" />
  21. </bean>
  22. <!-- redis 操作对象 -->
  23. <bean id="redisTemplate" class="org.springframework.data.redis.core.RedisTemplate">
  24. <property name="connectionFactory" ref="redisConnFactory" />
  25. </bean>

3、 spring集成ehcache和redis

通过上面两步注入的ehcache和redisTemplate我们就能自定义一个方法将两者整合起来。详见EhRedisCache类。

EhRedisCache.java


  1. /**
  2. * 两级缓存,一级:ehcache,二级为redisCache
  3. * @author yulin
  4. *
  5. */
  6. public class EhRedisCache implements Cache{
  7. private static final Logger LOG = LoggerFactory.getLogger(UserServiceImpl.class);
  8. private String name;
  9. private net.sf.ehcache.Cache ehCache;
  10. private RedisTemplate<String, Object> redisTemplate;
  11. private long liveTime = 1*60*60; //默认1h=1*60*60
  12. @Override
  13. public String getName() {
  14. return this.name;
  15. }
  16. @Override
  17. public Object getNativeCache() {
  18. return this;
  19. }
  20. @Override
  21. public ValueWrapper get(Object key) {
  22. Element value = ehCache.get(key);
  23. LOG.info("Cache L1 (ehcache) :{}={}",key,value);
  24. if (value!=null) {
  25. return (value != null ? new SimpleValueWrapper(value.getObjectValue()) : null);
  26. }
  27. //TODO 这样会不会更好?访问10次EhCache 强制访问一次redis 使得数据不失效
  28. final String keyStr = key.toString();
  29. Object objectValue = redisTemplate.execute(new RedisCallback<Object>() {
  30. public Object doInRedis(RedisConnection connection)
  31. throws DataAccessException {
  32. byte[] key = keyStr.getBytes();
  33. byte[] value = connection.get(key);
  34. if (value == null) {
  35. return null;
  36. }
  37. //每次获得,重置缓存过期时间
  38. if (liveTime > 0) {
  39. connection.expire(key, liveTime);
  40. }
  41. return toObject(value);
  42. }
  43. },true);
  44. ehCache.put(new Element(key, objectValue));//取出来之后缓存到本地
  45. LOG.info("Cache L2 (redis) :{}={}",key,objectValue);
  46. return (objectValue != null ? new SimpleValueWrapper(objectValue) : null);
  47. }
  48. @Override
  49. public void put(Object key, Object value) {
  50. ehCache.put(new Element(key, value));
  51. final String keyStr = key.toString();
  52. final Object valueStr = value;
  53. redisTemplate.execute(new RedisCallback<Long>() {
  54. public Long doInRedis(RedisConnection connection)
  55. throws DataAccessException {
  56. byte[] keyb = keyStr.getBytes();
  57. byte[] valueb = toByteArray(valueStr);
  58. connection.set(keyb, valueb);
  59. if (liveTime > 0) {
  60. connection.expire(keyb, liveTime);
  61. }
  62. return 1L;
  63. }
  64. },true);
  65. }
  66. @Override
  67. public void evict(Object key) {
  68. ehCache.remove(key);
  69. final String keyStr = key.toString();
  70. redisTemplate.execute(new RedisCallback<Long>() {
  71. public Long doInRedis(RedisConnection connection)
  72. throws DataAccessException {
  73. return connection.del(keyStr.getBytes());
  74. }
  75. },true);
  76. }
  77. @Override
  78. public void clear() {
  79. ehCache.removeAll();
  80. redisTemplate.execute(new RedisCallback<String>() {
  81. public String doInRedis(RedisConnection connection)
  82. throws DataAccessException {
  83. connection.flushDb();
  84. return "clear done.";
  85. }
  86. },true);
  87. }
  88. public net.sf.ehcache.Cache getEhCache() {
  89. return ehCache;
  90. }
  91. public void setEhCache(net.sf.ehcache.Cache ehCache) {
  92. this.ehCache = ehCache;
  93. }
  94. public RedisTemplate<String, Object> getRedisTemplate() {
  95. return redisTemplate;
  96. }
  97. public void setRedisTemplate(RedisTemplate<String, Object> redisTemplate) {
  98. this.redisTemplate = redisTemplate;
  99. }
  100. public long getLiveTime() {
  101. return liveTime;
  102. }
  103. public void setLiveTime(long liveTime) {
  104. this.liveTime = liveTime;
  105. }
  106. public void setName(String name) {
  107. this.name = name;
  108. }
  109. /**
  110. * 描述 : Object转byte[]. <br>
  111. * @param obj
  112. * @return
  113. */
  114. private byte[] toByteArray(Object obj) {
  115. byte[] bytes = null;
  116. ByteArrayOutputStream bos = new ByteArrayOutputStream();
  117. try {
  118. ObjectOutputStream oos = new ObjectOutputStream(bos);
  119. oos.writeObject(obj);
  120. oos.flush();
  121. bytes = bos.toByteArray();
  122. oos.close();
  123. bos.close();
  124. } catch (IOException ex) {
  125. ex.printStackTrace();
  126. }
  127. return bytes;
  128. }
  129. /**
  130. * 描述 : byte[]转Object . <br>
  131. * @param bytes
  132. * @return
  133. */
  134. private Object toObject(byte[] bytes) {
  135. Object obj = null;
  136. try {
  137. ByteArrayInputStream bis = new ByteArrayInputStream(bytes);
  138. ObjectInputStream ois = new ObjectInputStream(bis);
  139. obj = ois.readObject();
  140. ois.close();
  141. bis.close();
  142. } catch (IOException ex) {
  143. ex.printStackTrace();
  144. } catch (ClassNotFoundException ex) {
  145. ex.printStackTrace();
  146. }
  147. return obj;
  148. }
  149. }

spring注入自定义缓存


  1. <!-- 自定义ehcache+redis-->
  2. <bean id="ehRedisCacheManager" class="org.springframework.cache.support.SimpleCacheManager">
  3. <property name="caches">
  4. <set>
  5. <bean id="ehRedisCache" class="org.musicmaster.yulin.ercache.EhRedisCache">
  6. <property name="redisTemplate" ref="redisTemplate" />
  7. <property name="ehCache" ref="ehCache"/>
  8. <property name="name" value="userCache"/>
  9. <!-- <property name="liveTime" value="3600"/> -->
  10. </bean>
  11. </set>
  12. </property>
  13. </bean>
  14. <!-- 注解声明 -->
  15. <cache:annotation-driven cache-manager="ehRedisCacheManager"
  16. proxy-target-class="true" />

4、 模拟问题中提到的接口

此处假设该接口满足上述条件。

UserService.java


  1. public interface UserService {
  2. User findById(long id);
  3. List<User> findByPage(int startIndex, int limit);
  4. List<User> findBySex(Sex sex);
  5. List<User> findByAge(int lessAge);
  6. List<User> findByUsers(List<User> users);
  7. boolean update(User user);
  8. boolean deleteById(long id);
  9. }

UserServiceImpl.java


  1. @Service
  2. public class UserServiceImpl implements UserService{
  3. private static final Logger LOG = LoggerFactory.getLogger(UserServiceImpl.class);
  4. @Cacheable("userCache")
  5. @Override
  6. public User findById(long id) {
  7. LOG.info("visit business service findById,id:{}",id);
  8. User user = new User();
  9. user.setId(id);
  10. user.setUserName("tony");
  11. user.setPassWord("******");
  12. user.setSex(Sex.M);
  13. user.setAge(32);
  14. //耗时操作
  15. try {
  16. Thread.sleep(3000);
  17. } catch (InterruptedException e) {
  18. e.printStackTrace();
  19. }
  20. return user;
  21. }
  22. @Override
  23. public List<User> findByPage(int startIndex, int limit) {
  24. return null;
  25. }
  26. @Cacheable("userCache")
  27. @Override
  28. public List<User> findBySex(Sex sex) {
  29. LOG.info("visit business service findBySex,sex:{}",sex);
  30. List<User> users = new ArrayList<User>();
  31. for (int i = 0; i < 5; i++) {
  32. User user = new User();
  33. user.setId(i);
  34. user.setUserName("tony"+i);
  35. user.setPassWord("******");
  36. user.setSex(sex);
  37. user.setAge(32+i);
  38. users.add(user);
  39. }
  40. return users;
  41. }
  42. @Override
  43. public List<User> findByAge(int lessAge) {
  44. // TODO Auto-generated method stub
  45. return null;
  46. }
  47. //FIXME 此处将list参数的地址作为key存储,是否有问题?
  48. @Cacheable("userCache")
  49. @Override
  50. public List<User> findByUsers(List<User> users) {
  51. LOG.info("visit business service findByUsers,users:{}",users);
  52. return users;
  53. }
  54. @CacheEvict("userCache")
  55. @Override
  56. public boolean update(User user) {
  57. return true;
  58. }
  59. @CacheEvict("userCache")
  60. @Override
  61. public boolean deleteById(long id) {
  62. return false;
  63. }
  64. }

User.java

  1. public class User implements Serializable {
  2. private static final long serialVersionUID = 1L;
  3. public enum Sex{
  4. M,FM
  5. }
  6. private long id;
  7. private String userName;
  8. private String passWord;
  9. private int age;
  10. private Sex sex;
  11. public long getId() {
  12. return id;
  13. }
  14. public void setId(long id) {
  15. this.id = id;
  16. }
  17. public String getUserName() {
  18. return userName;
  19. }
  20. public void setUserName(String userName) {
  21. this.userName = userName;
  22. }
  23. public String getPassWord() {
  24. return passWord;
  25. }
  26. public void setPassWord(String passWord) {
  27. this.passWord = passWord;
  28. }
  29. public int getAge() {
  30. return age;
  31. }
  32. public void setAge(int age) {
  33. this.age = age;
  34. }
  35. public Sex getSex() {
  36. return sex;
  37. }
  38. public void setSex(Sex sex) {
  39. this.sex = sex;
  40. }
  41. @Override
  42. public String toString() {
  43. return "User [id=" + id + ", userName=" + userName + ", passWord="
  44. + passWord + ", age=" + age + ", sex=" + sex + "]";
  45. }
  46. }

实施结果

我们写个测试类来模拟下

TestEhRedisCache.java


  1. public class TestEhRedisCache{
  2. public static void main(String[] args) {
  3. ApplicationContext context = new ClassPathXmlApplicationContext("spring-ehRedisCache.xml");
  4. UserService userService= (UserService) context.getBean("userServiceImpl");
  5. System.out.println(userService.findById(5l));
  6. System.out.println(userService.findById(5l));
  7. System.out.println(userService.findById(5l));
  8. System.out.println(userService.findById(5l));
  9. System.out.println(userService.findById(5l));
  10. }
  11. }

TEST1 输出结果:


  1. Cache L1 (ehcache) :UserServiceImpl/findById/5=null
  2. Cache L2 (redis) :UserServiceImpl/findById/5=null
  3. visit business service findById,id:5
  4. User [id=5, userName=tony, passWord=******, age=32, sex=M]
  5. Cache L1 (ehcache) :UserServiceImpl/findById/5=User [id=5, userName=tony, passWord=******, age=32, sex=M]
  6. User [id=5, userName=tony, passWord=******, age=32, sex=M]
  7. Cache L1 (ehcache) :UserServiceImpl/findById/5=User [id=5, userName=tony, passWord=******, age=32, sex=M]
  8. User [id=5, userName=tony, passWord=******, age=32, sex=M]
  9. Cache L1 (ehcache) :UserServiceImpl/findById/5=User [id=5, userName=tony, passWord=******, age=32, sex=M]
  10. User [id=5, userName=tony, passWord=******, age=32, sex=M]
  11. Cache L1 (ehcache) :UserServiceImpl/findById/5=User [id=5, userName=tony, passWord=******, age=32, sex=M]
  12. User [id=5, userName=tony, passWord=******, age=32, sex=M]

上面第一次访问,一级缓存ehcache和二级缓存redis都没有数据,访问接口耗时操作,打印日志:

visit business service findById,id:5

第二次之后的访问,都会访问一级缓存ehcache,此时响应速度很快。

TEST2 在TEST1结束后,我们在liveTime的时间内,也就是redis缓存还未过期再次执行,会出现以下结果


  1. Cache L1 (ehcache) :UserServiceImpl/findById/5=null
  2. Cache L2 (redis) :UserServiceImpl/findById/5=User [id=5, userName=tony, passWord=******, age=32, sex=M]
  3. User [id=5, userName=tony, passWord=******, age=32, sex=M]
  4. Cache L1 (ehcache) :UserServiceImpl/findById/5=User [id=5, userName=tony, passWord=******, age=32, sex=M]
  5. User [id=5, userName=tony, passWord=******, age=32, sex=M]
  6. Cache L1 (ehcache) :UserServiceImpl/findById/5=User [id=5, userName=tony, passWord=******, age=32, sex=M]
  7. User [id=5, userName=tony, passWord=******, age=32, sex=M]
  8. Cache L1 (ehcache) :UserServiceImpl/findById/5=User [id=5, userName=tony, passWord=******, age=32, sex=M]
  9. User [id=5, userName=tony, passWord=******, age=32, sex=M]
  10. Cache L1 (ehcache) :UserServiceImpl/findById/5=User [id=5, userName=tony, passWord=******, age=32, sex=M]
  11. User [id=5, userName=tony, passWord=******, age=32, sex=M]

由于TEST1执行完结束后,ehcache为进程间的缓存,自然随着运行结束而释放,所以TEST2出现:

Cache L1 (ehcache) :UserServiceImpl/findById/5=null

然而在第二次访问二级缓存redis,还未到缓存过期时间,所以在redis中找到数据(同时数据入一级缓存ehcache):

Cache L2 (redis) :UserServiceImpl/findById/5=User [id=5, userName=tony, passWord=**, age=32, sex=M]

此处不会visit….没有经过接口的耗时操作,接下来数据都可以在本地缓存ehcache中获取。

spring(三、spring中的eheche缓存、redis使用)的更多相关文章

  1. 在Spring、Hibernate中使用Ehcache缓存(2)

    这里将介绍在Hibernate中使用查询缓存.一级缓存.二级缓存,整合Spring在HibernateTemplate中使用查询缓存.,这里是hibernate3,使用hibernate4类似,不过不 ...

  2. Spring Web MVC中的页面缓存支持 ——跟我学SpringMVC系列

    Spring Web MVC中的页面缓存支持 ——跟我学SpringMVC系列

  3. spring(三) spring事务操作

    前面一篇博文讲解了什么是AOP.学会了写AOP的实现,但是并没有实际运用起来,这一篇博文就算是对AOP技术应用的进阶把,重点是事务的处理. --wh 一.jdbcTemplate 什么是JdbcTem ...

  4. Spring(三) Spring IOC

    Spring 核心之 IOC 容器 再谈 IOC 与 DI IOC(Inversion of Control)控制反转:所谓控制反转,就是把原先我们代码里面需要实现的对象创 建.依赖的代码,反转给容器 ...

  5. Spring(三) Spring IOC 初体验

    Web IOC 容器初体验 我们还是从大家最熟悉的 DispatcherServlet 开始,我们最先想到的还是 DispatcherServlet 的 init() 方法.我们发现在 Dispath ...

  6. ASP.Net Core使用分布式缓存Redis从入门到实战演练

    一.课程介绍 人生苦短,我用.NET Core!缓存在很多情况下需要用到,合理利用缓存可以一方面可以提高程序的响应速度,同时可以减少对特定资源访问的压力.  所以经常要用到且不会频繁改变且被用户共享的 ...

  7. 第04项目:淘淘商城(SpringMVC+Spring+Mybatis)【第七天】(redis缓存)

    https://pan.baidu.com/s/1bptYGAb#list/path=%2F&parentPath=%2Fsharelink389619878-229862621083040 ...

  8. 【开源项目系列】如何基于 Spring Cache 实现多级缓存(同时整合本地缓存 Ehcache 和分布式缓存 Redis)

    一.缓存 当系统的并发量上来了,如果我们频繁地去访问数据库,那么会使数据库的压力不断增大,在高峰时甚至可以出现数据库崩溃的现象.所以一般我们会使用缓存来解决这个数据库并发访问问题,用户访问进来,会先从 ...

  9. 在spring boot环境中使用fastjson + redis的高速缓存技术

    因为项目需求,需要在spring boot环境中使用redis作数据缓存.之前的解决方案是参考的http://wiselyman.iteye.com/blog/2184884,具体使用的是Jackso ...

随机推荐

  1. Shell data、timedatectl

     data系统时间管理命令 命令date +%F xxxx—xx--xx #查看当前日期. 命令date +%T xx:xx:xx #查看当前时间. 命令date +%y xx #年2位 命令date ...

  2. [pytorch修改]dataloader.py 实现darknet中的subdivision功能

    dataloader.py import random import torch import torch.multiprocessing as multiprocessing from torch. ...

  3. PIL PNG格式通道问题的解决方法

    近来研究图片的剪切拼接,用到PIL,在打开PNG格式保存为JPEG格式的图片发现报错: import os from PIL import Image im = Image.open(r'E:\wor ...

  4. Learning-Python【19】:Python常用模块(2)—— os、sys、shutil

    os模块:与操作系统相关的模块 import os # 获取当前的工作目录 print(os.getcwd()) # 切换工作目录 os.chdir(r'E:\Python\test') print( ...

  5. JS对象、数据类型区别、函数

    对象 基本数据类型都是单一的值,值和值之间没有任何联系,变量之间独立,不能成为一个整体. 对象属于一种符合的数据类型,对象中可以保存对个不同数据类型的属性. 对象分类:  1.内建对象   由ES标准 ...

  6. [shell]输出内容到剪切板

    commandline和GUI下的clipboard的交互Mac下echo $PATH | pbcopy,copy to clipboardecho "$(pbpaste -Prefer t ...

  7. CSS font-family字体大合集

    在写文字内容占大篇幅的页面是,总是会面临着改变字体的需求,以下为font-family常用合集以及一部分文字效果: windows常见内置中文字体 字体中文名             字体英文名    ...

  8. 在git服务器上创建项目过程及遇到的问题

    一: 登录git服务器,输入用户名,密码等 二: New Project 添加项目 设置组可见,项目名称等. 创建成功的项目可以看到该项目的clone地址,可以通过http,ssh两种方式来获取: 三 ...

  9. linux是什么,有什么特点

    (1)Linux是一套免费使用和自由传播的类Unix操作系统,是一个基于POSIX和UNIX的多用户.多任务.支持多线程和多CPU的操作系统.它能运行主要的UNIX工具软件.应用程序和网络协议.它支持 ...

  10. map传参上下文赋值的问题

    今天开发遇到一个问题就是声明一个map<String,String> param ,给param赋值,明明有结果但是就是返回为空:下面附上代码: 因为在一个大的循环中,param是公用赋值 ...