Nacos
Nacos是 Dynamic Naming and Configuration Service 的首字母简称,一个更易于构建云原生应用的动态服务发现、配置管理和服务管理平台。
版本说明:版本说明 · alibaba/spring-cloud-alibaba Wiki · GitHub
<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.6.13</spring-boot.version>
<spring-cloud.version>2021.0.5</spring-cloud.version>
<spring-cloud-alibaba.version>2021.0.5.0</spring-cloud-alibaba.version>
</properties>
Quick Start
Windows安装Nacos(Linux同理)
去GitHub下载压缩包,将conf文件夹里的nacos-mysql.sql
表存储到数据库中
取消application.properties
这三行的注释,修改user和password
启动:双击startup.cmd
会报错,在cmd中输入startup.cmd -m standalone
。这是因为默认以集群方式启动,而集群启动需要配置,不配置会报错,所以以单例启动就可以
启动后访问:http://localhost:8848/nacos/index.html#
遇到的坑
- 初始化完成后登录显示用户名或密码错误,这是系统bug,重启服务
- 2.x版本连接报错
Client not connected, current status:STARTING
,防火墙开启8848、8948、8949 - 2.x版本集群8848、8849、8850时,8849启动失败。设置为8848、8850、8852即可
Nacos注册中心
服务注册与发现
概念
微服务与传统单体式应用架构最大区别就是强调软件模块的拆分。在单体架构下,一个应用系统的多个功能模块由于组织在一起在同一个应用进程内部署与运行,因此,模块之间直接通过方法调用即可完成对一次请求的响应。但在微服务系统中,需要对一个应用系统根据其功能特点,按照一定粒度进行拆分后单独部署,以便实现模块内的高内聚,模块间的低耦合,实现整个微服务系统的高可扩展性:
原来一次在一个应用内即可完成的请求处理,会出现跨进程跨主机的服务调用,如何让这个服务之间能互相发现像单体式应用一样提供统一对外的服务调用能力是微服务框架层面需要重点解决的核心问题之一。 在 Spring Cloud 生态中,采用了如下服务注册与发现模型,来实现微服务之间的互相发现与调用。
通过在微服务系统中引入一个叫做注册中心的组件,来作为协调者。其最简化的过程是,所有的微服务应用在启动过程中会将自身包含服务名称、主机IP地址和端口号等信息发送到注册中心中,然后上游的微服务在处理请求过程中,根据服务名称到注册中心中查找对应服务的所有实例IP地址和端口号来进行服务调用,整个过程如图中虚线所示。从而让分散的微服务系统之间能像一个整体一样对外提供请求处理能力。
项目demo1
创建一个springboot项目,加入相关依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
编写applicant.yml
配置文件
# 端口
server:
port: 8081
# 应用名称
spring:
application:
name: nacos-client-02
# nacos配置
cloud:
nacos:
username: nacos
password: edishena
# 向哪里注册
server-addr: 43.139.248.149:8848
discovery:
# 注册的服务名
service: nacos-service
# 注册到哪个命名空间
namespace: 79151db2-6086-49be-b3ac-17eaa5946e2c
# 注册到哪个组
group: A_GROUP
开启服务发现
package priv.happy.nacosclient01;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.openfeign.EnableFeignClients;
@SpringBootApplication
// 开启服务发现
@EnableDiscoveryClient
public class NacosClient01Application {
public static void main(String[] args) {
SpringApplication.run(NacosClient01Application.class, args);
}
}
启动项目后即可在nacos控制台看到当前的服务
跨命名空间调用
不能跨命名空间、跨组调用
集成OpenFeign
注意事项
添加依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
注意,SpringCloud Feign在Hoxton.M2 RELEASE版本之后不再使用ribbon,而是使用spring-cloud-loadbalancer,所以在不引入spring-cloud-loadbalancer情况下会报错
org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'testController': Unsatisfied dependency expressed through field 'feign'; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'priv.happy.nacosclient01.feign.FoodFeign': Unexpected exception during bean creation; nested exception is java.lang.IllegalStateException: No Feign Client for loadBalancing defined. Did you forget to include spring-cloud-starter-loadbalancer?
……
at priv.happy.nacosclient01.NacosClient01Application.main(NacosClient01Application.java:14) [classes/:na]
Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'priv.happy.nacosclient01.feign.FoodFeign': Unexpected exception during bean creation; nested exception is java.lang.IllegalStateException: No Feign Client for loadBalancing defined. Did you forget to include spring-cloud-starter-loadbalancer?
... 20 common frames omitted
Caused by: java.lang.IllegalStateException: No Feign Client for loadBalancing defined. Did you forget to include spring-cloud-starter-loadbalancer?
... 28 common frames omitted
解决方法
- 第一中,通过修改依赖解决
-
- 排除nacos中的ribbon依赖
- 添加loadbalancer依赖
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
<exclusions>
<exclusion>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-ribbon</artifactId>
</exclusion>
</exclusions>
</dependency>
<!--客户端负载均衡loadbalancer-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-loadbalancer</artifactId>
</dependency>
- 第二种,通过修改配置解决
spring.cloud.loadbalancer.ribbon.enabled=false
spring.cloud.loadbalancer.nacos.enabled=true
注意:需要在同一个namespace的同一个group下才能调用成功,否则报错503
2023-08-21 11:01:45.278 ERROR 9388 --- [nio-8080-exec-1] o.a.c.c.C.[.[.[/].[dispatcherServlet] : Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed; nested exception is feign.FeignException$ServiceUnavailable: [503] during [GET] to [http://nacos-service/food] [FoodFeign#getFood()]: [Load balancer does not contain an instance for the service nacos-service]] with root cause
feign.FeignException$ServiceUnavailable: [503] during [GET] to [http://nacos-service/food] [FoodFeign#getFood()]: [Load balancer does not contain an instance for the service nacos-service]
at feign.FeignException.serverErrorStatus(FeignException.java:256) ~[feign-core-11.10.jar:na]
at feign.FeignException.errorStatus(FeignException.java:197) ~[feign-core-11.10.jar:na]
at feign.FeignException.errorStatus(FeignException.java:185) ~[feign-core-11.10.jar:na]
at feign.codec.ErrorDecoder$Default.decode(ErrorDecoder.java:92) ~[feign-core-11.10.jar:na]
at feign.AsyncResponseHandler.handleResponse(AsyncResponseHandler.java:98) ~[feign-core-11.10.jar:na]
at feign.SynchronousMethodHandler.executeAndDecode(SynchronousMethodHandler.java:141) ~[feign-core-11.10.jar:na]
at feign.SynchronousMethodHandler.invoke(SynchronousMethodHandler.java:91) ~[feign-core-11.10.jar:na]
at feign.ReflectiveFeign$FeignInvocationHandler.invoke(ReflectiveFeign.java:100) ~[feign-core-11.10.jar:na]
at com.sun.proxy.$Proxy69.getFood(Unknown Source) ~[na:na]
at priv.happy.nacosclient01.controller.TestController.test(TestController.java:33) ~[classes/:na]
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:1.8.0_221]
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[na:1.8.0_221]
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:1.8.0_221]
at java.lang.reflect.Method.invoke(Method.java:498) ~[na:1.8.0_221]
项目demo2
服务提供者
新建一个上述的springboot项目,作为服务服务提供者,编写一个controller对外提供服务(这个服务的名称是nacos-service)
package priv.happy.nacosclient02.controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class ProviderController {
@GetMapping("food")
public String getFood() {
return "大米饭一碗";
}
}
服务消费者(由消费者发起远程调用)
新建一个demo1中的springboot项目,作为消费者,添加openfeign依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
开启远程调用
package priv.happy.nacosclient01;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.openfeign.EnableFeignClients;
@SpringBootApplication
@EnableDiscoveryClient
// 开启远程调用
@EnableFeignClients
public class NacosClient01Application {
public static void main(String[] args) {
SpringApplication.run(NacosClient01Application.class, args);
}
}
编写Feignclient,发起远程调用
package priv.happy.nacosclient01.feign;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
// value是要调用的那个服务的名称
@FeignClient(value = "nacos-service")
public interface FoodFeign {
@GetMapping("food")
String getFood() ;
}
编写消费者的controller,对用户提供访问功能
package priv.happy.nacosclient01.controller;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.discovery.DiscoveryClient;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import priv.happy.nacosclient01.feign.FoodFeign;
import java.util.List;
/**
* @Author: HAPPY
* @Project_name: nacos-practice
* @Package_name: priv.happy.nacosclient01.controller
* @Date: 2023/8/21 10:22
* @Description:
*/
@RestController
public class TestController {
@Autowired
DiscoveryClient discoveryClient;
@Autowired
FoodFeign feign;
@GetMapping("test")
public String test(){
List<ServiceInstance> instances = discoveryClient.getInstances("nacos-service");
System.out.println(instances);
return feign.getFood();
}
}
集成GateWay
加入gateway依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
端口:80
server:
port: 80
spring:
application:
name: gateway
cloud:
nacos:
server-addr: 43.139.248.149:8848
username: nacos
password: edishena.
discovery:
group: A_GROUP
namespace: 79151db2-6086-49be-b3ac-17eaa5946e2c
gateway:
discovery:
locator:
# 开启动态路由
enabled: true
lower-case-service-id: true
nacos集群
- 8848
-
- 9848
- 9849
- 8850
-
- 9850
- 9851
- 8852
-
- 9852
- 9853
Nacos2.0版本相比1.X新增了gRPC的通信方式,因此需要增加2个端口。新增端口是在配置的主端口(server.port)基础上,进行一定偏移量自动生成。
端口 | 与主端口的偏移量 | 描述 |
9848 | 1000 | 客户端gRPC请求服务端端口,用于客户端向服务端发起连接和请求 |
9849 | 1001 | 服务端gRPC请求服务端端口,用于服务间同步等 |
7848 | -1000 | Jraft请求服务端端口,用于处理服务端间的Raft相关请求 |
使用VIP/nginx请求时,需要配置成TCP转发,不能配置http2转发,否则连接会被nginx断开。 9849和7848端口为服务端之间的通信端口,请勿暴露到外部网络环境和客户端测。
配置文件
server:
port: 8080
spring:
application:
name: nacos-client-01
cloud:
nacos:
# server-addr: 43.139.248.149:8848
# 配置集群
server-addr: localhost:8848, localhost:8850, localhost:8852
# username: nacos
# password: edishena.
discovery:
namespace: c7ffbcd1-c4d8-425b-b41c-a1077693fa57
group: A_GROUP
Nacos配置管理
在单体架构的时候将配置写在配置文件中,缺点就是每次修改配置都需要重启服务才能生效。当应用程序实例比较少的时候还可以维护。如果转向微服务架构有成百上千个实例,每修改⼀次配置要将全部实例重启,不仅增加了系统的不稳定性,也提高了维护的成本。做到服务不重启就可以修改配置,产生了四个基础诉求:
- 需要支持动态修改配置
- 需要动态变更有多实时
- 变更快了之后如何管控控制变更风险,如灰度、回滚等
- 敏感配置如何做安全配置
采用分布式配置中心的软件架构如图所示,其可以在分布式场景中帮助解决以下问题:
- 管理应用程序配置:当有大量应用程序需要管理时,手动维护配置文件会变得非常困难。分布式配置中心提供了一个集中管理和分发配置信息的解决方案。
- 环境隔离:在开发、测试和生产等不同环境中,应用程序的配置信息往往都会有不用。使用分布式配置中心,可以轻松地管理和分发不同环境下的配置信息。
- 提高程序安全性:将配置信息存储在代码库或应用程序文件中可能会导致安全风险,因为这些信息可能会被意外地泄漏或被恶意攻击者利用。使用分布式配置,可以将配置信息加密和保护,并且可以进行访问权限控制。
- 动态更新配置:在应用程序运行时,可能需要动态地更新配置信息,以便应用程序可以及时响应变化。使用分布式配置中心,可以在运行时动态更新配置信息,而无需重新启动应用程序。
依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
</dependency>
<!-- springcloud 2020禁用了bootstrap,需要手动导入 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-bootstrap</artifactId>
</dependency>
配置文件名称为:prefix-active.file-extension
,需要在nacos控制台输入后缀
server:
port: 8082
spring:
application:
name: nacos-config-01
cloud:
nacos:
username: nacos
password: 123456
server-addr: localhost:8848
# 项目启动时在哪里找配置文件
config:
# 读哪个配置文件
prefix: nacos-config-01
# 配置文件格式
file-extension: yml
namespace: c7ffbcd1-c4d8-425b-b41c-a1077693fa57
# 配置文件名称为 prefix-active.file-extension
profiles:
active: dev
读取多个配置文件
server:
port: 8082
spring:
application:
name: nacos-config-01
cloud:
nacos:
username: nacos
password: 123456
server-addr: localhost:8848
# 项目启动时在哪里找配置文件
config:
# 配置文件格式
file-extension: yml
namespace: c7ffbcd1-c4d8-425b-b41c-a1077693fa57
# 读取多个配置文件
extension-configs:
- data-id: nacos-config-01-dev.yml
group: DEFAULT_GROUP
refresh: true
- data-id: nacos-config-02-dev.yml
group: A_GROUP
refresh: true
项目启动报错
[main] DEBUG org.springframework.boot.diagnostics.LoggingFailureAnalysisReporter - Application failed to start due to an exception
org.springframework.cloud.commons.ConfigDataMissingEnvironmentPostProcessor$ImportException: No spring.config.import set
***************************
APPLICATION FAILED TO START
***************************
Description:
No spring.config.import property has been defined
Action:
Add a spring.config.import=nacos: property to your configuration.
If configuration is not required add spring.config.import=optional:nacos: instead.
To disable this check, set spring.cloud.nacos.config.import-check.enabled=false.
加入bootstrap依赖
<!-- springcloud 2020 以后禁用了bootstrap,需要手动导入 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-bootstrap</artifactId>
</dependency>
问题
username和password不写也可以
使用Nacos时,如果不配置Nacos的用户名和密码,可以将username和password属性留空或不配置。Nacos客户端将以匿名方式连接到Nacos服务器。
standalone和cluster模式下点击下线没反应
Nacos已经下线了该功能,只是前端UI未进行修改
cluster配置集群的ip,每次电脑的ip都不一样,需要每次开机都修改cluster.conf中的ip地址
改成localhost:port
即可
只启动了三个集群多了一个
这是因为在nacos的配置文件中没有指定nacos的ip,因此默认使用192.168.0.1这种,但集群的配置文件里的ip又是127.0.0.1这种,因此会显示两个。在application.properties
文件中修改即可。
nacos控制台中,每个服务都有一个订阅者,且应用名是unknown
应用名unknown是因为需要进行配置,有两种方式(我只是了第一种),在运行配置中添加虚拟机选项 -Dproject.name=application
服务提供者的订阅者中,刚开始是没有消费者的,只有当消费者发送过请求,访问过服务提供者以后,服务提供者的订阅者中才会有消费者