简易redis注册中心

This commit is contained in:
hccake 2021-01-29 18:20:15 +08:00
parent 842496a534
commit 530efb65d7
15 changed files with 891 additions and 0 deletions

View File

@ -222,6 +222,13 @@
<version>${easyexcel.verion}</version>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-commons</artifactId>
<version>3.0.0</version>
</dependency>
</dependencies>

View File

@ -0,0 +1,20 @@
package cn.iocoder.dashboard.framework.register;
import cn.iocoder.dashboard.framework.register.registry.RedisRegistration;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* @author Hccake 2021/1/29
* @version 1.0
*/
@Configuration(proxyBeanMethods = false)
public class HeartBeatAuoConfiguration {
@Bean
public RedisHeartbeatDetection redisHeartbeatDetection(RedisRegisterManagement redisRegisterManagement, RedisRegistration redisRegistration){
return new RedisHeartbeatDetection(redisRegisterManagement, redisRegistration);
}
}

View File

@ -0,0 +1,51 @@
package cn.iocoder.dashboard.framework.register;
import cn.iocoder.dashboard.framework.register.registry.RedisRegistration;
import cn.iocoder.dashboard.framework.register.util.NamedThreadFactory;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
/**
* 心跳检测线程
* TODO 目前是在每一个客户端都维护一个线程可剥离出来放在一个中心去处理
* @author Hccake 2021/1/29
* @version 1.0
*/
@Slf4j
@RequiredArgsConstructor
public class RedisHeartbeatDetection {
private final RedisRegisterManagement redisRegisterManagement;
private final RedisRegistration redisRegistration;
private final ScheduledExecutorService detectionExecutor = Executors.newScheduledThreadPool(1, new NamedThreadFactory("RedisRegistryExpireTimer", true));
private ScheduledFuture<?> detectionFuture;
@PostConstruct
public void init(){
// 心跳检测线程
int detectionInterval = 60 * 1000;
this.detectionFuture = detectionExecutor.scheduleWithFixedDelay(() -> {
try {
redisRegisterManagement.removeExpiredInstances(redisRegistration.getServiceId()); // Extend the expiration time
} catch (Throwable t) { // Defensive fault tolerance
log.error("Unexpected exception occur at defer expire time, cause: " + t.getMessage(), t);
}
}, detectionInterval, detectionInterval, TimeUnit.MILLISECONDS);
}
@PreDestroy
public void destroy(){
detectionFuture.cancel(true);
detectionExecutor.shutdown();
}
}

View File

@ -0,0 +1,23 @@
package cn.iocoder.dashboard.framework.register;
import cn.iocoder.dashboard.framework.register.registry.RedisDiscoveryProperties;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.RequiredArgsConstructor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.core.StringRedisTemplate;
/**
* @author Hccake 2021/1/27
* @version 1.0
*/
@RequiredArgsConstructor
@Configuration(proxyBeanMethods = false)
public class RedisRegisterAutoConfiguration {
private final RedisDiscoveryProperties redisDiscoveryProperties;
@Bean
public RedisRegisterManagement redisRegisterManagement(ObjectMapper objectMapper, StringRedisTemplate stringRedisTemplate){
return new RedisRegisterManagement(objectMapper, stringRedisTemplate, redisDiscoveryProperties);
}
}

View File

