目录
Nacos简介
Nacos 致力于帮助您发现、配置和管理微服务。Nacos 提供了一组简单易用的特性集,帮助您快速实现动态服务发现、服务配置、服务元数据及流量管理。
Nacos 帮助您更敏捷和容易地构建、交付和管理微服务平台。 Nacos 是构建以“服务”为中心的现代应用架构 (例如微服务范式、云原生范式) 的服务基础设施。
Nacos 的关键特性包括:
- 服务发现和服务健康监测:Nacos 支持基于 DNS 和基于 RPC 的服务发现。服务提供者使用 原生SDK、OpenAPI、或一个独立的Agent TODO注册 Service 后,服务消费者可以使用DNS TODO 或HTTP&API查找和发现服务。
- 动态配置服务:动态配置服务可以让您以中心化、外部化和动态化的方式管理所有环境的应用配置和服务配置。动态配置消除了配置变更时重新部署应用和服务的需要,让配置管理变得更加高效和敏捷。配置中心化管理让实现无状态服务变得更简单,让服务按需弹性扩展变得更容易。
- 动态 DNS 服务:动态 DNS 服务支持权重路由,让您更容易地实现中间层负载均衡、更灵活的路由策略、流量控制以及数据中心内网的简单DNS解析服务。动态DNS服务还能让您更容易地实现以 DNS 协议为基础的服务发现,以帮助您消除耦合到厂商私有服务发现 API 上的风险。
- 服务及其元数据管理:Nacos 能让您从微服务平台建设的视角管理数据中心的所有服务及元数据,包括管理服务的描述、生命周期、服务的静态依赖分析、服务的健康状态、服务的流量管理、路由及安全策略、服务的 SLA 以及最首要的 metrics 统计数据。
nacos官网https://nacos.io/zh-cn/docs/what-is-nacos.html
Nacos 快速开始
可以参考官网https://nacos.io/zh-cn/docs/quick-start.html
1. 下载源码
- 因为github下载的速度特别慢,这里使用gitee的国内镜像下载源码,这里使用官方建议的1.3.1稳定版本
- 进入nacos目录,执行mvn -Prelease-nacos -Dmaven.test.skip=true clean install -U
2. 启动服务
- 到Nacos\distribution\target\nacos-server-1.3.1\nacos\bin目录下找到startup.cmd
- 右键编辑,如果是单机使用,必须是standalone,如果是集群使用,是cluster
- 双击启动
- 启动成功
- 访问http://localhost:8848/nacos,用户名密码默认都是nacos
服务注册
参考官方文档https://github.com/alibaba/spring-cloud-alibaba/wiki/Nacos-discovery
1. 创建一个micro-order的module
2. 引入依赖
- 版本之间需要匹配
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.example</groupId>
<artifactId>micro-order</artifactId>
<version>1.0-SNAPSHOT</version>
<name>micro-order</name>
<!-- FIXME change it to the project's website -->
<url>http://www.example.com</url>
<!--使用了springboot就要引入这个-->
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.2.2.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>1.7</maven.compiler.source>
<maven.compiler.target>1.7</maven.compiler.target>
<!--springcloud版本号-->
<spring.cloud.version>Hoxton.SR1</spring.cloud.version>
<!--springcloudalibaba版本号-->
<spring.cloud.alibaba.version>2.2.3.RELEASE</spring.cloud.alibaba.version>
</properties>
<dependencyManagement>
<dependencies>
<!--需要springcloud组件支撑需要引入这个-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>${spring.cloud.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<!--需要springcloudalibaba组件支撑需要引入这个-->
<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>
</dependencies>
</dependencyManagement>
<dependencies>
<!--web启动-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!--健康检查-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<!--服务注册-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<!--配置中心-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
</dependencies>
<build>
<pluginManagement><!-- lock down plugins versions to avoid using Maven defaults (may be moved to parent pom) -->
<plugins>
<!-- clean lifecycle, see https://maven.apache.org/ref/current/maven-core/lifecycles.html#clean_Lifecycle -->
<plugin>
<artifactId>maven-clean-plugin</artifactId>
<version>3.1.0</version>
</plugin>
<!-- default lifecycle, jar packaging: see https://maven.apache.org/ref/current/maven-core/default-bindings.html#Plugin_bindings_for_jar_packaging -->
<plugin>
<artifactId>maven-resources-plugin</artifactId>
<version>3.0.2</version>
</plugin>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.0</version>
</plugin>
<plugin>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.22.1</version>
</plugin>
<plugin>
<artifactId>maven-jar-plugin</artifactId>
<version>3.0.2</version>
</plugin>
<plugin>
<artifactId>maven-install-plugin</artifactId>
<version>2.5.2</version>
</plugin>
<plugin>
<artifactId>maven-deploy-plugin</artifactId>
<version>2.8.2</version>
</plugin>
<!-- site lifecycle, see https://maven.apache.org/ref/current/maven-core/lifecycles.html#site_Lifecycle -->
<plugin>
<artifactId>maven-site-plugin</artifactId>
<version>3.7.1</version>
</plugin>
<plugin>
<artifactId>maven-project-info-reports-plugin</artifactId>
<version>3.0.0</version>
</plugin>
</plugins>
</pluginManagement>
</build>
</project>
3. 配置文件
- 使用bootstrap.properties,后面使用nacos作为注册中心要求文件名必须是bootstrap,不能使用application
- 指定端口号,服务名称,nacos地址,开放全部请求端点
server.port=8081
spring.application.name=micro-order
spring.cloud.nacos.discovery.server-addr=127.0.0.1:8848
management.endpoints.web.exposure.include=*
4. 创建启动类
- 开启服务注册功能@EnableDiscoveryClient
package org.example;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.context.ConfigurableApplicationContext;
@SpringBootApplication
@EnableDiscoveryClient
public class MicroOrderApplication {
public static void main(String[] args) {
SpringApplication.run(MicroOrderApplication.class, args);
}
}
5. 创建一个controller
package org.example.Controller;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.core.env.Environment;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@Slf4j
@RestController
public class UserController {
@RequestMapping("/queryUser")
public String queryUser(HttpServletRequest request, HttpServletResponse response) {
log.info("========micro-order===queryUser");
return "========micro-order===queryUser" + request.getServerPort();
}
}
6. 启动服务
- 刷新服务列表
- 再启动一个服务,使用8082端口
- 创建一个启动类MicroOrderApplication2使用8082端口再次启动
- 刷新服务列表,这时候有2个实例了
负载均衡
1. 创建一个micro-web的module
2. 引入依赖
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.example</groupId>
<artifactId>micro-web</artifactId>
<version>1.0-SNAPSHOT</version>
<name>micro-web</name>
<!-- FIXME change it to the project's website -->
<url>http://www.example.com</url>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.2.2.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>1.7</maven.compiler.source>
<maven.compiler.target>1.7</maven.compiler.target>
<!--springcloud版本号-->
<spring.cloud.version>Hoxton.SR1</spring.cloud.version>
<!--springcloudalibaba版本号-->
<spring.cloud.alibaba.version>2.2.3.RELEASE</spring.cloud.alibaba.version>
</properties>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>${spring.cloud.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<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>
</dependencies>
</dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<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>
</dependencies>
<build>
<pluginManagement><!-- lock down plugins versions to avoid using Maven defaults (may be moved to parent pom) -->
<plugins>
<!-- clean lifecycle, see https://maven.apache.org/ref/current/maven-core/lifecycles.html#clean_Lifecycle -->
<plugin>
<artifactId>maven-clean-plugin</artifactId>
<version>3.1.0</version>
</plugin>
<!-- default lifecycle, jar packaging: see https://maven.apache.org/ref/current/maven-core/default-bindings.html#Plugin_bindings_for_jar_packaging -->
<plugin>
<artifactId>maven-resources-plugin</artifactId>
<version>3.0.2</version>
</plugin>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.0</version>
</plugin>
<plugin>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.22.1</version>
</plugin>
<plugin>
<artifactId>maven-jar-plugin</artifactId>
<version>3.0.2</version>
</plugin>
<plugin>
<artifactId>maven-install-plugin</artifactId>
<version>2.5.2</version>
</plugin>
<plugin>
<artifactId>maven-deploy-plugin</artifactId>
<version>2.8.2</version>
</plugin>
<!-- site lifecycle, see https://maven.apache.org/ref/current/maven-core/lifecycles.html#site_Lifecycle -->
<plugin>
<artifactId>maven-site-plugin</artifactId>
<version>3.7.1</version>
</plugin>
<plugin>
<artifactId>maven-project-info-reports-plugin</artifactId>
<version>3.0.0</version>
</plugin>
</plugins>
</pluginManagement>
</build>
</project>
3. 配置文件
server.port=8091
spring.application.name=micro-web
spring.cloud.nacos.discovery.server-addr=127.0.0.1:8848
management.endpoints.web.exposure.include=*
4. 创建启动类
- 使用带有负载均衡的RestTemplate
package org.example;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.context.annotation.Bean;
import org.springframework.web.client.RestTemplate;
@SpringBootApplication
@EnableDiscoveryClient
public class MicroWebAppliaction {
//开启负载均衡
@Bean
@LoadBalanced
RestTemplate restTemplate() {//http请求客户端
return new RestTemplate();
}
public static void main(String[] args) {
SpringApplication.run(MicroWebAppliaction.class, args);
}
}
5. 创建controller
package org.example.Controller;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;
@Slf4j
@RestController
public class UserController {
//指定调用的服务名称
private static String SERVER_NAME = "micro-order";
@Autowired
RestTemplate restTemplate;
@RequestMapping(value = "/queryUser")
public String queryUser() {
String result = restTemplate.getForObject("http://" + SERVER_NAME
+ "/queryUser", String.class);
log.info(result);
return result;
}
}
6. 启动服务
7. 服务调用
- 第一次请求http://localhost:8091/queryUser,请求到了8082端口的micro-order
- 第二次请求http://localhost:8091/queryUser,请求到了8081端口的micro-order
- 这里是默认的轮询负载均衡策略
配置中心
参考官网https://github.com/alibaba/spring-cloud-alibaba/wiki/Nacos-config
1. 引入依赖
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
</dependency>
2. 配置文件
spring.cloud.nacos.config.server-addr=127.0.0.1:8848
spring.cloud.nacos.config.file-extension=properties
3. 修改controller
- 返回从配置中心获得的值
package org.example.Controller;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.core.env.Environment;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
//动态刷新
@RefreshScope
@Slf4j
@RestController
public class UserController {
//获取配置中心的值
@Value("${server-name}")
private String serverName;
//获取配置中心的值
@Autowired
Environment environment;
@RequestMapping("/queryUser")
public String queryUser(HttpServletRequest request, HttpServletResponse response) {
log.info("========micro-order===queryUser");
return "serverName=" +serverName+";environmentValue="+
environment.getProperty("server-name");
}
}
4. 添加配置
properties
1、micro-order
- Data Id使用服务名称,不带properties后缀
- 对应的配置文件
- 启动服务,请求http://localhost:8081/queryUser,查看打印
2、micro-order.properties
- 再次请求http://localhost:8081/queryUser,不需要重启服务,查看打印
- 这里实现了动态刷新配置
- 这里得出结论,当data id只存在micro-order时,会获取micro-order配置下的值,如果data id既有micro-order又有micro-order.properties时,会优先获取micro-order.properties下的值
3、fisher-dev.properties
- 使用fisher作为data id的前缀,dev作为-dev,.properties必须要带上
spring.cloud.nacos.config.prefix=fisher
#需要配合spring.cloud.nacos.config.prefix使用,单独使用无效
spring.profiles.active=dev
- 重启服务,请求http://localhost:8081/queryUser,查看打印
yaml
1、micro-order
- 配置文件使用bootstrap.yml
- 把之前的data id为micro-order的配置改为yaml格式,这里server-name: micro-order使用冒号,不能用等号
- 重启服务,请求http://localhost:8081/queryUser,查看打印
2、micro-order.yaml
- 添加data id为micro-order.yaml的配置
- 再次请求http://localhost:8081/queryUser,不需要重启服务,查看打印
- 和properties格式的配置一样,这里也实现了动态刷新配置
- 同样的结论,当data id只存在micro-order时,会获取micro-order配置下的值,如果data id既有micro-order又有micro-order.yaml时,会优先获取micro-order.yaml下的值
3、myPrefix.yaml
nacos的弱一致性协议distro
- nacos支持cap理论中的cp和ap,ap使用的是distro协议,cp使用的是raft协议,这里介绍阿里自研的弱一致性协议distro。
- Client 与 Server 有两个重要的交互,服务注册与心跳发送;
- Client 向 Server 注册一个服务,继而以执行任务的模式向 Server 发送一次心跳,心跳包需要带上 Client 的全部信息, 站在 Client 的角度 Server 集群所有的节点对等,所以请求 Server 集群中的节点是随机的;
- Client 如果请求失败则换一个 Server 的节点重新发送请求;
- Server 集群的任意一个节点都存储所有数据,但每个节点只负责其中一部分服务,在接收到 Client 的“写“(注册、心跳、下线等)请求后,Server 节点判断请求的服务是否为自己负责,如果是,则处理,否则交由负责的节点处理;
- 每个 Server 节点主动发送健康检查到其他节点,响应的节点被该节点视为健康节点;
- Server 在接收到 Client 的心跳后,如果该服务不存在,则将该心跳请求当做注册请求来处理;
- Server 如果 15s 没有收到 Client 心跳,则把该 Client 标记为不健康;如果 30s 没有收到心跳,则下线该 Client ;
- 负责的 Server 节点在接收到 Client 的服务注册、服务心跳等写请求后将数据写入后即返回,后台异步地将数据同步给其他节点;
- Server 节点在收到读请求后直接从本机获取后返回,不管数据是否为最新 com.alibaba.nacos.naming.consistency.ephemeral.distro.DistroConsistencyServiceImpl#put taskDispatcher.addTask(key);
- 任意一个 Server 节点在启动的时候会去判断当前 Server 集群当中的节点是否大于 1;如果大于 1 则会去执行远程同步;否则会让线程睡眠 1s 继续判断集群节点是否大于 1;
- Client 在启动的时候(spring 容器启动的时候会实例化 NacosAutoServiceRegistration,会实例化 NacosServiceRegistry 构造方法,里面会调用 namingServiceInstance—>createNamingService---- >createNamingService–>new NacosNamingService—构造方法— init----xxxx----udp 等待 Server 发送数据)会建立一个 UDP 的接受方;Client 在向 Server 获取服务列表的时候 Server 会收集到 Client 的 UDP 信息;以后当 Server 更新的时候会把更新信息推送给 Client ;但是 Server 默认不发这个 Client 的?因为 Server 不知道这个 Client 需要数据;
- Client 在第一次向 Server 获取服务列表的时候;会创建一个线程池每隔 10s 钟会去 Server 获取一次最新的服务列表, 同时还会把当前 Client 的所有信息(包括udp)发给Server,Server 接受到信息之后把 Client 添加到发送 upd 的列表当中;
- Client 会把服务列表缓存到本地,下次请求会直接从本地缓存拿服务列表,如果 Server 中的服务列表有变化,就会通过 udp 把最新的服务列表发送给 Client ;
- distro 判断一个请求属于 Server 集群中的那个节点负责用的是一个非常简单的 hash 算法(int target = distroHash(serviceName) % healthyList.size();),假设 Server 某个节点发生故障,则会重新计算,其实可以采用一致性的 hash 算法,这样只会重新计算以前分发到这个故障节点的机器上的请求, 不需要全部重新计算。