目录
1.概述
Nacos 是阿里巴巴新的开源项目,可以快速实现动态服务发现、服务配置、服务元数据及流量管理。本篇博客以实现服务注册与发现为主。
中文官网
https://nacos.io/zh-cn/
中文文档
https://nacos.io/zh-cn/docs/quick-start.html官网文档
https://nacos.io/zhcn/docs/clustermodequickstart.html
安装包
https://github.com/alibaba/Nacos/releasesSpringCloudAlibaba 源码地址:
https://github.com/alibaba/spring-cloud-alibaba
Nacos源码地址https://github.com/alibaba/nacos
Nacos源码下载地址:https://github.com/alibaba/nacos/releases
Nacos源码加速下载地址:https://gitee.com/mirrors/Nacos/releases
在SpringCloud中也有一个同样优秀的服务注册与发现组件:Eureka,不过已经闭源了,这也是我为什么要学习 Nacos 的原因之一。
- 核心的相同之处:
- 引入依赖
- 配置注册中心地址
- 添加注解
最大的不同之处:
- 服务端的搭建方式
- Eureka Server 是自己手动搭建的
- Nacos Server 需要通过源码构建或者直接下载的方式搭建
- 再来说说 Nacos Server 一些其他的重要信息
部署方式: 三种部署模式,分别对应不同的使用场景
- 单机模式:方便本地测试用
- 集群模式:适用于生产环境
- 多集群模式:用于多数据中心
启动方式: 两种启动方式,分别对应上面部署方式中的单机模式和集群模式
- Linux / Unix / Mac 系统中单机模式启动需要添加 -m standalone 参数;反之,如果不加 -m standalone 参数则表示是集群模式启动。
-
完整命令:sh startup.sh -m standalone
-
- Windows 系统中目前只支持单机模式启动,直接双击启动脚本文件。或完整命令:cmd startup.cmd
数据源: 两种数据源,分别可以在配置文件中指定
- Derby:内嵌在应用中的数据库(目前默认的数据源)
- MySQL:免费开源的关系型数据库(版本需要 5.6.5+)
- Nacos Server 的版本选择: 目前官网推荐的稳定版本为 1.2.1
2.SpringBoot添加 Nacos依赖
项目版本说明
Spring Boot | Spring Cloud | Spring Cloud Alibaba | Nocos | JDK |
---|---|---|---|---|
2.7.7 | 2021.0.5 | 2021.0.5.0 | 2.2.9.RELEASE | 1.8 |
创建模块化项目,项目结构如下:
2.1.项目搭建
pom文件,一个是指明打包方式,其次配置子项目,使用dependencyManagement管理jar包的版本,让子项目中引用一个依赖而不用显示的列出版本号
<?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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.6.15</version>
</parent>
<groupId>com.nacos</groupId>
<artifactId>nocos-util</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>nocos-util</name>
<description>nocos-util</description>
<!--打包方式-->
<packaging>pom</packaging>
<modules>
<!-- 配置子项目-->
<!-- 对应artifactId-->
<module>customers</module>
<module>producers</module>
</modules>
<properties>
<java.version>1.8</java.version>
<lombok.version>1.18.0</lombok.version>
<nacos.config.version>0.2.7</nacos.config.version>
<spring.boot.version>2.6.15</spring.boot.version>
<spring.cloud.version>2021.0.3</spring.cloud.version>
<spring.cloud.alibaba.version>2021.0.5.0</spring.cloud.alibaba.version>
</properties>
<!-- 配置dependencyManagement,子项目导入jar包则无需指定版本信息,方便jar包版本统一管理-->
<dependencyManagement>
<!--Spring Cloud alibaba的版本管理, 通过dependency完成继承-->
<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>
<!--SpringBoot的版本管理-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>${spring.boot.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<!--Spring Cloud的版本管理-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>${spring.cloud.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<!--lombok-->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>${lombok.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
补充说明
Dependencies
相对于dependencyManagement,所有在dependencies里的依赖都会自动引入,并默认被所有的子项目继承
dependencyManagement和Dependencies区别
dependencies即使在子项目中不写该依赖项,那么子项目仍然会从父项目中继承该依赖项(全部继承)。
dependencyManagement里只是声明依赖,并不实现引入,因此子项目需要显示的声明需要用的依赖。如果不在子项目中声明依赖,是不会从父项目中继承下来的;只有在子项目中写了该依赖项,并且没有指定具体版本,才会从父项目中继承该项,并且version和scope都读取自父pom;另外如果子项目中指定了版本号,那么会使用子项目中指定的jar版本。
2.1.服务注册
创建一个子模块producers-service(生产者)
1)配置父项目
<parent>
<groupId>com.nacos</groupId>
<artifactId>nocos-util</artifactId>
<version>0.0.1-SNAPSHOT</version>
</parent>
2)pom.xml
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</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>
<!--nacos-服务注册发现-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<!--(nacos统一配置中心管理依赖)-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
</dependency>
<!--使用bootstrap.yml需要这个jar-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-context</artifactId>
</dependency>
<!-- Spring Cloud 新版本默认将 Bootstrap 禁用,需要将 spring-cloud-starter-bootstrap 依赖引入到工程中 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-bootstrap</artifactId>
</dependency>
</dependencies>
3)bootstrap.yml
spring:
application:
name: producers-service
profiles:
#指定启动环境
active: dev
cloud:
nacos:
discovery:
# Nacos服务注册中心地址
server-addr: 127.0.0.1:8848
# 分组,不同分组之间不能调用,用于微服务之间的隔离
group: dev
# 生产环境
namespace: 91d34e57-55b0-4182-b881-ab0b5aefabe9
config:
# 是否开启配置中心 默认true
enabled: true
# 指定Nacos配置中心的地址
server-addr: 127.0.0.1:8848
# 区分环境:开发环境、测试环境、预发布环境、⽣产环境
namespace: 91d34e57-55b0-4182-b881-ab0b5aefabe9
# 区分不同应⽤:同⼀个环境内,不同应⽤的配置,通过group来区分。
group: ${spring.profiles.active}
# 配置文件的格式:指定yaml格式,配置 默认properties
file-extension: yaml
# 自动刷新配置文件,默认true
refresh-enabled: true
# Nacos 认证用户
username: nacos
# Nacos 认证密码
password: nacos
application.yml 和bootStrap.yml 在同一目录下,则bootStrap.yml 的加载顺序要高于application.yml,即bootStrap.yml 会优先被加载。
原理:bootstrap.yml 用于应用程序上下文的引导阶段。
bootstrap.yml 由父Spring ApplicationContext加载。
•bootstrap.yml 可以理解成系统级别的一些参数配置,这些参数一般是不会变动的。
•application.yml 可以用来定义应用级别的,用户级的资源配置项
4)Controller
/**
* @ClassName: ProController
* @author: dyt
* @since: 2023/08/03 上午 10:35
*/
// 刷新配置
@RestController
@RequestMapping("/pro")
public class ProController {
@RequestMapping("/reduct")
public String reduct() throws InterruptedException {
System.out.println("扣减库存");
return "扣减库存:" ;
}
@RequestMapping("/reduct2")
public String reduct2() {
int a = 1 / 0;
System.out.println("扣减库存");
return "扣减库存:" ;
}
}
2.2.服务发现
创建一个子模块customers-service(消费者),同样先在pom中配置父项目(参考章节2.1),引入nacos服务管理和配置中心jar包
1)pom.xml
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!--nacos-服务注册发现-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<!--(nacos统一配置中心管理依赖)-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
</dependency>
<!--openfeign-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
<!-- Spring Cloud 新版本默认将 Bootstrap 禁用,需要将 spring-cloud-starter-bootstrap 依赖引入到工程中 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-bootstrap</artifactId>
</dependency>
</dependencies>
2)bootstrap.yml
spring:
application:
name: customers-service
profiles:
#指定启动环境
active: dev
cloud:
nacos:
discovery:
# Nacos服务注册中心地址
server-addr: 127.0.0.1:8848
# 分组,不同分组之间不能调用,用于微服务之间的隔离
group: ${spring.profiles.active}
# 生产环境
namespace: 91d34e57-55b0-4182-b881-ab0b5aefabe9
config:
# 是否开启配置中心 默认true
enabled: true
# 指定Nacos配置中心的地址
server-addr: 127.0.0.1:8848
# 区分环境:开发环境、测试环境、预发布环境、⽣产环境
namespace: 91d34e57-55b0-4182-b881-ab0b5aefabe9
# 区分不同应⽤:同⼀个环境内,不同应⽤的配置,通过group来区分。
group: ${spring.profiles.active}
# 配置文件的格式:指定yaml格式,配置 默认properties
file-extension: yaml
# 自动刷新配置文件,默认true
refresh-enabled: true
# Nacos 认证用户
username: nacos
# Nacos 认证密码
password: nacos
3)启动类
@SpringBootApplication
//@EnableDiscoveryClient
//引入了spring-cloud-starter-alibaba-nacos-discovery依赖时,默认情况下会自动启用Nacos的服务注册和发现功能
public class OrderApplication {
public static void main(String[] args) {
SpringApplication.run(OrderApplication.class,args);
}
@Bean
@LoadBalanced
public RestTemplate restTemplate(RestTemplateBuilder builder){
RestTemplate restTemplate = builder.build();
return restTemplate;
}
}
2.3.服务调用
Consumer 服务消费者的依赖的配置基本与 Provider 服务提供者相同,但是却要比它更复杂一点。因为在 Consumer 端需要去调用 Provider 端的 REST 服务。而调用方式也有很多种。
在这里使用2种方式消费服务,一种是RestTemplate,一种是Feign
第一种:RestTemplate 方式
RestTemplate可以使用Ribbon作为负载均衡组件:
Spring Cloud 2020版本以后,默认移除了对Netflix的依赖,其中就包括Ribbon,官方默认推荐使用Spring Cloud Loadbalancer正式替换Ribbon,并成为了Spring Cloud负载均衡器的唯一实现。
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-loadbalancer</artifactId>
<version>3.0.1</version>
</dependency>
在customers工程中引入ribbon的依赖,依赖、配置与 Provider 一致。
在消费者启动类上配置。如下所示:
@Bean
@LoadBalanced
public RestTemplate restTemplate(RestTemplateBuilder builder){
RestTemplate restTemplate = builder.build();
return restTemplate;
}
然后调用 Provider 服务提供者。如下所示:
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.client.RestTemplate;
/**
* @ClassName: CustomersController
* @author: dyt
* @since: 2023/08/03 上午 10:39
*/
@org.springframework.web.bind.annotation.RestController
@RequestMapping("/rest")
public class RestController {
@Autowired
public RestTemplate restTemplate;
@RequestMapping(value = "/test2/")
public String sayHelloService2() {
return restTemplate.getForObject("http://producers-service/pro/reduct/", String.class);
}
}
查看返回结果进行验证。
第二种:OpenFeign调用服务
OpenFeign 介绍
OpenFeign 的全称是 Spring Cloud OpenFeign,它是 Spring 官方推出的一种声明式服务调用和负载均衡组件 。它的出现就是为了替代已经进入停更维护状态的 Feign(Netflix Feign)的。也就是说 OpenFeign(Spring Cloud OpenFeign)是 Feign 的升级版,它们的关系如下图所示:
因为 Feign 停更维护了,所以 Spring 官方需要推出了一个新的新的框架来对 Feign 功能进行升级和扩展。
OpenFeign 使用
OpenFeign 是用在服务消费端的,有消费端就得有服务提供端
OpenFeign 使用过程
- 依赖导入 open-feign
- 定义feign接口,标记@FeignClient(“服务名”)
- Controller中注入Feign接口
- 主配置类上使用@EnableFeignClients
1)pom.xml添加依赖
customers子项目引入Feign的jar包,如下:
<!-- 添加 openfeign 框架依赖 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
2)启动类上添加 @EnableFeignClients 的注解,启用Feign客户端:
@SpringBootApplication
//启用nacos服务注册发现
@EnableDiscoveryClient
@EnableFeignClients // 启用 OpenFeign
public class UserConsumerApplication {
public static void main(String[] args) {
SpringApplication.run(UserConsumerApplication.class, args);
}
}
3)使用Fegin实现微服务调用,写一个interface来完成feign的服务调用:
import com.nacos.customers.service.imp.UserServiceFallback;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.RequestMapping;
/**
* @InterfaceName: UserService
* @author: dyt
* @since: 2023/08/03 下午 4:08
*/
@FeignClient(name = "producers-service")
public interface UserService {
@RequestMapping("/pro/reduct")
String config();
}
接口中,我们写了一个config()方法,方法上添加@RequestMapping的注解,并配置具体的路径。这样,我们在调用服务的时候,通过Feign调用到具体的服务提供者了。
我们写了一个UserService的接口,在接口上添加@FeignClient的注解,注解里有两个属性,
- name:服务提供者的名称,这里为producers-service;
- fallback:指定发生熔断时,调用的类
- 当我们的服务提供者不能正常提供服务时,就会触发熔断机制,会调用熔断服务类的逻辑,返回结果。
4)测试接口调用:
@RestController
@RequestMapping("/cust")
public class CustomersController {
@Autowired
private UserService userService;
@RequestMapping("consumer-feign")
public String userService() {
String result = userService.config();
return result;
}
}
注意事项
OpenFeign 默认的接口超时时间为 1s,所以如果接口的执行时间超过 1s,那么程序调用就会报错。
OpenFeign 熔断
想要在Feign中使用熔断器大概需要4步骤:
- 实现Feign接口,给每一个方法添加一个熔断处理
- @FeignClient(name='服务名',fallback =熔断接口实现类))
- 使用feign的hystrix那么在feign类上就不能添加@RequestMapping了
- (非必须)在调用Feign的地方把@Autowired换成@Resource 主要是防止爆红
import com.nacos.customers.service.imp.UserServiceFallback;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.RequestMapping;
/**
* @InterfaceName: UserService
* @author: dyt
* @since: 2023/08/03 下午 4:08
*/
@FeignClient(name = "producers-service",fallback = UserServiceFallback.class)
public interface UserService {
@RequestMapping("/pro/reduct")
String config();
}
我们再来看看熔断实现类UserServiceFallback的具体内容,如下:
//方法失败熔断器回调实现类
@Service
public class UserServiceFallback implements UserService {
@Override
public String config() {
return "网络异常,服务暂时无法访问";
}
public void sendEmail(){
System.out.println("发送邮件通知管理员,进行处理");
}
}
首先,它是UserService接口,也就是Feign接口的实现类,然后实现接口中的方法,我们直接返回user-fallback字符串。
feign的熔断开关,默认是关闭
@RestController
@RequestMapping("/rest")
public class FeignController {
// 加载 openfeign client
@Resource
private ProviderClient providerClient;
@RequestMapping("/consumer")
public String consumer(@RequestParam String name) {
// 向调用本地方法一样,调用 openfeign client 中的方法
return providerClient.call("reduct");
}
}
OpenFeign 默认的接口超时时间为 1s,测试熔断还是和以前一样将timeoutInMilliseconds
改为1000毫秒 然后停止生产者之后立即使用消费者去访问,基本上试几次就出效果了
请求和响应进行GZIP压缩
Spring Cloud Feign 支持对请求和响应进行GZIP压缩,以减少通信过程中的性能损耗。通过下面的参数即可开启请求的压缩功能简单来说就是发送的内容大小变小了,那么也就变相的加快了请求的速度 建议配置
修改consumer的application.yml配置
feign:
compression:
request:
enabled: true # 开启请求压缩
mime-types: text/html,application/xml,application/json # 设置压缩的数据类型
min-request-size: 2048 # 设置触发压缩的大小下限
请求日志会显示gzip
注意事项:
3.使用Nacos权重负载均衡
服务的调用方法都给大家介绍完了,但是,他们的负载均衡策略都是轮询,这有点不符合我们的要求,我们进入到Nacos的管理后台,调节一下服务的权重,如图:
我们将8080接口的服务权重由1改为10,点击确认,再多次刷新一下我们的访问地址,发现服务的调用还是在8080和8081之间轮询。**这是什么情况?这里就不和大家卖关子了,这是因为LoadBalancerClient、Feign和Ribbon3种方式,它们的底层都是使用Ribbon做负载均衡的,而Ribbon负载均衡默认使用的策略是ZoneAvoidanceRule,我们要修改Ribbon的默认策略,让它使用nacos的权重,那么该如何配置呢?
我们进入到nacos管理后台,修改consumer的配置,添加如下配置:
producers-service:
ribbon:
NFLoadBalancerRuleClassName: com.alibaba.cloud.nacos.ribbon.NacosRule
producers-service是我们服务的名称,你配置哪个服务的负载均衡策略,就写哪个服务的名字。
后面ribbon.NFLoadBalancerRuleClassName需要配置负载均衡策略的具体实现,这个实现类要实现IRule接口,在这里,我们指定实现类为com.alibaba.cloud.nacos.ribbon.NacosRule。这是nacos的负载均衡规则,它是实现了IRule接口的。
重启项目,调用我们之前的3个链接,调用哪个效果都是一样的,**我们发现返回tom:8080的次数明显增多,说明Nacos服务的权重配置生效了。
4.小结
SpringBoot整合Nacos引用jar包,存在两个版本:
1)基于Spring-cloud-starter-alibaba-nacos-discovery
2)基于nacos-discovery-spring-boot-starter
3)spring-cloud-starter-alibaba-nacos-discovery:这是Spring Cloud Alibaba项目提供的一个jar包,用于在Spring Cloud应用程序中集成Nacos服务发现功能。它是基于Spring Cloud框架的,可以与其他Spring Cloud组件(如Eureka、Consul等)无缝集成。
4)nacos-discovery-spring-boot-starter:这是Nacos官方提供的一个jar包,用于在Spring Boot应用程序中集成Nacos服务发现功能。它是基于Spring Boot框架的,可以直接在Spring Boot应用程序中引入并使用。
总的来说,它们的功能和使用方法是相同的,只是命名空间和版本号不同。如果你使用的是Spring Cloud框架,可以选择使用spring-cloud-starter-alibaba-nacos-discovery;如果你只是使用Spring Boot框架,可以选择使用nacos-discovery-spring-boot-starter。
5.问题描述
在测试中遇到一些小问题,记录下来,方便后续 查阅
问题描述
本地测试,springboot注册了nacos服务,然后一个springboot通过服务名(http://服务名/接口)调用服务,查看nacos控制台服务都已经注册了,但是无论我是否给RestTemplate添加@LoadBalanced注解,都是报UnknownHostException错误,或者 UnknownHostException: producers-service] with root cause
解决办法
在maven pom.xml中添加下面的依赖即可
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-loadbalancer</artifactId>
<version>3.0.1</version>
</dependency>
鸣谢
[1].https://blog.csdn.net/qq_41402200/article/details/107121599
[2].https://blog.csdn.net/ronaldoliubo/article/details/109675814
[3].https://blog.csdn.net/lll7777773/article/details/131629266
[4].https://blog.csdn.net/xiaozhu0301/article/details/105637307
[5].https://blog.csdn.net/lll7777773/article/details/131629266