@ -0,0 +1,160 @@
package cn.iocoder.dashboard.framework.register;
import cn.iocoder.dashboard.framework.register.registry.RedisDiscoveryProperties;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.RequiredArgsConstructor;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.serviceregistry.Registration;
import org.springframework.data.redis.core.HashOperations;
import org.springframework.data.redis.core.SetOperations;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.ZSetOperations;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import java.util.*;
/**
* redis注册中心的数据操作管理
* TODO 原子操作移除过期实例同时移除无实例的服务
* @author Hccake 2021/1/26
* @version 1.0
*/
@RequiredArgsConstructor
public class RedisRegisterManagement {
private final ObjectMapper objectMapper;
private final StringRedisTemplate stringRedisTemplate;
private final RedisDiscoveryProperties redisDiscoveryProperties;
/**
* 服务列表SET 类型member serviceId
*/
private final static String SERVICES_KEY = "redis-register-services";
/**
* 服务实例列表HASH类型field instanceId, value 是实例信息
*/
private final static String SERVICES_INSTANCE_KEY_PREFIX = "redis-register-service-instance:";
private static String getInstancesKey(String serviceId) {
return SERVICES_INSTANCE_KEY_PREFIX + serviceId;
}
/**
* 服务实例心跳维护ZSET 类型member instanceId, value 是过期时间
*/
private final static String SERVICES_INSTANCE_EXPIRES_KEY_PREFIX = "redis-register-service-instance-expires:";
private static String getInstanceExpiresKey(String serviceId) {
return SERVICES_INSTANCE_EXPIRES_KEY_PREFIX + serviceId;
}
/**
* 添加一个服务实例
*
* @param redisServiceInstance redis服务实例对象
* @throws JsonProcessingException json 序列化异常
*/
public void addServiceInstance(RedisServiceInstance redisServiceInstance) throws JsonProcessingException {
String serviceId = redisServiceInstance.getServiceId();
String instanceId = redisServiceInstance.getInstanceId();
// 服务列表
SetOperations<String, String> opsForSet = stringRedisTemplate.opsForSet();
opsForSet.add(SERVICES_KEY, serviceId);
// 服务实例
HashOperations<String, String, String> opsForHash = stringRedisTemplate.opsForHash();
String value = new ObjectMapper().writeValueAsString(redisServiceInstance);
opsForHash.put(getInstancesKey(serviceId), instanceId, value);
// 服务实例的过期时间
ZSetOperations<String, String> opsForZSet = stringRedisTemplate.opsForZSet();
opsForZSet.add(getInstanceExpiresKey(serviceId), instanceId, System.currentTimeMillis() + redisDiscoveryProperties.getHeartbeatInterval());
}
/**
* 移除一个服务实例
*
* @param registration 注册数据
*/
public void removeServiceInstance(Registration registration) {
String serviceId = registration.getServiceId();
String instanceId = registration.getInstanceId();
// 服务实例
HashOperations<String, String, String> opsForHash = stringRedisTemplate.opsForHash();
opsForHash.delete(getInstancesKey(serviceId), instanceId);
// 服务实例的过期时间
ZSetOperations<String, String> opsForZSet = stringRedisTemplate.opsForZSet();
opsForZSet.remove(getInstanceExpiresKey(serviceId), instanceId);
}
/**
* 根据 服务id 获取其实例列表
*
* @param serviceId 服务id
* @return List<ServiceInstance> 服务实例列表
* @throws JsonProcessingException json 序列化异常
*/
public List<ServiceInstance> getInstances(String serviceId) throws JsonProcessingException {
List<ServiceInstance> serviceInstances = new ArrayList<>();
HashOperations<String, String, String> opsForHash = stringRedisTemplate.opsForHash();
Map<String, String> entries = opsForHash.entries(getInstancesKey(serviceId));
for (Map.Entry<String, String> entry : entries.entrySet()) {
RedisServiceInstance redisServiceInstance = objectMapper.readValue(entry.getValue(), RedisServiceInstance.class);
serviceInstances.add(redisServiceInstance);
}
return serviceInstances;
}
/**
* 获取服务列表
*
* @return List<String> 服务列表
*/
public List<String> getServices() {
// 服务列表
SetOperations<String, String> opsForSet = stringRedisTemplate.opsForSet();
Set<String> members = opsForSet.members(SERVICES_KEY);
return members == null ? new ArrayList<>() : new ArrayList<>(members);
}
/**
* 延迟实例过期时间
* @param serviceId 服务ID
* @param instanceId 实例ID
*/
public void deferInstanceExpired(String serviceId, String instanceId) {
// 服务列表更新因为可能被别的线程移除了
SetOperations<String, String> opsForSet = stringRedisTemplate.opsForSet();
opsForSet.add(SERVICES_KEY, serviceId);
// 服务实例的过期时间
ZSetOperations<String, String> opsForZSet = stringRedisTemplate.opsForZSet();
opsForZSet.add(getInstanceExpiresKey(serviceId), instanceId, System.currentTimeMillis() + redisDiscoveryProperties.getHeartbeatInterval());
}
private final static String REMOVE_EXPIRED_INSTANCES_SCRIPT = "local instanceIds = redis.call('zrangeByScore', KEYS[1], 0, ARGV[1])\n" +
"if next(instanceIds) ~= nil then\n" +
" redis.call('zremrangeByScore', KEYS[1], 0, ARGV[1])\n" +
" redis.call('hdel', KEYS[2], unpack(instanceIds))\n" +
"end";
/**
* 移除过期实例
*/
public void removeExpiredInstances(String serviceId) {
DefaultRedisScript<Void> redisScript = new DefaultRedisScript<>(REMOVE_EXPIRED_INSTANCES_SCRIPT);
stringRedisTemplate.execute(redisScript,
Arrays.asList(getInstanceExpiresKey(serviceId), getInstancesKey(serviceId)),
String.valueOf(System.currentTimeMillis())
);
}
}

