refactor(SpringCloud): 重构 Nacos 配置并添加限流功能

- 更新 ServiceA 和 ServiceB 的 application.yaml 文件,调整 Nacos 配置
- 在 ServiceB 中添加基于 Nacos 配置的动态限流功能
- 更新 DynamicThreadPoolController 中的线程池初始化逻辑
- 移除 SpringDemo 中的 MyBatis相关配置
- 更新项目依赖版本:
  - Spring Cloud 版本:2023.0.3
  - Lombok版本:1.18.30
  - MyBatis-Plus 版本:3.5.9
  - MyBatis-Plus3 版本:3.5.9
  - Spring Boot 版本:3.1.5
  - Java版本:17
  - Maven 版本:3.8.6
This commit is contained in:
whaifree 2024-11-02 13:06:38 +08:00
parent b6e5672f09
commit 2b497eafb4
18 changed files with 687 additions and 30 deletions

View File

@ -0,0 +1,71 @@
package cn.whaifree.leetCode;
import org.junit.Test;
/**
* @version 1.0
* @Author whai文海
* @Date 2024/11/1 14:24
* @注释
*/
public class LeetCode283 {
@Test
public void test() {
int[] nums = {0, 4, 0, 3, 12, 0};
new Solution().moveZeroes(nums);
for (int num : nums) {
System.out.print(num + " ");
}
}
class Solution {
public void moveZeroes(int[] nums) {
for (int i = 1; i < nums.length; i++) {
// 插入排序
for (int j = i; j > 0; j--) {
if (nums[j - 1] == 0) {
swap(nums, j, j - 1);
} else if (nums[j] != 0 && nums[j - 1] == 0) { // 遇到非0就往前插入
swap(nums, j, j - 1);
} else {
break;
}
}
}
}
public void swap(int[] nums, int i, int j) {
int temp = nums[i];
nums[i] = nums[j];
nums[j] = temp;
}
}
@Test
public void test1() {
int[] nums = {0, 4, 0, 3, 12, 0};
new Solution1().moveZeroes(nums);
for (int num : nums) {
System.out.print(num + " ");
}
}
class Solution1 {
public void moveZeroes(int[] nums) {
int left = 0;
for (int i = 0; i < nums.length; i++) {
if (nums[i] != 0) {
nums[left] = nums[i];
left++;
}
}
for (int i = left; i < nums.length; i++) {
nums[i] = 0;
}
}
}
}

View File

@ -0,0 +1,96 @@
package cn.whaifree.leetCode;
import org.junit.Test;
import java.util.HashMap;
import java.util.Map;
/**
* @version 1.0
* @Author whai文海
* @Date 2024/11/1 15:17
* @注释
*/
public class LeetCode560 {
@Test
public void test() {
int[] nums = {-1, -1, 1};
int k = 0;
int res = new Solution().subarraySum(nums, k);
System.out.println(res);
}
class Solution {
/**
* -2 -5 1 2
*
* -2 -7 -6 -4
* 子数组(连续)的个数
*
* 前缀和前缀和的区间总面积为k的个数
*
* @param nums
* @param k
* @return
*/
public int subarraySum(int[] nums, int k) {
int len = nums.length;
int[] preSum = new int[len + 1];
for (int i = 0; i < len; i++) {
preSum[i + 1] = preSum[i] + nums[i];
}
int count = 0;
for (int left = 0; left < len; left++) {
for (int right = left; right < len; right++) {
// 区间和 [left..right]注意下标偏移
if (preSum[right + 1] - preSum[left] == k) {
count++;
}
}
}
return count;
}
/**
* 子数组(连续)的个数
*
* 前缀和前缀和的区间总面积为k的个数
*
*
* 1. 每次计算的元素n在前面计算好前缀和只要找到k-n的前缀和的个数
* --- 计算每个前缀和的数量
*
*
* @param nums
* @param k
* @return
*/
public int subarraySum1(int[] nums, int k) {
Map<Integer, Integer> map = new HashMap<>();
map.put(0, 1);
// map为各种前缀和的数量
int preSum = 0;
int res = 0;
for (int num : nums) {
preSum += num;
// 画图就是一个大的面积找到小的面积取其中的差值区域即找到一个范围==k
// | preSum-k | k |
// 整个是preSum
// PreSum当前的前缀和-某个之前的前缀和=k而map存储之前前缀和k的个数为v个
if (map.containsKey(preSum - k)) {
res += map.get(preSum - k);
}
map.put(preSum, map.getOrDefault(preSum, 0) + 1);
}
return res;
}
}
}

