From f523b8a996d80c00196006068aac054e95e0b915 Mon Sep 17 00:00:00 2001 From: ytChen <1650611030@qq.com> Date: Wed, 20 Mar 2024 09:38:53 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BC=98=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/recovery/admin/boot/AdminApp.java | 2 + .../admin/boot/aspect/WebLogAspect.java | 3 + .../boot/config/DynamicThreadPoolConfig.java | 109 ++++++++++++++++++ .../admin/boot/controller/testController.java | 17 ++- .../exception/GlobalExceptionHandler.java | 70 ++++++++++- .../boot/interceptor/JwtInterceptor.java | 24 ++-- .../src/main/resources/bootstrap.yml | 55 +++++++++ .../recovery/auth/aspect/WebLogAspect.java | 1 + .../common/base/config/GlobalCorsConfig.java | 4 + .../common/base/enums/ErrCodeEnums.java | 7 ++ .../common/base/util/HspHostUtil.java | 2 +- pom.xml | 6 + 12 files changed, 290 insertions(+), 10 deletions(-) create mode 100644 hoe-admin/admin-boot/src/main/java/com/recovery/admin/boot/config/DynamicThreadPoolConfig.java create mode 100644 hoe-common/common-base/src/main/java/com/recovery/common/base/config/GlobalCorsConfig.java create mode 100644 hoe-common/common-base/src/main/java/com/recovery/common/base/enums/ErrCodeEnums.java diff --git a/hoe-admin/admin-boot/src/main/java/com/recovery/admin/boot/AdminApp.java b/hoe-admin/admin-boot/src/main/java/com/recovery/admin/boot/AdminApp.java index d930c1d..2c6a3eb 100644 --- a/hoe-admin/admin-boot/src/main/java/com/recovery/admin/boot/AdminApp.java +++ b/hoe-admin/admin-boot/src/main/java/com/recovery/admin/boot/AdminApp.java @@ -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 { diff --git a/hoe-admin/admin-boot/src/main/java/com/recovery/admin/boot/aspect/WebLogAspect.java b/hoe-admin/admin-boot/src/main/java/com/recovery/admin/boot/aspect/WebLogAspect.java index 1682e07..b5fee59 100644 --- a/hoe-admin/admin-boot/src/main/java/com/recovery/admin/boot/aspect/WebLogAspect.java +++ b/hoe-admin/admin-boot/src/main/java/com/recovery/admin/boot/aspect/WebLogAspect.java @@ -83,6 +83,9 @@ public class WebLogAspect { //执行完切面后,将线程共享中的数据源名称清空 @After("webLog()") public void after(JoinPoint joinPoint){ + log.info("清除数据源==========================================="); + //清除 + HspHostUtil.clear(); DataSourceContextHolder.clearDBType(); } } diff --git a/hoe-admin/admin-boot/src/main/java/com/recovery/admin/boot/config/DynamicThreadPoolConfig.java b/hoe-admin/admin-boot/src/main/java/com/recovery/admin/boot/config/DynamicThreadPoolConfig.java new file mode 100644 index 0000000..9d16fba --- /dev/null +++ b/hoe-admin/admin-boot/src/main/java/com/recovery/admin/boot/config/DynamicThreadPoolConfig.java @@ -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); + } +} diff --git a/hoe-admin/admin-boot/src/main/java/com/recovery/admin/boot/controller/testController.java b/hoe-admin/admin-boot/src/main/java/com/recovery/admin/boot/controller/testController.java index b54c512..ff8bffe 100644 --- a/hoe-admin/admin-boot/src/main/java/com/recovery/admin/boot/controller/testController.java +++ b/hoe-admin/admin-boot/src/main/java/com/recovery/admin/boot/controller/testController.java @@ -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 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); } diff --git a/hoe-admin/admin-boot/src/main/java/com/recovery/admin/boot/exception/GlobalExceptionHandler.java b/hoe-admin/admin-boot/src/main/java/com/recovery/admin/boot/exception/GlobalExceptionHandler.java index d2e5082..7d41c49 100644 --- a/hoe-admin/admin-boot/src/main/java/com/recovery/admin/boot/exception/GlobalExceptionHandler.java +++ b/hoe-admin/admin-boot/src/main/java/com/recovery/admin/boot/exception/GlobalExceptionHandler.java @@ -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) { diff --git a/hoe-admin/admin-boot/src/main/java/com/recovery/admin/boot/interceptor/JwtInterceptor.java b/hoe-admin/admin-boot/src/main/java/com/recovery/admin/boot/interceptor/JwtInterceptor.java index bc6eace..a3c6290 100644 --- a/hoe-admin/admin-boot/src/main/java/com/recovery/admin/boot/interceptor/JwtInterceptor.java +++ b/hoe-admin/admin-boot/src/main/java/com/recovery/admin/boot/interceptor/JwtInterceptor.java @@ -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 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); } diff --git a/hoe-admin/admin-boot/src/main/resources/bootstrap.yml b/hoe-admin/admin-boot/src/main/resources/bootstrap.yml index a4dece1..530d7d3 100644 --- a/hoe-admin/admin-boot/src/main/resources/bootstrap.yml +++ b/hoe-admin/admin-boot/src/main/resources/bootstrap.yml @@ -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 + diff --git a/hoe-auth/src/main/java/com/recovery/auth/aspect/WebLogAspect.java b/hoe-auth/src/main/java/com/recovery/auth/aspect/WebLogAspect.java index d237083..0b55b68 100644 --- a/hoe-auth/src/main/java/com/recovery/auth/aspect/WebLogAspect.java +++ b/hoe-auth/src/main/java/com/recovery/auth/aspect/WebLogAspect.java @@ -71,6 +71,7 @@ public class WebLogAspect { //执行完切面后,将线程共享中的数据源名称清空 @After("webLog()") public void after(JoinPoint joinPoint){ + log.info("清除数据源==========================================="); //清除 HspHostUtil.clear(); DataSourceContextHolder.clearDBType(); diff --git a/hoe-common/common-base/src/main/java/com/recovery/common/base/config/GlobalCorsConfig.java b/hoe-common/common-base/src/main/java/com/recovery/common/base/config/GlobalCorsConfig.java new file mode 100644 index 0000000..bfe3663 --- /dev/null +++ b/hoe-common/common-base/src/main/java/com/recovery/common/base/config/GlobalCorsConfig.java @@ -0,0 +1,4 @@ +package com.recovery.common.base.config; + +public class GlobalCorsConfig { +} diff --git a/hoe-common/common-base/src/main/java/com/recovery/common/base/enums/ErrCodeEnums.java b/hoe-common/common-base/src/main/java/com/recovery/common/base/enums/ErrCodeEnums.java new file mode 100644 index 0000000..7edaa7e --- /dev/null +++ b/hoe-common/common-base/src/main/java/com/recovery/common/base/enums/ErrCodeEnums.java @@ -0,0 +1,7 @@ +package com.recovery.common.base.enums; + +/** + * 错误码 + */ +public enum ErrCodeEnums { +} diff --git a/hoe-common/common-base/src/main/java/com/recovery/common/base/util/HspHostUtil.java b/hoe-common/common-base/src/main/java/com/recovery/common/base/util/HspHostUtil.java index b5701cc..da926af 100644 --- a/hoe-common/common-base/src/main/java/com/recovery/common/base/util/HspHostUtil.java +++ b/hoe-common/common-base/src/main/java/com/recovery/common/base/util/HspHostUtil.java @@ -20,7 +20,7 @@ public class HspHostUtil { @Resource private static RedisUtil redisUtil; //医院代码 - private static final ThreadLocal HSP_HSOT = new ThreadLocal(); + private static final ThreadLocal HSP_HSOT = new InheritableThreadLocal<>(); public static String getHspHost() { return HSP_HSOT.get(); } diff --git a/pom.xml b/pom.xml index 35bee7c..02497de 100644 --- a/pom.xml +++ b/pom.xml @@ -58,6 +58,12 @@ sa-token-jwt 1.37.0 + + + cn.dynamictp + dynamic-tp-spring-cloud-starter-nacos + 1.0.8 + org.apache.commons commons-pool2