48
48
import org .springframework .context .ApplicationContext ;
49
49
import reactor .core .publisher .Flux ;
50
50
import reactor .core .publisher .Mono ;
51
+ import reactor .util .retry .Retry ;
51
52
52
53
import im .turms .plugin .minio .core .BucketPolicy ;
53
54
import im .turms .plugin .minio .core .BucketPolicyAction ;
85
86
import im .turms .server .common .infra .security .MacUtil ;
86
87
import im .turms .server .common .infra .time .DateRange ;
87
88
import im .turms .server .common .infra .time .DurationConst ;
89
+ import im .turms .server .common .infra .tracing .TracingCloseableContext ;
90
+ import im .turms .server .common .infra .tracing .TracingContext ;
88
91
import im .turms .server .common .storage .mongo .TurmsMongoClient ;
89
92
import im .turms .service .domain .group .service .GroupMemberService ;
90
93
import im .turms .service .domain .storage .bo .StorageResourceInfo ;
@@ -122,8 +125,10 @@ public class MinioStorageServiceProvider extends TurmsExtension implements Stora
122
125
* @implNote 1. We use HMAC(key, message) instead of a HASH(key + message) to avoid the length
123
126
* extension attack. To put simply, if a hacker knows the signature of the resource
124
127
* "1", and he can also know the signature of resource "12", "13", "123", and so on
125
- * without knowledge of the key. 2. Use MD5 because its output size (128 bits) is
126
- * small, and it is a 22-character Base62-encoded string.
128
+ * without knowledge of the key.
129
+ * <p>
130
+ * 2. Use MD5 because its output size (128 bits) is small, and it is a 22-character
131
+ * Base62-encoded string.
127
132
*/
128
133
private boolean isMacEnabled ;
129
134
@ Nullable
@@ -156,30 +161,30 @@ public class MinioStorageServiceProvider extends TurmsExtension implements Stora
156
161
}
157
162
158
163
@ Override
159
- public void onStarted () {
160
- setUp ();
164
+ public Mono < Void > onStarted () {
165
+ return setUp ();
161
166
}
162
167
163
- private void setUp () {
168
+ private Mono < Void > setUp () {
164
169
MinioStorageProperties properties = loadProperties (MinioStorageProperties .class );
165
170
if (!properties .isEnabled ()) {
166
- return ;
171
+ return Mono . empty () ;
167
172
}
168
173
String endpoint = properties .getEndpoint ();
169
174
URI uri ;
170
175
try {
171
176
uri = new URI (endpoint );
172
177
} catch (URISyntaxException e ) {
173
- throw new IllegalArgumentException (
178
+ return Mono . error ( new IllegalArgumentException (
174
179
"Illegal endpoint URL: "
175
180
+ endpoint ,
176
- e );
181
+ e )) ;
177
182
}
178
183
if (!uri .isAbsolute ()) {
179
- throw new IllegalArgumentException (
184
+ return Mono . error ( new IllegalArgumentException (
180
185
"The endpoint URL ("
181
186
+ endpoint
182
- + ") must be absolute" );
187
+ + ") must be absolute" )) ;
183
188
}
184
189
ApplicationContext context = getContext ();
185
190
node = context .getBean (Node .class );
@@ -217,15 +222,15 @@ private void setUp() {
217
222
key = Base64 .getDecoder ()
218
223
.decode (base64Key );
219
224
} catch (Exception e ) {
220
- throw new IllegalArgumentException (
225
+ return Mono . error ( new IllegalArgumentException (
221
226
"The HMAC key must be Base64-encoded, but got: "
222
227
+ base64Key ,
223
- e );
228
+ e )) ;
224
229
}
225
230
if (key .length < 16 ) {
226
- throw new IllegalArgumentException (
231
+ return Mono . error ( new IllegalArgumentException (
227
232
"The length of HMAC key must be greater than or equal to 16, but got: "
228
- + key .length );
233
+ + key .length )) ;
229
234
}
230
235
macKey = new SecretKeySpec (key , "HmacMD5" );
231
236
} else {
@@ -235,42 +240,32 @@ private void setUp() {
235
240
properties .getRegion (),
236
241
properties .getAccessKey (),
237
242
properties .getSecretKey ());
238
- Duration timeout = Duration .ofSeconds (INIT_BUCKETS_TIMEOUT_SECONDS );
239
- try {
240
- initBuckets ().block (timeout );
241
- isServing = true ;
242
- } catch (Exception e ) {
243
- MinioStorageProperties .Retry retry = properties .getRetry ();
244
- int maxAttempts = retry .getMaxAttempts ();
245
- if (!retry .isEnabled () || maxAttempts <= 0 ) {
246
- throw new RuntimeException ("Failed to initialize the MinIO client" , e );
247
- }
248
- LOGGER .error ("Failed to initialize the MinIO client. Retry times: 0" , e );
249
- try {
250
- Thread .sleep (retry .getInitialIntervalMillis ());
251
- } catch (InterruptedException ex ) {
252
- throw new RuntimeException ("Failed to initialize the MinIO client" , e );
253
- }
254
- for (int currentRetryTimes = 1 ; currentRetryTimes <= maxAttempts ; currentRetryTimes ++) {
255
- try {
256
- initBuckets ().block (timeout );
257
- } catch (Exception ex ) {
258
- LOGGER .error ("Failed to initialize the MinIO client. Retry times: "
259
- + currentRetryTimes , ex );
260
- if (currentRetryTimes == maxAttempts ) {
261
- throw new RuntimeException (
262
- "Failed to initialize the MinIO client with retries exhausted: "
263
- + maxAttempts );
243
+
244
+ Mono <Void > initBuckets =
245
+ initBuckets ().timeout (Duration .ofSeconds (INIT_BUCKETS_TIMEOUT_SECONDS ))
246
+ .doOnSuccess (unused -> isServing = true );
247
+
248
+ MinioStorageProperties .Retry retry = properties .getRetry ();
249
+ int maxAttempts = retry .getMaxAttempts ();
250
+ if (!retry .isEnabled () || maxAttempts <= 0 ) {
251
+ return initBuckets .onErrorMap (t -> true ,
252
+ t -> new RuntimeException ("Failed to initialize the MinIO client" , t ));
253
+ }
254
+ return initBuckets .retryWhen (Retry .max (maxAttempts )
255
+ .doBeforeRetryAsync (retrySignal -> Mono .deferContextual (contextView -> {
256
+ long totalRetries = retrySignal .totalRetries ();
257
+ try (TracingCloseableContext ignored =
258
+ TracingContext .getCloseableContext (contextView )) {
259
+ LOGGER .error ("Failed to initialize the MinIO client. Retry times: "
260
+ + totalRetries , retrySignal .failure ());
264
261
}
265
- try {
266
- Thread .sleep (retry .getIntervalMillis ());
267
- } catch (InterruptedException ignored ) {
268
- throw new RuntimeException ("Failed to initialize the MinIO client" , ex );
262
+ if (0 == totalRetries ) {
263
+ return Mono .delay (Duration .ofMillis (retry .getInitialIntervalMillis ()))
264
+ .then ();
269
265
}
270
- }
271
- }
272
- isServing = true ;
273
- }
266
+ return Mono .delay (Duration .ofMillis (retry .getIntervalMillis ()))
267
+ .then ();
268
+ })));
274
269
}
275
270
276
271
private void initClient (String endpoint , String region , String accessKey , String secretKey ) {
0 commit comments