package com.sohu.tv.builder;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.sohu.tv.cachecloud.client.basic.heartbeat.ClientStatusEnum;
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.JedisSentinelPool;
import redis.clients.jedis.Protocol;
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 sentinel 客户端的builder
* Created by yijunzhang on 14-9-11.
*/
public class RedisSentinelBuilder {
private static Logger logger = LoggerFactory.getLogger(RedisSentinelBuilder.class);
/**
* 应用id
*/
private final long appId;
/**
* jedis对象池配置
*/
private GenericObjectPoolConfig poolConfig;
/**
* jedis连接超时(单位:毫秒)
*/
private int connectionTimeout = Protocol.DEFAULT_TIMEOUT;
/**
* jedis读写超时(单位:毫秒)
*/
private int soTimeout = Protocol.DEFAULT_TIMEOUT;
/**
* jedis sentinel连接池
*/
private volatile JedisSentinelPool sentinelPool;
/**
* 构建锁
*/
private static final Lock LOCK = new ReentrantLock();
/**
* 构造函数package访问域,package外不能直接构造实例;
*
* @param appId
*/
RedisSentinelBuilder(final long appId) {
this.appId = appId;
poolConfig = new GenericObjectPoolConfig();
poolConfig.setMaxTotal(GenericObjectPoolConfig.DEFAULT_MAX_TOTAL * 3);
poolConfig.setMaxIdle(GenericObjectPoolConfig.DEFAULT_MAX_IDLE * 2);
poolConfig.setMinIdle(GenericObjectPoolConfig.DEFAULT_MIN_IDLE);
poolConfig.setMaxWaitMillis(1000L);
poolConfig.setJmxNamePrefix("jedis-sentinel-pool");
poolConfig.setJmxEnabled(true);
}
public JedisSentinelPool build() {
if (sentinelPool == null) {
while (true) {
try {
LOCK.tryLock(10, TimeUnit.MILLISECONDS);
if (sentinelPool == null) {
/**
* http请求返回的结果是空的;
*/
String response = HttpUtils.doGet(String.format(ConstUtils.REDIS_SENTINEL_URL, appId));
if (response == null || response.isEmpty()) {
logger.warn("get response from remote server error, appId: {}, continue...", appId);
continue;
}
/**
* http请求返回的结果是无效的;
*/
ObjectMapper mapper = new ObjectMapper();
JsonNode heartbeatInfo = null;
try {
heartbeatInfo = mapper.readTree(response);
} catch (Exception e) {
logger.error("heartbeat error, appId: {}. continue...", appId, e);
}
if (heartbeatInfo == null) {
logger.error("get sentinel info for appId: {} error. continue...", appId);
continue;
}
/** 检查客户端版本 **/
if (heartbeatInfo.get("status").intValue() == ClientStatusEnum.ERROR.getStatus()) {
throw new IllegalStateException(heartbeatInfo.get("message").textValue());
} else if (heartbeatInfo.get("status").intValue() == ClientStatusEnum.WARN.getStatus()) {
logger.warn(heartbeatInfo.get("message").textValue());
} else {
logger.info(heartbeatInfo.get("message").textValue());
}
/**
* 有效的请求:取出masterName和sentinels,并创建JedisSentinelPool的实例;
*/
String masterName = heartbeatInfo.get("masterName").asText();
String sentinels = heartbeatInfo.get("sentinels").asText();
Set<String> sentinelSet = new HashSet<String>();
for (String sentinelStr : sentinels.split(" ")) {
String[] sentinelArr = sentinelStr.split(":");
if (sentinelArr.length == 2) {
sentinelSet.add(sentinelStr);
}
}
//收集上报数据
// ClientDataCollectReportExecutor.getInstance();
sentinelPool = new JedisSentinelPool(masterName, sentinelSet, poolConfig, connectionTimeout, soTimeout, null, Protocol.DEFAULT_DATABASE);
return sentinelPool;
}
} catch (Throwable e) {//容错
logger.error("error in build, appId: {}", appId, e);
} finally {
LOCK.unlock();
}
try {
TimeUnit.MILLISECONDS.sleep(200 + new Random().nextInt(1000));//活锁
} catch (InterruptedException e) {
logger.error(e.getMessage(), e);
}
}
}
return sentinelPool;
}
/**
* 设置配置参数
*
* @param poolConfig
* @return
*/
public RedisSentinelBuilder setPoolConfig(GenericObjectPoolConfig poolConfig) {
this.poolConfig = poolConfig;
return this;
}
/**
* 设置jedis连接超时时间
* @param connectionTimeout
*/
public RedisSentinelBuilder setConnectionTimeout(int connectionTimeout) {
this.connectionTimeout = connectionTimeout;
return this;
}
/**
* 设置jedis读写超时时间
* @param soTimeout
*/
public RedisSentinelBuilder setSoTimeout(int soTimeout) {
this.soTimeout = soTimeout;
return this;
}
}