This is an automated email from the ASF dual-hosted git repository.

xcsnx pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/shenyu.git


The following commit(s) were added to refs/heads/master by this push:
     new 555f9b456f [type:feat]Change bootstrap heartbeat reporting (#6187)
555f9b456f is described below

commit 555f9b456f1d90dec93d4c60bc6c4a3aa1f54ab7
Author: xchoox <[email protected]>
AuthorDate: Mon Sep 29 15:31:05 2025 +0800

    [type:feat]Change bootstrap heartbeat reporting (#6187)
    
    * change
    
    * fix code style
    
    * change Conditional
    
    * fix
    
    ---------
    
    Co-authored-by: aias00 <[email protected]>
    Co-authored-by: moremind <[email protected]>
---
 .../src/main/resources/application.yml             | 14 ++--
 .../register/client/beat/HeartbeatListener.java    | 79 ++++++++++++++++++++--
 .../beat/ShenyuBootstrapHeartBeatConfig.java       | 56 +++++++++++++++
 .../beat/HeartbeatListenerConfiguration.java       | 34 +++++++---
 4 files changed, 161 insertions(+), 22 deletions(-)

diff --git a/shenyu-bootstrap/src/main/resources/application.yml 
b/shenyu-bootstrap/src/main/resources/application.yml
index e1da6a1a5b..1c4006de9e 100644
--- a/shenyu-bootstrap/src/main/resources/application.yml
+++ b/shenyu-bootstrap/src/main/resources/application.yml
@@ -194,12 +194,10 @@ shenyu:
 #      workerCount: 8
 #      daemon: true
   register:
-    enable: true
-    registerType: http
-    serverLists: http://localhost:9095
+    enabled: false
+    registerType: zookeeper #etcd #consul
+    serverLists: localhost:2181 #http://localhost:2379 #localhost:8848
     props:
-      username: admin
-      password: 123456
   cross:
     enabled: true
     allowedHeaders:
@@ -268,6 +266,12 @@ shenyu:
 #      url: http://localhost:8500
 #      waitTime: 10000
 #      watchDelay: 10000
+  heartbeat:
+    enabled: true
+    serverLists: http://localhost:9095
+    props:
+      username: admin
+      password: 123456
   exclude:
     enabled: false
     paths:
diff --git 
a/shenyu-register-center/shenyu-register-client-beat/src/main/java/org/apache/shenyu/register/client/beat/HeartbeatListener.java
 
b/shenyu-register-center/shenyu-register-client-beat/src/main/java/org/apache/shenyu/register/client/beat/HeartbeatListener.java
index a5db7aad43..36a54e2fcf 100644
--- 
a/shenyu-register-center/shenyu-register-client-beat/src/main/java/org/apache/shenyu/register/client/beat/HeartbeatListener.java
+++ 
b/shenyu-register-center/shenyu-register-client-beat/src/main/java/org/apache/shenyu/register/client/beat/HeartbeatListener.java
@@ -17,19 +17,32 @@
 
 package org.apache.shenyu.register.client.beat;
 
+import com.github.benmanes.caffeine.cache.CacheLoader;
+import com.github.benmanes.caffeine.cache.Caffeine;
+import com.github.benmanes.caffeine.cache.LoadingCache;
+import com.google.common.base.Splitter;
+import com.google.common.collect.Lists;
+import org.apache.commons.lang3.StringUtils;
 import org.apache.shenyu.common.concurrent.ShenyuThreadFactory;
 import org.apache.shenyu.common.config.ShenyuConfig;
+import org.apache.shenyu.common.constant.Constants;
 import org.apache.shenyu.common.constant.InstanceTypeConstants;
+import org.apache.shenyu.common.utils.AesUtils;
+import org.apache.shenyu.common.utils.GsonUtils;
 import org.apache.shenyu.common.utils.IpUtils;
 import org.apache.shenyu.common.utils.SystemInfoUtils;
-import org.apache.shenyu.register.client.api.ShenyuClientRegisterRepository;
+import org.apache.shenyu.register.client.http.utils.RegisterUtils;
 import org.apache.shenyu.register.common.dto.InstanceBeatInfoDTO;
+import org.checkerframework.checker.nullness.qual.NonNull;
+import org.checkerframework.checker.nullness.qual.Nullable;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.springframework.boot.autoconfigure.web.ServerProperties;
 import org.springframework.context.event.ContextClosedEvent;
 import org.springframework.context.event.EventListener;
 
+import java.util.List;
+import java.util.Optional;
 import java.util.concurrent.ScheduledThreadPoolExecutor;
 import java.util.concurrent.TimeUnit;
 
@@ -43,15 +56,49 @@ public class HeartbeatListener {
 
     private ScheduledThreadPoolExecutor executor;
 
-    private final ShenyuClientRegisterRepository httpClientRegisterRepository;
-
     private final ShenyuConfig shenyuConfig;
 
-    public HeartbeatListener(final ShenyuClientRegisterRepository 
httpClientRegisterRepository, final ShenyuConfig shenyuConfig, final 
ServerProperties serverProperties) {
+    private String username;
+
+    private String password;
+
+    private List<String> serverList;
+
+    /**
+     * server -> accessToken.
+     */
+    private LoadingCache<String, String> accessToken;
+
+    private ShenyuBootstrapHeartBeatConfig config;
+
+    public HeartbeatListener(final ShenyuBootstrapHeartBeatConfig config, 
final ShenyuConfig shenyuConfig, final ServerProperties serverProperties) {
         executor = new ScheduledThreadPoolExecutor(1, 
ShenyuThreadFactory.create("scheduled-instance-task", false));
-        this.httpClientRegisterRepository = httpClientRegisterRepository;
         this.shenyuConfig = shenyuConfig;
         LOG.info("Web server initialized on port {}, starting heartbeat 
reporter", serverProperties.getPort());
+        this.username = config.getProps().getProperty(Constants.USER_NAME);
+        this.password = config.getProps().getProperty(Constants.PASS_WORD);
+        this.config = config;
+        String secretKey = 
config.getProps().getProperty(Constants.AES_SECRET_KEY);
+        String secretIv = 
config.getProps().getProperty(Constants.AES_SECRET_IV);
+        if (StringUtils.isNotBlank(secretKey) && 
StringUtils.isNotBlank(secretIv)) {
+            this.password = AesUtils.cbcEncrypt(secretKey, secretIv, password);
+        }
+        this.serverList = 
Lists.newArrayList(Splitter.on(",").split(config.getServerLists()));
+        this.accessToken = Caffeine.newBuilder()
+                //see 
org.apache.shenyu.admin.config.properties.JwtProperties#expiredSeconds
+                .expireAfterWrite(24L, TimeUnit.HOURS)
+                .build(new CacheLoader<>() {
+                    @Override
+                    public @Nullable String load(@NonNull final String server) 
{
+                        try {
+                            Optional<?> login = 
RegisterUtils.doLogin(username, password, server.concat(Constants.LOGIN_PATH));
+                            return login.map(String::valueOf).orElse(null);
+                        } catch (Exception e) {
+                            LOG.error("Login admin url :{} is fail, will 
retry. cause: {} ", server, e.getMessage());
+                            return null;
+                        }
+                    }
+                });
         executor.scheduleAtFixedRate(() -> {
             InstanceBeatInfoDTO instanceBeatInfoDTO = new 
InstanceBeatInfoDTO();
             
instanceBeatInfoDTO.setInstancePort(String.valueOf(serverProperties.getPort()));
@@ -59,11 +106,31 @@ public class HeartbeatListener {
             instanceBeatInfoDTO.setNamespaceId(shenyuConfig.getNamespace());
             
instanceBeatInfoDTO.setInstanceInfo(SystemInfoUtils.getSystemInfo());
             
instanceBeatInfoDTO.setInstanceType(InstanceTypeConstants.BOOTSTRAP_INSTANCE_INFO);
-            httpClientRegisterRepository.sendHeartbeat(instanceBeatInfoDTO);
+            sendHeartbeat(instanceBeatInfoDTO);
             }, INITIAL_DELAY, CHECK_PERIOD, TimeUnit.SECONDS
         );
     }
 
+    private void sendHeartbeat(final InstanceBeatInfoDTO instanceBeatInfoDTO) {
+        int i = 0;
+        for (String server : serverList) {
+            i++;
+            String concat = server.concat(Constants.BEAT_URI_PATH);
+            try {
+                String accessToken = this.accessToken.get(server);
+                if (StringUtils.isBlank(accessToken)) {
+                    throw new NullPointerException("accessToken is null");
+                }
+                
RegisterUtils.doHeartBeat(GsonUtils.getInstance().toJson(instanceBeatInfoDTO), 
concat, Constants.HEARTBEAT, accessToken);
+            } catch (Exception e) {
+                LOG.error("HeartBeat admin url :{} is fail, will retry.", 
server, e);
+                if (i == serverList.size()) {
+                    throw new RuntimeException(e);
+                }
+            }
+        }
+    }
+
     @EventListener(ContextClosedEvent.class)
     public void onShutdown() {
         executor.shutdown();
diff --git 
a/shenyu-register-center/shenyu-register-client-beat/src/main/java/org/apache/shenyu/register/client/beat/ShenyuBootstrapHeartBeatConfig.java
 
b/shenyu-register-center/shenyu-register-client-beat/src/main/java/org/apache/shenyu/register/client/beat/ShenyuBootstrapHeartBeatConfig.java
new file mode 100644
index 0000000000..84241e2289
--- /dev/null
+++ 
b/shenyu-register-center/shenyu-register-client-beat/src/main/java/org/apache/shenyu/register/client/beat/ShenyuBootstrapHeartBeatConfig.java
@@ -0,0 +1,56 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.shenyu.register.client.beat;
+
+import org.apache.shenyu.register.common.config.PropertiesConfig;
+
+import java.util.Properties;
+
+public class ShenyuBootstrapHeartBeatConfig extends PropertiesConfig {
+
+    private String serverLists;
+
+    public ShenyuBootstrapHeartBeatConfig() {
+
+    }
+
+    public ShenyuBootstrapHeartBeatConfig(
+                                      final String serverLists,
+                                      final Properties props) {
+        this.serverLists = serverLists;
+        this.setProps(props);
+    }
+
+    /**
+     * getServerLists.
+     *
+     * @return String
+     */
+    public String getServerLists() {
+        return serverLists;
+    }
+
+    /**
+     * setServerLists.
+     *
+     * @param serverLists serverLists
+     */
+    public void setServerLists(final String serverLists) {
+        this.serverLists = serverLists;
+    }
+}
diff --git 
a/shenyu-spring-boot-starter/shenyu-spring-boot-starter-client/shenyu-spring-boot-starter-client-beat/src/main/java/org/apache/shenyu/register/client/beat/HeartbeatListenerConfiguration.java
 
b/shenyu-spring-boot-starter/shenyu-spring-boot-starter-client/shenyu-spring-boot-starter-client-beat/src/main/java/org/apache/shenyu/register/client/beat/HeartbeatListenerConfiguration.java
index a38d03815d..2fd1a464f3 100644
--- 
a/shenyu-spring-boot-starter/shenyu-spring-boot-starter-client/shenyu-spring-boot-starter-client-beat/src/main/java/org/apache/shenyu/register/client/beat/HeartbeatListenerConfiguration.java
+++ 
b/shenyu-spring-boot-starter/shenyu-spring-boot-starter-client/shenyu-spring-boot-starter-client-beat/src/main/java/org/apache/shenyu/register/client/beat/HeartbeatListenerConfiguration.java
@@ -18,32 +18,44 @@
 package org.apache.shenyu.register.client.beat;
 
 import org.apache.shenyu.common.config.ShenyuConfig;
-import org.apache.shenyu.register.client.api.ShenyuClientRegisterRepository;
-import 
org.apache.shenyu.springboot.starter.client.common.config.ShenyuClientCommonBeanConfiguration;
-import org.springframework.boot.autoconfigure.ImportAutoConfiguration;
-import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
+import 
org.springframework.boot.autoconfigure.condition.ConditionalOnExpression;
 import org.springframework.boot.autoconfigure.web.ServerProperties;
+import org.springframework.boot.context.properties.ConfigurationProperties;
 import org.springframework.context.annotation.Bean;
 import org.springframework.context.annotation.Configuration;
 
 @Configuration
-@ImportAutoConfiguration(ShenyuClientCommonBeanConfiguration.class)
-@ConditionalOnProperty(value = "shenyu.register.enabled", matchIfMissing = 
true, havingValue = "true")
+@ConditionalOnExpression(
+        "${shenyu.heartbeat.enabled:true} and "
+           + "'${shenyu.sync.websocket.urls:}'.isEmpty() and "
+           + "'${shenyu.sync.http.url:}'.isEmpty()"
+)
 public class HeartbeatListenerConfiguration {
 
     /**
      * Heartbeat bean listener.
      *
-     * @param httpClientRegisterRepository the http client register repository
-     * @param shenyuConfig the shenyu config
-     * @param serverProperties the server properties
+     * @param shenyuBootstrapHeartBeatConfig the shenyuBootstrapHeartBeatConfig
+     * @param shenyuConfig                   the shenyu config
+     * @param serverProperties               the server properties
      * @return the heartbeat bean listener.
      */
     @Bean
-    public HeartbeatListener heartbeatListener(final 
ShenyuClientRegisterRepository httpClientRegisterRepository,
+    public HeartbeatListener heartbeatListener(final 
ShenyuBootstrapHeartBeatConfig shenyuBootstrapHeartBeatConfig,
                                                final ShenyuConfig shenyuConfig,
                                                final ServerProperties 
serverProperties) {
-        return new HeartbeatListener(httpClientRegisterRepository, 
shenyuConfig, serverProperties);
+        return new HeartbeatListener(shenyuBootstrapHeartBeatConfig, 
shenyuConfig, serverProperties);
+    }
+
+    /**
+     * ShenyuBootstrapHeartBeatConfig.
+     *
+     * @return the shenyuBootstrapHeartBeatConfig.
+     */
+    @Bean
+    @ConfigurationProperties(prefix = "shenyu.heartbeat")
+    public ShenyuBootstrapHeartBeatConfig shenyuBootstrapHeartBeatConfig() {
+        return new ShenyuBootstrapHeartBeatConfig();
     }
 
 }

Reply via email to