掌柜大作战(2):京东Redis服务的使用

来源:互联网 发布:linux rsyslog配置 编辑:程序博客网 时间:2024/04/28 13:07
京东内部,封装了Redis,可以用来缓存关键数据,内部通常叫做“Jim”或“jimClient”。
对外的Redis缓存服务和具体用法,请参考京东云官网:
https://www.jcloud.com/products/redis

本篇简要介绍下,Jim在内部项目中的配置和使用方式。
1、Spring上下文spring-applicationContext.xml
<bean id="propertyConfigurer" class="PropertyPlaceholderConfigurer">
<property name="ignoreResourceNotFound" value="true" />
<property name="locations">
<list>
<value>classpath:redis.properties</value>
</list>
</property>
</bean>
<import resource="classpath:spring-redis.xml" />

2、Redis属性文件redis.properties
redis.configId=jim://abc123/def456
redis.token=jim:///abc123/def456

3、Reids相关bean配置spring-redis.xml
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd" default-autowire="byName">

<bean id="jimClient" class="JimClientFactoryBean">
<!-- 配置拥有集群的客户端配置ID(必选)-->
<property name="configId" value="${redis.configId}"/>
<!-- 配置拥有集群的客户端配置Token(必选)-->
<property name="token" value="${redis.token}"/>
</bean>

<!--redis统一封装类-->
<bean id="redisClient" class="com.jd.RedisClient"/>
<bean id="cacheUtil" class="CacheUtil"/>
</beans>

4、RedisClient
import com.jd.jim.cli.Cluster;
/**
* redis的封装类,redis实例的统一出口,
* 此类可以封装多个实例的客户端,保证接口的统一化
*/
public class RedisClient {
private Cluster jimClient; //jedis
public Cluster getClientInstance(){
return jimClient;
}
public void setJimClient(Cluster jimClient) {
this.jimClient = jimClient;
}
public Cluster getJimClient() {
return jimClient;
}
}

5、Cluster 底层实现

6、CacheUtil
缓存工具类,根据RedisClient 底层的Jim接口,提供项目需要的功能,一般会增加“日志监控”和“异常处理”功能。UmpProfiler是京东内部的“统一监控服务”。
public class CacheUtil {
private final static Log log = LogFactory.getLog(CacheUtil.class);
private RedisClient redisClient;

public void setRedisClient(RedisClient redisClient) {
this.redisClient = redisClient;
}

/**
* 获取读缓存开关
*/
public Boolean getReadCacheSwitch() {
String value = this.redisClient.getClientInstance().get(SwitchConstants.Test.READ_CACHE_SWITCH);
if (StringUtils.isEmpty(value) || value.equals("1")) {
return true;
}
return false;
}

/**
* 获取写缓存开关
*/
public Boolean getWriteCacheSwitch() {
String value = this.redisClient.getClientInstance().get(SwitchConstants.Test.WRITE_CACHE_SWITCH);
if (StringUtils.isEmpty(value) || value.equals("1")) {
return true;
}
return false;
}

/**
* 设置缓存
*/
public void setString(String key, String cacheContent) {
CallerInfo callerInfo = UmpProfiler.methodReg(UmpKeyConstants.METHOD.REDIS_STATUS_WRITE);
try {
if (StringUtils.isNotEmpty(key)) {
redisClient.getClientInstance().set(key, cacheContent);
UmpProfiler.methodRegEnd(callerInfo);
}
} catch (Exception e) {
log.error("#设置set---key=" + key + "-->>>>>", e);
UmpProfiler.funcError(callerInfo);
}
}
}

7、实际使用
a.先检查是否使用缓存。
b.如果使用,从缓存中查询值value。
c.如果value不为null,直接返回。
d.如果value为null,调用方法查询,然后缓存,保证下次直接从缓存中能查询到。

补充:代码中存在不严谨的地方,如果isUseCache 为false,不使用缓存get查询,
但是却使用cacheUtil.setString缓存数据。难道是为了方便调试?

@Service
public class UserServiceImpl implements UserService{
@Resource
private CacheUtil cacheUtil;
@Resource
private ConfigUtil configUtil ;
/**
* 检查是否存在用户
*/
public boolean isExistBpin(String pin) {
CallerInfo callerInfo = UmpProfiler.methodReg(UmpKeyConstants.UserService.IS_EXISTS_BPIN); //ump监控
Boolean result = false;
String keyBak = EasyUtils.addSperator(RedisConstants.User.IS_EXIST_BPIN_BAK , pin);
String value = null;
try {
String key = EasyUtils.addSperator(RedisConstants.User.IS_EXIST_BPIN , pin);
Boolean isUseCache = configUtil.getSwitch(ConfigConstants.User.USER_SERVICE_IS_EXIST_BPIN_SWITCH);
if (isUseCache){
value = cacheUtil.getString(key);
log.info("#isExistBpin读取缓存["+key+"="+value+"]");
}
if (StringUtils.isBlank(value)) {
log.info("#isExistBpin读取接口");
result = this.userRpc.isExistBpin(pin);
if (result != null) {
if (result) {//如果存在再进行缓存数据
value = String.valueOf(result);
Long timeout = configUtil.getLong(ConfigConstants.User.USER_SERVICE_IS_EXIST_BPIN_TIMEOUT, 10L);
cacheUtil.setStringEx(key, value, timeout, TimeUnit.MINUTES);
cacheUtil.setString(keyBak, value);
}
}
}
} catch (Exception e) {
log.error("#[" + pin + "]isExistBpin.error", e);
UmpProfiler.funcError(callerInfo);
} finally {
if (StringUtils.isBlank(value)) {
value = cacheUtil.getString(keyBak);
}
if (StringUtils.isNotBlank(value)) {
result = Boolean.parseBoolean(value);
}
UmpProfiler.methodRegEnd(callerInfo);
}
return result;
}
}

8、总结
很多项目都需要缓存服务,因此可以定义CacheUtil和set和get等接口,然后实现该接口。
接口的底层数据存储,可以使用Redis、Memcache、本地Map等。
如果是Redis,可以是本地安装1个Redis、京东内部的Redis私有云服务、京东云的Redis公有云服务。
如果是中小型创业公司,也可以选择阿里云的Redis缓存云。
具体到阿里内部,猜测下:内部开发有私有云和公有云(阿里云)2种选择。

9、思考
实际项目中,因为有了统一的Redis封装,开发者(软件工程师、程序员、码农)更加侧重应用开发、业务实现和API接口调用,对底层实现关注的不多。知道底层实现,可能有2个作用。
a、实际价值
偶尔会遇到一些问题,知道原理,可以快速定位问题,从而解决它。
b、面试
比较侧重技术的面试官,可能会问你底层原理。
如果啥都不懂,可能就GG了。
阅读全文
0 0
原创粉丝点击