feat: support aliyun instance and more.

This commit is contained in:
2024-05-21 01:53:45 +08:00
parent 478bebe66b
commit 059377b5d7
11 changed files with 173 additions and 49 deletions

View File

@@ -48,9 +48,11 @@ public abstract class AbstractInstanceContext implements InstanceContext, Multip
.forEach(ListableInstanceFactory::refresh);
// 加载、过滤所有实例
Set<Instance> instances = new HashSet<>();
listableInstanceFactories.forEach(f -> instances.addAll(f.getInstances()));
// TODO 加载实例, 按照实例工厂的优先级从低到高优先级排, 高优先级的实例会覆盖低优先级实例信息(如果存在重复的实例信息)
// 高优先级的实例工厂会覆盖低优先级实例工厂所加载的实例
listableInstanceFactories.stream()
.sorted()
.forEach(f -> instances.addAll(f.getInstances()));
// 初次载入
cacheInstanceMap = new HashMap<>(instances.stream().collect(Collectors.toMap(Instance::getName, i -> i)));
@@ -119,9 +121,12 @@ public abstract class AbstractInstanceContext implements InstanceContext, Multip
* 缓存清理
*/
protected void clearCache(){
if(cacheInstanceMap != null
&& !cacheInstanceMap.isEmpty()){
int size = cacheInstanceMap.size();
cacheInstanceMap.clear();
log.debug("缓存信息清理 => {} 条", size);
}
// 清理实例工厂的缓存信息
listableInstanceFactories.forEach(Refreshable::afterRefresh);
}

View File

