diff --git a/shenyu-admin/src/main/java/org/apache/shenyu/admin/config/ClusterConfiguration.java b/shenyu-admin/src/main/java/org/apache/shenyu/admin/config/ClusterConfiguration.java index bcb7b1b982b7..6f9eb0f23a4d 100644 --- a/shenyu-admin/src/main/java/org/apache/shenyu/admin/config/ClusterConfiguration.java +++ b/shenyu-admin/src/main/java/org/apache/shenyu/admin/config/ClusterConfiguration.java @@ -23,6 +23,7 @@ import org.apache.shenyu.admin.mode.cluster.filter.ClusterForwardFilter; import org.apache.shenyu.admin.mode.cluster.service.ClusterSelectMasterService; import org.apache.shenyu.admin.mode.cluster.service.ShenyuClusterService; +import org.apache.shenyu.admin.service.impl.InstanceCheckService; import org.apache.shenyu.admin.service.impl.UpstreamCheckService; import org.apache.shenyu.admin.service.manager.LoadServiceDocEntry; import org.slf4j.Logger; @@ -50,6 +51,7 @@ public class ClusterConfiguration { * * @param shenyuClusterSelectMasterService shenyu cluster select master service * @param upstreamCheckService upstream check service + * @param instanceCheckService instance check service * @param loadServiceDocEntry load service doc entry * @param clusterProperties cluster properties * @return Shenyu cluster service @@ -58,11 +60,13 @@ public class ClusterConfiguration { @ConditionalOnMissingBean public ShenyuRunningModeService shenyuRunningModeService(final ClusterSelectMasterService shenyuClusterSelectMasterService, final UpstreamCheckService upstreamCheckService, + final InstanceCheckService instanceCheckService, final LoadServiceDocEntry loadServiceDocEntry, final ClusterProperties clusterProperties) { LOGGER.info("starting in cluster mode ..."); return new ShenyuClusterService(shenyuClusterSelectMasterService, upstreamCheckService, + instanceCheckService, loadServiceDocEntry, clusterProperties ); diff --git a/shenyu-admin/src/main/java/org/apache/shenyu/admin/config/ShenyuAdminConfiguration.java b/shenyu-admin/src/main/java/org/apache/shenyu/admin/config/ShenyuAdminConfiguration.java index f3b9a8eb4c33..f4e568a7e693 100644 --- a/shenyu-admin/src/main/java/org/apache/shenyu/admin/config/ShenyuAdminConfiguration.java +++ b/shenyu-admin/src/main/java/org/apache/shenyu/admin/config/ShenyuAdminConfiguration.java @@ -19,6 +19,8 @@ import org.apache.shenyu.admin.service.converter.SelectorHandleConverter; import org.apache.shenyu.admin.service.converter.SelectorHandleConverterFactor; +import org.apache.shenyu.admin.service.publish.InstanceInfoReportEventPublisher; +import org.springframework.context.ApplicationEventPublisher; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.transaction.annotation.EnableTransactionManagement; @@ -62,4 +64,9 @@ public LocaleResolver localeResolver() { localeResolver.setSupportedLocales(Stream.of(Locale.US, Locale.SIMPLIFIED_CHINESE).collect(Collectors.toList())); return localeResolver; } + + @Bean + public InstanceInfoReportEventPublisher instanceInfoReportEventPublisher(final ApplicationEventPublisher publisher) { + return new InstanceInfoReportEventPublisher(publisher); + } } diff --git a/shenyu-admin/src/main/java/org/apache/shenyu/admin/config/StandaloneConfiguration.java b/shenyu-admin/src/main/java/org/apache/shenyu/admin/config/StandaloneConfiguration.java index 728cd50ffbf9..3aee40f5ef68 100644 --- a/shenyu-admin/src/main/java/org/apache/shenyu/admin/config/StandaloneConfiguration.java +++ b/shenyu-admin/src/main/java/org/apache/shenyu/admin/config/StandaloneConfiguration.java @@ -19,6 +19,7 @@ import org.apache.shenyu.admin.mode.ShenyuRunningModeService; import org.apache.shenyu.admin.mode.standalone.ShenyuStandaloneService; +import org.apache.shenyu.admin.service.impl.InstanceCheckService; import org.apache.shenyu.admin.service.impl.UpstreamCheckService; import org.apache.shenyu.admin.service.manager.LoadServiceDocEntry; import org.slf4j.Logger; @@ -40,6 +41,7 @@ public class StandaloneConfiguration { * Shenyu running mode standalone service. * * @param upstreamCheckService upstream check service + * @param instanceCheckService instance check service * @param loadServiceDocEntry load service doc entry * @return Shenyu standalone service */ @@ -47,11 +49,13 @@ public class StandaloneConfiguration { @ConditionalOnProperty(value = {"shenyu.cluster.enabled"}, havingValue = "false", matchIfMissing = true) @ConditionalOnMissingBean public ShenyuRunningModeService shenyuRunningModeService(final UpstreamCheckService upstreamCheckService, + final InstanceCheckService instanceCheckService, final LoadServiceDocEntry loadServiceDocEntry) { LOGGER.info("starting in standalone mode ..."); return new ShenyuStandaloneService( upstreamCheckService, - loadServiceDocEntry + loadServiceDocEntry, + instanceCheckService ); } diff --git a/shenyu-admin/src/main/java/org/apache/shenyu/admin/controller/InstanceController.java b/shenyu-admin/src/main/java/org/apache/shenyu/admin/controller/InstanceController.java index a2d521882153..ff515f39974b 100644 --- a/shenyu-admin/src/main/java/org/apache/shenyu/admin/controller/InstanceController.java +++ b/shenyu-admin/src/main/java/org/apache/shenyu/admin/controller/InstanceController.java @@ -17,8 +17,10 @@ package org.apache.shenyu.admin.controller; +import jakarta.validation.Valid; import jakarta.validation.constraints.NotNull; import org.apache.shenyu.admin.aspect.annotation.RestApi; +import org.apache.shenyu.register.common.dto.InstanceBeatInfoDTO; import org.apache.shenyu.admin.model.page.CommonPager; import org.apache.shenyu.admin.model.page.PageParameter; import org.apache.shenyu.admin.model.query.InstanceQuery; @@ -27,10 +29,14 @@ import org.apache.shenyu.admin.model.vo.InstanceInfoVO; import org.apache.shenyu.admin.service.InstanceInfoService; import org.apache.shenyu.admin.service.PageService; +import org.apache.shenyu.admin.service.impl.InstanceCheckService; import org.apache.shenyu.admin.utils.ShenyuResultMessage; import org.apache.shiro.authz.annotation.RequiresPermissions; +import org.springframework.util.CollectionUtils; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestParam; /** @@ -41,8 +47,11 @@ public class InstanceController implements PagedController commonPager = instanceInfoService.listByPage( - new InstanceQuery( - new PageParameter(currentPage, pageSize), - instanceType, - instanceIp, - instancePort, - namespaceId - ) + new InstanceQuery( + new PageParameter(currentPage, pageSize), + instanceType, + instanceIp, + instancePort, + namespaceId + ) ); + if (!CollectionUtils.isEmpty(commonPager.getDataList())) { + commonPager.getDataList().forEach(instanceInfoVO -> { + String instanceKey = instanceCheckService.getInstanceKey(instanceInfoVO); + InstanceInfoVO instanceHealthBeatInfo = instanceCheckService.getInstanceHealthBeatInfo(instanceKey); + instanceInfoVO.setLastHeartBeatTime(instanceHealthBeatInfo.getLastHeartBeatTime()); + instanceInfoVO.setInstanceState(instanceHealthBeatInfo.getInstanceState()); + instanceInfoVO.setDateUpdated(instanceHealthBeatInfo.getDateUpdated()); + }); + } return ShenyuAdminResult.success(ShenyuResultMessage.QUERY_SUCCESS, commonPager); } @@ -88,6 +106,29 @@ public ShenyuAdminResult detailInstanceInfo(@PathVariable("id") final String id) return ShenyuAdminResult.success(ShenyuResultMessage.DETAIL_SUCCESS, instanceInfoVO); } + /** + * receive beat. + * + * @param instanceBeatInfoDTO instanceBeatInfoDTO. + * @return {@linkplain ShenyuAdminResult} + */ + @PostMapping("/beat") + public String beat(@Valid @RequestBody final InstanceBeatInfoDTO instanceBeatInfoDTO) { + instanceCheckService.handleBeatInfo(instanceBeatInfoDTO); + return ShenyuResultMessage.SUCCESS; + } + + /** + * visual instance info. + * + * @param namespaceId namespace id. + * @return {@linkplain ShenyuAdminResult} + */ + @GetMapping("/analysis/{namespaceId}") + public ShenyuAdminResult getInstanceDataVisual(@PathVariable("namespaceId") final String namespaceId) { + return ShenyuAdminResult.success(ShenyuResultMessage.QUERY_SUCCESS, instanceCheckService.getInstanceDataVisual(namespaceId)); + } + @Override public PageService pageService() { diff --git a/shenyu-admin/src/main/java/org/apache/shenyu/admin/disruptor/subscriber/URIRegisterExecutorSubscriber.java b/shenyu-admin/src/main/java/org/apache/shenyu/admin/disruptor/subscriber/URIRegisterExecutorSubscriber.java index 5ff30e5ebdcc..254e268fa0cf 100644 --- a/shenyu-admin/src/main/java/org/apache/shenyu/admin/disruptor/subscriber/URIRegisterExecutorSubscriber.java +++ b/shenyu-admin/src/main/java/org/apache/shenyu/admin/disruptor/subscriber/URIRegisterExecutorSubscriber.java @@ -95,7 +95,7 @@ public void executor(final Collection dataList) { heartbeat.stream().map(URIRegisterDTO::getNamespaceId) .filter(StringUtils::isNotBlank) .findFirst() - .ifPresent(namespaceId -> service.heartbeat(selectorName, register, namespaceId)); + .ifPresent(namespaceId -> service.heartbeat(selectorName, heartbeat, namespaceId)); } if (CollectionUtils.isNotEmpty(offline)) { offline.stream().map(URIRegisterDTO::getNamespaceId) diff --git a/shenyu-admin/src/main/java/org/apache/shenyu/admin/mode/cluster/service/ShenyuClusterService.java b/shenyu-admin/src/main/java/org/apache/shenyu/admin/mode/cluster/service/ShenyuClusterService.java index 0430202159d9..87a87781a9c2 100644 --- a/shenyu-admin/src/main/java/org/apache/shenyu/admin/mode/cluster/service/ShenyuClusterService.java +++ b/shenyu-admin/src/main/java/org/apache/shenyu/admin/mode/cluster/service/ShenyuClusterService.java @@ -19,6 +19,7 @@ import org.apache.shenyu.admin.config.properties.ClusterProperties; import org.apache.shenyu.admin.mode.ShenyuRunningModeService; +import org.apache.shenyu.admin.service.impl.InstanceCheckService; import org.apache.shenyu.admin.service.impl.UpstreamCheckService; import org.apache.shenyu.admin.service.manager.LoadServiceDocEntry; import org.apache.shenyu.common.concurrent.ShenyuThreadFactory; @@ -43,9 +44,12 @@ public class ShenyuClusterService implements ShenyuRunningModeService { private final ScheduledExecutorService executorService; private final ClusterProperties clusterProperties; + + private final InstanceCheckService instanceCheckService; public ShenyuClusterService(final ClusterSelectMasterService shenyuClusterSelectMasterService, final UpstreamCheckService upstreamCheckService, + final InstanceCheckService instanceCheckService, final LoadServiceDocEntry loadServiceDocEntry, final ClusterProperties clusterProperties) { this.shenyuClusterSelectMasterService = shenyuClusterSelectMasterService; @@ -54,6 +58,7 @@ public ShenyuClusterService(final ClusterSelectMasterService shenyuClusterSelect this.clusterProperties = clusterProperties; this.executorService = new ScheduledThreadPoolExecutor(1, ShenyuThreadFactory.create("master-selector", true)); + this.instanceCheckService = instanceCheckService; } /** @@ -85,7 +90,8 @@ private void doSelectMaster(final String host, final String port, final String c // start upstream check task upstreamCheckService.setup(); - + + instanceCheckService.setup(); // load api loadServiceDocEntry.loadApiDocument(); @@ -106,6 +112,7 @@ private void doSelectMaster(final String host, final String port, final String c LOG.error("select master error", e); // close the upstream check service upstreamCheckService.close(); + instanceCheckService.close(); String message = String.format("renew master fail, %s", e.getMessage()); throw new ShenyuException(message); diff --git a/shenyu-admin/src/main/java/org/apache/shenyu/admin/mode/standalone/ShenyuStandaloneService.java b/shenyu-admin/src/main/java/org/apache/shenyu/admin/mode/standalone/ShenyuStandaloneService.java index ebb11c448892..36e441626edc 100644 --- a/shenyu-admin/src/main/java/org/apache/shenyu/admin/mode/standalone/ShenyuStandaloneService.java +++ b/shenyu-admin/src/main/java/org/apache/shenyu/admin/mode/standalone/ShenyuStandaloneService.java @@ -18,6 +18,7 @@ package org.apache.shenyu.admin.mode.standalone; import org.apache.shenyu.admin.mode.ShenyuRunningModeService; +import org.apache.shenyu.admin.service.impl.InstanceCheckService; import org.apache.shenyu.admin.service.impl.UpstreamCheckService; import org.apache.shenyu.admin.service.manager.LoadServiceDocEntry; @@ -26,17 +27,24 @@ public class ShenyuStandaloneService implements ShenyuRunningModeService { private final UpstreamCheckService upstreamCheckService; private final LoadServiceDocEntry loadServiceDocEntry; + + private final InstanceCheckService instanceCheckService; public ShenyuStandaloneService(final UpstreamCheckService upstreamCheckService, - final LoadServiceDocEntry loadServiceDocEntry) { + final LoadServiceDocEntry loadServiceDocEntry, + final InstanceCheckService instanceCheckService) { this.upstreamCheckService = upstreamCheckService; this.loadServiceDocEntry = loadServiceDocEntry; + this.instanceCheckService = instanceCheckService; } @Override public void start(final String host, final int port, final String contextPath) { // start upstream check task upstreamCheckService.setup(); + + instanceCheckService.setup(); + // load api loadServiceDocEntry.loadApiDocument(); } @@ -44,5 +52,6 @@ public void start(final String host, final int port, final String contextPath) { @Override public void shutdown() { upstreamCheckService.close(); + instanceCheckService.close(); } } diff --git a/shenyu-admin/src/main/java/org/apache/shenyu/admin/model/entity/InstanceInfoDO.java b/shenyu-admin/src/main/java/org/apache/shenyu/admin/model/entity/InstanceInfoDO.java index 30adf5f15825..c74417392ec4 100644 --- a/shenyu-admin/src/main/java/org/apache/shenyu/admin/model/entity/InstanceInfoDO.java +++ b/shenyu-admin/src/main/java/org/apache/shenyu/admin/model/entity/InstanceInfoDO.java @@ -17,7 +17,7 @@ package org.apache.shenyu.admin.model.entity; -import org.apache.shenyu.admin.model.dto.InstanceInfoDTO; +import org.apache.shenyu.admin.model.vo.InstanceInfoVO; import org.apache.shenyu.common.utils.UUIDUtils; import java.sql.Timestamp; @@ -39,6 +39,8 @@ public final class InstanceInfoDO extends BaseDO { private Integer instanceState; private String namespaceId; + + private long lastHeartBeatTime; public InstanceInfoDO() { @@ -99,27 +101,7 @@ public InstanceInfoDO(final String id, final Timestamp dateCreated, final Timest this.instanceInfo = instanceInfo; this.namespaceId = namespaceId; } - - /** - * build InstanceInfoDO. - * - * @param instanceInfoDTO instanceInfoDTO - * @return InstanceInfoDO - */ - public static InstanceInfoDO buildInstanceInfoDO(final InstanceInfoDTO instanceInfoDTO) { - Timestamp currentTime = new Timestamp(System.currentTimeMillis()); - return InstanceInfoDO.builder() - .id(UUIDUtils.getInstance().generateShortUuid()) - .instanceIp(instanceInfoDTO.getInstanceIp()) - .instancePort(instanceInfoDTO.getInstancePort()) - .instanceType(instanceInfoDTO.getInstanceType()) - .instanceInfo(instanceInfoDTO.getInstanceInfo()) - .instanceState(instanceInfoDTO.getInstanceState()) - .namespaceId(instanceInfoDTO.getNamespaceId()) - .dateCreated(currentTime) - .dateUpdated(currentTime) - .build(); - } + /** * get instanceIp. @@ -228,7 +210,25 @@ public String getNamespaceId() { public void setNamespaceId(final String namespaceId) { this.namespaceId = namespaceId; } - + + /** + * get lastHeartBeatTime. + * + * @return lastHeartBeatTime + */ + public long getLastHeartBeatTime() { + return lastHeartBeatTime; + } + + /** + * set lastHeartBeatTime. + * + * @param lastHeartBeatTime lastHeartBeatTime + */ + public void setLastHeartBeatTime(final long lastHeartBeatTime) { + this.lastHeartBeatTime = lastHeartBeatTime; + } + /** * builder. * @@ -262,6 +262,27 @@ public boolean equals(final Object o) { public int hashCode() { return Objects.hash(super.hashCode(), instanceIp, instancePort, instanceType, instanceInfo, instanceState, namespaceId); } + + /** + * build InstanceInfoDO. + * + * @param instanceInfoVO instanceInfoVO + * @return InstanceInfoDO + */ + public static InstanceInfoDO buildInstanceInfoDO(final InstanceInfoVO instanceInfoVO) { + Timestamp currentTime = new Timestamp(System.currentTimeMillis()); + return InstanceInfoDO.builder() + .id(UUIDUtils.getInstance().generateShortUuid()) + .instanceIp(instanceInfoVO.getInstanceIp()) + .instancePort(instanceInfoVO.getInstancePort()) + .instanceType(instanceInfoVO.getInstanceType()) + .instanceInfo(instanceInfoVO.getInstanceInfo()) + .instanceState(instanceInfoVO.getInstanceState()) + .namespaceId(instanceInfoVO.getNamespaceId()) + .dateCreated(currentTime) + .dateUpdated(currentTime) + .build(); + } public static final class InstanceInfoDOBuilder { diff --git a/shenyu-admin/src/main/java/org/apache/shenyu/admin/model/event/instance/InstanceInfoReportEvent.java b/shenyu-admin/src/main/java/org/apache/shenyu/admin/model/event/instance/InstanceInfoReportEvent.java new file mode 100644 index 000000000000..82f14b1a6b36 --- /dev/null +++ b/shenyu-admin/src/main/java/org/apache/shenyu/admin/model/event/instance/InstanceInfoReportEvent.java @@ -0,0 +1,301 @@ +/* + * 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.admin.model.event.instance; + +/** + * InstanceInfoReportDTO. + */ +public class InstanceInfoReportEvent { + + /** + * instance ip. + */ + private String instanceIp; + + /** + * instance port. + */ + private String instancePort; + + /** + * instance type. + */ + private String instanceType; + + /** + * instance info. + */ + private String instanceInfo; + + /** + * instance state. + */ + private Integer instanceState; + + /** + * namespace id. + */ + private String namespaceId; + + public InstanceInfoReportEvent(final String instanceIp, final String instancePort, final String instanceType, final String instanceInfo, final String namespaceId) { + this.instanceIp = instanceIp; + this.instancePort = instancePort; + this.instanceType = instanceType; + this.instanceInfo = instanceInfo; + this.namespaceId = namespaceId; + } + + private InstanceInfoReportEvent(final Builder builder) { + this.instanceIp = builder.instanceIp; + this.instancePort = builder.instancePort; + this.instanceType = builder.instanceType; + this.instanceInfo = builder.instanceInfo; + this.instanceState = builder.instanceSate; + this.namespaceId = builder.namespaceId; + } + + /** + * builder method. + * + * @return builder object. + */ + public static Builder builder() { + return new Builder(); + } + + /** + * get instance ip. + * + * @return instance ip + */ + public String getInstanceIp() { + return instanceIp; + } + + /** + * set instance ip. + * + * @param instanceIp instance ip + */ + public void setInstanceIp(final String instanceIp) { + this.instanceIp = instanceIp; + } + + /** + * get instance port. + * + * @return instance port + */ + public String getInstancePort() { + return instancePort; + } + + /** + * set instance port. + * + * @param instancePort instance port + */ + public void setInstancePort(final String instancePort) { + this.instancePort = instancePort; + } + + /** + * get instance type. + * + * @return instance type + */ + public String getInstanceType() { + return instanceType; + } + + /** + * set instance type. + * + * @param instanceType instance type + */ + public void setInstanceType(final String instanceType) { + this.instanceType = instanceType; + } + + /** + * get instance info. + * + * @return instance info + */ + public String getInstanceInfo() { + return instanceInfo; + } + + /** + * set instance info. + * + * @param instanceInfo instance info + */ + public void setInstanceInfo(final String instanceInfo) { + this.instanceInfo = instanceInfo; + } + + /** + * get instanceState. + * + * @return instanceState + */ + public Integer getInstanceState() { + return instanceState; + } + + /** + * set instanceState. + * + * @param instanceState instanceState + */ + public void setInstanceState(final Integer instanceState) { + this.instanceState = instanceState; + } + + /** + * get namespace id. + * + * @return namespace id + */ + public String getNamespaceId() { + return namespaceId; + } + + /** + * set namespace id. + * + * @param namespaceId namespace id + */ + public void setNamespaceId(final String namespaceId) { + this.namespaceId = namespaceId; + } + + + public static final class Builder { + + /** + * instance ip. + */ + private String instanceIp; + + /** + * instance port. + */ + private String instancePort; + + /** + * instance type. + */ + private String instanceType; + + /** + * instance info. + */ + private String instanceInfo; + + /** + * instance state. + */ + private Integer instanceSate; + + /** + * namespace id. + */ + private String namespaceId; + + private Builder() { + } + + /** + * instance type. + * + * @param instanceType instance type + * @return InstanceInfoRegisterDTO.Builder + */ + public Builder instanceType(final String instanceType) { + this.instanceType = instanceType; + return this; + } + + /** + * instance info. + * + * @param instanceInfo instance info + * @return InstanceInfoRegisterDTO.Builder + */ + public Builder instanceInfo(final String instanceInfo) { + this.instanceInfo = instanceInfo; + return this; + } + + /** + * instance ip. + * + * @param instanceIp instance ip + * @return InstanceInfoRegisterDTO.Builder + */ + public Builder instanceIp(final String instanceIp) { + this.instanceIp = instanceIp; + return this; + } + + /** + * instance port. + * + * @param instancePort instance port + * @return InstanceInfoRegisterDTO.Builder + */ + public Builder instancePort(final String instancePort) { + this.instancePort = instancePort; + return this; + } + + /** + * instance state. + * + * @param instanceState instance state + * @return InstanceInfoRegisterDTO.Builder + */ + public Builder instanceState(final Integer instanceState) { + this.instanceSate = instanceState; + return this; + } + + /** + * namespace id. + * + * @param namespaceId namespace id + * @return InstanceInfoRegisterDTO.Builder + */ + public Builder namespaceId(final String namespaceId) { + this.namespaceId = namespaceId; + return this; + } + + /** + * build. + * + * @return InstanceInfoRegisterDTO instance info register dto + */ + public InstanceInfoReportEvent build() { + return new InstanceInfoReportEvent(this); + } + + } +} diff --git a/shenyu-admin/src/main/java/org/apache/shenyu/admin/model/vo/InstanceDataVisualLineVO.java b/shenyu-admin/src/main/java/org/apache/shenyu/admin/model/vo/InstanceDataVisualLineVO.java new file mode 100644 index 000000000000..e2dd0960dfd9 --- /dev/null +++ b/shenyu-admin/src/main/java/org/apache/shenyu/admin/model/vo/InstanceDataVisualLineVO.java @@ -0,0 +1,72 @@ +/* + * 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.admin.model.vo; + +import java.util.List; + +public class InstanceDataVisualLineVO { + + private String name; + + private List data; + + public InstanceDataVisualLineVO() { + } + + public InstanceDataVisualLineVO(final String name, final List data) { + this.name = name; + this.data = data; + } + + /** + * Gets the value of name. + * + * @return the value of name + */ + public String getName() { + return name; + } + + /** + * set name. + * + * @param name name + */ + public void setName(final String name) { + this.name = name; + } + + /** + * Gets the value of data. + * + * @return the value of data + */ + public List getData() { + return data; + } + + /** + * set data. + * + * @param data data + */ + public void setData(final List data) { + this.data = data; + } + +} diff --git a/shenyu-admin/src/main/java/org/apache/shenyu/admin/model/vo/InstanceDataVisualVO.java b/shenyu-admin/src/main/java/org/apache/shenyu/admin/model/vo/InstanceDataVisualVO.java new file mode 100644 index 000000000000..202dd71aa5e2 --- /dev/null +++ b/shenyu-admin/src/main/java/org/apache/shenyu/admin/model/vo/InstanceDataVisualVO.java @@ -0,0 +1,112 @@ +/* + * 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.admin.model.vo; + + +import java.util.List; + +public class InstanceDataVisualVO { + + private List pieData; + + private List lineData; + + /** + * Gets the value of pieData. + * + * @return the value of pieData + */ + public List getPieData() { + return pieData; + } + + /** + * set pieData. + * + * @param pieData pieData + */ + public void setPieData(final List pieData) { + this.pieData = pieData; + } + + /** + * Gets the value of lineData. + * + * @return the value of lineData + */ + public List getLineData() { + return lineData; + } + + /** + * set lineData. + * + * @param lineData lineData + */ + public void setLineData(final List lineData) { + this.lineData = lineData; + } + + public static class Entry { + + private String name; + + private Long value; + + public Entry(final String name, final Long value) { + this.name = name; + this.value = value; + } + + /** + * Gets the value of name. + * + * @return the value of name + */ + public String getName() { + return name; + } + + /** + * set name. + * + * @param name name + */ + public void setName(final String name) { + this.name = name; + } + + /** + * Gets the value . + * + * @return the value + */ + public Long getValue() { + return value; + } + + /** + * set value. + * + * @param value value + */ + public void setValue(final Long value) { + this.value = value; + } + } +} diff --git a/shenyu-admin/src/main/java/org/apache/shenyu/admin/model/vo/InstanceInfoVO.java b/shenyu-admin/src/main/java/org/apache/shenyu/admin/model/vo/InstanceInfoVO.java index be41fe52b9b8..722f33a78524 100644 --- a/shenyu-admin/src/main/java/org/apache/shenyu/admin/model/vo/InstanceInfoVO.java +++ b/shenyu-admin/src/main/java/org/apache/shenyu/admin/model/vo/InstanceInfoVO.java @@ -52,6 +52,11 @@ public class InstanceInfoVO implements Serializable { * namespaceId. */ private String namespaceId; + + /** + * status. + */ + private int instanceState; /** * created time. @@ -62,6 +67,11 @@ public class InstanceInfoVO implements Serializable { * updated time. */ private Timestamp dateUpdated; + + /** + * lastBeat time. + */ + private long lastHeartBeatTime; /** * get instanceIp. @@ -188,7 +198,43 @@ public Timestamp getDateUpdated() { public void setDateUpdated(final Timestamp dateUpdated) { this.dateUpdated = dateUpdated; } - + + /** + * get instanceState. + * + * @return instanceState + */ + public int getInstanceState() { + return instanceState; + } + + /** + * set instanceState. + * + * @param instanceState instanceState + */ + public void setInstanceState(final int instanceState) { + this.instanceState = instanceState; + } + + /** + * get lastHeartBeatTime. + * + * @return lastHeartBeatTime + */ + public long getLastHeartBeatTime() { + return lastHeartBeatTime; + } + + /** + * set lastHeartBeatTime. + * + * @param lastHeartBeatTime lastHeartBeatTime + */ + public void setLastHeartBeatTime(final long lastHeartBeatTime) { + this.lastHeartBeatTime = lastHeartBeatTime; + } + @Override public boolean equals(final Object o) { if (this == o) { diff --git a/shenyu-admin/src/main/java/org/apache/shenyu/admin/service/InstanceInfoService.java b/shenyu-admin/src/main/java/org/apache/shenyu/admin/service/InstanceInfoService.java index 78cbb09d5646..54102d822e7c 100644 --- a/shenyu-admin/src/main/java/org/apache/shenyu/admin/service/InstanceInfoService.java +++ b/shenyu-admin/src/main/java/org/apache/shenyu/admin/service/InstanceInfoService.java @@ -17,20 +17,21 @@ package org.apache.shenyu.admin.service; -import org.apache.shenyu.admin.model.dto.InstanceInfoDTO; import org.apache.shenyu.admin.model.page.CommonPager; import org.apache.shenyu.admin.model.query.InstanceQuery; import org.apache.shenyu.admin.model.query.InstanceQueryCondition; import org.apache.shenyu.admin.model.vo.InstanceInfoVO; +import java.util.List; + public interface InstanceInfoService extends PageService { - + /** * Creates or updates an instance information record. * - * @param instanceInfoDTO the instance information data transfer object + * @param instanceInfoVO the instance information data transfer object */ - void createOrUpdate(InstanceInfoDTO instanceInfoDTO); + void createOrUpdate(InstanceInfoVO instanceInfoVO); /** * List instance info by page. @@ -39,6 +40,8 @@ public interface InstanceInfoService extends PageService listByPage(InstanceQuery instanceQuery); + + List list(); /** * findById. diff --git a/shenyu-admin/src/main/java/org/apache/shenyu/admin/service/impl/InstanceCheckService.java b/shenyu-admin/src/main/java/org/apache/shenyu/admin/service/impl/InstanceCheckService.java new file mode 100644 index 000000000000..e5654cfbb28d --- /dev/null +++ b/shenyu-admin/src/main/java/org/apache/shenyu/admin/service/impl/InstanceCheckService.java @@ -0,0 +1,262 @@ +/* + * 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.admin.service.impl; + +import jakarta.annotation.PreDestroy; +import org.apache.commons.lang3.StringUtils; +import org.apache.shenyu.admin.model.event.instance.InstanceInfoReportEvent; +import org.apache.shenyu.admin.model.vo.InstanceDataVisualLineVO; +import org.apache.shenyu.admin.model.vo.InstanceDataVisualVO; +import org.apache.shenyu.admin.model.vo.InstanceInfoVO; +import org.apache.shenyu.admin.service.InstanceInfoService; +import org.apache.shenyu.common.concurrent.ShenyuThreadFactory; +import org.apache.shenyu.common.enums.InstanceStatusEnum; +import org.apache.shenyu.register.common.dto.InstanceBeatInfoDTO; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.context.event.EventListener; +import org.springframework.stereotype.Component; +import org.springframework.util.CollectionUtils; + +import java.util.ArrayDeque; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Deque; +import java.util.List; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentLinkedDeque; +import java.util.concurrent.ScheduledThreadPoolExecutor; +import java.util.concurrent.TimeUnit; +import java.util.stream.Collectors; + +/** + * This is the client check service. + */ +@Component +public class InstanceCheckService { + + private static final int MAX_HISTORY_SIZE = 20; + + private static final Logger LOG = LoggerFactory.getLogger(InstanceCheckService.class); + + private ScheduledThreadPoolExecutor executor; + + private final int scheduledTime; + + private ConcurrentHashMap instanceHealthBeatInfo; + + private long instanceHeartBeatTimeOut; + + private long deleteTimeout; + + private InstanceInfoService instanceInfoService; + + private final Map> stateHistoryMap; + + public InstanceCheckService(final InstanceInfoService instanceInfoService) { + this.scheduledTime = 10; + this.instanceHealthBeatInfo = new ConcurrentHashMap<>(); + this.instanceHeartBeatTimeOut = 1000 * 20; + this.deleteTimeout = 1000 * 60; + this.instanceInfoService = instanceInfoService; + this.stateHistoryMap = new ConcurrentHashMap<>(); + } + + /** + * Set up. + */ + public void setup() { + this.fetchInstanceData(); + executor = new ScheduledThreadPoolExecutor(1, ShenyuThreadFactory.create("scheduled-instance-heartbeat-task", false)); + executor.scheduleWithFixedDelay(this::scheduled, 30, scheduledTime, TimeUnit.SECONDS); + executor.scheduleWithFixedDelay(this::syncDB, 40, scheduledTime, TimeUnit.SECONDS); + } + + /** + * fetch instance status data from db. + */ + public void fetchInstanceData() { + List list = instanceInfoService.list(); + list.forEach(instanceInfoVO -> { + String instanceKey = getInstanceKey(instanceInfoVO); + instanceHealthBeatInfo.put(instanceKey, instanceInfoVO); + }); + } + + public String getInstanceKey(final InstanceInfoVO instanceInfoVO) { + return instanceInfoVO.getInstanceIp() + ":" + instanceInfoVO.getInstancePort() + "@" + instanceInfoVO.getInstanceType() + "#" + instanceInfoVO.getNamespaceId(); + } + + public String getInstanceKey(final InstanceBeatInfoDTO instanceBeatInfoDTO) { + return instanceBeatInfoDTO.getInstanceIp() + ":" + instanceBeatInfoDTO.getInstancePort() + "@" + instanceBeatInfoDTO.getInstanceType() + "#" + instanceBeatInfoDTO.getNamespaceId(); + } + + public InstanceInfoVO getInstanceHealthBeatInfo(final InstanceBeatInfoDTO instanceBeatInfoDTO) { + return instanceHealthBeatInfo.get(getInstanceKey(instanceBeatInfoDTO)); + } + + public InstanceInfoVO getInstanceHealthBeatInfo(final String instanceKey) { + return instanceHealthBeatInfo.get(instanceKey); + } + + public void handleBeatInfo(final InstanceBeatInfoDTO instanceBeatInfoDTO) { + String instanceKey = getInstanceKey(instanceBeatInfoDTO); + if (instanceHealthBeatInfo.containsKey(instanceKey)) { + InstanceInfoVO instanceInfoVO = instanceHealthBeatInfo.get(instanceKey); + instanceInfoVO.setLastHeartBeatTime(System.currentTimeMillis()); + } else { + InstanceInfoVO instanceInfoVO = new InstanceInfoVO(); + instanceInfoVO.setInstanceIp(instanceBeatInfoDTO.getInstanceIp()); + instanceInfoVO.setInstanceState(1); + instanceInfoVO.setInstanceInfo(instanceBeatInfoDTO.getInstanceInfo()); + instanceInfoVO.setInstanceType(instanceBeatInfoDTO.getInstanceType()); + instanceInfoVO.setLastHeartBeatTime(System.currentTimeMillis()); + instanceInfoVO.setInstancePort(instanceBeatInfoDTO.getInstancePort()); + instanceInfoVO.setNamespaceId(instanceBeatInfoDTO.getNamespaceId()); + instanceInfoVO.setLastHeartBeatTime(System.currentTimeMillis()); + instanceHealthBeatInfo.put(instanceKey, instanceInfoVO); + } + } + + private void scheduled() { + try { + doCheck(); + } catch (Exception e) { + LOG.error("upstream scheduled check error", e); + } + } + + private void doCheck() { + instanceHealthBeatInfo.values().forEach(instance -> { + if (System.currentTimeMillis() - instance.getLastHeartBeatTime() > instanceHeartBeatTimeOut) { + if (InstanceStatusEnum.ONLINE.getCode() == instance.getInstanceState()) { + LOG.info("[instanceHealthInfo]namespace:{},type:{},Ip:{},Port:{} offline!", + instance.getNamespaceId(), instance.getInstanceType(), instance.getInstanceIp(), instance.getInstancePort()); + instance.setInstanceState(InstanceStatusEnum.OFFLINE.getCode()); + } + } else { + LOG.info("[instanceHealthInfo]namespace:{},type:{},Ip:{},Port:{} online!", + instance.getNamespaceId(), instance.getInstanceType(), instance.getInstanceIp(), instance.getInstancePort()); + instance.setInstanceState(InstanceStatusEnum.ONLINE.getCode()); + } + if (System.currentTimeMillis() - instance.getLastHeartBeatTime() > deleteTimeout) { + if (InstanceStatusEnum.OFFLINE.getCode() == instance.getInstanceState()) { + LOG.info("[instanceHealthInfo]namespace:{},type:{},Ip:{},Port:{} deleted!", + instance.getNamespaceId(), instance.getInstanceType(), instance.getInstanceIp(), instance.getInstancePort()); + instance.setInstanceState(InstanceStatusEnum.DELETED.getCode()); + } + } + collectStateData(); + }); + } + + public void syncDB() { + instanceHealthBeatInfo.values().forEach(vo -> { + instanceInfoService.createOrUpdate(vo); + }); + } + + /** + * Close relative resource on container destroy. + */ + @PreDestroy + public void close() { + syncDB(); + instanceHealthBeatInfo.clear(); + executor.shutdown(); + } + + /** + * listen {@link InstanceInfoReportEvent} instance info report event. + * + * @param event event + */ + @EventListener(InstanceInfoReportEvent.class) + public void onInstanceInfoReport(final InstanceInfoReportEvent event) { + InstanceBeatInfoDTO instanceBeatInfoDTO = buildInstanceInfoDTO(event); + handleBeatInfo(instanceBeatInfoDTO); + } + + private InstanceBeatInfoDTO buildInstanceInfoDTO(final InstanceInfoReportEvent instanceInfoRegisterDTO) { + InstanceBeatInfoDTO instanceInfoDTO = new InstanceBeatInfoDTO(); + instanceInfoDTO.setInstanceIp(instanceInfoRegisterDTO.getInstanceIp()); + instanceInfoDTO.setInstancePort(instanceInfoRegisterDTO.getInstancePort()); + instanceInfoDTO.setInstanceType(instanceInfoRegisterDTO.getInstanceType()); + instanceInfoDTO.setInstanceInfo(instanceInfoRegisterDTO.getInstanceInfo()); + instanceInfoDTO.setNamespaceId(instanceInfoRegisterDTO.getNamespaceId()); + return instanceInfoDTO; + } + + private void collectStateData() { + if (!CollectionUtils.isEmpty(instanceHealthBeatInfo)) { + Map pieData = instanceHealthBeatInfo.values().stream().collect(Collectors.groupingBy(InstanceInfoVO::getInstanceState, Collectors.counting())); + updateStateHistory(pieData); + } + } + + public InstanceDataVisualVO getInstanceDataVisual(final String namespaceId) { + InstanceDataVisualVO instanceDataVisualVO = new InstanceDataVisualVO(); + List instanceInfoVOS = instanceHealthBeatInfo.values().stream().toList(); + if (StringUtils.isNotBlank(namespaceId)) { + instanceInfoVOS = instanceInfoVOS.stream().filter(vo -> namespaceId.equals(vo.getNamespaceId())).collect(Collectors.toList()); + } + Map pieData = instanceInfoVOS.stream().collect(Collectors.groupingBy(InstanceInfoVO::getInstanceState, Collectors.counting())); + List lineList = new ArrayList<>(); + for (Integer state : Arrays.asList(0, 1, 2)) { + Deque queue = stateHistoryMap.getOrDefault(state, new ArrayDeque<>(MAX_HISTORY_SIZE)); + List data = new ArrayList<>(queue); + while (data.size() < MAX_HISTORY_SIZE) { + data.add(0, 0L); + } + InstanceDataVisualLineVO dto = new InstanceDataVisualLineVO( + InstanceStatusEnum.getNameByCode(state), + data + ); + lineList.add(dto); + } + List pieDataList = pieData.entrySet().stream() + .map(entry -> { + Integer stateCode = entry.getKey(); + String stateName = InstanceStatusEnum.getNameByCode(stateCode); + return new InstanceDataVisualVO.Entry(stateName, entry.getValue()); + }) + .collect(Collectors.toList()); + instanceDataVisualVO.setPieData(pieDataList); + instanceDataVisualVO.setLineData(lineList); + return instanceDataVisualVO; + } + + private void updateStateHistory(final Map currentData) { + ensureStateQueues(); + for (Integer state : Arrays.asList(0, 1, 2)) { + Long count = currentData.getOrDefault(state, 0L); + Deque queue = stateHistoryMap.get(state); + queue.addLast(count); + while (queue.size() > MAX_HISTORY_SIZE) { + queue.removeFirst(); + } + } + } + + private void ensureStateQueues() { + for (Integer state : Arrays.asList(0, 1, 2)) { + stateHistoryMap.putIfAbsent(state, new ConcurrentLinkedDeque<>()); + } + } +} diff --git a/shenyu-admin/src/main/java/org/apache/shenyu/admin/service/impl/InstanceInfoServiceImpl.java b/shenyu-admin/src/main/java/org/apache/shenyu/admin/service/impl/InstanceInfoServiceImpl.java index 4c13b9081edb..367905f588d5 100644 --- a/shenyu-admin/src/main/java/org/apache/shenyu/admin/service/impl/InstanceInfoServiceImpl.java +++ b/shenyu-admin/src/main/java/org/apache/shenyu/admin/service/impl/InstanceInfoServiceImpl.java @@ -18,7 +18,6 @@ package org.apache.shenyu.admin.service.impl; import org.apache.shenyu.admin.mapper.InstanceInfoMapper; -import org.apache.shenyu.admin.model.dto.InstanceInfoDTO; import org.apache.shenyu.admin.model.entity.InstanceInfoDO; import org.apache.shenyu.admin.model.page.CommonPager; import org.apache.shenyu.admin.model.page.PageResultUtils; @@ -50,16 +49,16 @@ public InstanceInfoServiceImpl(final InstanceInfoMapper instanceInfoMapper) { } @Override - public void createOrUpdate(final InstanceInfoDTO instanceInfoDTO) { + public void createOrUpdate(final InstanceInfoVO instanceInfoVO) { InstanceQuery instanceQuery = new InstanceQuery(); - instanceQuery.setInstanceIp(instanceInfoDTO.getInstanceIp()); - instanceQuery.setInstancePort(instanceInfoDTO.getInstancePort()); - instanceQuery.setInstanceType(instanceInfoDTO.getInstanceType()); - instanceQuery.setNamespaceId(instanceInfoDTO.getNamespaceId()); + instanceQuery.setInstanceIp(instanceInfoVO.getInstanceIp()); + instanceQuery.setInstancePort(instanceInfoVO.getInstancePort()); + instanceQuery.setInstanceType(instanceInfoVO.getInstanceType()); + instanceQuery.setNamespaceId(instanceInfoVO.getNamespaceId()); InstanceInfoDO infoDO = instanceInfoMapper.selectOneByQuery(instanceQuery); if (Objects.isNull(infoDO)) { - LOG.info("Register new instance info: {}", GsonUtils.getInstance().toJson(instanceInfoDTO)); - InstanceInfoDO instanceInfoDO = InstanceInfoDO.buildInstanceInfoDO(instanceInfoDTO); + LOG.info("Register new instance info: {}", GsonUtils.getInstance().toJson(instanceQuery)); + InstanceInfoDO instanceInfoDO = InstanceInfoDO.buildInstanceInfoDO(instanceInfoVO); try { instanceInfoMapper.insert(instanceInfoDO); } catch (Exception e) { @@ -67,13 +66,14 @@ public void createOrUpdate(final InstanceInfoDTO instanceInfoDTO) { } return; } - LOG.info("Update instance info: {}", GsonUtils.getInstance().toJson(instanceInfoDTO)); - infoDO.setInstanceIp(instanceInfoDTO.getInstanceIp()); - infoDO.setInstanceType(instanceInfoDTO.getInstanceType()); - infoDO.setInstanceInfo(instanceInfoDTO.getInstanceInfo()); - infoDO.setNamespaceId(instanceInfoDTO.getNamespaceId()); + LOG.info("Update instance info: {}", GsonUtils.getInstance().toJson(instanceInfoVO)); + infoDO.setInstanceIp(instanceInfoVO.getInstanceIp()); + infoDO.setInstanceType(instanceInfoVO.getInstanceType()); + infoDO.setInstanceInfo(instanceInfoVO.getInstanceInfo()); + infoDO.setNamespaceId(instanceInfoVO.getNamespaceId()); infoDO.setDateUpdated(Timestamp.from(Instant.now())); - infoDO.setInstanceState(instanceInfoDTO.getInstanceState()); + infoDO.setInstanceState(instanceInfoVO.getInstanceState()); + infoDO.setLastHeartBeatTime(instanceInfoVO.getLastHeartBeatTime()); instanceInfoMapper.updateById(infoDO); } @@ -83,6 +83,11 @@ public CommonPager listByPage(final InstanceQuery instanceQuery) return PageResultUtils.result(instanceQuery.getPageParameter(), () -> this.buildInstanceInfoVO(instanceInfoDOList)); } + @Override + public List list() { + return this.buildInstanceInfoVO(instanceInfoMapper.selectAll()); + } + @Override public InstanceInfoVO findById(final String id) { return null; diff --git a/shenyu-admin/src/main/java/org/apache/shenyu/admin/service/impl/UpstreamCheckService.java b/shenyu-admin/src/main/java/org/apache/shenyu/admin/service/impl/UpstreamCheckService.java index a370be0a906e..5e559ebddd71 100644 --- a/shenyu-admin/src/main/java/org/apache/shenyu/admin/service/impl/UpstreamCheckService.java +++ b/shenyu-admin/src/main/java/org/apache/shenyu/admin/service/impl/UpstreamCheckService.java @@ -279,7 +279,7 @@ private void scheduled() { doCheck(); waitFinish(); } catch (Exception e) { - LOG.error("upstream scheduled check error -------- ", e); + LOG.error("upstream scheduled check error", e); } } diff --git a/shenyu-admin/src/main/java/org/apache/shenyu/admin/service/publish/InstanceInfoReportEventPublisher.java b/shenyu-admin/src/main/java/org/apache/shenyu/admin/service/publish/InstanceInfoReportEventPublisher.java new file mode 100644 index 000000000000..f6648b5eb1e1 --- /dev/null +++ b/shenyu-admin/src/main/java/org/apache/shenyu/admin/service/publish/InstanceInfoReportEventPublisher.java @@ -0,0 +1,42 @@ +/* + * 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.admin.service.publish; + +import org.apache.shenyu.admin.model.event.instance.InstanceInfoReportEvent; +import org.springframework.context.ApplicationEventPublisher; + +/** + * InstanceInfoReportEventPublisher. + */ +public class InstanceInfoReportEventPublisher { + + private final ApplicationEventPublisher publisher; + + public InstanceInfoReportEventPublisher(final ApplicationEventPublisher publisher) { + this.publisher = publisher; + } + + /** + * event. + * + * @param event event. + */ + public void publish(final InstanceInfoReportEvent event) { + publisher.publishEvent(event); + } +} diff --git a/shenyu-admin/src/main/java/org/apache/shenyu/admin/service/register/AbstractShenyuClientRegisterServiceImpl.java b/shenyu-admin/src/main/java/org/apache/shenyu/admin/service/register/AbstractShenyuClientRegisterServiceImpl.java index 9a5baf41d79c..231e0f7a0005 100644 --- a/shenyu-admin/src/main/java/org/apache/shenyu/admin/service/register/AbstractShenyuClientRegisterServiceImpl.java +++ b/shenyu-admin/src/main/java/org/apache/shenyu/admin/service/register/AbstractShenyuClientRegisterServiceImpl.java @@ -28,6 +28,7 @@ import org.apache.shenyu.admin.model.dto.RuleDTO; import org.apache.shenyu.admin.model.entity.PluginDO; import org.apache.shenyu.admin.model.entity.SelectorDO; +import org.apache.shenyu.admin.model.event.instance.InstanceInfoReportEvent; import org.apache.shenyu.admin.model.vo.NamespacePluginVO; import org.apache.shenyu.admin.service.DiscoveryService; import org.apache.shenyu.admin.service.DiscoveryUpstreamService; @@ -36,9 +37,11 @@ import org.apache.shenyu.admin.service.SelectorService; import org.apache.shenyu.admin.service.impl.UpstreamCheckService; import org.apache.shenyu.admin.service.manager.RegisterApiDocService; +import org.apache.shenyu.admin.service.publish.InstanceInfoReportEventPublisher; import org.apache.shenyu.admin.utils.CommonUpstreamUtils; import org.apache.shenyu.admin.utils.ShenyuResultMessage; import org.apache.shenyu.common.constant.AdminConstants; +import org.apache.shenyu.common.constant.InstanceTypeConstants; import org.apache.shenyu.common.dto.DiscoverySyncData; import org.apache.shenyu.common.dto.DiscoveryUpstreamData; import org.apache.shenyu.common.dto.SelectorData; @@ -111,6 +114,9 @@ public abstract class AbstractShenyuClientRegisterServiceImpl extends FallbackSh @Resource private NamespacePluginRelMapper namespacePluginRelMapper; + @Resource + private InstanceInfoReportEventPublisher publisher; + /** * Selector handler string. * @@ -250,9 +256,25 @@ public String doHeartbeat(final String selectorName, final List uriRegisterDTO.getPort(), uriRegisterDTO.getProtocol(), uriRegisterDTO.getNamespaceId()); + + try { + // publish instance info event + InstanceInfoReportEvent instanceInfoReportEvent = new InstanceInfoReportEvent( + uriRegisterDTO.getHost(), + String.valueOf(uriRegisterDTO.getPort()), + InstanceTypeConstants.CLIENT_INSTANCE_TYPE, + uriRegisterDTO.getInstanceInfo(), + uriRegisterDTO.getNamespaceId() + ); + publisher.publish(instanceInfoReportEvent); + } catch (Exception e) { + LOG.error("publish instance info error", e); + } + LOG.info("change alive selectorId={}|url={}", selectorId, discoveryUpstreamDTO.getUrl()); discoveryUpstreamService.changeStatusBySelectorIdAndUrl(selectorId, discoveryUpstreamDTO.getUrl(), Boolean.TRUE); }); + DiscoverySyncData discoverySyncData = fetch(selectorId, selectorDO.getSelectorName(), pluginName, namespaceId); eventPublisher.publishEvent(new DataChangedEvent(ConfigGroupEnum.DISCOVER_UPSTREAM, DataEventTypeEnum.REFRESH, Collections.singletonList(discoverySyncData))); diff --git a/shenyu-bootstrap/pom.xml b/shenyu-bootstrap/pom.xml index 1f79d6e98763..5970db9fc5b7 100644 --- a/shenyu-bootstrap/pom.xml +++ b/shenyu-bootstrap/pom.xml @@ -610,6 +610,11 @@ shenyu-spring-boot-starter-plugin-basic-auth ${project.version} + + org.apache.shenyu + shenyu-spring-boot-starter-client-beat + ${project.version} + diff --git a/shenyu-bootstrap/src/main/resources/application.yml b/shenyu-bootstrap/src/main/resources/application.yml index 3c596c3d0c98..e1da6a1a5b0d 100644 --- a/shenyu-bootstrap/src/main/resources/application.yml +++ b/shenyu-bootstrap/src/main/resources/application.yml @@ -194,10 +194,12 @@ shenyu: # workerCount: 8 # daemon: true register: - enabled: false - registerType: zookeeper #etcd #consul - serverLists: localhost:2181 #http://localhost:2379 #localhost:8848 + enable: true + registerType: http + serverLists: http://localhost:9095 props: + username: admin + password: 123456 cross: enabled: true allowedHeaders: diff --git a/shenyu-client/shenyu-client-core/src/main/java/org/apache/shenyu/client/core/disruptor/subcriber/ShenyuClientURIExecutorSubscriber.java b/shenyu-client/shenyu-client-core/src/main/java/org/apache/shenyu/client/core/disruptor/subcriber/ShenyuClientURIExecutorSubscriber.java index 6fa228dc881d..cdfdbce3ea97 100644 --- a/shenyu-client/shenyu-client-core/src/main/java/org/apache/shenyu/client/core/disruptor/subcriber/ShenyuClientURIExecutorSubscriber.java +++ b/shenyu-client/shenyu-client-core/src/main/java/org/apache/shenyu/client/core/disruptor/subcriber/ShenyuClientURIExecutorSubscriber.java @@ -22,6 +22,7 @@ import org.apache.shenyu.client.core.shutdown.ShenyuClientShutdownHook; import org.apache.shenyu.client.core.shutdown.ShutdownHookManager; import org.apache.shenyu.common.concurrent.ShenyuThreadFactory; +import org.apache.shenyu.common.utils.SystemInfoUtils; import org.apache.shenyu.register.client.api.ShenyuClientRegisterRepository; import org.apache.shenyu.register.common.dto.URIRegisterDTO; import org.apache.shenyu.register.common.enums.EventType; @@ -63,7 +64,7 @@ public ShenyuClientURIExecutorSubscriber(final ShenyuClientRegisterRepository sh ThreadFactory requestFactory = ShenyuThreadFactory.create("heartbeat-reporter", true); executor = new ScheduledThreadPoolExecutor(1, requestFactory); - executor.scheduleAtFixedRate(() -> URIS.forEach(this::sendHeartbeat), 30, 30, TimeUnit.SECONDS); + executor.scheduleAtFixedRate(() -> URIS.forEach(this::sendHeartbeat), 30, 10, TimeUnit.SECONDS); } @Override @@ -116,6 +117,7 @@ public void executor(final Collection dataList) { } private void sendHeartbeat(final URIRegisterDTO uriRegisterDTO) { + uriRegisterDTO.setInstanceInfo(SystemInfoUtils.getSystemInfo()); shenyuClientRegisterRepository.sendHeartbeat(uriRegisterDTO); } } diff --git a/shenyu-common/pom.xml b/shenyu-common/pom.xml index 64ca38c5ab56..a9c4dbbb3188 100644 --- a/shenyu-common/pom.xml +++ b/shenyu-common/pom.xml @@ -89,11 +89,11 @@ bcprov-jdk18on - - - - - + + com.github.oshi + oshi-core + 6.7.0 + diff --git a/shenyu-common/src/main/java/org/apache/shenyu/common/constant/Constants.java b/shenyu-common/src/main/java/org/apache/shenyu/common/constant/Constants.java index ee7071aa9d44..0a5b4be8d8bc 100644 --- a/shenyu-common/src/main/java/org/apache/shenyu/common/constant/Constants.java +++ b/shenyu-common/src/main/java/org/apache/shenyu/common/constant/Constants.java @@ -31,7 +31,7 @@ public interface Constants { * The constant string dot. */ String DOT = "."; - + /** * The constant string separator. */ @@ -41,238 +41,238 @@ public interface Constants { * The constant string separator. */ String SEPARATOR_UNDERLINE = "_"; - + /** * The constant SUCCESS. */ String SUCCESS = "success"; - + /** * The constant APP_PARAM. */ String APP_PARAM = "appParam"; - + /** * The constant context. */ String CONTEXT = "context"; - + /** * The constant chain. */ String CHAIN = "chain"; - + /** * The constant context path. */ String CONTEXT_PATH = "contextPath"; - + /** * The constant META_DATA. */ String META_DATA = "metaData"; - + /** * The constant OLD_CONTEXT_PATH_META_DATA. */ String OLD_CONTEXT_PATH_META_DATA = "old_context_path_meta_data"; - + /** * The constant OLD_META_DATA. */ String OLD_META_DATA = "old_meta_data"; - + /** * The constant CLIENT_RESPONSE_ATTR. */ String CLIENT_RESPONSE_ATTR = "webHandlerClientResponse"; - + /** * The constant DUBBO_RPC_RESULT_EMPTY. */ String DUBBO_RPC_RESULT_EMPTY = "dubbo has not return value!"; - + /** * The constant SOFA_RPC_RESULT_EMPTY. */ String SOFA_RPC_RESULT_EMPTY = "sofa has not return value!"; - + /** * The constant RPC_RESULT. */ String RPC_RESULT = "rpc_result"; - + /** * The constant MOTAN_RPC_RESULT. */ String MOTAN_RPC_RESULT = "motan_rpc_result"; - + /** * The constant TARS_RPC_RESULT_EMPTY. */ String TARS_RPC_RESULT_EMPTY = "tars has not return value!"; - + /** * The constant MOTAN_RPC_RESULT_EMPTY. */ String MOTAN_RPC_RESULT_EMPTY = "motan has not return value!"; - + /** * The constant CLIENT_RESPONSE_RESULT_TYPE. */ String CLIENT_RESPONSE_RESULT_TYPE = "webHandlerClientResponseResultType"; - + /** * The constant CLIENT_RESPONSE_CONN_ATTR. */ String CLIENT_RESPONSE_CONN_ATTR = "nettyClientResponseConnection"; - + /** * The constant HTTP_TIME_OUT. */ String HTTP_TIME_OUT = "httpTimeOut"; - + /** * The constant HTTP_RETRY. */ String HTTP_RETRY = "httpRetry"; - + /** * The constant RETRY_STRATEGY. */ String RETRY_STRATEGY = "retryStrategy"; - + /** * The constant LOAD_BALANCE. */ String LOAD_BALANCE = "loadBalance"; - + /** * divide online selector id. */ String DIVIDE_SELECTOR_ID = "divideSelectorId"; - + /** * Original response Content-Type attribute name. */ String ORIGINAL_RESPONSE_CONTENT_TYPE_ATTR = "original_response_content_type"; - + /** * The constant HTTP_URI. */ String HTTP_URI = "httpUri"; - + /** * The constant HTTP_DOMAIN. */ String HTTP_DOMAIN = "httpDomain"; - + /** * The constant RPC_PARAM_TRANSFORM. */ String PARAM_TRANSFORM = "param_transform"; - + /** * The constant DECODE. */ String DECODE = "UTF-8"; - + /** * The constant MODULE. */ String MODULE = "module"; - + /** * RESPONSE_MONO. */ String RESPONSE_MONO = "RESPONSE_MONO"; - + /** * WATCHER_HTTP_STATUS. */ String WATCHER_HTTP_STATUS = "WATCHER_HTTP_STATUS"; - + /** * The constant METHOD. */ String METHOD = "method"; - + /** * The constant APP_KEY. */ String APP_KEY = "appKey"; - + /** * The constant RPC_TYPE. */ String RPC_TYPE = "rpcType"; - + /** * The constant SIGN. */ String SIGN = "sign"; - + /** * The constant PATH. */ String PATH = "path"; - + /** * The constant VERSION. */ String VERSION = "version"; - + /** * The constant SIGN_PARAMS_ERROR. */ String SIGN_PARAMS_ERROR = "sign parameters are incomplete!"; - + /** * The constant SIGN_VERSION_ERROR. */ String SIGN_VERSION_ERROR = "sign version does not exist or is wrong!"; - + /** * The constant SIGN_APP_KEY_IS_NOT_EXIST. */ String SIGN_APP_KEY_IS_NOT_EXIST = "sign appKey does not exist."; - + /** * The constant SIGN_PATH_NOT_EXIST. */ String SIGN_PATH_NOT_EXIST = "you have not configured the sign path."; - + /** * The constant SIGN_VALUE_IS_ERROR. */ String SIGN_VALUE_IS_ERROR = "signature value is error!"; - + /** * The constant TIMESTAMP. */ String TIMESTAMP = "timestamp"; - + /** * The constant REJECT_MSG. */ String REJECT_MSG = " You are forbidden to visit"; - + /** * The constant REWRITE_URI. */ String REWRITE_URI = "rewrite_uri"; - + /** * The constant REWRITE_CONTEXT_PATH. */ String REWRITE_CONTEXT_PATH = "rewrite_context_path"; - + /** * The constant URI. */ String URI = "uri"; - + /** * The constant HEARTBEAT. */ @@ -287,164 +287,164 @@ public interface Constants { * The constant LINE_SEPARATOR. */ String LINE_SEPARATOR = System.lineSeparator(); - + /** * The constant URL_SEPARATOR. */ String PATH_SEPARATOR = "/"; - + /** * hystrix withExecutionIsolationSemaphoreMaxConcurrentRequests. */ int MAX_CONCURRENT_REQUESTS = 100; - + /** * hystrix withCircuitBreakerErrorThresholdPercentage. */ int ERROR_THRESHOLD_PERCENTAGE = 50; - + /** * hystrix withCircuitBreakerRequestVolumeThreshold. */ int REQUEST_VOLUME_THRESHOLD = 20; - + /** * hystrix withCircuitBreakerSleepWindowInMilliseconds. */ int SLEEP_WINDOW_INMILLISECONDS = 5000; - + /** * The constant TIME_OUT. */ long TIME_OUT = 3000; - + /** * The constant COLONS. */ String COLONS = ":"; - + /** * hystrix thead pool core size. */ int HYSTRIX_THREAD_POOL_CORE_SIZE = 10; - + /** * hystrix thread pool max size. */ int HYSTRIX_THREAD_POOL_MAX_SIZE = 10; - + /** * hystrix thread pool keep alive time minutes. */ int HYSTRIX_THREAD_KEEP_ALIVE_TIME_MINUTE = 1; - + /** * hystrix thread pool queue size. */ int HYSTRIX_THREAD_POOL_QUEUE_SIZE = 12; - + /** * ratelimiter timeoutDurationRate. */ int TIMEOUT_DURATION_RATE = 5000; - + /** * ratelimiter limitRefreshPeriod. */ int LIMIT_REFRESH_PERIOD = 500; - + /** * ratelimiter limitForPeriod. */ int LIMIT_FOR_PERIOD = 50; - + /** * circuitBreaker circuitEnable. */ int CIRCUIT_DISABLE = 0; - + /** * circuitBreaker enable. */ int CIRCUIT_ENABLE = 1; - + /** * circuitBreaker timeoutDuration. */ long TIMEOUT_DURATION = 30000; - + /** * circuitBreaker slidingWindowSize. */ int SLIDING_WINDOW_SIZE = 100; - + /** * circuitBreaker slidingWindowType. */ int SLIDING_WINDOW_TYPE = 0; - + /** * circuitBreaker minimumNumberOfCalls. */ int MINIMUM_NUMBER_OF_CALLS = 100; - + /** * circuitBreaker waitIntervalFunctionInOpenState. */ int WAIT_INTERVAL_FUNCTION_IN_OPEN_STATE = 60000; - + /** * circuitBreaker waitIntervalFunctionInOpenState. */ int PERMITTED_NUMBER_OF_CALLS_IN_HALF_OPEN_STATE = 10; - + /** * circuitBreaker failureRateThreshold. */ float FAILURE_RATE_THRESHOLD = 50; - + /** * circuitBreaker automaticTransitionFromOpenToHalfOpenEnabled. */ boolean AUTOMATIC_TRANSITION_FROM_OPEN_TO_HALF_OPEN_ENABLED = false; - + /** * jwt handle key for secretKey. */ String SECRET_KEY = "secretKey"; - + /** * basicAuth handle key for defaultHandleJson. */ String DEFAULT_HANDLE_JSON = "defaultHandleJson"; - + /** * local key. */ String LOCAL_KEY = "localKey"; - + /** * jwt handle key for filterPath. */ String FILTER_PATH = "filterPath"; - + /** - * Disable flow control rules. + * Disable flow control rules. */ int FLOW_RULE_ENABLE_OFF = 0; /** - * Enable flow control rules. + * Enable flow control rules. */ int FLOW_RULE_ENABLE_ON = 1; /** - * Disable flow degrade rules. + * Disable flow degrade rules. */ int DEGRADE_RULE_ENABLE_OFF = 0; /** - * Enable flow degrade rules. + * Enable flow degrade rules. */ int DEGRADE_RULE_ENABLE_ON = 1; @@ -452,122 +452,122 @@ public interface Constants { * Enable the flow rule. */ int SENTINEL_ENABLE_FLOW_RULE = 1; - + /** * Sentinel qps flow grade. */ int SENTINEL_QPS_FLOW_GRADE = 1; - + /** * Sentinel flow reject behavior. */ int SENTINEL_FLOW_REJECT = 0; - + /** * Enable the degrade rule. */ int SENTINEL_ENABLE_DEGRADE_RULE = 1; - + /** * Sentinel response RT degrade rule. */ int SENTINEL_RESPONSE_RULE_GRADE = 0; - + /** * Sentinel degrade rule default min request. */ int SENTINEL_MIN_REQUEST_AMOUNT = 5; - + /** * Sentinel degrade rule default slow ratio threshold. */ double SENTINEL_SLOW_RATIO_THRESHOLD = 1.0d; - + /** * Sentinel degrade rule default stat intervals. */ int SENTINEL_STAT_INTERVALS = 1; - + /** * default warmup. */ int DEFAULT_WARMUP = 10 * 60 * 1000; - + /** * default register type. */ String DEFAULT_REGISTER_TYPE = "http"; - + /** * is checked. */ String IS_CHECKED = "checked"; - + /** * default checked value. */ String DEFAULT_CHECK_VALUE = "false"; - + /** * zombie check threads. */ String ZOMBIE_CHECK_THREADS = "zombieCheckThreads"; - + /** * default zombie check threads value. */ String ZOMBIE_CHECK_THREADS_VALUE = "10"; - + /** * zombie check times. */ String ZOMBIE_CHECK_TIMES = "zombieCheckTimes"; - + /** * default zombie check times value. */ String ZOMBIE_CHECK_TIMES_VALUE = "5"; - + /** * scheduled time. */ String SCHEDULED_TIME = "scheduledTime"; - + /** * default scheduled time value. */ String SCHEDULED_TIME_VALUE = "10"; - + /** * default headerMaxSize value. */ int HEADER_MAX_SIZE = 10240; - + /** * default requestMaxSize value. */ int REQUEST_MAX_SIZE = 102400; - + /** * String default. */ String DEFAULT = "DEFAULT"; - + /** * context path name prefix. */ String CONTEXT_PATH_NAME_PREFIX = "/context-path"; - + /** * dubbo gray release selector id. */ String DUBBO_SELECTOR_ID = "dubboSelectorId"; - + /** * dubbo gray release rule id. */ String DUBBO_RULE_ID = "dubboRuleId"; - + /** * dubbo remote address. */ @@ -587,67 +587,72 @@ public interface Constants { * dubbo default application name. */ String DUBBO_DEFAULT_APPLICATION_NAME = "shenyu_proxy"; - + /** * dubbo group. */ String GROUP = "group"; - + /** * redis script path. */ String SCRIPT_PATH = "/META-INF/scripts/"; - + /** * cache max count. */ int CACHE_MAX_COUNT = 1000; - + /** * the empty json. */ String EMPTY_JSON = "{}"; - + /** * http accept encoding gzip. */ String HTTP_ACCEPT_ENCODING_GZIP = "gzip"; - + /** * general context. */ String GENERAL_CONTEXT = "generalContext"; - + /** * addGeneralContextType. */ String ADD_GENERAL_CONTEXT_TYPE = "addGeneralContext"; - + /** * transmitHeaderToGeneralContext. */ String TRANSMIT_HEADER_TO_GENERAL_CONTEXT_TYPE = "transmitHeaderToGeneralContext"; - + /** * When register by http, the meta register path. */ String META_PATH = "/shenyu-client/register-metadata"; - + /** * When register by http, the meta type. */ String META_TYPE = "metadata"; - + /** * When register by http, the uri path. */ String URI_PATH = "/shenyu-client/register-uri"; - + + /** + * When register by http, the uri path. + */ + String BEAT_URI_PATH = "/instance/beat"; + /** * When register by http, the offline path. */ String OFFLINE_PATH = "/shenyu-client/offline"; - + /** * The constant API_DOC_TYPE. */ @@ -677,12 +682,12 @@ public interface Constants { * When register by http, admin username. */ String USER_NAME = "username"; - + /** * Login name. */ String LOGIN_NAME = "userName"; - + /** * When register by http, admin password. */ @@ -702,87 +707,87 @@ public interface Constants { * X-Access-Token. */ String X_ACCESS_TOKEN = "X-Access-Token"; - + /** * The admin return result code. */ String ADMIN_RESULT_CODE = "code"; - + /** * The admin return result data. */ String ADMIN_RESULT_DATA = "data"; - + /** * The admin return result token. */ String ADMIN_RESULT_TOKEN = "token"; - + /** * The admin return result expired time. */ String ADMIN_RESULT_EXPIRED_TIME = "expiredTime"; - + /** * The admin userName. */ String ADMIN_RESULT_USERNAME = "userName"; - + /** * The admin password. */ String ADMIN_RESULT_PASSWORD = "password"; - + /** * shenyu admin path configs fetch. */ String SHENYU_ADMIN_PATH_CONFIGS_FETCH = "/configs/fetch"; - + /** * shenyu admin path configs listener. */ String SHENYU_ADMIN_PATH_CONFIGS_LISTENER = "/configs/listener"; - + /** * zombie removal times. */ String ZOMBIE_REMOVAL_TIMES = "zombieRemovalTimes"; - + /** * The default zombie removal time value, unit is second. */ String ZOMBIE_REMOVAL_TIMES_VALUE = "60"; - + /** * shared thread pool type. */ String SHARED = "shared"; - + /** * fixed thread pool type. */ String FIXED = "fixed"; - + /** * eager thread pool type. */ String EAGER = "eager"; - + /** * limited thread pool type. */ String LIMITED = "limited"; - + /** * cached thread pool type. */ String CACHED = "cached"; - + /** * user can use the specify-domain to replace of upstream url of the divide plugin. */ String SPECIFY_DOMAIN = "specify-domain"; - + /** * The maximum free memory reserved by the blocking queue for the JVM. */ @@ -794,33 +799,32 @@ public interface Constants { * The default cluster of dubbo client. */ String DEFAULT_CLUSTER = "failover"; - + /** * cache data max size, means map size. */ Long LRU_MAP_MAXSIZE = 65536L; - + /** * namespace,sush as nacos . */ String NAMESPACE = "namespace"; - + /** * brpc spi bizThreadPoolName. */ String SHARED_BIZTHREADPOOLNAME = "shared"; - - /** + /** * trie default children size. */ Integer TRIE_CHILDREN_SIZE = 512; - + /** * trie default path variables size. */ Integer TRIE_PATH_VARIABLES_SIZE = 128; - + /** * trie default path cache size. */ @@ -830,7 +834,7 @@ public interface Constants { * the default warm time is ten minutes. */ int WARMUP_TIME = 10 * 60 * 1000; - + /** * The constant DEFAULT_RULE. */ @@ -875,82 +879,82 @@ public interface Constants { * Hystrix plugin metrics. */ String METRICS_HYSTRIX = "metricsHystrix"; - + /** * The constant shenyu namespace id. */ String SHENYU_NAMESPACE_ID = "namespaceId"; - + /** * The constant SYS_DEFAULT_NAMESPACE_ID. */ String SYS_DEFAULT_NAMESPACE_ID = "649330b6-c2d7-4edc-be8e-8a54df9eb385"; - + /** * The constant DEFAULT_NAMESPACE_PRIMARY_KEY. */ String DEFAULT_NAMESPACE_PRIMARY_KEY = "1"; - + /** * The constant EVENT_NAME_DICT. */ String EVENT_NAME_DICT = "dict"; - + /** * The constant EVENT_NAME_PLUGIN_HANDLE. */ String EVENT_NAME_PLUGIN_HANDLE = "plugin-handle"; - + /** * The constant EVENT_NAME_META_DATA. */ String EVENT_NAME_META_DATA = "meta-data"; - + /** * The constant EVENT_NAME_NAMESPACE. */ String EVENT_NAME_NAMESPACE = "namespace"; - + /** * The constant EVENT_NAME_PLUGIN. */ String EVENT_NAME_PLUGIN = "plugin"; - + /** * The constant EVENT_NAME_NAMESPACE_PLUGIN. */ String EVENT_NAME_NAMESPACE_PLUGIN = "namespace-plugin"; - + /** * The constant EVENT_NAME_RESOURCE. */ String EVENT_NAME_RESOURCE = "resource"; - + /** * The constant EVENT_NAME_ROLE. */ String EVENT_NAME_ROLE = "role"; - + /** * The constant EVENT_NAME_RULE. */ String EVENT_NAME_RULE = "rule"; - + /** * The constant EVENT_NAME_SELECTOR. */ String EVENT_NAME_SELECTOR = "selector"; - + /** * The constant EVENT_NAME_USER. */ String EVENT_NAME_USER = "user"; - + /** * The constant EVENT_NAME_DATA. */ String EVENT_NAME_DATA = "data"; - + /** * The constant EVENT_NAME_REGISTER. */ @@ -980,7 +984,7 @@ public interface Constants { * The constant messages. */ String MESSAGES = "messages"; - + /** * The constant Content-Encoding. */ @@ -994,12 +998,12 @@ public interface Constants { * The constant Content. */ String CONTENT = "content"; - + /** * The constant ROLE. */ String ROLE = "role"; - + /** * The constant USAGE. */ @@ -1014,21 +1018,46 @@ public interface Constants { * The stream_options. */ String STREAM_OPTIONS = "stream_options"; - + /** * The constant COMPLETION_TOKENS. */ String COMPLETION_TOKENS = "completion_tokens"; - + /** * The constant AI_MODEL. */ String AI_MODEL = "ai_model"; - + + /** + * The constant arch. + */ + String ARCH = "arch"; + + /** + * The constant arch. + */ + String OPERATING_SYSTEM = "operatingSystem"; + + /** + * The constant availableProcessors. + */ + String AVAILABLE_PROCESSORS = "availableProcessors"; + + /** + * The constant totalMemorySizeGB. + */ + String TOTAL_MEMORY_SIZE_GB = "totalMemorySizeGB"; + + /** + * The constant GB. + */ + String GB = "GB"; + /** * String q. */ default void findConstants() { } - + } diff --git a/shenyu-common/src/main/java/org/apache/shenyu/common/enums/InstanceStatusEnum.java b/shenyu-common/src/main/java/org/apache/shenyu/common/enums/InstanceStatusEnum.java new file mode 100644 index 000000000000..bba3fb5ac818 --- /dev/null +++ b/shenyu-common/src/main/java/org/apache/shenyu/common/enums/InstanceStatusEnum.java @@ -0,0 +1,61 @@ +/* + * 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.common.enums; + +public enum InstanceStatusEnum { + + /** + * DELETED. + */ + DELETED(0, "DELETED"), + /** + * ONLINE. + */ + ONLINE(1, "ONLINE"), + + /** + * OFFLINE. + */ + OFFLINE(2, "OFFLINE"); + + private final int code; + + private final String name; + + InstanceStatusEnum(final int code, final String name) { + this.code = code; + this.name = name; + } + + public int getCode() { + return code; + } + + public String getName() { + return name; + } + + public static String getNameByCode(final int code) { + for (InstanceStatusEnum status : values()) { + if (status.getCode() == code) { + return status.getName(); + } + } + return DELETED.name; + } +} diff --git a/shenyu-common/src/main/java/org/apache/shenyu/common/utils/SystemInfoUtils.java b/shenyu-common/src/main/java/org/apache/shenyu/common/utils/SystemInfoUtils.java new file mode 100644 index 000000000000..828db2e52675 --- /dev/null +++ b/shenyu-common/src/main/java/org/apache/shenyu/common/utils/SystemInfoUtils.java @@ -0,0 +1,87 @@ +/* + * 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.common.utils; + +import com.sun.management.OperatingSystemMXBean; +import org.apache.shenyu.common.exception.ShenyuException; +import oshi.SystemInfo; + +import java.lang.management.ManagementFactory; +import java.math.BigDecimal; +import java.math.RoundingMode; +import java.util.Map; + +import static org.apache.shenyu.common.constant.Constants.ARCH; +import static org.apache.shenyu.common.constant.Constants.AVAILABLE_PROCESSORS; +import static org.apache.shenyu.common.constant.Constants.GB; +import static org.apache.shenyu.common.constant.Constants.OPERATING_SYSTEM; +import static org.apache.shenyu.common.constant.Constants.TOTAL_MEMORY_SIZE_GB; + +/** + * The type System info utils. + */ +public final class SystemInfoUtils { + + private static final int BYTES_IN_KB = 1024; + + private static final int BYTES_IN_MB = BYTES_IN_KB * 1024; + + private static final int BYTES_IN_GB = BYTES_IN_MB * 1024; + + private static final int DECIMAL_PLACES = 2; + + private static final RoundingMode ROUNDING_MODE = RoundingMode.HALF_UP; + + /** + * Gets system info. + * + * @return the system info + */ + public static String getSystemInfo() { + try { + // Get host information using OSHI + SystemInfo systemInfo = new SystemInfo(); + + // Get host information + OperatingSystemMXBean osBean = + (OperatingSystemMXBean) ManagementFactory.getOperatingSystemMXBean(); + Map hostInfo = Map.of( + ARCH, osBean.getArch(), + OPERATING_SYSTEM, systemInfo.getOperatingSystem().toString(), + AVAILABLE_PROCESSORS, osBean.getAvailableProcessors(), + TOTAL_MEMORY_SIZE_GB, bytesToGB(osBean.getTotalMemorySize()) + GB + ); + return GsonUtils.getInstance().toJson(hostInfo); + } catch (Exception e) { + // Handle any exceptions that may occur + throw new ShenyuException("Error retrieving system information: " + e.getMessage()); + } + } + + /** + * Bytes to gb double. + * + * @param bytesValue the bytes value + * @return the double + */ + private static double bytesToGB(final long bytesValue) { + return BigDecimal.valueOf(bytesValue / (double) BYTES_IN_GB) + .setScale(DECIMAL_PLACES, ROUNDING_MODE) + .doubleValue(); + } +} diff --git a/shenyu-register-center/pom.xml b/shenyu-register-center/pom.xml index aa42126f0c2f..6ca886e6feba 100644 --- a/shenyu-register-center/pom.xml +++ b/shenyu-register-center/pom.xml @@ -29,6 +29,7 @@ shenyu-register-common shenyu-register-client + shenyu-register-client-beat diff --git a/shenyu-register-center/shenyu-register-client-beat/pom.xml b/shenyu-register-center/shenyu-register-client-beat/pom.xml new file mode 100644 index 000000000000..272105abd08c --- /dev/null +++ b/shenyu-register-center/shenyu-register-client-beat/pom.xml @@ -0,0 +1,42 @@ + + + + + org.apache.shenyu + shenyu-register-center + 2.7.0.3-SNAPSHOT + + 4.0.0 + + shenyu-register-client-beat + + + + org.apache.shenyu + shenyu-register-client-http + ${project.version} + + + org.springframework.boot + spring-boot-starter-web + provided + + + 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 new file mode 100644 index 000000000000..a5db7aad439e --- /dev/null +++ b/shenyu-register-center/shenyu-register-client-beat/src/main/java/org/apache/shenyu/register/client/beat/HeartbeatListener.java @@ -0,0 +1,78 @@ +/* + * 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.common.concurrent.ShenyuThreadFactory; +import org.apache.shenyu.common.config.ShenyuConfig; +import org.apache.shenyu.common.constant.InstanceTypeConstants; +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.common.dto.InstanceBeatInfoDTO; +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.concurrent.ScheduledThreadPoolExecutor; +import java.util.concurrent.TimeUnit; + +public class HeartbeatListener { + + private static final Logger LOG = LoggerFactory.getLogger(HeartbeatListener.class); + + private static final int INITIAL_DELAY = 0; + + private static final int CHECK_PERIOD = 5; + + private ScheduledThreadPoolExecutor executor; + + private final ShenyuClientRegisterRepository httpClientRegisterRepository; + + private final ShenyuConfig shenyuConfig; + + public HeartbeatListener(final ShenyuClientRegisterRepository httpClientRegisterRepository, 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()); + executor.scheduleAtFixedRate(() -> { + InstanceBeatInfoDTO instanceBeatInfoDTO = new InstanceBeatInfoDTO(); + instanceBeatInfoDTO.setInstancePort(String.valueOf(serverProperties.getPort())); + instanceBeatInfoDTO.setInstanceIp(IpUtils.getHost()); + instanceBeatInfoDTO.setNamespaceId(shenyuConfig.getNamespace()); + instanceBeatInfoDTO.setInstanceInfo(SystemInfoUtils.getSystemInfo()); + instanceBeatInfoDTO.setInstanceType(InstanceTypeConstants.BOOTSTRAP_INSTANCE_INFO); + httpClientRegisterRepository.sendHeartbeat(instanceBeatInfoDTO); + }, INITIAL_DELAY, CHECK_PERIOD, TimeUnit.SECONDS + ); + } + + @EventListener(ContextClosedEvent.class) + public void onShutdown() { + executor.shutdown(); + try { + if (!executor.awaitTermination(5, TimeUnit.SECONDS)) { + executor.shutdownNow(); + } + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + } + } +} \ No newline at end of file diff --git a/shenyu-register-center/shenyu-register-client/shenyu-register-client-api/src/main/java/org/apache/shenyu/register/client/api/ShenyuClientRegisterRepository.java b/shenyu-register-center/shenyu-register-client/shenyu-register-client-api/src/main/java/org/apache/shenyu/register/client/api/ShenyuClientRegisterRepository.java index 43eead031afb..e12963160d98 100644 --- a/shenyu-register-center/shenyu-register-client/shenyu-register-client-api/src/main/java/org/apache/shenyu/register/client/api/ShenyuClientRegisterRepository.java +++ b/shenyu-register-center/shenyu-register-client/shenyu-register-client-api/src/main/java/org/apache/shenyu/register/client/api/ShenyuClientRegisterRepository.java @@ -19,6 +19,7 @@ import org.apache.shenyu.register.common.config.ShenyuRegisterCenterConfig; import org.apache.shenyu.register.common.dto.ApiDocRegisterDTO; +import org.apache.shenyu.register.common.dto.InstanceBeatInfoDTO; import org.apache.shenyu.register.common.dto.MetaDataRegisterDTO; import org.apache.shenyu.register.common.dto.URIRegisterDTO; import org.apache.shenyu.spi.SPI; @@ -68,6 +69,14 @@ default void offline(URIRegisterDTO offlineDTO) { default void sendHeartbeat(URIRegisterDTO heartbeatDTO) { } + /** + * Send heartbeat. + * + * @param instanceBeatInfoDTO the instanceBeatInfo dto + */ + default void sendHeartbeat(InstanceBeatInfoDTO instanceBeatInfoDTO) { + } + /** * persistApiDoc. * @param apiDocRegisterDTO apiDocRegisterDTO diff --git a/shenyu-register-center/shenyu-register-client/shenyu-register-client-http/src/main/java/org/apache/shenyu/register/client/http/HttpClientRegisterRepository.java b/shenyu-register-center/shenyu-register-client/shenyu-register-client-http/src/main/java/org/apache/shenyu/register/client/http/HttpClientRegisterRepository.java index 17569fa9d301..71adc4c229ab 100644 --- a/shenyu-register-center/shenyu-register-client/shenyu-register-client-http/src/main/java/org/apache/shenyu/register/client/http/HttpClientRegisterRepository.java +++ b/shenyu-register-center/shenyu-register-client/shenyu-register-client-http/src/main/java/org/apache/shenyu/register/client/http/HttpClientRegisterRepository.java @@ -32,6 +32,7 @@ import org.apache.shenyu.register.common.config.ShenyuRegisterCenterConfig; import org.apache.shenyu.register.common.dto.ApiDocRegisterDTO; import org.apache.shenyu.register.common.dto.DiscoveryConfigRegisterDTO; +import org.apache.shenyu.register.common.dto.InstanceBeatInfoDTO; import org.apache.shenyu.register.common.dto.MetaDataRegisterDTO; import org.apache.shenyu.register.common.dto.URIRegisterDTO; import org.apache.shenyu.register.common.enums.EventType; @@ -138,7 +139,12 @@ public void sendHeartbeat(final URIRegisterDTO heartbeatDTO) { heartbeatDTO.setEventType(EventType.HEARTBEAT); doHeartbeat(heartbeatDTO, Constants.URI_PATH); } - + + @Override + public void sendHeartbeat(final InstanceBeatInfoDTO instanceBeatInfoDTO) { + doHeartbeat(instanceBeatInfoDTO, Constants.BEAT_URI_PATH); + } + /** * doPersistApiDoc. * diff --git a/shenyu-admin/src/main/java/org/apache/shenyu/admin/model/dto/InstanceInfoDTO.java b/shenyu-register-center/shenyu-register-common/src/main/java/org/apache/shenyu/register/common/dto/InstanceBeatInfoDTO.java similarity index 58% rename from shenyu-admin/src/main/java/org/apache/shenyu/admin/model/dto/InstanceInfoDTO.java rename to shenyu-register-center/shenyu-register-common/src/main/java/org/apache/shenyu/register/common/dto/InstanceBeatInfoDTO.java index e3dc98a42966..410364c5d99b 100644 --- a/shenyu-admin/src/main/java/org/apache/shenyu/admin/model/dto/InstanceInfoDTO.java +++ b/shenyu-register-center/shenyu-register-common/src/main/java/org/apache/shenyu/register/common/dto/InstanceBeatInfoDTO.java @@ -15,27 +15,9 @@ * limitations under the License. */ -package org.apache.shenyu.admin.model.dto; - -import jakarta.validation.constraints.NotBlank; -import org.apache.shenyu.admin.mapper.NamespaceMapper; -import org.apache.shenyu.admin.validation.annotation.Existed; - -import java.io.Serializable; -import java.util.Objects; - -/** - * InstanceInfoDTO. - */ -public class InstanceInfoDTO implements Serializable { - - private static final long serialVersionUID = 3644412439977354321L; - - /** - * instance id. - */ - private String instanceId; +package org.apache.shenyu.register.common.dto; +public class InstanceBeatInfoDTO { /** * instance ip. */ @@ -56,36 +38,11 @@ public class InstanceInfoDTO implements Serializable { */ private String instanceInfo; - /** - * instance state. - */ - private Integer instanceState; - /** * namespaceId. */ - @NotBlank - @Existed(message = "namespaceId is not existed", provider = NamespaceMapper.class) private String namespaceId; - /** - * get getInstanceId. - * - * @return InstanceId - */ - public String getInstanceId() { - return instanceId; - } - - /** - * set instanceId. - * - * @param instanceId instanceId - */ - public void setInstanceId(final String instanceId) { - this.instanceId = instanceId; - } - /** * get instanceIp. * @@ -158,24 +115,6 @@ public void setInstanceInfo(final String instanceInfo) { this.instanceInfo = instanceInfo; } - /** - * get instanceState. - * - * @return instanceState - */ - public Integer getInstanceState() { - return instanceState; - } - - /** - * set instanceState. - * - * @param instanceState instanceState - */ - public void setInstanceState(final Integer instanceState) { - this.instanceState = instanceState; - } - /** * get namespaceId. * @@ -194,24 +133,4 @@ public void setNamespaceId(final String namespaceId) { this.namespaceId = namespaceId; } - @Override - public boolean equals(final Object o) { - if (this == o) { - return true; - } - if (Objects.isNull(o) || getClass() != o.getClass()) { - return false; - } - - InstanceInfoDTO that = (InstanceInfoDTO) o; - return Objects.equals(instanceIp, that.instanceIp) - && Objects.equals(instanceType, that.instanceType) - && Objects.equals(instanceInfo, that.instanceInfo) - && Objects.equals(namespaceId, that.namespaceId); - } - - @Override - public int hashCode() { - return Objects.hash(instanceIp, instanceType, instanceInfo, namespaceId); - } } diff --git a/shenyu-register-center/shenyu-register-common/src/main/java/org/apache/shenyu/register/common/dto/URIRegisterDTO.java b/shenyu-register-center/shenyu-register-common/src/main/java/org/apache/shenyu/register/common/dto/URIRegisterDTO.java index 36b169029081..032c4102e2fd 100644 --- a/shenyu-register-center/shenyu-register-common/src/main/java/org/apache/shenyu/register/common/dto/URIRegisterDTO.java +++ b/shenyu-register-center/shenyu-register-common/src/main/java/org/apache/shenyu/register/common/dto/URIRegisterDTO.java @@ -27,7 +27,7 @@ * The type URI register dto. */ public class URIRegisterDTO implements DataTypeParent { - + private String protocol; private String appName; @@ -41,9 +41,14 @@ public class URIRegisterDTO implements DataTypeParent { private Integer port; private EventType eventType; - + private String namespaceId; - + + /** + * instance info. + */ + private String instanceInfo; + /** * Instantiates a new Uri register dto. * @@ -54,10 +59,12 @@ public class URIRegisterDTO implements DataTypeParent { * @param host the host * @param port the port * @param eventType the event type + * @param namespaceId the namespace id + * @param instanceInfo the instance info */ public URIRegisterDTO(final String protocol, final String appName, final String contextPath, final String rpcType, final String host, final Integer port, - final EventType eventType, final String namespaceId) { + final EventType eventType, final String namespaceId, final String instanceInfo) { this.protocol = protocol; this.appName = appName; this.contextPath = contextPath; @@ -66,8 +73,9 @@ public URIRegisterDTO(final String protocol, final String appName, final String this.port = port; this.eventType = eventType; this.namespaceId = namespaceId; + this.instanceInfo = instanceInfo; } - + /** * Instantiates a new Uri register dto. */ @@ -83,8 +91,9 @@ private URIRegisterDTO(final Builder builder) { port = builder.port; eventType = builder.eventType; namespaceId = builder.namespaceId; + instanceInfo = builder.instanceInfo; } - + /** * Trans form uri register dto. * @@ -99,7 +108,7 @@ public static URIRegisterDTO transForm(final MetaDataRegisterDTO metaDataRegiste .host(metaDataRegisterDTO.getHost()) .port(metaDataRegisterDTO.getPort()).build(); } - + /** * return builder. * @@ -118,7 +127,7 @@ public static Builder builder() { public DataType getType() { return DataType.URI; } - + /** * Gets protocol. * @@ -127,7 +136,7 @@ public DataType getType() { public String getProtocol() { return protocol; } - + /** * Sets protocol. * @@ -136,7 +145,7 @@ public String getProtocol() { public void setProtocol(final String protocol) { this.protocol = protocol; } - + /** * getAppName. * @@ -145,7 +154,7 @@ public void setProtocol(final String protocol) { public String getAppName() { return appName; } - + /** * setAppName. * @@ -154,7 +163,7 @@ public String getAppName() { public void setAppName(final String appName) { this.appName = appName; } - + /** * getContextPath. * @@ -163,7 +172,7 @@ public void setAppName(final String appName) { public String getContextPath() { return contextPath; } - + /** * setContextPath. * @@ -172,7 +181,7 @@ public String getContextPath() { public void setContextPath(final String contextPath) { this.contextPath = contextPath; } - + /** * getRpcType. * @@ -181,7 +190,7 @@ public void setContextPath(final String contextPath) { public String getRpcType() { return rpcType; } - + /** * setRpcType. * @@ -190,7 +199,7 @@ public String getRpcType() { public void setRpcType(final String rpcType) { this.rpcType = rpcType; } - + /** * getHost. * @@ -199,7 +208,7 @@ public void setRpcType(final String rpcType) { public String getHost() { return host; } - + /** * setHost. * @@ -208,7 +217,7 @@ public String getHost() { public void setHost(final String host) { this.host = host; } - + /** * getPort. * @@ -217,7 +226,7 @@ public void setHost(final String host) { public Integer getPort() { return port; } - + /** * setPort. * @@ -226,7 +235,7 @@ public Integer getPort() { public void setPort(final Integer port) { this.port = port; } - + /** * getEventType. * @@ -235,7 +244,7 @@ public void setPort(final Integer port) { public EventType getEventType() { return eventType; } - + /** * getNamespaceId. * @@ -244,7 +253,7 @@ public EventType getEventType() { public String getNamespaceId() { return namespaceId; } - + /** * setNamespaceId. * @@ -253,7 +262,25 @@ public String getNamespaceId() { public void setNamespaceId(final String namespaceId) { this.namespaceId = namespaceId; } - + + /** + * getInstanceInfo. + * + * @return String instance info + */ + public String getInstanceInfo() { + return instanceInfo; + } + + /** + * setInstanceInfo. + * + * @param instanceInfo instance info + */ + public void setInstanceInfo(final String instanceInfo) { + this.instanceInfo = instanceInfo; + } + /** * setEventType. * @@ -281,12 +308,14 @@ public boolean equals(final Object o) { && Objects.equals(getHost(), that.getHost()) && Objects.equals(getPort(), that.getPort()) && getEventType() == that.getEventType() - && Objects.equals(getNamespaceId(), that.getNamespaceId()); + && Objects.equals(getNamespaceId(), that.getNamespaceId()) + && Objects.equals(getInstanceInfo(), that.getInstanceInfo()); } @Override public int hashCode() { - return Objects.hash(getProtocol(), getAppName(), getContextPath(), getRpcType(), getHost(), getPort(), getEventType()); + return Objects.hash(getProtocol(), getAppName(), getContextPath(), getRpcType(), getHost(), + getPort(), getEventType(), getNamespaceId(), getInstanceInfo()); } @Override @@ -313,6 +342,8 @@ public String toString() { + eventType + ", namespaceId=" + namespaceId + + ", instanceInfo=" + + instanceInfo + '}'; } @@ -320,7 +351,7 @@ public String toString() { * The type Builder. */ public static final class Builder { - + private String protocol; private String appName; @@ -334,12 +365,14 @@ public static final class Builder { private Integer port; private EventType eventType; - + private String namespaceId; + private String instanceInfo; + private Builder() { } - + /** * protocol. * @@ -350,7 +383,7 @@ public Builder protocol(final String protocol) { this.protocol = protocol; return this; } - + /** * appName. * @@ -361,7 +394,7 @@ public Builder appName(final String appName) { this.appName = appName; return this; } - + /** * contextPath. * @@ -372,7 +405,7 @@ public Builder contextPath(final String contextPath) { this.contextPath = contextPath; return this; } - + /** * rpcType. * @@ -383,7 +416,7 @@ public Builder rpcType(final String rpcType) { this.rpcType = rpcType; return this; } - + /** * host. * @@ -394,7 +427,7 @@ public Builder host(final String host) { this.host = host; return this; } - + /** * port. * @@ -405,7 +438,7 @@ public Builder port(final Integer port) { this.port = port; return this; } - + /** * eventType. * @@ -416,7 +449,7 @@ public Builder eventType(final EventType eventType) { this.eventType = eventType; return this; } - + /** * namespace. * @@ -428,6 +461,17 @@ public Builder namespaceId(final String namespaceId) { return this; } + /** + * instanceInfo. + * + * @param instanceInfo instance info + * @return Builder builder + */ + public Builder instanceInfo(final String instanceInfo) { + this.instanceInfo = instanceInfo; + return this; + } + /** * build. * diff --git a/shenyu-spring-boot-starter/shenyu-spring-boot-starter-client/pom.xml b/shenyu-spring-boot-starter/shenyu-spring-boot-starter-client/pom.xml index 80b601a60ae2..693897416f43 100644 --- a/shenyu-spring-boot-starter/shenyu-spring-boot-starter-client/pom.xml +++ b/shenyu-spring-boot-starter/shenyu-spring-boot-starter-client/pom.xml @@ -35,5 +35,6 @@ shenyu-spring-boot-starter-client-common shenyu-spring-boot-starter-client-motan shenyu-spring-boot-starter-client-spring-websocket + shenyu-spring-boot-starter-client-beat diff --git a/shenyu-spring-boot-starter/shenyu-spring-boot-starter-client/shenyu-spring-boot-starter-client-beat/pom.xml b/shenyu-spring-boot-starter/shenyu-spring-boot-starter-client/shenyu-spring-boot-starter-client-beat/pom.xml new file mode 100644 index 000000000000..61c7c53eff76 --- /dev/null +++ b/shenyu-spring-boot-starter/shenyu-spring-boot-starter-client/shenyu-spring-boot-starter-client-beat/pom.xml @@ -0,0 +1,48 @@ + + + + + org.apache.shenyu + shenyu-spring-boot-starter-client + 2.7.0.3-SNAPSHOT + + 4.0.0 + + shenyu-spring-boot-starter-client-beat + + + 17 + 17 + UTF-8 + + + + + org.apache.shenyu + shenyu-spring-boot-starter-client-common + ${project.version} + + + org.apache.shenyu + shenyu-register-client-beat + ${project.version} + + + 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 new file mode 100644 index 000000000000..a38d03815d3a --- /dev/null +++ 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 @@ -0,0 +1,49 @@ +/* + * 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.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.web.ServerProperties; +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") +public class HeartbeatListenerConfiguration { + + /** + * Heartbeat bean listener. + * + * @param httpClientRegisterRepository the http client register repository + * @param shenyuConfig the shenyu config + * @param serverProperties the server properties + * @return the heartbeat bean listener. + */ + @Bean + public HeartbeatListener heartbeatListener(final ShenyuClientRegisterRepository httpClientRegisterRepository, + final ShenyuConfig shenyuConfig, + final ServerProperties serverProperties) { + return new HeartbeatListener(httpClientRegisterRepository, shenyuConfig, serverProperties); + } + +} diff --git a/shenyu-spring-boot-starter/shenyu-spring-boot-starter-client/shenyu-spring-boot-starter-client-beat/src/main/resources/META-INF/spring.factories b/shenyu-spring-boot-starter/shenyu-spring-boot-starter-client/shenyu-spring-boot-starter-client-beat/src/main/resources/META-INF/spring.factories new file mode 100644 index 000000000000..23326a881f1f --- /dev/null +++ b/shenyu-spring-boot-starter/shenyu-spring-boot-starter-client/shenyu-spring-boot-starter-client-beat/src/main/resources/META-INF/spring.factories @@ -0,0 +1,19 @@ +# +# 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. +# + +org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ +org.apache.shenyu.register.client.beat.HeartbeatListenerConfiguration diff --git a/shenyu-spring-boot-starter/shenyu-spring-boot-starter-client/shenyu-spring-boot-starter-client-beat/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports b/shenyu-spring-boot-starter/shenyu-spring-boot-starter-client/shenyu-spring-boot-starter-client-beat/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports new file mode 100644 index 000000000000..f79b9bac1e8d --- /dev/null +++ b/shenyu-spring-boot-starter/shenyu-spring-boot-starter-client/shenyu-spring-boot-starter-client-beat/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports @@ -0,0 +1,18 @@ +# +# 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. +# + +org.apache.shenyu.register.client.beat.HeartbeatListenerConfiguration