fixed admin

This commit is contained in:
agapple
2019-08-31 12:24:41 +08:00
parent 274e91f8aa
commit 4e0b8a0278
59 changed files with 523 additions and 406 deletions

View File

@@ -0,0 +1,17 @@
package com.alibaba.otter.canal.admin.common;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
public class Threads {
public static int pool = 60;
public static final ExecutorService executorService = new ThreadPoolExecutor(pool,
pool,
0L,
TimeUnit.MILLISECONDS,
new ArrayBlockingQueue<Runnable>(pool * 20),
DaemonThreadFactory.daemonThreadFactory);
}

View File

@@ -5,7 +5,7 @@ import com.alibaba.otter.canal.protocol.exception.CanalClientException;
/**
* canal数据操作客户端
*
*
* @author zebin.xuzb @ 2012-6-19
* @author jianghang
* @version 1.0.0
@@ -14,14 +14,14 @@ public interface AdminConnector {
/**
* 链接对应的canal server
*
*
* @throws CanalClientException
*/
void connect() throws ServiceException;
/**
* 释放链接
*
*
* @throws CanalClientException
*/
void disconnect() throws ServiceException;
@@ -54,13 +54,6 @@ public interface AdminConnector {
*/
boolean restart();
/**
* 获取所有当前节点下所有实例
*
* @return 实例信息
*/
String getInstances();
/**
* 获取所有当前节点下运行中的实例
*
@@ -70,7 +63,7 @@ public interface AdminConnector {
/**
* 通过实例名检查
*
*
* @param destination
* @return
*/
@@ -92,6 +85,14 @@ public interface AdminConnector {
*/
boolean stopInstance(String destination);
/**
* 通过实例名释放,主要针对cluster模式有效(通知当前主机释放instance运行交给其他人来抢占)
*
* @param destination 实例名
* @return 是否成功
*/
boolean releaseInstance(String destination);
/**
* 通过实例名重启实例
*
@@ -116,7 +117,7 @@ public interface AdminConnector {
/**
* 获取Instance的机器日志列表
*
*
* @param destination
*/
String listInstanceLog(String destination);

View File

@@ -156,11 +156,6 @@ public class SimpleAdminConnector implements AdminConnector {
return BooleanUtils.toBoolean(Integer.parseInt(doServerAdmin("restart")));
}
@Override
public String getInstances() {
return doServerAdmin("instances");
}
@Override
public String getRunningInstances() {
return doServerAdmin("list");
@@ -181,6 +176,11 @@ public class SimpleAdminConnector implements AdminConnector {
return BooleanUtils.toBoolean(Integer.parseInt(doInstanceAdmin(destination, "stop")));
}
@Override
public boolean releaseInstance(String destination) {
return BooleanUtils.toBoolean(Integer.parseInt(doInstanceAdmin(destination, "release")));
}
@Override
public boolean restartInstance(String destination) {
return BooleanUtils.toBoolean(Integer.parseInt(doInstanceAdmin(destination, "restart")));
@@ -232,8 +232,11 @@ public class SimpleAdminConnector implements AdminConnector {
try {
writeWithHeader(Packet.newBuilder()
.setType(PacketType.INSTANCE)
.setBody(
InstanceAdmin.newBuilder().setDestination(destination).setAction(action).build().toByteString())
.setBody(InstanceAdmin.newBuilder()
.setDestination(destination)
.setAction(action)
.build()
.toByteString())
.build()
.toByteArray());

View File

@@ -162,6 +162,6 @@ public class CanalInstanceController {
*/
@GetMapping(value = "/active/instances/{serverId}")
public BaseModel<List<CanalInstanceConfig>> activeInstances(@PathVariable Long serverId, @PathVariable String env) {
return BaseModel.getInstance(canalInstanceConfigService.findActiveInstanceByServerId(serverId));
return BaseModel.getInstance(canalInstanceConfigService.findActiveInstaceByServerId(serverId));
}
}

View File

@@ -1,9 +1,15 @@
package com.alibaba.otter.canal.admin.controller;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import com.alibaba.otter.canal.admin.model.BaseModel;
import com.alibaba.otter.canal.admin.model.NodeServer;

View File

@@ -20,6 +20,8 @@ public interface CanalInstanceService {
CanalInstanceConfig detail(Long id);
CanalInstanceConfig findOne(String name);
void updateContent(CanalInstanceConfig canalInstanceConfig);
void delete(Long id);
@@ -30,5 +32,5 @@ public interface CanalInstanceService {
boolean instanceOperation(Long id, String option);
List<CanalInstanceConfig> findActiveInstanceByServerId(Long serverId);
List<CanalInstanceConfig> findActiveInstaceByServerId(Long serverId);
}

View File

@@ -1,17 +1,16 @@
package com.alibaba.otter.canal.admin.service.impl;
import io.ebean.Query;
import java.util.List;
import com.alibaba.otter.canal.admin.common.exception.ServiceException;
import com.alibaba.otter.canal.admin.model.CanalInstanceConfig;
import com.alibaba.otter.canal.admin.model.NodeServer;
import org.springframework.stereotype.Service;
import com.alibaba.otter.canal.admin.common.exception.ServiceException;
import com.alibaba.otter.canal.admin.model.CanalCluster;
import com.alibaba.otter.canal.admin.model.NodeServer;
import com.alibaba.otter.canal.admin.service.CanalClusterServic;
import io.ebean.Query;
@Service
public class CanalClusterServiceImpl implements CanalClusterServic {
@@ -31,16 +30,10 @@ public class CanalClusterServiceImpl implements CanalClusterServic {
// 判断集群下是否存在server信息
int serverCnt = NodeServer.find.query().where().eq("clusterId", id).findCount();
if (serverCnt > 0) {
throw new ServiceException("Servers exist, delete failed");
throw new ServiceException("当前集群下存在Server, 无法删除");
}
// 判断集群下是否存在instance信息
int instanceCnt = CanalInstanceConfig.find.query().where().eq("clusterId", id).findCount();
if (instanceCnt > 0) {
throw new ServiceException("Instances exist, delete failed");
}
CanalCluster canalCluster = CanalCluster.find.byId(id);
CanalCluster canalCluster = CanalCluster.find.byId(id);
if (canalCluster != null) {
canalCluster.delete();
}

View File

@@ -5,19 +5,17 @@ import java.io.InputStream;
import java.security.NoSuchAlgorithmException;
import java.util.Date;
import com.alibaba.otter.canal.admin.common.exception.ServiceException;
import com.alibaba.otter.canal.admin.model.CanalCluster;
import com.alibaba.otter.canal.admin.model.NodeServer;
import com.alibaba.otter.canal.protocol.SecurityUtil;
import io.ebean.Query;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;
import com.alibaba.otter.canal.admin.common.exception.ServiceException;
import com.alibaba.otter.canal.admin.model.CanalConfig;
import com.alibaba.otter.canal.admin.model.NodeServer;
import com.alibaba.otter.canal.admin.service.CanalConfigService;
import com.alibaba.otter.canal.protocol.SecurityUtil;
/**
* Canal配置信息业务层

View File

@@ -1,23 +1,32 @@
package com.alibaba.otter.canal.admin.service.impl;
import java.security.NoSuchAlgorithmException;
import java.util.*;
import java.util.concurrent.*;
import io.ebean.Query;
import java.security.NoSuchAlgorithmException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import com.alibaba.otter.canal.admin.model.Pager;
import org.apache.commons.lang.StringUtils;
import org.springframework.stereotype.Service;
import com.alibaba.otter.canal.admin.common.DaemonThreadFactory;
import com.alibaba.otter.canal.admin.common.Threads;
import com.alibaba.otter.canal.admin.common.exception.ServiceException;
import com.alibaba.otter.canal.admin.connector.AdminConnector;
import com.alibaba.otter.canal.admin.connector.SimpleAdminConnectors;
import com.alibaba.otter.canal.admin.model.CanalInstanceConfig;
import com.alibaba.otter.canal.admin.model.NodeServer;
import com.alibaba.otter.canal.admin.model.Pager;
import com.alibaba.otter.canal.admin.service.CanalInstanceService;
import com.alibaba.otter.canal.protocol.SecurityUtil;
import io.ebean.Query;
import com.google.common.collect.Lists;
/**
* Canal实例配置信息业务层
@@ -28,8 +37,7 @@ import io.ebean.Query;
@Service
public class CanalInstanceServiceImpl implements CanalInstanceService {
public Pager<CanalInstanceConfig> findList(CanalInstanceConfig canalInstanceConfig,
Pager<CanalInstanceConfig> pager) {
public Pager<CanalInstanceConfig> findList(CanalInstanceConfig canalInstanceConfig, Pager<CanalInstanceConfig> pager) {
Query<CanalInstanceConfig> query = CanalInstanceConfig.find.query()
.setDisableLazyLoading(true)
.select("clusterId, serverId, name, modifiedTime")
@@ -62,12 +70,9 @@ public class CanalInstanceServiceImpl implements CanalInstanceService {
}
// check all canal instances running status
ExecutorService executorService = Executors.newFixedThreadPool(canalInstanceConfigs.size(),
DaemonThreadFactory.daemonThreadFactory);
List<Future<Void>> futures = new ArrayList<>(canalInstanceConfigs.size());
for (CanalInstanceConfig canalInstanceConfig1 : canalInstanceConfigs) {
futures.add(executorService.submit(() -> {
futures.add(Threads.executorService.submit(() -> {
List<NodeServer> nodeServers;
if (canalInstanceConfig1.getClusterId() != null) { // 集群模式
nodeServers = NodeServer.find.query()
@@ -81,15 +86,17 @@ public class CanalInstanceServiceImpl implements CanalInstanceService {
}
for (NodeServer nodeServer : nodeServers) {
String runningInstances = SimpleAdminConnectors
.execute(nodeServer.getIp(), nodeServer.getAdminPort(), AdminConnector::getRunningInstances);
String runningInstances = SimpleAdminConnectors.execute(nodeServer.getIp(),
nodeServer.getAdminPort(),
AdminConnector::getRunningInstances);
if (runningInstances == null) {
continue;
}
String[] instances = runningInstances.split(",");
for (String instance : instances) {
if (instance.equals(canalInstanceConfig1.getName())) {
if (canalInstanceConfig1.getNodeServer() == null) { // 集群模式下 server 对象为空
// 集群模式下server对象为空
if (canalInstanceConfig1.getNodeServer() == null) {
canalInstanceConfig1.setNodeServer(nodeServer);
}
canalInstanceConfig1.setRunningStatus("1");
@@ -109,8 +116,6 @@ public class CanalInstanceServiceImpl implements CanalInstanceService {
// ignore
}
});
executorService.shutdownNow();
return pager;
}
@@ -119,29 +124,20 @@ public class CanalInstanceServiceImpl implements CanalInstanceService {
*
* @param serverId server id
*/
public List<CanalInstanceConfig> findActiveInstanceByServerId(Long serverId) {
public List<CanalInstanceConfig> findActiveInstaceByServerId(Long serverId) {
NodeServer nodeServer = NodeServer.find.byId(serverId);
if (nodeServer == null) {
return null;
}
String runningInstances = null;
if (nodeServer.getClusterId() != null) {// 集群模式
// 只取活动的instances
runningInstances = SimpleAdminConnectors
.execute(nodeServer.getIp(), nodeServer.getAdminPort(), AdminConnector::getRunningInstances);
} else {
// 取所属所有instances
runningInstances = SimpleAdminConnectors
.execute(nodeServer.getIp(), nodeServer.getAdminPort(), AdminConnector::getInstances);
}
String runningInstances = SimpleAdminConnectors.execute(nodeServer.getIp(),
nodeServer.getAdminPort(),
AdminConnector::getRunningInstances);
if (runningInstances == null) {
return null;
}
String[] instances = runningInstances.split(",");
Object obj[] = Lists.newArrayList(instances).toArray();
// 单机模式和集群模式区分处理
if (nodeServer.getClusterId() != null) { // 集群模式
List<CanalInstanceConfig> list = CanalInstanceConfig.find.query()
@@ -149,7 +145,7 @@ public class CanalInstanceServiceImpl implements CanalInstanceService {
.select("clusterId, serverId, name, modifiedTime")
.where()
// 暂停的实例也显示 .eq("status", "1")
.in("name", instances)
.in("name", obj)
.findList();
list.forEach(config -> config.setRunningStatus("1"));
return list; // 集群模式直接返回当前运行的Instances
@@ -237,6 +233,16 @@ public class CanalInstanceServiceImpl implements CanalInstanceService {
}
}
@Override
public CanalInstanceConfig findOne(String name) {
CanalInstanceConfig config = CanalInstanceConfig.find.query()
.setDisableLazyLoading(true)
.where()
.eq("name", name)
.findOne();
return config;
}
public Map<String, String> remoteInstanceLog(Long id, Long nodeId) {
Map<String, String> result = new HashMap<>();
@@ -279,33 +285,19 @@ public class CanalInstanceServiceImpl implements CanalInstanceService {
if (canalInstanceConfig == null) {
return false;
}
Boolean resutl = null;
Boolean result = null;
if ("start".equals(option)) {
if (nodeServer.getClusterId() == null) { // 非集群模式
return instanceOperation(id, "start");
// resutl = SimpleAdminConnectors.execute(nodeServer.getIp(),
// nodeServer.getAdminPort(),
// adminConnector ->
// adminConnector.startInstance(canalInstanceConfig.getName()));
} else {
throw new ServiceException("集群模式不允许指定server启动");
}
} else if ("stop".equals(option)) {
if (nodeServer.getClusterId() != null) {
resutl = SimpleAdminConnectors.execute(nodeServer.getIp(),
// 集群模式,通知主动释放
result = SimpleAdminConnectors.execute(nodeServer.getIp(),
nodeServer.getAdminPort(),
adminConnector -> adminConnector.stopInstance(canalInstanceConfig.getName()));
NodeServer nodeServerTmp = nodeServer;
// 集群模式下停止实例后过五秒钟再次启动进行启动抢占
Thread thread = new Thread(() -> {
try {
Thread.sleep(5000);
SimpleAdminConnectors.execute(nodeServerTmp.getIp(),
nodeServerTmp.getAdminPort(),
adminConnector -> adminConnector.startInstance(canalInstanceConfig.getName()));
} catch (Throwable e) {
}
});
thread.start();
adminConnector -> adminConnector.releaseInstance(canalInstanceConfig.getName()));
} else { // 非集群模式下直接将状态置为0
return instanceOperation(id, "stop");
}
@@ -313,10 +305,10 @@ public class CanalInstanceServiceImpl implements CanalInstanceService {
return false;
}
if (resutl == null) {
resutl = false;
if (result == null) {
result = false;
}
return resutl;
return result;
}
public boolean instanceOperation(Long id, String option) {

View File

@@ -1,19 +1,25 @@
package com.alibaba.otter.canal.admin.service.impl;
import com.alibaba.otter.canal.admin.common.DaemonThreadFactory;
import com.alibaba.otter.canal.admin.model.*;
import io.ebean.Query;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.*;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import org.apache.commons.lang.StringUtils;
import org.springframework.stereotype.Service;
import com.alibaba.otter.canal.admin.common.Threads;
import com.alibaba.otter.canal.admin.common.exception.ServiceException;
import com.alibaba.otter.canal.admin.connector.AdminConnector;
import com.alibaba.otter.canal.admin.connector.SimpleAdminConnectors;
import com.alibaba.otter.canal.admin.model.CanalConfig;
import com.alibaba.otter.canal.admin.model.CanalInstanceConfig;
import com.alibaba.otter.canal.admin.model.NodeServer;
import com.alibaba.otter.canal.admin.model.Pager;
import com.alibaba.otter.canal.admin.service.NodeServerService;
/**
@@ -123,12 +129,10 @@ public class NodeServerServiceImpl implements NodeServerService {
return pager;
}
ExecutorService executorService = Executors.newFixedThreadPool(nodeServers.size(),
DaemonThreadFactory.daemonThreadFactory);
List<Future<Boolean>> futures = new ArrayList<>(nodeServers.size());
// get all nodes status
for (NodeServer ns : nodeServers) {
futures.add(executorService.submit(() -> {
futures.add(Threads.executorService.submit(() -> {
boolean status = SimpleAdminConnectors.execute(ns.getIp(), ns.getAdminPort(), AdminConnector::check);
ns.setStatus(status ? "1" : "0");
return !status;
@@ -142,8 +146,6 @@ public class NodeServerServiceImpl implements NodeServerService {
}
});
executorService.shutdownNow();
return pager;
}
@@ -157,8 +159,9 @@ public class NodeServerServiceImpl implements NodeServerService {
if (nodeServer == null) {
return "";
}
return SimpleAdminConnectors
.execute(nodeServer.getIp(), nodeServer.getAdminPort(), adminConnector -> adminConnector.canalLog(100));
return SimpleAdminConnectors.execute(nodeServer.getIp(),
nodeServer.getAdminPort(),
adminConnector -> adminConnector.canalLog(100));
}
public boolean remoteOperation(Long id, String option) {
@@ -168,8 +171,7 @@ public class NodeServerServiceImpl implements NodeServerService {
}
Boolean result = null;
if ("start".equals(option)) {
result = SimpleAdminConnectors
.execute(nodeServer.getIp(), nodeServer.getAdminPort(), AdminConnector::start);
result = SimpleAdminConnectors.execute(nodeServer.getIp(), nodeServer.getAdminPort(), AdminConnector::start);
} else if ("stop".equals(option)) {
result = SimpleAdminConnectors.execute(nodeServer.getIp(), nodeServer.getAdminPort(), AdminConnector::stop);
} else {

View File

@@ -7,6 +7,7 @@ import java.util.stream.Collectors;
import org.apache.commons.lang.StringUtils;
import org.springframework.stereotype.Service;
import com.alibaba.otter.canal.admin.common.exception.ServiceException;
import com.alibaba.otter.canal.admin.model.CanalConfig;
import com.alibaba.otter.canal.admin.model.CanalInstanceConfig;
import com.alibaba.otter.canal.admin.model.NodeServer;
@@ -28,7 +29,11 @@ public class PollingConfigServiceImpl implements PollingConfigServer {
} else { // 单机模式
canalConfig = CanalConfig.find.query().where().eq("serverId", server.getId()).findOne();
}
if (canalConfig != null && !canalConfig.getContentMd5().equals(md5)) { // 内容发生变化
if (canalConfig == null) {
throw new ServiceException("canal.properties config is empty");
}
if (!canalConfig.getContentMd5().equals(md5)) { // 内容发生变化
return canalConfig;
}
return null;

View File

@@ -7,8 +7,8 @@ spring:
spring.datasource:
url: jdbc:mysql://127.0.0.1:3306/canal_manager?useUnicode=true&characterEncoding=UTF-8&useSSL=false
username: root
password: 121212
username: canal
password: canal
driver-class-name: com.mysql.jdbc.Driver
hikari:
maximum-pool-size: 10

View File

@@ -1 +1 @@
<!DOCTYPE html><html><head><meta charset=utf-8><meta http-equiv=X-UA-Compatible content="IE=edge,chrome=1"><meta name=viewport content="width=device-width,initial-scale=1,maximum-scale=1,user-scalable=no"><link rel=icon href=/favicon.ico><title>Canal Admin</title><link href=/static/css/chunk-elementUI.18b11d0e.css rel=stylesheet><link href=/static/css/chunk-libs.5cf311f0.css rel=stylesheet><link href=/static/css/app.bb951cb3.css rel=stylesheet></head><body><noscript><strong>We're sorry but Canal Admin doesn't work properly without JavaScript enabled. Please enable it to continue.</strong></noscript><div id=app></div><script src=/static/js/chunk-elementUI.667f4c87.js></script><script src=/static/js/chunk-libs.c04beefc.js></script><script>(function(e){function n(n){for(var r,c,f=n[0],a=n[1],i=n[2],d=0,h=[];d<f.length;d++)c=f[d],u[c]&&h.push(u[c][0]),u[c]=0;for(r in a)Object.prototype.hasOwnProperty.call(a,r)&&(e[r]=a[r]);l&&l(n);while(h.length)h.shift()();return o.push.apply(o,i||[]),t()}function t(){for(var e,n=0;n<o.length;n++){for(var t=o[n],r=!0,c=1;c<t.length;c++){var f=t[c];0!==u[f]&&(r=!1)}r&&(o.splice(n--,1),e=a(a.s=t[0]))}return e}var r={},c={runtime:0},u={runtime:0},o=[];function f(e){return a.p+"static/js/"+({}[e]||e)+"."+{"chunk-0dca2f22":"a2bc28b8","chunk-2301924a":"b0e97fa6","chunk-98f505d0":"aade2e3b","chunk-bd1d44ee":"c2f6b1a9","chunk-101fc062":"bc898027","chunk-14b5f7a4":"d0531bb5","chunk-22553be3":"21abed4f","chunk-37c49cbf":"64d26540","chunk-49959c8b":"6d226f70","chunk-4f09fed2":"ff28d88d","chunk-55380ff2":"681c71c9","chunk-69386cf0":"76d77f5c","chunk-7ec889b7":"295b8aad"}[e]+".js"}function a(n){if(r[n])return r[n].exports;var t=r[n]={i:n,l:!1,exports:{}};return e[n].call(t.exports,t,t.exports,a),t.l=!0,t.exports}a.e=function(e){var n=[],t={"chunk-2301924a":1,"chunk-98f505d0":1,"chunk-bd1d44ee":1,"chunk-101fc062":1,"chunk-14b5f7a4":1,"chunk-22553be3":1,"chunk-37c49cbf":1,"chunk-49959c8b":1,"chunk-4f09fed2":1,"chunk-69386cf0":1,"chunk-7ec889b7":1};c[e]?n.push(c[e]):0!==c[e]&&t[e]&&n.push(c[e]=new Promise(function(n,t){for(var r="static/css/"+({}[e]||e)+"."+{"chunk-0dca2f22":"31d6cfe0","chunk-2301924a":"160e7b4a","chunk-98f505d0":"5280f88f","chunk-bd1d44ee":"1528199a","chunk-101fc062":"fad9926f","chunk-14b5f7a4":"f3e06673","chunk-22553be3":"f3e06673","chunk-37c49cbf":"efc21a9c","chunk-49959c8b":"e8e2beee","chunk-4f09fed2":"70ec0b86","chunk-55380ff2":"31d6cfe0","chunk-69386cf0":"741ff14e","chunk-7ec889b7":"c0585512"}[e]+".css",u=a.p+r,o=document.getElementsByTagName("link"),f=0;f<o.length;f++){var i=o[f],d=i.getAttribute("data-href")||i.getAttribute("href");if("stylesheet"===i.rel&&(d===r||d===u))return n()}var h=document.getElementsByTagName("style");for(f=0;f<h.length;f++){i=h[f],d=i.getAttribute("data-href");if(d===r||d===u)return n()}var l=document.createElement("link");l.rel="stylesheet",l.type="text/css",l.onload=n,l.onerror=function(n){var r=n&&n.target&&n.target.src||u,o=new Error("Loading CSS chunk "+e+" failed.\n("+r+")");o.code="CSS_CHUNK_LOAD_FAILED",o.request=r,delete c[e],l.parentNode.removeChild(l),t(o)},l.href=u;var s=document.getElementsByTagName("head")[0];s.appendChild(l)}).then(function(){c[e]=0}));var r=u[e];if(0!==r)if(r)n.push(r[2]);else{var o=new Promise(function(n,t){r=u[e]=[n,t]});n.push(r[2]=o);var i,d=document.createElement("script");d.charset="utf-8",d.timeout=120,a.nc&&d.setAttribute("nonce",a.nc),d.src=f(e),i=function(n){d.onerror=d.onload=null,clearTimeout(h);var t=u[e];if(0!==t){if(t){var r=n&&("load"===n.type?"missing":n.type),c=n&&n.target&&n.target.src,o=new Error("Loading chunk "+e+" failed.\n("+r+": "+c+")");o.type=r,o.request=c,t[1](o)}u[e]=void 0}};var h=setTimeout(function(){i({type:"timeout",target:d})},12e4);d.onerror=d.onload=i,document.head.appendChild(d)}return Promise.all(n)},a.m=e,a.c=r,a.d=function(e,n,t){a.o(e,n)||Object.defineProperty(e,n,{enumerable:!0,get:t})},a.r=function(e){"undefined"!==typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},a.t=function(e,n){if(1&n&&(e=a(e)),8&n)return e;if(4&n&&"object"===typeof e&&e&&e.__esModule)return e;var t=Object.create(null);if(a.r(t),Object.defineProperty(t,"default",{enumerable:!0,value:e}),2&n&&"string"!=typeof e)for(var r in e)a.d(t,r,function(n){return e[n]}.bind(null,r));return t},a.n=function(e){var n=e&&e.__esModule?function(){return e["default"]}:function(){return e};return a.d(n,"a",n),n},a.o=function(e,n){return Object.prototype.hasOwnProperty.call(e,n)},a.p="/",a.oe=function(e){throw console.error(e),e};var i=window["webpackJsonp"]=window["webpackJsonp"]||[],d=i.push.bind(i);i.push=n,i=i.slice();for(var h=0;h<i.length;h++)n(i[h]);var l=d;t()})([]);</script><script src=/static/js/app.cae6e777.js></script></body></html>
<!DOCTYPE html><html><head><meta charset=utf-8><meta http-equiv=X-UA-Compatible content="IE=edge,chrome=1"><meta name=viewport content="width=device-width,initial-scale=1,maximum-scale=1,user-scalable=no"><link rel=icon href=/favicon.ico><title>Canal Admin</title><link href=/static/css/chunk-elementUI.18b11d0e.css rel=stylesheet><link href=/static/css/chunk-libs.5cf311f0.css rel=stylesheet><link href=/static/css/app.bb951cb3.css rel=stylesheet></head><body><noscript><strong>We're sorry but Canal Admin doesn't work properly without JavaScript enabled. Please enable it to continue.</strong></noscript><div id=app></div><script src=/static/js/chunk-elementUI.667f4c87.js></script><script src=/static/js/chunk-libs.c04beefc.js></script><script>(function(e){function n(n){for(var r,c,f=n[0],a=n[1],i=n[2],h=0,d=[];h<f.length;h++)c=f[h],u[c]&&d.push(u[c][0]),u[c]=0;for(r in a)Object.prototype.hasOwnProperty.call(a,r)&&(e[r]=a[r]);l&&l(n);while(d.length)d.shift()();return o.push.apply(o,i||[]),t()}function t(){for(var e,n=0;n<o.length;n++){for(var t=o[n],r=!0,c=1;c<t.length;c++){var f=t[c];0!==u[f]&&(r=!1)}r&&(o.splice(n--,1),e=a(a.s=t[0]))}return e}var r={},c={runtime:0},u={runtime:0},o=[];function f(e){return a.p+"static/js/"+({}[e]||e)+"."+{"chunk-0dca2f22":"a2bc28b8","chunk-2301924a":"1774b851","chunk-98f505d0":"f3096ce7","chunk-bd1d44ee":"8c8282cc","chunk-101fc062":"372a5ca5","chunk-14b5f7a4":"013449e9","chunk-22553be3":"e6d72de5","chunk-37c49cbf":"92ebe0ae","chunk-49959c8b":"058266cb","chunk-4f09fed2":"d107437b","chunk-55380ff2":"430ee174","chunk-69386cf0":"bdbe3f0c","chunk-7ec889b7":"5f730d9b"}[e]+".js"}function a(n){if(r[n])return r[n].exports;var t=r[n]={i:n,l:!1,exports:{}};return e[n].call(t.exports,t,t.exports,a),t.l=!0,t.exports}a.e=function(e){var n=[],t={"chunk-2301924a":1,"chunk-98f505d0":1,"chunk-bd1d44ee":1,"chunk-101fc062":1,"chunk-14b5f7a4":1,"chunk-22553be3":1,"chunk-37c49cbf":1,"chunk-49959c8b":1,"chunk-4f09fed2":1,"chunk-69386cf0":1,"chunk-7ec889b7":1};c[e]?n.push(c[e]):0!==c[e]&&t[e]&&n.push(c[e]=new Promise(function(n,t){for(var r="static/css/"+({}[e]||e)+"."+{"chunk-0dca2f22":"31d6cfe0","chunk-2301924a":"160e7b4a","chunk-98f505d0":"5280f88f","chunk-bd1d44ee":"1528199a","chunk-101fc062":"fad9926f","chunk-14b5f7a4":"f3e06673","chunk-22553be3":"f3e06673","chunk-37c49cbf":"efc21a9c","chunk-49959c8b":"e8e2beee","chunk-4f09fed2":"70ec0b86","chunk-55380ff2":"31d6cfe0","chunk-69386cf0":"741ff14e","chunk-7ec889b7":"c0585512"}[e]+".css",u=a.p+r,o=document.getElementsByTagName("link"),f=0;f<o.length;f++){var i=o[f],h=i.getAttribute("data-href")||i.getAttribute("href");if("stylesheet"===i.rel&&(h===r||h===u))return n()}var d=document.getElementsByTagName("style");for(f=0;f<d.length;f++){i=d[f],h=i.getAttribute("data-href");if(h===r||h===u)return n()}var l=document.createElement("link");l.rel="stylesheet",l.type="text/css",l.onload=n,l.onerror=function(n){var r=n&&n.target&&n.target.src||u,o=new Error("Loading CSS chunk "+e+" failed.\n("+r+")");o.code="CSS_CHUNK_LOAD_FAILED",o.request=r,delete c[e],l.parentNode.removeChild(l),t(o)},l.href=u;var s=document.getElementsByTagName("head")[0];s.appendChild(l)}).then(function(){c[e]=0}));var r=u[e];if(0!==r)if(r)n.push(r[2]);else{var o=new Promise(function(n,t){r=u[e]=[n,t]});n.push(r[2]=o);var i,h=document.createElement("script");h.charset="utf-8",h.timeout=120,a.nc&&h.setAttribute("nonce",a.nc),h.src=f(e),i=function(n){h.onerror=h.onload=null,clearTimeout(d);var t=u[e];if(0!==t){if(t){var r=n&&("load"===n.type?"missing":n.type),c=n&&n.target&&n.target.src,o=new Error("Loading chunk "+e+" failed.\n("+r+": "+c+")");o.type=r,o.request=c,t[1](o)}u[e]=void 0}};var d=setTimeout(function(){i({type:"timeout",target:h})},12e4);h.onerror=h.onload=i,document.head.appendChild(h)}return Promise.all(n)},a.m=e,a.c=r,a.d=function(e,n,t){a.o(e,n)||Object.defineProperty(e,n,{enumerable:!0,get:t})},a.r=function(e){"undefined"!==typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},a.t=function(e,n){if(1&n&&(e=a(e)),8&n)return e;if(4&n&&"object"===typeof e&&e&&e.__esModule)return e;var t=Object.create(null);if(a.r(t),Object.defineProperty(t,"default",{enumerable:!0,value:e}),2&n&&"string"!=typeof e)for(var r in e)a.d(t,r,function(n){return e[n]}.bind(null,r));return t},a.n=function(e){var n=e&&e.__esModule?function(){return e["default"]}:function(){return e};return a.d(n,"a",n),n},a.o=function(e,n){return Object.prototype.hasOwnProperty.call(e,n)},a.p="/",a.oe=function(e){throw console.error(e),e};var i=window["webpackJsonp"]=window["webpackJsonp"]||[],h=i.push.bind(i);i.push=n,i=i.slice();for(var d=0;d<i.length;d++)n(i[d]);var l=h;t()})([]);</script><script src=/static/js/app.eee35d99.js></script></body></html>

View File

@@ -1 +0,0 @@
.line[data-v-01afb045]{text-align:center}

View File

@@ -1 +0,0 @@
.wscn-http404-container[data-v-c095f994]{-webkit-transform:translate(-50%,-50%);transform:translate(-50%,-50%);position:absolute;top:40%;left:50%}.wscn-http404[data-v-c095f994]{position:relative;width:1200px;padding:0 50px;overflow:hidden}.wscn-http404 .pic-404[data-v-c095f994]{position:relative;float:left;width:600px;overflow:hidden}.wscn-http404 .pic-404__parent[data-v-c095f994]{width:100%}.wscn-http404 .pic-404__child[data-v-c095f994]{position:absolute}.wscn-http404 .pic-404__child.left[data-v-c095f994]{width:80px;top:17px;left:220px;opacity:0;-webkit-animation-name:cloudLeft-data-v-c095f994;animation-name:cloudLeft-data-v-c095f994;-webkit-animation-duration:2s;animation-duration:2s;-webkit-animation-timing-function:linear;animation-timing-function:linear;-webkit-animation-fill-mode:forwards;animation-fill-mode:forwards;-webkit-animation-delay:1s;animation-delay:1s}.wscn-http404 .pic-404__child.mid[data-v-c095f994]{width:46px;top:10px;left:420px;opacity:0;-webkit-animation-name:cloudMid-data-v-c095f994;animation-name:cloudMid-data-v-c095f994;-webkit-animation-duration:2s;animation-duration:2s;-webkit-animation-timing-function:linear;animation-timing-function:linear;-webkit-animation-fill-mode:forwards;animation-fill-mode:forwards;-webkit-animation-delay:1.2s;animation-delay:1.2s}.wscn-http404 .pic-404__child.right[data-v-c095f994]{width:62px;top:100px;left:500px;opacity:0;-webkit-animation-name:cloudRight-data-v-c095f994;animation-name:cloudRight-data-v-c095f994;-webkit-animation-duration:2s;animation-duration:2s;-webkit-animation-timing-function:linear;animation-timing-function:linear;-webkit-animation-fill-mode:forwards;animation-fill-mode:forwards;-webkit-animation-delay:1s;animation-delay:1s}@-webkit-keyframes cloudLeft-data-v-c095f994{0%{top:17px;left:220px;opacity:0}20%{top:33px;left:188px;opacity:1}80%{top:81px;left:92px;opacity:1}to{top:97px;left:60px;opacity:0}}@keyframes cloudLeft-data-v-c095f994{0%{top:17px;left:220px;opacity:0}20%{top:33px;left:188px;opacity:1}80%{top:81px;left:92px;opacity:1}to{top:97px;left:60px;opacity:0}}@-webkit-keyframes cloudMid-data-v-c095f994{0%{top:10px;left:420px;opacity:0}20%{top:40px;left:360px;opacity:1}70%{top:130px;left:180px;opacity:1}to{top:160px;left:120px;opacity:0}}@keyframes cloudMid-data-v-c095f994{0%{top:10px;left:420px;opacity:0}20%{top:40px;left:360px;opacity:1}70%{top:130px;left:180px;opacity:1}to{top:160px;left:120px;opacity:0}}@-webkit-keyframes cloudRight-data-v-c095f994{0%{top:100px;left:500px;opacity:0}20%{top:120px;left:460px;opacity:1}80%{top:180px;left:340px;opacity:1}to{top:200px;left:300px;opacity:0}}@keyframes cloudRight-data-v-c095f994{0%{top:100px;left:500px;opacity:0}20%{top:120px;left:460px;opacity:1}80%{top:180px;left:340px;opacity:1}to{top:200px;left:300px;opacity:0}}.wscn-http404 .bullshit[data-v-c095f994]{position:relative;float:left;width:300px;padding:30px 0;overflow:hidden}.wscn-http404 .bullshit__oops[data-v-c095f994]{font-size:32px;line-height:40px;color:#1482f0;margin-bottom:20px;-webkit-animation-fill-mode:forwards;animation-fill-mode:forwards}.wscn-http404 .bullshit__headline[data-v-c095f994],.wscn-http404 .bullshit__oops[data-v-c095f994]{font-weight:700;opacity:0;-webkit-animation-name:slideUp-data-v-c095f994;animation-name:slideUp-data-v-c095f994;-webkit-animation-duration:.5s;animation-duration:.5s}.wscn-http404 .bullshit__headline[data-v-c095f994]{font-size:20px;line-height:24px;color:#222;margin-bottom:10px;-webkit-animation-delay:.1s;animation-delay:.1s;-webkit-animation-fill-mode:forwards;animation-fill-mode:forwards}.wscn-http404 .bullshit__info[data-v-c095f994]{font-size:13px;line-height:21px;color:grey;margin-bottom:30px;-webkit-animation-delay:.2s;animation-delay:.2s;-webkit-animation-fill-mode:forwards;animation-fill-mode:forwards}.wscn-http404 .bullshit__info[data-v-c095f994],.wscn-http404 .bullshit__return-home[data-v-c095f994]{opacity:0;-webkit-animation-name:slideUp-data-v-c095f994;animation-name:slideUp-data-v-c095f994;-webkit-animation-duration:.5s;animation-duration:.5s}.wscn-http404 .bullshit__return-home[data-v-c095f994]{display:block;float:left;width:110px;height:36px;background:#1482f0;border-radius:100px;text-align:center;color:#fff;font-size:14px;line-height:36px;cursor:pointer;-webkit-animation-delay:.3s;animation-delay:.3s;-webkit-animation-fill-mode:forwards;animation-fill-mode:forwards}@-webkit-keyframes slideUp-data-v-c095f994{0%{-webkit-transform:translateY(60px);transform:translateY(60px);opacity:0}to{-webkit-transform:translateY(0);transform:translateY(0);opacity:1}}@keyframes slideUp-data-v-c095f994{0%{-webkit-transform:translateY(60px);transform:translateY(60px);opacity:0}to{-webkit-transform:translateY(0);transform:translateY(0);opacity:1}}

View File

@@ -1 +0,0 @@
.line[data-v-ae6e8604]{text-align:center}

View File

@@ -1 +0,0 @@
.line[data-v-250c9dcc]{text-align:center}

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -1 +0,0 @@
(window["webpackJsonp"]=window["webpackJsonp"]||[]).push([["chunk-1ef583f0"],{"7a8b":function(n,t,e){},b0a2:function(n,t,e){"use strict";e.r(t);var o=function(){var n=this,t=n.$createElement,e=n._self._c||t;return e("div",[e("el-form",{ref:"form",attrs:{model:n.form}},[e("div",{staticStyle:{"padding-left":"10px","padding-top":"20px"}},[e("el-form-item",[n._v("\n "+n._s(n.form.name)+"    \n "),e("el-button",{attrs:{type:"primary"},on:{click:n.onSubmit}},[n._v("修改")]),n._v(" "),e("el-button",{attrs:{type:"warning"},on:{click:n.onCancel}},[n._v("重置")]),n._v(" "),e("el-button",{attrs:{type:"info"},on:{click:n.onBack}},[n._v("返回")])],1)],1),n._v(" "),e("editor",{attrs:{lang:"properties",theme:"chrome",width:"100%",height:800},on:{init:n.editorInit},model:{value:n.form.content,callback:function(t){n.$set(n.form,"content",t)},expression:"form.content"}})],1)],1)},a=[],c=(e("7f7f"),e("f546")),r={components:{editor:e("7c9e")},data:function(){return{form:{id:null,name:"",content:""}}},created:function(){this.loadCanalConfig()},methods:{editorInit:function(){e("2099"),e("be9d"),e("2968"),e("e0e5"),e("bb36"),e("0329"),e("95b8"),e("6a21")},loadCanalConfig:function(){var n=this;Object(c["b"])(this.$route.query.id).then(function(t){var e=t.data;n.form.id=e.id,n.form.name=e.name+"/instance.propertios",n.form.content=e.content})},onSubmit:function(){var n=this;this.$confirm("修改Instance配置可能会导致重启,是否继续?","确定修改",{confirmButtonText:"确定",cancelButtonText:"取消",type:"warning"}).then(function(){Object(c["h"])(n.form).then(function(t){"success"===t.data?(n.$message({message:"修改成功",type:"success"}),n.loadCanalConfig()):n.$message({message:"修改失败",type:"error"})})})},onCancel:function(){this.loadCanalConfig()},onBack:function(){history.go(-1)}}},i=r,u=(e("d213"),e("2877")),f=Object(u["a"])(i,o,a,!1,null,"01afb045",null);t["default"]=f.exports},d213:function(n,t,e){"use strict";var o=e("7a8b"),a=e.n(o);a.a},f546:function(n,t,e){"use strict";e.d(t,"d",function(){return a}),e.d(t,"b",function(){return c}),e.d(t,"h",function(){return r}),e.d(t,"a",function(){return i}),e.d(t,"c",function(){return u}),e.d(t,"f",function(){return f}),e.d(t,"g",function(){return s}),e.d(t,"e",function(){return l});var o=e("b775");function a(n){return Object(o["a"])({url:"/canal/instances",method:"get",params:n})}function c(n){return Object(o["a"])({url:"/canal/instance/"+n,method:"get"})}function r(n){return Object(o["a"])({url:"/canal/instance",method:"put",data:n})}function i(n){return Object(o["a"])({url:"/canal/instance",method:"post",data:n})}function u(n){return Object(o["a"])({url:"/canal/instance/"+n,method:"delete"})}function f(n,t){return Object(o["a"])({url:"/canal/instance/start/"+n+"/"+t,method:"put"})}function s(n,t){return Object(o["a"])({url:"/canal/instance/stop/"+n+"/"+t,method:"put"})}function l(n,t){return Object(o["a"])({url:"/canal/instance/log/"+n+"/"+t,method:"get"})}}}]);

View File

@@ -1 +0,0 @@
(window["webpackJsonp"]=window["webpackJsonp"]||[]).push([["chunk-238a81e9"],{"26fc":function(t,s,a){t.exports=a.p+"static/img/404_cloud.0f4bc32b.png"},"8cdb":function(t,s,a){"use strict";a.r(s);var e=function(){var t=this,s=t.$createElement,a=t._self._c||s;return a("div",{staticClass:"wscn-http404-container"},[a("div",{staticClass:"wscn-http404"},[t._m(0),t._v(" "),a("div",{staticClass:"bullshit"},[a("div",{staticClass:"bullshit__oops"},[t._v("OOPS!")]),t._v(" "),t._m(1),t._v(" "),a("div",{staticClass:"bullshit__headline"},[t._v(t._s(t.message))]),t._v(" "),a("div",{staticClass:"bullshit__info"},[t._v("Please check that the URL you entered is correct, or click the button below to return to the homepage.")]),t._v(" "),a("a",{staticClass:"bullshit__return-home",attrs:{href:""}},[t._v("Back to home")])])])])},c=[function(){var t=this,s=t.$createElement,e=t._self._c||s;return e("div",{staticClass:"pic-404"},[e("img",{staticClass:"pic-404__parent",attrs:{src:a("a36b"),alt:"404"}}),t._v(" "),e("img",{staticClass:"pic-404__child left",attrs:{src:a("26fc"),alt:"404"}}),t._v(" "),e("img",{staticClass:"pic-404__child mid",attrs:{src:a("26fc"),alt:"404"}}),t._v(" "),e("img",{staticClass:"pic-404__child right",attrs:{src:a("26fc"),alt:"404"}})])},function(){var t=this,s=t.$createElement,a=t._self._c||s;return a("div",{staticClass:"bullshit__info"},[t._v("All rights reserved\n "),a("a",{staticStyle:{color:"#20a0ff"},attrs:{href:"https://wallstreetcn.com",target:"_blank"}},[t._v("wallstreetcn")])])}],i={name:"Page404",computed:{message:function(){return"The webmaster said that you can not enter this page..."}}},l=i,n=(a("97ef"),a("2877")),r=Object(n["a"])(l,e,c,!1,null,"c095f994",null);s["default"]=r.exports},"97ef":function(t,s,a){"use strict";var e=a("b51e"),c=a.n(e);c.a},a36b:function(t,s,a){t.exports=a.p+"static/img/404.a57b6f31.png"},b51e:function(t,s,a){}}]);

View File

@@ -1 +0,0 @@
(window["webpackJsonp"]=window["webpackJsonp"]||[]).push([["chunk-5560c396"],{1248:function(t,n,e){"use strict";e.r(n);var a=function(){var t=this,n=t.$createElement,e=t._self._c||n;return e("div",[e("el-form",{ref:"form",attrs:{model:t.form}},[e("div",{staticClass:"filter-container",staticStyle:{"padding-left":"10px","padding-top":"20px"}},[e("el-input",{staticClass:"filter-item",staticStyle:{width:"200px"},attrs:{placeholder:"Instance名称"},model:{value:t.form.name,callback:function(n){t.$set(t.form,"name",n)},expression:"form.name"}}),t._v("\n  \n "),e("el-button",{staticClass:"filter-item",attrs:{type:"primary"},on:{click:t.onSubmit}},[t._v("保存")]),t._v(" "),e("el-button",{staticClass:"filter-item",attrs:{type:"info"},on:{click:t.onBack}},[t._v("返回")])],1),t._v(" "),e("editor",{attrs:{lang:"properties",theme:"chrome",width:"100%",height:800},on:{init:t.editorInit},model:{value:t.form.content,callback:function(n){t.$set(t.form,"content",n)},expression:"form.content"}})],1)],1)},c=[],r=(e("7f7f"),e("f546")),o={components:{editor:e("7c9e")},data:function(){return{form:{name:"",content:""}}},created:function(){},methods:{editorInit:function(){e("2099"),e("be9d"),e("2968"),e("e0e5"),e("bb36"),e("0329"),e("95b8"),e("6a21")},onSubmit:function(){var t=this;""!==this.form.name?this.$confirm("确定新建","确定新建",{confirmButtonText:"确定",cancelButtonText:"取消",type:"warning"}).then(function(){Object(r["a"])(t.form).then(function(n){"success"===n.data?(t.$message({message:"新建成功",type:"success"}),t.$router.push("/canalServer/canalInstances")):t.$message({message:"新建失败",type:"error"})})}):this.$message({message:"请输入Instance名称",type:"error"})},onBack:function(){history.go(-1)}}},i=o,s=(e("d881"),e("2877")),u=Object(s["a"])(i,a,c,!1,null,"ae6e8604",null);n["default"]=u.exports},"193c":function(t,n,e){},d881:function(t,n,e){"use strict";var a=e("193c"),c=e.n(a);c.a},f546:function(t,n,e){"use strict";e.d(n,"d",function(){return c}),e.d(n,"b",function(){return r}),e.d(n,"h",function(){return o}),e.d(n,"a",function(){return i}),e.d(n,"c",function(){return s}),e.d(n,"f",function(){return u}),e.d(n,"g",function(){return f}),e.d(n,"e",function(){return l});var a=e("b775");function c(t){return Object(a["a"])({url:"/canal/instances",method:"get",params:t})}function r(t){return Object(a["a"])({url:"/canal/instance/"+t,method:"get"})}function o(t){return Object(a["a"])({url:"/canal/instance",method:"put",data:t})}function i(t){return Object(a["a"])({url:"/canal/instance",method:"post",data:t})}function s(t){return Object(a["a"])({url:"/canal/instance/"+t,method:"delete"})}function u(t,n){return Object(a["a"])({url:"/canal/instance/start/"+t+"/"+n,method:"put"})}function f(t,n){return Object(a["a"])({url:"/canal/instance/stop/"+t+"/"+n,method:"put"})}function l(t,n){return Object(a["a"])({url:"/canal/instance/log/"+t+"/"+n,method:"get"})}}}]);

View File

@@ -1 +0,0 @@
(window["webpackJsonp"]=window["webpackJsonp"]||[]).push([["chunk-6f6d6145"],{"09a8":function(n,t,e){"use strict";e.r(t);var o=function(){var n=this,t=n.$createElement,e=n._self._c||t;return e("div",[e("el-form",{ref:"form",attrs:{model:n.form}},[e("div",{staticStyle:{"padding-left":"10px","padding-top":"20px"}},[e("el-form-item",[n._v("\n "+n._s(n.form.name)+"    \n "),e("el-button",{attrs:{type:"primary"},on:{click:n.onSubmit}},[n._v("修改")]),n._v(" "),e("el-button",{attrs:{type:"warning"},on:{click:n.onCancel}},[n._v("重置")])],1)],1),n._v(" "),e("editor",{attrs:{lang:"properties",theme:"chrome",width:"100%",height:800},on:{init:n.editorInit},model:{value:n.form.content,callback:function(t){n.$set(n.form,"content",t)},expression:"form.content"}})],1)],1)},a=[],c=(e("7f7f"),e("b775"));function i(){return Object(c["a"])({url:"/canal/config",method:"get"})}function r(n){return Object(c["a"])({url:"/canal/config",method:"put",data:n})}var f={components:{editor:e("7c9e")},data:function(){return{form:{id:null,name:"",content:""}}},created:function(){this.loadCanalConfig()},methods:{editorInit:function(){e("2099"),e("be9d"),e("2968"),e("e0e5"),e("bb36"),e("0329"),e("95b8"),e("6a21")},loadCanalConfig:function(){var n=this;i().then(function(t){var e=t.data;n.form.id=e.id,n.form.name=e.name,n.form.content=e.content})},onSubmit:function(){var n=this;this.$confirm("修改Server主配置可能会导致Server重启,是否继续?","确定修改",{confirmButtonText:"确定",cancelButtonText:"取消",type:"warning"}).then(function(){r(n.form).then(function(t){"success"===t.data?(n.$message({message:"修改成功",type:"success"}),n.loadCanalConfig()):n.$message({message:"修改失败",type:"error"})})})},onCancel:function(){this.loadCanalConfig()}}},s=f,u=(e("fbdc"),e("2877")),l=Object(u["a"])(s,o,a,!1,null,"250c9dcc",null);t["default"]=l.exports},8351:function(n,t,e){},fbdc:function(n,t,e){"use strict";var o=e("8351"),a=e.n(o);a.a}}]);

View File

@@ -1 +0,0 @@
(window["webpackJsonp"]=window["webpackJsonp"]||[]).push([["chunk-7ec889b7"],{"11ec":function(t,n,e){},"7f19":function(t,n,e){"use strict";var c=e("11ec"),r=e.n(c);r.a},"7f84":function(t,n,e){"use strict";e.r(n);var c=function(){var t=this,n=t.$createElement,e=t._self._c||n;return e("div",[e("el-form",{ref:"form",attrs:{model:t.form}},[e("div",{staticStyle:{"padding-left":"10px","padding-right":"10px","padding-top":"20px"}},[e("el-form-item",[t._v("\n "+t._s(t.form.instance)+"    \n "),e("el-button",{attrs:{type:"primary"},on:{click:t.onRefresh}},[t._v("刷新")]),t._v(" "),e("el-button",{attrs:{type:"info"},on:{click:t.onBack}},[t._v("返回")])],1),t._v(" "),e("el-input",{attrs:{rows:35,readonly:"readonly",type:"textarea"},model:{value:t.form.desc,callback:function(n){t.$set(t.form,"desc",n)},expression:"form.desc"}})],1)])],1)},r=[],a=e("f546"),o={data:function(){return{form:{instance:"",desc:""}}},created:function(){this.fetchData()},methods:{fetchData:function(){var t=this;Object(a["e"])(this.$route.query.id,this.$route.query.nodeId).then(function(n){t.form.instance=n.data.instance+".log",t.form.desc=n.data.log})},onRefresh:function(){this.fetchData()},onBack:function(){history.go(-1)}}},u=o,i=(e("7f19"),e("2877")),f=Object(i["a"])(u,c,r,!1,null,"0b80198c",null);n["default"]=f.exports},f546:function(t,n,e){"use strict";e.d(n,"d",function(){return r}),e.d(n,"b",function(){return a}),e.d(n,"h",function(){return o}),e.d(n,"a",function(){return u}),e.d(n,"c",function(){return i}),e.d(n,"f",function(){return f}),e.d(n,"g",function(){return s}),e.d(n,"e",function(){return d});var c=e("b775");function r(t){return Object(c["a"])({url:"/canal/instances",method:"get",params:t})}function a(t){return Object(c["a"])({url:"/canal/instance/"+t,method:"get"})}function o(t){return Object(c["a"])({url:"/canal/instance",method:"put",data:t})}function u(t){return Object(c["a"])({url:"/canal/instance",method:"post",data:t})}function i(t){return Object(c["a"])({url:"/canal/instance/"+t,method:"delete"})}function f(t,n){return Object(c["a"])({url:"/canal/instance/start/"+t+"/"+n,method:"put"})}function s(t,n){return Object(c["a"])({url:"/canal/instance/stop/"+t+"/"+n,method:"put"})}function d(t,n){return Object(c["a"])({url:"/canal/instance/log/"+t+"/"+n,method:"get"})}}}]);

View File

@@ -72,7 +72,7 @@
<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-item @click.native="handleInstances(scope.row)">实例</el-dropdown-item>
<el-dropdown-item @click.native="handleInstances(scope.row)">详情</el-dropdown-item>
<el-dropdown-item @click.native="handleLog(scope.row)">日志</el-dropdown-item>
</el-dropdown-menu>
</el-dropdown>
@@ -113,7 +113,7 @@
<el-button type="primary" @click="dataOperation()">确定</el-button>
</div>
</el-dialog>
<el-dialog :visible.sync="dialogInstances" title="实例列表" width="800px">
<el-dialog :visible.sync="dialogInstances" title="instance 列表" width="800px">
<div class="filter-container">
<el-button class="filter-item" type="info" @click="activeInstances()">刷新列表</el-button>
</div>
@@ -310,6 +310,10 @@ export default {
}
},
handleConfig(row) {
if (row.canalCluster !== null) {
this.$message({ message: '集群模式Server不允许单独变更配置,请在集群配置变更', type: 'error' })
return
}
this.$router.push('/canalServer/nodeServer/config?serverId=' + row.id)
},
handleUpdate(row) {
@@ -374,7 +378,7 @@ export default {
this.$message({ message: '当前Server不是启动状态,无法停止', type: 'error' })
return
}
this.$confirm('停止 Server 服务', '确定停止Server服务', {
this.$confirm('停止Server服务会导致所有Instance都停止服务', '确定停止Server服务', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'

View File

@@ -16,6 +16,7 @@ public class ServerRunningData implements Serializable {
private static final long serialVersionUID = 92260481691855281L;
@Deprecated
private Long cid;
private String address;
private boolean active = true;
@@ -23,8 +24,7 @@ public class ServerRunningData implements Serializable {
public ServerRunningData(){
}
public ServerRunningData(Long cid, String address){
this.cid = cid;
public ServerRunningData(String address){
this.address = address;
}

View File

@@ -59,7 +59,6 @@ public class ServerRunningMonitor extends AbstractCanalLifeCycle {
}
if (!runningData.isActive() && isMine(runningData.getAddress())) { // 说明出现了主动释放的操作,并且本机之前是active
release = true;
releaseRunning();// 彻底释放mainstem
}
@@ -116,7 +115,7 @@ public class ServerRunningMonitor extends AbstractCanalLifeCycle {
if (zkClient != null) {
releaseRunning(); // 尝试一下release
} else {
processActiveExit(); // 没有zk,直接启动
processActiveExit(); // 没有zk,直接退出
}
}
@@ -185,8 +184,8 @@ public class ServerRunningMonitor extends AbstractCanalLifeCycle {
boolean result = isMine(activeData.getAddress());
if (!result) {
logger.warn("canal is running in node[{}] , but not in node[{}]",
activeData.getCid(),
serverData.getCid());
activeData.getAddress(),
serverData.getAddress());
}
return result;
} catch (ZkNoNodeException e) {
@@ -204,6 +203,7 @@ public class ServerRunningMonitor extends AbstractCanalLifeCycle {
private boolean releaseRunning() {
if (check()) {
release = true;
String path = ZookeeperPathUtils.getDestinationServerRunning(destination);
zkClient.delete(path);
mutex.set(false);

View File

@@ -16,6 +16,7 @@ import com.alibaba.otter.canal.common.zookeeper.ZookeeperPathUtils;
import com.alibaba.otter.canal.common.zookeeper.running.ServerRunningData;
import com.alibaba.otter.canal.common.zookeeper.running.ServerRunningListener;
import com.alibaba.otter.canal.common.zookeeper.running.ServerRunningMonitor;
@Ignore
public class ServerRunningTest extends AbstractZkTest {
@@ -38,7 +39,7 @@ public class ServerRunningTest extends AbstractZkTest {
@Test
public void testOneServer() {
final CountDownLatch countLatch = new CountDownLatch(2);
ServerRunningMonitor runningMonitor = buildServerRunning(countLatch, 1L, "127.0.0.1", 2088);
ServerRunningMonitor runningMonitor = buildServerRunning(countLatch, "127.0.0.1", 2088);
runningMonitor.start();
sleep(2000L);
runningMonitor.stop();
@@ -52,9 +53,9 @@ public class ServerRunningTest extends AbstractZkTest {
@Test
public void testMultiServer() {
final CountDownLatch countLatch = new CountDownLatch(30);
final ServerRunningMonitor runningMonitor1 = buildServerRunning(countLatch, 1L, "127.0.0.1", 2088);
final ServerRunningMonitor runningMonitor2 = buildServerRunning(countLatch, 2L, "127.0.0.1", 2089);
final ServerRunningMonitor runningMonitor3 = buildServerRunning(countLatch, 3L, "127.0.0.1", 2090);
final ServerRunningMonitor runningMonitor1 = buildServerRunning(countLatch, "127.0.0.1", 2088);
final ServerRunningMonitor runningMonitor2 = buildServerRunning(countLatch, "127.0.0.1", 2089);
final ServerRunningMonitor runningMonitor3 = buildServerRunning(countLatch, "127.0.0.1", 2090);
final ExecutorService executor = Executors.newFixedThreadPool(3);
executor.submit(new Runnable() {
@@ -110,29 +111,29 @@ public class ServerRunningTest extends AbstractZkTest {
sleep(30000L);
}
private ServerRunningMonitor buildServerRunning(final CountDownLatch countLatch, final Long cid, final String ip,
private ServerRunningMonitor buildServerRunning(final CountDownLatch countLatch, final final String ip,
final int port) {
ServerRunningData serverData = new ServerRunningData(cid, ip + ":" + port);
ServerRunningData serverData = new ServerRunningData(ip + ":" + port);
ServerRunningMonitor runningMonitor = new ServerRunningMonitor(serverData);
runningMonitor.setDestination(destination);
runningMonitor.setListener(new ServerRunningListener() {
public void processActiveEnter() {
System.out.println(String.format("cid:%s ip:%s:%s has start", cid, ip, port));
System.out.println(String.format("cid:%s ip:%s:%s has start", ip, port));
countLatch.countDown();
}
public void processActiveExit() {
System.out.println(String.format("cid:%s ip:%s:%s has stop", cid, ip, port));
System.out.println(String.format("cid:%s ip:%s:%s has stop", ip, port));
countLatch.countDown();
}
public void processStart() {
System.out.println(String.format("cid:%s ip:%s:%s processStart", cid, ip, port));
System.out.println(String.format("cid:%s ip:%s:%s processStart", ip, port));
}
public void processStop() {
System.out.println(String.format("cid:%s ip:%s:%s processStop", cid, ip, port));
System.out.println(String.format("cid:%s ip:%s:%s processStop", ip, port));
}
});

View File

@@ -7,7 +7,8 @@ if "%OS%" == "Windows_NT" set ENV_PATH=%~dp0%
set conf_dir=%ENV_PATH%\..\conf
set canal_conf=%conf_dir%\canal.properties
set logback_configurationFile=%conf_dir%\logback.xml
set canal_local_conf=%conf_dir%\canal_local.properties
@rem set logback_configurationFile=%conf_dir%\logback.xml
set CLASSPATH=%conf_dir%
set CLASSPATH=%conf_dir%\..\lib\*;%CLASSPATH%

View File

@@ -11,6 +11,7 @@ case "`uname`" in
esac
base=${bin_abs_path}/..
canal_conf=$base/conf/canal.properties
canal_local_conf=$base/conf/canal_local.properties
logback_configurationFile=$base/conf/logback.xml
export LANG=en_US.UTF-8
export BASE=$base
@@ -48,21 +49,29 @@ in
;;
1 )
var=$*
if [ -f $var ] ; then
canal_conf=$var
if [ "$var" = "local" ]; then
canal_conf=$canal_local_conf
else
echo "THE PARAMETER IS NOT CORRECT.PLEASE CHECK AGAIN."
exit
if [ -f $var ] ; then
canal_conf=$var
else
echo "THE PARAMETER IS NOT CORRECT.PLEASE CHECK AGAIN."
exit
fi
fi;;
2 )
var=$1
if [ -f $var ] ; then
canal_conf=$var
else
if [ "$1" = "debug" ]; then
DEBUG_PORT=$2
DEBUG_SUSPEND="n"
JAVA_DEBUG_OPT="-Xdebug -Xnoagent -Djava.compiler=NONE -Xrunjdwp:transport=dt_socket,address=$DEBUG_PORT,server=y,suspend=$DEBUG_SUSPEND"
if [ "$var" = "local" ]; then
canal_conf=$canal_local_conf
else
if [ -f $var ] ; then
canal_conf=$var
else
if [ "$1" = "debug" ]; then
DEBUG_PORT=$2
DEBUG_SUSPEND="n"
JAVA_DEBUG_OPT="-Xdebug -Xnoagent -Djava.compiler=NONE -Xrunjdwp:transport=dt_socket,address=$DEBUG_PORT,server=y,suspend=$DEBUG_SUSPEND"
fi
fi
fi;;
* )

View File

@@ -46,7 +46,7 @@ import com.google.common.collect.MigrateMap;
*/
public class CanalController {
private static final Logger logger = LoggerFactory.getLogger(CanalController.class);
private static final Logger logger = LoggerFactory.getLogger(CanalController.class);
private Long cid;
private String ip;
private String registerIp;
@@ -54,11 +54,10 @@ public class CanalController {
private int adminPort;
// 默认使用spring的方式载入
private Map<String, InstanceConfig> instanceConfigs;
private Map<String, String> runningInstances = new MapMaker().makeMap();
private InstanceConfig globalInstanceConfig;
private Map<String, PlainCanalConfigClient> managerClients;
// 监听instance config的变化
private boolean autoScan = true;
private boolean autoScan = true;
private InstanceAction defaultAction;
private Map<InstanceMode, InstanceConfigMonitor> instanceConfigMonitors;
private CanalServerWithEmbedded embededCanalServer;
@@ -153,91 +152,88 @@ public class CanalController {
zkclientx.createPersistent(ZookeeperPathUtils.CANAL_CLUSTER_ROOT_NODE, true);
}
final ServerRunningData serverData = new ServerRunningData(cid, registerIp + ":" + port);
final ServerRunningData serverData = new ServerRunningData(registerIp + ":" + port);
ServerRunningMonitors.setServerData(serverData);
ServerRunningMonitors
.setRunningMonitors(MigrateMap.makeComputingMap(new Function<String, ServerRunningMonitor>() {
ServerRunningMonitors.setRunningMonitors(MigrateMap.makeComputingMap(new Function<String, ServerRunningMonitor>() {
public ServerRunningMonitor apply(final String destination) {
ServerRunningMonitor runningMonitor = new ServerRunningMonitor(serverData);
runningMonitor.setDestination(destination);
runningMonitor.setListener(new ServerRunningListener() {
public ServerRunningMonitor apply(final String destination) {
ServerRunningMonitor runningMonitor = new ServerRunningMonitor(serverData);
runningMonitor.setDestination(destination);
runningMonitor.setListener(new ServerRunningListener() {
public void processActiveEnter() {
try {
MDC.put(CanalConstants.MDC_DESTINATION, String.valueOf(destination));
embededCanalServer.start(destination);
if (canalMQStarter != null) {
canalMQStarter.startDestination(destination);
}
runningInstances.put(destination, destination);
} finally {
MDC.remove(CanalConstants.MDC_DESTINATION);
public void processActiveEnter() {
try {
MDC.put(CanalConstants.MDC_DESTINATION, String.valueOf(destination));
embededCanalServer.start(destination);
if (canalMQStarter != null) {
canalMQStarter.startDestination(destination);
}
} finally {
MDC.remove(CanalConstants.MDC_DESTINATION);
}
public void processActiveExit() {
try {
MDC.put(CanalConstants.MDC_DESTINATION, String.valueOf(destination));
if (canalMQStarter != null) {
canalMQStarter.stopDestination(destination);
}
embededCanalServer.stop(destination);
runningInstances.remove(destination);
} finally {
MDC.remove(CanalConstants.MDC_DESTINATION);
}
}
public void processStart() {
try {
if (zkclientx != null) {
final String path = ZookeeperPathUtils.getDestinationClusterNode(destination,
registerIp + ":" + port);
initCid(path);
zkclientx.subscribeStateChanges(new IZkStateListener() {
public void handleStateChanged(KeeperState state) throws Exception {
}
public void handleNewSession() throws Exception {
initCid(path);
}
@Override
public void handleSessionEstablishmentError(Throwable error) throws Exception {
logger.error("failed to connect to zookeeper", error);
}
});
}
} finally {
MDC.remove(CanalConstants.MDC_DESTINATION);
}
}
public void processStop() {
try {
MDC.put(CanalConstants.MDC_DESTINATION, String.valueOf(destination));
if (zkclientx != null) {
final String path = ZookeeperPathUtils.getDestinationClusterNode(destination,
registerIp + ":" + port);
releaseCid(path);
}
} finally {
MDC.remove(CanalConstants.MDC_DESTINATION);
}
}
});
if (zkclientx != null) {
runningMonitor.setZkClient(zkclientx);
}
// 触发创建一下cid节点
runningMonitor.init();
return runningMonitor;
public void processActiveExit() {
try {
MDC.put(CanalConstants.MDC_DESTINATION, String.valueOf(destination));
if (canalMQStarter != null) {
canalMQStarter.stopDestination(destination);
}
embededCanalServer.stop(destination);
} finally {
MDC.remove(CanalConstants.MDC_DESTINATION);
}
}
public void processStart() {
try {
if (zkclientx != null) {
final String path = ZookeeperPathUtils.getDestinationClusterNode(destination,
registerIp + ":" + port);
initCid(path);
zkclientx.subscribeStateChanges(new IZkStateListener() {
public void handleStateChanged(KeeperState state) throws Exception {
}
public void handleNewSession() throws Exception {
initCid(path);
}
@Override
public void handleSessionEstablishmentError(Throwable error) throws Exception {
logger.error("failed to connect to zookeeper", error);
}
});
}
} finally {
MDC.remove(CanalConstants.MDC_DESTINATION);
}
}
public void processStop() {
try {
MDC.put(CanalConstants.MDC_DESTINATION, String.valueOf(destination));
if (zkclientx != null) {
final String path = ZookeeperPathUtils.getDestinationClusterNode(destination,
registerIp + ":" + port);
releaseCid(path);
}
} finally {
MDC.remove(CanalConstants.MDC_DESTINATION);
}
}
});
if (zkclientx != null) {
runningMonitor.setZkClient(zkclientx);
}
}));
// 触发创建一下cid节点
runningMonitor.init();
return runningMonitor;
}
}));
// 初始化monitor机制
autoScan = BooleanUtils.toBoolean(getProperty(properties, CanalConstants.CANAL_AUTO_SCAN));
@@ -284,13 +280,26 @@ public class CanalController {
logger.info("auto notify reload {} successful.", destination);
}
@Override
public void release(String destination) {
// 此处的release,代表强制释放,主要针对HA机制释放运行,让给其他机器抢占
InstanceConfig config = instanceConfigs.get(destination);
if (config != null) {
ServerRunningMonitor runningMonitor = ServerRunningMonitors.getRunningMonitor(destination);
if (runningMonitor.isStart()) {
runningMonitor.release();
}
}
logger.info("auto notify release {} successful.", destination);
}
};
instanceConfigMonitors = MigrateMap.makeComputingMap(new Function<InstanceMode, InstanceConfigMonitor>() {
public InstanceConfigMonitor apply(InstanceMode mode) {
int scanInterval = Integer
.valueOf(getProperty(properties, CanalConstants.CANAL_AUTO_SCAN_INTERVAL));
int scanInterval = Integer.valueOf(getProperty(properties, CanalConstants.CANAL_AUTO_SCAN_INTERVAL));
if (mode.isSpring()) {
SpringInstanceConfigMonitor monitor = new SpringInstanceConfigMonitor();
@@ -366,7 +375,7 @@ public class CanalController {
}
if (config.getMode().isManager()) {
PlainCanalInstanceGenerator instanceGenerator = new PlainCanalInstanceGenerator(properties);
PlainCanalInstanceGenerator instanceGenerator = new PlainCanalInstanceGenerator();
instanceGenerator.setCanalConfigClient(managerClients.get(config.getManagerAddress()));
instanceGenerator.setSpringXml(config.getSpringXml());
return instanceGenerator.generate(destination);
@@ -386,7 +395,7 @@ public class CanalController {
}
private PlainCanalConfigClient getManagerClient(String managerAddress) {
return new PlainCanalConfigClient(managerAddress, this.adminUser, this.adminPasswd, null, adminPort);
return new PlainCanalConfigClient(managerAddress, this.adminUser, this.adminPasswd, this.registerIp, adminPort);
}
private void initInstanceConfig(Properties properties) {
@@ -398,8 +407,7 @@ public class CanalController {
InstanceConfig oldConfig = instanceConfigs.put(destination, config);
if (oldConfig != null) {
logger
.warn("destination:{} old config:{} has replace by new config:{}", destination, oldConfig, config);
logger.warn("destination:{} old config:{} has replace by new config:{}", destination, oldConfig, config);
}
}
}
@@ -595,7 +603,4 @@ public class CanalController {
return instanceConfigs;
}
public Map<String, String> getRunningInstances() {
return runningInstances;
}
}

View File

@@ -11,6 +11,7 @@ import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.alibaba.otter.canal.common.utils.AddressUtils;
import com.alibaba.otter.canal.common.utils.NamedThreadFactory;
import com.alibaba.otter.canal.instance.manager.plain.PlainCanal;
import com.alibaba.otter.canal.instance.manager.plain.PlainCanalConfigClient;
@@ -27,7 +28,7 @@ public class CanalLauncher {
private static final Logger logger = LoggerFactory.getLogger(CanalLauncher.class);
public static final CountDownLatch runningLatch = new CountDownLatch(1);
private static ScheduledExecutorService executor = Executors.newScheduledThreadPool(1,
new NamedThreadFactory("canal-server-scan"));
new NamedThreadFactory("canal-server-scan"));
public static void main(String[] args) {
try {
@@ -49,55 +50,54 @@ public class CanalLauncher {
if (StringUtils.isNotEmpty(managerAddress)) {
String user = properties.getProperty(CanalConstants.CANAL_ADMIN_USER);
String passwd = properties.getProperty(CanalConstants.CANAL_ADMIN_PASSWD);
String adminPort = properties.getProperty(CanalConstants.CANAL_ADMIN_PORT, "11110");
String adminPort = properties.getProperty(CanalConstants.CANAL_ADMIN_PORT);
String registerIp = properties.getProperty(CanalConstants.CANAL_REGISTER_IP);
if (StringUtils.isEmpty(adminPort)) {
adminPort = "11110";
}
if (StringUtils.isEmpty(registerIp)) {
registerIp = AddressUtils.getHostIp();
}
final PlainCanalConfigClient configClient = new PlainCanalConfigClient(managerAddress,
user,
passwd,
"",
registerIp,
Integer.parseInt(adminPort));
PlainCanal canalConfig = configClient.findServer(null);
if (canalConfig != null) {
properties = canalConfig.getProperties();
// 用本地配置覆盖
properties.put(CanalConstants.CANAL_ADMIN_MANAGER, managerAddress);
properties.put(CanalConstants.CANAL_ADMIN_USER, user);
properties.put(CanalConstants.CANAL_ADMIN_PASSWD, passwd);
properties.put(CanalConstants.CANAL_ADMIN_PORT, adminPort);
int scanIntervalInSecond = Integer
.parseInt(properties.getProperty(CanalConstants.CANAL_AUTO_SCAN_INTERVAL, "5"));
executor.scheduleWithFixedDelay(new Runnable() {
private PlainCanal lastCanalConfig;
public void run() {
try {
if (lastCanalConfig == null) {
lastCanalConfig = configClient.findServer(null);
} else {
PlainCanal newCanalConfig = configClient.findServer(lastCanalConfig.getMd5());
if (newCanalConfig != null) {
// 远程配置canal.properties修改重新加载整个应用
canalStater.stop();
Properties properties1 = newCanalConfig.getProperties();
// 用本地配置覆盖
properties1.put(CanalConstants.CANAL_ADMIN_MANAGER, managerAddress);
properties1.put(CanalConstants.CANAL_ADMIN_USER, user);
properties1.put(CanalConstants.CANAL_ADMIN_PASSWD, passwd);
properties1.put(CanalConstants.CANAL_ADMIN_PORT, adminPort);
canalStater.setProperties(properties1);
canalStater.start();
lastCanalConfig = newCanalConfig;
}
}
} catch (Throwable e) {
logger.error("scan failed", e);
}
}
}, 0, scanIntervalInSecond, TimeUnit.SECONDS);
if (canalConfig == null) {
throw new IllegalArgumentException("managerAddress:" + managerAddress
+ " can't not found config for [" + registerIp + ":" + adminPort
+ "]");
}
properties = canalConfig.getProperties();
int scanIntervalInSecond = Integer.valueOf(properties.getProperty(CanalConstants.CANAL_AUTO_SCAN_INTERVAL,
"5"));
executor.scheduleWithFixedDelay(new Runnable() {
private PlainCanal lastCanalConfig;
public void run() {
try {
if (lastCanalConfig == null) {
lastCanalConfig = configClient.findServer(null);
} else {
PlainCanal newCanalConfig = configClient.findServer(lastCanalConfig.getMd5());
if (newCanalConfig != null) {
// 远程配置canal.properties修改重新加载整个应用
canalStater.stop();
canalStater.setProperties(newCanalConfig.getProperties());
canalStater.start();
lastCanalConfig = newCanalConfig;
}
}
} catch (Throwable e) {
logger.error("scan failed", e);
}
}
}, 0, scanIntervalInSecond, TimeUnit.SECONDS);
}
canalStater.setProperties(properties);
@@ -118,4 +118,5 @@ public class CanalLauncher {
}
});
}
}

View File

@@ -1,9 +1,5 @@
package com.alibaba.otter.canal.deployer;
import java.io.File;
import java.io.FileFilter;
import java.util.Arrays;
import java.util.List;
import java.util.Properties;
import org.apache.commons.lang.StringUtils;
@@ -17,9 +13,6 @@ import com.alibaba.otter.canal.kafka.CanalKafkaProducer;
import com.alibaba.otter.canal.rocketmq.CanalRocketMQProducer;
import com.alibaba.otter.canal.server.CanalMQStarter;
import com.alibaba.otter.canal.spi.CanalMQProducer;
import com.google.common.base.Function;
import com.google.common.base.Joiner;
import com.google.common.collect.Lists;
/**
* Canal server 启动类
@@ -76,40 +69,8 @@ public class CanalStater {
if (canalMQProducer != null) {
// disable netty
System.setProperty(CanalConstants.CANAL_WITHOUT_NETTY, "true");
String autoScan = CanalController.getProperty(properties, CanalConstants.CANAL_AUTO_SCAN);
// 设置为raw避免ByteString->Entry的二次解析
System.setProperty("canal.instance.memory.rawEntry", "false");
if ("true".equals(autoScan)) {
String rootDir = CanalController.getProperty(properties, CanalConstants.CANAL_CONF_DIR);
if (StringUtils.isEmpty(rootDir)) {
rootDir = "../conf";
}
File rootdir = new File(rootDir);
if (rootdir.exists()) {
File[] instanceDirs = rootdir.listFiles(new FileFilter() {
public boolean accept(File pathname) {
String filename = pathname.getName();
return pathname.isDirectory() && !"spring".equalsIgnoreCase(filename)
&& !"metrics".equalsIgnoreCase(filename);
}
});
if (instanceDirs != null && instanceDirs.length > 0) {
List<String> instances = Lists.transform(Arrays.asList(instanceDirs),
new Function<File, String>() {
@Override
public String apply(File instanceDir) {
return instanceDir.getName();
}
});
System.setProperty(CanalConstants.CANAL_DESTINATIONS, Joiner.on(",").join(instances));
}
}
} else {
String destinations = CanalController.getProperty(properties, CanalConstants.CANAL_DESTINATIONS);
System.setProperty(CanalConstants.CANAL_DESTINATIONS, destinations);
}
}
logger.info("## start the canal server.");
@@ -136,7 +97,8 @@ public class CanalStater {
if (canalMQProducer != null) {
canalMQStarter = new CanalMQStarter(canalMQProducer);
MQProperties mqProperties = buildMQProperties(properties);
canalMQStarter.start(mqProperties);
String destinations = CanalController.getProperty(properties, CanalConstants.CANAL_DESTINATIONS);
canalMQStarter.start(mqProperties, destinations);
controller.setCanalMQStarter(canalMQStarter);
}

View File

@@ -2,6 +2,7 @@ package com.alibaba.otter.canal.deployer.admin;
import java.io.File;
import java.security.NoSuchAlgorithmException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Map;
@@ -14,7 +15,6 @@ import org.slf4j.LoggerFactory;
import com.alibaba.otter.canal.admin.CanalAdmin;
import com.alibaba.otter.canal.common.utils.FileUtils;
import com.alibaba.otter.canal.deployer.CanalController;
import com.alibaba.otter.canal.deployer.CanalStater;
import com.alibaba.otter.canal.deployer.InstanceConfig;
import com.alibaba.otter.canal.deployer.monitor.InstanceAction;
@@ -28,7 +28,7 @@ import com.google.common.base.Joiner;
/**
* 提供canal admin的管理操作
*
*
* @author agapple 2019年8月24日 下午11:39:01
* @since 1.1.4
*/
@@ -102,32 +102,18 @@ public class CanalAdminController implements CanalAdmin {
return false;
}
@Override
public String getInstances() {
try {
CanalController controller = canalStater.getController();
if (controller != null) {
Map<String, InstanceConfig> instanceConfigs = controller.getInstanceConfigs();
if (instanceConfigs != null) {
return Joiner.on(",").join(instanceConfigs.keySet());
}
}
} catch (Throwable e) {
logger.error(e.getMessage(), e);
}
return "";
}
@Override
public String getRunningInstances() {
try {
CanalController controller = canalStater.getController();
if (controller != null) {
Map<String, String> instanceConfigs = controller.getRunningInstances();
if (instanceConfigs != null) {
return Joiner.on(",").join(instanceConfigs.keySet());
Map<String, CanalInstance> instances = CanalServerWithEmbedded.instance().getCanalInstances();
List<String> runningInstances = new ArrayList<String>();
instances.forEach((destination, instance) -> {
if (instance.isStart()) {
runningInstances.add(destination);
}
}
});
return Joiner.on(",").join(runningInstances);
} catch (Throwable e) {
logger.error(e.getMessage(), e);
}
@@ -173,6 +159,20 @@ public class CanalAdminController implements CanalAdmin {
return false;
}
@Override
public boolean releaseInstance(String destination) {
try {
InstanceAction instanceAction = getInstanceAction(destination);
if (instanceAction != null) {
instanceAction.release(destination);
return true;
}
} catch (Throwable e) {
logger.error(e.getMessage(), e);
}
return false;
}
@Override
public boolean restartInstance(String destination) {
try {

View File

@@ -13,6 +13,11 @@ public interface InstanceAction {
*/
void start(String destination);
/**
* 主动释放destination运行
*/
void release(String destination);
/**
* 停止destination
*/

View File

@@ -1,7 +1,6 @@
#################################################
######### common argument #############
#################################################
canal.id = 1
# tcp bind ip
canal.ip =
# register ip to zookeeper
@@ -12,12 +11,6 @@ canal.metrics.pull.port = 11112
# canal.user = canal
# canal.passwd = E3619321C1A937C46A0D8BD1DAC39F93B27D4458
# canal admin config
#canal.admin.manager = 127.0.0.1:8089
canal.admin.port = 11110
canal.admin.user = admin
canal.admin.passwd = 4ACFE3202A5FF5CF467898FC58AAB1D615029441
canal.zkServers =
# flush data to zk
canal.zookeeper.flush.period = 1000

View File

@@ -0,0 +1,8 @@
# register ip
canal.register.ip =
# canal admin config
canal.admin.manager = 127.0.0.1:8089
canal.admin.port = 11110
canal.admin.user = admin
canal.admin.passwd = 4ACFE3202A5FF5CF467898FC58AAB1D615029441

View File

@@ -0,0 +1,139 @@
#################################################
######### common argument #############
#################################################
# tcp bind ip
canal.ip =
# register ip to zookeeper
canal.register.ip =
canal.port = 11111
canal.metrics.pull.port = 11112
# canal instance user/passwd
canal.user = canal
canal.passwd = E3619321C1A937C46A0D8BD1DAC39F93B27D4458
# canal admin config
canal.admin.manager = 127.0.0.1:8089
canal.admin.port = 11110
canal.admin.user = admin
canal.admin.passwd = 4ACFE3202A5FF5CF467898FC58AAB1D615029441
canal.zkServers =
# flush data to zk
canal.zookeeper.flush.period = 1000
canal.withoutNetty = false
# tcp, kafka, RocketMQ
canal.serverMode = tcp
# flush meta cursor/parse position to file
canal.file.data.dir = ${canal.conf.dir}
canal.file.flush.period = 1000
## memory store RingBuffer size, should be Math.pow(2,n)
canal.instance.memory.buffer.size = 16384
## memory store RingBuffer used memory unit size , default 1kb
canal.instance.memory.buffer.memunit = 1024
## meory store gets mode used MEMSIZE or ITEMSIZE
canal.instance.memory.batch.mode = MEMSIZE
canal.instance.memory.rawEntry = true
## detecing config
canal.instance.detecting.enable = false
#canal.instance.detecting.sql = insert into retl.xdual values(1,now()) on duplicate key update x=now()
canal.instance.detecting.sql = select 1
canal.instance.detecting.interval.time = 3
canal.instance.detecting.retry.threshold = 3
canal.instance.detecting.heartbeatHaEnable = false
# support maximum transaction size, more than the size of the transaction will be cut into multiple transactions delivery
canal.instance.transaction.size = 1024
# mysql fallback connected to new master should fallback times
canal.instance.fallbackIntervalInSeconds = 60
# network config
canal.instance.network.receiveBufferSize = 16384
canal.instance.network.sendBufferSize = 16384
canal.instance.network.soTimeout = 30
# binlog filter config
canal.instance.filter.druid.ddl = true
canal.instance.filter.query.dcl = false
canal.instance.filter.query.dml = false
canal.instance.filter.query.ddl = false
canal.instance.filter.table.error = false
canal.instance.filter.rows = false
canal.instance.filter.transaction.entry = false
# binlog format/image check
canal.instance.binlog.format = ROW,STATEMENT,MIXED
canal.instance.binlog.image = FULL,MINIMAL,NOBLOB
# binlog ddl isolation
canal.instance.get.ddl.isolation = false
# parallel parser config
canal.instance.parser.parallel = true
## concurrent thread number, default 60% available processors, suggest not to exceed Runtime.getRuntime().availableProcessors()
#canal.instance.parser.parallelThreadSize = 16
## disruptor ringbuffer size, must be power of 2
canal.instance.parser.parallelBufferSize = 256
# table meta tsdb info
canal.instance.tsdb.enable = true
canal.instance.tsdb.dir = ${canal.file.data.dir:../conf}/${canal.instance.destination:}
canal.instance.tsdb.url = jdbc:h2:${canal.instance.tsdb.dir}/h2;CACHE_SIZE=1000;MODE=MYSQL;
canal.instance.tsdb.dbUsername = canal
canal.instance.tsdb.dbPassword = canal
# dump snapshot interval, default 24 hour
canal.instance.tsdb.snapshot.interval = 24
# purge snapshot expire , default 360 hour(15 days)
canal.instance.tsdb.snapshot.expire = 360
# aliyun ak/sk , support rds/mq
canal.aliyun.accessKey =
canal.aliyun.secretKey =
#################################################
######### destinations #############
#################################################
canal.destinations =
# conf root dir
canal.conf.dir = ../conf
# auto scan instance dir add/remove and start/stop instance
canal.auto.scan = true
canal.auto.scan.interval = 5
canal.instance.tsdb.spring.xml = classpath:spring/tsdb/h2-tsdb.xml
#canal.instance.tsdb.spring.xml = classpath:spring/tsdb/mysql-tsdb.xml
canal.instance.global.mode = manager
canal.instance.global.lazy = false
canal.instance.global.manager.address = ${canal.admin.manager}
#canal.instance.global.spring.xml = classpath:spring/memory-instance.xml
canal.instance.global.spring.xml = classpath:spring/file-instance.xml
#canal.instance.global.spring.xml = classpath:spring/default-instance.xml
##################################################
######### MQ #############
##################################################
canal.mq.servers = 127.0.0.1:6667
canal.mq.retries = 0
canal.mq.batchSize = 16384
canal.mq.maxRequestSize = 1048576
canal.mq.lingerMs = 100
canal.mq.bufferMemory = 33554432
canal.mq.canalBatchSize = 50
canal.mq.canalGetTimeout = 100
canal.mq.flatMessage = true
canal.mq.compressionType = none
canal.mq.acks = all
#canal.mq.properties. =
canal.mq.producerGroup = test
# Set this value to "cloud", if you want open message trace feature in aliyun.
canal.mq.accessChannel = local
# aliyun mq namespace
#canal.mq.namespace =
##################################################
######### Kafka Kerberos Info #############
##################################################
canal.mq.kafka.kerberos.enable = false
canal.mq.kafka.kerberos.krb5FilePath = "../conf/kerberos/krb5.conf"
canal.mq.kafka.kerberos.jaasFilePath = "../conf/kerberos/jaas.conf"

View File

@@ -13,11 +13,9 @@ import com.alibaba.otter.canal.instance.manager.plain.PlainCanal;
import com.alibaba.otter.canal.instance.manager.plain.PlainCanalConfigClient;
import com.alibaba.otter.canal.instance.spring.SpringCanalInstanceGenerator;
import java.util.Properties;
/**
* 基于manager生成对应的{@linkplain CanalInstance}
*
*
* @author jianghang 2012-7-12 下午05:37:09
* @version 1.0.0
*/
@@ -28,11 +26,6 @@ public class PlainCanalInstanceGenerator implements CanalInstanceGenerator {
private PlainCanalConfigClient canalConfigClient;
private String defaultName = "instance";
private BeanFactory beanFactory;
private Properties canalConfig;
public PlainCanalInstanceGenerator(Properties canalConfig){
this.canalConfig = canalConfig;
}
public CanalInstance generate(String destination) {
synchronized (CanalInstanceGenerator.class) {
@@ -41,11 +34,8 @@ public class PlainCanalInstanceGenerator implements CanalInstanceGenerator {
if (canal == null) {
throw new CanalException("instance : " + destination + " config is not found");
}
Properties properties = canal.getProperties();
properties.putAll(canalConfig);
// 设置动态properties,替换掉本地properties
com.alibaba.otter.canal.instance.spring.support.PropertyPlaceholderConfigurer.propertiesLocal
.set(properties);
com.alibaba.otter.canal.instance.spring.support.PropertyPlaceholderConfigurer.propertiesLocal.set(canal.getProperties());
// 设置当前正在加载的通道,加载spring查找文件时会用到该变量
System.setProperty("canal.instance.destination", destination);
this.beanFactory = getBeanFactory(springXml);

View File

@@ -2,8 +2,6 @@ package com.alibaba.otter.canal.instance.manager.plain;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.nio.charset.StandardCharsets;
import java.security.NoSuchAlgorithmException;
import java.util.HashMap;
@@ -46,14 +44,10 @@ public class PlainCanalConfigClient extends AbstractCanalLifeCycle implements Ca
this.user = user;
this.passwd = passwd;
this.httpHelper = new HttpHelper();
if (StringUtils.isNotEmpty(localIp)) {
this.localIp = localIp;
if (StringUtils.isEmpty(localIp)) {
this.localIp = "127.0.0.1";// 本地测试用
} else {
try {
this.localIp = InetAddress.getLocalHost().getHostAddress();
} catch (UnknownHostException e) {
e.printStackTrace();
}
this.localIp = localIp;
}
this.adminPort = adminPort;
}

View File

@@ -2,7 +2,7 @@ package com.alibaba.otter.canal.admin;
/**
* Canal Admin动态管理接口
*
*
* @author agapple 2019年8月24日 下午9:45:49
* @since 1.1.4
*/
@@ -41,13 +41,6 @@ public interface CanalAdmin {
*/
boolean restart();
/**
* 获取所有当前节点下所有实例
*
* @return 实例信息
*/
String getInstances();
/**
* 获取所有当前节点下运行中的实例
*
@@ -57,7 +50,7 @@ public interface CanalAdmin {
/**
* 通过实例名检查
*
*
* @param destination
* @return
*/
@@ -79,6 +72,14 @@ public interface CanalAdmin {
*/
boolean stopInstance(String destination);
/**
* 通过实例名释放,主要针对cluster模式有效(通知当前主机释放instance运行交给其他人来抢占)
*
* @param destination 实例名
* @return 是否成功
*/
boolean releaseInstance(String destination);
/**
* 通过实例名重启实例
*
@@ -103,7 +104,7 @@ public interface CanalAdmin {
/**
* 获取Instance的机器日志列表
*
*
* @param destination
*/
String listInstanceLog(String destination);

View File

@@ -58,9 +58,6 @@ public class SessionHandler extends SimpleChannelHandler {
case "list":
message = canalAdmin.getRunningInstances();
break;
case "instances":
message = canalAdmin.getInstances();
break;
default:
byte[] errorBytes = AdminNettyUtils.errorPacket(301,
MessageFormatter.format("ServerAdmin action={} is unknown", action).getMessage());
@@ -83,6 +80,9 @@ public class SessionHandler extends SimpleChannelHandler {
case "stop":
message = canalAdmin.stopInstance(destination) ? "1" : "0";
break;
case "release":
message = canalAdmin.releaseInstance(destination) ? "1" : "0";
break;
case "restart":
message = canalAdmin.restartInstance(destination) ? "1" : "0";
break;
@@ -132,11 +132,9 @@ public class SessionHandler extends SimpleChannelHandler {
}
} catch (Throwable exception) {
byte[] errorBytes = AdminNettyUtils.errorPacket(400,
MessageFormatter
.format("something goes wrong with channel:{}, exception={}",
ctx.getChannel(),
ExceptionUtils.getStackTrace(exception))
.getMessage());
MessageFormatter.format("something goes wrong with channel:{}, exception={}",
ctx.getChannel(),
ExceptionUtils.getStackTrace(exception)).getMessage());
AdminNettyUtils.write(ctx.getChannel(), errorBytes);
}
}

View File

@@ -42,7 +42,7 @@ public class CanalMQStarter {
this.canalMQProducer = canalMQProducer;
}
public synchronized void start(MQProperties properties) {
public synchronized void start(MQProperties properties, String destinations) {
try {
if (running) {
return;
@@ -60,8 +60,8 @@ public class CanalMQStarter {
executorService = Executors.newCachedThreadPool();
logger.info("## start the MQ workers.");
String[] destinations = StringUtils.split(System.getProperty("canal.destinations"), ",");
for (String destination : destinations) {
String[] dsts = StringUtils.split(destinations, ",");
for (String destination : dsts) {
destination = destination.trim();
CanalMQRunnable canalMQRunnable = new CanalMQRunnable(destination);
canalMQWorks.put(destination, canalMQRunnable);