Ver código fonte

canal instance manager module finished

mcy 6 anos atrás
pai
commit
f3daceea6b

+ 12 - 2
canal-admin/canal-admin-server/src/main/java/com/alibaba/otter/canal/admin/controller/CanalInstanceController.java

@@ -22,7 +22,7 @@ public class CanalInstanceController {
         return BaseModel.getInstance(canalInstanceConfigService.findList(canalInstanceConfig));
     }
 
-    @PostMapping(value = "/iInstance")
+    @PostMapping(value = "/instance")
     public BaseModel<String> save(@RequestBody CanalInstanceConfig canalInstanceConfig, @PathVariable String env) {
         canalInstanceConfigService.save(canalInstanceConfig);
         return BaseModel.getInstance("success");
@@ -34,7 +34,7 @@ public class CanalInstanceController {
     }
 
     @PutMapping(value = "/instance")
-    public BaseModel<String> update(@RequestBody  CanalInstanceConfig canalInstanceConfig, @PathVariable String env) {
+    public BaseModel<String> update(@RequestBody CanalInstanceConfig canalInstanceConfig, @PathVariable String env) {
         canalInstanceConfigService.updateContent(canalInstanceConfig);
         return BaseModel.getInstance("success");
     }
@@ -44,4 +44,14 @@ public class CanalInstanceController {
         canalInstanceConfigService.delete(id);
         return BaseModel.getInstance("success");
     }
+
+    @PutMapping(value = "/instance/start/{id}")
+    public BaseModel<Boolean> start(@PathVariable Long id, @PathVariable String env) {
+        return BaseModel.getInstance(canalInstanceConfigService.remoteOperation(id, null, "start"));
+    }
+
+    @PutMapping(value = "/instance/stop/{id}/{nodeId}")
+    public BaseModel<Boolean> stop(@PathVariable Long id, @PathVariable Long nodeId, @PathVariable String env) {
+        return BaseModel.getInstance(canalInstanceConfigService.remoteOperation(id, nodeId, "stop"));
+    }
 }

+ 10 - 0
canal-admin/canal-admin-server/src/main/java/com/alibaba/otter/canal/admin/model/CanalInstanceConfig.java

@@ -29,6 +29,8 @@ public class CanalInstanceConfig extends Model {
     private String content;
     private Date   modifiedTime;
 
+    @Transient
+    private Long nodeId;
     @Transient
     private String nodeIp;
 
@@ -64,6 +66,14 @@ public class CanalInstanceConfig extends Model {
         this.modifiedTime = modifiedTime;
     }
 
+    public Long getNodeId() {
+        return nodeId;
+    }
+
+    public void setNodeId(Long nodeId) {
+        this.nodeId = nodeId;
+    }
+
     public String getNodeIp() {
         return nodeIp;
     }

+ 2 - 0
canal-admin/canal-admin-server/src/main/java/com/alibaba/otter/canal/admin/service/CanalInstanceService.java

@@ -15,4 +15,6 @@ public interface CanalInstanceService {
     void updateContent(CanalInstanceConfig canalInstanceConfig);
 
     void delete(Long id);
+
+    boolean remoteOperation(Long id, Long nodeId, String option);
 }

+ 37 - 0
canal-admin/canal-admin-server/src/main/java/com/alibaba/otter/canal/admin/service/impl/CanalInstanceServiceImpl.java

@@ -40,6 +40,7 @@ public class CanalInstanceServiceImpl implements CanalInstanceService {
             for (String instance : instances) {
                 for (CanalInstanceConfig cig : canalInstanceConfigs) {
                     if (instance.equals(cig.getName())) {
+                        cig.setNodeId(nodeServer.getId());
                         cig.setNodeIp(nodeServer.getIp());
                         break;
                     }
@@ -69,4 +70,40 @@ public class CanalInstanceServiceImpl implements CanalInstanceService {
         }
     }
 
+    public boolean remoteOperation(Long id, Long nodeId, String option) {
+        NodeServer nodeServer = null;
+        if ("start".equals(option)) {
+            // select the first node server
+            nodeServer = NodeServer.find.query().findOne();
+        } else {
+            if (nodeId == null) {
+                return false;
+            }
+            nodeServer = NodeServer.find.byId(nodeId);
+        }
+        if (nodeServer == null) {
+            return false;
+        }
+        CanalInstanceConfig canalInstanceConfig = CanalInstanceConfig.find.byId(id);
+        if (canalInstanceConfig == null) {
+            return false;
+        }
+        Boolean resutl = null;
+        if ("start".equals(option)) {
+            resutl = JMXConnection.execute(nodeServer.getIp(),
+                nodeServer.getPort(),
+                canalServerMXBean -> canalServerMXBean.startInstance(canalInstanceConfig.getName()));
+        } else if ("stop".equals(option)) {
+            resutl = JMXConnection.execute(nodeServer.getIp(),
+                nodeServer.getPort(),
+                canalServerMXBean -> canalServerMXBean.stopInstance(canalInstanceConfig.getName()));
+        } else {
+            return false;
+        }
+
+        if (resutl == null) {
+            resutl = false;
+        }
+        return resutl;
+    }
 }

+ 1 - 1
canal-admin/canal-admin-server/src/main/java/com/alibaba/otter/canal/admin/service/impl/NodeServerServiceImpl.java

@@ -116,7 +116,7 @@ public class NodeServerServiceImpl implements NodeServerService {
         if ("start".equals(option)) {
             resutl = JMXConnection.execute(nodeServer.getIp(), nodeServer.getPort(), CanalServerMXBean::start);
         } else if ("stop".equals(option)) {
-            JMXConnection.execute(nodeServer.getIp(), nodeServer.getPort(), CanalServerMXBean::stop);
+            resutl = JMXConnection.execute(nodeServer.getIp(), nodeServer.getPort(), CanalServerMXBean::stop);
         } else {
             return false;
         }

+ 29 - 0
canal-admin/canal-admin-ui/src/api/canalInstance.js

@@ -22,3 +22,32 @@ export function updateCanalInstance(data) {
     data
   })
 }
+
+export function addCanalInstance(data) {
+  return request({
+    url: '/canal/instance',
+    method: 'post',
+    data
+  })
+}
+
+export function deleteCanalInstance(id) {
+  return request({
+    url: '/canal/instance/' + id,
+    method: 'delete'
+  })
+}
+
+export function startInstance(id) {
+  return request({
+    url: '/canal/instance/start/' + id,
+    method: 'put'
+  })
+}
+
+export function stopNodeServer(id, nodeId) {
+  return request({
+    url: '/canal/instance/stop/' + id + '/' + nodeId,
+    method: 'put'
+  })
+}

+ 15 - 1
canal-admin/canal-admin-ui/src/router/index.js

@@ -75,10 +75,24 @@ export const constantRoutes = [
         meta: { title: 'Canal主配置', icon: 'form' }
       },
       {
-        path: 'canalInstance',
+        path: 'canalInstances',
         name: '实例管理',
         component: () => import('@/views/canalServer/canalInstance'),
         meta: { title: '实例管理', icon: 'nested' }
+      },
+      {
+        path: 'canalInstance/add',
+        name: '新建实例配置',
+        component: () => import('@/views/canalServer/canalInstanceAdd'),
+        meta: { title: '新建实例配置' },
+        hidden: true
+      },
+      {
+        path: 'canalInstance/modify',
+        name: '修改实例配置',
+        component: () => import('@/views/canalServer/canalInstanceUpdate'),
+        meta: { title: '修改实例配置' },
+        hidden: true
       }
     ]
   },

