-
-
Notifications
You must be signed in to change notification settings - Fork 10.2k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: support use database as a registry (#4595)
* feat: support use database as a registry less dependency, config, startup fail, more reliability, * fix: test fail when missing bean RegistryService * Delete RegistryController.java * feat: add spring.cloud.discovery.enabled=false * refactor: healthCheckInterval -> healthCheckIntervalInSecond * fix: 'com.ctrip.framework.apollo.biz.repository.RegistryRepository' that could not be found * refactor: Registry -> ServiceRegistry. change all config prefix to apollo.service * feat: add service registry config heartbeatIntervalInSecond * feat: clear service instances 1 days ago * fix: move '@transactional' from repository up to service * refactor: Change all to use jvm `LocalDateTime.now()` * fix test fail * Update apolloconfigdb.sql * refactor: split heartbeat and deregister * fix: catch Throwable instead of catch Exception * refactor: use stream and lambda instead of for each loop filter * delete USING BTREE * fix: label -> cluster * Update scripts/sql/apolloconfigdb.sql Co-authored-by: Jason Song <[email protected]> * Update scripts/sql/delta/v210-v220/apolloconfigdb-v210-v220.sql Co-authored-by: Jason Song <[email protected]> * test: add bean exist check * feat: add Metadata column * refactor: only use cluster in DatabaseDiscoveryClientImpl * feat: add metadata config in code -Dapollo.service.registry.metadata.a=1 -Dapollo.service.registry.metadata.isAutoRegister=true generate {"a":"1","isAutoRegister":"true"} in database * test: add tests of registry * docs: Enable database-discovery to replace built-in eureka * Update CHANGES.md * room -> cluster Co-authored-by: Jason Song <[email protected]> * room -> cluster * feat: when database fail, database-discovery still be useful. by decorator pattern, memory cache * test: fix when DatabaseDiscoveryClient wrap by decorator Co-authored-by: Jason Song <[email protected]>
- Loading branch information
Showing
40 changed files
with
2,685 additions
and
3 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
22 changes: 22 additions & 0 deletions
22
apollo-adminservice/src/main/resources/application-database-discovery.properties
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
# | ||
# Copyright 2022 Apollo Authors | ||
# | ||
# Licensed 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. | ||
# | ||
eureka.client.enabled=false | ||
spring.cloud.discovery.enabled=false | ||
|
||
apollo.service.registry.enabled=true | ||
apollo.service.registry.cluster=default | ||
apollo.service.registry.heartbeatIntervalInSecond=10 | ||
|
47 changes: 47 additions & 0 deletions
47
apollo-biz/src/main/java/com/ctrip/framework/apollo/biz/entity/JpaMapFieldJsonConverter.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,47 @@ | ||
/* | ||
* Copyright 2022 Apollo Authors | ||
* | ||
* Licensed 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 com.ctrip.framework.apollo.biz.entity; | ||
|
||
import com.google.common.reflect.TypeToken; | ||
import com.google.gson.Gson; | ||
import java.lang.reflect.Type; | ||
import java.util.HashMap; | ||
import java.util.Map; | ||
import javax.persistence.AttributeConverter; | ||
import javax.persistence.Converter; | ||
|
||
@Converter(autoApply = true) | ||
class JpaMapFieldJsonConverter implements AttributeConverter<Map<String, String>, String> { | ||
|
||
private static final Gson GSON = new Gson(); | ||
|
||
private static final TypeToken<HashMap<String, String>> TYPE_TOKEN = new TypeToken<HashMap<String, String>>() { | ||
}; | ||
|
||
@SuppressWarnings("unchecked") | ||
private static final Type TYPE = TYPE_TOKEN.getType(); | ||
|
||
@Override | ||
public String convertToDatabaseColumn(Map<String, String> attribute) { | ||
return GSON.toJson(attribute); | ||
} | ||
|
||
@Override | ||
public Map<String, String> convertToEntityAttribute(String dbData) { | ||
return GSON.fromJson(dbData, TYPE); | ||
} | ||
} |
152 changes: 152 additions & 0 deletions
152
apollo-biz/src/main/java/com/ctrip/framework/apollo/biz/entity/ServiceRegistry.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,152 @@ | ||
/* | ||
* Copyright 2022 Apollo Authors | ||
* | ||
* Licensed 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 com.ctrip.framework.apollo.biz.entity; | ||
|
||
import com.ctrip.framework.apollo.biz.registry.ServiceInstance; | ||
import java.time.LocalDateTime; | ||
import java.util.HashMap; | ||
import java.util.Map; | ||
import javax.persistence.Column; | ||
import javax.persistence.Convert; | ||
import javax.persistence.Entity; | ||
import javax.persistence.GeneratedValue; | ||
import javax.persistence.GenerationType; | ||
import javax.persistence.Id; | ||
import javax.persistence.PrePersist; | ||
import javax.persistence.Table; | ||
|
||
/** | ||
* use database as a registry instead of eureka, zookeeper, consul etc. | ||
* <p> | ||
* persist {@link ServiceInstance} | ||
*/ | ||
@Entity | ||
@Table(name = "ServiceRegistry") | ||
public class ServiceRegistry { | ||
|
||
@Id | ||
@GeneratedValue(strategy = GenerationType.IDENTITY) | ||
@Column(name = "Id") | ||
private long id; | ||
|
||
@Column(name = "ServiceName", nullable = false) | ||
private String serviceName; | ||
|
||
/** | ||
* @see ServiceInstance#getUri() | ||
*/ | ||
@Column(name = "Uri", nullable = false) | ||
private String uri; | ||
|
||
/** | ||
* @see ServiceInstance#getCluster() | ||
*/ | ||
@Column(name = "Cluster", nullable = false) | ||
private String cluster; | ||
|
||
@Column(name = "Metadata", nullable = false) | ||
@Convert(converter = JpaMapFieldJsonConverter.class) | ||
private Map<String, String> metadata; | ||
|
||
@Column(name = "DataChange_CreatedTime", nullable = false) | ||
private LocalDateTime dataChangeCreatedTime; | ||
|
||
/** | ||
* modify by heartbeat | ||
*/ | ||
@Column(name = "DataChange_LastTime", nullable = false) | ||
private LocalDateTime dataChangeLastModifiedTime; | ||
|
||
@PrePersist | ||
protected void prePersist() { | ||
if (this.dataChangeCreatedTime == null) { | ||
dataChangeCreatedTime = LocalDateTime.now(); | ||
} | ||
if (this.dataChangeLastModifiedTime == null) { | ||
dataChangeLastModifiedTime = dataChangeCreatedTime; | ||
} | ||
} | ||
|
||
@Override | ||
public String toString() { | ||
return "Registry{" + | ||
"id=" + id + | ||
", serviceName='" + serviceName + '\'' + | ||
", uri='" + uri + '\'' + | ||
", cluster='" + cluster + '\'' + | ||
", metadata='" + metadata + '\'' + | ||
", dataChangeCreatedTime=" + dataChangeCreatedTime + | ||
", dataChangeLastModifiedTime=" + dataChangeLastModifiedTime + | ||
'}'; | ||
} | ||
|
||
public long getId() { | ||
return id; | ||
} | ||
|
||
public void setId(long id) { | ||
this.id = id; | ||
} | ||
|
||
public String getServiceName() { | ||
return serviceName; | ||
} | ||
|
||
public void setServiceName(String serviceName) { | ||
this.serviceName = serviceName; | ||
} | ||
|
||
public String getUri() { | ||
return uri; | ||
} | ||
|
||
public void setUri(String uri) { | ||
this.uri = uri; | ||
} | ||
|
||
public String getCluster() { | ||
return cluster; | ||
} | ||
|
||
public void setCluster(String cluster) { | ||
this.cluster = cluster; | ||
} | ||
|
||
public Map<String, String> getMetadata() { | ||
return metadata; | ||
} | ||
|
||
public void setMetadata(Map<String, String> metadata) { | ||
this.metadata = metadata; | ||
} | ||
|
||
public LocalDateTime getDataChangeCreatedTime() { | ||
return dataChangeCreatedTime; | ||
} | ||
|
||
public void setDataChangeCreatedTime(LocalDateTime dataChangeCreatedTime) { | ||
this.dataChangeCreatedTime = dataChangeCreatedTime; | ||
} | ||
|
||
public LocalDateTime getDataChangeLastModifiedTime() { | ||
return dataChangeLastModifiedTime; | ||
} | ||
|
||
public void setDataChangeLastModifiedTime(LocalDateTime dataChangeLastModifiedTime) { | ||
this.dataChangeLastModifiedTime = dataChangeLastModifiedTime; | ||
} | ||
} |
34 changes: 34 additions & 0 deletions
34
...lo-biz/src/main/java/com/ctrip/framework/apollo/biz/registry/DatabaseDiscoveryClient.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,34 @@ | ||
/* | ||
* Copyright 2022 Apollo Authors | ||
* | ||
* Licensed 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 com.ctrip.framework.apollo.biz.registry; | ||
|
||
import com.ctrip.framework.apollo.biz.registry.configuration.support.ApolloServiceRegistryProperties; | ||
import java.util.List; | ||
|
||
/** | ||
* @see org.springframework.cloud.client.discovery.DiscoveryClient | ||
*/ | ||
public interface DatabaseDiscoveryClient { | ||
|
||
/** | ||
* find by {@link ApolloServiceRegistryProperties#getServiceName()}, | ||
* then filter by {@link ApolloServiceRegistryProperties#getCluster()} | ||
* | ||
* @return empty list if there is no instance | ||
*/ | ||
List<ServiceInstance> getInstances(String serviceName); | ||
} |
84 changes: 84 additions & 0 deletions
84
...mework/apollo/biz/registry/DatabaseDiscoveryClientAlwaysAddSelfInstanceDecoratorImpl.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,84 @@ | ||
/* | ||
* Copyright 2022 Apollo Authors | ||
* | ||
* Licensed 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 com.ctrip.framework.apollo.biz.registry; | ||
|
||
import java.net.URI; | ||
import java.util.ArrayList; | ||
import java.util.List; | ||
import java.util.Objects; | ||
|
||
/** | ||
* decorator pattern | ||
* <p> | ||
* when database crash, even cannot register self instance to database, | ||
* <p> | ||
* this decorator will ensure return's result contains self instance. | ||
*/ | ||
public class DatabaseDiscoveryClientAlwaysAddSelfInstanceDecoratorImpl | ||
implements DatabaseDiscoveryClient { | ||
|
||
private final DatabaseDiscoveryClient delegate; | ||
|
||
private final ServiceInstance selfInstance; | ||
|
||
public DatabaseDiscoveryClientAlwaysAddSelfInstanceDecoratorImpl( | ||
DatabaseDiscoveryClient delegate, | ||
ServiceInstance selfInstance | ||
) { | ||
this.delegate = delegate; | ||
this.selfInstance = selfInstance; | ||
} | ||
|
||
static boolean containSelf(List<ServiceInstance> serviceInstances, ServiceInstance selfInstance) { | ||
final String selfServiceName = selfInstance.getServiceName(); | ||
final URI selfUri = selfInstance.getUri(); | ||
final String cluster = selfInstance.getCluster(); | ||
for (ServiceInstance serviceInstance : serviceInstances) { | ||
if (Objects.equals(selfServiceName, serviceInstance.getServiceName())) { | ||
if (Objects.equals(selfUri, serviceInstance.getUri())) { | ||
if (Objects.equals(cluster, serviceInstance.getCluster())) { | ||
return true; | ||
} | ||
} | ||
} | ||
} | ||
return false; | ||
} | ||
|
||
/** | ||
* if the serviceName is same with self, always return self's instance | ||
* @return never be empty list when serviceName is same with self | ||
*/ | ||
@Override | ||
public List<ServiceInstance> getInstances(String serviceName) { | ||
if (Objects.equals(serviceName, this.selfInstance.getServiceName())) { | ||
List<ServiceInstance> serviceInstances = this.delegate.getInstances(serviceName); | ||
if (containSelf(serviceInstances, this.selfInstance)) { | ||
// contains self instance already | ||
return serviceInstances; | ||
} | ||
|
||
// add self instance to result | ||
List<ServiceInstance> result = new ArrayList<>(serviceInstances.size() + 1); | ||
result.add(this.selfInstance); | ||
result.addAll(serviceInstances); | ||
return result; | ||
} else { | ||
return this.delegate.getInstances(serviceName); | ||
} | ||
} | ||
} |
Oops, something went wrong.