CacheCloud平台提供了对redis集群的集中化管理、运维、监控等功能,但是CacheCloud是如何对机器进行监控和维护的呢?是
如何获得redis所在服务器的信息呢?
通过查看源代码,发现平台引入了SSH-2协议的工具包:ganymed-ssh2,这个工具包提供了java连接linux服务器的功能,通过这个工具包就可以在服务器上面执行应用逻辑
<dependency>
<groupId>ch.ethz.ganymed</groupId>
<artifactId>ganymed-ssh2</artifactId>
<version>build210</version>
</dependency>
CacheCloud中用于SSH-2连接的类主要包含了以下几个:
/**
* 通过回调执行命令
* @param ip
* @param port
* @param username
* @param password
* @param callback 可以使用Session执行多个命令
* @throws SSHException
*/
public Result execute(String ip, int port, String username, String password,
SSHCallback callback) throws SSHException{
Connection conn = null;
try {
conn = getConnection(ip, port, username, password);
return callback.call(new SSHSession(conn, ip+":"+port));
} catch (Exception e) {
throw new SSHException("SSH err: " + e.getMessage(), e);
} finally {
close(conn);
}
}
/**
* 执行命令并返回结果,可以执行多次
* @param cmd 执行命令
* @param lineProcessor 回调消息解析器
* @return 如果lineProcessor不为null,那么永远返回Result.true
*/
public Result executeCommand(String cmd, LineProcessor lineProcessor, int timoutMillis) {
Session session = null;
try {
//通过SSH连接打开session会话
session = conn.openSession();
return executeCommand(session, cmd, timoutMillis, lineProcessor);
} catch (Exception e) {
logger.error("execute ip:"+conn.getHostname()+" cmd:"+cmd, e);
return new Result(e);
} finally {
close(session);
}
}
public Result executeCommand(final Session session, final String cmd,
final int timoutMillis, final LineProcessor lineProcessor) throws Exception{
//通过线程池的方式异步执行linux命令
Future<Result> future = taskPool.submit(new Callable<Result>() {
public Result call() throws Exception {
//具体执行linux命令的代码
session.execCommand(cmd);
//如果客户端需要进行行处理,则直接进行回调
if(lineProcessor != null) {
processStream(session.getStdout(), lineProcessor);
} else {
//获取标准输出
String rst = getResult(session.getStdout());
if(rst != null) {
return new Result(true, rst);
}
//返回为null代表可能有异常,需要检测标准错误输出,以便记录日志
Result errResult = tryLogError(session.getStderr(), cmd);
if(errResult != null) {
return errResult;
}
}
return new Result(true, null);
}
});
Result rst = null;
try {
//当timeoutMillis毫秒还没有返回结果,则取消执行,否则返回结果,不过Future只能确定线程执行完,
//不能确定执行是否成功,因为Future只提供了isDone()方法确定线程执行完毕
rst = future.get(timoutMillis, TimeUnit.MILLISECONDS);
future.cancel(true);
} catch (TimeoutException e) {
logger.error("exec ip:{} {} timeout:{}", conn.getHostname(), cmd, timoutMillis);
throw new SSHException(e);
}
return rst;
}
/**
* Copy a set of local files to a remote directory, uses the specified mode when
* creating the file on the remote side.
* @param localFiles
* Path and name of local file.
* @param remoteFiles
* name of remote file.
* @param remoteTargetDirectory
* Remote target directory. Use an empty string to specify the default directory.
* @param mode
* a four digit string (e.g., 0644, see "man chmod", "man open")
* @throws IOException
*/
public Result scp(String[] localFiles, String[] remoteFiles, String remoteTargetDirectory, String mode) {
try {
//通过连接创建SCP客户端,使用put方法将文件上传到远程服务器
SCPClient client = conn.createSCPClient();
client.put(localFiles, remoteFiles, remoteTargetDirectory, mode);
return new Result(true);
} catch (Exception e) {
logger.error("scp local="+Arrays.toString(localFiles)+" to "+
remoteTargetDirectory+" remote="+Arrays.toString(remoteFiles)+" err", e);
return new Result(e);
}
}
/**
* 执行命令回调
*/
public interface SSHCallback{
/**
* 执行回调
* @param session
*/
Result call(SSHSession session);
}
/**
* 从流中直接解析数据
*/
public static interface LineProcessor{
/**
* 处理行
* @param line 内容
* @param lineNum 行号,从1开始
* @throws Exception
*/
void process(String line, int lineNum) throws Exception;
/**
* 所有的行处理完毕回调该方法
*/
void finish();
}
//默认的数据解析器
public static abstract class DefaultLineProcessor implements LineProcessor{
public void finish() {}
}
大致整理一下调用逻辑:
如果有不懂之处,欢迎关注微信公众号:代码小栈,期待为您解决更多难题