diff --git a/Tc.md b/Tc.md index 24914c7..e69de29 100644 --- a/Tc.md +++ b/Tc.md @@ -1,175 +0,0 @@ -```java - public List findInstanceList(List instanceList, TsfBaseEntity entity) { - if (CollectionUtils.isEmpty(instanceList)) { - return new ArrayList<>(); - } - List instanceRunList = new ArrayList<>(); - //虚机节点 - long start, end; - start = System.currentTimeMillis(); - List instanceCVMList = instanceList.stream().filter(item -> - ClusterConstant.CLUSTER_TYPE.CVM.equals(item.getClusterType())).collect(Collectors.toList()); - List instanceIdList = instanceCVMList.stream().map(Instance::getInstanceId).collect(Collectors.toList()); - List instanceAgentList = instanceAgentJpaRepository.findByInstanceIdIn(instanceIdList); - Map instanceAgentMap = TsfMapUtil.list2HashMap(instanceAgentList, "getInstanceId"); - if (!CollectionUtils.isEmpty(instanceAgentMap)) { - for (Instance ins : instanceCVMList) { - InstanceAgent instanceAgent = instanceAgentMap.get(ins.getInstanceId()); - if (instanceAgent != null) { - Long currentTimestamp = System.currentTimeMillis(); - Long heartbeatTimestamp = instanceAgent.getUpdateTime() != null ? instanceAgent.getUpdateTime().getTime() : 0L; - if (heartbeatTimestamp + 18 * 60 * 1000 > currentTimestamp) { - instanceRunList.add(ins); - } - } - } - } - end = System.currentTimeMillis(); - logger.info("call cvm clusterInstances cost {}ms", (end - start)); - - //容器节点 - start = System.currentTimeMillis(); - List instanceCCSList = instanceList.stream().filter(item -> - ClusterConstant.CLUSTER_TYPE.CCS.equals(item.getClusterType())).collect(Collectors.toList()); - Map> instanceCcsMap = instanceCCSList.stream().collect(Collectors.groupingBy(Instance::getClusterId)); - instanceCcsMap.forEach((clusterId,instanceCcsList) -> { - Cluster cluster = new Cluster(); - cluster.transferBase(entity); - cluster.setClusterId(clusterId); - List ccsRunInstanceList = commonContainerInstanceService.findRunInstanceList(cluster, instanceCcsList); - instanceRunList.addAll(ccsRunInstanceList); - }); - end = System.currentTimeMillis(); - logger.info("call ccs clusterInstances cost {}ms", (end - start)); - return instanceRunList; -} - -``` - - - -117789438 - - -CountDownLatch 的使用 - -```java -import java.util.concurrent.CountDownLatch; - -public class CountDownLatchExample { - public static void main(String[] args) throws InterruptedException { - final int numberOfTasks = 3; // 假设有3个任务需要完成 - CountDownLatch latch = new CountDownLatch(numberOfTasks); - - for (int i = 0; i < numberOfTasks; i++) { - new Thread(() -> { - System.out.println("任务 " + Thread.currentThread().getName() + " 开始执行..."); - // 模拟任务执行需要一些时间 - try { - Thread.sleep((long) (Math.random() * 1000)); - } catch (InterruptedException e) { - e.printStackTrace(); - } - System.out.println("任务 " + Thread.currentThread().getName() + " 执行完成"); - latch.countDown(); // 任务完成,计数减1 - }).start(); - } - - System.out.println("主线程等待所有任务完成..."); - latch.await(); // 主线程等待所有任务完成 - System.out.println("所有任务已完成,主线程继续执行..."); - } -} - -``` - - -**使用AtomicInteger、CountDownLatch、线程池的并发方式优化查询集群数量接口和实例数量资源接口的性能,解决超时问题。在查询实例或集群数量过多时,查询DB,还需要查询容器平台、会导致查询时延达到15s秒** -使用redis封装,避免短时间缓存击穿 - -线程工厂 -```java -package com.tencent.tsf.resource.common.util; - -import java.util.concurrent.ThreadFactory; -import java.util.concurrent.atomic.AtomicInteger; - -public class CustomThreadFactory implements ThreadFactory { - private final ThreadGroup group; - private final AtomicInteger threadNumber = new AtomicInteger(1); - private final String namePrefix; - - public CustomThreadFactory(String prefix) { - SecurityManager s = System.getSecurityManager(); - group = (s != null) ? s.getThreadGroup() : Thread.currentThread().getThreadGroup(); - namePrefix = prefix + "-thread-"; - } - - @Override - public Thread newThread(Runnable r) { - Thread t = new Thread(group, r, namePrefix + threadNumber.getAndIncrement(), 0); - if (t.isDaemon()) - t.setDaemon(false); - if (t.getPriority() != Thread.NORM_PRIORITY) - t.setPriority(Thread.NORM_PRIORITY); - return t; - } - -} -``` - -线程池 - -```java -private ExecutorService overviewResourceUsageExecutor = - new ThreadPoolExecutor(Runtime.getRuntime().availableProcessors() * 2, Runtime.getRuntime().availableProcessors() * 2,0, TimeUnit.SECONDS, - new ArrayBlockingQueue<>(2048), - new CustomThreadFactory("overviewResourceUsageThreadPool"), - new ThreadPoolExecutor.AbortPolicy()); - -``` -固定线程数量的线程池,我是8核的cpu,此时能用8个cpu处理16个线程,当线程满了时就进入阻塞队列ArrayBlockingQueue -1. 核心线程数(corePoolSize):Runtime.getRuntime().availableProcessors() * 2。这意味着线程池会创建一个核心线程数,等于当前系统可用处理器的两倍。这样的设置通常是为了确保线程池能够充分利用系统资源,同时避免线程过多导致上下文切换开销过大。 -2. 最大线程数(maximumPoolSize):同样设置为Runtime.getRuntime().availableProcessors() * 2。这意味着线程池允许存在的最大线程数也是系统可用处理器的两倍。由于核心线程数和最大线程数相等,这个线程池实际上是一个固定大小的线程池,它不会根据任务的多少动态地增加或减少线程数量。 -3. 线程空闲时间(keepAliveTime):设置为0秒。这意味着线程一旦空闲下来就会被立即回收,不会等待其他任务的到来。这个设置适用于任务持续不断地到来的场景,可以节省资源。 -4. 时间单位(unit):设置为TimeUnit.SECONDS,表示线程空闲时间的单位是秒。 -5. 工作队列(workQueue):使用ArrayBlockingQueue作为工作队列,容量为2048。这意味着当线程池中的线程都在忙碌时,新来的任务会进入这个队列等待执行。队列的容量较大,可以容纳较多的待执行任务,有助于减少线程创建和销毁的频率。 -6. 线程工厂(threadFactory):使用CustomThreadFactory自定义线程工厂来创建线程。这样可以自定义线程的名称、优先级等属性,便于管理和调试。 -7. 拒绝策略(RejectedExecutionHandler):使用ThreadPoolExecutor.AbortPolicy()作为拒绝策略。当线程池和工作队列都满时,新提交的任务会导致抛出RejectedExecutionException异常。这种策略比较直接,可以让调用者感知到任务无法执行的情况并进行相应处理。 - - -接口超时,超过15s了 -【此接口内部有一个循环查询逻辑,循环里面需要查询DB,还需要查询容器平台,故性能会随着用户集群的增多而下降】 - -两个接口超时的原因基本是一样的: - -1、DescribeGroupResourceUsage, 首先会查询出所有的集群,包括虚拟机集群和容器集群,之后再将所有的集群进行遍历: - -如果是容器集群,先根据集群ID在DB里面查询出关联的所有group列表,然后就会调用apiserver获取所有的pod信息,然后累计计算部署组中的pod个数和健康状况。 - -如果是虚拟机集群,先根据集群ID在DB里面查询出关联的所有group列表,然后再去调用masterapi的相关接口获取group的实例信息,最后再统计。 - -最终再将所有集群计算出来的数据进行累加,这里的耗时体现在,每个集群的查询耗时是串行累加的,不是并行,所以集群数量越多,累加时间肯定越大。 - - - -2、DescribeInstanceResourceUsage, 首先会查询出所有的集群,包括虚拟机集群和容器集群,之后再将所有的集群进行遍历: - -如果是容器集群,先根据集群ID在DB里面查询出关联的所有group列表,然后就会调用apiserver获取所有的pod信息,然后累计计算部署组中的pod个数。 - -如果是虚拟机集群,根据clusterID去调用masterapi的相关接口获取集群下面的instance列表,然后再统计各个维度的instance个数。 - -然后再将所有集群计算出来的数据进行累加,这里的耗时体现在,每个集群的查询耗时是串行累加的,不是并行,所以集群数量越多,累加时间肯定越大。 - -最后还需要统计所有容器集群的健康状态,此时需要调用TKE的接口,去批量查询容器集群健康状态。 - - - -- 优化策略:将串行查询优化为并行查询 - -- 问题: 在查询实例或集群数量过多时,查询DB,还需要查询容器平台、会导致查询时延达到15s秒 - -- 原因:查询出所有的集群,包括虚拟机集群和容器集群,之后再将所有的集群进行遍历;如果是容器集群,先根据集群ID在DB里面查询出关联的所有group列表,然后就会调用apiserver获取所有的pod信息,然后累计计算部署组中的pod个数和健康状况; -如果是虚拟机集群,先根据集群ID在DB里面查询出关联的所有group列表,然后再去调用masterapi的相关接口获取group的实例信息,最后再统计。 -最终再将所有集群计算出来的数据进行累加,这里的耗时体现在,每个集群的查询耗时是串行累加的,不是并行,所以集群数量越多,累加时间肯定越大。 -- 解决:引入线程池来并发处理查询请求,使用AtomicInteger来安全地统计实例数量,并利用CountDownLatch确保所有并发任务完成后才返回结果,从而提高了接口的并发处理能力并解决了超时问题。 diff --git a/src/main/java/cn/whaifree/redo/redo_all_240924/LCR001.java b/src/main/java/cn/whaifree/redo/redo_all_240924/LCR001.java new file mode 100644 index 0000000..6f4d90e --- /dev/null +++ b/src/main/java/cn/whaifree/redo/redo_all_240924/LCR001.java @@ -0,0 +1,61 @@ +package cn.whaifree.redo.redo_all_240924; + +import org.junit.Test; + +/** + * @version 1.0 + * @Author whai文海 + * @Date 2024/9/27 11:25 + * @注释 + */ +public class LCR001 { + + @Test + public void test() { + System.out.println(new Solution().divide(-2147483648, 1)); + } + + + class Solution { + /** + * 17/3 + * 17 << 3<<2 = 5 5 << 0 余数2 + * 2^2+2^0 // 向上取整 + * + * @param a + * @param b + * @return + */ + public int divide(int a, int b) { + // 特殊情况2, b=-1 + if (b == -1) { + return a == Integer.MIN_VALUE ? Integer.MAX_VALUE : -a; + } + long A = a; + long B = b; + boolean reverse = false; + if ((A <= 0 && B <= 0) || (A >= 0 && B >= 0)) { + reverse = true; + } + A = Math.abs(A); + B = Math.abs(B); + + long base = A; + long res = 0; + while (base >= B) { + long tmpPoint = 1; + long item = B; + while ((item << 2) < base) { + item <<= 1; + tmpPoint <<= 1; + } + base -= item; + res += tmpPoint; + } + + + return (int) (reverse ? res : -res); + } + } + +} diff --git a/src/main/java/cn/whaifree/redo/redo_all_240924/LeetCode1049.java b/src/main/java/cn/whaifree/redo/redo_all_240924/LeetCode1049.java new file mode 100644 index 0000000..fbf92be --- /dev/null +++ b/src/main/java/cn/whaifree/redo/redo_all_240924/LeetCode1049.java @@ -0,0 +1,75 @@ +package cn.whaifree.redo.redo_all_240924; + +import org.junit.Test; + +import java.util.Arrays; + +/** + * @version 1.0 + * @Author whai文海 + * @Date 2024/9/27 11:01 + * @注释 + */ +public class LeetCode1049 { + + @Test + public void test() { + int[] stones = {2,7,4,1,8,1}; + System.out.println(new Solution1().lastStoneWeightII(stones)); + } + + class Solution { + /** + * 0 - 1 背包 + *

+ * 背包容量sumStones/2 + *

+ * dp[i][j] 表示 从0-i任意选择石头,放入容量为j的背包的最大价值 + * 剩余的石头就是sumStones/2- dp[i][j] + * + * @param stones + * @return + */ + public int lastStoneWeightII(int[] stones) { + int sumStones = Arrays.stream(stones).sum(); + int pkgSize = sumStones / 2; + int[][] dp = new int[stones.length][pkgSize + 1]; + for (int i = stones[0]; i <= pkgSize; i++) { + dp[0][i] = stones[0]; + } + for (int i = 1; i < stones.length; i++) { + for (int j = 0; j <= pkgSize; j++) { + // 放得下 + if (stones[i] <= j) { + dp[i][j] = Math.max(dp[i - 1][j - stones[i]] + stones[i], dp[i - 1][j]); + } else { + dp[i][j] = dp[i - 1][j]; + } + } + } + return sumStones - 2 * dp[stones.length - 1][pkgSize]; + + } + } + + class Solution1 { + + public int lastStoneWeightII(int[] stones) { + int sum = 0; + for (int i : stones) { + sum += i; + } + int pkgSize = sum / 2; + + int[] dp = new int[pkgSize + 1]; + for (int i = 0; i < stones.length; i++) { + for (int j = pkgSize; j >= stones[i]; j--) { + dp[j] = Math.max(dp[j], dp[j - stones[i]] + stones[i]); + } + } + return sum - 2 * dp[pkgSize]; + } + + + } +} diff --git a/src/main/java/cn/whaifree/redo/redo_all_240924/LeetCode11.java b/src/main/java/cn/whaifree/redo/redo_all_240924/LeetCode11.java new file mode 100644 index 0000000..3ff948d --- /dev/null +++ b/src/main/java/cn/whaifree/redo/redo_all_240924/LeetCode11.java @@ -0,0 +1,35 @@ +package cn.whaifree.redo.redo_all_240924; + +import org.junit.Test; + +/** + * @version 1.0 + * @Author whai文海 + * @Date 2024/9/27 14:46 + * @注释 + */ +public class LeetCode11 { + + @Test + public void test() { + int[] height = {1, 8, 6, 2, 5, 4, 8, 3, 7}; + System.out.println(new Solution().maxArea(height)); + } + + class Solution { + public int maxArea(int[] height) { + int left = 0; + int right = height.length - 1; + int max = Integer.MIN_VALUE; + while (left < right) { + max = Math.max(max, (right - left) * Math.min(height[left], height[right])); + if (height[left] < height[right]) { + left++; + } else { + right--; + } + } + return max; + } + } +} diff --git a/src/main/java/cn/whaifree/redo/redo_all_240924/LeetCode277.java b/src/main/java/cn/whaifree/redo/redo_all_240924/LeetCode277.java new file mode 100644 index 0000000..22833ef --- /dev/null +++ b/src/main/java/cn/whaifree/redo/redo_all_240924/LeetCode277.java @@ -0,0 +1,54 @@ +package cn.whaifree.redo.redo_all_240924; + +import org.junit.Test; + +/** + * @version 1.0 + * @Author whai文海 + * @Date 2024/9/27 16:04 + * @注释 + */ +public class LeetCode277 { + + @Test + public void test() { + int[] nums = {1, 2, 3}; + int target = 4; + System.out.println(new Solution().combinationSum4(nums, target)); + } + + class Solution { + /** + * 物品 nums + * 容量 target + *

+ * 0 1 2 3 4 + * 1 1 1 1 1 1 + * 2 1 1 2 2 2 + * 3 1 1 2 3 3 + * + * @param nums + * @param target + * @return + */ + public int combinationSum4(int[] nums, int target) { + + int[] dp = new int[target + 1]; + dp[0] = 1; + // 先遍历背包,再遍历物品 排列(有排序) + // 先遍历物品,再遍历背包,组合(无排序) + for (int j = 0; j < target + 1; j++) { + for (int i = 0; i < nums.length; i++) { + if (nums[i] <= j) { + dp[j] = dp[j - nums[i]] + dp[j]; + // dp[j] 原来的可能性 + // dp[j-nums[i]] 使用容量j-nums的可能性 + } + } + } + return dp[target]; + } + + + } +} diff --git a/src/main/java/cn/whaifree/redo/redo_all_240924/LeetCode279.java b/src/main/java/cn/whaifree/redo/redo_all_240924/LeetCode279.java new file mode 100644 index 0000000..e9e1f65 --- /dev/null +++ b/src/main/java/cn/whaifree/redo/redo_all_240924/LeetCode279.java @@ -0,0 +1,50 @@ +package cn.whaifree.redo.redo_all_240924; + +import org.junit.Test; + +/** + * @version 1.0 + * @Author whai文海 + * @Date 2024/9/27 15:04 + * @注释 + */ +public class LeetCode279 { + + @Test + public void test() { + Solution solution = new Solution(); + int i = solution.numSquares(13); + System.out.println(i); + } + + class Solution { + /** + * 背包容量n + * + * 物品 1 4 9 16 + * + * 任意背包 + * @param n + * @return + */ + public int numSquares(int n) { + + int sqrt = (int) Math.sqrt(n); + + int[] dp = new int[n + 1]; + for (int i = 1; i < dp.length; i++) { + dp[i] = i; + } + + for (int i = 2; i <= sqrt; i++) { + for (int j = 0; j <= n; j++) { + int size = i * i; + if (size <= j) { + dp[j] = Math.min(dp[j], dp[j - size] + 1); + } + } + } + return dp[n]; + } + } +} diff --git a/src/main/java/cn/whaifree/redo/redo_all_240924/LeetCode322.java b/src/main/java/cn/whaifree/redo/redo_all_240924/LeetCode322.java new file mode 100644 index 0000000..a5a7526 --- /dev/null +++ b/src/main/java/cn/whaifree/redo/redo_all_240924/LeetCode322.java @@ -0,0 +1,59 @@ +package cn.whaifree.redo.redo_all_240924; + +import org.junit.Test; + +import java.util.Arrays; + +/** + * @version 1.0 + * @Author whai文海 + * @Date 2024/9/27 15:35 + * @注释 + */ + +public class LeetCode322 { + + @Test + public void test() { + int[] coins = new int[]{2,5,10,1}; + int amount = 27; + System.out.println(new Solution().coinChange(coins, amount)); + } + + class Solution { + /** + * 从0-i中任意选择,凑满amount的最少硬币数量 + * 0 1 2 3 4 5 6 7 8 9 10 11 + * 2 1 - 1 - 2 - 3 - 4 - 4 - + * 1 1 1 1 + * 5 + * + * @param coins + * @param amount + * @return + */ + public int coinChange(int[] coins, int amount) { + + int[] dp = new int[amount + 1]; + Arrays.fill(dp, Integer.MAX_VALUE); + + for (int i = coins[0]; i <= amount; i += 1) { + if (i % coins[0] == 0) { + dp[i] = i / coins[0]; + } + } + dp[0] = 0; + for (int i = 1; i < coins.length; i++) { + for (int j = coins[i]; j <= amount; j++) { + int X = dp[j - coins[i]]; + if (X != Integer.MAX_VALUE) { + dp[j] = Math.min(dp[j], X + 1); + } + } + } + return dp[dp.length - 1] == Integer.MAX_VALUE ? -1 : dp[dp.length - 1]; + } + } + + +} diff --git a/src/main/java/cn/whaifree/redo/redo_all_240924/LeetCode713.java b/src/main/java/cn/whaifree/redo/redo_all_240924/LeetCode713.java new file mode 100644 index 0000000..50c8c0d --- /dev/null +++ b/src/main/java/cn/whaifree/redo/redo_all_240924/LeetCode713.java @@ -0,0 +1,63 @@ +package cn.whaifree.redo.redo_all_240924; + +import org.junit.Test; + +import java.util.ArrayList; +import java.util.List; + +/** + * @version 1.0 + * @Author whai文海 + * @Date 2024/9/27 12:12 + * @注释 + */ +public class LeetCode713 { + + @Test + public void test() { + int[] nums = {10,5,2,6}; + int k = 100; + System.out.println(new Solution().numSubarrayProductLessThanK(nums, k)); + } + + + class Solution { + + public int numSubarrayProductLessThanK(int[] nums, int k) { + int left = 0; + int right = 0; + int sum = 1; + int res = 0; + while (right < nums.length) { + + sum *= nums[right]; + while (left < right && sum >= k) { + // 大了就收缩 + sum /= nums[left]; + left++; + } + right++; + if (sum < k) { + res += right - left; // 一共有right-left个数字,所以有right-left种可能以right结尾的 + /** + * 如果一个子串的乘积小于k,那么他的每个子集都小于k,而一个长度为n的数组,他的所有连续子串数量是1+2+...n,但是会和前面的重复。 + * 比如例子中[10, 5, 2, 6],第一个满足条件的子串是[10],第二个满足的是[10, 5],但是第二个数组的子集[10]和前面的已经重复了, + * 因此我们只需要计算包含最右边的数字的子串数量,就不会重复了,也就是在计算[10, 5]这个数组的子串是,只加入[5]和[10, 5], + * 而不加入[10],这部分的子串数量刚好是r - l + 1 + */ + + /** + * 每次 j 增加时,以 j 结尾的满足乘积小于 k 的子数组数量为 j - i + 1。 + * 因为以 j 结尾的子数组可以从 i 到 j 的任意一个位置作为起点,都满足乘积小于 k。 + */ + } + } + + + + return res; + + } + } + +}