View File

@ -0,0 +1,88 @@
package cn.iocoder.dashboard.framework.register;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import lombok.Setter;
import org.springframework.cloud.client.DefaultServiceInstance;
import org.springframework.cloud.client.ServiceInstance;
import java.net.URI;
import java.util.LinkedHashMap;
import java.util.Map;
/**
* @author Hccake 2021/1/26
* @version 1.0
*/
@Setter
@JsonIgnoreProperties({"scheme", "uri"})
public class RedisServiceInstance implements ServiceInstance {
private String serviceId;
private String instanceId;
private String host;
private int port;
private boolean secure;
private Map<String, String> metadata = new LinkedHashMap<>();
public RedisServiceInstance(){
}
@Override
public String getServiceId() {
return serviceId;
}
@Override
public String getInstanceId() {
return this.instanceId;
}
public void setInstanceId(String instanceId) {
this.instanceId = instanceId;
}
@Override
public String getHost() {
return host;
}
@Override
public int getPort() {
return port;
}
@Override
public boolean isSecure() {
return secure;
}
@Override
public URI getUri() {
return DefaultServiceInstance.getUri(this);
}
@Override
public Map<String, String> getMetadata() {
return metadata;
}
@Override
public String getScheme() {
return this.getUri().getScheme();
}
public void setUri(URI uri) {
this.host = uri.getHost();
this.port = uri.getPort();
String scheme = uri.getScheme();
if ("https".equals(scheme)) {
this.secure = true;
}
}
}

View File

@ -0,0 +1,19 @@
package cn.iocoder.dashboard.framework.register.discovery;
import cn.iocoder.dashboard.framework.register.RedisRegisterManagement;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* 服务发现自动配置类
* @author Hccake 2021/1/26
* @version 1.0
*/
@Configuration(proxyBeanMethods = false)
public class RedisDiscoveryAutoConfiguration {
@Bean
public RedisDiscoveryClient redisDiscoveryClient(RedisRegisterManagement redisRegisterManagement){
return new RedisDiscoveryClient(redisRegisterManagement);
}
}

View File

@ -0,0 +1,36 @@
package cn.iocoder.dashboard.framework.register.discovery;
import cn.iocoder.dashboard.framework.register.RedisRegisterManagement;
import lombok.RequiredArgsConstructor;
import lombok.SneakyThrows;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.discovery.DiscoveryClient;
import java.util.List;
/**
* 服务发现客户端
* @author Hccake 2021/1/26
* @version 1.0
*/
@RequiredArgsConstructor
public class RedisDiscoveryClient implements DiscoveryClient {
private final RedisRegisterManagement redisRegisterManagement;
@Override
public String description() {
return "Redis Discovery Client";
}
@SneakyThrows
@Override
public List<ServiceInstance> getInstances(String serviceId) {
return redisRegisterManagement.getInstances(serviceId);
}
@Override
public List<String> getServices() {
return redisRegisterManagement.getServices();
}
}

View File

