package com.sohu.tv.builder;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.sohu.tv.cachecloud.client.basic.heartbeat.ClientStatusEnum;
import com.sohu.tv.cachecloud.client.basic.heartbeat.HeartbeatInfo;
import com.sohu.tv.cachecloud.client.basic.util.ConstUtils;
import com.sohu.tv.cachecloud.client.basic.util.HttpUtils;
import com.sohu.tv.cachecloud.client.jedis.stat.ClientDataCollectReportExecutor;
import org.apache.commons.pool2.impl.GenericObjectPoolConfig;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import redis.clients.jedis.HostAndPort;
import redis.clients.jedis.JedisCluster;
import redis.clients.jedis.Protocol;
import java.io.IOException;
import java.util.HashSet;
import java.util.Random;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
/**
* redis cluster 客户端builder
* Created by yijunzhang on 14-7-27.
*/
public class RedisClusterBuilder {
private final Logger logger = LoggerFactory.getLogger(this.getClass());
/**
* 应用id
*/
private final long appId;
/**
* jedis对象池配置
*/
private GenericObjectPoolConfig jedisPoolConfig;
/**
* jedis集群对象
*/
private JedisCluster jedisCluster;
/**
* jedis连接超时(单位:毫秒)
*/
private int connectionTimeout = Protocol.DEFAULT_TIMEOUT;
/**
* jedis读写超时(单位:毫秒)
*/
private int soTimeout = Protocol.DEFAULT_TIMEOUT;
/**
* 节点定位重试次数:默认3次
*/
private int maxRedirections = 5;
/**
* 构建锁
*/
private final Lock lock = new ReentrantLock();
/**
* 构造函数package访问域,package外不能直接构造实例;
*
* @param appId
*/
RedisClusterBuilder(final long appId) {
this.appId = appId;
GenericObjectPoolConfig poolConfig = new GenericObjectPoolConfig();
poolConfig.setMaxTotal(GenericObjectPoolConfig.DEFAULT_MAX_TOTAL * 5);
poolConfig.setMaxIdle(GenericObjectPoolConfig.DEFAULT_MAX_IDLE * 2);
poolConfig.setMinIdle(GenericObjectPoolConfig.DEFAULT_MAX_IDLE);
//JedisPool.borrowObject最大等待时间
poolConfig.setMaxWaitMillis(1000L);
poolConfig.setJmxNamePrefix("jedis-pool");
poolConfig.setJmxEnabled(true);
this.jedisPoolConfig = poolConfig;
}
public JedisCluster build() {
if (jedisCluster == null) {
while (true) {
try {
lock.tryLock(10, TimeUnit.SECONDS);
if (jedisCluster != null) {
return jedisCluster;
}
String url = String.format(ConstUtils.REDIS_CLUSTER_URL, String.valueOf(appId));
String response = HttpUtils.doGet(url);
ObjectMapper objectMapper = new ObjectMapper();
HeartbeatInfo heartbeatInfo = null;
try {
heartbeatInfo = objectMapper.readValue(response, HeartbeatInfo.class);
} catch (IOException e) {
logger.error("remote build error, appId: {}", appId, e);
}
if (heartbeatInfo == null) {
continue;
}
/** 检查客户端版本 **/
if (heartbeatInfo.getStatus() == ClientStatusEnum.ERROR.getStatus()) {
throw new IllegalStateException(heartbeatInfo.getMessage());
} else if (heartbeatInfo.getStatus() == ClientStatusEnum.WARN.getStatus()) {
logger.warn(heartbeatInfo.getMessage());
} else {
logger.info(heartbeatInfo.getMessage());
}
Set<HostAndPort> nodeList = new HashSet<HostAndPort>();
//形如 ip1:port1,ip2:port2,ip3:port3
String nodeInfo = heartbeatInfo.getShardInfo();
//为了兼容,如果允许直接nodeInfo.split(" ")
nodeInfo = nodeInfo.replace(" ", ",");
String[] nodeArray = nodeInfo.split(",");
for (String node : nodeArray) {
String[] ipAndPort = node.split(":");
if (ipAndPort.length < 2) {
continue;
}
String ip = ipAndPort[0];
int port = Integer.parseInt(ipAndPort[1]);
nodeList.add(new HostAndPort(ip, port));
}
//收集上报数据
// ClientDataCollectReportExecutor.getInstance();
jedisCluster = new JedisCluster(nodeList, connectionTimeout, soTimeout, maxRedirections, jedisPoolConfig);
return jedisCluster;
} catch (Throwable e) {
logger.error(e.getMessage(), e);
} finally {
lock.unlock();
}
try {
TimeUnit.MILLISECONDS.sleep(200 + new Random().nextInt(1000));//活锁
} catch (InterruptedException e) {
logger.error(e.getMessage(), e);
}
}
} else {
return jedisCluster;
}
}
/**
* 设置配置
*
* @param jedisPoolConfig
* @return
*/
public RedisClusterBuilder setJedisPoolConfig(GenericObjectPoolConfig jedisPoolConfig) {
this.jedisPoolConfig = jedisPoolConfig;
return this;
}
/**
* 设置jedis连接超时时间
* @param connectionTimeout
*/
public RedisClusterBuilder setConnectionTimeout(int connectionTimeout) {
this.connectionTimeout = connectionTimeout;
return this;
}
/**
* 设置jedis读写超时时间
* @param soTimeout
*/
public RedisClusterBuilder setSoTimeout(int soTimeout) {
this.soTimeout = soTimeout;
return this;
}
/**
* 节点定位重试次数:默认5次
*/
public RedisClusterBuilder setMaxRedirections(final int maxRedirections) {
this.maxRedirections = maxRedirections;
return this;
}
}