@@ -77,10 +77,13 @@ public abstract class AbstractInstanceFactory implements InstanceFactory, Listab
@Override
public void afterRefresh() {
if(instanceMap != null
&& !instanceMap.isEmpty()){
int size = instanceMap.size();
instanceMap.clear();
log.debug("缓存信息清理 => {} 条", size);
}
}
/**
* 交由子类去加载实例信息

View File

@@ -5,10 +5,14 @@ import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlRootElement;
import com.serliunx.ddns.constant.InstanceSource;
import com.serliunx.ddns.constant.InstanceType;
import com.serliunx.ddns.support.NetworkContextHolder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import static com.serliunx.ddns.constant.SystemConstants.XML_ROOT_INSTANCE_NAME;
/**
* 实例抽象实现
* @author SerLiunx
* @since 1.0
*/
@@ -17,6 +21,7 @@ import static com.serliunx.ddns.constant.SystemConstants.XML_ROOT_INSTANCE_NAME;
@JacksonXmlRootElement(localName = XML_ROOT_INSTANCE_NAME)
public abstract class AbstractInstance implements Instance {
private static final Logger log = LoggerFactory.getLogger(AbstractInstance.class);
/**
* 实例名称
* <li> 全局唯一
@@ -56,8 +61,30 @@ public abstract class AbstractInstance implements Instance {
@Override
public void run() {
if(query())
value = query();
final String ipAddress = NetworkContextHolder.getIpAddress();
try {
if (value != null && !value.isEmpty()
&& ipAddress != null && !ipAddress.isEmpty()) {
if (value.equals(ipAddress))
return;
}
value = ipAddress;
run0();
}catch (Exception e){
log.error(e.getMessage());
}finally {
this.value = null;
}
}
@Override
public boolean validate() {
// 校验通用参数, 具体子类的参数交由子类校验
if(name == null || name.isEmpty() || interval <= 0 || type == null){
return false;
}
return validate0();
}
@Override
@@ -105,15 +132,6 @@ public abstract class AbstractInstance implements Instance {
return source;
}
@Override
public boolean validate() {
// 校验通用参数, 具体子类的参数交由子类校验
if(name == null || name.isEmpty() || interval <= 0 || type == null){
return false;
}
return validate0();
}
/**
* 具体的初始化逻辑
*/
@@ -125,10 +143,14 @@ public abstract class AbstractInstance implements Instance {
protected abstract boolean validate0();
/**
* 更新前检查是否需要更新
* @return 无需更新返回假, 否则返回真
* 获取解析当前的ip地址
* <li> 由子类完成具体逻辑
* @return 返回当前解析记录的ip地址, 由子类决定.
* @see AliyunInstance
* @see TencentInstance
* @see HuaweiInstance
*/
protected abstract boolean query();
protected abstract String query();
/**
* 具体执行逻辑

View File

@@ -3,13 +3,11 @@ package com.serliunx.ddns.core.instance;
import com.aliyun.auth.credentials.Credential;
import com.aliyun.auth.credentials.provider.StaticCredentialProvider;
import com.aliyun.sdk.service.alidns20150109.AsyncClient;
import com.aliyun.sdk.service.alidns20150109.models.DescribeDomainRecordInfoRequest;
import com.aliyun.sdk.service.alidns20150109.models.DescribeDomainRecordInfoResponse;
import com.aliyun.sdk.service.alidns20150109.models.DescribeDomainRecordInfoResponseBody;
import com.aliyun.sdk.service.alidns20150109.models.*;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.json.JsonMapper;
import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlRootElement;
import com.serliunx.ddns.support.NetworkContextHolder;
import darabonba.core.client.ClientOverrideConfiguration;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -107,12 +105,34 @@ public class AliyunInstance extends AbstractInstance {
@Override
protected void run0() {
log("test");
UpdateDomainRecordRequest request = UpdateDomainRecordRequest.builder()
.recordId(recordId)
.rr(rr)
.type(recordType)
.value(value)
.build();
debug("正在更新解析记录.");
CompletableFuture<UpdateDomainRecordResponse> requestResponse = client.updateDomainRecord(request);
try {
requestResponse.whenComplete((v, t) -> {
if(t != null){ //出现异常
handleThrowable(t);
}else{
String result = null;
try {
result = jsonMapper.writeValueAsString(v.getBody());
} catch (JsonProcessingException ignored) {} finally {
debug("操作结束, 结果: {}", result == null ? v : result);
}
}
});
} catch (Exception e) {
throw new RuntimeException(e);
}
}
@Override
protected boolean query() {
debug("正在校验是否需要更新记录.");
protected String query() {
DescribeDomainRecordInfoRequest describeDomainRecordInfoRequest = DescribeDomainRecordInfoRequest.builder()
.recordId(recordId)
.build();
@@ -122,24 +142,15 @@ public class AliyunInstance extends AbstractInstance {
DescribeDomainRecordInfoResponse response = responseCompletableFuture.get(5, TimeUnit.SECONDS);
DescribeDomainRecordInfoResponseBody body = response.getBody();
if(body != null){
String recordValue = body.getValue();
String ipAddress = NetworkContextHolder.getIpAddress();
debug("当前记录值 => {}", recordValue);
boolean result = !(recordValue != null && !recordValue.isEmpty()
&& recordValue.equals(ipAddress));
if(result)
debug("需要更新IP地址: {} => {}", recordValue, ipAddress);
else
debug("无需更新.");
return result;
return body.getValue();
}
return false;
return null;
} catch (InterruptedException | ExecutionException e) {
error("出现了不应该出现的异常 => {}", e);
return false;
return null;
} catch (TimeoutException e) {
error("记录查询超时! 将跳过查询直接执行更新操作.");
return true;
error("记录查询超时!");
return null;
}
}
@@ -206,6 +217,25 @@ public class AliyunInstance extends AbstractInstance {
this.jsonMapper = jsonMapper;
}
@Override
public String toString() {
return "AliyunInstance{" +
"accessKeyId='" + accessKeyId + '\'' +
", accessKeySecret='" + accessKeySecret + '\'' +
", recordId='" + recordId + '\'' +
", rr='" + rr + '\'' +
", recordType='" + recordType + '\'' +
", client=" + client +
", jsonMapper=" + jsonMapper +
", name='" + name + '\'' +
", fatherName='" + fatherName + '\'' +
", interval=" + interval +
", type=" + type +
", source=" + source +
", value='" + value + '\'' +
'}';
}
private void handleThrowable(Throwable t){
error("出现异常 {}:", t.getCause(), t.getMessage());
}

View File

@@ -0,0 +1,29 @@
package com.serliunx.ddns.core.instance;
/**
* 华为云实例定义
* @author SerLiunx
* @since 1.0
*/
public class HuaweiInstance extends AbstractInstance {
@Override
protected void init() {
}
@Override
protected boolean validate0() {
return false;
}
@Override
protected String query() {
return null;
}
@Override
protected void run0() {
}
}

View File

@@ -1,6 +1,7 @@
package com.serliunx.ddns.core.instance;
/**
* 腾讯云实例定义
* @author SerLiunx
* @since 1.0
*/
@@ -17,8 +18,8 @@ public class TencentInstance extends AbstractInstance {
}
@Override
protected boolean query() {
return false;
protected String query() {
return null;
}
@Override

View File

@@ -38,7 +38,6 @@ public final class NetworkContextHolder {
}
public static String getIpAddress(){
log.debug("正在尝试获取最新的IP地址.");
if(IP_ADDRESS != null)
return IP_ADDRESS;
try {
@@ -46,7 +45,6 @@ public final class NetworkContextHolder {
log.error("IP地址获取超时.");
return null;
}
log.debug("最新的IP地址获取成功.");
return IP_ADDRESS;
} catch (InterruptedException e) {
log.error("IP地址获取出现异常 => {}", e.getMessage());

View File

@@ -16,7 +16,5 @@ public interface Refreshable {
/**
* 刷新后逻辑定义, 一般用于资源清理
*/
default void afterRefresh(){
}
default void afterRefresh(){}
}

View File

@@ -17,6 +17,7 @@ import java.io.OutputStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
@@ -38,6 +39,7 @@ public final class SystemInitializer implements Refreshable{
private ScheduledThreadPoolExecutor scheduledThreadPoolExecutor;
private Set<Instance> instances;
private Map<String, Instance> runningInstances;
SystemInitializer(Configuration configuration, MultipleSourceInstanceContext instanceContext) {
this.configuration = configuration;

View File

@@ -0,0 +1,19 @@
package com.serliunx.ddns.test;
import com.serliunx.ddns.constant.SystemConstants;
import com.serliunx.ddns.core.factory.YamlFileInstanceFactory;
import org.junit.Test;
/**
* @author SerLiunx
* @since 1.0
*/
public class FactoryTest {
@Test
public void testYamlFileFactory(){
YamlFileInstanceFactory factory = new YamlFileInstanceFactory(SystemConstants.USER_INSTANCE_DIR);
factory.refresh();
factory.getInstances().forEach(System.out::println);
}
}

View File

@@ -0,0 +1,17 @@
package com.serliunx.ddns.test.support;
import com.serliunx.ddns.support.feign.client.IPAddressClient;
import org.junit.Test;
/**
* @author SerLiunx
* @since 1.0
*/
public class ClientTest {
@Test
public void test(){
IPAddressClient client = IPAddressClient.instance;
System.out.println(client.getIPAddress().getQuery());
}
}