1. Trampling Records in Spring Use
- Spring uses multiple data sources through annotations
Pit: @Autowire is automatically injected by byType, while @Resource is automatically injected by byName by default, @Primary is the preferred choice.
For example, in a project there are two Redis sources, dataRedisTemplate and redisTemplate, respectively.
Redis Bean1: dataRedisTemplate, clusterNodes is ${data-redis.cluster.nodes}
@Bean(name = "dataRedisTemplate") public RedisTemplate dataRedisTemplate() { RedisTemplate template = new RedisTemplate(); template.setConnectionFactory(sessionLettuceConnectionFactory); Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class); ObjectMapper om = new ObjectMapper(); om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY); om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL); jackson2JsonRedisSerializer.setObjectMapper(om); template.setKeySerializer(new StringRedisSerializer()); template.setValueSerializer(jackson2JsonRedisSerializer); template.setHashKeySerializer(jackson2JsonRedisSerializer); template.setHashValueSerializer(new StringRedisSerializer()); template.afterPropertiesSet(); return template; } // factory @Resource @Qualifier(value = "dataLettuceConnectionFactory") private RedisConnectionFactory dataLettuceConnectionFactory; // clusterNodes @Value("${spring.data-redis.cluster.nodes}") private String clusterNodes;
Redis Bean2: redisTemplate, cluster Nodes: ${redis.cluster.nodes}
@Primary @Bean(name = "redisTemplate") public RedisTemplate<String, Object> redisTemplate() { RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>(); redisTemplate.setConnectionFactory(lettuceConnectionFactory); redisTemplate.setKeySerializer(new StringRedisSerializer()); Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class); ObjectMapper objectMapper = new ObjectMapper(); objectMapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY); objectMapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL); jackson2JsonRedisSerializer.setObjectMapper(objectMapper); redisTemplate.setValueSerializer(jackson2JsonRedisSerializer); redisTemplate.setHashKeySerializer(new StringRedisSerializer()); redisTemplate.setHashValueSerializer(new StringRedisSerializer()); redisTemplate.afterPropertiesSet(); return redisTemplate; } // factory @Resource @Qualifier(value = "lettuceConnectionFactory") private RedisConnectionFactory lettuceConnectionFactory; // clusterNodes @Value("${spring.redis.cluster.nodes}") private String clusterNodes;
In an application, I put data into Redis corresponding to "Redis Bean1: data Redis Template", so I use it in the following way:
@Autowired private RedisTemplate dataRedisTemplate; // Obtain data according to key Object obj = dataRedisTemplate.opsForValue().get(key);
In fact, Redis Bean2: redisTemplate is used.
Solution 1: Replace @Autowire with @Resource annotation. As follows:
@Autowired private RedisTemplate dataRedisTemplate; // Replace @Autowire with @Resource @Resource private RedisTemplate dataRedisTemplate;
@ The biggest difference between Autowire and @Resource is that @Autowire is automatically injected by byType, while @Resource is automatically injected by byName by default.
A note @Primary should also be noted here. The official explanation is as follows:
Indicates that a bean should be given preference when multiple candidates are qualified to autowire a single-valued dependency. If exactly one 'primary' bean exists among the candidates, it will be the autowired value.
@Primary Priority scheme, annotated implementation, priority injection
Usually @Autowired is injected by byType, but when there are multiple implementation classes, byType is no longer the only way to inject, but it needs to be injected by byName, which by default is based on the variable name.
That is to say, if you don't add @Primary to redisTemplate(), there's no problem because @Autowire will be injected through byName when there are multiple implementations, but as mentioned above, Bean redisTemplate will be preferred because of the @Primary,@Autowire annotation.
Another solution is to add @Qualifier (value = data RedisTemplate), as follows:
@Autowired private RedisTemplate dataRedisTemplate; // Replace: Add @Qualifier (value = data RedisTemplate) @Autowired @Qualifier(value = "dataRedisTemplate") private RedisTemplate dataRedisTemplate;
- Spring transaction @Transactional failure problem
Pit: If other methods in the same class without @Transactional annotations call methods with @Transactional annotations internally, transactions of methods with @Transactional annotations are ignored and no rollback occurs.
FooService.class
public interface FooService { void insertRecord(); void insertThenRollback() throws Exception; void invokeInsertThenRollback() throws Exception; void invokeInsertThenRollbackTwo() throws Exception; }
FooServiceImpl.class
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.stereotype.Component; import org.springframework.transaction.annotation.Transactional; @Component public class FooServiceImpl implements FooService { @Autowired private JdbcTemplate jdbcTemplate; @Autowired private FooService fooService; @Override @Transactional public void insertRecord() { jdbcTemplate.execute("INSERT INTO FOO (BAR) VALUES ('AAA')"); } @Override @Transactional(rollbackFor = Exception.class) public void insertThenRollback() throws Exception { jdbcTemplate.execute("INSERT INTO FOO (BAR) VALUES ('BBB')"); throw new Exception(); } @Override public void invokeInsertThenRollback() throws Exception { insertThenRollback(); } @Override public void invokeInsertThenRollbackTwo() throws Exception { fooService.insertThenRollback(); } }
implement
import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.CommandLineRunner; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.context.annotation.AdviceMode; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.transaction.annotation.EnableTransactionManagement; @SpringBootApplication @EnableTransactionManagement(mode = AdviceMode.PROXY) @Slf4j public class DeclarativeTransactionDemoApplication implements CommandLineRunner { @Autowired private FooService fooService; @Autowired private JdbcTemplate jdbcTemplate; public static void main(String[] args) { SpringApplication.run(DeclarativeTransactionDemoApplication.class, args); } @Override public void run(String... args) throws Exception { // Markup 1: Output AAA 1 fooService.insertRecord(); log.info("AAA {}",jdbcTemplate.queryForObject("SELECT COUNT(*) FROM FOO WHERE BAR='AAA'", Long.class)); // Markup 2: Output BBB 0, transaction takes effect try { fooService.insertThenRollback(); } catch (Exception e) { log.info("BBB {}",jdbcTemplate.queryForObject("SELECT COUNT(*) FROM FOO WHERE BAR='BBB'", Long.class)); } // Markup 3: Output BBB 1, transaction not valid // This place is the easiest place to step on the pit!!! * * * try { fooService.invokeInsertThenRollback(); } catch (Exception e) { log.info("BBB {}",jdbcTemplate.queryForObject("SELECT COUNT(*) FROM FOO WHERE BAR='BBB'", Long.class)); } // Markup 4: Output BBB 1, transaction takes effect (if the code of Markup 3 is commented out, output BBB 0) // *** This is a way to avoid stepping on pits*** try { fooService.invokeInsertThenRollbackTwo(); } catch (Exception e) { log.info("BBB {}",jdbcTemplate.queryForObject("SELECT COUNT(*) FROM FOO WHERE BAR='BBB'", Long.class)); } } }
2. Records of trampling pits during the use of RocketMQ
-
Rocket opens the VIP channel by default, causing the 10909 failed problem
Pit: Rocket opens the VIP channel by default, and the VIP channel port is 10911-2 = 10909. If the Rocket server does not start port 10909, then report connect to <:10909> failed.
Solution: No VIP channel.
producer.setVipChannelEnabled(false); consumer.setVipChannelEnabled(false);
- The Rocket instanceName parameter is not configured, resulting in repeated consumption problems
Pit: One is that if Rocket does not configure instance Name, it will use pid to do instance Name. If instance Name is the same, it will repeat consumption, because cluster consumption mode is based on instance Name as the only consumption instance.
Looking at the source code, if no instance Name is specified, pid will be treated as instance Name by default, as follows:
if (this.instanceName.equals("DEFAULT")) { this.instanceName = String.valueOf(UtilAll.getPid()); } public static int getPid() { RuntimeMXBean runtime = ManagementFactory.getRuntimeMXBean(); String name = runtime.getName(); // format: "pid@hostname" try { return Integer.parseInt(name.substring(0, name.indexOf('@'))); } catch (Exception e) { return -1; } }
Solution: Operations configuration has $MQ_INSTANCE_NAME environment variable, different machines are different, so you can use: mq. consumer. instance Name: ${MQ_INSTANCE_NAME: default value} to configure.
@Value("${rocketmq.consumer.instanceName:${MQ_INSTANCE_NAME:fota}}") private String clientInstanceName; consumer.setInstanceName(clientInstanceName);