一、安装配置
1、jar包启动
1、下载nacos包
https://github.com/alibaba/nacos/
下载安装包(默认下载部署到服务器上)
2、解压
mkdir -p /home/zukxu/nacos-1.4.2tar -zxvf nacos-server-1.4.1.tar.gz -C /home/zukxu/nacos
3、启动
进入bin目录,使用启动脚本进行启动
nacos启动分为两种环境
单机环境启动(standalone)
集群环境(cluster)
单机环境下启动需要添加参数 -m standalone
./startup.sh -m standalone
#查看
jps

日志路径
/usr/local/soft/nacos/logs/start.out
默认端口为8848
访问路径为/nacos
输入浏览器进行访问
默认账户密码为nacos
4、数据持久化
nacos支持数据持久化
- 将conf目录下 的nacos-mysql.sql文件运行
- 更改配置文件
- 启动 ```xml spring.datasource.platform=mysql
db.num=1 db.url.0=jdbc:mysql://127.0.0.1:3306/nacos_config?characterEncoding=utf8&connectTimeout=10000&socketTimeout=30000&autoReconnect=true db.user=root db.password=123456
<a name="WLAG9"></a>
# 二、服务注册/发现
<a name="BPAE8"></a>
## 1、流程
<br />提供者启动<br />注册到注册中心<br />消费者启动,到注册中心进行订阅<br />服务发生变化,通知消费者<br />统计调用次数等
spring-cloud-starter-alibaba-nacos-discovery通过自动配置以及其他Spring 编程模型的习惯用法<br />为 Spring Boot 应用程序在服务注册与发现方面提供和Nacos的无缝集成,<br />通过一些简单的注解,就可以快速来注册一个服务,<br />并使用Nacos组件来作为大规模分布式系统的服务注册中心;
开发提供者和消费者服务<br />只需要在pom文件之中添加相关依赖即可
<a name="Nk6oz"></a>
### 核心功能
- 服务注册
Nacos Client会通过发送REST请求 的方式向Nacos Server注册自己的服务,提供自身的元数据<br />例如:Ip,端口等信息,<br />Nacos Server 收到注册请求后,就会把这些元数据信息存储到一个双层 的Map中
- 服务心跳
服务注册后,Nacos Client会维护一个定时心跳来持续通知Nacos Server 说明服务一直处于可用状态,防止被剔除,默认5S一次心跳
- 服务同步
Nacos 集群之间会进行相互同步实例的信息,用以保证信息一致性
- 服务发现
服务消费者(Nacos Client) 会在调用服务提供者的服务时,会发送一个REST请求给Nacos Server 获取上面注册的服务清单,并且缓存在Nacos Client本地,同时会在Nacos Client 本地开启一个定时任务定时拉取服务端最新的注册表信息更新到本地缓存
- 服务健康检查
- Nacos Server会开启一个定时任务用来检查注册服务实例的健康状况,对于超过了15S没有收到客户端心跳的实例会将他的headlthy属性置为fakse,【客户端服务发现时不会发现】如果超过30S 没有收到心跳,就将剔除该实例,如果服务恢复发送心跳则会重新注册
<a name="iVAtl"></a>
### 服务注册中心对比
| **功能名称** | **Nacos** | **Eureka** | **Consul** | **Zookeeper** |
| --- | --- | --- | --- | --- |
| | **开源,功能齐全,推荐使用** | **Netflix(已闭源)新的功能都不支持,不推荐使用** | **除了雪崩保护不支持外,其余都支持** | **Dubbo家族中的组件** |
| **一致性协议** | **支持CP+AP 切换,默认AP** | **AP** | **CP** | **CP** |
| **健康检查** | **TCP/HTTP/MySql/Client Beat** | **Client Beat** | **TCP/HTTP/gRPC/CMD** | **Keep Alive** |
| **负载均衡** | **权重/metadata/selector**<br />**可以集成三方:Ribbon** | **Ribbon** | **Fabio** | **——** |
| **雪崩保护** | **Y** | **N** | **N** | **N** |
| **自动注销实例** | **Y** | **Y** | **Y** | **Y** |
| **访问协议** | **HTTP/DNS** | **HTTP** | **HTTP/DNS** | **TCP** |
| **监听支持** | **Y** | **Y** | **Y** | **Y** |
| **多数据中心** | **Y** | **Y** | **Y** | **N** |
| **跨注册中心同步** | **Y** | **N** | **Y** | **N** |
| **SpringCloud集成** | **Y** | **Y** | **Y** | **Y** |
| **Dubbo集成** | **Y** | **N** | **Y** | **Y** |
| **K8S集成** | **Y** | **N** | **Y** | **N** |
| | | | | |
| | **CAP:C一致性 A 可用性 P 分区容错性**
**任何一个架构都无法保障三个同时启用** | | | |
<a name="E19ph"></a>
## 2、开发
<a name="bJ5RY"></a>
### 服务注册——provider服务
<a name="y5WBW"></a>
#### 依赖
web依赖<br />SpringBoot依赖<br />nacos注册服务依赖<br />SpringCloud 依赖
```xml
<!--spring boot web依赖-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!--spring-cloud-alibaba 服务注册发现-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
依赖管理
<!--导入父依赖-->
<dependencyManagement>
<dependencies>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-alibaba-dependencies</artifactId>
<version>${spring-cloud-alibaba.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>${spring-boot.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
配置文件
bootstrap.yml
server:
port: 36001
spring:
application:
name: nacos-discovery-provider
cloud:
nacos:
config:
file-extension: yaml #nacos配置文件扩展名
#group: DEFAULT_GROUP #分组
server-addr: 192.168.100.215:8848 #配置中心地址
#namespace: 7d5515a8-c9c5-48da-83be-d0af8962a343 #命名空间id
discovery:
server-addr: 192.168.100.215:8848 #注册中心地址
#登录信息
password: nacos
username: nacos
#profiles:
#active: dev #不同开发环境
启动类配置
添加相关注解【@EnableDiscoveryClient】
/**
* @author zukxu
*/
@EnableDiscoveryClient
@SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
编写请求接口
@Slf4j
@RestController
@RequestMapping("/provider")
public class ProviderController {
@GetMapping("/")
public String index() {
log.info("provider index");
return "index首页";
}
@GetMapping("/test")
public String test() {
log.info("provider test");
return "test测试页";
}
@GetMapping("/divide/{str}")
public String divide(@PathVariable("str") String s) {
log.info("provider divide/{str}");
return "Hello Nacos divide"+s;
}
}
注册结果
服务发现——consumer服务
依赖
<properties>
<java.version>1.8</java.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<spring-boot.version>2.3.0.RELEASE</spring-boot.version>
<spring-cloud-alibaba.version>2.2.1.RELEASE</spring-cloud-alibaba.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!--spring-cloud-alibaba 服务注册发现-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-ribbon</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-alibaba-dependencies</artifactId>
<version>${spring-cloud-alibaba.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>${spring-boot.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Hoxton.SR3</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
配置文件
server:
port: 36002
spring:
application:
name: nacos-discovery-consumer
cloud:
nacos:
config:
file-extension: yaml #nacos配置文件扩展名
group: MY_GROUP #分组
server-addr: 192.168.100.215:8848 #配置中心地址
namespace: 7d5515a8-c9c5-48da-83be-d0af8962a343
discovery:
server-addr: 192.168.100.215:8848 #注册中心地址
#登录信息
password: nacos
username: nacos
启动类
@EnableFeignClients
@EnableDiscoveryClient
@SpringBootApplication
public class Application {
@Bean
public RestTemplate restTemplate(){
return new RestTemplate();
}
public static void main(String[] args) throws InterruptedException {
ConfigurableApplicationContext applicationContext = SpringApplication.run(Application.class, args);
while(true) {
//当动态配置刷新时,会更新到 Enviroment中,因此此处每隔一秒从Enviroment中获取配置
String userName = applicationContext.getEnvironment().getProperty("user.name");
String userAge = applicationContext.getEnvironment().getProperty("user.age");
System.out.println("user name : " + userName + "; age: " + userAge);
//获取当前部署的环境
String currentEnv = applicationContext.getEnvironment().getProperty("current.env");
System.err.println("in [ "+currentEnv+" ] enviroment; "+"user name :" + userName + "; age: " + userAge);
TimeUnit.SECONDS.sleep(1);
}
}
}
请求接口
@Slf4j
@RestController
public class ConsumerController {
@Autowired
FeignService feignService;
@Autowired
private LoadBalancerClient loadBalancerClient;
@Autowired
private RestTemplate restTemplate;
@GetMapping("/echoApp/{app}")
public String echoAppName(@PathVariable("app") String app) {
//使用 LoadBalanceClient 和 RestTemplate 结合的方式来访问
//传递参数为服务名,得到服务实例
ServiceInstance serviceInstance = loadBalancerClient.choose("nacos-discovery-provider");
// 拼接获取远程服务路径
String url = String.format("http://%s:%s/echo/%s", serviceInstance.getHost(), serviceInstance.getPort(), app);
System.out.println("request url:" + url);
return restTemplate.getForObject(url, String.class);
}
@GetMapping("/test")
public String test() {
//要想使用服务名进行访问,就需要在启动类上添加负载均衡注解@LoadBalanced
//不然只能写死IP和端口
return restTemplate.getForObject("http://nacos-discovery-provider/test", String.class);
}
@GetMapping("/feign")
public Object feign() {
return feignService.notfound();
}
}
Feign接口
@FeignClient(name = "nacos-discovery-provider", fallback = FeignServiceFallback.class, configuration = FeignConfiguration.class)
public interface FeignService {
@GetMapping("/echo/{app}")
String echo(@PathVariable("app") String app);
@GetMapping("/notfound")
String notfound();
}
/***************************************************************/
public class FeignConfiguration {
@Bean
public FeignServiceFallback feignServiceFallback() {
return new FeignServiceFallback();
}
}
/***************************************************************/
public class FeignServiceFallback implements FeignService{
@Override
public String echo(@PathVariable("msg") String msg) {
return "echo error";
}
@Override
public String notfound() {
return " notfound error";
}
}
注册结果

请求结果
| 服务 | 请求路径 | 返回结果 |
|---|---|---|
| provider | http://localhost:18081/test | test |
| http://localhost:18081/echo/Hello World | provider Hello World | |
| http://localhost:18081/notFound | provide notFound | |
| consumer | http://localhost:18082/echo/Hello World | provider Hello World |
| http://localhost:18082/test | 报错,需要在启动类上添加负载均衡注解 @LoadBalanced |
|
| http://localhost:18082/feign | 404,服务提供者需要实现 |
还可以为服务添加SpringBoot的端口检测包
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
可以暴露端点查看相关信息
spring-cloud-starter-alibaba-nacos-discovery在实现的时候提供了一个
EndPoint,EndPoint的访问地址为
http://ip:port/actuator/nacos-discovery
EndPoint的信息主要提供了两类:
1、subscribe:显示了当前有哪些服务订阅者
2、NacosDiscoveryProperties:显示了当前服务实例关于Nacos的基础配置
三、消费者负载均衡调用提供者
底层使用ribbon组件实现
可以使用两种方式
restTemplate和feign
四、Nacos客户端信息缓存
Nacos即使宕机,服务之间也是可以进行调用的,因为Nacos内部为我们做了简单的缓存
