package com.sohu.cache.machine;
import com.sohu.cache.constant.EmptyObjectConstant;
import com.sohu.cache.constant.SymbolConstant;
import com.sohu.cache.exception.SSHException;
import com.sohu.cache.ssh.SSHUtil;
import com.sohu.cache.util.ConstUtils;
import com.google.common.util.concurrent.AtomicLongMap;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang.math.NumberUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* 生成一个redis可用端口
*
* @author: lingguo
* @time: 2014/8/25 20:57
*/
public class PortGenerator {
private static Logger logger = LoggerFactory.getLogger(PortGenerator.class);
/**
* redis port常量
*/
private static final Integer REDIS_START_PORT = 6379;
private static AtomicLongMap<String> redisPortHolder = AtomicLongMap.create();
/**
* 返回一个redis的可用端口:
* - 1. 通过shell查询redis当前已用的最大port;
* - 2. 为什么同步:防止多线程访问时获取到同样的端口;
* - 3. 为什么还用原子计数:连续两次调用时,如果进程还没启动,则拿到的仍然是相同的端口;
*
* @param ip
* @return
*/
public static synchronized Integer getRedisPort(final String ip) {
if (redisPortHolder.get(ip) == 0L) {
redisPortHolder.put(ip, REDIS_START_PORT);
}
String maxPortStr = "";
try {
int sshPort = SSHUtil.getSshPort(ip);
maxPortStr = getMaxPortStr(ip, sshPort);
} catch (SSHException e) {
logger.error("cannot get max port of redis by ssh, ip: {}", ip, e);
}
if (StringUtils.isBlank(maxPortStr) || !StringUtils.isNumeric(maxPortStr)) {
logger.warn("{} the max port of redis is invalid, maxPortStr: {}", ip, maxPortStr);
return new Long(redisPortHolder.getAndIncrement(ip)).intValue();
}
int availablePort = Integer.valueOf(maxPortStr) + 1;
// 兼容连续调用的情况
if (availablePort < redisPortHolder.get(ip)) {
availablePort = new Long(redisPortHolder.getAndIncrement(ip)).intValue();
} else { // 正常情况,以及兼容系统重启和当前端口不可用的情形
redisPortHolder.put(ip, availablePort + 1);
}
try {
while (SSHUtil.isPortUsed(ip, availablePort)) {
availablePort++;
}
} catch (SSHException e) {
logger.error("check port error, ip: {}, port: {}", ip, availablePort, e);
}
redisPortHolder.put(ip, availablePort+1);
return availablePort;
}
@Deprecated
public static String getMaxPortStrOld(String ip, int sshPort) throws SSHException {
String redisPidCmd = "ps -ef | grep redis | grep -v 'grep' | awk -F '*:' '{print $2}' " +
" | awk -F ' ' '{print $1}' | sort -r | head -1";
return SSHUtil.execute(ip, sshPort, ConstUtils.USERNAME, ConstUtils.PASSWORD, redisPidCmd);
}
/**
* 直接解析ps -ef | grep redis | grep -v 'grep'
* @param ip
* @param sshPort
* @return
* @throws SSHException
*/
public static String getMaxPortStr(String ip, int sshPort) throws SSHException {
String redisPidCmd = "ps -ef | grep redis | grep -v 'grep'";
String redisProcessStr = SSHUtil.execute(ip, sshPort, ConstUtils.USERNAME, ConstUtils.PASSWORD, redisPidCmd);
if (StringUtils.isBlank(redisProcessStr)) {
return EmptyObjectConstant.EMPTY_STRING;
}
int maxPort = 0;
String[] lines = redisProcessStr.split(SymbolConstant.ENTER);
for (String line : lines) {
if (StringUtils.isBlank(line)) {
continue;
}
int redisServerIndex = line.indexOf("redis-server");
int redisSentinelIndex = line.indexOf("redis-sentinel");
if (redisServerIndex >= 0) {
line = line.substring(redisServerIndex);
}
if (redisSentinelIndex >= 0) {
line = line.substring(redisSentinelIndex);
}
if (redisServerIndex < 0 && redisSentinelIndex < 0) {
continue;
}
String[] items = line.split(SymbolConstant.SPACE);
if (items.length >= 2) {
String hostPort = items[1];
if (StringUtils.isBlank(hostPort)) {
continue;
}
String[] hostPortArr = hostPort.split(SymbolConstant.COLON);
if (hostPortArr.length != 2) {
continue;
}
String portStr = hostPortArr[1];
if (!NumberUtils.isDigits(portStr)) {
continue;
}
int port = NumberUtils.toInt(portStr);
if (port > maxPort) {
maxPort = port;
}
}
}
return maxPort == 0 ? EmptyObjectConstant.EMPTY_STRING : String.valueOf(maxPort);
}
}