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); .forEach(ListableInstanceFactory::refresh);
// 加载、过滤所有实例 // 加载、过滤所有实例
Set<Instance> instances = new HashSet<>(); 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))); 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(){ protected void clearCache(){
int size = cacheInstanceMap.size(); if(cacheInstanceMap != null
cacheInstanceMap.clear(); && !cacheInstanceMap.isEmpty()){
log.debug("缓存信息清理 => {} 条", size); int size = cacheInstanceMap.size();
cacheInstanceMap.clear();
log.debug("缓存信息清理 => {} 条", size);
}
// 清理实例工厂的缓存信息 // 清理实例工厂的缓存信息
listableInstanceFactories.forEach(Refreshable::afterRefresh); listableInstanceFactories.forEach(Refreshable::afterRefresh);
} }

View File

@@ -77,9 +77,12 @@ public abstract class AbstractInstanceFactory implements InstanceFactory, Listab
@Override @Override
public void afterRefresh() { public void afterRefresh() {
int size = instanceMap.size(); if(instanceMap != null
instanceMap.clear(); && !instanceMap.isEmpty()){
log.debug("缓存信息清理 => {} 条", size); 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.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlRootElement;
import com.serliunx.ddns.constant.InstanceSource; import com.serliunx.ddns.constant.InstanceSource;
import com.serliunx.ddns.constant.InstanceType; 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; import static com.serliunx.ddns.constant.SystemConstants.XML_ROOT_INSTANCE_NAME;
/** /**
* 实例抽象实现
* @author SerLiunx * @author SerLiunx
* @since 1.0 * @since 1.0
*/ */
@@ -17,6 +21,7 @@ import static com.serliunx.ddns.constant.SystemConstants.XML_ROOT_INSTANCE_NAME;
@JacksonXmlRootElement(localName = XML_ROOT_INSTANCE_NAME) @JacksonXmlRootElement(localName = XML_ROOT_INSTANCE_NAME)
public abstract class AbstractInstance implements Instance { public abstract class AbstractInstance implements Instance {
private static final Logger log = LoggerFactory.getLogger(AbstractInstance.class);
/** /**
* 实例名称 * 实例名称
* <li> 全局唯一 * <li> 全局唯一
@@ -56,8 +61,30 @@ public abstract class AbstractInstance implements Instance {
@Override @Override
public void run() { 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(); 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 @Override
@@ -105,15 +132,6 @@ public abstract class AbstractInstance implements Instance {
return source; 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(); protected abstract boolean validate0();
/** /**
* 更新前检查是否需要更新 * 获取解析当前的ip地址
* @return 无需更新返回假, 否则返回真 * <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.Credential;
import com.aliyun.auth.credentials.provider.StaticCredentialProvider; import com.aliyun.auth.credentials.provider.StaticCredentialProvider;
import com.aliyun.sdk.service.alidns20150109.AsyncClient; import com.aliyun.sdk.service.alidns20150109.AsyncClient;
import com.aliyun.sdk.service.alidns20150109.models.DescribeDomainRecordInfoRequest; import com.aliyun.sdk.service.alidns20150109.models.*;
import com.aliyun.sdk.service.alidns20150109.models.DescribeDomainRecordInfoResponse;
import com.aliyun.sdk.service.alidns20150109.models.DescribeDomainRecordInfoResponseBody;
import com.fasterxml.jackson.annotation.JsonIgnore; import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.json.JsonMapper; import com.fasterxml.jackson.databind.json.JsonMapper;
import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlRootElement; import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlRootElement;
import com.serliunx.ddns.support.NetworkContextHolder;
import darabonba.core.client.ClientOverrideConfiguration; import darabonba.core.client.ClientOverrideConfiguration;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
@@ -107,12 +105,34 @@ public class AliyunInstance extends AbstractInstance {
@Override @Override
protected void run0() { 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 @Override
protected boolean query() { protected String query() {
debug("正在校验是否需要更新记录.");
DescribeDomainRecordInfoRequest describeDomainRecordInfoRequest = DescribeDomainRecordInfoRequest.builder() DescribeDomainRecordInfoRequest describeDomainRecordInfoRequest = DescribeDomainRecordInfoRequest.builder()
.recordId(recordId) .recordId(recordId)
.build(); .build();
@@ -122,24 +142,15 @@ public class AliyunInstance extends AbstractInstance {
DescribeDomainRecordInfoResponse response = responseCompletableFuture.get(5, TimeUnit.SECONDS); DescribeDomainRecordInfoResponse response = responseCompletableFuture.get(5, TimeUnit.SECONDS);
DescribeDomainRecordInfoResponseBody body = response.getBody(); DescribeDomainRecordInfoResponseBody body = response.getBody();
if(body != null){ if(body != null){
String recordValue = body.getValue(); return 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 false; return null;
} catch (InterruptedException | ExecutionException e) { } catch (InterruptedException | ExecutionException e) {
error("出现了不应该出现的异常 => {}", e); error("出现了不应该出现的异常 => {}", e);
return false; return null;
} catch (TimeoutException e) { } catch (TimeoutException e) {
error("记录查询超时! 将跳过查询直接执行更新操作."); error("记录查询超时!");
return true; return null;
} }
} }
@@ -206,6 +217,25 @@ public class AliyunInstance extends AbstractInstance {
this.jsonMapper = jsonMapper; 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){ private void handleThrowable(Throwable t){
error("出现异常 {}:", t.getCause(), t.getMessage()); 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; package com.serliunx.ddns.core.instance;
/** /**
* 腾讯云实例定义
* @author SerLiunx * @author SerLiunx
* @since 1.0 * @since 1.0
*/ */
@@ -17,8 +18,8 @@ public class TencentInstance extends AbstractInstance {
} }
@Override @Override
protected boolean query() { protected String query() {
return false; return null;
} }
@Override @Override

View File

@@ -38,7 +38,6 @@ public final class NetworkContextHolder {
} }
public static String getIpAddress(){ public static String getIpAddress(){
log.debug("正在尝试获取最新的IP地址.");
if(IP_ADDRESS != null) if(IP_ADDRESS != null)
return IP_ADDRESS; return IP_ADDRESS;
try { try {
@@ -46,7 +45,6 @@ public final class NetworkContextHolder {
log.error("IP地址获取超时."); log.error("IP地址获取超时.");
return null; return null;
} }
log.debug("最新的IP地址获取成功.");
return IP_ADDRESS; return IP_ADDRESS;
} catch (InterruptedException e) { } catch (InterruptedException e) {
log.error("IP地址获取出现异常 => {}", e.getMessage()); 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.Files;
import java.nio.file.Path; import java.nio.file.Path;
import java.nio.file.Paths; import java.nio.file.Paths;
import java.util.Map;
import java.util.Set; import java.util.Set;
import java.util.concurrent.ScheduledThreadPoolExecutor; import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
@@ -38,6 +39,7 @@ public final class SystemInitializer implements Refreshable{
private ScheduledThreadPoolExecutor scheduledThreadPoolExecutor; private ScheduledThreadPoolExecutor scheduledThreadPoolExecutor;
private Set<Instance> instances; private Set<Instance> instances;
private Map<String, Instance> runningInstances;
SystemInitializer(Configuration configuration, MultipleSourceInstanceContext instanceContext) { SystemInitializer(Configuration configuration, MultipleSourceInstanceContext instanceContext) {
this.configuration = configuration; 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());
}
}