+ 95 - 5
canal-admin/canal-admin-ui/src/views/canalServer/canalInstance.vue

@@ -1,5 +1,12 @@
 <template>
   <div class="app-container">
+    <div class="filter-container">
+      <el-input v-model="listQuery.name" placeholder="实例名称" style="width: 200px;" class="filter-item" />
+      <el-button class="filter-item" type="primary" icon="el-icon-search" plain @click="fetchData()">查询</el-button>
+      &nbsp;&nbsp;
+      <el-button class="filter-item" type="primary" @click="handleCreate()">新建实例</el-button>
+      <el-button class="filter-item" type="info" @click="fetchData()">刷新列表</el-button>
+    </div>
     <el-table
       v-loading="listLoading"
       :data="list"
@@ -30,8 +37,8 @@
               操作<i class="el-icon-arrow-down el-icon--right" />
             </el-button>
             <el-dropdown-menu slot="dropdown">
-              <el-dropdown-item @click.native="handleUpdate(scope.row)">修改节点</el-dropdown-item>
-              <el-dropdown-item @click.native="handleDelete(scope.row)">删除节点</el-dropdown-item>
+              <el-dropdown-item @click.native="handleUpdate(scope.row)">修改实例</el-dropdown-item>
+              <el-dropdown-item @click.native="handleDelete(scope.row)">删除实例</el-dropdown-item>
               <el-dropdown-item @click.native="handleStart(scope.row)">启动服务</el-dropdown-item>
               <el-dropdown-item @click.native="handleStop(scope.row)">停止服务</el-dropdown-item>
             </el-dropdown-menu>
