DinoZhang %!s(int64=4) %!d(string=hai) anos
pai
achega
72ae7ba8d7

+ 7 - 0
admin/admin-ui/src/api/canalInstance.js

@@ -59,6 +59,13 @@ export function instanceLog(id, nodeId) {
   })
 }
 
+export function instanceMeta(id, nodeId) {
+  return request({
+    url: '/canal/instance/meta/' + id + '/' + nodeId,
+    method: 'get'
+  })
+}
+
 export function instanceStatus(id, option) {
   return request({
     url: '/canal/instance/status/' + id + '?option=' + option,

+ 7 - 0
admin/admin-ui/src/router/index.js

@@ -128,6 +128,13 @@ export const constantRoutes = [
         component: () => import('@/views/canalServer/CanalInstanceLogDetail'),
         meta: { title: 'Instance 日志' },
         hidden: true
+      },
+      {
+        path: 'canalInstance/meta',
+        name: 'Instance meta',
+        component: () => import('@/views/canalServer/CanalInstanceMetaDetail'),
+        meta: { title: 'Instance Meta' },
+        hidden: true
       }
     ]
   },

+ 9 - 0
admin/admin-ui/src/views/canalServer/CanalInstance.vue

@@ -64,6 +64,7 @@
               <el-dropdown-item @click.native="handleStart(scope.row)">启动</el-dropdown-item>
               <el-dropdown-item @click.native="handleStop(scope.row)">停止</el-dropdown-item>
               <el-dropdown-item @click.native="handleLog(scope.row)">日志</el-dropdown-item>
+              <el-dropdown-item @click.native="handleMeta(scope.row)">meta</el-dropdown-item>
             </el-dropdown-menu>
           </el-dropdown>
         </template>
@@ -117,6 +118,7 @@ export default {
     }
   },
   created() {
+    this.listQuery.name = this.$route.query.name
     getClustersAndServers().then((res) => {
       this.options = res.data
     })
@@ -222,6 +224,13 @@ export default {
         return
       }
       this.$router.push('canalInstance/log?id=' + row.id + '&nodeId=' + row.nodeServer.id)
+    },
+    handleMeta(row) {
+      if (row.nodeId === null) {
+        this.$message({ message: '当前Instance不是启动状态,无法查看meta', type: 'warning' })
+        return
+      }
+      this.$router.push('canalInstance/meta?id=' + row.id + '&nodeId=' + row.nodeServer.id)
     }
   }
 }

+ 53 - 0
admin/admin-ui/src/views/canalServer/CanalInstanceMetaDetail.vue

@@ -0,0 +1,53 @@
+<template>
+  <div>
+    <el-form ref="form" :model="form">
+      <div style="padding-left: 10px;padding-right: 10px;padding-top: 20px;">
+        <el-form-item>
+          {{ form.instance }}.meta&nbsp;&nbsp;&nbsp;&nbsp;
+          <el-button type="primary" @click="onRefresh">刷新</el-button>
+          <el-button type="info" @click="onBack(form.instance)">返回</el-button>
+        </el-form-item>
+        <el-input v-model="form.desc" :rows="35" :readonly="'readonly'" type="textarea" />
+      </div>
+    </el-form>
+  </div>
+</template>
+
+<script>
+import { instanceMeta } from '@/api/canalInstance'
+
+export default {
+  data() {
+    return {
+      form: {
+        instance: '',
+        desc: ''
+      }
+    }
+  },
+  created() {
+    this.fetchData()
+  },
+  methods: {
+    fetchData() {
+      instanceMeta(this.$route.query.id, this.$route.query.nodeId).then(res => {
+        this.form.instance = res.data.instance
+        this.form.desc = res.data.meta
+      })
+    },
+    onRefresh() {
+      this.fetchData()
+    },
+    onBack(instance) {
+      this.$router.push('/canalServer/canalInstances?name=' + instance)
+    }
+  }
+}
+</script>
+
+<style scoped>
+.line{
+  text-align: center;
+}
+</style>
+

+ 8 - 0
admin/admin-web/src/main/java/com/alibaba/otter/canal/admin/connector/AdminConnector.java

@@ -128,4 +128,12 @@ public interface AdminConnector {
      */
     String instanceLog(String destination, String fileName, int lines);
 
+    /**
+     * meta
+     * @param destination
+     * @param fileName
+     * @return
+     */
+    String instanceMeta(String destination, String fileName);
+
 }

+ 5 - 0
admin/admin-web/src/main/java/com/alibaba/otter/canal/admin/connector/SimpleAdminConnector.java

@@ -206,6 +206,11 @@ public class SimpleAdminConnector implements AdminConnector {
         return doLogAdmin("instance", "file", destination, fileName, lines);
     }
 