View File

@ -0,0 +1,45 @@
package cn.whaifree.redo.redo_all_241016;
import org.junit.Test;
import java.util.Arrays;
/**
* @version 1.0
* @Author whai文海
* @Date 2024/11/1 14:08
* @注释
*/
public class LeetCode1005 {
@Test
public void test() {
int[] nums = {4,-5,4,-5,9,4,5};
int k = 1;
int result = new Solution().largestSumAfterKNegations(nums, k);
System.out.println(result);
}
class Solution {
public int largestSumAfterKNegations(int[] nums, int k) {
Arrays.sort(nums);
int res = 0;
int minAbs = Integer.MAX_VALUE;
for (int i = 0; i < nums.length; i++) {
if (nums[i] < 0 && k > 0) {
nums[i] = -nums[i];
k--;
}
minAbs = Math.min(minAbs, nums[i]);
res += nums[i];
}
if (k % 2 == 0) {
// 偶数
return res;
}else {
return res - minAbs * 2;
}
}
}
}

View File

@ -0,0 +1,48 @@
package cn.whaifree.redo.redo_all_241016;
/**
* @version 1.0
* @Author whai文海
* @Date 2024/10/31 12:03
* @注释
*/
public class LeetCode123 {
class Solution {
/**
*
* dp[1] 表示手里没有第一支股票的最大利润
* - 前一天就没 dp[i-1][1]
* - 前一天有第一支今天刚刚卖了 dp[i-1][2] + prices[i]
* dp[2] 表示手里有第一支股票的最大利润
* - 前一天就有 dp[i-1][2]
* - 前一天没有刚刚买入第一支 dp[i-1][1] - prices[i]
*
* dp[3] 表示手里没有第二支股票的最大利润
*
* dp[4] 表示手里有第二支股票的最大利润
*
* @param prices
* @return
*/
public int maxProfit(int[] prices) {
int[][] dp = new int[prices.length][5];
dp[0][1] = 0;
dp[0][2] = -prices[0];
dp[0][3] = 0;
dp[0][4] = -prices[0];
for (int i = 1; i < prices.length; i++) {
dp[i][2] = Math.max(dp[i - 1][2], dp[i - 1][0] - prices[i]);
dp[i][1] = Math.max(dp[i - 1][1], dp[i - 1][2] + prices[i]);
// 手里有第二支股票的最大利润 前一天就有第二支前一天没有第二支也没有第一支但今天买入
dp[i][4] = Math.max(dp[i - 1][4], dp[i - 1][1] - prices[i]);
// 手里没有第二支股票的最大利润 前一天就没有第二支前一天有第二支但今天卖出
dp[i][3] = Math.max(dp[i - 1][3], dp[i - 1][4] + prices[i]);
}
return dp[prices.length - 1][3];
}
}
}

View File

