Skip to content

Commit 46dbcc5

Browse files
committed
fix: add channel shutdown listener to reconnect
1 parent b0adda4 commit 46dbcc5

6 files changed

Lines changed: 62 additions & 41 deletions

File tree

.github/workflows/release.yml

Lines changed: 14 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,18 @@
11
name: release
22
on:
33
release:
4-
types: [ released ]
4+
types: [ released, prereleased ]
55
jobs:
66
release:
77
runs-on: ubuntu-latest
88
steps:
9-
- uses: actions/checkout@v4
9+
- uses: actions/checkout@v6
1010
- name: Set env
1111
run: echo "RELEASE_VERSION=${GITHUB_REF#refs/*/v}" >> $GITHUB_ENV
1212
- name: Upgrade Gradle.properties
1313
run: sed -i 's/version=.*/version=${{ env.RELEASE_VERSION }}/g' gradle.properties
1414
- name: Generate Changelog
15-
uses: heinrichreimer/github-changelog-generator-action@v2.3
15+
uses: heinrichreimer/github-changelog-generator-action@v2.4
1616
with:
1717
token: ${{ secrets.GITHUB_TOKEN }}
1818
pullRequests: true
@@ -21,7 +21,7 @@ jobs:
2121
issuesWoLabels: true
2222
stripGeneratorNotice: true
2323
- name: Set up JDK 17
24-
uses: actions/setup-java@v3
24+
uses: actions/setup-java@v5
2525
with:
2626
distribution: temurin
2727
java-version: 17
@@ -35,13 +35,13 @@ jobs:
3535
env:
3636
ORG_GRADLE_PROJECT_sonatypeUsername: ${{ secrets.MAVEN_USERNAME }}
3737
ORG_GRADLE_PROJECT_sonatypePassword: ${{ secrets.MAVEN_PASSWORD }}
38-
- name: Push Changelog
39-
uses: github-actions-x/commit@v2.9
40-
with:
41-
github-token: ${{ secrets.GITHUB_TOKEN }}
42-
push-branch: 'master'
43-
commit-message: 'Automatic docs and changelog generation [skip ci]'
44-
force-add: 'true'
45-
files: CHANGELOG.md gradle.properties
46-
name: ${{ github.actor }}
47-
email: ${{ github.actor }}@users.noreply.github.com
38+
# - name: Push Changelog
39+
# uses: github-actions-x/commit@v2.9
40+
# with:
41+
# github-token: ${{ secrets.GITHUB_TOKEN }}
42+
# push-branch: 'master'
43+
# commit-message: 'Automatic docs and changelog generation [skip ci]'
44+
# force-add: 'true'
45+
# files: CHANGELOG.md gradle.properties
46+
# name: ${{ github.actor }}
47+
# email: ${{ github.actor }}@users.noreply.github.com

async/async-commons/async-commons.gradle

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ dependencies {
1010
compileOnly 'io.projectreactor:reactor-core'
1111
api 'io.projectreactor.rabbitmq:reactor-rabbitmq:1.5.6'
1212
api 'com.fasterxml.jackson.core:jackson-databind'
13-
implementation 'commons-io:commons-io:2.16.1'
13+
implementation 'commons-io:commons-io:2.21.0'
1414

1515
testImplementation 'io.projectreactor:reactor-test'
1616
}

