All Projects → dilaverdemirel → spring-cloud-stream-outbox-extension

dilaverdemirel / spring-cloud-stream-outbox-extension

Licence: Apache-2.0 license
Spring Cloud Stream Transactional Messaging Extension

Programming Languages

java
68154 projects - #9 most used programming language

Projects that are alternatives of or similar to spring-cloud-stream-outbox-extension

Pitstop
This repo contains a sample application based on a Garage Management System for Pitstop - a fictitious garage. The primary goal of this sample is to demonstrate several software-architecture concepts like: Microservices, CQRS, Event Sourcing, Domain Driven Design (DDD), Eventual Consistency.
Stars: ✭ 708 (+3833.33%)
Mutual labels:  rabbitmq, event-driven
Dotnet Istanbul Microservices Demo
This is the demo application that i created for my talk 'Microservice Architecture & Implementation with Asp.Net Core' at Dotnet İstanbul Meetup Group.
Stars: ✭ 109 (+505.56%)
Mutual labels:  rabbitmq, event-driven
Remit
RabbitMQ-backed microservices supporting RPC, pubsub, automatic service discovery and scaling with no code changes.
Stars: ✭ 24 (+33.33%)
Mutual labels:  rabbitmq, event-driven
Run Aspnetcore Microservices
Microservices on .Net platforms which used Asp.Net Web API, Docker, RabbitMQ, MassTransit, Grpc, Ocelot API Gateway, MongoDB, Redis, PostgreSQL, SqlServer, Dapper, Entity Framework Core, CQRS and Clean Architecture implementation. Also includes Cross-Cutting concerns like Implementing Centralized Distributed Logging with Elasticsearch, Kibana and SeriLog, use the HealthChecks with Watchdog, Implement Retry and Circuit Breaker patterns with Polly and so on.. See Microservices Architecture and Step by Step Implementation on .NET Course w/ discount->
Stars: ✭ 406 (+2155.56%)
Mutual labels:  rabbitmq, event-driven
grabbit
A lightweight transactional message bus on top of RabbitMQ
Stars: ✭ 87 (+383.33%)
Mutual labels:  rabbitmq, outbox
Plumber
A swiss army knife CLI tool for interacting with Kafka, RabbitMQ and other messaging systems.
Stars: ✭ 514 (+2755.56%)
Mutual labels:  rabbitmq, event-driven
Rabbus
A tiny wrapper over amqp exchanges and queues 🚌 ✨
Stars: ✭ 86 (+377.78%)
Mutual labels:  rabbitmq, event-driven
Cap
Distributed transaction solution in micro-service base on eventually consistency, also an eventbus with Outbox pattern
Stars: ✭ 5,208 (+28833.33%)
Mutual labels:  rabbitmq, outbox
Watermill
Building event-driven applications the easy way in Go.
Stars: ✭ 3,504 (+19366.67%)
Mutual labels:  rabbitmq, event-driven
Qmq
QMQ是去哪儿网内部广泛使用的消息中间件,自2012年诞生以来在去哪儿网所有业务场景中广泛的应用,包括跟交易息息相关的订单场景; 也包括报价搜索等高吞吐量场景。
Stars: ✭ 2,420 (+13344.44%)
Mutual labels:  rabbitmq, event-driven
Spring Cloud Stream Demo
Simple Event Driven Microservices with Spring Cloud Stream
Stars: ✭ 58 (+222.22%)
Mutual labels:  rabbitmq, event-driven
sample-message-driven-microservices
sample spring cloud application that integrates with rabbitmq through spring cloud stream framework as shows how to setup message-driven microservices basing on publish-subscribe model, consumer groups
Stars: ✭ 28 (+55.56%)
Mutual labels:  rabbitmq, spring-cloud-stream
Eventflow.example
DDD+CQRS+Event-sourcing examples using EventFlow following CQRS-ES architecture. It is configured with RabbitMQ, MongoDB(Snapshot store), PostgreSQL(Read store), EventStore(GES). It's targeted to .Net Core 2.2 and include docker compose file.
Stars: ✭ 131 (+627.78%)
Mutual labels:  rabbitmq, event-driven
sample-spring-cloud-stream
sample microservices communicating asynchronously using spring cloud stream, rabbitmq
Stars: ✭ 22 (+22.22%)
Mutual labels:  rabbitmq, spring-cloud-stream
event-driven-example
An example Event-Driven application in Go built with Watermill library.
Stars: ✭ 81 (+350%)
Mutual labels:  rabbitmq, event-driven
Funky
Funky is a functional utility library written in Objective-C.
Stars: ✭ 41 (+127.78%)
Mutual labels:  extension
waspy
WASP framework for Python
Stars: ✭ 43 (+138.89%)
Mutual labels:  rabbitmq
nest-rabbit-tasks
nest-rabbit-worker is a TaskQueue based upon RabbitMQ for NestJS
Stars: ✭ 29 (+61.11%)
Mutual labels:  rabbitmq
TelephoneDirectory
microservices-> .net 6, golang - Docker, Ocelot, RabbitMq, MassTransit, mssql, postgresql, elasticsearch, kibana, jwt
Stars: ✭ 40 (+122.22%)
Mutual labels:  rabbitmq
google-translate-anki
No description or website provided.
Stars: ✭ 28 (+55.56%)
Mutual labels:  extension

