原作者:nacos 实战(史上最全) - 疯狂创客圈 - 博客园
RuoYi-Cloud: 基于Spring Boot、Spring Cloud & Alibaba的分布式微服务架构权限管理系统,同时提供了 Vue3 的版本
# Tomcat
server:
port: 9203
# Spring
spring:
application:
# 应用名称
name: ruoyi-job
profiles:
# 环境配置
active: dev
cloud:
nacos:
discovery:
# 服务注册地址
server-addr: 127.0.0.1:8848
config:
# 配置中心地址
server-addr: 127.0.0.1:8848
# 配置文件格式
file-extension: yml
# 共享配置
shared-configs:
- application-${spring.profiles.active}.${spring.cloud.nacos.config.file-extension}
网上博客的配置共享方式:Nacos系列(10)-Nacos开启shared-configs配置共享,读取多个配置_云烟成雨TD的博客-CSDN博客_shared-configs[0]
源码都行:
public static class Config {
private String dataId;
private String group;
private boolean refresh;
public Config() {
this.group = "DEFAULT_GROUP";
this.refresh = false;
}
public Config(String dataId) {
this.group = "DEFAULT_GROUP";
this.refresh = false;
this.dataId = dataId;
}
public Config(String dataId, String group) {
this(dataId);
this.group = group;
}
public Config(String dataId, boolean refresh) {
this(dataId);
this.refresh = refresh;
}
public Config(String dataId, String group, boolean refresh) {
this(dataId, group);
this.refresh = refresh;
}
public String getDataId() {
return this.dataId;
}
public NacosConfigProperties.Config setDataId(String dataId) {
this.dataId = dataId;
return this;
}
public String getGroup() {
return this.group;
}
public NacosConfigProperties.Config setGroup(String group) {
this.group = group;
return this;
}
public boolean isRefresh() {
return this.refresh;
}
public NacosConfigProperties.Config setRefresh(boolean refresh) {
this.refresh = refresh;
return this;
}
public String toString() {
return "Config{dataId='" + this.dataId + '\'' + ", group='" + this.group + '\'' + ", refresh=" + this.refresh + '}';
}
public boolean equals(Object o) {
if (this == o) {
return true;
} else if (o != null && this.getClass() == o.getClass()) {
NacosConfigProperties.Config config = (NacosConfigProperties.Config)o;
return this.refresh == config.refresh && Objects.equals(this.dataId, config.dataId) && Objects.equals(this.group, config.group);
} else {
return false;
}
}
public int hashCode() {
return Objects.hash(new Object[]{this.dataId, this.group, this.refresh});
}
}
}
xxxxx的扩展配置方式:
server:
port: 18301
spring:
application:
name: xxxx
dbname: xxxxx
profiles:
# include: common
active: dev
server:
ip: 192.168.79.102
cloud:
nacos:
config:
server-addr: ${spring.server.ip}:8848
extension-configs:
# - data-id: xxxx-xx-common.yaml
# group: fusionsite-are
# refresh: true
- data-id: xxxx-xx-test.yaml
group: DEFAULT_GROUP
refresh: true
discovery:
server-addr: ${spring.server.ip}:8848
#ip : 192.168.70.158
main:
allow-bean-definition-overriding: true
注意:共享配置的自动刷新默认是关闭的,本服务的配置文件自动刷新默认是开启的!
nacos日志等级默认info,可以进行配置:
logging:
level:
com.alibaba.nacos.client.naming: debug
问题,既然有了Eureka ,为啥还要用Nacos?
而 Nacos 作为微服务核心的服务注册与发现中心,让大家在 Eureka 和 Consule 之外有了新的选择,开箱即用,上手简洁,暂时也没发现有太大的坑。
1 eureka 2.0闭源码了。
2 从官网来看nacos 的注册的实例数是大于eureka的,
3 因为nacos使用的raft协议,nacos集群的一致性要远大于eureka集群.
分布式一致性协议 Raft,自 2013 年论文发表,之后就受到了技术领域的热捧,与其他的分布式一致性算法比,Raft 相对比较简单并且易于实现,这也是 Raft 能异军突起的主要因素。
Raft 的数据一致性策略
Raft 协议强依赖 Leader 节点来确保集群数据一致性。即 client 发送过来的数据均先到达 Leader 节点,Leader 接收到数据后,先将数据标记为 uncommitted 状态,随后 Leader 开始向所有 Follower 复制数据并等待响应,在获得集群中大于 N/2 个 Follower 的已成功接收数据完毕的响应后,Leader 将数据的状态标记为 committed,随后向 client 发送数据已接收确认,在向 client 发送出已数据接收后,再向所有 Follower 节点发送通知表明该数据状态为committed。
三大优势:
springcloud config大部分场景结合git 使用, 动态变更还需要依赖Spring Cloud Bus 消息总线来通过所有的客户端变化.
springcloud config不提供可视化界面
nacos config使用长连接更新配置, 一旦配置有变动后,通知Provider的过程非常的迅速, 从速度上秒杀springcloud原来的config几条街,
目前 Spring Cloud Alibaba 主要有三个组件:
Nacos:一个更易于构建云原生应用的动态服务发现、配置管理和服务管理平台。
Sentinel:把流量作为切入点,从流量控制、熔断降级、系统负载保护等多个维度保护服务的稳定性。
AliCloud OSS: 阿里云对象存储服务(Object Storage Service,简称
OSS),是阿里云提供的海量、安全、低成本、高可靠的云存储服务。您可以在任何应用、任何时间、任何地点存储和访问任意类型的数据。
仔细看看各组件的功能描述,Spring Cloud Alibaba 套件和Spring Cloud Netflix套件大致的对应关系:
这是 Nacos 的架构图,可以看到它确实是融合了服务注册发现中心、配置中心、服务管理等功能,类似于 Eureka/Consule + Config + Admin 的合体。
另外通过官方文档发现,Nacos 除了可以和 Spring Cloud 集成,还可以和 Spring、SpringBoot 进行集成。
不过我们只关注于 Spring Cloud,别的就略过了,直接上手实战吧。
在使用 Nacos 之前,需要先下载 Nacos 并启动 Nacos Server。
安装的参考教程:
Nacos 安装(带视频) - 疯狂创客圈 - 博客园
Nacos Server 有两种运行模式:
此模式一般用于 demo 和测试,不用改任何配置,直接敲以下命令执行
sh bin/startup.sh -m standalone
Windows 的话就是
cmd bin/startup.cmd -m standalone
然后从 http://cdh1:8848/nacos/index.html 进入控制台就能看到如下界面了
默认账号和密码为:nacos nacos
测试环境,可以先用 standalone 模式撸起来,享受 coding 的快感,但是,生产环境可以使用 cluster 模式。
conf/cluster.conf
conf/application.properties
大致如下:
1: cluster.conf,填入要运行 Nacos Server 机器的 ip
192.168.100.155
192.168.100.156
db.num=1
db.url.0=jdbc:mysql://localhost:3306/nacos_config?characterEncoding=utf8&connectTimeout=1000&socketTimeout=3000&autoReconnect=true
db.user=root
db.password=root
创建一个名为nacos_config的 database,将NACOS_PATH/conf/nacos-mysql.sql中的表结构导入刚才创建的库中,这几张表的用途就自己研究吧
问题来了: Nacos Server 的配置数据是存在哪里呢?
我们没有对 Nacos Server 做任何配置,那么数据只有两个位置可以存储:
如果我们现在重启刚刚在运行的 Nacos Server,会发现刚才加的 nacos.properties 配置还在,说明不是内存存储的。
这时候我们打开NACOS_PATH/data,会发现里边有个derby-data目录,我们的配置数据现在就存储在这个库中。
Derby 是 Java 编写的数据库,属于 Apache 的一个开源项目
如果将数据源改为我们熟悉的 MySQL 呢?当然可以。
注意:不支持 MySQL 8.0 版本
这里有两个坑:
Nacos Server 的数据源是用 Derby 还是 MySQL 完全是由其运行模式决定的:
standalone 的话仅会使用 Derby,即使在 application.properties 里边配置 MySQL 也照样无视;
cluster 模式会自动使用 MySQL,这时候如果没有 MySQL 的配置,是会报错的。
防止 Nacos 宕机或重启后数据丢失,Nacos 支持将数据统一持久化到数据库 Mysql(在不配置Nacos持久化到Mysql时,
默认 Nacos 内置了一个嵌入式数据库derby,将一些数据保存到了内置的数据库上,多台 Nacos 就会出现多个内置数据库)。
官方提供的 cluster.conf 示例如下
#it is ip
#example
10.10.109.214
11.16.128.34
11.16.128.36
以上配置结束后,运行 Nacos Server 就能看到效果了。
实战的工程的目录结构如下:
首先引入 Spring Cloud Alibaba 的 BOM
org.springframework.boot
spring-boot-starter-parent
2.0.4.RELEASE
Finchley.SR2
0.2.0.RELEASE
org.springframework.cloud
spring-cloud-alibaba-dependencies
${spring-cloud-alibaba.version}
pom
import
org.springframework.cloud
spring-cloud-dependencies
${spring-cloud.version}
pom
import
这里版本号有坑,文档上说和 Spring Boot 2.0.x 版本兼容,但是实测 2.0.6.RELEASE 报错
java.lang.NoClassDefFoundError: org/springframework/core/env/EnvironmentCapable
服务注册中心和服务发现的服务端都是由 Nacos Server 来提供的,我们只需要提供 Service 向其注册就好了。
这里模拟提供两个 service:provider 和 consumer
alibaba
├── service-provider-demo
│ ├── pom.xml
│ └── src
└── sevice-consumer-demo
│ ├── pom.xml
│ └── src
└── pom.xml
step1:在 provider 和 consumer 的 pom 添加以下依赖:
org.springframework.boot
spring-boot-starter-web
org.springframework.cloud
spring-cloud-starter-alibaba-nacos-discovery
step2:启动类
使用 Spring Cloud 的原生注解 @EnableDiscoveryClient 开启服务注册与发现
package com.crazymaker.cloud.nacos.demo.starter;
import com.crazymaker.springcloud.standard.context.SpringContextUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.core.env.Environment;
import springfox.documentation.swagger2.annotations.EnableSwagger2;
import java.util.List;
@EnableSwagger2
@SpringBootApplication
@EnableDiscoveryClient
@Slf4j
public class ServiceProviderApplication {
public static void main(String[] args) {
ConfigurableApplicationContext applicationContext = SpringApplication.run(ServiceProviderApplication.class, args);
Environment env = applicationContext.getEnvironment();
String port = env.getProperty("server.port");
String path = env.getProperty("server.servlet.context-path");
System.out.println("\n--------------------------------------\n\t" +
"Application is running! Access URLs:\n\t" +
"Local: \t\thttp://localhost:" + port + path+ "/index.html\n\t" +
"swagger-ui: \thttp://localhost:" + port + path + "/swagger-ui.html\n\t" +
"----------------------------------------------------------");
}
}
step3:服务提供者的 Rest 服务接口
service-provider-demo 提供 一个非常简单的 Rest 服务接口以供访问
package com.crazymaker.cloud.nacos.demo.controller;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("/echo")
public class EchoController {
//回显服务
@RequestMapping(value = "/{string}", method = RequestMethod.GET)
public String echo(@PathVariable String string) {
return "echo: " + string;
}
}
step4:配置文件
spring:
application:
name: service-provider-demo
cloud:
nacos:
discovery:
server-addr: ${NACOS_SERVER:cdh1:8848}
server:
port: 18080
step5:启动之后,通过swagger UI访问:
在 NacosConsumerApplication 中集成 RestTemplate 和 Ribbon
@LoadBalanced
@Bean
public RestTemplate restTemplate() {
return new RestTemplate();
}
消费者的controller 类
package com.crazymaker.cloud.nacos.demo.consumer.controller;
import com.crazymaker.cloud.nacos.demo.consumer.client.EchoClient;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.Resource;
@RestController
@RequestMapping("/echo")
@Api(tags = "服务- 消费者")
public class EchoConsumerController {
//注入 @FeignClient 注解配置 所配置的 EchoClient 客户端Feign实例
@Resource
EchoClient echoClient;
//回显服务
@ApiOperation(value = "消费回显服务接口")
@RequestMapping(value = "/{string}", method = RequestMethod.GET)
public String echoRemoteEcho(@PathVariable String string) {
return "provider echo is:" + echoClient.echo(string);
}
}
消费者配置文件
spring:
application:
name: sevice-consumer-demo
cloud:
nacos:
discovery:
server-addr: 127.0.0.1:8848
server:
port: 18081
通过swagger UI访问消费者:
访问远程的echo API:
服务提供者 service-provider-demo:
http://localhost:18080/provider/swagger-ui.html#/Echo_%E6%BC%94%E7%A4%BA
服务消费者:
[http://localhost:18081/consumer/swagger-ui.html#/服务- 消费者/echoRemoteEchoUsingGET](http://localhost:18081/consumer/swagger-ui.html#/服务- 消费者/echoRemoteEchoUsingGET)
注册中心Nacos:
http://cdh1:8848/nacos/index.html#/serviceManagement?dataId=&group=&appName=&namespace=
这时候查看 Nacos Console 也能看到已注册的服务列表及其详情
1、首先,新建一个springboot项目,引入nacos依赖到项目中
com.alibaba.cloud
spring-cloud-starter-alibaba-nacos-config
2、在resources目录下新建bootstrap.yml文件
spring:
application:
name: nacos-config
cloud:
nacos:
config:
#配置中心地址
server-addr: 39.108.146.20:8848
#配置方式yml,目前仅支持properties/yaml
file-extension: yml
#默认开启自动刷新
refresh-enabled: true
#不同组可以有相同的属性
group: dev
#可以根据配置环境不同相互隔离
# namespace: pro
main:
allow-bean-definition-overriding: true
3、nacos配置中心新建yaml格式配置文件
Nacos 中 DataId配置信息_我家老洋的博客-CSDN博客_nacos配置dataid
配置信息如图:
解释一下:
1、dataId(唯一标识): {prefix}-{spring.profile.active}.${file-extension}
prefix 默认为 spring.application.name 的值,也可以通过配置项 spring.cloud.nacos.config.prefix来配置。
spring.profile.active 即为当前环境对应的 profile,详情可以参考 Spring Boot文档。 注意:当
spring.profile.active 为空时,对应的连接符 - 也将不存在,dataId 的拼接格式变成 ${prefix}.${file-extension}
file-exetension 为配置内容的数据格式,可以通过配置项 spring.cloud.nacos.config.file-extension 来配置。目前只支持 properties 和 yaml 类型。
2、group(配置组):在不同组中可以有相同的dataId
3、namespace(命名空间):可以隔离区分不同的环境配置文件,如:dev、test、pro、uat等环境
4、配置格式:暂时仅支持yaml、properties格式文件
5、配置内容:也就是你要配置的一些数据库敏感信息,能够实现动态获取配置。
配置完成以后如图:
@RefreshScope的作用:
经过@RefreshScope注解修饰的bean,将被RefreshScope进行代理,用来实现配置、实例热加载,即当配置变更时可以在不重启应用的前提下刷新bean中相关的属性值。
@RefreshScope+@Value实现,需要特别注意的是:共享的配置需要配置 refresh:true
注意点:如何我们只要注册中心不使用nacos的配置中心的话,去除配置中心的依赖即可,如果去掉不起作用,加入下面的pom代码,参考博客:滑动验证页面
com.alibaba.cloud
spring-cloud-starter-alibaba-nacos-config
在实际的应用中,存在着以下几种环境隔离的要求:
1、开发环境、测试环境、准生产环境和生产环境需要隔离
2、不同项目需要隔离
3、同一项目,不同的模块需要隔离
可以通过三种方式来进行配置隔离:Nacos的服务器、namespace命名空间、group分组,在bootstrap.yml文件中可以通过配置Nacos的server-addr、namespace和group来区分不同的配置信息。
如果我们要搭建集群的话,那么肯定是不能用内嵌的数据库,不然数据无法共享。所以,集群搭建的时候我们需要将Nacos对接Mysql进行数据存储。
集群模式跟我们平时进行扩容是一样的,可以通过Nginx转发到多个节点,最前面挂一个域名即可,如下图:
通常如果我们只是为了体验的话,直接在本地起动3个实例就可以了,没必要真的去搞三台服务器,下面我们就以在本地的方式来搭建集群。 将Nacos的解压包复制分成3份,分别是:
nacos
nacos1
nacos2
进入nacos的conf目录,编辑application.properties文件,增加数据库配置
# 指定数据源为Mysql
spring.datasource.platform=mysql
# 数据库实例数量
db.num=1
db.url.0=jdbc:mysql://localhost:3306/nacos?characterEncoding=utf8&connectTimeout=1000&socketTimeout=3000&autoReconnect=true
db.user=root
db.password=123456
复制代码同样的步骤进入nacos1和nacos2操作一遍,唯一需要修改的就是application.properties文件中的server.port,默认nacos的server.port=8848,
我们在本地启动三个实例,那么端口肯定会冲突,所以其他2个实例的端口我们需要进行修改,比如nacos1修改成8847,nacos2修改成8846。
数据库配置信息好了后,我们需要将对应的数据库和表进行初始化,数据库脚本在conf目录下的nacos-mysql.sql中,执行即可。
最后一步需要配置一份集群节点信息,配置文件在conf目录下的cluster.conf.example文件,我们进行重命名成cluster.conf。 然后编辑cluster.conf文件,增加3个节点的信息,格式为IP:PORT,三个目录都一致即可。
127.0.0.1:8848
127.0.0.1:8847
127.0.0.1:8846
启动的话直接到bin目录下,执行./startup.sh就可以了,默认就是集群模式,不需要加任何参数。
上面的集群,虽然可用, 但仍不是真正的集群, 我们一般不会这么用。nacos集群的使用一般有4种方式:
http://ip1:port/openAPI 直连ip模式,不同的节点,则需要修改ip才可以使用。
http://VIP:port/openAPI VIP模式高可用,客户端vip即可,VIP下面挂server真实ip,部署比较麻烦,需要部署vip(keepalive)。
http://nacos.com:port/openAPI 域名模式,可读性好,而且换ip方便,在host文件配置本地域名即可。
http://反向代理:port/openAPI 反向代理模式
这里介绍一下反向代理模式。
关于Nginx的安装和配置,本文就不进行讲解了,不会的可以自己去尝试下,反向代理模式 核心配置如下:
upstream nacos_server {
server 127.0.0.1:8848;
server 127.0.0.1:8847;
server 127.0.0.1:8846;
}
server {
listen 8648;
server_name localhost;
#charset koi8-r;
#access_log logs/host.access.log main;
location / {
proxy_pass http://nacos_server;
index index.html index.htm;
}
}
整体来说,nacos的集群搭建方式还是挺简单的,没什么特别要注意的,最好是能通过域名的方式来进行访问,另外数据库这块如果上生产环境,也需要考虑高可用问题,至少也得有个主从。
8648 的nginx 提供的 nacos 服务接口,可以自定义。 我们访问
http://localhost:8648/nacos/#/clusterManagement?dataId=&group=&appName=&namespace=&serverId=
,就可以看到:
我们可以简单测试一下,杀掉 一个的 nacos ,看服务是否正常。 后面,我们对微服务提供nacos服务的时候,只要配置这个nginx 端口就好了!!
Nacos官方文档:Open API 指南
读取Nacos配置中心配置信息并修改上传或删除_不爱吃酸奶的博客-CSDN博客_nacos配置文件修改
Yaml yaml = new Yaml();
Map map= yaml.load(confStr); confStr为字符串
Yaml yaml = new Yaml();
confStr = yaml.dumpAsMap(confMap); confMap为map
代码:
@FeignClient(contextId = "remoteNacosService",url = "http://192.168.79.102:8848",name = "remoteNacosService")
public interface RemoteNacosService {
@PostMapping(value = "/nacos/v1/cs/configs")
boolean setNacosConfig(@RequestParam(value = "dataId") String dataId,@RequestParam(value = "group") String group,@RequestParam(value = "content") String content,@RequestParam(value = "type") String type);
@GetMapping(value = "/nacos/v1/cs/configs")
String getNacosConfig(@RequestParam(value = "dataId") String dataId,@RequestParam(value = "group") String group);
}
@RestController
@RequestMapping("/nacos")
@Slf4j
@CrossOrigin
@Api(tags = "系统初始化时,选择船云系统")
public class SetNacosSystemTypeController {
@Autowired
RemoteNacosService remoteNacosService;
@PostMapping()
public ResponseResult setNacosSystemType(@RequestParam String type,
@RequestParam String imo,
@RequestParam String cloudIp){
//获取配置
String nacosConfig = remoteNacosService.getNacosConfig("application-dev.yml", "DEFAULT_GROUP");
Yaml yaml = new Yaml();
Map map = yaml.load(nacosConfig);
//已存在system配置就删除
if (ObjectUtil.isNotEmpty(map.get("system"))){
map.remove("system");
}
if (type.equals("1")){ //1是船
LinkedHashMap config = new LinkedHashMap<>();
config.put("systemType","shipSystem");
config.put("shipImo",imo);
config.put("cloudIp",cloudIp);
map.put("system",config);
}else { //2是云
LinkedHashMap config = new LinkedHashMap<>();
config.put("systemType","dataCenter");
config.put("shipImo","dataCenter");
map.put("system",config);
}
Yaml yamlNew = new Yaml();
String data = yamlNew.dumpAsMap(map);
remoteNacosService.setNacosConfig("application-dev.yml","DEFAULT_GROUP",data,ConfigType.YAML.getType());
return ResponseResult.success("设置后的nacos配置文件:"+data);
}
@GetMapping
public ResponseResult nacos(){
//获取配置
String nacosConfig = remoteNacosService.getNacosConfig("application-dev.yml", "DEFAULT_GROUP");
Yaml yaml = new Yaml();
Map map = yaml.load(nacosConfig);
Map systemMap = (Map) map.get("system");
String systemType = systemMap.get("systemType");
//船端表示1.云端表示2,没有配置表示0
if (ObjectUtil.isEmpty(map.get("system"))){
return ResponseResult.success(0);
}else {
if (systemType.equals("dataCenter")) {
return ResponseResult.success(2);
} else {
return ResponseResult.success(1);
}
}
}
}