This commit is contained in:
ytChen 2024-03-20 09:38:53 +08:00
parent de5d9ea54c
commit f523b8a996
12 changed files with 290 additions and 10 deletions

View File

@ -1,5 +1,6 @@
package com.recovery.admin.boot;
import com.dtp.core.spring.EnableDynamicTp;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@ -12,6 +13,7 @@ import org.springframework.context.annotation.ComponentScan;
@EnableDiscoveryClient
@RefreshScope
@EnableFeignClients
@EnableDynamicTp
@ComponentScan(basePackages = {"com.recovery.admin.boot","com.recovery.common.base"})
@MapperScan("com.recovery.admin.boot.mapper")
public class AdminApp {

View File

@ -83,6 +83,9 @@ public class WebLogAspect {
//执行完切面后将线程共享中的数据源名称清空
@After("webLog()")
public void after(JoinPoint joinPoint){
log.info("清除数据源===========================================");
//清除
HspHostUtil.clear();
DataSourceContextHolder.clearDBType();
}
}

View File

@ -0,0 +1,109 @@
package com.recovery.admin.boot.config;
import com.alibaba.cloud.nacos.NacosConfigManager;
import com.alibaba.cloud.nacos.NacosConfigProperties;
import com.alibaba.nacos.api.config.listener.Listener;
import com.google.common.util.concurrent.ThreadFactoryBuilder;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.context.annotation.Configuration;
import java.util.concurrent.*;
/**
* @author 小工匠
* @version 1.0
* @mark: show me the code , change the world
*/
@RefreshScope
@Configuration
@Slf4j
public class DynamicThreadPoolConfig implements InitializingBean {
private String coreSize = "5";
private String maxSize = "5";
private static ThreadPoolExecutor threadPoolExecutor;
@Autowired
private NacosConfigManager nacosConfigManager;
@Autowired
private NacosConfigProperties nacosConfigProperties;
@Override
public void afterPropertiesSet() throws Exception {
//按照nacos配置初始化线程池
threadPoolExecutor = new ThreadPoolExecutor(Integer.parseInt(coreSize), Integer.parseInt(maxSize), 10L, TimeUnit.SECONDS,
new LinkedBlockingQueue<>(10),
new ThreadFactoryBuilder().setNameFormat("c_t_%d").build(),
new RejectedExecutionHandler() {
@Override
public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
log.info("Warn Warn Warn : rejected executed!!!");
}
});
//nacos配置变更监听
nacosConfigManager.getConfigService().addListener("order-service-dev.yml", nacosConfigProperties.getGroup(),
new Listener() {
@Override
public Executor getExecutor() {
return null;
}
@Override
public void receiveConfigInfo(String configInfo) {
//配置变更修改线程池配置
log.info("收到Nacos Config Server推来的配置变更修改线程池配置", configInfo);
changeThreadPoolConfig(Integer.parseInt(coreSize), Integer.parseInt(maxSize));
}
});
}
/**
* 打印当前线程池的状态
*/
public String printThreadPoolStatus() {
return String.format("core_size:%s,thread_current_size:%s;" +
"thread_max_size:%s;queue_current_size:%s,total_task_count:%s", threadPoolExecutor.getCorePoolSize(),
threadPoolExecutor.getActiveCount(), threadPoolExecutor.getMaximumPoolSize(), threadPoolExecutor.getQueue().size(),
threadPoolExecutor.getTaskCount());
}
/**
* 给线程池增加任务
*
* @param count
*/
public void dynamicThreadPoolAddTask(int count) {
for (int i = 0; i < count; i++) {
int finalI = i;
threadPoolExecutor.execute(() -> {
try {
log.info("dynamicThreadPoolAddTask->", finalI);
Thread.sleep(10000);
} catch (InterruptedException e) {
e.printStackTrace();
}
});
}
}
/**
* 修改线程池核心参数
*
* @param coreSize
* @param maxSize
*/
private void changeThreadPoolConfig(int coreSize, int maxSize) {
threadPoolExecutor.setCorePoolSize(coreSize);
threadPoolExecutor.setMaximumPoolSize(maxSize);
}
}

View File