codecov

Spring Cloud Stream Outbox Extension

This library provides an extension if you already use spring cloud stream with the transactional database for application messaging. This extension provides transactional messaging. It prevents message lost. If the transaction is succeed, it publishes the message to binder.

If an error occurred when sending the message to the binder(for exp. RabbitMQ),Status of the message will be "FAILED". You can send the failed messages using the predefined scheduler service.

This extension saves all messages. But the message sends immediately when the transaction is succeeded. Only failed messages is sent through the scheduled job. So this extension mostly sends the messages on time. This method provides a high performance.

There are three steps;

  • Send a message with the message wrapper object(OutboxMessageEvent) over spring ApplicationEventPublisher
  • OutboxMessageHandler catches the message to save to DB in same transaction
  • OutboxMessagePublisher catches and send the message to binder(RabbitMQ or Kafka) after the transaction complete

You can see sequence diagram below.

Sequence diagram

What does do this extension?

  • Sends the message on time safely
  • Sends the messages in transactional integrity
  • Saves all messages and tracks the message statuses
  • Resend the failed messages
  • Prevents the message lost

Installation and Usage

You should add the dependency below to pom.xml file.

<dependency>
  <groupId>com.dilaverdemirel.spring</groupId>
  <artifactId>spring-cloud-stream-outbox-extension</artifactId>
  <version>1.0.1</version>
</dependency>

You should edit your configuration like below for activation the extension.

@EnableJpaRepositories(basePackages = {
        "com.dilaverdemirel.spring.outbox.repository"
})
@EntityScan(basePackages = {
        "com.dilaverdemirel.spring.outbox.domain"
})
@ComponentScan({
        "com.dilaverdemirel.spring.outbox"
})
@SpringBootApplication
public class DemoApplication {
    public static void main(String[] args) {
        SpringApplication.run(DemoApplication.class, args);
    }
}

After that if you don't use auto ddl, you should create OUTBOX_TABLE in your db. You can use script below;

CREATE TABLE outbox_message 
  ( 
     id             VARCHAR(255) NOT NULL, 
     channel        VARCHAR(255) NOT NULL, 
     created_at     TIMESTAMP NOT NULL, 
     message_class  VARCHAR(255) NOT NULL, 
     payload        CLOB NOT NULL, 
     sent_at        TIMESTAMP, 
     source         VARCHAR(255) NOT NULL, 
     source_id      VARCHAR(255) NOT NULL, 
     status         VARCHAR(6) NOT NULL,
     retry_count    INT(3) NOT NULL,
     status_message CLOB,
     PRIMARY KEY (id) 
  ) 

If you use liquibase, you can use xml below;

