一、前言
nacos
是第二代微服务SpringCloudAlibaba
开源的一款注册中心和分布式配置中心组件,其功能上为第一代微服务SpringCloud
中Eureka
和Config
的结合体。简而言之,Nacos
可以实现分布式服务注册与发现和分布式配置中心功能。
二、完整项目示例
在搭建项目实战之前,先贴一下本篇博客的项目源码完整目录 :
三、Nacos与Eureka区别
上面有提到过,阿里巴巴开源的nacos
组件可替代eureka
和config
,那么nacos
与eureka
相比,有哪些不同与相同呢?说到注册中心nacos
和eureka
之间的区别,不得不谈谈基于微服务与分布式领域中的CAP
定律和BASE
理论, , 大家在想了解nacos
与eureka
这两个注册中心之间的区别的时候,建议先看看这篇文章,那么我这里就先简单介绍下CAP
定律:
1. CAP理论
- C(一致性): 各个节点数据必须统一保持一致;
- A(可用性) : 保证服务的高可用 必须返回响应结果
- P(分区容错) : 网络分区可能会造成脑裂问题,部分server与集群其他节点失去联系 无法组成一个群体;
不能三者兼顾 ,只能在三者之间实现取舍 : CP、AP、CA
2. 区别
-
nacos
最大的不同就是它同时支持AP
和CP
模式,而Eureka
只支持AP
。 -
nacos
既可以做为注册中心又可以担任配置中心,做配置中心时nacos
客户端与nacos
服务端保持长链接,服务端会有监听,一旦nacos
服务端有配置信息更新变化,立马推送到客户端,然后更新到客户端的JVM
内存中。 -
nacos
在集群环境下,一旦leader
宕机了,会重新选出一个leader
,其他节点都为Follower
,期间不影响服务之间互相调用、不影响服务注册、不影响服务正常启动读取配置文件。
四、Nacos-discovery默认支持Ribbon负载均衡
为了保持好奇心,特地分析了一下SpringCloud
常见的注册中心组件: Eureka
、Consul
、nacos
等,默认都是集成并开启了Ribbon负载均衡
,本节为nacos
主题,我们分析下nacos
的服务发现组件,不难看出,他也是默认集成并且支持ribbon
负载均衡的。
五、环境要求
注意: 在后续的文章中,我们全部使用以下环境来搭建学习第二代服务教程时的环境。
1. 版本说明
- spring-cloud-dependencies.version: Spring Cloud Hoxton.SR3
- spring-cloud.version: 2.2.2.RELEASE
- spring-cloud-alibaba-version: 2.2.0.RELEASE
- spring-boot-version: 2.2.5.RELEASE
- nacos-version: 1.1.4
2. 版本要求
2. Nacos的安装
nacos
的安装和部署、启动都可参照nacos
官方文档,Windwos
平台直接下载解压,双击bin
目录下面的startup.cmd
启动即可。
初始访问URL: 127.0.0.1:8848/nacos/index.html
六、环境搭建
以订单(Order
)调用商品(Product
)为例,分别创建父工程springcloud-alibaba-nacos
、子工程springcloud-alibaba-provider
、springcloud-alibaba-consumer
1. 搭建springcloud-alibaba-nacos
- pom.xml
<modules>
<module>springcloud-alibaba-provider</module>
<module>springcloud-alibaba-consumer</module>
</modules>
<packaging>pom</packaging>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>2.2.5.RELEASE</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<!--Spring Cloud -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Hoxton.SR3</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<!-- Spring Cloud Alibaba-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-alibaba-dependencies</artifactId>
<version>2.2.1.RELEASE</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
2. 搭建springcloud-alibaba-provider服务提供者
- pom.xml
<parent>
<groupId>com.thinkingcao</groupId>
<artifactId>springcloud-alibaba-nacos</artifactId>
<version>1.0-SNAPSHOT</version>
<relativePath>/</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
<name>springcloud-alibaba-provider</name>
<artifactId>springcloud-alibaba-provider</artifactId>
<dependencies>
<!-- web组件 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<exclusions>
<!--排除tomcat依赖 -->
<exclusion>
<artifactId>spring-boot-starter-tomcat</artifactId>
<groupId>org.springframework.boot</groupId>
</exclusion>
</exclusions>
</dependency>
<!--undertow容器 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-undertow</artifactId>
</dependency>
<!--nacos服务注册与发现 -->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<!-- lombok插件 -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
- application.yml
server:
port: 8715
spring:
cloud:
nacos:
discovery:
server-addr: 127.0.0.1:8848
enabled: true
application:
name: nacos-product
- 启动类AppProvider
package com.thinkingcao.nacos;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
/**
* @desc:
* @author: cao_wencao
* @date: 2020-04-16 22:54
*/
@SpringBootApplication
public class AppProvider {
public static void main(String[] args) {
SpringApplication.run(AppProvider.class);
}
}
- Product提供接口
package com.thinkingcao.nacos.api;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
/**
* @desc: 生产者——>商品服务
* @author: cao_wencao
* @date: 2020-04-17 20:58
*/
@Slf4j
@RestController
public class ProductController {
@Value("${server.port}")
private String port;
@GetMapping("/getProductMsg")
public String getProductMsg() {
log.info("开始调用商品服务信息啦");
return "我是商品服务, 调用商品服务接口成功了===>> : " + port;
}
}
2. 搭建springcloud-alibaba-consumer服务消费者
- pom.xml
<parent>
<artifactId>springcloud-alibaba-nacos</artifactId>
<groupId>com.thinkingcao</groupId>
<version>1.0-SNAPSHOT</version>
<relativePath>/</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>springcloud-alibaba-consumer</artifactId>
<dependencies>
<!--nacos服务注册与发现 -->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<!-- web组件 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<exclusions>
<!--排除tomcat依赖 -->
<exclusion>
<artifactId>spring-boot-starter-tomcat</artifactId>
<groupId>org.springframework.boot</groupId>
</exclusion>
</exclusions>
</dependency>
<!--undertow容器 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-undertow</artifactId>
</dependency>
<!-- lombok插件 -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
- application.yml
server:
port: 8710
spring:
cloud:
nacos:
discovery:
server-addr: 127.0.0.1:8848
enabled: true
application:
name: nacos-order
- 启动类AppConsumer
package com.thinkingcao.nacos;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
/**
* @desc:
* @author: cao_wencao
* @date: 2020-04-16 22:55
*/
@SpringBootApplication
public class AppConsumer {
public static void main(String[] args) {
SpringApplication.run(AppConsumer.class);
}
}
- Order消费商品的接口(3种方式)
package com.thinkingcao.nacos.api;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.discovery.DiscoveryClient;
import org.springframework.cloud.client.loadbalancer.LoadBalancerClient;
import org.springframework.context.annotation.Bean;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;
import java.util.List;
/**
* @desc: 消费者——>订单服务
* @author: cao_wencao
* @date: 2020-04-17 20:58
*/
@Slf4j
@RestController
public class OrderController {
@Autowired(required = false)
private RestTemplate restTemplate;
@Autowired
private LoadBalancerClient loadBalancerClient;
@Autowired
private DiscoveryClient discoveryClient;
//方式一 :通过IP地址
@GetMapping("/getProductMsg")
public String getProductMsg() {
String url = "http://127.0.0.1:8715/getProductMsg";
String response = restTemplate.getForObject(url, String.class);
log.info("response==>: {}",response);
return response;
}
//方式二 :通过服务名(这种方式不可行,在Eureka中可以)
@GetMapping("/getProductMsg2")
public String getProductMsg2() {
String url = "http://nacos-product/getProductMsg";
String response = restTemplate.getForObject(url, String.class);
log.info("response==>: {}",response);
return response;
}
//方式三 :通过LoadBalancerClient获取服务调用地址进行调用
@GetMapping("/getProductMsg3")
public String getProductMsg3() {
//discoveryClient.getInstances()
ServiceInstance serviceInstance = loadBalancerClient.choose("nacos-product");
String url = String.format("http://%s:%s", serviceInstance.getHost(), serviceInstance.getPort() + "/getProductMsg");
String response = restTemplate.getForObject(url, String.class);
log.info("response结果==>>>: {}", response);
return response;
}
//方式四 :通过LoadBalancerClient获取服务调用地址进行调用
@GetMapping("/getProductMsg4")
public String getProductMsg4() {
List<ServiceInstance> serviceInstanceList = discoveryClient.getInstances("nacos-product");
//任意选择一个,实现本地RPC调用
ServiceInstance serviceInstance = serviceInstanceList.get(0);
String response = restTemplate.getForObject(serviceInstance.getUri()+ "/getProductMsg", String.class);
log.info("response结果==>>>: {}", response);
return response;
}
@Bean
public RestTemplate restTemplate() {
return new RestTemplate();
}
}
七、基于RestTemplate测试
使用nacos做注册中心时,服务消费有两大类,一类是RestTemplate,另一类是Feign客户端,其中RestTemplate具体实现服务消费有3种方式
- 启动项目,先观察nacos控制台,发现服务都注册上去了
- 通过IP地址调用: http://127.0.0.1:8710/getProductMsg
- 通过LoadBalancerClient获取服务调用地址进行调用: http://127.0.0.1:8710/getProductMsg3
- 通过DiscoveryClient获取服务调用地址进行调用: http://127.0.0.1:8710/getProductMsg4