@ -0,0 +1,46 @@
package cn.iocoder.dashboard.framework.register.registry;
import org.springframework.cloud.client.serviceregistry.AbstractAutoServiceRegistration;
import org.springframework.cloud.client.serviceregistry.AutoServiceRegistrationProperties;
import org.springframework.cloud.client.serviceregistry.Registration;
import org.springframework.cloud.client.serviceregistry.ServiceRegistry;
/**
* @author Hccake 2021/1/26
* @version 1.0
*/
public class RedisAutoServiceRegistration extends AbstractAutoServiceRegistration<Registration> {
private final RedisRegistration redisRegistration;
protected RedisAutoServiceRegistration(ServiceRegistry<Registration> serviceRegistry, AutoServiceRegistrationProperties properties, RedisRegistration registration) {
super(serviceRegistry, properties);
this.redisRegistration = registration;
}
/**
* @return The object used to configure the registration.
*/
@Override
protected Object getConfiguration() {
return null;
}
/**
* @return True, if this is enabled.
*/
@Override
protected boolean isEnabled() {
return true;
}
@Override
protected RedisRegistration getRegistration() {
return redisRegistration;
}
@Override
protected RedisRegistration getManagementRegistration() {
return null;
}
}

View File

@ -0,0 +1,19 @@
package cn.iocoder.dashboard.framework.register.registry;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
/**
* @author Hccake 2021/1/26
* @version 1.0
*/
@Data
@ConfigurationProperties("spring.cloud.redis.discovery")
public class RedisDiscoveryProperties {
/**
* 心跳间隔时间
*/
private int heartbeatInterval = 60 * 1000;
}

View File

@ -0,0 +1,180 @@
package cn.iocoder.dashboard.framework.register.registry;
import cn.iocoder.dashboard.framework.register.RedisRegisterManagement;
import cn.iocoder.dashboard.framework.register.util.InetUtil;
import cn.iocoder.dashboard.framework.register.util.NamedThreadFactory;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.discovery.ManagementServerPortUtils;
import org.springframework.cloud.client.serviceregistry.Registration;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.core.env.Environment;
import org.springframework.util.StringUtils;
import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import java.net.InetAddress;
import java.net.URI;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
/**
* @author Hccake 2021/1/26
* @version 1.0
*/
@Slf4j
@Setter
public class RedisRegistration implements Registration, ServiceInstance, ApplicationContextAware {
private static final String MANAGEMENT_SCHEME = "management.scheme";
private static final String MANAGEMENT_ADDRESS = "management.address";
private static final String MANAGEMENT_PORT = "management.port";
private static final String MANAGEMENT_CONTEXT_PATH = "management.context-path";
private static final String KEY_HEALTH_PATH = "health.path";
private final RedisRegisterManagement redisRegisterManagement;
private final RedisDiscoveryProperties redisDiscoveryProperties;
private ApplicationContext context;
@Value("${spring.application.name}")
private String applicationName;
@Value("${server.port: 8080}")
private Integer port;
private String host;
private Map<String, String> metadata = new HashMap<>();
private final ScheduledExecutorService expireExecutor = Executors.newScheduledThreadPool(1, new NamedThreadFactory("RedisRegistryExpireTimer", true));
private ScheduledFuture<?> expireFuture;
public RedisRegistration(RedisRegisterManagement redisRegisterManagement, RedisDiscoveryProperties redisDiscoveryProperties) {
this.redisRegisterManagement = redisRegisterManagement;
this.redisDiscoveryProperties = redisDiscoveryProperties;
}
@PostConstruct
public void init() {
Environment env = context.getEnvironment();
Integer managementPort = ManagementServerPortUtils.getPort(context);
if (null != managementPort) {
metadata.put(MANAGEMENT_PORT, managementPort.toString());
String contextPath = env
.getProperty("management.server.servlet.context-path");
String address = env.getProperty("management.server.address");
if (!StringUtils.hasText(contextPath)) {
metadata.put(MANAGEMENT_CONTEXT_PATH, contextPath);
}
if (!StringUtils.hasText(address)) {
metadata.put(MANAGEMENT_ADDRESS, address);
}
}
// 过期时间延长线程
int heartbeatInterval = redisDiscoveryProperties.getHeartbeatInterval();
this.expireFuture = expireExecutor.scheduleWithFixedDelay(() -> {
try {
redisRegisterManagement.deferInstanceExpired(this.getServiceId(), this.getInstanceId()); // Extend the expiration time
} catch (Throwable t) { // Defensive fault tolerance
log.error("Unexpected exception occur at defer expire time, cause: " + t.getMessage(), t);
}
}, heartbeatInterval / 2, heartbeatInterval / 2, TimeUnit.MILLISECONDS);
}
@PreDestroy
public void destroy(){
expireFuture.cancel(true);
expireExecutor.shutdown();
}
/**
* @return The service ID as registered.
*/
@Override
public String getServiceId() {
return applicationName;
}
@Override
public String getInstanceId() {
return this.host + '-' + this.applicationName + '-' + this.port;
}
/**
* @return The hostname of the registered service instance.
*/
@Override
public String getHost() {
try {
if (this.host == null) {
InetAddress address = InetUtil.getLocalHostLANAddress();
if (address != null){
this.host = address.getHostAddress();
}
}
return host;
} catch (Exception e) {
e.printStackTrace();
}
return host;
}
/**
* @return The port of the registered service instance.
*/
@Override
public int getPort() {
return port;
}
/**
* @return Whether the port of the registered service instance uses HTTPS.
*/
@Override
public boolean isSecure() {
return false;
}
/**
* @return The service URI address.
*/
@Override
public URI getUri() {
return null;
}
/**
* @return The key / value pair metadata associated with the service instance.
*/
@Override
public Map<String, String> getMetadata() {
return metadata;
}
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.context = applicationContext;
}
}