@ -0,0 +1,87 @@
package cn.whaifree.redo.redo_all_241016;
import org.junit.Test;
/**
* @version 1.0
* @Author whai文海
* @Date 2024/10/31 11:28
* @注释
*/
public class LeetCode289 {
@Test
public void test() {
int[][] board = new int[][]{{0,1,0},{0,0,1},{1,1,1},{0,0,0}};
new Solution().gameOfLife(board);
for (int i = 0; i < board.length; i++) {
for (int j = 0; j < board[0].length; j++) {
System.out.print(board[i][j] + " ");
}
}
}
class Solution {
/**
* 如果活细胞周围八个位置的活细胞数少于两个则该位置活细胞死亡1 2
* 如果活细胞周围八个位置有两个或三个活细胞则该位置活细胞仍然存活 1 1
* 如果活细胞周围八个位置有超过三个活细胞则该位置活细胞死亡1 2
* 如果死细胞周围正好有三个活细胞则该位置死细胞复活0 3
*
* 2 之前是活着现在死了
* 3 之前是死的现在活了
*
* 最后遍历吧3变成1把2变成0
*
* @param board
*/
public void gameOfLife(int[][] board) {
for (int i = 0; i < board.length; i++) {
for (int j = 0; j < board[0].length; j++) {
int count = aliveCount(board, i, j);
if (board[i][j] == 1) { // 之前是活着的
if (count < 2) {
board[i][j] = 2;
}else if (count > 3) {
board[i][j] = 2;
}
}else {
if (count == 3) {
board[i][j] = 3;
}
}
}
}
for (int i = 0; i < board.length; i++) {
for (int j = 0; j < board[0].length; j++) {
if (board[i][j] == 3) {
board[i][j] = 1;
} else if (board[i][j] == 2) {
board[i][j] = 0;
}
}
}
}
public int aliveCount(int[][] board, int i, int j) {
int res = 0;
for (int ti = i - 1; ti < i + 2; ti++) {
for (int tj = j - 1; tj < j + 2; tj++) {
if (ti == i && tj == j) {
continue;
}
if (ti >= 0 && ti < board.length && tj >= 0 && tj < board[0].length) {
if (board[ti][tj] == 1 || board[ti][tj] == 2) {
res++;
}
}
}
}
return res;
}
}
}

View File

@ -0,0 +1,10 @@
package cn.whaifree.redo.redo_all_241016;
/**
* @version 1.0
* @Author whai文海
* @Date 2024/10/31 12:56
* @注释
*/
public class LeetCode468 {
}

View File

@ -0,0 +1,58 @@
package cn.whaifree.redo.redo_all_241016;
import org.junit.Test;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* @version 1.0
* @Author whai文海
* @Date 2024/11/1 13:55
* @注释
*/
public class LeetCode49 {
@Test
public void test() {
String[] strs = {"abbbbbbbbbbb","aaaaaaaaaaab"};
List<List<String>> result = new Solution().groupAnagrams(strs);
for (List<String> list : result) {
System.out.println(list);
}
}
class Solution {
public List<List<String>> groupAnagrams(String[] strs) {
Map<String, List<String>> map = new HashMap<>();
for (int i = 0; i < strs.length; i++) {
int[] counts = new int[26];
String item = strs[i];
for (int j = 0; j < item.length(); j++) {
counts[item.charAt(j) - 'a']++;
}
StringBuilder str = new StringBuilder();
for (int j = 0; j < counts.length; j++) {
if (counts[j] != 0) {
str.append((char) ('a' + j)).append(counts[j]);
}
}
if (!map.containsKey(str.toString())) {
map.put(str.toString(), new ArrayList<>());
}
map.get(str.toString()).add(strs[i]);
}
List<List<String>> result = new ArrayList<>();
for (Map.Entry<String, List<String>> entry : map.entrySet()) {
result.add(entry.getValue());
}
return result;
}
}
}

View File

