TaskService.java 9.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251
  1. package com.fdkankan.mqcontroller.task;
  2. import cn.hutool.core.date.DateUnit;
  3. import cn.hutool.core.date.DateUtil;
  4. import cn.hutool.log.Log;
  5. import com.fdkankan.mqcontroller.entity.*;
  6. import com.fdkankan.mqcontroller.service.IMqEcsService;
  7. import com.fdkankan.mqcontroller.service.IMqQueueConfigService;
  8. import com.fdkankan.mqcontroller.service.IMqScalingConfigService;
  9. import com.fdkankan.mqcontroller.service.IMqSendLogService;
  10. import com.fdkankan.mqcontroller.utils.ECSUtils;
  11. import com.fdkankan.mqcontroller.utils.RabbitMqUtils;
  12. import com.fdkankan.mqcontroller.utils.RedisKey;
  13. import com.fdkankan.rabbitmq.util.RabbitMqProducer;
  14. import com.fdkankan.redis.util.RedisUtil;
  15. import lombok.extern.slf4j.Slf4j;
  16. import org.apache.commons.lang3.StringUtils;
  17. import org.springframework.beans.factory.annotation.Autowired;
  18. import org.springframework.beans.factory.annotation.Value;
  19. import org.springframework.cloud.context.config.annotation.RefreshScope;
  20. import org.springframework.scheduling.annotation.Async;
  21. import org.springframework.stereotype.Service;
  22. import java.sql.SQLOutput;
  23. import java.util.*;
  24. import java.util.concurrent.LinkedBlockingQueue;
  25. import java.util.stream.Collectors;
  26. @Service
  27. @RefreshScope
  28. @Slf4j
  29. public class TaskService {
  30. @Autowired
  31. RabbitMqProducer rabbitMqProducer;
  32. @Autowired
  33. IMqSendLogService mqSendLogService;
  34. @Autowired
  35. IMqQueueConfigService queueConfigService;
  36. @Autowired
  37. IMqScalingConfigService mqScalingConfigService;
  38. @Autowired
  39. IMqEcsService mqEcsService;
  40. @Autowired
  41. RedisUtil redisUtil;
  42. public static Integer checkOpenCount = 0;
  43. private static final LinkedBlockingQueue<DelEcsVo> delList = new LinkedBlockingQueue<>();
  44. private static final HashMap<String,LinkedBlockingQueue<DelEcsVo>> openMap = new HashMap<>();
  45. @Async
  46. public void sendMq() {
  47. checkCount();
  48. List<MqSendLog> mqSendLogs = mqSendLogService.getNoSendMsg();
  49. if(mqSendLogs.isEmpty()){
  50. return;
  51. }
  52. log.info("未分配的mq队列数:{}",mqSendLogs.size());
  53. List<MqQueueConfig> queueConfigList = queueConfigService.list();
  54. for (MqQueueConfig mqQueueConfig : queueConfigList) {
  55. List<MqSendLog> msgList = mqSendLogs.stream().filter(e -> e.getQueue().equals(mqQueueConfig.getQueueName())).collect(Collectors.toList());
  56. if(msgList.isEmpty()){
  57. continue;
  58. }
  59. MqMsg mqMsg = getRabbitMqMsg(mqQueueConfig.getQueueName());
  60. if(mqMsg == null){
  61. log.info("获取mq队列数据失败:{}",mqQueueConfig);
  62. continue;
  63. }
  64. if(mqQueueConfig.getOpenScaling() == 0){ //不开启弹性伸缩
  65. if(mqMsg.getMessages_ready() >0){ //待计算队列中有任务
  66. continue;
  67. }
  68. }
  69. if(mqQueueConfig.getOpenScaling() == 1){ //开启弹性伸缩
  70. checkOpenEcs(mqQueueConfig,msgList.size(),mqMsg.getMessages_ready());
  71. }
  72. sendRabbitMq(msgList,mqMsg.getConsumers() - mqMsg.getMessages_unacknowledged() - mqMsg.getMessages_ready());
  73. }
  74. }
  75. private void checkCount() {
  76. if(checkOpenCount > 10000){ //一个W为一个循环
  77. checkOpenCount = 0;
  78. }
  79. checkOpenCount ++;
  80. }
  81. private void sendRabbitMq(List<MqSendLog> msgList, Integer msgCount) {
  82. for (int i = 0;i < msgCount ;i++){
  83. if(i > msgList.size() -1){
  84. continue;
  85. }
  86. MqSendLog mqSendLog = msgList.get(i);
  87. mqSendLog.setStatus(1);
  88. mqSendLog.setUpdateTime(null);
  89. mqSendLogService.updateById(mqSendLog);
  90. rabbitMqProducer.sendByWorkQueue(mqSendLog.getQueue(),mqSendLog.getContent());
  91. }
  92. }
  93. private void checkOpenEcs(MqQueueConfig mqQueueConfig,Integer msgCount,Integer readCount) {
  94. List<MqEcs> list = mqEcsService.getNoModelingByQueueName(mqQueueConfig.getQueueName());
  95. LinkedBlockingQueue<DelEcsVo> openList = openMap.get(mqQueueConfig.getQueueName());
  96. if(openList == null){
  97. openList = new LinkedBlockingQueue<>();
  98. openMap.put(mqQueueConfig.getQueueName(),openList);
  99. }
  100. if(msgCount + readCount > mqQueueConfig.getScalingThreshold() + list.size() + openList.size() && mqQueueConfig.getOpenScalingTime() % checkOpenCount == 0){
  101. MqScalingConfig mqScalingConfig = mqScalingConfigService.getById(mqQueueConfig.getScalingConfigId());
  102. DelEcsVo vo = new DelEcsVo(null,mqScalingConfig,mqQueueConfig.getQueueName(),new Date());
  103. openList.offer(vo);
  104. }
  105. }
  106. @Async
  107. public void openEcsList() {
  108. try {
  109. HashMap<String, MqQueueConfig> queueMap = queueConfigService.getQueueMap();
  110. for (String key : openMap.keySet()) {
  111. LinkedBlockingQueue<DelEcsVo> openList = openMap.get(key);
  112. if(openList.isEmpty()){
  113. return;
  114. }
  115. DelEcsVo take = openList.poll();
  116. List<MqSendLog> msgList = mqSendLogService.getNoSendMsgByQueueName(key);
  117. MqQueueConfig mqQueueConfig = queueMap.get(key);
  118. if(msgList.size() <= mqQueueConfig.getScalingThreshold()){
  119. log.info("openEcsList--待计算任务为:{}未超过阈值:{}无需开启弹性伸缩:{}",msgList.size(),mqQueueConfig.getScalingThreshold() ,key);
  120. return;
  121. }
  122. log.info("openEcsList--开启弹性伸缩数量:{},{}",key,openList.size());
  123. Boolean flag = createEcs( take.getMqScalingConfig());
  124. if(flag){
  125. mqEcsService.add(take.getQueueName());
  126. Thread.sleep(1000L * 5);
  127. }else {
  128. openList.offer(take);
  129. }
  130. }
  131. }catch (Exception e){
  132. log.info("openEcsList--开启弹性伸缩失败:",e);
  133. }
  134. }
  135. public void checkDelEcs() {
  136. List<MqEcs> mqEcsList = mqEcsService.getScalingNotStopList();
  137. if(mqEcsList.isEmpty()){
  138. return;
  139. }
  140. log.info("启动中的弹性伸缩数量为:{}",mqEcsList.size());
  141. HashMap<String,MqQueueConfig> queueMap = queueConfigService.getQueueMap();
  142. HashMap<Integer,MqScalingConfig> scalingMap = mqScalingConfigService.getIdMap();
  143. for (MqEcs mqEcs : mqEcsList) {
  144. if(StringUtils.isBlank(mqEcs.getEcsName()) || StringUtils.isBlank(mqEcs.getQueueName())){
  145. continue;
  146. }
  147. MqQueueConfig mqQueueConfig = queueMap.get(mqEcs.getQueueName());
  148. if(mqQueueConfig == null || mqQueueConfig.getScalingConfigId() == null){
  149. continue;
  150. }
  151. Long between = DateUtil.between(mqEcs.getCreateTime(), new Date(), DateUnit.MINUTE);
  152. //弹性伸缩按照一个小时计费
  153. Long count = between/60;
  154. boolean flag = delList.stream().anyMatch(e -> e.getMqEcs().getEcsName().equals(mqEcs.getEcsName()));
  155. if(between >= mqQueueConfig.getStopScalingTime() + 60 * count && !flag){
  156. log.info("checkDelEcs-实例开启时间大于{}分钟,开始关闭:{}",mqQueueConfig.getStopScalingTime(),mqEcs.getEcsName());
  157. DelEcsVo vo = new DelEcsVo(mqEcs,scalingMap.get(mqQueueConfig.getScalingConfigId()),null,new Date());
  158. delList.offer(vo);
  159. }
  160. }
  161. }
  162. @Async
  163. public void delEcsList() {
  164. try {
  165. if(delList.isEmpty()){
  166. return;
  167. }
  168. log.info("delEcsList--关闭弹性伸缩数量:{}",delList.size());
  169. DelEcsVo take = delList.poll();
  170. if(take == null){
  171. return;
  172. }
  173. String stopKey = String.format(RedisKey.ecsStopKey,take.getMqEcs().getEcsName());
  174. redisUtil.set(stopKey,take.getMqEcs().getEcsName(),60 * 60 * 24); //设置计算暂停锁
  175. String modelingKey = String.format(RedisKey.modelingKey,take.getMqEcs().getEcsName());
  176. if(redisUtil.hasKey(modelingKey)){
  177. redisUtil.del(stopKey);
  178. return;
  179. }
  180. Boolean delFlag = delEcs(take.getMqScalingConfig(), take.getMqEcs().getEcsName());
  181. if(delFlag){
  182. log.info("checkDelEcs--关闭弹性伸缩实例成功:{}", take.getMqEcs().getEcsName());
  183. mqEcsService.updateMqEcs(take.getMqEcs());
  184. }
  185. }catch (Exception e){
  186. log.info("delEcsList--关闭弹性伸缩失败:",e);
  187. }
  188. }
  189. @Value("${spring.rabbitmq.host}")
  190. public String host;
  191. @Value("${spring.rabbitmq.username}")
  192. public String username;
  193. @Value("${spring.rabbitmq.password}")
  194. public String password;
  195. @Value("${spring.rabbitmq.virtual-host}")
  196. public String virtualHost;
  197. @Value("${spring.rabbitmq.mgmt-url}")
  198. public String mgmtUrl;
  199. @Value("${spring.rabbitmq.mgmt-host}")
  200. public String mgmtHost;
  201. public MqMsg getRabbitMqMsg(String queueName) {
  202. return RabbitMqUtils.getRabbitMqMsg(mgmtUrl+host+":"+mgmtHost,virtualHost,username,password,queueName);
  203. }
  204. public synchronized Boolean createEcs(MqScalingConfig mqScaling){
  205. try {
  206. return ECSUtils.createEcs(mqScaling.getAccessKey(),mqScaling.getSecret(),mqScaling.getEndpoint(),mqScaling.getScalingRuleAri());
  207. }catch (Exception e){
  208. log.info("触发弹性伸缩失败:",e);
  209. }
  210. return false;
  211. }
  212. public synchronized Boolean delEcs(MqScalingConfig mqScaling, String instanceId){
  213. try {
  214. return ECSUtils.delEcs(mqScaling.getAccessKey(),mqScaling.getSecret(),mqScaling.getEndpoint(),mqScaling.getScalingGroupId(),instanceId);
  215. }catch (Exception e){
  216. log.info("关闭弹性伸缩失败:",e);
  217. }
  218. return false;
  219. }
  220. }