View File

@ -0,0 +1,81 @@
package cn.iocoder.dashboard.framework.register.registry;
import cn.iocoder.dashboard.framework.register.RedisRegisterManagement;
import cn.iocoder.dashboard.framework.register.RedisServiceInstance;
import lombok.RequiredArgsConstructor;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.springframework.cloud.client.serviceregistry.Registration;
import org.springframework.cloud.client.serviceregistry.ServiceRegistry;
import org.springframework.cloud.client.serviceregistry.endpoint.ServiceRegistryEndpoint;
/**
* @author Hccake 2021/1/26
* @version 1.0
*/
@Slf4j
@RequiredArgsConstructor
public class RedisServiceRegistry implements ServiceRegistry<Registration> {
private final RedisRegisterManagement redisRegisterManagement;
/**
* Registers the registration. A registration typically has information about an
* instance, such as its hostname and port.
*
* @param registration registration meta data
*/
@SneakyThrows
@Override
public void register(Registration registration) {
RedisServiceInstance redisServiceInstance = new RedisServiceInstance();
String serviceId = registration.getServiceId();
redisServiceInstance.setServiceId(serviceId);
redisServiceInstance.setHost(registration.getHost());
redisServiceInstance.setPort(registration.getPort());
redisServiceInstance.setMetadata(registration.getMetadata());
redisServiceInstance.setInstanceId(registration.getInstanceId());
redisRegisterManagement.addServiceInstance(redisServiceInstance);
}
/**
* Deregisters the registration.
*
* @param registration registration meta data
*/
@Override
public void deregister(Registration registration) {
redisRegisterManagement.removeServiceInstance(registration);
}
/**
* Closes the ServiceRegistry. This is a lifecycle method.
*/
@Override
public void close() {
}
/**
* Sets the status of the registration. The status values are determined by the
* individual implementations.
*
* @param registration The registration to update.
* @param status The status to set.
* @see ServiceRegistryEndpoint
*/
@Override
public void setStatus(Registration registration, String status) {
}
/**
* Gets the status of a particular registration.
*
* @param registration The registration to query.
* @return The status of the registration.
* @see ServiceRegistryEndpoint
*/
@Override
public <T> T getStatus(Registration registration) {
return null;
}
}

View File

