pom 依赖
<gson.version>2.10</gson.version>
<fjson.version>1.2.15</fjson.version>
<hutool.version>5.8.11</hutool.version>
<apcommon.version>3.1</apcommon.version>
<google.guava.version>30.1-jre</google.guava.version>
<spring.mvc.version>5.2.7.RELEASE</spring.mvc.version>
<jasypt-spring-boot-starter.version>3.0.4</jasypt-spring-boot-starter.version>
<dependencies>
<!-- nacos 服务注册发现 -->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
<exclusions>
<!-- 将ribbon排除 -->
<exclusion>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-ribbon</artifactId>
</exclusion>
</exclusions>
</dependency>
<!--添加loadbalancer依赖
由于 Netflix Ribbon 进入停更维护阶段,因此 SpringCloud 2020.0.1 版本之后 删除了eureka中的ribbon,替代ribbon的是spring cloud自带的LoadBalancer,默认使用的是轮询的方式
新版本的 Nacos discovery 都已经移除了 Ribbon ,此时我们需要引入 loadbalancer 代替,才能调用服务提供者提供的服务
-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-loadbalancer</artifactId>
</dependency>
<!--配置中心-->
<!-- <dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
</dependency>-->
<!-- 支持读取bootstrap配置 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-bootstrap</artifactId>
</dependency>
<!--encryption tool starts encryption-->
<dependency>
<groupId>com.github.ulisesbocchio</groupId>
<artifactId>jasypt-spring-boot-starter</artifactId>
<version>${jasypt-spring-boot-starter.version}</version>
</dependency>
<!--actuator-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
<exclusions>
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-logging</artifactId>
</exclusion>
</exclusions>
</dependency>
<!-- web依赖包-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<!-- 去掉springboot默认配置 -->
<exclusions>
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-logging</artifactId>
</exclusion>
</exclusions>
</dependency>
<!-- 引入log4j2依赖 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-log4j2</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<!--fastjson-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>${fjson.version}</version>
</dependency>
<!--guava-->
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>${google.guava.version}</version>
</dependency>
<!--commons-lang3-->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>${apcommon.version}</version>
</dependency>
<!--hutool-->
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>${hutool.version}</version>
</dependency>
<!--dataformat-->
<!-- 加上这个才能辨认到log4j2.yml文件 -->
<dependency>
<groupId>com.fasterxml.jackson.dataformat</groupId>
<artifactId>jackson-dataformat-yaml</artifactId>
</dependency>
<!-- 支持读取bootstrap配置 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-bootstrap</artifactId>
</dependency>
<!--
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-consul-discovery</artifactId>
<optional>true</optional>
</dependency>-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>
</dependencies>
<dependencyManagement>
<!--SpringBoot整合Spring Cloud-->
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>2021.0.4</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<!--SpringBoot整合Spring Cloud Alibaba-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-alibaba-dependencies</artifactId>
<version>2021.0.4.0</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
yml配置
spring:
application:
name: hlvy-nacos-regis
description: 服务发现与注册服务
cloud:
nacos:
discovery:
server-addr: 你的nacos地址
#指定命名空间 可以用于区分环境 只有同一环境下服务才可以调用
namespace: 命名空间
#指定集群名称
cluster-name: ShangHai
username: 账号
password: 密码
server:
port: 8849
management:
endpoints:
web:
exposure:
include: '*'
endpoint:
health:
show-details: always
wii:
# 异构微服务的IP,异构微服务的端口,注册服务名,分组默认DEFAULT_GROUP,异构微服务的健康检查URL可以不填则不检测(注意如果需要检测定义一个接口返回status: "UP"即可)
# 127.0.0.1,8060,huf-user,DEFAULT_GROUP,http://localhost:8060/health.json
regisService:
- 127.0.0.0,8847,hlvy-test,DEFAULT_GROUP
代码:
WiiProperties
import lombok.Data;
import java.net.URI;
/**
* @title: 服务注册信息
* @author: luo heng
* @date: 2023/2/28 16:44
* @description: TODO
* @version:
* @return:
*/
@Data
public class WiiProperties {
/**
* polyglot service's ip
*/
private String ip;
/**
* polyglot service's port
*/
private Integer port;
/**
* 注册名
*/
private String nacosName ;
/**
* 分组
*/
private String group = "DEFAULT_GROUP";
/**
* polyglot service's health check url.
* this endpoint must return json and the format must follow spring boot actuator's health endpoint.
* eg. {"status": "UP"}
*/
private URI healthCheckUrl;
}
ExWillProperties
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.PropertySource;
import java.net.URI;
import java.util.ArrayList;
import java.util.List;
/**
* @program: hyf-nacos-regis
* @description: 需要注册的服务信息
* @author: loren
* @Description: TODO
* @create: 2023-02-28 11:15
**/
@Slf4j
@Data
@ConfigurationProperties(prefix = "wii")
@PropertySource(value = "classpath:application-${spring.profiles.active}.yml", encoding = "UTF-8")
public class ExWillProperties {
private List<String> regisService;
/**
* 解析配置
* @return
*/
public List<WiiProperties> getWiiProperties(){
List<WiiProperties> list = new ArrayList<>();
try {
WiiProperties wiiProperties = null;
if (null != this.regisService){
for (String info : regisService) {
String[] split = info.split(",");
int length = split.length;
if (length >= 3) {
wiiProperties = new WiiProperties();
wiiProperties.setIp(split[0]);
wiiProperties.setPort(Integer.valueOf(split[1]));
wiiProperties.setNacosName(split[2]);
if (length >= 4) {
wiiProperties.setGroup(split[3]);
if (length >= 5) {
wiiProperties.setHealthCheckUrl(URI.create(split[4]));
}
}
list.add(wiiProperties);
}
}
}
}catch (Exception e){
log.warn("wiiProperties error:{}",e.getMessage());
}
return list;
}
}
WiiHealthIndicator
import lombok.RequiredArgsConstructor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.actuate.health.Health;
import org.springframework.core.ParameterizedTypeReference;
import org.springframework.http.HttpMethod;
import org.springframework.http.ResponseEntity;
import org.springframework.web.client.RestTemplate;
import java.net.URI;
import java.util.Map;
/**
* @title: 健康检查
* @author: luo heng
* @date: 2023/2/28 16:43
* @description: TODO
* @version:
* @return:
*/
@RequiredArgsConstructor(onConstructor = @__(@Autowired))
public class WiiHealthIndicator {
private final RestTemplate restTemplate;
public Health doHealthCheck(Health.Builder builder,URI healthCheckUrl) throws Exception {
try {
URI uri = healthCheckUrl;
if (uri == null) {
builder.up();
return builder.build();
}
ResponseEntity<Map<String, Object>> exchange = this.restTemplate.exchange(
uri,
HttpMethod.GET,
null,
new ParameterizedTypeReference<Map<String, Object>>() {
}
);
Map<String, Object> map = exchange.getBody();
if (map == null) {
this.getWarning(builder);
return builder.build();
}
Object status = map.get("status");
if (status instanceof String) {
builder.status(status.toString());
} else {
this.getWarning(builder);
}
} catch (Exception e) {
builder.down().withDetail("error", e.getMessage());
}
return builder.build();
}
private void getWarning(Health.Builder builder) {
builder.unknown().withDetail("warning", "no status field in response");
}
}
WiiAutoConfiguration
import com.alibaba.cloud.nacos.NacosDiscoveryProperties;
import com.hy.hyfnacosregis.nacos.properties.ExWillProperties;
import com.hy.hyfnacosregis.nacos.wii.nacos.WiiChecker;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.client.RestTemplate;
/**
* @title: 服务注册配置
* @author: luo heng
* @date: 2023/2/28 16:44
* @description: TODO
* @version:
* @return:
*/
@Configuration
@EnableConfigurationProperties({ExWillProperties.class})
public class WiiAutoConfiguration {
@Bean
@ConditionalOnMissingBean
public RestTemplate restTemplate() {
return new RestTemplate();
}
@Bean
public WiiHealthIndicator wiiHealthIndicator(RestTemplate restTemplate) {
return new WiiHealthIndicator(restTemplate);
}
@Bean
public WiiChecker wiiCleaner(NacosDiscoveryProperties nacosDiscoveryProperties, WiiHealthIndicator wiiHealthIndicator, ExWillProperties exWillProperties ) {
WiiChecker cleaner = new WiiChecker(nacosDiscoveryProperties, wiiHealthIndicator,exWillProperties);
cleaner.check();
return cleaner;
}
}
WiiChecker
import com.alibaba.cloud.nacos.NacosDiscoveryProperties;
import com.hy.hyfnacosregis.nacos.properties.ExWillProperties;
import com.hy.hyfnacosregis.nacos.properties.WiiProperties;
import com.hy.hyfnacosregis.nacos.wii.WiiHealthIndicator;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.actuate.health.Health;
import org.springframework.boot.actuate.health.Status;
import reactor.core.scheduler.Schedulers;
import java.util.List;
import java.util.concurrent.TimeUnit;
/**
* @author www.itmuch.com
*/
@Slf4j
@RequiredArgsConstructor(onConstructor = @__(@Autowired))
public class WiiChecker {
private final NacosDiscoveryProperties nacosDiscoveryProperties;
private final WiiHealthIndicator wiiHealthIndicator;
private final ExWillProperties exWillProperties;
/**
* 健康检查
*/
public void check() {
Schedulers.single()
.schedulePeriodically(
() -> {
try {
List<WiiProperties> wiiPropertiesList = exWillProperties.getWiiProperties();
if (null != wiiPropertiesList &&wiiPropertiesList.size() > 0){
for (WiiProperties properties : wiiPropertiesList) {
String ip = properties.getIp();
Integer port = properties.getPort();
Health.Builder builder = new Health.Builder();
Status status = new Status("DOWN");
status = wiiHealthIndicator.doHealthCheck(builder,properties.getHealthCheckUrl()).getStatus();
String applicationName = properties.getNacosName();
if (status.equals(Status.UP)) {
this.nacosDiscoveryProperties.namingServiceInstance().registerInstance(applicationName, ip, port);
log.debug("Health check success. register this instance. applicationName = {}, ip = {}, port = {}, status = {}",
applicationName, ip, port, status
);
} else {
log.warn("Health check failed. unregister this instance. applicationName = {}, ip = {}, port = {}, status = {}",
applicationName, ip, port, status
);
this.nacosDiscoveryProperties.namingServiceInstance().deregisterInstance(applicationName, ip, port);
}
// Thread.sleep(1000L);
}
}
} catch (Exception e) {
throw new RuntimeException(e);
}
},
0,
60,
TimeUnit.SECONDS
);
}
}
注意:目前暂未支持命名空间注册,所配置的服务都会注册到当前的命名空间的nacos中,分组也暂未实现 ,可根据当前代码扩展