From a0ad14b0a0fb60604b0a9cf978a537e64479a8d4 Mon Sep 17 00:00:00 2001 From: chenjiahe <763432473@qq.com> Date: 星期一, 27 六月 2022 19:05:01 +0800 Subject: [PATCH] 新增Redis可以切换数据库 --- src/main/java/com/hx/redis/templates/entity/RedissondbConfigEntity.java | 30 + src/main/java/com/hx/redis/templates/manager/RedisManager.java | 41 + src/main/java/com/hx/redis/templates/config/RedisTempConfig.java | 37 + src/main/java/com/hx/redis/templates/config/RedisTempsUtil.java | 991 +++++++++++++++++++++++++++++++++++++++++++ src/main/java/com/hx/redis/templates/config/RedisBeanInit.java | 95 ++++ src/main/java/com/hx/redis/templates/register/RedisRegister.java | 141 ++++++ src/main/java/com/hx/bean/annotations/RedisTempsClient.java | 28 + pom.xml | 7 8 files changed, 1,370 insertions(+), 0 deletions(-) diff --git a/pom.xml b/pom.xml index 03e7429..f759c02 100644 --- a/pom.xml +++ b/pom.xml @@ -285,6 +285,13 @@ <version>1.5.0-RC2</version> </dependency> + <!-- https://mvnrepository.com/artifact/org.redisson/redisson-spring-boot-starter --> + <dependency> + <groupId>org.redisson</groupId> + <artifactId>redisson-spring-boot-starter</artifactId> + </dependency> + + <!-- redis --> <dependency> <groupId>org.springframework.boot</groupId> diff --git a/src/main/java/com/hx/bean/annotations/RedisTempsClient.java b/src/main/java/com/hx/bean/annotations/RedisTempsClient.java new file mode 100644 index 0000000..99afaae --- /dev/null +++ b/src/main/java/com/hx/bean/annotations/RedisTempsClient.java @@ -0,0 +1,28 @@ +package com.hx.bean.annotations; + +import com.hx.redis.RedisConfig; +import com.hx.redis.RedisUtil; +import com.hx.redis.templates.config.RedisBeanInit; +import com.hx.redis.templates.config.RedisTempConfig; +import com.hx.redis.templates.config.RedisTempsUtil; +import com.hx.redis.templates.register.RedisRegister; +import org.springframework.boot.autoconfigure.AutoConfigureBefore; +import org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration; +import org.springframework.context.annotation.Import; + +import java.lang.annotation.*; + +/**浣跨敤redis閾炬帴 + *杩欓噷鐨処mport鐨勪袱涓被灏辨槸闇�瑕佸姞杞界殑bean锛岃繖鏍峰氨鍙互閫氳繃绠�鍗曠殑娣诲姞涓�涓敞瑙f潵鍔犺浇鑷繁鑷畾涔夌殑bean浜嗭紝鑰屼笖鍙� + *浠ユ槸寰堝涓紝鍙互鎵撳埌jar鍖呴噷闈㈤�氳繃Maven寮曞叆閮芥槸ok鐨勶紱 + * @author CJH + * @date 202-06-17 + */ +@Target(ElementType.TYPE) +@Retention(RetentionPolicy.RUNTIME) +@Documented +@Import({RedisBeanInit.class,RedisRegister.class, RedisTempsUtil.class,RedisTempConfig.class}) +public @interface RedisTempsClient { + + +} \ No newline at end of file diff --git a/src/main/java/com/hx/redis/templates/config/RedisBeanInit.java b/src/main/java/com/hx/redis/templates/config/RedisBeanInit.java new file mode 100644 index 0000000..59c1a70 --- /dev/null +++ b/src/main/java/com/hx/redis/templates/config/RedisBeanInit.java @@ -0,0 +1,95 @@ +package com.hx.redis.templates.config; + +import com.hx.redis.templates.entity.RedissondbConfigEntity; +import com.hx.redis.templates.manager.RedisManager; +import com.hx.redis.templates.register.RedisRegister; +import org.apache.commons.collections.CollectionUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.BeansException; +import org.springframework.boot.autoconfigure.AutoConfigureBefore; +import org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration; +import org.springframework.boot.context.properties.bind.Binder; +import org.springframework.context.ApplicationContext; +import org.springframework.context.ApplicationContextAware; +import org.springframework.context.EnvironmentAware; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Import; +import org.springframework.core.env.Environment; +import org.springframework.data.redis.core.RedisTemplate; + +import javax.annotation.PostConstruct; +import java.util.List; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +/** + * RedisTemplate 鍒濆鍖栫被 + * + * @author CJH + * @date 2020骞�12鏈�16鏃�22:38:46 + */ +/**瑕佸湪RedisAutoConfiguration 鑷姩閰嶇疆鍓嶆墽琛�*/ +@AutoConfigureBefore({RedisAutoConfiguration.class}) +@Configuration +/**瀹炵幇 EnvironmentAware 鐢ㄤ簬鑾峰彇鍏ㄥ眬鐜 +瀹炵幇 ApplicationContextAware 鐢ㄤ簬鑾峰彇Spring Context 涓婁笅鏂�*/ +public class RedisBeanInit implements EnvironmentAware, ApplicationContextAware { + private Logger logger = LoggerFactory.getLogger(RedisBeanInit.class); + + /**鐢ㄤ簬鑾峰彇鐜閰嶇疆*/ + private Environment environment; + /**鐢ㄤ簬缁戝畾瀵硅薄*/ + private Binder binder; + /**Spring context*/ + private ApplicationContext applicationContext; + /**绾跨▼瀹夊叏鐨刪ashmap*/ + private Map<String, RedisTemplate> redisTemplateMap = new ConcurrentHashMap<>(); + + @Override + public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { + this.applicationContext = applicationContext; + } + /** + * 璁剧疆鐜 + * + * @param environment + */ + @Override + public void setEnvironment(Environment environment) { + this.environment = environment; + this.binder = Binder.get(environment); + } + + + @PostConstruct // Constructor >> @Autowired >> @PostConstruct 鐢ㄤ簬鎵ц涓�涓� 闈為潤鎬佺殑void 鏂规硶锛屽父搴旂敤浜庡垵濮嬪寲璧勬簮 + public void initAllRedisTemlate() { + logger.info("<<<鍒濆鍖栫郴缁熺殑RedisTemlate寮�濮�>>>"); + RedissondbConfigEntity redissondb; + try { + redissondb = binder.bind("redissondb", RedissondbConfigEntity.class).get(); + } catch (Exception e) { + logger.error("璇诲彇redissondb鐜閰嶇疆澶辫触", e); + return; + } + List<Integer> databases = redissondb.getDatabases(); + if (CollectionUtils.isNotEmpty(databases)) { + databases.forEach(db -> { + Object bean = applicationContext.getBean("redisTemplate" + db); + if (bean != null && bean instanceof RedisTemplate) { + redisTemplateMap.put("redisTemplate" + db, (RedisTemplate) bean); + } else { + throw new RuntimeException("鍒濆鍖朢edisTemplate" + db + "澶辫触锛岃妫�鏌ラ厤缃�"); + } + }); + } + logger.info("宸茬粡瑁呴厤鐨剅edistempleate锛宮ap:{}", redisTemplateMap); + logger.info("<<<鍒濆鍖栫郴缁熺殑RedisTemlate瀹屾瘯>>>"); + } + + @Bean + public RedisManager getRedisManager() { + return new RedisManager(redisTemplateMap); + } +} diff --git a/src/main/java/com/hx/redis/templates/config/RedisTempConfig.java b/src/main/java/com/hx/redis/templates/config/RedisTempConfig.java new file mode 100644 index 0000000..4337a0f --- /dev/null +++ b/src/main/java/com/hx/redis/templates/config/RedisTempConfig.java @@ -0,0 +1,37 @@ +package com.hx.redis.templates.config; + +import com.fasterxml.jackson.annotation.JsonAutoDetect; +import com.fasterxml.jackson.annotation.PropertyAccessor; +import com.fasterxml.jackson.databind.ObjectMapper; +import org.springframework.cache.annotation.EnableCaching; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.data.redis.connection.RedisConnectionFactory; +import org.springframework.data.redis.core.RedisTemplate; +import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer; +import org.springframework.data.redis.serializer.StringRedisSerializer; + +/**閰嶇疆瀹炰緥鍖� + * CJH + */ +@Configuration +@EnableCaching +public class RedisTempConfig { + @Bean + public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) { + RedisTemplate<String, Object> template = new RedisTemplate<String, Object>(); + template.setConnectionFactory(factory); + Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class); + ObjectMapper om = new ObjectMapper(); + om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY); + om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL); + jackson2JsonRedisSerializer.setObjectMapper(om); + StringRedisSerializer stringRedisSerializer = new StringRedisSerializer(); + template.setKeySerializer(stringRedisSerializer); + template.setHashKeySerializer(stringRedisSerializer); + template.setValueSerializer(jackson2JsonRedisSerializer); + template.setHashValueSerializer(jackson2JsonRedisSerializer); + template.afterPropertiesSet(); + return template; + } +} diff --git a/src/main/java/com/hx/redis/templates/config/RedisTempsUtil.java b/src/main/java/com/hx/redis/templates/config/RedisTempsUtil.java new file mode 100644 index 0000000..0b38bb4 --- /dev/null +++ b/src/main/java/com/hx/redis/templates/config/RedisTempsUtil.java @@ -0,0 +1,991 @@ +package com.hx.redis.templates.config; + +import com.hx.redis.templates.manager.RedisManager; +import org.redisson.Redisson; +import org.redisson.api.RedissonClient; +import org.redisson.client.codec.StringCodec; +import org.redisson.config.Config; +import org.redisson.config.SingleServerConfig; +import org.redisson.spring.data.connection.RedissonConnectionFactory; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Bean; +import org.springframework.data.redis.core.RedisTemplate; +import org.springframework.data.redis.core.ZSetOperations; +import org.springframework.data.redis.support.atomic.RedisAtomicLong; +import org.springframework.stereotype.Component; +import org.springframework.util.CollectionUtils; + +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.TimeUnit; + +/** + * <h1>RedisUtil 鎿嶄綔宸ュ叿绫�</h1> + * @author CJH + */ +@Component +public class RedisTempsUtil { + + private Logger logger = LoggerFactory.getLogger(RedisTempsUtil.class); + + @Autowired + private RedisManager redisManager; + + @Autowired + private RedisTemplate redisTemplate; + + /** + * 鎸囧畾缂撳瓨澶辨晥鏃堕棿 + * + * @param key 閿� + * @param time 鏃堕棿(绉�) + * @return + */ + public boolean expire(String key, long time) { + try { + if (time > 0) { + redisTemplate.expire(key, time, TimeUnit.SECONDS); + } + return true; + } catch (Exception e) { + e.printStackTrace(); + return false; + } + } + + + /** + * 鎸囧畾缂撳瓨澶辨晥鏃堕棿 + * + * @param key 閿� + * @param time 鏃堕棿 + * @param timeUnit 鏃堕棿绫诲瀷 + * @return + */ + public boolean expire(String key, long time, TimeUnit timeUnit) { + try { + if (time > 0) { + redisTemplate.expire(key, time, timeUnit); + } + return true; + } catch (Exception e) { + e.printStackTrace(); + return false; + } + } + + /** + * 鏍规嵁key 鑾峰彇杩囨湡鏃堕棿 + * + * @param key 閿� 涓嶈兘涓簄ull + * @return 鏃堕棿(绉�) 杩斿洖0浠h〃涓烘案涔呮湁鏁� + */ + public long getExpire(String key) { + return redisTemplate.getExpire(key, TimeUnit.SECONDS); + } + + /** + * 鍒ゆ柇key鏄惁瀛樺湪 + * + * @param key 閿� + * @return true 瀛樺湪 false涓嶅瓨鍦� + */ + public boolean hasKey(String key) { + try { + return redisTemplate.hasKey(key); + } catch (Exception e) { + e.printStackTrace(); + return false; + } + } + + /** + * 鍒犻櫎缂撳瓨 + * + * @param key 鍙互浼犱竴涓�� 鎴栧涓� + */ + @SuppressWarnings("unchecked") + public void del(String... key) { + if (key != null && key.length > 0) { + if (key.length == 1) { + redisTemplate.delete(key[0]); + } else { + redisTemplate.delete(CollectionUtils.arrayToList(key)); + } + } + } + + // ============================String============================= + + /** + * 鏅�氱紦瀛樿幏鍙� + * + * @param key 閿� + * @return 鍊� + */ + public Object get(String key) { + return key == null ? null : redisTemplate.opsForValue().get(key); + } + + /** + * 鏅�氱紦瀛樻斁鍏� + * + * @param key 閿� + * @param value 鍊� + * @return true鎴愬姛 false澶辫触 + */ + public boolean set(String key, Object value) { + try { + redisTemplate.opsForValue().set(key, value); + return true; + } catch (Exception e) { + e.printStackTrace(); + return false; + } + + } + + + /** + * 鏅�氱紦瀛樻斁鍏� + * + * @param key 閿� + * @param value 鍊� + * @return true鎴愬姛 false澶辫触 + */ + public boolean set(String key, Object value,int db) { + try { + RedisTemplate redisTemplate = redisManager.getRedisTemplate(db); + redisTemplate.opsForValue().set(key, value); + return true; + } catch (Exception e) { + e.printStackTrace(); + return false; + } + + } + + + /** + * 鏅�氱紦瀛樻斁鍏ュ苟璁剧疆鏃堕棿 + * + * @param key 閿� + * @param value 鍊� + * @param time 鏃堕棿(绉�) time瑕佸ぇ浜�0 濡傛灉time灏忎簬绛変簬0 灏嗚缃棤闄愭湡 + * @return true鎴愬姛 false 澶辫触 + */ + public boolean set(String key, Object value, long time) { + try { + if (time > 0) { + redisTemplate.opsForValue().set(key, value, time, TimeUnit.SECONDS); + } else { + set(key, value); + } + return true; + } catch (Exception e) { + e.printStackTrace(); + return false; + } + } + + /** + * 鏅�氱紦瀛樻斁鍏ュ苟璁剧疆鏃堕棿锛屽苟澧炲姞鏃堕棿绫诲瀷閫夋嫨 + * + * @param key 閿� + * @param value 鍊� + * @param time 鏃堕棿 time瑕佸ぇ浜�0 濡傛灉time灏忎簬绛変簬0 灏嗚缃棤闄愭湡 + * @param timeUnit 鏃堕棿绫诲瀷 + * @return + */ + public boolean set(String key, Object value, long time, TimeUnit timeUnit) { + try { + if (time > 0) { + redisTemplate.opsForValue().set(key, value, time, timeUnit); + } else { + set(key, value); + } + return true; + } catch (Exception e) { + e.printStackTrace(); + return false; + } + } + + /** + * 閫掑 + * + * @param key 閿� + * @param delta 瑕佸鍔犲嚑(澶т簬0) + * @return + */ + public long incr(String key, long delta) { + if (delta < 0) { + throw new RuntimeException("閫掑鍥犲瓙蹇呴』澶т簬0"); + } + return redisTemplate.opsForValue().increment(key, delta); + } + + /** + * 閫掑噺 + * + * @param key 閿� + * @param delta 瑕佸噺灏戝嚑(灏忎簬0) + * @return + */ + public long decr(String key, long delta) { + if (delta < 0) { + throw new RuntimeException("閫掑噺鍥犲瓙蹇呴』澶т簬0"); + } + return redisTemplate.opsForValue().increment(key, -delta); + } + + // ================================Map================================= + + /** + * HashGet + * + * @param key 閿� 涓嶈兘涓簄ull + * @param item 椤� 涓嶈兘涓簄ull + * @return 鍊� + */ + public Object hget(String key, String item) { + return redisTemplate.opsForHash().get(key, item); + } + + /** + * 鑾峰彇hashKey瀵瑰簲鐨勬墍鏈夐敭鍊� + * + * @param key 閿� + * @return 瀵瑰簲鐨勫涓敭鍊� + */ + public Map<Object, Object> hmget(String key) { + return redisTemplate.opsForHash().entries(key); + } + + /** + * HashSet + * + * @param key 閿� + * @param map 瀵瑰簲澶氫釜閿�� + * @return true 鎴愬姛 false 澶辫触 + */ + public boolean hmset(String key, Map<String, Object> map) { + try { + redisTemplate.opsForHash().putAll(key, map); + return true; + } catch (Exception e) { + e.printStackTrace(); + return false; + } + } + + /** + * HashSet 骞惰缃椂闂� + * + * @param key 閿� + * @param map 瀵瑰簲澶氫釜閿�� + * @param time 鏃堕棿(绉�) + * @return true鎴愬姛 false澶辫触 + */ + public boolean hmset(String key, Map<String, Object> map, long time) { + try { + redisTemplate.opsForHash().putAll(key, map); + if (time > 0) { + expire(key, time); + } + return true; + } catch (Exception e) { + e.printStackTrace(); + return false; + } + } + + /** + * 鍚戜竴寮爃ash琛ㄤ腑鏀惧叆鏁版嵁,濡傛灉涓嶅瓨鍦ㄥ皢鍒涘缓 + * + * @param key 閿� + * @param item 椤� + * @param value 鍊� + * @return true 鎴愬姛 false澶辫触 + */ + public boolean hset(String key, String item, Object value) { + try { + redisTemplate.opsForHash().put(key, item, value); + return true; + } catch (Exception e) { + e.printStackTrace(); + return false; + } + } + + /** + * 鍚戜竴寮爃ash琛ㄤ腑鏀惧叆鏁版嵁,濡傛灉涓嶅瓨鍦ㄥ皢鍒涘缓 + * + * @param key 閿� + * @param item 椤� + * @param value 鍊� + * @param time 鏃堕棿(绉�) 娉ㄦ剰:濡傛灉宸插瓨鍦ㄧ殑hash琛ㄦ湁鏃堕棿,杩欓噷灏嗕細鏇挎崲鍘熸湁鐨勬椂闂� + * @return true 鎴愬姛 false澶辫触 + */ + public boolean hset(String key, String item, Object value, long time) { + try { + redisTemplate.opsForHash().put(key, item, value); + if (time > 0) { + expire(key, time); + } + return true; + } catch (Exception e) { + e.printStackTrace(); + return false; + } + } + + /** + * 鍒犻櫎hash琛ㄤ腑鐨勫�� + * + * @param key 閿� 涓嶈兘涓簄ull + * @param item 椤� 鍙互浣垮涓� 涓嶈兘涓簄ull + */ + public void hdel(String key, Object... item) { + redisTemplate.opsForHash().delete(key, item); + } + + /** + * 鍒ゆ柇hash琛ㄤ腑鏄惁鏈夎椤圭殑鍊� + * + * @param key 閿� 涓嶈兘涓簄ull + * @param item 椤� 涓嶈兘涓簄ull + * @return true 瀛樺湪 false涓嶅瓨鍦� + */ + public boolean hHasKey(String key, String item) { + return redisTemplate.opsForHash().hasKey(key, item); + } + + /** + * hash閫掑 濡傛灉涓嶅瓨鍦�,灏变細鍒涘缓涓�涓� 骞舵妸鏂板鍚庣殑鍊艰繑鍥� + * + * @param key 閿� + * @param item 椤� + * @param by 瑕佸鍔犲嚑(澶т簬0) + * @return + */ + public double hincr(String key, String item, double by) { + return redisTemplate.opsForHash().increment(key, item, by); + } + + /** + * hash閫掑噺 + * + * @param key 閿� + * @param item 椤� + * @param by 瑕佸噺灏戣(灏忎簬0) + * @return + */ + public double hdecr(String key, String item, double by) { + return redisTemplate.opsForHash().increment(key, item, -by); + } + + // ============================set============================= + + /** + * 鏍规嵁key鑾峰彇Set涓殑鎵�鏈夊�� + * + * @param key 閿� + * @return + */ + public Set<Object> sGet(String key) { + try { + return redisTemplate.opsForSet().members(key); + } catch (Exception e) { + e.printStackTrace(); + return null; + } + } + + /** + * 鏍规嵁key鑾峰彇Set涓殑鎸囧畾鍑犱釜闅忔満鍐呭 + * + * @param key 閿� + * @param size 涓暟 + * @return + */ + public List sRandomGet(String key, Integer size) { + try { + return redisTemplate.opsForSet().randomMembers(key, size); + } catch (Exception e) { + e.printStackTrace(); + return null; + } + } + + /** + * 鏍规嵁value浠庝竴涓猻et涓煡璇�,鏄惁瀛樺湪 + * + * @param key 閿� + * @param value 鍊� + * @return true 瀛樺湪 false涓嶅瓨鍦� + */ + public boolean sHasKey(String key, Object value) { + try { + return redisTemplate.opsForSet().isMember(key, value); + } catch (Exception e) { + e.printStackTrace(); + return false; + } + } + + /** + * 灏嗘暟鎹斁鍏et缂撳瓨 + * + * @param key 閿� + * @param values 鍊� 鍙互鏄涓� + * @return 鎴愬姛涓暟 + */ + public long sSet(String key, Object... values) { + try { + return redisTemplate.opsForSet().add(key, values); + } catch (Exception e) { + e.printStackTrace(); + return 0; + } + } + + /** + * 灏唖et鏁版嵁鏀惧叆缂撳瓨 + * + * @param key 閿� + * @param time 鏃堕棿(绉�) + * @param values 鍊� 鍙互鏄涓� + * @return 鎴愬姛涓暟 + */ + public long sSetAndTime(String key, long time, Object... values) { + try { + Long count = redisTemplate.opsForSet().add(key, values); + if (time > 0) { + expire(key, time); + } + return count; + } catch (Exception e) { + e.printStackTrace(); + return 0; + } + } + + /** + * 鑾峰彇set缂撳瓨鐨勯暱搴� + * + * @param key 閿� + * @return + */ + public long sGetSetSize(String key) { + try { + return redisTemplate.opsForSet().size(key); + } catch (Exception e) { + e.printStackTrace(); + return 0; + } + } + + /** + * 绉婚櫎鍊间负value鐨� + * + * @param key 閿� + * @param values 鍊� 鍙互鏄涓� + * @return 绉婚櫎鐨勪釜鏁� + */ + public long setRemove(String key, Object... values) { + try { + Long count = redisTemplate.opsForSet().remove(key, values); + return count; + } catch (Exception e) { + e.printStackTrace(); + return 0; + } + } + // ===============================list================================= + + /** + * 鑾峰彇list缂撳瓨鐨勫唴瀹� + * + * @param key 閿� + * @param start 寮�濮� + * @param end 缁撴潫 0 鍒� -1浠h〃鎵�鏈夊�� + * @return + */ + public List<Object> lGet(String key, long start, long end) { + try { + return redisTemplate.opsForList().range(key, start, end); + } catch (Exception e) { + e.printStackTrace(); + return null; + } + } + + /** + * 鑾峰彇list缂撳瓨鐨勯暱搴� + * + * @param key 閿� + * @return + */ + public long lGetListSize(String key) { + try { + return redisTemplate.opsForList().size(key); + } catch (Exception e) { + e.printStackTrace(); + return 0; + } + } + + /** + * 閫氳繃绱㈠紩 鑾峰彇list涓殑鍊� + * + * @param key 閿� + * @param index 绱㈠紩 index>=0鏃讹紝 0 琛ㄥご锛�1 绗簩涓厓绱狅紝渚濇绫绘帹锛沬ndex<0鏃讹紝-1锛岃〃灏撅紝-2鍊掓暟绗簩涓厓绱狅紝渚濇绫绘帹 + * @return + */ + public Object lGetIndex(String key, long index) { + try { + return redisTemplate.opsForList().index(key, index); + } catch (Exception e) { + e.printStackTrace(); + return null; + } + } + + /** + * 灏唋ist鏀惧叆缂撳瓨 + * + * @param key 閿� + * @param value 鍊� + * @return + */ + public boolean lSet(String key, Object value) { + try { + redisTemplate.opsForList().rightPush(key, value); + return true; + } catch (Exception e) { + e.printStackTrace(); + return false; + } + } + + /** + * 灏唋ist鏀惧叆缂撳瓨 + * + * @param key 閿� + * @param value 鍊� + * @param time 鏃堕棿(绉�) + * @return + */ + public boolean lSet(String key, Object value, long time) { + try { + redisTemplate.opsForList().rightPush(key, value); + if (time > 0) { + expire(key, time); + } + return true; + } catch (Exception e) { + e.printStackTrace(); + return false; + } + } + + /** + * 灏唋ist鏀惧叆缂撳瓨 + * + * @param key 閿� + * @param value 鍊� + * @return + */ + public boolean lSetAll(String key, List<Object> value) { + try { + redisTemplate.opsForList().rightPushAll(key, value); + return true; + } catch (Exception e) { + e.printStackTrace(); + return false; + } + } + + /** + * 灏唋ist鏀惧叆缂撳瓨 + * + * @param key 閿� + * @param value 鍊� + * @param time 鏃堕棿(绉�) + * @return + */ + public boolean lSetAll(String key, List<Object> value, long time) { + try { + // 璁剧疆瓒呮椂鏃堕棿 鍘熷瓙鍖� + redisTemplate.opsForList().rightPushAll(key, value); + if (time > 0) { + expire(key, time); + } + return true; + } catch (Exception e) { + e.printStackTrace(); + return false; + } + } + + /** + * 鏍规嵁绱㈠紩淇敼list涓殑鏌愭潯鏁版嵁 + * + * @param key 閿� + * @param index 绱㈠紩 + * @param value 鍊� + * @return + */ + public boolean lUpdateIndex(String key, long index, Object value) { + try { + redisTemplate.opsForList().set(key, index, value); + return true; + } catch (Exception e) { + e.printStackTrace(); + return false; + } + } + + /** + * 绉婚櫎N涓�间负value + * + * @param key 閿� + * @param count 绉婚櫎澶氬皯涓� + * @param value 鍊� + * @return 绉婚櫎鐨勪釜鏁� + */ + public long lRemove(String key, long count, Object value) { + try { + Long remove = redisTemplate.opsForList().remove(key, count, value); + return remove; + } catch (Exception e) { + e.printStackTrace(); + return 0; + } + } + + + /** + * 鏍规嵁缁欏畾鐨勫竷闅嗚繃婊ゅ櫒娣诲姞鍊� + * + * @param bloomFilterHelper bloom甯冮殕杩囨护鍣ㄨВ鏋愮被 + * @param key redis 鐨刱ey + * @param value redis 鐨剉alue + * @param <T> 鍊肩殑绫诲瀷 + */ + /*public <T> void addByBloomFilter(BloomFilterHelper<T> bloomFilterHelper, String key, T value) { + Preconditions.checkArgument(bloomFilterHelper != null, "bloomFilterHelper涓嶈兘涓虹┖"); + int[] offset = bloomFilterHelper.murmurHashOffset(value); + for (int i : offset) { + redisTemplate.opsForValue().setBit(key, i, true); + } + }*/ + + + /** + * 鏍规嵁缁欏畾鐨勫竷闅嗚繃婊ゅ櫒鍒ゆ柇鍊兼槸鍚﹀瓨鍦� + * + * @param bloomFilterHelper bloom甯冮殕杩囨护鍣ㄨВ鏋愮被 + * @param key redis 鐨刱ey + * @param value redis 鐨剉alue + * @param <T> 鍊肩殑绫诲瀷 + * @return 瀛樺湪 true + */ + /* public <T> boolean includeByBloomFilter(BloomFilterHelper<T> bloomFilterHelper, String key, T value) { + Preconditions.checkArgument(bloomFilterHelper != null, "bloomFilterHelper涓嶈兘涓虹┖"); + int[] offset = bloomFilterHelper.murmurHashOffset(value); + for (int i : offset) { + if (!redisTemplate.opsForValue().getBit(key, i)) { + return false; + } + } + return true; + }*/ + + + /** + * zset 娣诲姞鍏冪礌 + * + * @param key + * @param time + * @param tuples + * @return + */ + public long zsSetAndTime(String key, long time, Set<ZSetOperations.TypedTuple<Object>> tuples) { + try { + Long count = redisTemplate.opsForZSet().add(key, tuples); + if (time > 0) { + expire(key, time); + } + return count; + } catch (Exception e) { + e.printStackTrace(); + return 0; + } + } + + /** + * zset 娣诲姞鍏冪礌 + * + * @param key + * @param tuples + * @return + */ + public long zsSetAndTime(String key, Set<ZSetOperations.TypedTuple<Object>> tuples) { + try { + Long count = redisTemplate.opsForZSet().add(key, tuples); + return count; + } catch (Exception e) { + e.printStackTrace(); + return 0; + } + } + + /** + * zs 绉婚櫎鍏冪礌 + * + * @param key + * @param values + * @return + */ + public long zsRemove(String key, Object... values) { + try { + Long remove = redisTemplate.opsForZSet().remove(key, values); + return remove; + } catch (Exception e) { + e.printStackTrace(); + return 0; + } + } + + + /** + * 閫氳繃绱㈠紩鍖洪棿杩斿洖鏈夊簭闆嗗悎鎴愭寚瀹氬尯闂村唴鐨勬垚鍛橈紝鍏朵腑鏈夊簭闆嗘垚鍛樻寜鍒嗘暟鍊奸�掑(浠庡皬鍒板ぇ)椤哄簭鎺掑垪 + * + * @param key 閿� + * @param start 璧峰浣嶇疆 0 + * @param end 鏈熬浣嶇疆 -1 + * @return 0 -1 杩斿洖鎸夊垎鏁伴�掑鐨勯『搴忛泦鍚� 浠呰繑鍥� key + */ + public Set zsGet(String key, long start, long end) { + try { + return redisTemplate.opsForZSet().range(key, start, end); + } catch (Exception e) { + e.printStackTrace(); + return null; + } + } + + + /** + * 閫氳繃绱㈠紩鍖洪棿杩斿洖鏈夊簭闆嗗悎鎴愭寚瀹氬尯闂村唴鐨勬垚鍛橈紝鍏朵腑鏈夊簭闆嗘垚鍛樻寜鍒嗘暟鍊奸�掑(浠庡皬鍒板ぇ)椤哄簭鎺掑垪 + * + * @param key 閿� + * @param start 璧峰浣嶇疆 0 + * @param end 鏈熬浣嶇疆 -1 + * @return 0 -1 杩斿洖鎸夊垎鏁伴�掑鐨勯『搴忛泦鍚堬紝杩斿洖鎴愬憳瀵硅薄 + */ + public Set zsGetWithScores(String key, long start, long end) { + try { + return redisTemplate.opsForZSet().rangeWithScores(key, start, end); + } catch (Exception e) { + e.printStackTrace(); + return null; + } + } + + /** + * 閫氳繃绱㈠紩鍖洪棿杩斿洖鏈夊簭闆嗗悎鎴愭寚瀹氬尯闂村唴鐨勬垚鍛橈紝鍏朵腑鏈夊簭闆嗘垚鍛樻寜鍒嗘暟鍊奸�掑噺(浠庡ぇ鍒板皬)椤哄簭鎺掑垪 + * + * @param key 閿� + * @param start 璧峰浣嶇疆 0 + * @param end 鏈熬浣嶇疆 -1 + * @return 0 -1 杩斿洖鎸夊垎鏁伴�掑鐨勯『搴忛泦鍚� 浠呰繑鍥� key + */ + public Set zsGetReverse(String key, long start, long end) { + try { + return redisTemplate.opsForZSet().reverseRange(key, start, end); + } catch (Exception e) { + e.printStackTrace(); + return null; + } + } + + + /** + * 閫氳繃绱㈠紩鍖洪棿杩斿洖鏈夊簭闆嗗悎鎴愭寚瀹氬尯闂村唴鐨勬垚鍛樺璞★紝鍏朵腑鏈夊簭闆嗘垚鍛樻寜鍒嗘暟鍊奸�掑噺(浠庡ぇ鍒板皬)椤哄簭鎺掑垪 + * + * @param key 閿� + * @param start 璧峰浣嶇疆 0 + * @param end 鏈熬浣嶇疆 -1 + * @return 0 -1 杩斿洖鎸夊垎鏁伴�掑鐨勯『搴忛泦鍚堬紝杩斿洖鎴愬憳瀵硅薄 + */ + public Set zsGetReverseWithScores(String key, long start, long end) { + try { + return redisTemplate.opsForZSet().reverseRangeWithScores(key, start, end); + } catch (Exception e) { + e.printStackTrace(); + return null; + } + } + + /** + * 鍒囨崲鏁版嵁搴� + * + * @return + */ + public RedisTemplate switchDatabase(Integer dbIndex) { + logger.info("閲嶆柊寤虹珛redis鏁版嵁搴撹繛鎺ュ紑濮�"); + Config config = new Config(); + config.setCodec(StringCodec.INSTANCE); + SingleServerConfig singleConfig = config.useSingleServer(); + singleConfig.setAddress("redis://120.53.235.63:6378"); + singleConfig.setPassword("gengzi666"); + singleConfig.setDatabase(dbIndex); + RedissonClient redissonClient = Redisson.create(config); + RedissonConnectionFactory redisConnectionFactory = new RedissonConnectionFactory(redissonClient); + redisTemplate.setConnectionFactory(redisConnectionFactory); + logger.info("閲嶆柊寤虹珛redis鏁版嵁搴撹繛鎺ョ粨鏉�"); + return redisTemplate; + } + + /** + * 閫氳繃绱㈠紩鍖洪棿杩斿洖鏈夊簭闆嗗悎鎴愭寚瀹氬尯闂村唴鐨勬垚鍛橈紝鍏朵腑鏈夊簭闆嗘垚鍛樻寜鍒嗘暟鍊奸�掑(浠庡皬鍒板ぇ)椤哄簭鎺掑垪 + * + * @param key 閿� + * @param start 璧峰浣嶇疆 0 + * @param end 鏈熬浣嶇疆 -1 + * @return 0 -1 杩斿洖鎸夊垎鏁伴�掑鐨勯『搴忛泦鍚� 浠呰繑鍥� key + */ + public Set zsGet(String key, long start, long end, Integer db) { + try { + RedisTemplate redisTemplate = redisManager.getRedisTemplate(db); + return redisTemplate.opsForZSet().range(key, start, end); + } catch (Exception e) { + e.printStackTrace(); + return null; + } + } + + + /** + * 閫氳繃绱㈠紩鍖洪棿杩斿洖鏈夊簭闆嗗悎鎴愭寚瀹氬尯闂村唴鐨勬垚鍛橈紝鍏朵腑鏈夊簭闆嗘垚鍛樻寜鍒嗘暟鍊奸�掑(浠庡皬鍒板ぇ)椤哄簭鎺掑垪 + * + * @param key 閿� + * @param start 璧峰浣嶇疆 0 + * @param end 鏈熬浣嶇疆 -1 + * @return 0 -1 杩斿洖鎸夊垎鏁伴�掑鐨勯『搴忛泦鍚堬紝杩斿洖鎴愬憳瀵硅薄 + */ + public Set zsGetWithScores(String key, long start, long end, Integer db) { + try { + RedisTemplate redisTemplate = redisManager.getRedisTemplate(db); + return redisTemplate.opsForZSet().rangeWithScores(key, start, end); + } catch (Exception e) { + e.printStackTrace(); + return null; + } + } + + /** + * 閫氳繃绱㈠紩鍖洪棿杩斿洖鏈夊簭闆嗗悎鎴愭寚瀹氬尯闂村唴鐨勬垚鍛橈紝鍏朵腑鏈夊簭闆嗘垚鍛樻寜鍒嗘暟鍊奸�掑噺(浠庡ぇ鍒板皬)椤哄簭鎺掑垪 + * + * @param key 閿� + * @param start 璧峰浣嶇疆 0 + * @param end 鏈熬浣嶇疆 -1 + * @return 0 -1 杩斿洖鎸夊垎鏁伴�掑鐨勯『搴忛泦鍚� 浠呰繑鍥� key + */ + public Set zsGetReverse(String key, long start, long end, Integer db) { + try { + RedisTemplate redisTemplate = redisManager.getRedisTemplate(db); + return redisTemplate.opsForZSet().reverseRange(key, start, end); + } catch (Exception e) { + e.printStackTrace(); + return null; + } + } + + + /** + * 閫氳繃绱㈠紩鍖洪棿杩斿洖鏈夊簭闆嗗悎鎴愭寚瀹氬尯闂村唴鐨勬垚鍛樺璞★紝鍏朵腑鏈夊簭闆嗘垚鍛樻寜鍒嗘暟鍊奸�掑噺(浠庡ぇ鍒板皬)椤哄簭鎺掑垪 + * + * @param key 閿� + * @param start 璧峰浣嶇疆 0 + * @param end 鏈熬浣嶇疆 -1 + * @return 0 -1 杩斿洖鎸夊垎鏁伴�掑鐨勯『搴忛泦鍚堬紝杩斿洖鎴愬憳瀵硅薄 + */ + public Set zsGetReverseWithScores(String key, long start, long end, Integer db) { + try { + RedisTemplate redisTemplate = redisManager.getRedisTemplate(db); + return redisTemplate.opsForZSet().reverseRangeWithScores(key, start, end); + } catch (Exception e) { + e.printStackTrace(); + return null; + } + } + + /** + * zset 娣诲姞鍏冪礌 + * + * @param key + * @param time + * @param tuples + * @return + */ + public long zsSetAndTime(String key, long time, Set<ZSetOperations.TypedTuple<Object>> tuples, Integer db) { + try { + RedisTemplate redisTemplate = redisManager.getRedisTemplate(db); + Long count = redisTemplate.opsForZSet().add(key, tuples); + if (time > 0) { + expire(key, time); + } + return count; + } catch (Exception e) { + e.printStackTrace(); + return 0; + } + } + + /** + * zset 娣诲姞鍏冪礌 + * + * @param key + * @param tuples + * @return + */ + public long zsSetAndTime(String key, Set<ZSetOperations.TypedTuple<Object>> tuples, Integer db) { + try { + // 鏍规嵁db搴忓彿锛岃幏鍙栧搴旂殑 RedisTemplate + RedisTemplate redisTemplate = redisManager.getRedisTemplate(db); + Long count = redisTemplate.opsForZSet().add(key, tuples); + return count; + } catch (Exception e) { + e.printStackTrace(); + return 0; + } + } + + /** + * zs 绉婚櫎鍏冪礌 + * + * @param key + * @param values + * @return + */ + public long zsRemove(String key, Integer db, Object... values) { + try { + RedisTemplate redisTemplate = redisManager.getRedisTemplate(db); + Long remove = redisTemplate.opsForZSet().remove(key, values); + return remove; + } catch (Exception e) { + e.printStackTrace(); + return 0; + } + } + +} diff --git a/src/main/java/com/hx/redis/templates/entity/RedissondbConfigEntity.java b/src/main/java/com/hx/redis/templates/entity/RedissondbConfigEntity.java new file mode 100644 index 0000000..a70cfd3 --- /dev/null +++ b/src/main/java/com/hx/redis/templates/entity/RedissondbConfigEntity.java @@ -0,0 +1,30 @@ +package com.hx.redis.templates.entity; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.io.Serializable; +import java.util.ArrayList; +import java.util.List; + +/** + * redisconfig 閰嶇疆瀹炰綋绫� + * + * @author CJH + * @date 2022-06-27 + */ +@Data +@AllArgsConstructor +@NoArgsConstructor +public class RedissondbConfigEntity implements Serializable { + /**鍦板潃*/ + private String host; + /**绔彛*/ + private String port; + /**瀵嗙爜*/ + private String password; + /**鎵�鏈夌殑db搴忓彿*/ + private List<Integer> databases = new ArrayList<>(); +} + diff --git a/src/main/java/com/hx/redis/templates/manager/RedisManager.java b/src/main/java/com/hx/redis/templates/manager/RedisManager.java new file mode 100644 index 0000000..68c4256 --- /dev/null +++ b/src/main/java/com/hx/redis/templates/manager/RedisManager.java @@ -0,0 +1,41 @@ +package com.hx.redis.templates.manager; + +import org.springframework.data.redis.core.RedisTemplate; + +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +/** + * redis绠$悊 + * + * @author CJH + * @date 2022-06-27 + */ +public class RedisManager { + + private Map<String, RedisTemplate> redisTemplateMap = new ConcurrentHashMap<>(); + + /** + * 鏋勯�犳柟娉曞垵濮嬪寲 redisTemplateMap 鐨勬暟鎹� + * + * @param redisTemplateMap + */ + public RedisManager(Map<String, RedisTemplate> redisTemplateMap) { + this.redisTemplateMap = redisTemplateMap; + } + + /** + * 鏍规嵁鏁版嵁搴撳簭鍙凤紝杩斿洖瀵瑰簲鐨凴edisTemplate + * + * @param dbIndex 搴忓彿 + * @return {@link RedisTemplate} + */ + public RedisTemplate getRedisTemplate(Integer dbIndex) { + RedisTemplate redisTemplate = redisTemplateMap.get("redisTemplate" + dbIndex); + if (redisTemplate == null) { + throw new RuntimeException("Map涓嶅瓨鍦ㄨredisTemplate"); + } + return redisTemplate; + } + +} diff --git a/src/main/java/com/hx/redis/templates/register/RedisRegister.java b/src/main/java/com/hx/redis/templates/register/RedisRegister.java new file mode 100644 index 0000000..6387306 --- /dev/null +++ b/src/main/java/com/hx/redis/templates/register/RedisRegister.java @@ -0,0 +1,141 @@ +package com.hx.redis.templates.register; + +import com.fasterxml.jackson.annotation.JsonAutoDetect; +import com.fasterxml.jackson.annotation.PropertyAccessor; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.jsontype.impl.LaissezFaireSubTypeValidator; +import com.hx.redis.templates.entity.RedissondbConfigEntity; +import org.apache.commons.collections.CollectionUtils; +import org.redisson.Redisson; +import org.redisson.api.RedissonClient; +import org.redisson.client.codec.StringCodec; +import org.redisson.config.Config; +import org.redisson.config.SingleServerConfig; +import org.redisson.spring.data.connection.RedissonConnectionFactory; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.config.AutowireCapableBeanFactory; +import org.springframework.beans.factory.support.BeanDefinitionRegistry; +import org.springframework.beans.factory.support.GenericBeanDefinition; +import org.springframework.boot.context.properties.bind.Binder; +import org.springframework.context.EnvironmentAware; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.ImportBeanDefinitionRegistrar; +import org.springframework.core.env.Environment; +import org.springframework.core.type.AnnotationMetadata; +import org.springframework.data.redis.connection.RedisConnectionFactory; +import org.springframework.data.redis.core.RedisTemplate; +import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer; +import org.springframework.data.redis.serializer.RedisSerializer; +import org.springframework.data.redis.serializer.StringRedisSerializer; + +import java.util.List; + +/** + * <h1>redistemplate鍒濆鍖�</h1> + * <p> + * 浣滅敤锛� + * <p> + * 璇诲彇绯荤粺閰嶇疆锛岀郴缁熷惎鍔ㄦ椂锛岃鍙杛edis 鐨勯厤缃紝鍒濆鍖栨墍鏈夌殑redistemplate + * 骞跺姩鎬佹敞鍐屼负bean + * + * @author gengzi + * @date 2021骞�1鏈�5鏃�22:16:29 + */ +@Configuration +public class RedisRegister implements EnvironmentAware, ImportBeanDefinitionRegistrar { + + private Logger logger = LoggerFactory.getLogger(RedisRegister.class); + + /**鐢ㄤ簬鑾峰彇鐜閰嶇疆*/ + private Environment environment; + /**鐢ㄤ簬缁戝畾瀵硅薄*/ + private Binder binder; + + /** + * 璁剧疆鐜 + * + * @param environment + */ + @Override + public void setEnvironment(Environment environment) { + this.environment = environment; + this.binder = Binder.get(environment); + } + + /** + * 娉ㄥ唽bean + * + * @param importingClassMetadata + * @param registry + */ + @Override + public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) { + logger.info("銆娿�娿�婂姩鎬佹敞鍐宐ean寮�濮嬨�嬨�嬨��"); + RedissondbConfigEntity redissondb; + try { + redissondb = binder.bind("redissondb", RedissondbConfigEntity.class).get(); + } catch (Exception e) { + logger.error("璇诲彇redissondb鐜閰嶇疆澶辫触", e); + return; + } + List<Integer> databases = redissondb.getDatabases(); + if (CollectionUtils.isNotEmpty(databases)) { + databases.forEach(db -> { + // 鍗曟満妯″紡锛岄泦缇ゅ彧鑳戒娇鐢╠b0 + Config config = new Config(); + config.setCodec(StringCodec.INSTANCE); + SingleServerConfig singleConfig = config.useSingleServer(); + singleConfig.setAddress("redis://"+redissondb.getHost()+":"+redissondb.getPort()); + singleConfig.setPassword(redissondb.getPassword()); + singleConfig.setDatabase(db); + + System.out.println("getHost:"+singleConfig.getAddress()); + System.out.println("getHost:"+singleConfig.getPassword()); + RedissonClient redissonClient = Redisson.create(config); + // 鏋勯�燫edissonConnectionFactory + RedissonConnectionFactory redisConnectionFactory = new RedissonConnectionFactory(redissonClient); + // bean瀹氫箟 + GenericBeanDefinition redisTemplate = new GenericBeanDefinition(); + // 璁剧疆bean 鐨勭被鍨� + redisTemplate.setBeanClass(RedisTemplate.class); + // 璁剧疆鑷姩娉ㄥ叆鐨勫舰寮忥紝鏍规嵁鍚嶇О + redisTemplate.setAutowireMode(AutowireCapableBeanFactory.AUTOWIRE_BY_NAME); + // redisTemplate 鐨勫睘鎬ч厤缃� + redisTemplate(redisTemplate, redisConnectionFactory); + // 娉ㄥ唽Bean + registry.registerBeanDefinition("redisTemplate" + db, redisTemplate); + }); + } + logger.info("銆娿�娿�婂姩鎬佹敞鍐宐ean缁撴潫銆嬨�嬨��"); + + } + + /** + * redisTemplate 鐨勫睘鎬ч厤缃� + * + * @param redisTemplate 娉涘瀷bean + * @param redisConnectionFactory 杩炴帴宸ュ巶 + * @return + */ + public GenericBeanDefinition redisTemplate(GenericBeanDefinition redisTemplate, RedisConnectionFactory redisConnectionFactory) { + RedisSerializer<String> stringRedisSerializer = new StringRedisSerializer(); + Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class); + // 瑙e喅鏌ヨ缂撳瓨杞崲寮傚父鐨勯棶棰� + ObjectMapper om = new ObjectMapper(); + om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY); + om.activateDefaultTyping(LaissezFaireSubTypeValidator.instance, + ObjectMapper.DefaultTyping.NON_FINAL); + jackson2JsonRedisSerializer.setObjectMapper(om); + // key閲囩敤String鐨勫簭鍒楀寲鏂瑰紡锛寁alue閲囩敤json搴忓垪鍖栨柟寮� + // 閫氳繃鏂规硶璁剧疆灞炴�у�� + redisTemplate.getPropertyValues().add("connectionFactory", redisConnectionFactory); + redisTemplate.getPropertyValues().add("keySerializer", stringRedisSerializer); + redisTemplate.getPropertyValues().add("hashKeySerializer", stringRedisSerializer); + redisTemplate.getPropertyValues().add("valueSerializer", jackson2JsonRedisSerializer); + redisTemplate.getPropertyValues().add("hashValueSerializer", jackson2JsonRedisSerializer); + + return redisTemplate; + } +} + -- Gitblit v1.8.0