cachecloud源码参考 ssh登录 关键技术点之一就是搞定代码通过ssh同服务器交互。
前置条件
redis机器放开cachecloud代码所在服务器到redis部署机器的ssh登录。默认开放端口22
所有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里
最后组合起来的list,写到服务器上的conf文件
创建配置文件 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
用java的socket直接连接