async/async-rabbit-starter/src/main/java/org/reactivecommons/async/rabbit/config/RabbitMqConfig.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -134,6 +134,7 @@ public BrokerConfig brokerConfig() {
134134
public ConnectionFactoryProvider rabbitRConnectionFactory(RabbitProperties properties)
135135
throws NoSuchAlgorithmException, KeyManagementException {
136136
final ConnectionFactory factory = new ConnectionFactory();
137+
factory.setAutomaticRecoveryEnabled(true);
137138
PropertyMapper map = PropertyMapper.get();
138139
map.from(properties::determineHost).whenNonNull().to(factory::setHost);
139140
map.from(properties::determinePort).to(factory::setPort);

async/async-rabbit/src/main/java/org/reactivecommons/async/rabbit/listeners/GenericMessageListener.java

Lines changed: 39 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
package org.reactivecommons.async.rabbit.listeners;
22

33
import com.rabbitmq.client.AMQP;
4+
import com.rabbitmq.client.Channel;
5+
import com.rabbitmq.client.ShutdownSignalException;
46
import lombok.extern.java.Log;
57
import org.reactivecommons.async.commons.DiscardNotifier;
68
import org.reactivecommons.async.commons.FallbackStrategy;
@@ -10,6 +12,7 @@
1012
import org.reactivecommons.async.rabbit.RabbitMessage;
1113
import org.reactivecommons.async.rabbit.communications.ReactiveMessageListener;
1214
import org.reactivecommons.async.rabbit.communications.TopologyCreator;
15+
import reactor.core.Disposable;
1316
import reactor.core.publisher.Flux;
1417
import reactor.core.publisher.Mono;
1518
import reactor.core.scheduler.Scheduler;
@@ -25,6 +28,7 @@
2528
import java.util.List;
2629
import java.util.Optional;
2730
import java.util.concurrent.ConcurrentHashMap;
31+
import java.util.concurrent.atomic.AtomicReference;
2832
import java.util.function.Function;
2933
import java.util.logging.Level;
3034

@@ -49,7 +53,8 @@ public abstract class GenericMessageListener {
4953
private final DiscardNotifier discardNotifier;
5054
private final String objectType;
5155
private final CustomReporter customReporter;
52-
private volatile Flux<AcknowledgableDelivery> messageFlux;
56+
private final AtomicReference<Channel> channelRef = new AtomicReference<>();
57+
private Disposable listenerSubscription;
5358

5459
public GenericMessageListener(String queueName, ReactiveMessageListener listener, boolean useDLQRetries,
5560
boolean createTopology, long maxRetries, long retryDelay, DiscardNotifier discardNotifier,
@@ -78,34 +83,45 @@ protected Mono<Void> setUpBindings(TopologyCreator creator) {
7883
return Mono.empty();
7984
}
8085

81-
public void startListener() {
82-
log.log(Level.INFO, "Using max concurrency {0}, in queue: {1}", new Object[]{messageListener.getMaxConcurrency(), queueName});
83-
if (useDLQRetries) {
84-
log.log(Level.INFO, "ATTENTION! Using DLQ Strategy for retries with {0} + 1 Max Retries configured!", new Object[]{maxRetries});
85-
} else {
86-
log.log(Level.INFO, "ATTENTION! Using infinite fast retries as Retry Strategy");
86+
public synchronized void startListener() {
87+
Channel current = channelRef.get();
88+
if (current != null && current.isOpen()) {
89+
log.warning("Channel is already open, no need to restart listener");
90+
return;
8791
}
92+
stopListener();
93+
var baseSubscriber = new LoggerSubscriber<>(getClass().getName());
94+
listenerSubscription = baseSubscriber;
95+
Flux.defer(this::buildConsumeFlux)
96+
.subscribe(baseSubscriber);
97+
}
8898

89-
ConsumeOptions consumeOptions = new ConsumeOptions();
90-
consumeOptions.qos(messageListener.getPrefetchCount());
91-
92-
if (createTopology) {
93-
this.messageFlux = setUpBindings(messageListener.getTopologyCreator())
94-
.thenMany(receiver.consumeManualAck(queueName, consumeOptions)
95-
.transform(this::consumeFaultTolerant));
96-
} else {
97-
this.messageFlux = receiver.consumeManualAck(queueName, consumeOptions)
98-
.doOnError(err -> log.log(Level.SEVERE, "Error listening queue", err))
99-
.transform(this::consumeFaultTolerant);
100-
}
10199

100+
private Flux<AcknowledgableDelivery> buildConsumeFlux() {
101+
ConsumeOptions options = new ConsumeOptions()
102+
.qos(messageListener.getPrefetchCount())
103+
.channelCallback(channel -> {
104+
channelRef.set(channel);
105+
channel.addShutdownListener(this::onChannelShutdown);
106+
});
107+
108+
Flux<AcknowledgableDelivery> source = createTopology
109+
? setUpBindings(messageListener.getTopologyCreator())
110+
.thenMany(receiver.consumeManualAck(queueName, options))
111+
: receiver.consumeManualAck(queueName, options);
102112

103-
onTerminate();
113+
return source.transform(this::consumeFaultTolerant);
104114
}
105115

106-
private void onTerminate() {
107-
messageFlux.doOnTerminate(this::onTerminate)
108-
.subscribe(new LoggerSubscriber<>(getClass().getName()));
116+
private void onChannelShutdown(ShutdownSignalException cause) {
117+
log.log(Level.SEVERE, "Channel shutdown detected in listener", cause);
118+
startListener();
119+
}
120+
121+
public synchronized void stopListener() {
122+
if (listenerSubscription != null && !listenerSubscription.isDisposed()) {
123+
listenerSubscription.dispose();
124+
}
109125
}
110126

111127
protected Mono<AcknowledgableDelivery> handle(AcknowledgableDelivery msj, Instant initTime) {

build.gradle

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ buildscript {
1313
plugins {
1414
id 'jacoco'
1515
id 'org.sonarqube' version '5.1.0.4882'
16-
id 'org.springframework.boot' version '3.3.1' apply false
16+
id 'org.springframework.boot' version '3.5.13' apply false
1717
id 'io.github.gradle-nexus.publish-plugin' version '2.0.0'
1818
id 'co.com.bancolombia.cleanArchitecture' version '3.17.13'
1919
}

main.gradle

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,10 @@ allprojects {
1616

1717
nexusPublishing {
1818
repositories {
19-
sonatype()
19+
sonatype {
20+
nexusUrl.set(uri("https://ossrh-staging-api.central.sonatype.com/service/local/"))
21+
snapshotRepositoryUrl.set(uri("https://central.sonatype.com/repository/maven-snapshots/"))
22+
}
2023
}
2124
}
2225

@@ -32,6 +35,7 @@ subprojects {
3235

3336
dependencies {
3437
testImplementation 'org.springframework.boot:spring-boot-starter-test'
38+
testRuntimeOnly 'org.junit.platform:junit-platform-launcher'
3539
testImplementation 'io.projectreactor:reactor-test'
3640
compileOnly 'org.projectlombok:lombok'
3741
annotationProcessor 'org.projectlombok:lombok'
@@ -55,7 +59,7 @@ subprojects {
5559

5660
dependencyManagement {
5761
imports {
58-
mavenBom 'org.springframework.boot:spring-boot-dependencies:3.3.1'
62+
mavenBom 'org.springframework.boot:spring-boot-dependencies:3.5.13'
5963
}
6064
}
6165

0 commit comments

Comments
 (0)