@@ -43,7 +50,7 @@
 </template>
 
 <script>
-import { getCanalInstances } from '@/api/canalInstance'
+import { getCanalInstances, deleteCanalInstance, startInstance, stopNodeServer } from '@/api/canalInstance'
 
 export default {
   filters: {
@@ -59,7 +66,10 @@ export default {
   data() {
     return {
       list: null,
-      listLoading: true
+      listLoading: true,
+      listQuery: {
+        name: ''
+      }
     }
   },
   created() {
@@ -68,10 +78,90 @@ export default {
   methods: {
     fetchData() {
       this.listLoading = true
-      getCanalInstances().then(res => {
+      getCanalInstances(this.listQuery).then(res => {
         this.list = res.data
         this.listLoading = false
       })
+    },
+    handleCreate() {
+      this.$router.push('/canalServer/canalInstance/add')
+    },
+    handleUpdate(row) {
+      this.$router.push('/canalServer/canalInstance/modify?id=' + row.id)
+    },
+    handleDelete(row) {
+      this.$confirm('删除实例配置会导致Canal实例停止', '确定删除实例信息', {
+        confirmButtonText: '确定',
+        cancelButtonText: '取消',
+        type: 'warning'
+      }).then(() => {
+        deleteCanalInstance(row.id).then((res) => {
+          if (res.data === 'success') {
+            this.fetchData()
+            this.$message({
+              message: '删除实例信息成功',
+              type: 'success'
+            })
+          } else {
+            this.$message({
+              message: '删除实例点信息失败',
+              type: 'error'
+            })
+          }
+        })
+      })
+    },
+    handleStart(row) {
+      if (row.nodeId !== null) {
+        this.$message({ message: '当前实例不是停止状态,无法启动', type: 'error' })
+        return
+      }
+      this.$confirm('启动Canal Instance: ' + row.name, '确定启动实例服务', {
+        confirmButtonText: '确定',
+        cancelButtonText: '取消',
+        type: 'warning'
+      }).then(() => {
+        startInstance(row.id).then((res) => {
+          if (res.data) {
+            this.fetchData()
+            this.$message({
+              message: '启动成功',
+              type: 'success'
+            })
+          } else {
+            this.$message({
+              message: '启动实例服务出现异常',
+              type: 'error'
+            })
+          }
+        })
+      })
+    },
+    handleStop(row) {
+      if (row.nodeId === null) {
+        this.$message({ message: '当前实例不是启动状态,无法停止', type: 'error' })
+        return
+      }
+      this.$confirm('停止Canal Instance: ' + row.name, '确定停止实例服务', {
+        confirmButtonText: '确定',
+        cancelButtonText: '取消',
+        type: 'warning'
+      }).then(() => {
+        stopNodeServer(row.id, row.nodeId).then((res) => {
+          if (res.data) {
+            this.fetchData()
+            this.$message({
+              message: '停止成功',
+              type: 'success'
+            })
+          } else {
+            this.$message({
+              message: '停止实例服务出现异常',
+              type: 'error'
+            })
+          }
+        })
+      })
     }
   }
 }

+ 88 - 0
canal-admin/canal-admin-ui/src/views/canalServer/canalInstanceAdd.vue

@@ -0,0 +1,88 @@
+<template>
+  <div>
+    <el-form ref="form" :model="form">
+      <div class="filter-container" style="padding-left: 10px;padding-top: 20px;">
+        <el-input v-model="form.name" placeholder="实例名称" style="width: 200px;" class="filter-item" />
+        &nbsp;
+        <el-button class="filter-item" type="primary" @click="onSubmit">新建</el-button>
+        <el-button class="filter-item" type="info" @click="onBack">返回</el-button>
+      </div>
+      <editor v-model="form.content" lang="properties" theme="chrome" width="100%" :height="800" @init="editorInit" />
+    </el-form>
+  </div>
+</template>
+
+<script>
+import { addCanalInstance } from '@/api/canalInstance'
+
+export default {
+  components: {
+    editor: require('vue2-ace-editor')
+  },
+  data() {
+    return {
+      form: {
+        name: '',
+        content: ''
+      }
+    }
+  },
+  created() {
+  },
+  methods: {
+    editorInit() {
+      require('brace/ext/language_tools')
+      require('brace/mode/html')
+      require('brace/mode/yaml')
+      require('brace/mode/properties')
+      require('brace/mode/javascript')
+      require('brace/mode/less')
+      require('brace/theme/chrome')
+      require('brace/snippets/javascript')
+    },
+    onSubmit() {
+      if (this.form.name === '') {
+        this.$message({
+          message: '请输入实例名称',
+          type: 'error'
+        })
+        return
+      }
+      this.$confirm(
+        '确定新建',
+        '确定新建',
+        {
+          confirmButtonText: '确定',
+          cancelButtonText: '取消',
+          type: 'warning'
+        }
+      ).then(() => {
+        addCanalInstance(this.form).then(response => {
+          if (response.data === 'success') {
+            this.$message({
+              message: '新建成功',
+              type: 'success'
+            })
+            this.$router.push('/canalServer/canalInstances')
+          } else {
+            this.$message({
+              message: '新建失败',
+              type: 'error'
+            })
+          }
+        })
+      })
+    },
+    onBack() {
+      history.go(-1)
+    }
+  }
+}
+</script>
+
+<style scoped>
+.line{
+  text-align: center;
+}
+</style>
+

+ 10 - 6
canal-admin/canal-admin-ui/src/views/canalServer/canalInstanceUpdate.vue

@@ -5,7 +5,8 @@
         <el-form-item>
           {{ form.name }}&nbsp;&nbsp;&nbsp;&nbsp;
           <el-button type="primary" @click="onSubmit">修改</el-button>
-          <el-button @click="onCancel">重置</el-button>
+          <el-button type="warning" @click="onCancel">重置</el-button>
+          <el-button type="info" @click="onBack">返回</el-button>
         </el-form-item>
       </div>
       <editor v-model="form.content" lang="properties" theme="chrome" width="100%" :height="800" @init="editorInit" />
@@ -14,7 +15,7 @@
 </template>
 
 <script>
-import { getCanalConfig, updateCanalConfig } from '@/api/canalConfig'
+import { canalInstanceDetail, updateCanalInstance } from '@/api/canalInstance'
 
 export default {
   components: {
@@ -44,16 +45,16 @@ export default {
       require('brace/snippets/javascript')
     },
     loadCanalConfig() {
-      getCanalConfig().then(response => {
+      canalInstanceDetail(this.$route.query.id).then(response => {
         const data = response.data
         this.form.id = data.id
-        this.form.name = data.name
+        this.form.name = data.name + '/instance.propertios'
         this.form.content = data.content
       })
     },
     onSubmit() {
       this.$confirm(
-        '修改Canal主配置可能会导致Server重启,是否继续?',
+        '修改Canal实例配置可能会导致实例重启,是否继续?',
         '确定修改',
         {
           confirmButtonText: '确定',
@@ -61,7 +62,7 @@ export default {
           type: 'warning'
         }
       ).then(() => {
-        updateCanalConfig(this.form).then(response => {
+        updateCanalInstance(this.form).then(response => {
           if (response.data === 'success') {
             this.$message({
               message: '修改成功',
@@ -79,6 +80,9 @@ export default {
     },
     onCancel() {
       this.loadCanalConfig()
+    },
+    onBack() {
+      history.go(-1)
     }
   }
 }

+ 1 - 1
canal-admin/canal-admin-ui/src/views/canalServer/config.vue

@@ -5,7 +5,7 @@
         <el-form-item>
           {{ form.name }}&nbsp;&nbsp;&nbsp;&nbsp;
           <el-button type="primary" @click="onSubmit">修改</el-button>
-          <el-button @click="onCancel">重置</el-button>
+          <el-button type="warning" @click="onCancel">重置</el-button>
         </el-form-item>
       </div>
       <editor v-model="form.content" lang="properties" theme="chrome" width="100%" :height="800" @init="editorInit" />

+ 4 - 4
canal-admin/canal-admin-ui/src/views/canalServer/nodeServer.vue

@@ -100,7 +100,7 @@
 </template>
 
 <script>
-import { addNodeServer, getNodeServers, updateNodeServer, deleteNodeServer, startNodeServer } from '@/api/nodeServer'
+import { addNodeServer, getNodeServers, updateNodeServer, deleteNodeServer, startNodeServer, stopNodeServer } from '@/api/nodeServer'
 
 export default {
   filters: {
@@ -227,12 +227,12 @@ export default {
           if (res.data === 'success') {
             this.fetchData()
             this.$message({
-              message: '删除点信息成功',
+              message: '删除点信息成功',
               type: 'success'
             })
           } else {
             this.$message({
-              message: '删除点信息失败',
+              message: '删除点信息失败',
               type: 'error'
             })
           }
@@ -275,7 +275,7 @@ export default {
         cancelButtonText: '取消',
         type: 'warning'
       }).then(() => {
-        startNodeServer(row.id).then((res) => {
+        stopNodeServer(row.id).then((res) => {
           if (res.data) {
             this.fetchData()
             this.$message({