@ -33,7 +33,6 @@ public class DynamicThreadPoolController {
@PostConstruct
public void init() {
executor = new ThreadPoolExecutor(
Runtime.getRuntime().availableProcessors(), Runtime.getRuntime().availableProcessors() * 4, 10, TimeUnit.SECONDS,
new ArrayBlockingQueue<>(100),
@ -68,6 +67,7 @@ class DynamicThreadPoolConfig implements ApplicationListener<EnvironmentChangeEv
@Override
public void onApplicationEvent(EnvironmentChangeEvent event) {
System.out.println("DynamicThreadPoolConfig.onApplicationEvent");
if (event.getKeys().contains("dynamic.coreSize")) {
// 获取值
Integer coreSize = threadConfigProperties.getCoreSize();

View File

@ -15,7 +15,7 @@ spring:
# 配置所属命名空间的id我们配置名称为dev的id在命名空间列表查看id的值
namespace: 97ff159f-6177-4aab-b735-bd75458949d4
# 文件名,如果没有配置则默认为 ${spring.application.name}
prefix: springboot3-nacos
# prefix: springboot3-nacos
# 配置所属分组
group: DEFAULT_GROUP
# 后缀名,只支持 properties 和 yaml 类型
@ -24,12 +24,9 @@ spring:
server-addr: localhost:8848
# 配置自动刷新
refresh-enabled: true
# 启用远程同步配置
enable-remote-sync-config: true
config:
import:
- optional:nacos:${spring.application.name}.${spring.cloud.nacos.config.file-extension}
- optional:nacos:springboot3-nacos.${spring.cloud.nacos.config.file-extension}
# Logger Config
logging:

View File

@ -1,11 +1,25 @@
package com.whai.springcloud.serviceb.controller;
import cn.hutool.core.util.StrUtil;
import com.alibaba.cloud.nacos.NacosConfigManager;
import com.alibaba.nacos.api.config.ConfigService;
import com.alibaba.nacos.api.config.listener.Listener;
import com.alibaba.nacos.api.exception.NacosException;
import com.alibaba.nacos.shaded.com.google.common.util.concurrent.RateLimiter;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.core.env.Environment;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Executor;
/**
* @version 1.0
* @Author whai文海
@ -13,13 +27,44 @@ import org.springframework.web.bind.annotation.RestController;
* @注释
*/
@RestController
@RefreshScope
public class BProviderController {
private final Environment environment;
private RateLimiter rateLimiter;
@Autowired
public BProviderController(Environment environment) {
public BProviderController(Environment environment, @Qualifier("nacosConfigManager") NacosConfigManager nacosConfigManager) {
this.environment = environment;
initRateLimiter();
ConfigService configService = nacosConfigManager.getConfigService();
try {
configService.addListener("cvm.reset.ratelimit", "DEFAULT_GROUP", new Listener() {
@Override
public Executor getExecutor() {
return null;
}
@Override
public void receiveConfigInfo(String configInfo) {
System.out.println("收到配置变更cvm.reset.ratelimit" + configInfo);
rateLimiter.setRate(Double.parseDouble(configInfo));
}
});
} catch (NacosException e) {
throw new RuntimeException(e);
}
}
@Value("${cvm.reset.ratelimit}")
private int rateLimitCount;
private void initRateLimiter() {
if (rateLimitCount <= 0) {
rateLimitCount = 1;
}
System.out.println("初始化限流器:" + rateLimitCount);
rateLimiter = RateLimiter.create(rateLimitCount);
}
// 提供服务
@RequestMapping("/getB")
@ -28,4 +73,20 @@ public class BProviderController {
}
@PostMapping("/CVM/reset")
public String reset(@RequestBody List<String> InstanceIds) {
System.out.println("当前限流数:" + rateLimiter.getRate());
List<String> succ = new ArrayList<>();
for (String instanceId : InstanceIds) {
if (rateLimiter.tryAcquire()) {
// 处理重置请求
succ.add(instanceId);
}else {
return "请求过于频繁,请稍后重试";
}
}
return StrUtil.format("重置成功,成功实例:{}", succ);
}
}

View File

@ -1,6 +1,5 @@
spring:
application:
name: ServiceB
cloud:
@ -12,23 +11,21 @@ spring:
group: DEFAULT_GROUP
server-addr: localhost:8848
config:
# ?????????id????????dev?id??????????id??
# 配置所属命名空间的id我们配置名称为dev的id在命名空间列表查看id的值
namespace: 97ff159f-6177-4aab-b735-bd75458949d4
# ?????????????? ${spring.application.name}
# 文件名,如果没有配置则默认为 ${spring.application.name}
# prefix: springboot3-nacos
# ??????
# 配置所属分组
group: DEFAULT_GROUP
# ??????? properties ? yaml ??
# 后缀名,只支持 properties 和 yaml 类型
file-extension: yaml
# nacos?????
# nacos服务器地址
server-addr: localhost:8848
# ??????
# 配置自动刷新
refresh-enabled: true
# ????????
enable-remote-sync-config: true
config:
import:
- optional:nacos:${spring.application.name}.${spring.cloud.nacos.config.file-extension}
- optional:nacos:springboot3-nacos.${spring.cloud.nacos.config.file-extension}
# Logger Config
logging:
@ -37,4 +34,3 @@ logging:
hexadecimal:
name: whai
# --server.port=12139

View File

@ -31,6 +31,13 @@
<spring-cloud.version>2023.0.3</spring-cloud.version>
</properties>
<dependencies>
<!-- Lombok -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.30</version>
<scope>provided</scope>
</dependency>
<!--hutool-->
<dependency>
<groupId>cn.hutool</groupId>

View File

@ -38,14 +38,8 @@
<!-- https://mvnrepository.com/artifact/com.baomidou/mybatis-plus-boot-starter -->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.5.1</version>
</dependency>
<!--mybatis-->
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>3.0.2</version>
<artifactId>mybatis-plus-spring-boot3-starter</artifactId>
<version>3.5.9</version>
</dependency>
<dependency>
<groupId>com.github.xiaoymin</groupId>

View File

@ -0,0 +1,16 @@
package cn.whaifree.springdemo.config;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.context.annotation.Configuration;
/**
* @version 1.0
* @Author whai文海
* @Date 2024/11/1 12:41
* @注释
*/
@Configuration
@MapperScan("cn.whaifree.springdemo.mybatis.mapper")
public class MybatisConfig {
}

View File

@ -0,0 +1,138 @@
//package cn.whaifree.springdemo.tech;
//
//import jakarta.annotation.Resource;
//import org.springframework.beans.factory.annotation.Autowired;
//import org.springframework.context.annotation.AnnotationConfigApplicationContext;
//import org.springframework.context.annotation.Lazy;
//import org.springframework.context.annotation.Scope;
//import org.springframework.stereotype.Component;
//
///**
// * @version 1.0
// * @Author whai文海
// * @Date 2024/10/31 13:57
// * @注释
// */
//public class CircleRelation {
//
//
//}
//
//
//// ================================================================
//
//@Component
////@Scope("prototype")
//class A1{
//// @Resource
//// public B1 b1;
//
// public static void main(String[] args) {
// AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext("cn.whaifree.springdemo.tech");
//// A1 bean = context.getBean(A1.class);
//// System.out.println(bean.b1.a1);
//
//// LazyA1 lazyA1 = context.getBean(LazyA1.class);
//// System.out.println(lazyA1.b1);
// }
//}
//
///**
// * BeanCurrentlyInCreationException:
// * Error creating bean with name 'a1':
// * Requested bean is currently in creation: Is there an unresolvable circular reference?
// *
// * A 实例创建后populateBean 会触发 B 的加载
// * B 实例创建后populateBean 会触发 A 的加载由于 A scope=prototype需要的时候都会创建一个全新的 A
// * 这样就会进入一个死循环Spring 肯定是解决不了这种情况下的循环依赖的所以提前进行了 check并抛出了异常
// *
// * A需要一个新的BB需要一个新的A不能用为初始化的A活着B
// */
//@Component
////@Scope("prototype")
//class B1{
// @Resource
// public A1 a1;
//}
//
//
//@Component
//@Scope("prototype")
//class LazyA1{
//
// @Autowired
// @Lazy
// public LazyB1 b1;
//}
///**
// *
// */
//@Component
//@Scope("prototype")
//class LazyB1{
// @Autowired
// public LazyA1 a1;
//}
//// ================================================================
//
//
//@Component
//class ConstructA {
// ConstructB b;
// public ConstructA(@Lazy ConstructB b) {
// this.b = b;
// }
//}
//@Component
//class ConstructB {
// ConstructA a;
// public ConstructB(ConstructA a) {
// this.a = a;
// }
// public static void main(String[] args) {
// /**
// * A 实例在创建时(createBeanInstance)由于是构造注入这时会触发 B 的加载
// * B 实例在创建时(createBeanInstance)又会触发 A 的加载此时A 还没有添加到三级缓存工厂所以就会创建一个全新的 A
// * 这样就会进入一个死循环Spring 是解决不了这种情况下的循环依赖的所以提前进行了 check并抛出了异常
// *
// * 解决@Lazy放在构造器上这样A 实例在创建时不会触发 B 的加载
// *
// * 构造函数注入要求在创建bean实例时必须提供所有依赖
// *
// */
// AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext("cn.whaifree.springdemo.tech");
// ConstructA bean = context.getBean(ConstructA.class);
// System.out.println(bean.b);
// // Unsatisfied dependency expressed through constructor parameter 0:
// // Error creating bean with name 'constructA': Requested bean is currently in creation: Is there an unresolvable circular reference?
// }
//}
//
//
//
//@Component
//class SetA {
// SetB b;
//
// @Autowired
// public void setB(SetB b) {
// this.b = b;
// }
//}
//@Component
//class SetB {
//
// SetA a;
//
// @Autowired
// public void setA(SetA a) {
// this.a = a;
// }
//
// public static void main(String[] args) {
//
// AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext("cn.whaifree.springdemo.tech");
// SetA bean = context.getBean(SetA.class);
// System.out.println(bean.b);
// }
//}

View File

@ -40,8 +40,6 @@ spring:
direct:
acknowledge-mode: manual
mybatis:
mapper-locations: '"classpath:/mapper/**.xml"'
mybatis-plus:
configuration:
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
@ -49,6 +47,7 @@ mybatis-plus:
db-config:
logic-delete-field: 1
logic-not-delete-value: 0
mapper-locations: "classpath:/mapper/**.xml"

View File

@ -0,0 +1,34 @@
package cn.whaifree.springdemo.MybatisTest;
import cn.whaifree.springdemo.mybatis.domain.Orders;
import cn.whaifree.springdemo.mybatis.mapper.OrdersMapper;
import cn.whaifree.springdemo.mybatis.service.OrdersService;
import jakarta.annotation.Resource;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
import java.util.List;
/**
* @version 1.0
* @Author whai文海
* @Date 2024/11/1 12:43
* @注释
*/
@SpringBootTest
public class MybatisTest {
@Resource
private OrdersService ordersService;
@Resource
private OrdersMapper ordersMapper;
@Test
public void test() {
List<Orders> orders = ordersMapper.selectList(null);
System.out.println(orders);
List<Orders> orders1 = ordersMapper.selectByExample();
System.out.println(orders1);
}
}

View File

@ -7,7 +7,7 @@
<groupId>org.example</groupId> <!--groupId表示当前 Maven 项目隶属的组织或公司-->
<artifactId>LeetCode</artifactId> <!--当前 Maven 项目的名称-->
<version>1.0-SNAPSHOT</version><!--定义了 Maven 项目当前所处版本-->
<packaging>pom</packaging>
<packaging>jar</packaging>
<modules>
<module>ForJdk8</module>
<module>ForJdk17</module>