@ -2,6 +2,8 @@ package com.recovery.admin.boot.controller;
import cn.dev33.satoken.stp.StpUtil;
import cn.hutool.log.Log;
import com.recovery.admin.boot.config.DynamicThreadPoolConfig;
import com.recovery.admin.boot.service.ISysUserService;
import com.recovery.common.base.constant.Constants;
import com.recovery.common.base.dto.UserAuthDTO;
@ -9,6 +11,7 @@ import com.recovery.common.base.result.ApiResult;
import com.recovery.common.base.util.HspHostUtil;
import com.recovery.common.base.util.RedisUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
@ -27,6 +30,8 @@ public class testController {
ISysUserService iSysUserService;
@Resource
RedisUtil redisUtil;
@Autowired
private DynamicThreadPoolConfig dynamicThreadPool;
/**
* cs
@ -35,9 +40,19 @@ public class testController {
public ApiResult<UserAuthDTO> cs(@RequestParam String name, HttpServletRequest request) {
UserAuthDTO authDTO = iSysUserService.getByUsername(name);
log.info("测试库:"+authDTO.getStatus());
String user = StpUtil.getLoginIdDefaultNull()+"";
log.info("111获取线程的数据=="+HspHostUtil.getHspHost());
StpUtil.kickoutByTokenValue(Constants.LOGIN_USRE_TOKEN);
Thread thread = new Thread(()->{
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
log.info("获取线程的数据=="+HspHostUtil.getHspHost());
},"测试线程");
thread.start();
// StpUtil.logout();
dynamicThreadPool.printThreadPoolStatus();
return ApiResult.ok(authDTO);
}

View File

@ -2,13 +2,24 @@ package com.recovery.admin.boot.exception;
import cn.dev33.satoken.exception.NotLoginException;
import cn.dev33.satoken.exception.NotPermissionException;
import cn.dev33.satoken.util.SaResult;
import com.recovery.common.base.result.ApiResult;
import com.recovery.common.base.result.ResultCode;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpStatus;
import org.springframework.validation.BindException;
import org.springframework.validation.BindingResult;
import org.springframework.validation.ObjectError;
import org.springframework.web.HttpRequestMethodNotSupportedException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import javax.validation.ConstraintViolation;
import javax.validation.ConstraintViolationException;
/**
* 全局异常处理
*/
@ -64,11 +75,68 @@ public class GlobalExceptionHandler {
else {
message = "当前会话未登录";
}
// 返回给前端
return ApiResult.failed(message);
}
/**
* 拦截表单参数校验
*/
@ResponseBody
@ExceptionHandler(BindException.class)
public ApiResult bindExceptionHandler(BindException ex) {
StringBuffer sb = new StringBuffer();
BindingResult bindingResult = ex.getBindingResult();
if (bindingResult.hasErrors()) {
for (int i = 0; i < bindingResult.getAllErrors().size(); i++) {
ObjectError error = bindingResult.getAllErrors().get(i);
sb.append((i == 0 ? "" : "\n") + error.getDefaultMessage());
}
}
return ApiResult.failed(sb.toString());
}
@ExceptionHandler(ConstraintViolationException.class)
@ResponseBody
public ApiResult handler(ConstraintViolationException ex) {
StringBuffer sb = new StringBuffer();
int i = 0;
for (ConstraintViolation violation : ex.getConstraintViolations()) {
sb.append((++i == 1 ? "" : "\n") + violation.getMessage());
}
return ApiResult.failed(sb.toString());
}
/**
* 请求方式不支持
*/
@ExceptionHandler(HttpRequestMethodNotSupportedException.class)
public ApiResult httpReqMethodNotSupported(HttpRequestMethodNotSupportedException e) {
log.error("错误信息:{}", e.getLocalizedMessage());
return ApiResult.failed("请求方式不支持");
}
/**
* 未登录异常
*/
@ExceptionHandler(NotLoginException.class)
public ApiResult notLoginException(NotLoginException e) {
return ApiResult.failed(ResultCode.LOGIN_ERROR);
}
/**
* 通用异常
*/
@ResponseStatus(HttpStatus.OK)
@ExceptionHandler(Exception.class)
public ApiResult exception(Exception e) {
if (e instanceof NotPermissionException){
return ApiResult.failed("没有操作权限");
}
e.printStackTrace();
return ApiResult.failed(ResultCode.ACCESS_UNAUTHORIZED);
}
//Security
// @ExceptionHandler(value = AccessDeniedException.class)
// public void accessDeniedException(AccessDeniedException e) {

View File

@ -2,6 +2,9 @@ package com.recovery.admin.boot.interceptor;
import cn.dev33.satoken.stp.StpUtil;
import cn.dev33.satoken.strategy.SaStrategy;
import cn.dev33.satoken.util.SaFoxUtil;
import cn.hutool.extra.spring.SpringUtil;
import com.alibaba.fastjson.JSONObject;
import com.auth0.jwt.interfaces.DecodedJWT;
@ -36,14 +39,21 @@ public class JwtInterceptor implements HandlerInterceptor {
}
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
HashMap<String, String> map=new HashMap<>();
// //获取请求参数
// RequestWrapper requestWrapper = new RequestWrapper(request);
// //这里getBodyString()方法无参数
// log.info("RequestBody: {}", requestWrapper.getBodyString());
//从http请求头获取token
response.setHeader( "Set-Cookie" , "cookiename=httponlyTest;Path=/;Domain=domainvalue;Max-Age=seconds;HTTPOnly");
response.setHeader( "Content-Security-Policy" , "default-src 'self'; script-src 'self'; frame-ancestors 'self'");
response.setHeader("Access-Control-Allow-Origin", (request).getHeader("Origin"));
response.setHeader("Access-Control-Allow-Credentials", "true");
response.setHeader("Referrer-Policy","no-referrer");
response.setContentType("application/json");
response.setCharacterEncoding("UTF-8");
// 获取当前token这个token获取的是请求头的token也可以用 request 获取
String token = StpUtil.getTokenValue();
long tokenTimeout = StpUtil.getTokenTimeout();// 获取过期时间
//token没过期过期时间不是-1的时候每次请求都刷新过期时间
if (tokenTimeout != -1){
StpUtil.renewTimeout(3600);// 用于token续期
}
String token = request.getHeader(Constants.LOGIN_USRE_TOKEN);
if (StringUtils.isEmpty(token)) {
throw new BusinessException(ResultCode.LOGIN_ERROR);
}

View File

@ -19,5 +19,60 @@ spring:
prefix: hoe-admin
group: dev
namespace: 11bfd099-10d6-4f2c-b969-58b76e435cce
dynamic:
tp:
enabled: true
enabledBanner: true # 是否开启banner打印默认true
enabledCollect: true # 是否开启监控指标采集默认false
collectorTypes: micrometer,logging # 监控数据采集器类型logging | micrometer | internal_logging默认micrometer
logPath: /home/logs # 监控日志数据路径,默认 ${user.home}/logs采集类型非logging不用配置
monitorInterval: 5 # 监控时间间隔报警判断、指标采集默认5s
platforms: # 通知报警平台配置
- platform: email
receivers: 1650611030@qq.com
tomcatTp: # tomcat webserver线程池配置
corePoolSize: 100
maximumPoolSize: 200
keepAliveTime: 60
executors: # 动态线程池配置
- threadPoolName: dtpExecutor
# 线程池别名
executorType: common # 线程池类型common、eager适用于io密集型
corePoolSize: 5 # 核心线程数
maximumPoolSize: 18 # 最大线程数
queueCapacity: 400 # 任务队列容量
queueType: VariableLinkedBlockingQueue # 任务队列查看源码QueueTypeEnum枚举类
rejectedHandlerType: CallerRunsPolicy # 拒绝策略查看RejectedTypeEnum枚举类
keepAliveTime: 50
allowCoreThreadTimeOut: false # 是否允许核心线程池超时
threadNamePrefix: test # 线程名前缀
waitForTasksToCompleteOnShutdown: false # 参考spring线程池设计优雅关闭线程池
awaitTerminationSeconds: 5 # 单位s
preStartAllCoreThreads: false # 是否预热所有核心线程默认false
runTimeout: 200 # 任务执行超时阈值目前只做告警用单位ms
queueTimeout: 100 # 任务在队列等待超时阈值目前只做告警用单位ms
taskWrapperNames: [ "ttl" ] # 任务包装器名称集成TaskWrapper接口
notifyItems: # 报警项,不配置自动会按默认值配置(变更通知、容量报警、活性报警、拒绝报警、任务超时报警)
- type: capacity # 报警项类型,查看源码 NotifyTypeEnum枚举类
enabled: true
threshold: 80 # 报警阈值
platforms: [ ding,wechat ] # 可选配置不配置默认拿上层platforms配置的所以平台
interval: 120 # 报警间隔单位s
- type: change
enabled: true
- type: liveness
enabled: true
threshold: 80
- type: reject
enabled: true
threshold: 1
- type: run_timeout
enabled: true
threshold: 1
- type: queue_timeout
enabled: true
threshold: 1
server:
port: 9002

View File

@ -71,6 +71,7 @@ public class WebLogAspect {
//执行完切面后将线程共享中的数据源名称清空
@After("webLog()")
public void after(JoinPoint joinPoint){
log.info("清除数据源===========================================");
//清除
HspHostUtil.clear();
DataSourceContextHolder.clearDBType();

View File

@ -0,0 +1,4 @@
package com.recovery.common.base.config;
public class GlobalCorsConfig {
}

View File

@ -0,0 +1,7 @@
package com.recovery.common.base.enums;
/**
* 错误码
*/
public enum ErrCodeEnums {
}

View File

@ -20,7 +20,7 @@ public class HspHostUtil {
@Resource
private static RedisUtil redisUtil;
//医院代码
private static final ThreadLocal<String> HSP_HSOT = new ThreadLocal<String>();
private static final ThreadLocal<String> HSP_HSOT = new InheritableThreadLocal<>();
public static String getHspHost() {
return HSP_HSOT.get();
}

View File

@ -58,6 +58,12 @@
<artifactId>sa-token-jwt</artifactId>
<version>1.37.0</version>
</dependency>
<dependency>
<groupId>cn.dynamictp</groupId>
<artifactId>dynamic-tp-spring-cloud-starter-nacos</artifactId>
<version>1.0.8</version>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-pool2</artifactId>