From c73979681f9b2f53e9b48715565b3753e1e1438c Mon Sep 17 00:00:00 2001 From: whaifree Date: Thu, 10 Oct 2024 22:49:32 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E6=B7=BB=E5=8A=A0LeetCode210=E3=80=81L?= =?UTF-8?q?eetCode215=E3=80=81LeetCode238=E3=80=81LeetCode189=E8=A7=A3?= =?UTF-8?q?=E9=A2=98=E4=BB=A3=E7=A0=81=20feat:=20=E6=B7=BB=E5=8A=A0?= =?UTF-8?q?=E9=99=90=E6=B5=81=E5=8A=9F=E8=83=BD=20feat:=20=E6=B7=BB?= =?UTF-8?q?=E5=8A=A0Redis=E9=85=8D=E7=BD=AE=EF=BC=8C=E5=AE=9E=E7=8E=B0?= =?UTF-8?q?=E5=AF=B9=E6=8C=87=E5=AE=9A=E6=96=B9=E6=B3=95=E7=9A=84=E9=99=90?= =?UTF-8?q?=E6=B5=81=E6=8E=A7=E5=88=B6=E3=80=82=E5=90=8C=E6=97=B6=E6=B7=BB?= =?UTF-8?q?=E5=8A=A0=E9=99=90=E6=B5=81=E7=9B=B8=E5=85=B3=E7=9A=84=E9=85=8D?= =?UTF-8?q?=E7=BD=AE=E5=92=8C=E6=B5=8B=E8=AF=95=E6=8E=A5=E5=8F=A3=E3=80=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Default Changelist application.yaml CacheConstants.java fx.java LeetCode146.java LeetCode167.java LeetCode189.java LeetCode210.java LeetCode215.java LeetCode238.java LimitType.java P1.java RateLimitAspect.java RateLimiter.java RedisConfig.java TestController.java ThreadLocalExample.java --- .../java/cn/whaifree/interview/Szml/P1.java | 50 ++++++++ .../redo/redo_all_240924/LeetCode146.java | 58 ++++++++++ .../redo/redo_all_240924/LeetCode167.java | 39 +++++++ .../redo/redo_all_240924/LeetCode189.java | 37 ++++++ .../redo/redo_all_240924/LeetCode210.java | 71 ++++++++++++ .../redo/redo_all_240924/LeetCode215.java | 77 +++++++++++++ .../redo/redo_all_240924/LeetCode238.java | 57 +++++++++ .../main/java/cn/whaifree/tech/java/fx.java | 35 ++++++ .../cn/whaifree/test/ThreadLocalExample.java | 2 + .../springdemo/aspect/RateLimitAspect.java | 100 ++++++++++++++++ .../aspect/annotation/RateLimiter.java | 38 ++++++ .../springdemo/config/RedisConfig.java | 78 +++++++++++++ .../springdemo/constant/CacheConstants.java | 11 ++ .../springdemo/constant/LimitType.java | 13 +++ .../springdemo/controller/TestController.java | 109 ++++++++++++++++++ .../src/main/resources/application.yaml | 13 +++ 16 files changed, 788 insertions(+) create mode 100644 ForJdk17/src/main/java/cn/whaifree/interview/Szml/P1.java create mode 100644 ForJdk17/src/main/java/cn/whaifree/redo/redo_all_240924/LeetCode146.java create mode 100644 ForJdk17/src/main/java/cn/whaifree/redo/redo_all_240924/LeetCode167.java create mode 100644 ForJdk17/src/main/java/cn/whaifree/redo/redo_all_240924/LeetCode189.java create mode 100644 ForJdk17/src/main/java/cn/whaifree/redo/redo_all_240924/LeetCode210.java create mode 100644 ForJdk17/src/main/java/cn/whaifree/redo/redo_all_240924/LeetCode215.java create mode 100644 ForJdk17/src/main/java/cn/whaifree/redo/redo_all_240924/LeetCode238.java create mode 100644 ForJdk17/src/main/java/cn/whaifree/tech/java/fx.java create mode 100644 springDemo/src/main/java/cn/whaifree/springdemo/aspect/RateLimitAspect.java create mode 100644 springDemo/src/main/java/cn/whaifree/springdemo/aspect/annotation/RateLimiter.java create mode 100644 springDemo/src/main/java/cn/whaifree/springdemo/config/RedisConfig.java create mode 100644 springDemo/src/main/java/cn/whaifree/springdemo/constant/CacheConstants.java create mode 100644 springDemo/src/main/java/cn/whaifree/springdemo/constant/LimitType.java create mode 100644 springDemo/src/main/java/cn/whaifree/springdemo/controller/TestController.java create mode 100644 springDemo/src/main/resources/application.yaml diff --git a/ForJdk17/src/main/java/cn/whaifree/interview/Szml/P1.java b/ForJdk17/src/main/java/cn/whaifree/interview/Szml/P1.java new file mode 100644 index 0000000..e1ea2e1 --- /dev/null +++ b/ForJdk17/src/main/java/cn/whaifree/interview/Szml/P1.java @@ -0,0 +1,50 @@ +package cn.whaifree.interview.Szml; + +import java.util.Scanner; + +/** + * @version 1.0 + * @Author whai文海 + * @Date 2024/10/8 19:50 + * @注释 + */ +public class P1 { + + public static void main(String[] args) { + Scanner in = new Scanner(System.in); + // 注意 hasNext 和 hasNextLine 的区别 + while (true) { + String next = in.next(""); + System.out.print(next); + System.out.print(" -> "); + System.out.println(method(next)); + } + } + + public static String method(String s) { + char[] charArray = s.toCharArray(); + reverse(charArray, 0, charArray.length - 1); // 整体翻转 + for (int i = 0; i < s.length(); i++) { + // 遇到连续的三个lia,对这个lia再次翻转 + if (i < s.length() - 2 && charArray[i] == 'i' && charArray[i + 1] == 'l' && charArray[i + 2] == 'a') { + reverse(charArray, i, i + 2); + i += 2; // 跳过这三个字符 + }else { + charArray[i] = Character.toUpperCase(charArray[i]); // 变为大写 + } + } + return new String(charArray); + } + + public static void reverse(char[] chars,int left ,int right) { + while (left < right) { + char tmp = chars[left]; + chars[left] = chars[right]; + chars[right] = tmp; + left++; + right--; + } + } + + +} diff --git a/ForJdk17/src/main/java/cn/whaifree/redo/redo_all_240924/LeetCode146.java b/ForJdk17/src/main/java/cn/whaifree/redo/redo_all_240924/LeetCode146.java new file mode 100644 index 0000000..a7b9d4d --- /dev/null +++ b/ForJdk17/src/main/java/cn/whaifree/redo/redo_all_240924/LeetCode146.java @@ -0,0 +1,58 @@ +package cn.whaifree.redo.redo_all_240924; + +import java.util.HashMap; +import java.util.Map; + +/** + * @version 1.0 + * @Author whai文海 + * @Date 2024/10/8 11:22 + * @注释 + */ +public class LeetCode146 { + + class LRUCache { + + class V{ + int value; + V next; + + public V(int value, V next) { + this.value = value; + this.next = next; + } + } + + Map map; + int maxSize; + Integer headIndex; + + + public LRUCache(int capacity) { + map = new HashMap<>(); + maxSize = capacity; + } + + public int get(int key) { + int value = map.get(key).value; + map.remove(key); + + + return value; + } + + public void put(int key, int value) { + V beforeHea = map.get(headIndex); + V newV = new V(value, beforeHea); + headIndex = key; + map.put(key, newV); + } + } + +/** + * Your LRUCache object will be instantiated and called as such: + * LRUCache obj = new LRUCache(capacity); + * int param_1 = obj.get(key); + * obj.put(key,value); + */ +} diff --git a/ForJdk17/src/main/java/cn/whaifree/redo/redo_all_240924/LeetCode167.java b/ForJdk17/src/main/java/cn/whaifree/redo/redo_all_240924/LeetCode167.java new file mode 100644 index 0000000..c2cfbc5 --- /dev/null +++ b/ForJdk17/src/main/java/cn/whaifree/redo/redo_all_240924/LeetCode167.java @@ -0,0 +1,39 @@ +package cn.whaifree.redo.redo_all_240924; + +import org.junit.Test; + +import java.util.Arrays; + +/** + * @version 1.0 + * @Author whai文海 + * @Date 2024/10/8 11:35 + * @注释 + */ +public class LeetCode167 { + + @Test + public void test() { + Solution solution = new Solution(); + int[] ints = solution.twoSum(new int[]{2, 7, 11, 15}, 9); + System.out.println(Arrays.toString(ints)); + } + + class Solution { + public int[] twoSum(int[] numbers, int target) { + int left = 0; + int right = numbers.length - 1; + while (left < right) { + int v = numbers[left] + numbers[right]; + if (v == target) { + return new int[]{left + 1, right + 1}; + } else if (v < target) { + left++; + }else { + right--; + } + } + return new int[]{-1, -1}; + } + } +} diff --git a/ForJdk17/src/main/java/cn/whaifree/redo/redo_all_240924/LeetCode189.java b/ForJdk17/src/main/java/cn/whaifree/redo/redo_all_240924/LeetCode189.java new file mode 100644 index 0000000..a831d21 --- /dev/null +++ b/ForJdk17/src/main/java/cn/whaifree/redo/redo_all_240924/LeetCode189.java @@ -0,0 +1,37 @@ +package cn.whaifree.redo.redo_all_240924; + +import org.junit.Test; + +/** + * @version 1.0 + * @Author whai文海 + * @Date 2024/10/8 11:53 + * @注释 + */ +public class LeetCode189 { + + @Test + public void test() { + int[] nums = new int[]{-1}; + int k = 2; + new Solution().rotate(nums, k); + for (int i : nums) { + System.out.println(i); + } + } + + // 7 6 5 4 3 2 1 + // 5 6 7 1 2 3 4 + class Solution { + public void rotate(int[] nums, int k) { + if (k > nums.length) { + k = k % nums.length; + } + int start = nums.length - k; + int[] newN = new int[nums.length * 2]; + System.arraycopy(nums, 0, newN, 0, nums.length); + System.arraycopy(nums, 0, newN, nums.length, nums.length); + System.arraycopy(newN, start, nums, 0, nums.length); + } + } +} diff --git a/ForJdk17/src/main/java/cn/whaifree/redo/redo_all_240924/LeetCode210.java b/ForJdk17/src/main/java/cn/whaifree/redo/redo_all_240924/LeetCode210.java new file mode 100644 index 0000000..515b496 --- /dev/null +++ b/ForJdk17/src/main/java/cn/whaifree/redo/redo_all_240924/LeetCode210.java @@ -0,0 +1,71 @@ +package cn.whaifree.redo.redo_all_240924; + +import org.junit.Test; + +import java.util.*; + +/** + * @version 1.0 + * @Author whai文海 + * @Date 2024/10/9 12:25 + * @注释 + */ +public class LeetCode210 { + + @Test + public void test() { + int[][] prerequisites = {{1,0},{2,0},{3,1},{3,2}}; + int[] ints = new Solution().findOrder(4, prerequisites); + System.out.println(Arrays.toString(ints)); + } + + class Solution { + public int[] findOrder(int numCourses, int[][] prerequisites) { + // 统计所有入度 + Map> map = new HashMap<>(); + for (int[] prerequisite : prerequisites) { + if (!map.containsKey(prerequisite[0])) { + map.put(prerequisite[0], new ArrayList<>()); + } + map.get(prerequisite[0]).add(prerequisite[1]); + } + + Deque deque = new LinkedList<>(); + for (int i = 0; i < numCourses; i++) { + if (!map.containsKey(i)) { + deque.add(i); + } + } + + List list = new ArrayList<>(); + int exec = 0; + while (!deque.isEmpty()) { + Integer pop = deque.pop(); + list.add(pop); + exec++; + for (int[] prerequisite : prerequisites) { + int start = prerequisite[1]; // 出 + int end = prerequisite[0]; // 入 + if (start == pop) { + List item = map.get(end); + item.remove(Integer.valueOf(start)); + // 如果此时item还有元素,则不加入队列 + if (item.isEmpty()) { + deque.add(end); + } + } + } + } + + if (exec != numCourses) { + return new int[]{}; + } + + if (list.size() == numCourses) { + return list.stream().mapToInt(i -> i).toArray(); + } + + return new int[]{}; + } + } +} diff --git a/ForJdk17/src/main/java/cn/whaifree/redo/redo_all_240924/LeetCode215.java b/ForJdk17/src/main/java/cn/whaifree/redo/redo_all_240924/LeetCode215.java new file mode 100644 index 0000000..c104979 --- /dev/null +++ b/ForJdk17/src/main/java/cn/whaifree/redo/redo_all_240924/LeetCode215.java @@ -0,0 +1,77 @@ +package cn.whaifree.redo.redo_all_240924; + +import org.junit.Test; + +import java.util.Random; + +/** + * @version 1.0 + * @Author whai文海 + * @Date 2024/10/10 11:17 + * @注释 + */ +public class LeetCode215 { + + @Test + public void test() { + int[] nums = {3,2,1,5,6,4}; + int k = 2; + Solution solution = new Solution(); + int kthLargest = solution.findKthLargest(nums, k); + System.out.println(kthLargest); + } + + class Solution { + public int findKthLargest(int[] nums, int k) { + return find(nums, 0, nums.length - 1, nums.length - k); + } + + public int find(int[] nums, int start, int end, int k) { + if (start > end) { + return nums[end]; + } + + int q = new Random().nextInt(end - start + 1) + start; + + swap(nums, q, end); + + int base = nums[end]; + int left = start; + int right = end; + while (left < right) { + + // 从左往右遍历,当左指针指向的元素小于等于基数时,i++。左指针持续向右移动 + while (nums[left] >= base && left < right) { + left++; + } + // 从右往左遍历,当右指针指向的元素大于等于基数时,j--。右指针持续向左移动 + while (nums[right] <= base && left < right) { + right--; + } + if (left < right) { + // 当左右两个指针停下来时,交换两个元素 + swap(nums, left, right); + } + } + swap(nums, left, end); + + // 从大到小排序,如果左边k-1个,则left就是第k个,左边k-1个比他大 + if (left == k - 1) { + return nums[left]; + } + // 左边的数量太少了,往右边找 + if (left < k - 1) { + return find(nums, left + 1, end, k); + } + return find(nums, start, left - 1, k); + } + + public void swap(int[] nums, int i, int j) { + int temp = nums[i]; + nums[i] = nums[j]; + nums[j] = temp; + } + } + + +} diff --git a/ForJdk17/src/main/java/cn/whaifree/redo/redo_all_240924/LeetCode238.java b/ForJdk17/src/main/java/cn/whaifree/redo/redo_all_240924/LeetCode238.java new file mode 100644 index 0000000..6470000 --- /dev/null +++ b/ForJdk17/src/main/java/cn/whaifree/redo/redo_all_240924/LeetCode238.java @@ -0,0 +1,57 @@ +package cn.whaifree.redo.redo_all_240924; + +import org.junit.Test; + +/** + * @version 1.0 + * @Author whai文海 + * @Date 2024/10/10 11:36 + * @注释 + */ +public class LeetCode238 { + + @Test + public void test() + { + int[] nums = new int[]{-1,1,0,-3,3}; + Solution solution = new Solution(); + int[] res = solution.productExceptSelf(nums); + for (int i : res) { + System.out.println(i); + } + } + + class Solution { + public int[] productExceptSelf(int[] nums) { + + int countOfZero = 0; + int product = 1; + int zeroIndex = 0; + for (int i = 0; i < nums.length; i++) { + int num = nums[i]; + if (num == 0) { + countOfZero++; + zeroIndex = i; + }else { + product *= num; + } + } + + if (countOfZero >= 2) { + return new int[nums.length]; + } + + if (countOfZero == 1) { + int[] res = new int[nums.length]; + res[zeroIndex] = product; + return res; + } + + int[] res = new int[nums.length]; + for (int i = 0; i < nums.length; i++) { + res[i] = product / nums[i]; + } + return res; + } + } +} diff --git a/ForJdk17/src/main/java/cn/whaifree/tech/java/fx.java b/ForJdk17/src/main/java/cn/whaifree/tech/java/fx.java new file mode 100644 index 0000000..e8d9c65 --- /dev/null +++ b/ForJdk17/src/main/java/cn/whaifree/tech/java/fx.java @@ -0,0 +1,35 @@ +package cn.whaifree.tech.java; + +/** + * @version 1.0 + * @Author whai文海 + * @Date 2024/10/9 22:24 + * @注释 + */ +public class fx { + + class Base{ + + } + + class User { // ,是并列的关系 + + + } + + interface interfaceM{ + + } + + class Book { // 必须类在前,接口在后 + + } + + class BookBase extends Base implements interfaceM{ + // 满足,同时是Base的子类和interfaceM的实现类 + } + + public void method() { + new Book(); + } +} diff --git a/ForJdk17/src/main/java/cn/whaifree/test/ThreadLocalExample.java b/ForJdk17/src/main/java/cn/whaifree/test/ThreadLocalExample.java index 4767ddf..3e6e280 100644 --- a/ForJdk17/src/main/java/cn/whaifree/test/ThreadLocalExample.java +++ b/ForJdk17/src/main/java/cn/whaifree/test/ThreadLocalExample.java @@ -6,6 +6,8 @@ public class ThreadLocalExample { public static void main(String[] args) throws InterruptedException { for(int i=0 ; i<3; i++){ + threadLocal.remove(); + new Thread(new MyThread()).start(); } } diff --git a/springDemo/src/main/java/cn/whaifree/springdemo/aspect/RateLimitAspect.java b/springDemo/src/main/java/cn/whaifree/springdemo/aspect/RateLimitAspect.java new file mode 100644 index 0000000..a5f33e8 --- /dev/null +++ b/springDemo/src/main/java/cn/whaifree/springdemo/aspect/RateLimitAspect.java @@ -0,0 +1,100 @@ +package cn.whaifree.springdemo.aspect; + +import cn.whaifree.springdemo.aspect.annotation.RateLimiter; +import cn.whaifree.springdemo.constant.LimitType; +import org.aspectj.lang.JoinPoint; +import org.aspectj.lang.annotation.Aspect; +import org.aspectj.lang.annotation.Before; +import org.aspectj.lang.reflect.MethodSignature; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.redis.core.RedisTemplate; +import org.springframework.data.redis.core.script.RedisScript; +import org.springframework.stereotype.Component; +import org.springframework.web.context.request.RequestContextHolder; +import org.springframework.web.context.request.ServletRequestAttributes; + +import java.lang.reflect.Method; +import java.util.Collections; +import java.util.List; +import java.util.Objects; + +/** + * @version 1.0 + * @Author whai文海 + * @Date 2024/10/9 17:46 + * @注释 + */ +@Aspect +@Component +public class RateLimitAspect { + + private static final Logger log = LoggerFactory.getLogger(RateLimitAspect.class); + + private RedisTemplate redisTemplate; + + private RedisScript limitScript; + + @Autowired + public void setRedisTemplate1(RedisTemplate redisTemplate) { + this.redisTemplate = redisTemplate; + } + + @Autowired + public void setLimitScript(RedisScript limitScript) { + this.limitScript = limitScript; + } + + /** + * key 对应方法,value 已经被访问的次数 + * + * lua的逻辑: + * + * + * + * @param point + * @param rateLimiter + * @throws Throwable + */ + @Before("@annotation(rateLimiter)") + public void doBefore(JoinPoint point, RateLimiter rateLimiter) throws Throwable { + int time = rateLimiter.time(); // 多长时间 + int count = rateLimiter.count(); // 允许次数 + + + String combineKey = getCombineKey(rateLimiter, point); // 组合key, Class-Method + List keys = Collections.singletonList(combineKey); + try { + Long number = redisTemplate.execute(limitScript, keys, count, time); + if (Objects.isNull(number) || number.intValue() > count) { // 如果超过限额,报错 + throw new Exception("访问过于频繁,请稍候再试"); + } + log.info("限制请求'{}',当前请求'{}',缓存key'{}'", count, number.intValue(), combineKey); + } catch (Exception e) { + + throw e; + } + } + + public String getCombineKey(RateLimiter rateLimiter, JoinPoint point) { + StringBuffer stringBuffer = new StringBuffer(rateLimiter.key()); // 获取key +// if (rateLimiter.limitType() == LimitType.IP) { +// stringBuffer.append(IpUtils.getIpAddr(ServletUtils.getRequest())).append("-"); +// } + if (rateLimiter.limitType() == LimitType.USER) { + ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes(); + String username = attributes.getRequest().getParameter("userId"); + if (username != null) { + stringBuffer.append(username).append("-"); + }else { + throw new RuntimeException("用户id为空"); // 抛出异常,禁止继续执行,防止缓存穿透,缓存穿透指缓存中不存在该key,但是数据库中存在该ke + } + } + MethodSignature signature = (MethodSignature) point.getSignature(); + Method method = signature.getMethod(); + Class targetClass = method.getDeclaringClass(); + stringBuffer.append(targetClass.getName()).append("-").append(method.getName()); + return stringBuffer.toString(); + } +} diff --git a/springDemo/src/main/java/cn/whaifree/springdemo/aspect/annotation/RateLimiter.java b/springDemo/src/main/java/cn/whaifree/springdemo/aspect/annotation/RateLimiter.java new file mode 100644 index 0000000..d1bd2f7 --- /dev/null +++ b/springDemo/src/main/java/cn/whaifree/springdemo/aspect/annotation/RateLimiter.java @@ -0,0 +1,38 @@ +package cn.whaifree.springdemo.aspect.annotation; + +import cn.whaifree.springdemo.constant.CacheConstants; +import cn.whaifree.springdemo.constant.LimitType; + +import java.lang.annotation.*; + +/** + * @version 1.0 + * @Author whai文海 + * @Date 2024/10/9 17:43 + * @注释 + */ +@Target(ElementType.METHOD) +@Retention(RetentionPolicy.RUNTIME) +@Documented +public @interface RateLimiter { + + /** + * 限流key + */ + public String key() default CacheConstants.RATE_LIMIT_KEY; + + /** + * 限流时间,单位秒 + */ + public int time() default 60; + + /** + * 限流次数 + */ + public int count() default 100; + + /** + * 限流类型 + */ + public LimitType limitType() default LimitType.DEFAULT; +} diff --git a/springDemo/src/main/java/cn/whaifree/springdemo/config/RedisConfig.java b/springDemo/src/main/java/cn/whaifree/springdemo/config/RedisConfig.java new file mode 100644 index 0000000..2351966 --- /dev/null +++ b/springDemo/src/main/java/cn/whaifree/springdemo/config/RedisConfig.java @@ -0,0 +1,78 @@ +package cn.whaifree.springdemo.config; + +import com.alibaba.fastjson.support.spring.FastJsonRedisSerializer; +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.core.script.DefaultRedisScript; +import org.springframework.data.redis.serializer.StringRedisSerializer; + +/** + * @version 1.0 + * @Author whai文海 + * @Date 2024/10/9 17:47 + * @注释 + */ +@Configuration +public class RedisConfig { + + @Bean + @SuppressWarnings(value = {"unchecked", "rawtypes"}) + public RedisTemplate redisTemplate(RedisConnectionFactory connectionFactory) { + RedisTemplate template = new RedisTemplate<>(); + template.setConnectionFactory(connectionFactory); + + FastJsonRedisSerializer jsonRedisSerializer = new FastJsonRedisSerializer(Object.class); + + // 使用StringRedisSerializer来序列化和反序列化redis的key值 + template.setKeySerializer(new StringRedisSerializer()); + template.setValueSerializer(jsonRedisSerializer); + + // Hash的key也采用StringRedisSerializer的序列化方式 + template.setHashKeySerializer(new StringRedisSerializer()); + template.setHashValueSerializer(jsonRedisSerializer); + + template.afterPropertiesSet(); + return template; + } + + + /** + * RedisScript是Redis官方Java客户端Jedis中的一个类,用于执行Redis脚本(Lua脚本)。 + * + * 在Redis中,Lua脚本是一种强大的工具,可以用于执行复杂的操作,如事务、发布/订阅、锁等。通过使用Lua脚本,你可以将多个操作封装在一个脚本中,然后一次性发送给Redis服务器执行,这样可以减少网络延迟和服务器负载。 + * + * RedisScript类提供了一些方法,可以用于设置脚本的返回类型、执行脚本、获取脚本的返回值等。 + * + * @return + */ + @Bean + public DefaultRedisScript limitScript() { + DefaultRedisScript redisScript = new DefaultRedisScript<>(); + redisScript.setScriptText(limitScriptText()); + redisScript.setResultType(Long.class); + return redisScript; + } + + /** + * 限流脚本 + */ + private String limitScriptText() { + + // 传入的参数有 time多长时间 count多少次 + + return "local key = KEYS[1]\n" + //test:cn.whaifree.springdemo.controller.TestController-test + "local count = tonumber(ARGV[1])\n" + // 传入的次数, 比如1次 + "local time = tonumber(ARGV[2])\n" + // 传入的时间,比如2s + "local current = redis.call('get', key);\n" + // 获取当前的次数 + "if current and tonumber(current) > count then\n" + + " return tonumber(current);\n" + // 当前的次数超过count 表示当前的次数超过限额,直接返回,表示拒绝 + "end\n" + + "current = redis.call('incr', key)\n" + // 如果没有超过限额,对value,即current增加 + "if tonumber(current) == 1 then\n" + // 如果是第一次,增加过期时间 + " redis.call('expire', key, time)\n" + + "end\n" + + "return tonumber(current);"; + } +} diff --git a/springDemo/src/main/java/cn/whaifree/springdemo/constant/CacheConstants.java b/springDemo/src/main/java/cn/whaifree/springdemo/constant/CacheConstants.java new file mode 100644 index 0000000..990b18f --- /dev/null +++ b/springDemo/src/main/java/cn/whaifree/springdemo/constant/CacheConstants.java @@ -0,0 +1,11 @@ +package cn.whaifree.springdemo.constant; + +/** + * @version 1.0 + * @Author whai文海 + * @Date 2024/10/9 17:44 + * @注释 + */ +public class CacheConstants { + public static final String RATE_LIMIT_KEY = "rate_limit:"; +} diff --git a/springDemo/src/main/java/cn/whaifree/springdemo/constant/LimitType.java b/springDemo/src/main/java/cn/whaifree/springdemo/constant/LimitType.java new file mode 100644 index 0000000..de3c45e --- /dev/null +++ b/springDemo/src/main/java/cn/whaifree/springdemo/constant/LimitType.java @@ -0,0 +1,13 @@ +package cn.whaifree.springdemo.constant; + +/** + * @version 1.0 + * @Author whai文海 + * @Date 2024/10/9 17:45 + * @注释 + */ +public enum LimitType { + IP, + USER, + DEFAULT; +} diff --git a/springDemo/src/main/java/cn/whaifree/springdemo/controller/TestController.java b/springDemo/src/main/java/cn/whaifree/springdemo/controller/TestController.java new file mode 100644 index 0000000..e830029 --- /dev/null +++ b/springDemo/src/main/java/cn/whaifree/springdemo/controller/TestController.java @@ -0,0 +1,109 @@ +package cn.whaifree.springdemo.controller; + +import cn.whaifree.springdemo.aspect.annotation.RateLimiter; +import cn.whaifree.springdemo.constant.LimitType; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import java.util.List; +import java.util.concurrent.*; + +/** + * @version 1.0 + * @Author whai文海 + * @Date 2024/10/9 17:40 + * @注释 + */ +@RestController +public class TestController { + @RequestMapping("/test") + @RateLimiter(key = "test:", time = 10, count = 1) // 10s只能1次 + public String test() { + return "test"; + } + + /** + * + * BG:重复导入多个容器,有一定几率触发15s响应超时 + * 1. 限频率,如果重复导入直接返回已经在导入中 + * 2. 控制导入的数量,对导入的数量分片+线程池处理 + * + * @return + */ + @RequestMapping("addContainer") + @RateLimiter(key = "addContainer:", limitType = LimitType.USER, time = 5, count = 1) // 10s只能1次 + public String addContainerInstanceToCluster(@RequestBody List instances, int userId) { + // 导入容器节点到集群中 + return addToCluster(instances, "clusterId", userId); + } + + + + /** + * + * @param instances 导入实例的ID + * @param userID + * @return + */ + public String addToCluster(List instances,String clusterId, int userID) { + return new MockK8sAPI().loadTo(instances, clusterId).toString(); + } + + ThreadPoolExecutor executor = (ThreadPoolExecutor) Executors.newFixedThreadPool(10); + + + class CallableRun implements Callable { + + String id; + + public CallableRun(String id, CountDownLatch countDownLatch) { + this.id = id; + } + + @Override + public Object call() throws Exception { + + return null; + } + } + + class MockK8sAPI{ + + /** + * + * @param instances + * @param clusterId + * @return + */ + public List loadTo(List instances, String clusterId) { + + CountDownLatch countDownLatch = new CountDownLatch(instances.size()); + + for (String instance : instances) { + executor.submit(new Callable() { + @Override + public String call() throws Exception { + try { + System.out.println(instance); + }finally { + countDownLatch.countDown(); + } + return instance; + } + }); + } + try { + countDownLatch.await(10, TimeUnit.SECONDS); + } catch (InterruptedException e) { + System.out.println("超时"); + throw new RuntimeException(e); + } + + return instances; + } + } + + + +} diff --git a/springDemo/src/main/resources/application.yaml b/springDemo/src/main/resources/application.yaml new file mode 100644 index 0000000..89721b3 --- /dev/null +++ b/springDemo/src/main/resources/application.yaml @@ -0,0 +1,13 @@ +spring: + datasource: + url: jdbc:mysql://localhost:3306/springTest?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone=Asia/Shanghai + username: root + password: 123456 + driver-class-name: com.mysql.cj.jdbc.Driver + + data: + redis: + host: localhost + port: 6379 + # 选择db1 + database: 3