<createTable tableName="outbox_message">
    <column name="id" type="VARCHAR(36)">
        <constraints primaryKey="true"/>
    </column>
    <column name="channel" type="VARCHAR(36)">
        <constraints nullable="false"/>
    </column>
    <column name="created_at" type="datetime">
        <constraints nullable="false"/>
    </column>
    <column name="sent_at" type="datetime">
        <constraints nullable="false"/>
    </column>
    <column name="message_class" type="VARCHAR(255)">
        <constraints nullable="false"/>
    </column>
    <column name="payload" type="CLOB">
        <constraints nullable="false"/>
    </column>
    <column name="source" type="VARCHAR(255)">
        <constraints nullable="false"/>
    </column>
    <column name="source_id" type="VARCHAR(255)">
        <constraints nullable="false"/>
    </column>
    <column name="status" type="VARCHAR(6)">
        <constraints nullable="false"/>
    </column>
    <column name="retry_count" type="INT(3)">
        <constraints nullable="false"/>
    </column>
    <column name="status_message" type="CLOB"/>
</createTable>

And then, you can send a message in transaction like below

@Service
public class PaymentService {
    private final PaymentRepository paymentRepository;
    private final ApplicationEventPublisher applicationEventPublisher;

    public PaymentService(PaymentRepository paymentRepository,
                          ApplicationEventPublisher applicationEventPublisher) {
        this.paymentRepository = paymentRepository;
        this.applicationEventPublisher = applicationEventPublisher;
    }

    @Transactional
    public void doPayment(Payment payment) {
        paymentRepository.save(payment);
        final var outboxMessageEvent = OutboxMessageEvent.builder()
                .source("payment")
                .sourceId(payment.getId())
                .payload(payment)
                .channel("stockOperationOutputChannel")
                .build();

        applicationEventPublisher.publishEvent(outboxMessageEvent);
    }
}

You can resend the failed messages with FailedOutboxMessageSchedulerService, but you need to active the scheduler service.

At this point, you should be careful. Because, if your application running as multiple instance, this job causes that message to be sent duplicate. To solve this problem, you should use a distributed scheduler like that.

There is a parameter for retry threshold.

dilaverdemirel.spring.outbox.failed-messages.retry-count-threshold=3

@Configuration
@EnableScheduling
public class DemoApplication {
    @Bean
    public FailedOutboxMessageSchedulerService failedOutboxMessageSchedulerService(){
        return new FailedOutboxMessageSchedulerService();
    }    
}

If you want to manage saved messages, you can use OutboxMessageRepository repository.

@RestController
@RequestMapping(path = "/outbox-messages")
public class OutboxMessageController {
    private final OutboxMessageRepository outboxMessageRepository;

    public OutboxMessageController(OutboxMessageRepository outboxMessageRepository) {
        this.outboxMessageRepository = outboxMessageRepository;
    }

    @GetMapping
    public ResponseEntity<Iterable<OutboxMessage>> getAll() {
        return new ResponseEntity<>(outboxMessageRepository.findAll(), HttpStatus.OK);
    }
}

Dead Letter Support

If you want to manage dead letters, the extension gives some features.

@Autowired
private FailedOutboxMessageService failedOutboxMessageService;

@Autowired
private OutboxMessageRepository outboxMessageRepository;

@RabbitListener(queues = DLQ)
public void handleDLQMessage(Message failedMessage) {
    final var outboxMessageId = MessageUtils.extractOutboxMessageId(failedMessage);
    final var exceptionMessage = MessageUtils.extractExceptionStackTrace(failedMessage);
    failedOutboxMessageService.markAsFailedWithExceptionMessage(outboxMessageId, exceptionMessage);

    final var outboxMessageOpt = outboxMessageRepository.findById(outboxMessageId);
    if (outboxMessageOpt.isPresent()) {
        final var outboxMessage = outboxMessageOpt.get();
        
        //You can do something use this "outboxMessage.getSourceId()"
    }
}

Example Application

If you want to see how is work, there is an example application.

Notes

Logo source

Note that the project description data, including the texts, logos, images, and/or trademarks, for each open source project belongs to its rightful owner. If you wish to add or remove any projects, please contact us at [email protected].