hello云胜

技术与生活

0%

cachecloud源码参考

ssh登录

关键技术点之一就是搞定代码通过ssh同服务器交互。

前置条件

  1. redis机器放开cachecloud代码所在服务器到redis部署机器的ssh登录。默认开放端口22

  2. 所有redis机器配置统一的用户名和密码。并且建议密码永不过期。

    数据库表system_config

SSHUtil

统一的工具类。

com.sohu.cache.ssh.SSHUtil#execute(java.lang.String, int, java.lang.String, java.lang.String, java.lang.String)

通过SSHUtil执行命令

com.sohu.cache.ssh.SSHTemplate#execute(java.lang.String, int, java.lang.String, java.lang.String, com.sohu.cache.ssh.SSHTemplate.SSHCallback)

先建立连接

com.sohu.cache.ssh.SSHTemplate#getConnection

这就到了最后的依赖

1
2
3
4
5
<dependency>
<groupId>ch.ethz.ganymed</groupId>
<artifactId>ganymed-ssh2</artifactId>
<version>build210</version>
</dependency>

执行cmd

com.sohu.cache.ssh.SSHTemplate.SSHSession#executeCommand(ch.ethz.ssh2.Session, java.lang.String, int, com.sohu.cache.ssh.SSHTemplate.LineProcessor)

redis部署

入口 com.sohu.cache.web.controller.AppManageController#doAddAppDeploy

传到service层com.sohu.cache.stats.app.impl.AppDeployCenterImpl#allocateResourceApp

重点看部署cluster模式com.sohu.cache.stats.app.impl.AppDeployCenterImpl#deployCluster

com.sohu.cache.redis.impl.RedisDeployCenterImpl#deployClusterInstance

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// 循环填写的部署信息。每一行是主节点:最大内存:备节点
for (RedisClusterNode node : clusterNodes) {
...
// 前面的是主节点
boolean isMasterRun = runInstance(appDesc, masterHost, masterPort, maxMemory, true);
...
// 再启动备节点
boolean isSlaveRun = runInstance(appDesc, slaveHost, slavePort, maxMemory, true);

}

// 前面的步骤只是启动了多个单独的redis实例,并没有配置为集群
// 配置为集群
isCluster = startCluster(appId, clusterMap);
// 收尾工作。保存信息到instance_info表
// 部署定时任务收集redis运行数据

1,获取可用端口

com.sohu.cache.machine.impl.MachineCenterImpl#getAvailablePort

MachineCenter是所有和服务器进行交互的接口

先通过shell到服务器查询服务器已用的最大port。

执行的命令是

1
ps -ef | grep redis | grep -v 'grep'

经过一些列的解析代码,拿到当前服务器上redis的最大端口号。

(从这里我们也可以看到,redis的机器上最好只部署redis,不要搞上其他的服务)

再去数据库表,验证这个port确实从来没有用过。

2,启动实例

com.sohu.cache.redis.impl.RedisDeployCenterImpl#runInstance

组装redis的config配置

com.sohu.cache.redis.impl.RedisDeployCenterImpl#handleCommonConfig

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
/**
* 获取redis 基础配置
*
* @param port
* @param maxMemory
* @return
*/
public List<String> handleCommonConfig(int port, int maxMemory) {
List<String> configs = null;
try {
configs = redisConfigTemplateService.handleCommonConfig(port, maxMemory);
} catch (Exception e) {
logger.error(e.getMessage(), e);
}
if (CollectionUtils.isEmpty(configs)) {
configs = redisConfigTemplateService.handleCommonDefaultConfig(port, maxMemory);
}
return configs;
}

组装的是一个String列表

默认的配置项是保存在数据库表instance_config里

image-20220105153533957

最后组合起来的list,写到服务器上的conf文件

image-20220105160904507

创建配置文件

com.sohu.cache.machine.impl.MachineCenterImpl#createRemoteFile

先将config配置写到本地的/tmp/cachecloud/redis-cluster-xxxx.conf

然后通过scp将本地的配置文件推到redis服务器上

1
SSHUtil.scpFileToRemote(host, localAbsolutePath, MachineProtocol.CONF_DIR);

组装的redis的启动命令

1
redis-server %s > " + MachineProtocol.LOG_DIR + "redis-%d-%s.log 2>&1 &

启动实例

1
boolean isMasterShell = machineCenter.startProcessAtPort(host, port, runShell);

3.配置集群

这是重点

代码就是这个com.sohu.cache.redis.impl.RedisDeployCenterImpl#startCluster方法

1
2
3
4
5
6
7
8
9
10
11
12
// 先随便选一个redis实例,后面的cluster meet命令只发给一个redis就行了。只要一个redis有了全部redis的连接信息,通过gossip协议自动会组建成一个cluster
final Jedis jedis = new ArrayList<Jedis>(clusterMap.keySet()).get(0);
// 对redis实例循环执行ClusterMeet连接其他redis
boolean isMeet = clusterMeet(jedis, appId, master.getClient().getHost(), master.getClient().getPort());
// 分配slot
String response = masterJedis.clusterAddSlots(slotArr);
// 设置从节点
// 先获取主节点的nodeID,通过cluster nodes命令,取出myself
final String nodeId = getClusterNodeId(masterJedis);
// 再设置为备节点cluster replicate命令
response = slaveJedis.clusterReplicate(nodeId);

clusterMeet实际就是发送cluster meet命令。CLUSTER MEET命令被用来连接不同的开启集群支持的 Redis 节点,以进入集群。

分配slot用的是cluster addslots命令,将16384个slot分给对应的主节点

redis命令:https://redis.io/commands/cluster-addslots,注意版本

jedis客户端

通过查看源码发现,cachecloud提供的jedis客户端,完全是sohu自己实现的。不是常用的开源的那个jedis。

jedis封装了redis指令。

通过jedis客户端连接redis实例,并发送配置redis指令。

连接的代码redis.clients.jedis.Connection#connect

image-20220105175314592

用java的socket直接连接