文章整理来源:Spring编程常见错误50例_spring_spring编程_bean_AOP_SpringCloud_SpringWeb_测试_事务_Data-极客时间
案例37:混用 Redis 有不同序列化方式的 Template
Redis 提供的两种 Template,一种 RedisTemplate,一种 stringRedisTemplate,用后者保存的数据,前者却获取不到
@SpringBootApplication
public class SpringdataApplication {
SpringdataApplication(RedisTemplate redisTemplate,
StringRedisTemplate stringRedisTemplate){
String key = "mykey";
stringRedisTemplate.opsForValue().set(key, "myvalue");
Object valueGotFromStringRedisTemplate = stringRedisTemplate.opsForValue().get(key);
System.out.println(valueGotFromStringRedisTemplate);
Object valueGotFromRedisTemplate = redisTemplate.opsForValue().get(key);
System.out.println(valueGotFromRedisTemplate);
}
public static void main(String[] args) {
SpringApplication.run(SpringdataApplication.class, args);
}
}
解析:RedisTemplate 默认使用的是 JDK 序列化,而 stringRedisTemplate 使用的是 StringRedisSerializer
解决:统一指定 Serializer
案例 38:不适用的默认配置
例如在一个依赖 Cassandra 的项目中,有时候在写入数据之后,并不能立马读到写入的数据
解析:Cassandra 存在很多默认的配置,其中一项很重要的配置是 Consistency,在 driver 中默认为 LOCAL_ONE
basic.request {
# The consistency level.
#
# Required: yes
# Modifiable at runtime: yes, the new value will be used for requests issued after the change.
# Overridable in a profile: yes
consistency = LOCAL_ONE
//省略其他非关键配置
}
LOCAL_ONE 适合于独立部署的 Cassandra ,但大多数 Cassandra 有多数据中心多节点,比较合适的是用 LOCAL_QURAM
解决:修改 Cassandra 的默认属性配置
@Override
protected SessionBuilderConfigurer getSessionBuilderConfigurer() {
return cqlSessionBuilder -> {
DefaultProgrammaticDriverConfigLoaderBuilder defaultProgrammaticDriverConfigLoaderBuilder = new DefaultProgrammaticDriverConfigLoaderBuilder();
driverConfigLoaderBuilderCustomizer().customize(defaultProgrammaticDriverConfigLoaderBuilder);
cqlSessionBuilder.withConfigLoader(defaultProgrammaticDriverConfigLoaderBuilder.build());
return cqlSessionBuilder;
};
}
@Bean
public DriverConfigLoaderBuilderCustomizer driverConfigLoaderBuilderCustomizer() {
return loaderBuilder -> loaderBuilder
.withString(REQUEST_CONSISTENCY, ConsistencyLevel.LOCAL_QUORUM.name())
}
案例39:继承 Config 注入冗余的 Bean
Spring Data Cassandra 在连接 Cassandra 之后,会获取 Cassandra 的 Metadata 信息,这个内存占用量是比较大的,因为它存储了数据的 Token Range 等信息。如下图所示,有 4 个占用 40 多 M 连接
解析:CassandraConfig 继承了 AbstractCassandraConfiguration ,在 AbstractCassandraConfiguration 已经定义了一个名为 cassandraSession 的 CqlSessionFactoryBean ,而 CassandraConfig 同样也定义了一个名为 session 的 CqlSessionFactoryBean ,从而导致容器中有两个 CqlSessionFactoryBean
@Configuration
@EnableCassandraRepositories
public class CassandraConfig extends AbstractCassandraConfiguration
@Bean
@Primary
public CqlSessionFactoryBean session() {
log.info("init session");
CqlSessionFactoryBean cqlSessionFactoryBean = new CqlSessionFactoryBean();
//省略其他非关键代码
return cqlSessionFactoryBean ;
}
//省略其他非关键代码
}
-----------------------------------------------
@Configuration
public abstract class AbstractSessionConfiguration implements BeanFactoryAware
@Bean
public CqlSessionFactoryBean cassandraSession() {
CqlSessionFactoryBean bean = new CqlSessionFactoryBean();
bean.setContactPoints(getContactPoints());
//省略其他非关键代码
return bean;
}
//省略其他非关键代码
}
同时, 使用 Spring Data Cassandra 会创建两个 Session,systemSession 和 session ,它们都会获取 metadata。具体可参考代码 CqlSessionFactoryBean#afterPropertiesSet
@Override
public void afterPropertiesSet() {
CqlSessionBuilder sessionBuilder = buildBuilder();
// system session 的创建
this.systemSession = buildSystemSession(sessionBuilder);
initializeCluster(this.systemSession);
// normal session 的创建
this.session = buildSession(sessionBuilder);
executeCql(getStartupScripts().stream(), this.session);
performSchemaAction();
this.systemSession.refreshSchema();
this.session.refreshSchema();
}
解决:修改 CassandraConfig 将 session() 改为 cassandraSession() ,覆盖掉父类创建 sessionBean