@ -0,0 +1,47 @@
package cn.iocoder.dashboard.framework.register.registry;
import cn.iocoder.dashboard.framework.register.RedisRegisterManagement;
import lombok.RequiredArgsConstructor;
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.cloud.client.serviceregistry.AutoServiceRegistrationAutoConfiguration;
import org.springframework.cloud.client.serviceregistry.AutoServiceRegistrationConfiguration;
import org.springframework.cloud.client.serviceregistry.AutoServiceRegistrationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* @author Hccake 2021/1/26
* @version 1.0
*/
@EnableConfigurationProperties(RedisDiscoveryProperties.class)
@Configuration(proxyBeanMethods = false)
@AutoConfigureAfter({ AutoServiceRegistrationConfiguration.class,
AutoServiceRegistrationAutoConfiguration.class })
@RequiredArgsConstructor
public class RedisServiceRegistryAutoConfiguration {
private final RedisRegisterManagement redisRegisterManagement;
private final RedisDiscoveryProperties redisDiscoveryProperties;
@Bean
public RedisServiceRegistry redisServiceRegistry() {
return new RedisServiceRegistry(redisRegisterManagement);
}
@Bean
public RedisRegistration redisRegistration() {
return new RedisRegistration(redisRegisterManagement, redisDiscoveryProperties);
}
@Bean
public RedisAutoServiceRegistration registration(
RedisServiceRegistry redisServiceRegistry,
AutoServiceRegistrationProperties autoServiceRegistrationProperties,
RedisRegistration redisRegistration) {
return new RedisAutoServiceRegistration(redisServiceRegistry,
autoServiceRegistrationProperties, redisRegistration);
}
}

View File

@ -0,0 +1,50 @@
package cn.iocoder.dashboard.framework.register.util;
import java.net.InetAddress;
import java.net.NetworkInterface;
import java.util.Enumeration;
/**
* @author Hccake 2021/1/26
* @version 1.0
*/
public class InetUtil {
public static InetAddress getLocalHostLANAddress() throws Exception {
try {
InetAddress candidateAddress = null;
Enumeration<NetworkInterface> networkInterfaces = NetworkInterface.getNetworkInterfaces();
// 遍历所有的网络接口
while (networkInterfaces.hasMoreElements()){
NetworkInterface networkInterface = networkInterfaces.nextElement();
// 在所有的接口下再遍历IP
Enumeration<InetAddress> inetAddresses = networkInterface.getInetAddresses();
while (inetAddresses.hasMoreElements()) {
InetAddress inetAddr = inetAddresses.nextElement();
// 排除loopback类型地址
if (inetAddr.isLoopbackAddress()) {
continue;
}
if (inetAddr.isSiteLocalAddress()) {
// 如果是site-local地址就是它了
return inetAddr;
} else if (candidateAddress == null) {
// site-local类型的地址未被发现先记录候选地址
candidateAddress = inetAddr;
}
}
}
if (candidateAddress != null) {
return candidateAddress;
}
// 如果没有发现 non-loopback地址.只能用最次选的方案
return InetAddress.getLocalHost();
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
}

View File

@ -0,0 +1,64 @@
/*
* 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 cn.iocoder.dashboard.framework.register.util;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.atomic.AtomicInteger;
/**
* Copy form Dubbo
* InternalThreadFactory.
*/
public class NamedThreadFactory implements ThreadFactory {
protected static final AtomicInteger POOL_SEQ = new AtomicInteger(1);
protected final AtomicInteger mThreadNum = new AtomicInteger(1);
protected final String mPrefix;
protected final boolean mDaemon;
protected final ThreadGroup mGroup;
public NamedThreadFactory() {
this("pool-" + POOL_SEQ.getAndIncrement(), false);
}
public NamedThreadFactory(String prefix) {
this(prefix, false);
}
public NamedThreadFactory(String prefix, boolean daemon) {
mPrefix = prefix + "-thread-";
mDaemon = daemon;
SecurityManager s = System.getSecurityManager();
mGroup = (s == null) ? Thread.currentThread().getThreadGroup() : s.getThreadGroup();
}
@Override
public Thread newThread(Runnable runnable) {
String name = mPrefix + mThreadNum.getAndIncrement();
Thread ret = new Thread(mGroup, runnable, name, 0);
ret.setDaemon(mDaemon);
return ret;
}
public ThreadGroup getThreadGroup() {
return mGroup;
}
}