From 24d098d86344e3708e6f0a86167badadfc30e454 Mon Sep 17 00:00:00 2001 From: tjq Date: Wed, 24 Jun 2020 20:01:53 +0800 Subject: [PATCH] [dev] add cluster info cleaner --- others/script/jenkins_auto_build.sh | 4 +-- .../powerjob/common/model/InstanceDetail.java | 2 +- .../service/ha/ClusterStatusHolder.java | 19 ++++++++++-- .../service/ha/WorkerManagerService.java | 6 ++-- .../server/service/timing/CleanService.java | 3 +- .../src/main/resources/static/js/1.js | 4 +-- .../src/main/resources/static/js/6.js | 2 +- .../src/main/resources/static/js/app.js | 2 +- .../worker/common/utils/LRUCache.java | 31 +++++++++++++------ .../tracker/task/FrequentTaskTracker.java | 6 ++-- 10 files changed, 52 insertions(+), 27 deletions(-) diff --git a/others/script/jenkins_auto_build.sh b/others/script/jenkins_auto_build.sh index 1d4e9958..c8a454ec 100755 --- a/others/script/jenkins_auto_build.sh +++ b/others/script/jenkins_auto_build.sh @@ -26,7 +26,7 @@ docker run -d \ --name powerjob-server \ -p 7700:7700 -p 10086:10086 -p 5001:5005 -p 10001:10000 \ -e JVMOPTIONS="-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=5005 -Dcom.sun.management.jmxremote -Dcom.sun.management.jmxremote.port=10000 -Dcom.sun.management.jmxremote.rmi.port=10000 -Dcom.sun.management.jmxremote.authenticate=false -Dcom.sun.management.jmxremote.ssl=false" \ - -e PARAMS="--spring.profiles.active=product --spring.datasource.core.jdbc-url=jdbc:mysql://124.70.67.79:3306/powerjob-product?useUnicode=true&characterEncoding=UTF-8 --spring.data.mongodb.uri=mongodb://124.70.67.79:27017/powerjob-product" \ + -e PARAMS="--spring.profiles.active=product --spring.datasource.core.jdbc-url=jdbc:mysql://139.224.83.134:3306/powerjob-product?useUnicode=true&characterEncoding=UTF-8 --spring.data.mongodb.uri=mongodb://139.224.83.134:27017/powerjob-product" \ -v ~/docker/powerjob-server:/root/powerjob-server -v ~/.m2:/root/.m2 \ tjqq/powerjob-server:latest sleep 60 @@ -45,8 +45,8 @@ docker run -d \ tjqq/powerjob-agent:latest docker run -d \ - --name powerjob-agent2 \ --restart=always \ + --name powerjob-agent2 \ -p 27778:27777 -p 5003:5005 -p 10003:10000 \ -e JVMOPTIONS="-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=5005 -Dcom.sun.management.jmxremote -Dcom.sun.management.jmxremote.port=10000 -Dcom.sun.management.jmxremote.rmi.port=10000 -Dcom.sun.management.jmxremote.authenticate=false -Dcom.sun.management.jmxremote.ssl=false" \ -e PARAMS="--app powerjob-agent-test --server $serverAddress" \ diff --git a/powerjob-common/src/main/java/com/github/kfcfans/powerjob/common/model/InstanceDetail.java b/powerjob-common/src/main/java/com/github/kfcfans/powerjob/common/model/InstanceDetail.java index 4f6a7fca..c9ab4eaa 100644 --- a/powerjob-common/src/main/java/com/github/kfcfans/powerjob/common/model/InstanceDetail.java +++ b/powerjob-common/src/main/java/com/github/kfcfans/powerjob/common/model/InstanceDetail.java @@ -43,7 +43,7 @@ public class InstanceDetail implements OmsSerializable { private String startTime; private String finishedTime; private String result; - private String status; + private int status; } // MapReduce 和 Broadcast 任务的 extra -> diff --git a/powerjob-server/src/main/java/com/github/kfcfans/powerjob/server/service/ha/ClusterStatusHolder.java b/powerjob-server/src/main/java/com/github/kfcfans/powerjob/server/service/ha/ClusterStatusHolder.java index 80eb67ad..abaabc61 100644 --- a/powerjob-server/src/main/java/com/github/kfcfans/powerjob/server/service/ha/ClusterStatusHolder.java +++ b/powerjob-server/src/main/java/com/github/kfcfans/powerjob/server/service/ha/ClusterStatusHolder.java @@ -30,7 +30,7 @@ public class ClusterStatusHolder { // 集群中所有机器的最后心跳时间 private Map address2ActiveTime; - private static final long WORKER_TIMEOUT_MS = 30000; + private static final long WORKER_TIMEOUT_MS = 60000; public ClusterStatusHolder(String appName) { this.appName = appName; @@ -131,10 +131,25 @@ public class ClusterStatusHolder { /** * 释放所有本地存储的容器信息(该操作会导致短暂的 listDeployedContainer 服务不可用) */ - public void releaseContainerInfos() { + public void release() { log.info("[ClusterStatusHolder-{}] clean the containerInfos, listDeployedContainer service may down about 1min~", appName); // 丢弃原来的所有数据,准备重建 containerId2Infos = Maps.newConcurrentMap(); + + // 丢弃超时机器的信息 + List timeoutAddress = Lists.newLinkedList(); + address2Metrics.forEach((addr, lastActiveTime) -> { + if (timeout(addr)) { + timeoutAddress.add(addr); + } + }); + if (!timeoutAddress.isEmpty()) { + log.info("[ClusterStatusHolder-{}] detective timeout workers({}), try to release their infos.", appName, timeoutAddress); + timeoutAddress.forEach(addr -> { + address2ActiveTime.remove(addr); + address2Metrics.remove(addr); + }); + } } private boolean timeout(String address) { diff --git a/powerjob-server/src/main/java/com/github/kfcfans/powerjob/server/service/ha/WorkerManagerService.java b/powerjob-server/src/main/java/com/github/kfcfans/powerjob/server/service/ha/WorkerManagerService.java index 0ab2bf02..02b667fc 100644 --- a/powerjob-server/src/main/java/com/github/kfcfans/powerjob/server/service/ha/WorkerManagerService.java +++ b/powerjob-server/src/main/java/com/github/kfcfans/powerjob/server/service/ha/WorkerManagerService.java @@ -94,9 +94,9 @@ public class WorkerManagerService { } /** - * 释放所有本地存储的容器信息(该操作会导致短暂的 listDeployedContainer 服务不可用) + * 清理缓存信息,防止 OOM */ - public static void releaseContainerInfos() { - appId2ClusterStatus.values().forEach(ClusterStatusHolder::releaseContainerInfos); + public static void cleanUp() { + appId2ClusterStatus.values().forEach(ClusterStatusHolder::release); } } diff --git a/powerjob-server/src/main/java/com/github/kfcfans/powerjob/server/service/timing/CleanService.java b/powerjob-server/src/main/java/com/github/kfcfans/powerjob/server/service/timing/CleanService.java index a74ca467..781dd5e5 100644 --- a/powerjob-server/src/main/java/com/github/kfcfans/powerjob/server/service/timing/CleanService.java +++ b/powerjob-server/src/main/java/com/github/kfcfans/powerjob/server/service/timing/CleanService.java @@ -5,7 +5,6 @@ import com.github.kfcfans.powerjob.server.persistence.core.repository.InstanceIn import com.github.kfcfans.powerjob.server.persistence.core.repository.WorkflowInstanceInfoRepository; import com.github.kfcfans.powerjob.server.persistence.mongodb.GridFsManager; import com.github.kfcfans.powerjob.server.service.ha.WorkerManagerService; -import com.github.kfcfans.powerjob.server.service.instance.InstanceManager; import com.google.common.annotations.VisibleForTesting; import com.google.common.base.Stopwatch; import lombok.extern.slf4j.Slf4j; @@ -59,7 +58,7 @@ public class CleanService { public void timingClean() { // 释放本地缓存 - WorkerManagerService.releaseContainerInfos(); + WorkerManagerService.cleanUp(); // 删除数据库运行记录 cleanInstanceLog(); diff --git a/powerjob-server/src/main/resources/static/js/1.js b/powerjob-server/src/main/resources/static/js/1.js index bc9688bc..48e4e67d 100644 --- a/powerjob-server/src/main/resources/static/js/1.js +++ b/powerjob-server/src/main/resources/static/js/1.js @@ -8,7 +8,7 @@ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; -eval("__webpack_require__.r(__webpack_exports__);\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n/* harmony default export */ __webpack_exports__[\"default\"] = ({\n name: \"InstanceDetail\",\n // 数据传递\n props: [\"instanceId\"],\n data: function data() {\n return {\n instanceDetail: {}\n };\n },\n methods: {\n fetchInstanceDetail: function fetchInstanceDetail() {\n var that = this;\n var url = \"/instance/detail?instanceId=\" + this.instanceId;\n this.axios.get(url).then(function (res) {\n return that.instanceDetail = res;\n });\n }\n },\n mounted: function mounted() {\n console.log(\"using InstanceId: \" + this.instanceId);\n this.fetchInstanceDetail();\n }\n});\n\n//# sourceURL=webpack:///./src/components/common/InstanceDetail.vue?./node_modules/cache-loader/dist/cjs.js??ref--12-0!./node_modules/babel-loader/lib!./node_modules/cache-loader/dist/cjs.js??ref--0-0!./node_modules/vue-loader/lib??vue-loader-options"); +eval("__webpack_require__.r(__webpack_exports__);\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n/* harmony default export */ __webpack_exports__[\"default\"] = ({\n name: \"InstanceDetail\",\n // 数据传递\n props: [\"instanceId\"],\n data: function data() {\n return {\n instanceDetail: {}\n };\n },\n methods: {\n fetchInstanceDetail: function fetchInstanceDetail() {\n var that = this;\n var url = \"/instance/detail?instanceId=\" + this.instanceId;\n this.axios.get(url).then(function (res) {\n return that.instanceDetail = res;\n });\n }\n },\n mounted: function mounted() {\n console.log(\"using InstanceId: \" + this.instanceId);\n this.fetchInstanceDetail();\n }\n});\n\n//# sourceURL=webpack:///./src/components/common/InstanceDetail.vue?./node_modules/cache-loader/dist/cjs.js??ref--12-0!./node_modules/babel-loader/lib!./node_modules/cache-loader/dist/cjs.js??ref--0-0!./node_modules/vue-loader/lib??vue-loader-options"); /***/ }), @@ -20,7 +20,7 @@ eval("__webpack_require__.r(__webpack_exports__);\n//\n//\n//\n//\n//\n//\n//\n/ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; -eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"render\", function() { return render; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"staticRenderFns\", function() { return staticRenderFns; });\nvar render = function() {\n var _vm = this\n var _h = _vm.$createElement\n var _c = _vm._self._c || _h\n return _c(\n \"div\",\n [\n _c(\n \"el-row\",\n [\n _c(\n \"el-col\",\n { attrs: { offset: 20 } },\n [\n _c(\n \"el-button\",\n {\n attrs: { type: \"primary\" },\n on: { click: _vm.fetchInstanceDetail }\n },\n [_vm._v(_vm._s(_vm.$t(\"message.refresh\")))]\n )\n ],\n 1\n )\n ],\n 1\n ),\n _c(\n \"el-row\",\n [\n _c(\"el-col\", { attrs: { span: 24 } }, [\n _vm._v(\" \" + _vm._s(_vm.$t(\"message.instanceId\")) + \": \"),\n _c(\"span\", { staticClass: \"title\" }, [\n _vm._v(_vm._s(_vm.instanceId))\n ])\n ])\n ],\n 1\n ),\n _c(\n \"el-row\",\n { staticStyle: { \"margin-top\": \"-20px\" } },\n [\n _c(\"el-col\", { attrs: { span: 8 } }, [\n _vm._v(\" \" + _vm._s(_vm.$t(\"message.status\")) + \": \"),\n _c(\"span\", { staticClass: \"title\" }, [\n _vm._v(\n _vm._s(\n this.common.translateInstanceStatus(_vm.instanceDetail.status)\n )\n )\n ])\n ]),\n _c(\"el-col\", { attrs: { span: 16 } }, [\n _vm._v(\" \" + _vm._s(_vm.$t(\"message.runningTimes\")) + \":\"),\n _c(\"span\", { staticClass: \"title\" }, [\n _vm._v(_vm._s(_vm.instanceDetail.runningTimes))\n ])\n ])\n ],\n 1\n ),\n _c(\n \"el-row\",\n [\n _c(\"el-col\", { attrs: { span: 24 } }, [\n _vm._v(\" \" + _vm._s(_vm.$t(\"message.taskTrackerAddress\")) + \": \"),\n _c(\"span\", { staticClass: \"title\" }, [\n _vm._v(\" \" + _vm._s(_vm.instanceDetail.taskTrackerAddress))\n ])\n ])\n ],\n 1\n ),\n _c(\n \"el-row\",\n [\n _c(\"el-col\", { attrs: { span: 8 } }, [\n _vm._v(\" \" + _vm._s(_vm.$t(\"message.startTime\")) + \": \"),\n _c(\"span\", { staticClass: \"title\" }, [\n _vm._v(\n \" \" +\n _vm._s(\n this.common.timestamp2Str(\n _vm.instanceDetail.actualTriggerTime\n )\n )\n )\n ])\n ]),\n _c(\"el-col\", { attrs: { span: 8 } }, [\n _vm._v(\" \" + _vm._s(_vm.$t(\"message.finishedTime\")) + \": \"),\n _c(\"span\", { staticClass: \"title\" }, [\n _vm._v(\n _vm._s(\n this.common.timestamp2Str(_vm.instanceDetail.finishedTime)\n )\n )\n ])\n ])\n ],\n 1\n ),\n _c(\n \"el-row\",\n [\n _c(\"el-col\", { attrs: { span: 24 } }, [\n _vm._v(\" \" + _vm._s(_vm.$t(\"message.result\")) + \": \"),\n _c(\"span\", { staticClass: \"title\" }, [\n _vm._v(\" \" + _vm._s(_vm.instanceDetail.result))\n ])\n ])\n ],\n 1\n ),\n _c(\"el-row\", { attrs: { id: \"taskDetail\" } }, [\n _vm._v(\" \" + _vm._s(_vm.$t(\"message.subTaskInfo\")) + \": \"),\n _c(\"span\", { staticClass: \"title\" }, [\n _vm._v(_vm._s(_vm.instanceDetail.taskDetail))\n ])\n ]),\n _c(\n \"el-row\",\n [\n _c(\"span\", { staticClass: \"title\" }, [\n _vm._v(_vm._s(_vm.$t(\"message.secondlyJobHistory\")) + \":\")\n ]),\n _c(\n \"el-table\",\n {\n staticStyle: { width: \"100%\" },\n attrs: { data: _vm.instanceDetail.subInstanceDetails }\n },\n [\n _c(\"el-table-column\", {\n attrs: {\n prop: \"subInstanceId\",\n label: _vm.$t(\"message.subInstanceId\"),\n width: \"120\"\n }\n }),\n _c(\"el-table-column\", {\n attrs: {\n prop: \"startTime\",\n label: _vm.$t(\"message.startTime\"),\n width: \"160\"\n }\n }),\n _c(\"el-table-column\", {\n attrs: {\n prop: \"finishedTime\",\n label: _vm.$t(\"message.finishedTime\"),\n width: \"160\"\n }\n }),\n _c(\"el-table-column\", {\n attrs: {\n prop: \"status\",\n label: _vm.$t(\"message.status\"),\n width: \"160\"\n }\n }),\n _c(\"el-table-column\", {\n attrs: { prop: \"result\", label: _vm.$t(\"message.result\") }\n })\n ],\n 1\n )\n ],\n 1\n )\n ],\n 1\n )\n}\nvar staticRenderFns = []\nrender._withStripped = true\n\n\n\n//# sourceURL=webpack:///./src/components/common/InstanceDetail.vue?./node_modules/cache-loader/dist/cjs.js?%7B%22cacheDirectory%22:%22node_modules/.cache/vue-loader%22,%22cacheIdentifier%22:%2241f1f4da-vue-loader-template%22%7D!./node_modules/vue-loader/lib/loaders/templateLoader.js??vue-loader-options!./node_modules/cache-loader/dist/cjs.js??ref--0-0!./node_modules/vue-loader/lib??vue-loader-options"); +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"render\", function() { return render; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"staticRenderFns\", function() { return staticRenderFns; });\nvar render = function() {\n var _vm = this\n var _h = _vm.$createElement\n var _c = _vm._self._c || _h\n return _c(\n \"div\",\n [\n _c(\n \"el-row\",\n [\n _c(\n \"el-col\",\n { attrs: { offset: 20 } },\n [\n _c(\n \"el-button\",\n {\n attrs: { type: \"primary\" },\n on: { click: _vm.fetchInstanceDetail }\n },\n [_vm._v(_vm._s(_vm.$t(\"message.refresh\")))]\n )\n ],\n 1\n )\n ],\n 1\n ),\n _c(\n \"el-row\",\n [\n _c(\"el-col\", { attrs: { span: 24 } }, [\n _vm._v(\" \" + _vm._s(_vm.$t(\"message.instanceId\")) + \": \"),\n _c(\"span\", { staticClass: \"title\" }, [\n _vm._v(_vm._s(_vm.instanceId))\n ])\n ])\n ],\n 1\n ),\n _c(\n \"el-row\",\n { staticStyle: { \"margin-top\": \"-20px\" } },\n [\n _c(\"el-col\", { attrs: { span: 8 } }, [\n _vm._v(\" \" + _vm._s(_vm.$t(\"message.status\")) + \": \"),\n _c(\"span\", { staticClass: \"title\" }, [\n _vm._v(\n _vm._s(\n this.common.translateInstanceStatus(_vm.instanceDetail.status)\n )\n )\n ])\n ]),\n _c(\"el-col\", { attrs: { span: 16 } }, [\n _vm._v(\" \" + _vm._s(_vm.$t(\"message.runningTimes\")) + \":\"),\n _c(\"span\", { staticClass: \"title\" }, [\n _vm._v(_vm._s(_vm.instanceDetail.runningTimes))\n ])\n ])\n ],\n 1\n ),\n _c(\n \"el-row\",\n [\n _c(\"el-col\", { attrs: { span: 24 } }, [\n _vm._v(\" \" + _vm._s(_vm.$t(\"message.taskTrackerAddress\")) + \": \"),\n _c(\"span\", { staticClass: \"title\" }, [\n _vm._v(\" \" + _vm._s(_vm.instanceDetail.taskTrackerAddress))\n ])\n ])\n ],\n 1\n ),\n _c(\n \"el-row\",\n [\n _c(\"el-col\", { attrs: { span: 8 } }, [\n _vm._v(\" \" + _vm._s(_vm.$t(\"message.startTime\")) + \": \"),\n _c(\"span\", { staticClass: \"title\" }, [\n _vm._v(\n \" \" +\n _vm._s(\n this.common.timestamp2Str(\n _vm.instanceDetail.actualTriggerTime\n )\n )\n )\n ])\n ]),\n _c(\"el-col\", { attrs: { span: 8 } }, [\n _vm._v(\" \" + _vm._s(_vm.$t(\"message.finishedTime\")) + \": \"),\n _c(\"span\", { staticClass: \"title\" }, [\n _vm._v(\n _vm._s(\n this.common.timestamp2Str(_vm.instanceDetail.finishedTime)\n )\n )\n ])\n ])\n ],\n 1\n ),\n _c(\n \"el-row\",\n [\n _c(\"el-col\", { attrs: { span: 24 } }, [\n _vm._v(\" \" + _vm._s(_vm.$t(\"message.result\")) + \": \"),\n _c(\"span\", { staticClass: \"title\" }, [\n _vm._v(\" \" + _vm._s(_vm.instanceDetail.result))\n ])\n ])\n ],\n 1\n ),\n _c(\"el-row\", { attrs: { id: \"taskDetail\" } }, [\n _vm._v(\" \" + _vm._s(_vm.$t(\"message.subTaskInfo\")) + \": \"),\n _c(\"span\", { staticClass: \"title\" }, [\n _vm._v(_vm._s(_vm.instanceDetail.taskDetail))\n ])\n ]),\n _c(\n \"el-row\",\n [\n _c(\"span\", { staticClass: \"title\" }, [\n _vm._v(_vm._s(_vm.$t(\"message.secondlyJobHistory\")) + \":\")\n ]),\n _c(\n \"el-table\",\n {\n staticStyle: { width: \"100%\" },\n attrs: { data: _vm.instanceDetail.subInstanceDetails }\n },\n [\n _c(\"el-table-column\", {\n attrs: {\n prop: \"subInstanceId\",\n label: _vm.$t(\"message.subInstanceId\"),\n width: \"120\"\n }\n }),\n _c(\"el-table-column\", {\n attrs: {\n prop: \"startTime\",\n label: _vm.$t(\"message.startTime\"),\n width: \"160\"\n }\n }),\n _c(\"el-table-column\", {\n attrs: {\n prop: \"finishedTime\",\n label: _vm.$t(\"message.finishedTime\"),\n width: \"160\"\n }\n }),\n _c(\"el-table-column\", {\n attrs: { label: _vm.$t(\"message.status\"), width: \"160\" },\n scopedSlots: _vm._u([\n {\n key: \"default\",\n fn: function(scope) {\n return [\n _vm._v(\n \" \" +\n _vm._s(\n _vm.common.translateInstanceStatus(\n scope.row.status\n )\n ) +\n \" \"\n )\n ]\n }\n }\n ])\n }),\n _c(\"el-table-column\", {\n attrs: { prop: \"result\", label: _vm.$t(\"message.result\") }\n })\n ],\n 1\n )\n ],\n 1\n )\n ],\n 1\n )\n}\nvar staticRenderFns = []\nrender._withStripped = true\n\n\n\n//# sourceURL=webpack:///./src/components/common/InstanceDetail.vue?./node_modules/cache-loader/dist/cjs.js?%7B%22cacheDirectory%22:%22node_modules/.cache/vue-loader%22,%22cacheIdentifier%22:%2241f1f4da-vue-loader-template%22%7D!./node_modules/vue-loader/lib/loaders/templateLoader.js??vue-loader-options!./node_modules/cache-loader/dist/cjs.js??ref--0-0!./node_modules/vue-loader/lib??vue-loader-options"); /***/ }), diff --git a/powerjob-server/src/main/resources/static/js/6.js b/powerjob-server/src/main/resources/static/js/6.js index 072f1767..4b82c425 100644 --- a/powerjob-server/src/main/resources/static/js/6.js +++ b/powerjob-server/src/main/resources/static/js/6.js @@ -8,7 +8,7 @@ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; -eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var core_js_modules_es_array_for_each__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! core-js/modules/es.array.for-each */ \"./node_modules/core-js/modules/es.array.for-each.js\");\n/* harmony import */ var core_js_modules_es_array_for_each__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(core_js_modules_es_array_for_each__WEBPACK_IMPORTED_MODULE_0__);\n/* harmony import */ var core_js_modules_web_dom_collections_for_each__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! core-js/modules/web.dom-collections.for-each */ \"./node_modules/core-js/modules/web.dom-collections.for-each.js\");\n/* harmony import */ var core_js_modules_web_dom_collections_for_each__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(core_js_modules_web_dom_collections_for_each__WEBPACK_IMPORTED_MODULE_1__);\n\n\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n/* harmony default export */ __webpack_exports__[\"default\"] = ({\n name: \"Welcome\",\n data: function data() {\n return {\n // 应用注册表单是否可见\n appRegisterFormVisible: false,\n // 用户注册表单是否可见\n userRegisterFormVisible: false,\n // 应用注册表单对象\n appRegisterForm: {\n appName: \"\",\n password: \"\"\n },\n // 用户注册表单对象\n userRegisterForm: {\n username: \"\",\n phone: \"\",\n email: \"\"\n },\n // 控制台登陆对象\n appLoginForm: {\n appName: undefined,\n password: undefined\n },\n // 是否保持登录状态\n stayLogged: true\n };\n },\n methods: {\n // 请求应用下拉框数据\n queryAppNames: function queryAppNames(queryString, cb) {\n var array = [];\n var that = this;\n var url = \"/appInfo/list?condition=\" + queryString;\n this.axios.get(url).then(function (result) {\n result.forEach(function (appInfo) {\n array.push({\n \"value\": appInfo.appName\n });\n cb(array);\n });\n }, function (error) {\n return that.$message.error(error);\n });\n clearTimeout(this.timeout);\n this.timeout = setTimeout(function () {\n cb(array);\n }, 3000);\n },\n // 注册应用\n registerApp: function registerApp() {\n var _this = this;\n\n var that = this;\n this.axios.post(\"/appInfo/save\", this.appRegisterForm).then(function () {\n that.$message.success(_this.$t('message.success'));\n that.appRegisterFormVisible = false;\n }, that.appRegisterFormVisible = false);\n },\n // 注册用户(仅用于报警通知)\n registerUser: function registerUser() {\n var _this2 = this;\n\n var that = this;\n this.axios.post(\"/user/save\", this.userRegisterForm).then(function () {\n that.$message.success(_this2.$t('message.success'));\n that.userRegisterFormVisible = false;\n }, that.userRegisterFormVisible = false);\n },\n // 登陆控制台\n login: function login() {\n var _this3 = this;\n\n var that = this;\n this.axios.post(\"/appInfo/assert\", this.appLoginForm).then(function (res) {\n // 勾选了保持登录状态,就开启自动登录,直接本地存用户名密码(内部系统浏览器明文存问题不大)\n if (_this3.stayLogged) {\n window.localStorage.setItem('oms_auto_login', JSON.stringify(_this3.appLoginForm));\n }\n\n var appInfo = {\n id: res,\n appName: that.appLoginForm.appName\n }; // 将 appId 存储到 VueStore\n\n _this3.$store.commit(\"initAppInfo\", appInfo); // 跳转到主界面\n\n\n _this3.$router.push(\"/oms/home\");\n }, function (error) {\n window.localStorage.removeItem('oms_auto_login');\n that.$message.error(error);\n });\n },\n // 自动登录\n autoLogin: function autoLogin() {\n var autoLoginString = window.localStorage.getItem(\"oms_auto_login\");\n\n if (autoLoginString === undefined || autoLoginString === null) {\n return;\n }\n\n this.appLoginForm = JSON.parse(autoLoginString);\n this.login();\n }\n },\n mounted: function mounted() {\n // 加载默认语言配置文件\n var localLang = window.localStorage.getItem('oms_lang');\n console.log(\"language from localStorage is %o\", localLang);\n\n if (localLang != null) {\n this.$i18n.locale = localLang;\n } else {\n var lang = navigator.language;\n console.log(\"language from system is %o\", lang);\n\n switch (lang) {\n case \"zh-HK\":\n case \"zh-TW\":\n case \"zh-SG\":\n case \"zh-CN\":\n this.$i18n.locale = \"cn\";\n break;\n\n default:\n this.$i18n.locale = \"en\";\n }\n } // 自动登录\n\n\n this.autoLogin();\n }\n});\n\n//# sourceURL=webpack:///./src/components/Welcome.vue?./node_modules/cache-loader/dist/cjs.js??ref--12-0!./node_modules/babel-loader/lib!./node_modules/cache-loader/dist/cjs.js??ref--0-0!./node_modules/vue-loader/lib??vue-loader-options"); +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var core_js_modules_es_array_for_each__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! core-js/modules/es.array.for-each */ \"./node_modules/core-js/modules/es.array.for-each.js\");\n/* harmony import */ var core_js_modules_es_array_for_each__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(core_js_modules_es_array_for_each__WEBPACK_IMPORTED_MODULE_0__);\n/* harmony import */ var core_js_modules_web_dom_collections_for_each__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! core-js/modules/web.dom-collections.for-each */ \"./node_modules/core-js/modules/web.dom-collections.for-each.js\");\n/* harmony import */ var core_js_modules_web_dom_collections_for_each__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(core_js_modules_web_dom_collections_for_each__WEBPACK_IMPORTED_MODULE_1__);\n\n\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n/* harmony default export */ __webpack_exports__[\"default\"] = ({\n name: \"Welcome\",\n data: function data() {\n return {\n // 应用注册表单是否可见\n appRegisterFormVisible: false,\n // 用户注册表单是否可见\n userRegisterFormVisible: false,\n // 应用注册表单对象\n appRegisterForm: {\n appName: \"\",\n password: undefined\n },\n // 用户注册表单对象\n userRegisterForm: {\n username: \"\",\n phone: \"\",\n email: \"\"\n },\n // 控制台登陆对象\n appLoginForm: {\n appName: undefined,\n password: undefined\n },\n // 是否保持登录状态\n stayLogged: true\n };\n },\n methods: {\n // 请求应用下拉框数据\n queryAppNames: function queryAppNames(queryString, cb) {\n var array = [];\n var that = this;\n var url = \"/appInfo/list?condition=\" + queryString;\n this.axios.get(url).then(function (result) {\n result.forEach(function (appInfo) {\n array.push({\n \"value\": appInfo.appName\n });\n cb(array);\n });\n }, function (error) {\n return that.$message.error(error);\n });\n clearTimeout(this.timeout);\n this.timeout = setTimeout(function () {\n cb(array);\n }, 3000);\n },\n // 注册应用\n registerApp: function registerApp() {\n var _this = this;\n\n var that = this;\n this.axios.post(\"/appInfo/save\", this.appRegisterForm).then(function () {\n that.$message.success(_this.$t('message.success'));\n that.appRegisterFormVisible = false;\n }, that.appRegisterFormVisible = false);\n },\n // 注册用户(仅用于报警通知)\n registerUser: function registerUser() {\n var _this2 = this;\n\n var that = this;\n this.axios.post(\"/user/save\", this.userRegisterForm).then(function () {\n that.$message.success(_this2.$t('message.success'));\n that.userRegisterFormVisible = false;\n }, that.userRegisterFormVisible = false);\n },\n // 登陆控制台\n login: function login() {\n var _this3 = this;\n\n var that = this;\n this.axios.post(\"/appInfo/assert\", this.appLoginForm).then(function (res) {\n // 勾选了保持登录状态,就开启自动登录,直接本地存用户名密码(内部系统浏览器明文存问题不大)\n if (_this3.stayLogged) {\n window.localStorage.setItem('oms_auto_login', JSON.stringify(_this3.appLoginForm));\n }\n\n var appInfo = {\n id: res,\n appName: that.appLoginForm.appName\n }; // 将 appId 存储到 VueStore\n\n _this3.$store.commit(\"initAppInfo\", appInfo); // 跳转到主界面\n\n\n _this3.$router.push(\"/oms/home\");\n }, function (error) {\n window.localStorage.removeItem('oms_auto_login');\n that.$message.error(error);\n });\n },\n // 自动登录\n autoLogin: function autoLogin() {\n var autoLoginString = window.localStorage.getItem(\"oms_auto_login\");\n\n if (autoLoginString === undefined || autoLoginString === null) {\n return;\n }\n\n this.appLoginForm = JSON.parse(autoLoginString);\n this.login();\n }\n },\n mounted: function mounted() {\n // 加载默认语言配置文件\n var localLang = window.localStorage.getItem('oms_lang');\n console.log(\"language from localStorage is %o\", localLang);\n\n if (localLang != null) {\n this.$i18n.locale = localLang;\n } else {\n var lang = navigator.language;\n console.log(\"language from system is %o\", lang);\n\n switch (lang) {\n case \"zh-HK\":\n case \"zh-TW\":\n case \"zh-SG\":\n case \"zh-CN\":\n this.$i18n.locale = \"cn\";\n break;\n\n default:\n this.$i18n.locale = \"en\";\n }\n } // 自动登录\n\n\n this.autoLogin();\n }\n});\n\n//# sourceURL=webpack:///./src/components/Welcome.vue?./node_modules/cache-loader/dist/cjs.js??ref--12-0!./node_modules/babel-loader/lib!./node_modules/cache-loader/dist/cjs.js??ref--0-0!./node_modules/vue-loader/lib??vue-loader-options"); /***/ }), diff --git a/powerjob-server/src/main/resources/static/js/app.js b/powerjob-server/src/main/resources/static/js/app.js index 53786159..dfb6909a 100644 --- a/powerjob-server/src/main/resources/static/js/app.js +++ b/powerjob-server/src/main/resources/static/js/app.js @@ -497,7 +497,7 @@ eval("module.exports = __webpack_require__.p + \"img/powerjob-console-logo.ac01c /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; -eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _i18n_i18n__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./i18n/i18n */ \"./src/i18n/i18n.js\");\n\n\nvar timestamp2Str = function timestamp2Str(ts) {\n if (ts < 10000) {\n return \"N/A\";\n }\n\n try {\n if (ts) {\n var time = new Date(ts);\n var y = time.getFullYear();\n var M = time.getMonth() + 1;\n var d = time.getDate();\n var h = time.getHours();\n var m = time.getMinutes();\n var s = time.getSeconds();\n return y + '-' + addZero(M) + '-' + addZero(d) + ' ' + addZero(h) + ':' + addZero(m) + ':' + addZero(s);\n } else {\n return '';\n }\n } catch (e) {\n return \"N/A\";\n }\n}; // 公共函数,涉及到 i18n ,放进 common.js 报错,暂时先放在这里吧\n\n\nvar translateInstanceStatus = function translateInstanceStatus(status) {\n console.log(\"zzzzzzzz %o\", status);\n\n switch (status) {\n case 1:\n return _i18n_i18n__WEBPACK_IMPORTED_MODULE_0__[\"default\"].t('message.waitingDispatch');\n\n case 2:\n return _i18n_i18n__WEBPACK_IMPORTED_MODULE_0__[\"default\"].t('message.waitingWorkerReceive');\n\n case 3:\n return _i18n_i18n__WEBPACK_IMPORTED_MODULE_0__[\"default\"].t('message.running');\n\n case 4:\n return _i18n_i18n__WEBPACK_IMPORTED_MODULE_0__[\"default\"].t('message.failed');\n\n case 5:\n return _i18n_i18n__WEBPACK_IMPORTED_MODULE_0__[\"default\"].t('message.success');\n\n case 10:\n return _i18n_i18n__WEBPACK_IMPORTED_MODULE_0__[\"default\"].t('message.stopped');\n\n default:\n return \"unknown\";\n }\n};\n\nvar translateWfInstanceStatus = function translateWfInstanceStatus(status) {\n switch (status) {\n case 1:\n return _i18n_i18n__WEBPACK_IMPORTED_MODULE_0__[\"default\"].t('message.wfWaiting');\n\n case 2:\n return _i18n_i18n__WEBPACK_IMPORTED_MODULE_0__[\"default\"].t('message.running');\n\n case 3:\n return _i18n_i18n__WEBPACK_IMPORTED_MODULE_0__[\"default\"].t('message.failed');\n\n case 4:\n return _i18n_i18n__WEBPACK_IMPORTED_MODULE_0__[\"default\"].t('message.success');\n\n case 10:\n return _i18n_i18n__WEBPACK_IMPORTED_MODULE_0__[\"default\"].t('message.stopped');\n\n default:\n return \"unknown\";\n }\n}; // 更换语言\n\n\nvar switchLanguage = function switchLanguage(cmd) {\n console.log(\"switch language to %o\", cmd);\n _i18n_i18n__WEBPACK_IMPORTED_MODULE_0__[\"default\"].locale = cmd; // 存储到LangStorage\n\n window.localStorage.setItem('oms_lang', cmd);\n};\n\nfunction addZero(m) {\n return m < 10 ? '0' + m : m;\n}\n\n/* harmony default export */ __webpack_exports__[\"default\"] = ({\n timestamp2Str: timestamp2Str,\n translateInstanceStatus: translateInstanceStatus,\n translateWfInstanceStatus: translateWfInstanceStatus,\n switchLanguage: switchLanguage\n});\n\n//# sourceURL=webpack:///./src/common.js?"); +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _i18n_i18n__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./i18n/i18n */ \"./src/i18n/i18n.js\");\n\n\nvar timestamp2Str = function timestamp2Str(ts) {\n if (ts < 10000) {\n return \"N/A\";\n }\n\n try {\n if (ts) {\n var time = new Date(ts);\n var y = time.getFullYear();\n var M = time.getMonth() + 1;\n var d = time.getDate();\n var h = time.getHours();\n var m = time.getMinutes();\n var s = time.getSeconds();\n return y + '-' + addZero(M) + '-' + addZero(d) + ' ' + addZero(h) + ':' + addZero(m) + ':' + addZero(s);\n } else {\n return '';\n }\n } catch (e) {\n return \"N/A\";\n }\n}; // 公共函数,涉及到 i18n ,放进 common.js 报错,暂时先放在这里吧\n\n\nvar translateInstanceStatus = function translateInstanceStatus(status) {\n switch (status) {\n case 1:\n return _i18n_i18n__WEBPACK_IMPORTED_MODULE_0__[\"default\"].t('message.waitingDispatch');\n\n case 2:\n return _i18n_i18n__WEBPACK_IMPORTED_MODULE_0__[\"default\"].t('message.waitingWorkerReceive');\n\n case 3:\n return _i18n_i18n__WEBPACK_IMPORTED_MODULE_0__[\"default\"].t('message.running');\n\n case 4:\n return _i18n_i18n__WEBPACK_IMPORTED_MODULE_0__[\"default\"].t('message.failed');\n\n case 5:\n return _i18n_i18n__WEBPACK_IMPORTED_MODULE_0__[\"default\"].t('message.success');\n\n case 10:\n return _i18n_i18n__WEBPACK_IMPORTED_MODULE_0__[\"default\"].t('message.stopped');\n\n default:\n return \"unknown\";\n }\n};\n\nvar translateWfInstanceStatus = function translateWfInstanceStatus(status) {\n switch (status) {\n case 1:\n return _i18n_i18n__WEBPACK_IMPORTED_MODULE_0__[\"default\"].t('message.wfWaiting');\n\n case 2:\n return _i18n_i18n__WEBPACK_IMPORTED_MODULE_0__[\"default\"].t('message.running');\n\n case 3:\n return _i18n_i18n__WEBPACK_IMPORTED_MODULE_0__[\"default\"].t('message.failed');\n\n case 4:\n return _i18n_i18n__WEBPACK_IMPORTED_MODULE_0__[\"default\"].t('message.success');\n\n case 10:\n return _i18n_i18n__WEBPACK_IMPORTED_MODULE_0__[\"default\"].t('message.stopped');\n\n default:\n return \"unknown\";\n }\n}; // 更换语言\n\n\nvar switchLanguage = function switchLanguage(cmd) {\n console.log(\"switch language to %o\", cmd);\n _i18n_i18n__WEBPACK_IMPORTED_MODULE_0__[\"default\"].locale = cmd; // 存储到LangStorage\n\n window.localStorage.setItem('oms_lang', cmd);\n};\n\nfunction addZero(m) {\n return m < 10 ? '0' + m : m;\n}\n\n/* harmony default export */ __webpack_exports__[\"default\"] = ({\n timestamp2Str: timestamp2Str,\n translateInstanceStatus: translateInstanceStatus,\n translateWfInstanceStatus: translateWfInstanceStatus,\n switchLanguage: switchLanguage\n});\n\n//# sourceURL=webpack:///./src/common.js?"); /***/ }), diff --git a/powerjob-worker/src/main/java/com/github/kfcfans/powerjob/worker/common/utils/LRUCache.java b/powerjob-worker/src/main/java/com/github/kfcfans/powerjob/worker/common/utils/LRUCache.java index 059fe7b3..429af541 100644 --- a/powerjob-worker/src/main/java/com/github/kfcfans/powerjob/worker/common/utils/LRUCache.java +++ b/powerjob-worker/src/main/java/com/github/kfcfans/powerjob/worker/common/utils/LRUCache.java @@ -1,26 +1,37 @@ package com.github.kfcfans.powerjob.worker.common.utils; -import java.util.LinkedHashMap; -import java.util.Map; +import com.google.common.cache.Cache; +import com.google.common.cache.CacheBuilder; + +import java.util.function.BiConsumer; /** * LRU(Least Recently Used) 缓存 + * before v3.1.1 使用 LinkedHashMap,但存在修改时访问报错问题,改用 Guava * * @author tjq * @since 2020/4/8 */ -public class LRUCache extends LinkedHashMap { +public class LRUCache { - private final int cacheSize; + private final Cache innerCache; public LRUCache(int cacheSize) { - super((int) Math.ceil(cacheSize / 0.75) + 1, 0.75f, false); - this.cacheSize = cacheSize; + innerCache = CacheBuilder.newBuilder() + .concurrencyLevel(2) + .initialCapacity(cacheSize) + .build(); } - @Override - protected boolean removeEldestEntry(Map.Entry eldest) { - // 超过阈值时返回true,进行LRU淘汰 - return size() > cacheSize; + public void forEach(BiConsumer action) { + innerCache.asMap().forEach(action); + } + + public V get(K key) { + return innerCache.getIfPresent(key); + } + + public void put(K key, V value) { + innerCache.put(key, value); } } diff --git a/powerjob-worker/src/main/java/com/github/kfcfans/powerjob/worker/core/tracker/task/FrequentTaskTracker.java b/powerjob-worker/src/main/java/com/github/kfcfans/powerjob/worker/core/tracker/task/FrequentTaskTracker.java index 2d60b3ce..c73f25bb 100644 --- a/powerjob-worker/src/main/java/com/github/kfcfans/powerjob/worker/core/tracker/task/FrequentTaskTracker.java +++ b/powerjob-worker/src/main/java/com/github/kfcfans/powerjob/worker/core/tracker/task/FrequentTaskTracker.java @@ -114,7 +114,7 @@ public class FrequentTaskTracker extends TaskTracker { InstanceDetail.SubInstanceDetail subDetail = new InstanceDetail.SubInstanceDetail(); BeanUtils.copyProperties(subInstanceInfo, subDetail); InstanceStatus status = InstanceStatus.of(subInstanceInfo.status); - subDetail.setStatus(status.getDes()); + subDetail.setStatus(status.getV()); subDetail.setSubInstanceId(subId); // 设置时间 @@ -347,8 +347,8 @@ public class FrequentTaskTracker extends TaskTracker { subInstanceId2TimeHolder.remove(subInstanceId); // 更新缓存数据 - if (recentSubInstanceInfo.containsKey(subInstanceId)) { - SubInstanceInfo subInstanceInfo = recentSubInstanceInfo.get(subInstanceId); + SubInstanceInfo subInstanceInfo = recentSubInstanceInfo.get(subInstanceId); + if (subInstanceInfo != null) { subInstanceInfo.status = success ? InstanceStatus.SUCCEED.getV() : InstanceStatus.FAILED.getV(); subInstanceInfo.result = result; subInstanceInfo.finishedTime = System.currentTimeMillis();