+    @Override
+    public String instanceMeta(final String destination, final String fileName) {
+        return doLogAdmin("meta", "file", destination, fileName,100);
+    }
+
     // ==================== helper method ====================
 
     private String doServerAdmin(String action) {

+ 14 - 0
admin/admin-web/src/main/java/com/alibaba/otter/canal/admin/controller/CanalInstanceController.java

@@ -161,6 +161,20 @@ public class CanalInstanceController {
         return BaseModel.getInstance(canalInstanceConfigService.remoteInstanceLog(id, nodeId));
     }
 
+    /**
+     * 获取实例meta信息
+     *
+     * @param id
+     * @param nodeId
+     * @param env
+     * @return
+     */
+    @GetMapping(value = "/instance/meta/{id}/{nodeId}")
+    public BaseModel<Map<String, String>> meta(@PathVariable Long id, @PathVariable Long nodeId,
+                                               @PathVariable String env) {
+        return BaseModel.getInstance(canalInstanceConfigService.remoteInstanceMeta(id, nodeId));
+    }
+
     /**
      * 通过Server id获取所有活动的Instance
      *

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

@@ -26,6 +26,8 @@ public interface CanalInstanceService {
 
     Map<String, String> remoteInstanceLog(Long id, Long nodeId);
 
+    Map<String, String> remoteInstanceMeta(Long id, Long nodeId);
+
     boolean remoteOperation(Long id, Long nodeId, String option);
 
     boolean instanceOperation(Long id, String option);

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

@@ -1,8 +1,10 @@
 package com.alibaba.otter.canal.admin.service.impl;
 
+import com.alibaba.otter.canal.common.zookeeper.ZkClientx;
 import io.ebean.Query;
 
 import java.security.NoSuchAlgorithmException;
+import java.text.MessageFormat;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collections;
@@ -256,6 +258,33 @@ public class CanalInstanceServiceImpl implements CanalInstanceService {
         return result;
     }
 
+    @Override
+    public Map<String, String> remoteInstanceMeta(final Long id, final Long nodeId) {
+        Map<String, String> result = new HashMap<>();
+
+        NodeServer nodeServer = NodeServer.find.byId(nodeId);
+        if (nodeServer == null) {
+            return result;
+        }
+        CanalInstanceConfig canalInstanceConfig = CanalInstanceConfig.find.byId(id);
+        if (canalInstanceConfig == null) {
+            return result;
+        }
+        String meta;
+        if (nodeServer.getCanalCluster() != null) {
+            ZkClientx zkClientx = ZkClientx.getZkClient(nodeServer.getCanalCluster().getZkHosts());
+            String zkPath = MessageFormat.format("/{0}/{1}/{2}/{3}/{4}/{5}", "otter", "canal", "destinations", canalInstanceConfig.getName(), "1001", "cursor");
+            meta = new String((byte[]) zkClientx.readData(zkPath));
+        } else {
+            meta = SimpleAdminConnectors.execute(nodeServer.getIp(),
+                    nodeServer.getAdminPort(),
+                    adminConnector -> adminConnector.instanceMeta(canalInstanceConfig.getName(), "meta.dat"));
+        }
+        result.put("instance", canalInstanceConfig.getName());
+        result.put("meta", meta);
+        return result;
+    }
+
     public boolean remoteOperation(Long id, Long nodeId, String option) {
         NodeServer nodeServer = null;
         if ("start".equals(option)) {

+ 5 - 0
deployer/src/main/java/com/alibaba/otter/canal/deployer/admin/CanalAdminController.java

@@ -218,6 +218,11 @@ public class CanalAdminController implements CanalAdmin {
         return FileUtils.readFileFromOffset("../logs/" + destination + "/" + fileName, lines, "UTF-8");
     }
 
+    @Override
+    public String instanceMeta(String destination, String fileName) {
+        return FileUtils.readFileFromOffset("../conf/" + destination + "/" + fileName, 100, "UTF-8");
+    }
+
     private InstanceAction getInstanceAction(String destination) {
         Map<InstanceConfig.InstanceMode, InstanceConfigMonitor> monitors = canalStater.getController()
             .getInstanceConfigMonitors();

+ 7 - 0
server/src/main/java/com/alibaba/otter/canal/admin/CanalAdmin.java

@@ -115,4 +115,11 @@ public interface CanalAdmin {
      * @return 日志信息
      */
     String instanceLog(String destination, String fileName, int lines);
+
+    /**
+     * 获取meta
+     * @param destination
+     * @return
+     */
+    String instanceMeta(String destination,String fileName);
 }

+ 3 - 0
server/src/main/java/com/alibaba/otter/canal/admin/handler/SessionHandler.java

@@ -116,6 +116,9 @@ public class SessionHandler extends SimpleChannelHandler {
                                 message = canalAdmin.instanceLog(destination, file, count);
                             }
                             break;
+                        case "meta":
+                            message = canalAdmin.instanceMeta(destination, file);
+                            break;
                         default:
                             byte[] errorBytes = AdminNettyUtils.errorPacket(301,
                                 MessageFormatter.format("LogAdmin type={} is unknown", type).getMessage());