{"id":2146,"date":"2023-04-02T13:20:45","date_gmt":"2023-04-02T05:20:45","guid":{"rendered":"https:\/\/www.appblog.cn\/?p=2146"},"modified":"2023-04-05T19:54:10","modified_gmt":"2023-04-05T11:54:10","slug":"spring-boot-and-redis-bloom-filter-anti-malicious-traffic-breakdown-cache","status":"publish","type":"post","link":"https:\/\/www.appblog.cn\/index.php\/2023\/04\/02\/spring-boot-and-redis-bloom-filter-anti-malicious-traffic-breakdown-cache\/","title":{"rendered":"SpringBoot+Redis\u5e03\u9686\u8fc7\u6ee4\u5668\u9632\u6076\u610f\u6d41\u91cf\u51fb\u7a7f\u7f13\u5b58"},"content":{"rendered":"<h2>\u5e03\u9686\u8fc7\u6ee4\u5668<\/h2>\n<p>BloomFilter\u662f\u4e00\u79cd\u7a7a\u95f4\u6548\u7387\u7684\u6982\u7387\u578b\u6570\u636e\u7ed3\u6784\uff0c\u7531Burton Howard Bloom 1970\u5e74\u63d0\u51fa\u7684\u3002\u901a\u5e38\u7528\u6765\u5224\u65ad\u4e00\u4e2a\u5143\u7d20\u662f\u5426\u5728\u96c6\u5408\u4e2d\u3002\u5177\u6709\u6781\u9ad8\u7684\u7a7a\u95f4\u6548\u7387\uff0c\u4f46\u662f\u4f1a\u5e26\u6765\u5047\u9633\u6027(False positive)\u7684\u9519\u8bef\u3002<\/p>\n<p><!-- more --><\/p>\n<ul>\n<li>False positive &amp;&amp; False negatives: \u7531\u4e8eBloomFiter\u727a\u7272\u4e86\u4e00\u5b9a\u7684\u51c6\u786e\u7387\u6362\u53d6\u7a7a\u95f4\u6548\u7387\u3002\u6240\u4ee5\u5e26\u6765\u4e86False positive\u7684\u95ee\u9898<\/li>\n<li>False positive: BloomFilter\u5728\u5224\u65ad\u4e00\u4e2a\u5143\u7d20\u5728\u96c6\u5408\u4e2d\u7684\u65f6\u5019\uff0c\u4f1a\u51fa\u73b0\u4e00\u5b9a\u7684\u9519\u8bef\u7387\uff0c\u8fd9\u4e2a\u9519\u8bef\u7387\u79f0\u4e3aFalse positive\u7684\u3002\u901a\u5e38\u7f29\u5199\u4e3afpp<\/li>\n<li>False negatives: BloomFilter\u5224\u65ad\u4e00\u4e2a\u5143\u7d20\u4e0d\u5728\u96c6\u5408\u4e2d\u7684\u65f6\u5019\u7684\u9519\u8bef\u7387\u3002BloomFilter\u5224\u65ad\u8be5\u5143\u7d20\u4e0d\u5728\u96c6\u5408\u4e2d\uff0c\u5219\u8be5\u5143\u7d20\u4e00\u5b9a\u4e0d\u518d\u96c6\u5408\u4e2d\u3002\u6545False negatives\u6982\u7387\u4e3a0<\/li>\n<\/ul>\n<p>BloomFilter\u4f7f\u7528\u957f\u5ea6\u4e3am bit\u7684\u5b57\u8282\u6570\u7ec4\uff0c\u4f7f\u7528k\u4e2ahash\u51fd\u6570\uff0c\u589e\u52a0\u4e00\u4e2a\u5143\u7d20: \u901a\u8fc7k\u6b21hash\u5c06\u5143\u7d20\u6620\u5c04\u5230\u5b57\u8282\u6570\u7ec4\u4e2dk\u4e2a\u4f4d\u7f6e\u4e2d\uff0c\u5e76\u8bbe\u7f6e\u5bf9\u5e94\u4f4d\u7f6e\u7684\u5b57\u8282\u4e3a1\u3002<br \/>\n\u67e5\u8be2\u5143\u7d20\u662f\u5426\u5b58\u5728: \u5c06\u5143\u7d20k\u6b21hash\u5f97\u5230k\u4e2a\u4f4d\u7f6e\uff0c\u5982\u679c\u5bf9\u5e94k\u4e2a\u4f4d\u7f6e\u7684bit\u662f1\u5219\u8ba4\u4e3a\u5b58\u5728\uff0c\u53cd\u4e4b\u5219\u8ba4\u4e3a\u4e0d\u5b58\u5728\u3002<\/p>\n<p>\u7531\u4e8e\u5b83\u91cc\u9762\u5b58\u7684\u90fd\u662fbit\uff0c\u56e0\u6b64\u8fd9\u4e2a\u6570\u636e\u91cf\u4f1a\u5f88\u5c0f\u5f88\u5c0f\uff0c\u5c0f\u5230\u4ec0\u4e48\u6837\u7684\u7a0b\u5ea6\u5462\uff1f\u5728\u5199\u672c\u535a\u5ba2\u65f6\u6211\u63d2\u4e86100\u4e07\u6761email\u4fe1\u606f\u8fdb\u5165Redis\u7684bloom filter\u4e5f\u53ea\u5360\u7528\u4e863Mb\u4e0d\u5230\u3002<\/p>\n<p>Bloom Filter\u4f1a\u6709\u51e0\u6bd4\u8f83\u5173\u952e\u7684\u503c\uff0c\u6839\u636e\u8fd9\u4e2a\u503c\u4f60\u662f\u5927\u81f4\u53ef\u4ee5\u7b97\u51fa\u653e\u591a\u5c11\u6761\u6570\u636e\u7136\u540e\u5b83\u7684\u8bef\u4f24\u7387\u5728\u591a\u5c11\u65f6\u4f1a\u5360\u7528\u591a\u5c11\u7cfb\u7edf\u8d44\u6e90\u7684\u3002\u8fd9\u4e2a\u7b97\u6cd5\u6709\u4e00\u4e2a\u7f51\u5740\uff1a<a target=\"_blank\" rel=\"noopener\" href=\"https:\/\/krisives.github.io\/bloom-calculator\">https:\/\/krisives.github.io\/bloom-calculator<\/a> \uff0c\u6211\u4eec\u653e\u5165100\u4e07\u6761\u6570\u636e\uff0c\u5047\u8bbe\u8bef\u4f24\u7387\u57280.001%\uff0c\u5b83\u81ea\u52a8\u5f97\u51faRedis\u9700\u8981\u7533\u8bf7\u7684\u7cfb\u7edf\u5185\u5b58\u8d44\u6e90\u662f\u591a\u5c11\uff1f<\/p>\n<p><img decoding=\"async\" src=\"http:\/\/www.yezhou.me\/AppBlog\/images\/Java\/Bloom%20Filter%20Calculator.png\" alt=\"Bloom Filter Calculator\" \/><\/p>\n<p>\u90a3\u4e48\u600e\u4e48\u89e3\u51b3\u8fd9\u4e2a\u8bef\u4f24\u7387\u5462\uff1f\u5f88\u7b80\u5355\u7684\uff0c\u5f53\u6709\u8bef\u4f24\u65f6\u4e1a\u52a1\u6216\u8005\u662f\u8fd0\u8425\u4f1a\u6765\u62a5\u8bef\u4f24\u7387\uff0c\u8fd9\u65f6\u53ea\u8981\u6dfb\u52a0\u4e00\u4e2a\u767d\u540d\u5355\u5373\u53ef\uff0c\u76f8\u5bf9\u4e8e100\u4e07\u6761\u6570\u636e\u6765\u8bf4\uff0c1000\u4e2a\u767d\u540d\u5355\u4e0d\u662f\u95ee\u9898\u3002\u5e76\u4e14bloom filter\u7684\u8fd4\u56de\u901f\u5ea6\u8d85\u5757\uff0c80-100\u6beb\u79d2\u5185\u5373\u8fd4\u56de\u8c03\u7528\u7aef\u8be5Key\u5b58\u5728\u6216\u8005\u662f\u4e0d\u5b58\u5728\u3002<\/p>\n<h2>\u7ed9Redis\u5b89\u88c5Bloom Filter<\/h2>\n<p>Redis\u4ece4.0\u624d\u5f00\u59cb\u652f\u6301bloom filter\uff0c\u56e0\u6b64\u672c\u4f8b\u4e2d\u6211\u4eec\u4f7f\u7528\u7684\u662fRedis 5.4<\/p>\n<p>Redis\u7684bloom filter\u4e0b\u8f7d\u5730\u5740\uff1a<a target=\"_blank\" rel=\"noopener\" href=\"https:\/\/github.com\/RedisLabsModules\/redisbloom.git\">https:\/\/github.com\/RedisLabsModules\/redisbloom.git<\/a><\/p>\n<pre><code class=\"language-bash\">git clone https:\/\/github.com\/RedisLabsModules\/redisbloom.git\ncd redisbloom\nmake # \u7f16\u8bd1<\/code><\/pre>\n<p>\u8ba9Redis\u542f\u52a8\u65f6\u53ef\u4ee5\u52a0\u8f7dbloom filter\u6709\u4e24\u79cd\u65b9\u5f0f\uff1a<\/p>\n<p>\uff081\uff09<strong>\u624b\u5de5\u52a0\u8f7d\u5f0f<\/strong><\/p>\n<pre><code class=\"language-bash\">redis-server --loadmodule .\/redisbloom\/rebloom.so<\/code><\/pre>\n<p>\uff082\uff09<strong>\u6bcf\u6b21\u542f\u52a8\u81ea\u52a0\u8f7d<\/strong><\/p>\n<p>\u7f16\u8f91Redis\u7684<code>redis.conf<\/code>\u6587\u4ef6\uff0c\u52a0\u5165\uff1a<\/p>\n<pre><code class=\"language-bash\">loadmodule \/soft\/redisbloom\/redisbloom.so<\/code><\/pre>\n<h2>\u5728Redis\u91cc\u4f7f\u7528Bloom Filter<\/h2>\n<p>\u57fa\u672c\u6307\u4ee4\uff1a<\/p>\n<ul>\n<li><code>bf.reserve {key} {error_rate} {size}<\/code><\/li>\n<\/ul>\n<pre><code class=\"language-bash\">127.0.0.1:6379&gt; bf.reserve userid 0.01 100000\nOK<\/code><\/pre>\n<p>\u4e0a\u9762\u8fd9\u6761\u547d\u4ee4\u5c31\u662f\uff1a\u521b\u5efa\u4e00\u4e2a\u7a7a\u7684\u5e03\u9686\u8fc7\u6ee4\u5668\uff0c\u5e76\u8bbe\u7f6e\u4e00\u4e2a\u671f\u671b\u7684\u9519\u8bef\u7387\u548c\u521d\u59cb\u5927\u5c0f\u3002{error_rate}\u8fc7\u6ee4\u5668\u7684\u9519\u8bef\u7387\u57280-1\u4e4b\u95f4\uff0c\u5982\u679c\u8981\u8bbe\u7f6e0.1%\uff0c\u5219\u5e94\u8be5\u662f0.001\u3002\u8be5\u6570\u503c\u8d8a\u63a5\u8fd10\uff0c\u5185\u5b58\u6d88\u8017\u8d8a\u5927\uff0c\u5bf9cpu\u5229\u7528\u7387\u8d8a\u9ad8<\/p>\n<ul>\n<li><code>bf.add {key} {item}<\/code><\/li>\n<\/ul>\n<pre><code class=\"language-bash\">127.0.0.1:6379&gt; bf.add userid &#039;181920&#039;\n(integer) 1<\/code><\/pre>\n<p>\u4e0a\u9762\u8fd9\u6761\u547d\u4ee4\u5c31\u662f\uff1a\u5f80\u8fc7\u6ee4\u5668\u4e2d\u6dfb\u52a0\u5143\u7d20\u3002\u5982\u679ckey\u4e0d\u5b58\u5728\uff0c\u8fc7\u6ee4\u5668\u4f1a\u81ea\u52a8\u521b\u5efa<\/p>\n<ul>\n<li><code>bf.exists {key} {item}<\/code><\/li>\n<\/ul>\n<pre><code class=\"language-bash\">127.0.0.1:6379&gt; bf.exists userid &#039;101310299&#039;\n(integer) 1<\/code><\/pre>\n<p>\u4e0a\u9762\u8fd9\u6761\u547d\u4ee4\u5c31\u662f\uff1a\u5224\u65ad\u6307\u5b9akey\u7684value\u662f\u5426\u5728bloomfilter\u91cc\u5b58\u5728\u3002\u5b58\u5728\uff1a\u8fd4\u56de1\uff0c\u4e0d\u5b58\u5728\uff1a\u8fd4\u56de0<\/p>\n<h2>\u7ed3\u5408SpringBoot\u4f7f\u7528<\/h2>\n<h3>BloomFilter\u5b9e\u73b0<\/h3>\n<p>\u501f\u7528google\u7684guava\u5305\u6765\u5b9e\u73b0\u6838\u5fc3\u7b97\u6cd5\uff0c\u6838\u5fc3\u4ee3\u7801\u5982\u4e0b\uff1a<\/p>\n<p><code>BloomFilterHelper.java<\/code><\/p>\n<pre><code class=\"language-java\">import com.google.common.base.Preconditions;\nimport com.google.common.hash.Funnel;\nimport com.google.common.hash.Hashing;\n\npublic class BloomFilterHelper&lt;T&gt; {\n    private int numHashFunctions;\n\n    private int bitSize;\n\n    private Funnel&lt;T&gt; funnel;\n\n    public BloomFilterHelper(Funnel&lt;T&gt; funnel, int expectedInsertions, double fpp) {\n        Preconditions.checkArgument(funnel != null, &quot;funnel\u4e0d\u80fd\u4e3a\u7a7a&quot;);\n        this.funnel = funnel;\n        bitSize = optimalNumOfBits(expectedInsertions, fpp);\n        numHashFunctions = optimalNumOfHashFunctions(expectedInsertions, bitSize);\n    }\n\n    int[] murmurHashOffset(T value) {\n        int[] offset = new int[numHashFunctions];\n\n        long hash64 = Hashing.murmur3_128().hashObject(value, funnel).asLong();\n        int hash1 = (int) hash64;\n        int hash2 = (int) (hash64 &gt;&gt;&gt; 32);\n        for (int i = 1; i &lt;= numHashFunctions; i++) {\n            int nextHash = hash1 + i * hash2;\n            if (nextHash &lt; 0) {\n                nextHash = ~nextHash;\n            }\n            offset[i - 1] = nextHash % bitSize;\n        }\n\n        return offset;\n    }\n\n    \/**\n     * \u8ba1\u7b97bit\u6570\u7ec4\u7684\u957f\u5ea6\n     *\/\n    private int optimalNumOfBits(long n, double p) {\n        if (p == 0) {\n            p = Double.MIN_VALUE;\n        }\n        return (int) (-n * Math.log(p) \/ (Math.log(2) * Math.log(2)));\n    }\n\n    \/**\n     * \u8ba1\u7b97hash\u65b9\u6cd5\u6267\u884c\u6b21\u6570\n     *\/\n    private int optimalNumOfHashFunctions(long n, long m) {\n        return Math.max(1, (int) Math.round((double) m \/ n * Math.log(2)));\n    }\n}<\/code><\/pre>\n<h3>\u9879\u76eeRedis\u914d\u7f6e<\/h3>\n<p>\uff081\uff09Redis\u8fde\u63a5\u914d\u7f6e<\/p>\n<pre><code>spring.redis.database=0\nspring.redis.host=192.168.56.101\nspring.redis.port=6379\nspring.redis.password=111111\nspring.redis.pool.max-active=10\nspring.redis.pool.max-wait=-1\nspring.redis.pool.max-idle=10\nspring.redis.pool.min-idle=0\nspring.redis.timeout=1000<\/code><\/pre>\n<p>\uff082\uff09<code>RedisConfig.java<\/code><\/p>\n<pre><code class=\"language-java\">import com.fasterxml.jackson.annotation.JsonAutoDetect;\nimport com.fasterxml.jackson.annotation.PropertyAccessor;\nimport com.fasterxml.jackson.databind.ObjectMapper;\nimport org.springframework.cache.CacheManager;\nimport org.springframework.cache.annotation.CachingConfigurerSupport;\nimport org.springframework.cache.annotation.EnableCaching;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.data.redis.cache.RedisCacheManager;\nimport org.springframework.data.redis.connection.RedisConnectionFactory;\nimport org.springframework.data.redis.core.*;\nimport org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;\nimport org.springframework.data.redis.serializer.StringRedisSerializer;\n\n@Configuration\n@EnableCaching\npublic class RedisConfig extends CachingConfigurerSupport {\n    \/**\n     * \u9009\u62e9redis\u4f5c\u4e3a\u9ed8\u8ba4\u7f13\u5b58\u5de5\u5177\n     * \n     * @param redisTemplate\n     * @return\n     *\/\n    @Bean\n    public CacheManager cacheManager(RedisTemplate redisTemplate) {\n        RedisCacheManager rcm = new RedisCacheManager(redisTemplate);\n        return rcm;\n    }\n\n    \/**\n     * retemplate\u76f8\u5173\u914d\u7f6e\n     * \n     * @param factory\n     * @return\n     *\/\n    @Bean\n    public RedisTemplate&lt;String, Object&gt; redisTemplate(RedisConnectionFactory factory) {\n\n        RedisTemplate&lt;String, Object&gt; template = new RedisTemplate&lt;&gt;();\n        \/\/ \u914d\u7f6e\u8fde\u63a5\u5de5\u5382\n        template.setConnectionFactory(factory);\n\n        \/\/ \u4f7f\u7528Jackson2JsonRedisSerializer\u6765\u5e8f\u5217\u5316\u548c\u53cd\u5e8f\u5217\u5316redis\u7684value\u503c\uff08\u9ed8\u8ba4\u4f7f\u7528JDK\u7684\u5e8f\u5217\u5316\u65b9\u5f0f\uff09\n        Jackson2JsonRedisSerializer jacksonSeial = new Jackson2JsonRedisSerializer(Object.class);\n\n        ObjectMapper om = new ObjectMapper();\n        \/\/ \u6307\u5b9a\u8981\u5e8f\u5217\u5316\u7684\u57df\uff0cfield,get\u548cset,\u4ee5\u53ca\u4fee\u9970\u7b26\u8303\u56f4\uff0cANY\u662f\u90fd\u6709\u5305\u62ecprivate\u548cpublic\n        om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);\n        \/\/ \u6307\u5b9a\u5e8f\u5217\u5316\u8f93\u5165\u7684\u7c7b\u578b\uff0c\u7c7b\u5fc5\u987b\u662f\u975efinal\u4fee\u9970\u7684\uff0cfinal\u4fee\u9970\u7684\u7c7b\uff0c\u6bd4\u5982String,Integer\u7b49\u4f1a\u8dd1\u51fa\u5f02\u5e38\n        om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);\n        jacksonSeial.setObjectMapper(om);\n\n        \/\/ \u503c\u91c7\u7528json\u5e8f\u5217\u5316\n        template.setValueSerializer(jacksonSeial);\n        \/\/ \u4f7f\u7528StringRedisSerializer\u6765\u5e8f\u5217\u5316\u548c\u53cd\u5e8f\u5217\u5316redis\u7684key\u503c\n        template.setKeySerializer(new StringRedisSerializer());\n\n        \/\/ \u8bbe\u7f6ehash key \u548cvalue\u5e8f\u5217\u5316\u6a21\u5f0f\n        template.setHashKeySerializer(new StringRedisSerializer());\n        template.setHashValueSerializer(jacksonSeial);\n        template.afterPropertiesSet();\n\n        return template;\n    }\n\n    \/**\n     * \u5bf9hash\u7c7b\u578b\u7684\u6570\u636e\u64cd\u4f5c\n     *\n     * @param redisTemplate\n     * @return\n     *\/\n    @Bean\n    public HashOperations&lt;String, String, Object&gt; hashOperations(RedisTemplate&lt;String, Object&gt; redisTemplate) {\n        return redisTemplate.opsForHash();\n    }\n\n    \/**\n     * \u5bf9redis\u5b57\u7b26\u4e32\u7c7b\u578b\u6570\u636e\u64cd\u4f5c\n     *\n     * @param redisTemplate\n     * @return\n     *\/\n    @Bean\n    public ValueOperations&lt;String, Object&gt; valueOperations(RedisTemplate&lt;String, Object&gt; redisTemplate) {\n        return redisTemplate.opsForValue();\n    }\n\n    \/**\n     * \u5bf9\u94fe\u8868\u7c7b\u578b\u7684\u6570\u636e\u64cd\u4f5c\n     *\n     * @param redisTemplate\n     * @return\n     *\/\n    @Bean\n    public ListOperations&lt;String, Object&gt; listOperations(RedisTemplate&lt;String, Object&gt; redisTemplate) {\n        return redisTemplate.opsForList();\n    }\n\n    \/**\n     * \u5bf9\u65e0\u5e8f\u96c6\u5408\u7c7b\u578b\u7684\u6570\u636e\u64cd\u4f5c\n     *\n     * @param redisTemplate\n     * @return\n     *\/\n    @Bean\n    public SetOperations&lt;String, Object&gt; setOperations(RedisTemplate&lt;String, Object&gt; redisTemplate) {\n        return redisTemplate.opsForSet();\n    }\n\n    \/**\n     * \u5bf9\u6709\u5e8f\u96c6\u5408\u7c7b\u578b\u7684\u6570\u636e\u64cd\u4f5c\n     *\n     * @param redisTemplate\n     * @return\n     *\/\n    @Bean\n    public ZSetOperations&lt;String, Object&gt; zSetOperations(RedisTemplate&lt;String, Object&gt; redisTemplate) {\n        return redisTemplate.opsForZSet();\n    }\n}<\/code><\/pre>\n<p>\uff083\uff09<code>RedisUtil.java<\/code><\/p>\n<pre><code class=\"language-java\">import org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.data.redis.core.RedisTemplate;\nimport org.springframework.stereotype.Component;\n\nimport java.util.Collection;\nimport java.util.Date;\nimport java.util.Set;\nimport java.util.concurrent.TimeUnit;\nimport java.util.stream.Collectors;\nimport java.util.stream.Stream;\nimport com.google.common.base.Preconditions;\nimport org.springframework.data.redis.core.RedisTemplate;\n\n@Component\npublic class RedisUtil {\n    @Autowired\n    private RedisTemplate&lt;String, String&gt; redisTemplate;\n\n    \/**\n     * \u9ed8\u8ba4\u8fc7\u671f\u65f6\u957f\uff0c\u5355\u4f4d\uff1a\u79d2\n     *\/\n    public static final long DEFAULT_EXPIRE = 60 * 60 * 24;\n\n    \/**\n     * \u4e0d\u8bbe\u7f6e\u8fc7\u671f\u65f6\u957f\n     *\/\n    public static final long NOT_EXPIRE = -1;\n\n    public boolean existsKey(String key) {\n        return redisTemplate.hasKey(key);\n    }\n\n    \/**\n     * \u91cd\u540d\u540dkey\uff0c\u5982\u679cnewKey\u5df2\u7ecf\u5b58\u5728\uff0c\u5219newKey\u7684\u539f\u503c\u88ab\u8986\u76d6\n     *\n     * @param oldKey\n     * @param newKey\n     *\/\n    public void renameKey(String oldKey, String newKey) {\n        redisTemplate.rename(oldKey, newKey);\n    }\n\n    \/**\n     * newKey\u4e0d\u5b58\u5728\u65f6\u624d\u91cd\u547d\u540d\n     *\n     * @param oldKey\n     * @param newKey\n     * @return \u4fee\u6539\u6210\u529f\u8fd4\u56detrue\n     *\/\n    public boolean renameKeyNotExist(String oldKey, String newKey) {\n        return redisTemplate.renameIfAbsent(oldKey, newKey);\n    }\n\n    \/**\n     * \u5220\u9664key\n     *\n     * @param key\n     *\/\n    public void deleteKey(String key) {\n        redisTemplate.delete(key);\n    }\n\n    \/**\n     * \u5220\u9664\u591a\u4e2akey\n     *\n     * @param keys\n     *\/\n    public void deleteKey(String... keys) {\n        Set&lt;String&gt; kSet = Stream.of(keys).map(k -&gt; k).collect(Collectors.toSet());\n        redisTemplate.delete(kSet);\n    }\n\n    \/**\n     * \u5220\u9664Key\u7684\u96c6\u5408\n     *\n     * @param keys\n     *\/\n    public void deleteKey(Collection&lt;String&gt; keys) {\n        Set&lt;String&gt; kSet = keys.stream().map(k -&gt; k).collect(Collectors.toSet());\n        redisTemplate.delete(kSet);\n    }\n\n    \/**\n     * \u8bbe\u7f6ekey\u7684\u751f\u547d\u5468\u671f\n     *\n     * @param key\n     * @param time\n     * @param timeUnit\n     *\/\n    public void expireKey(String key, long time, TimeUnit timeUnit) {\n        redisTemplate.expire(key, time, timeUnit);\n    }\n\n    \/**\n     * \u6307\u5b9akey\u5728\u6307\u5b9a\u7684\u65e5\u671f\u8fc7\u671f\n     *\n     * @param key\n     * @param date\n     *\/\n    public void expireKeyAt(String key, Date date) {\n        redisTemplate.expireAt(key, date);\n    }\n\n    \/**\n     * \u67e5\u8be2key\u7684\u751f\u547d\u5468\u671f\n     *\n     * @param key\n     * @param timeUnit\n     * @return\n     *\/\n    public long getKeyExpire(String key, TimeUnit timeUnit) {\n        return redisTemplate.getExpire(key, timeUnit);\n    }\n\n    \/**\n     * \u5c06key\u8bbe\u7f6e\u4e3a\u6c38\u4e45\u6709\u6548\n     *\n     * @param key\n     *\/\n    public void persistKey(String key) {\n        redisTemplate.persist(key);\n    }\n\n    \/**\n     * \u6839\u636e\u7ed9\u5b9a\u7684\u5e03\u9686\u8fc7\u6ee4\u5668\u6dfb\u52a0\u503c\n     *\/\n    public &lt;T&gt; void addByBloomFilter(BloomFilterHelper&lt;T&gt; bloomFilterHelper, String key, T value) {\n        Preconditions.checkArgument(bloomFilterHelper != null, &quot;bloomFilterHelper\u4e0d\u80fd\u4e3a\u7a7a&quot;);\n        int[] offset = bloomFilterHelper.murmurHashOffset(value);\n        for (int i : offset) {\n            redisTemplate.opsForValue().setBit(key, i, true);\n        }\n    }\n\n    \/**\n     * \u6839\u636e\u7ed9\u5b9a\u7684\u5e03\u9686\u8fc7\u6ee4\u5668\u5224\u65ad\u503c\u662f\u5426\u5b58\u5728\n     *\/\n    public &lt;T&gt; boolean includeByBloomFilter(BloomFilterHelper&lt;T&gt; bloomFilterHelper, String key, T value) {\n        Preconditions.checkArgument(bloomFilterHelper != null, &quot;bloomFilterHelper\u4e0d\u80fd\u4e3a\u7a7a&quot;);\n        int[] offset = bloomFilterHelper.murmurHashOffset(value);\n        for (int i : offset) {\n            if (!redisTemplate.opsForValue().getBit(key, i)) {\n                return false;\n            }\n        }\n        return true;\n    }\n}<\/code><\/pre>\n<p>\uff084\uff09<code>RedisKeyUtil.java<\/code><\/p>\n<pre><code class=\"language-java\">public class RedisKeyUtil {\n    \/**\n     * redis\u7684key \u5f62\u5f0f\u4e3a\uff1a \u8868\u540d:\u4e3b\u952e\u540d:\u4e3b\u952e\u503c:\u5217\u540d\n     *\n     * @param tableName     \u8868\u540d\n     * @param majorKey      \u4e3b\u952e\u540d\n     * @param majorKeyValue \u4e3b\u952e\u503c\n     * @param column        \u5217\u540d\n     * @return\n     *\/\n    public static String getKeyWithColumn(String tableName, String majorKey, String majorKeyValue, String column) {\n        StringBuffer buffer = new StringBuffer();\n        buffer.append(tableName).append(&quot;:&quot;);\n        buffer.append(majorKey).append(&quot;:&quot;);\n        buffer.append(majorKeyValue).append(&quot;:&quot;);\n        buffer.append(column);\n        return buffer.toString();\n    }\n\n    \/**\n     * redis\u7684key \u5f62\u5f0f\u4e3a\uff1a \u8868\u540d:\u4e3b\u952e\u540d:\u4e3b\u952e\u503c\n     *\n     * @param tableName     \u8868\u540d\n     * @param majorKey      \u4e3b\u952e\u540d\n     * @param majorKeyValue \u4e3b\u952e\u503c\n     * @return\n     *\/\n    public static String getKey(String tableName, String majorKey, String majorKeyValue) {\n        StringBuffer buffer = new StringBuffer();\n        buffer.append(tableName).append(&quot;:&quot;);\n        buffer.append(majorKey).append(&quot;:&quot;);\n        buffer.append(majorKeyValue).append(&quot;:&quot;);\n        return buffer.toString();\n    }\n}<\/code><\/pre>\n<h3>\u6d4b\u8bd5\u63a5\u53e3<\/h3>\n<p><code>UserVO.java<\/code><\/p>\n<pre><code class=\"language-java\">@Data\npublic class UserVO implements Serializable {\n    private String name;\n    private String address;\n    private Integer age;\n    private String email;\n}<\/code><\/pre>\n<p><code>UserController.java<\/code><\/p>\n<pre><code class=\"language-java\">import java.util.HashMap;\nimport java.util.Map;\nimport java.util.concurrent.TimeUnit;\n\nimport javax.annotation.Resource;\n\nimport org.sky.platform.util.BloomFilterHelper;\nimport org.sky.platform.util.RedisUtil;\nimport org.sky.vo.UserVO;\nimport org.springframework.data.redis.core.RedisTemplate;\nimport org.springframework.data.redis.core.ValueOperations;\nimport org.springframework.http.HttpHeaders;\nimport org.springframework.http.HttpStatus;\nimport org.springframework.http.MediaType;\nimport org.springframework.http.ResponseEntity;\nimport org.springframework.web.bind.annotation.PostMapping;\nimport org.springframework.web.bind.annotation.RequestBody;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RestController;\n\nimport com.alibaba.fastjson.JSON;\nimport com.alibaba.fastjson.JSONObject;\nimport com.google.common.base.Charsets;\nimport com.google.common.hash.Funnel;\n\n@RestController\n@RequestMapping(&quot;user&quot;)\npublic class UserController extends BaseController {\n\n    @Resource\n    private RedisTemplate redisTemplate;\n\n    @Resource\n    private RedisUtil redisUtil;\n\n    @PostMapping(value = &quot;\/addEmailToBloom&quot;, produces = &quot;application\/json&quot;)\n    public ResponseEntity&lt;String&gt; addUser(@RequestBody String params) {\n        ResponseEntity&lt;String&gt; response = null;\n        String returnResultStr;\n        HttpHeaders headers = new HttpHeaders();\n        headers.setContentType(MediaType.APPLICATION_JSON_UTF8);\n        Map&lt;String, Object&gt; result = new HashMap&lt;&gt;();\n        try {\n            JSONObject requestJsonObj = JSON.parseObject(params);\n            UserVO inputUser = getUserFromJson(requestJsonObj);\n            BloomFilterHelper&lt;String&gt; myBloomFilterHelper = new BloomFilterHelper&lt;&gt;((Funnel&lt;String&gt;) (from,\n                    into) -&gt; into.putString(from, Charsets.UTF_8).putString(from, Charsets.UTF_8), 1500000, 0.00001);\n            redisUtil.addByBloomFilter(myBloomFilterHelper, &quot;email_existed_bloom&quot;, inputUser.getEmail());\n            result.put(&quot;code&quot;, HttpStatus.OK.value());\n            result.put(&quot;message&quot;, &quot;add into bloomFilter successfully&quot;);\n            result.put(&quot;email&quot;, inputUser.getEmail());\n            returnResultStr = JSON.toJSONString(result);\n            logger.info(&quot;returnResultStr======&gt;&quot; + returnResultStr);\n            response = new ResponseEntity&lt;&gt;(returnResultStr, headers, HttpStatus.OK);\n        } catch (Exception e) {\n            logger.error(&quot;add a new product with error: &quot; + e.getMessage(), e);\n            result.put(&quot;message&quot;, &quot;add a new product with error: &quot; + e.getMessage());\n            returnResultStr = JSON.toJSONString(result);\n            response = new ResponseEntity&lt;&gt;(returnResultStr, headers, HttpStatus.INTERNAL_SERVER_ERROR);\n        }\n        return response;\n    }\n\n    @PostMapping(value = &quot;\/checkEmailInBloom&quot;, produces = &quot;application\/json&quot;)\n    public ResponseEntity&lt;String&gt; findEmailInBloom(@RequestBody String params) {\n        ResponseEntity&lt;String&gt; response = null;\n        String returnResultStr;\n        HttpHeaders headers = new HttpHeaders();\n        headers.setContentType(MediaType.APPLICATION_JSON_UTF8);\n        Map&lt;String, Object&gt; result = new HashMap&lt;&gt;();\n        try {\n            JSONObject requestJsonObj = JSON.parseObject(params);\n            UserVO inputUser = getUserFromJson(requestJsonObj);\n            BloomFilterHelper&lt;String&gt; myBloomFilterHelper = new BloomFilterHelper&lt;&gt;((Funnel&lt;String&gt;) (from,\n                    into) -&gt; into.putString(from, Charsets.UTF_8).putString(from, Charsets.UTF_8), 1500000, 0.00001);\n            boolean answer = redisUtil.includeByBloomFilter(myBloomFilterHelper, &quot;email_existed_bloom&quot;,\n                    inputUser.getEmail());\n            result.put(&quot;code&quot;, HttpStatus.OK.value());\n            result.put(&quot;email&quot;, inputUser.getEmail());\n            result.put(&quot;exist&quot;, answer);\n            returnResultStr = JSON.toJSONString(result);\n            logger.info(&quot;returnResultStr======&gt;&quot; + returnResultStr);\n            response = new ResponseEntity&lt;&gt;(returnResultStr, headers, HttpStatus.OK);\n        } catch (Exception e) {\n            logger.error(&quot;add a new product with error: &quot; + e.getMessage(), e);\n            result.put(&quot;message&quot;, &quot;add a new product with error: &quot; + e.getMessage());\n            returnResultStr = JSON.toJSONString(result);\n            response = new ResponseEntity&lt;&gt;(returnResultStr, headers, HttpStatus.INTERNAL_SERVER_ERROR);\n        }\n        return response;\n    }\n\n    private UserVO getUserFromJson(JSONObject requestObj) {\n        String userName = requestObj.getString(&quot;username&quot;);\n        String userAddress = requestObj.getString(&quot;address&quot;);\n        String userEmail = requestObj.getString(&quot;email&quot;);\n        int userAge = requestObj.getInteger(&quot;age&quot;);\n        UserVO u = new UserVO();\n        u.setName(userName);\n        u.setAge(userAge);\n        u.setEmail(userEmail);\n        u.setAddress(userAddress);\n        return u;\n    }\n}<\/code><\/pre>\n","protected":false},"excerpt":{"rendered":"<p>\u5e03\u9686\u8fc7\u6ee4\u5668 BloomFilter\u662f\u4e00\u79cd\u7a7a\u95f4\u6548\u7387\u7684\u6982\u7387\u578b\u6570\u636e\u7ed3\u6784\uff0c\u7531Burton Howard Bloom 1 [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[14,41],"tags":[544,543],"class_list":["post-2146","post","type-post","status-publish","format-standard","hentry","category-redis","category-spring-boot","tag-bloomfilter","tag-543"],"_links":{"self":[{"href":"https:\/\/www.appblog.cn\/index.php\/wp-json\/wp\/v2\/posts\/2146","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.appblog.cn\/index.php\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.appblog.cn\/index.php\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.appblog.cn\/index.php\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.appblog.cn\/index.php\/wp-json\/wp\/v2\/comments?post=2146"}],"version-history":[{"count":0,"href":"https:\/\/www.appblog.cn\/index.php\/wp-json\/wp\/v2\/posts\/2146\/revisions"}],"wp:attachment":[{"href":"https:\/\/www.appblog.cn\/index.php\/wp-json\/wp\/v2\/media?parent=2146"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.appblog.cn\/index.php\/wp-json\/wp\/v2\/categories?post=2146"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.appblog.cn\/index.php\/wp-json\/wp\/v2\/tags?post=2146"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}