Practical Coding Techniques: Kafka Transaction Commit, Redis Distributed Lock Simplification, and Business Log Decoupling

This article shares several practical coding techniques for backend development, including how to commit Kafka transactions safely, simplify Redis distributed lock usage with Redisson, implement AOP-based method locking, and decouple business logging using thread pools and asynchronous processing.

Top Architect
Top Architect
Top Architect
Practical Coding Techniques: Kafka Transaction Commit, Redis Distributed Lock Simplification, and Business Log Decoupling

In this technical note, a senior architect shares a collection of useful backend coding tricks that can improve reliability and maintainability of services.

1. Kafka transaction commit method – The article explains the problem of sending Kafka messages before a database transaction commits and provides a Spring @Transactional method that sends the message only after the transaction succeeds. The code snippet shows a service method using KafkaTemplate and a helper class KafkaTemplateHelper that registers a synchronization callback to send the message after commit.

@Autowired
private KafkaTemplate kafkaTemplate;

@Transactional(rollbackFor = Exception.class)
public void saveServiceOrder(ServiceOrder serviceOrder) {
    // do something
    NoticeListDTO notice = NoticeListDTO.builder().build();
    kafkaTemplate.send(TopicNameConstants.SERVICE_ORDER_CHANGE_NOTIFY,
        JSONObject.toJSONString(notice));
}

The helper class KafkaTemplateHelper checks if a transaction is active and registers an afterCommit callback to send the message, otherwise it sends immediately.

@Component
@Slf4j
public class KafkaTemplateHelper {
    @Autowired
    private KafkaTemplate kafkaTemplate;

    public <T> void send(String topic, Object data) {
        if (TransactionSynchronizationManager.isSynchronizationActive()) {
            TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronizationAdapter() {
                @Override
                public void afterCommit() {
                    log.info("Transaction committed, sending message: topic:{}, data:{}", topic,
                        JSONObject.toJSONString(data));
                    kafkaTemplate.send(topic, data);
                }
            });
        } else {
            log.info("No transaction, sending message directly: topic:{}, data:{}", topic,
                JSONObject.toJSONString(data));
            kafkaTemplate.send(topic, data);
        }
    }
}

2. Redis distributed lock simplification – Using Redisson, the article shows how to lock a single resource with a concise annotation and how to lock multiple keys in batch operations. Example code demonstrates a method annotated with @Transactional and a custom @AopLock annotation that generates a lock key from method parameters.

@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD})
public @interface AopLock {
    String value();          // SpEL expression for lock key
    int waitTime() default 0;
    int leaseTime() default 6;
    int errorCode() default 2733;
    String errorMsg() default "操作过于频繁,请稍后再试";
}

The corresponding aspect intercepts the method, acquires the lock via Redisson, proceeds with the business logic, and releases the lock in a finally block.

@Around("@annotation(common.aop.annos.AopLock)")
public Object lock(ProceedingJoinPoint joinPoint) throws Throwable {
    // evaluate SpEL, acquire lock, invoke method, release lock
}

For batch updates where several orders need to be locked simultaneously, the article builds a list of Redis keys, attempts to lock each, performs the update, and finally releases all locks in a finally block.

List<String> redisKeys = new ArrayList<>();
for (ServiceOrder order : list) {
    redisKeys.add("mh:scs:updateServiceOrders:" + order.getServiceOrderId());
}
try {
    for (String key : redisKeys) {
        boolean lock = redissonDistributedLocker.tryLock(key, 5L, 30L);
        AssertUtil.businessInvalid(!lock, "批量更新服务单获取锁失败,请稍后尝试!");
    }
    // business logic
} finally {
    redisKeys.forEach(redissonDistributedLocker::unlock);
}

3. Business log decoupling – To avoid slowing down the main request path, the article proposes two strategies. The first uses a dedicated thread pool to write logs asynchronously; the second packages log data into an object and sends it to Kafka or Redis for asynchronous processing. Sample utility class ServiceOrderLogUtils shows how logs are built, trimmed, and persisted either synchronously or via a background thread.

public static void saveLog(Long serviceOrderId, OperTypeEnum operType, String operContent) {
    saveLog(createLog(serviceOrderId, operType, operContent));
}

Overall, the article provides concrete code examples and best‑practice recommendations for handling Kafka transactional messaging, simplifying Redisson locks, applying AOP‑based method locking, and off‑loading business logging to improve system reliability.

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

aopredisspringdistributed-lock
Top Architect
Written by

Top Architect

Top Architect focuses on sharing practical architecture knowledge, covering enterprise, system, website, large‑scale distributed, and high‑availability architectures, plus architecture adjustments using internet technologies. We welcome idea‑driven, sharing‑oriented architects to exchange and learn together.

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.