什么是服务发现和配置中心?
在SpringCloud的架构中,一个核心的组件就是服务发现、配置中心。它在整个系统项目中主要负责:
- 服务发现:其他微服务模块向服务注册中心进行注册,由于这些模块有可能增加、减少或重新部署。所以为了方便管理,在微服务的项目中引入了服务注册中心的概念。
- 负载均衡:有了这个服务注册中心,借助于
SpringCloud
提供的loadbalance
等负载均衡工具,能够容易的实现服务的负载均衡,以实现整个系统的高可用
- 去中心化与解耦:服务注册中心可以独立于项目进行部署,实现了松耦合;并且可以由
Nacos
配置中心的功能提高整个系统的可维护性和灵活性
Nacos是什么?
简单来说,Nacos
是服务发现和配置中心的一个实现。而诸如此类的,还有例如Eureka
、Consul
、Zookeeper
等。
而Nacos
是alibaba开源的,在国内经历了多次“双十一”、“618”活动的考验,稳定性也是比较可靠的。
它支持的服务包含:
- Kubernetes Service
- gRPC & Dubbo RPC Service
- Spring Cloud RESTful Service
功能的话比较啰嗦,详情可以查看官方文档
nacos单机部署实战
直接开始。首先,我们要知道,nacos
就是一个软件,独立于微服务项目而运行,而运行起来的nacos
就相当于服务端,我们微服务中的各个模块就相当于客户端。所以,要运行服务端必备的三大步骤:
下载
https://github.com/alibaba/nacos/releases 选择合适的版本进行下载
注意:版本一定要和项目中SpringCloud
、SpringCloudAlibaba
的版本相兼容,具体可以查看:
https://github.com/alibaba/spring-cloud-alibaba/wiki/版本说明
和
https://sca.aliyun.com/docs/2023/overview/version-explain/?spm=5176.29160081.0.0.74805a752XWBvK
在这里我使用的版本环境如下:
- 操作系统:Windows10 专业版
- IDE: IDEA-2024.2.4
- JDK:OracleJDK-17.0.12
- SpringCloud:2022.0.4
- SpringCloudAlibaba:2022.0.0.0
- Nacos:2.2.3
安装和运行
下载解压后,进入nacos/bin目录下,打开cmd命令提示符运行单机部署
startup.cmd -m standalone
|
显示这个界面就代表运行成功了

nacos
默认运行在8848端口,此时访问localhost:8848/nacos
如果能正常显示页面就代表部署服务成功了
有两个注意的点,是我在部署时遇到的坑,详细的可以去官网查看文档说明


代码实战
nacos运行起来之后,不要关闭cmd窗口否则会停止服务的运行。此时,我们就可以在我们的微服务项目中给每个模块添加服务发现了,具体的父工程的pom.xml
长这样:
<?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>love.mpc520</groupId> <artifactId>demo_cloud</artifactId> <version>1.0.0</version> <packaging>pom</packaging> <modules> <module>commons</module> <module>cloud-provider-pay8001</module> <module>cloud-consumer-order80</module> <module>cloud-nacos-config-3333</module> <module>cloud-sentinel-8899</module> </modules> <properties> <maven.compiler.source>17</maven.compiler.source> <maven.compiler.target>17</maven.compiler.target> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <hutool.version>5.8.22</hutool.version> <lombok.version>1.18.26</lombok.version> <druid.version>1.2.20</druid.version> <mybatis.springboot.version>3.0.2</mybatis.springboot.version> <mybatis-plus.version>3.5.4.1</mybatis-plus.version> <mariadb.version>3.3.2</mariadb.version> <swagger3.version>2.2.0</swagger3.version> <fastjson2.version>2.0.40</fastjson2.version> <spring.boot.test.version>3.1.5</spring.boot.test.version> <spring.boot.version>3.1.5</spring.boot.version> <spring.cloud.version>2022.0.4</spring.cloud.version> <spring.cloud.alibaba.version>2022.0.0.0</spring.cloud.alibaba.version> </properties>
<dependencyManagement> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>${spring.boot.version}</version> <type>pom</type> <scope>import</scope> </dependency> <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> <dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId> <version>${spring.cloud.alibaba.version}</version> </dependency> <dependency> <groupId>com.baomidou</groupId> <artifactId>mybatis-plus-boot-starter</artifactId> <version>${mybatis-plus.version}</version> </dependency> <dependency> <groupId>org.mariadb.jdbc</groupId> <artifactId>mariadb-java-client</artifactId> <version>${mariadb.version}</version> </dependency> <dependency> <groupId>com.alibaba</groupId> <artifactId>druid-spring-boot-starter</artifactId> <version>${druid.version}</version> </dependency> <dependency> <groupId>com.alibaba.fastjson2</groupId> <artifactId>fastjson2</artifactId> <version>${fastjson2.version}</version> </dependency> <dependency> <groupId>org.springdoc</groupId> <artifactId>springdoc-openapi-starter-webmvc-ui</artifactId> <version>${swagger3.version}</version> </dependency> <dependency> <groupId>cn.hutool</groupId> <artifactId>hutool-all</artifactId> <version>${hutool.version}</version> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <version>${lombok.version}</version> <optional>true</optional> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <version>${spring.boot.test.version}</version> <scope>test</scope> </dependency> </dependencies> </dependencyManagement>
</project>
|
微服务模块的pom.xml
需要引入
<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>
|
其次是微服务模块的application.yml
:
spring: application: name: service-test cloud: nacos: server-addr: localhost:8848 server: address: 0.0.0.0 port: 8899
|
在主启动类上多添加一个@EnableDiscoveryClient
注解,代表开启服务发现的功能,允许nacos
服务端发现当前微服务模块
其次,再构建一个用于测试的controller
:
@RestController @Slf4j public class OrderController { @GetMapping("/test") public String addOrder(){ return "OK"; } }
|
最后,启动项目,能够在nacos
的web界面中看到类似于这个样子,有服务出现就算成功。

在这里还有一个实战中遇到的坑
- 在系统多网卡环境下,有可能nacos会运行在虚拟网卡上,发现的服务也会发现到虚拟网卡的IP上。我具体的解决方案是:因为实在折腾不动这个,就直接在系统中禁用其他所有虚拟网卡。问题完美解决
服务A通过nacos调用服务B
要想实现通过服务名去调用某个服务,这时可以引入负载均衡组件loadbalance
,如果将刚才我们写好的测试controller的这个模块比喻成一个服务的提供者,那么接下来要做的就是就是造出来一个消费者。在新模块的 pom.xml
中添加(在之前的依赖基础上添加)
<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-loadbalancer</artifactId> </dependency>
|
其次,我们使用Spring提供的RestTemplate
这个API进行测试,新建一个config的配置类
@Configuration public class RestTemplateConfig { @Bean @LoadBalanced public RestTemplate restTemplate(){ return new RestTemplate(); } }
|
由于需要调用另一个服务,我们可以使用注入的方式,在application.yml中显式声明一下要调用的目标服务的服务名
server: port: 81 address: 0.0.0.0 spring: application: name: service-consume cloud: nacos: discovery: server-addr: localhost:8848 main: allow-bean-definition-overriding: true service-target: servicename: service-test
|
构建一个用于测试的controller:
@RestController @Slf4j public class OrderController { private final RestTemplate restTemplate; private final LoadBalancerClient loadBalancerClient;
@Value("${service-target.servicename}") private String serviceName;
public OrderController(RestTemplate restTemplate, LoadBalancerClient loadBalancerClient) { this.restTemplate = restTemplate; this.loadBalancerClient = loadBalancerClient; }
@GetMapping("/test") public String addOrder(PayDTO payDTO){ ServiceInstance instance = loadBalancerClient.choose(serviceName); String url=instance.getUri()+"/test"; System.out.println("-----------------------"+url+"-------------------------"); return restTemplate.getForObject(url, String.class); }
}
|
最后,同时启动两个微服务,访问消费者这一端的/test
路径(端口81),当返回OK
时,就代表大功告成。
另外,为了测试loadbalance
负载均衡功能生效,我们可以复制一个生产者的模块,并在复制的模块的启动参数上加上-Dserver.port=<其他闲置端口>
或直接修改application.yml
中server.port
。同时启动两个服务的提供者和服务消费者。
多次访问,查看控制台输出的url
的端口变化。
负载均衡
负载均衡是一个很宽泛的概念,并不局限于微服务中。其他类似的:
- Nginx代理多个相同服务时,为了使每个服务都能够正常运行,往往通过一定的策略使其分散在每个服务上
- 还有比较形象的例子,当你想喝蜜雪冰城时,楼下的店爆满;而街对面就有另一家,这时往往会选择去到街对面的店
所谓均衡,并不是一定要均匀地使请求散步在每个服务上。有时候可能蜜雪冰城的A店比较大,能够承载更多的客户。B店比较小,承载的客户量较少。这时如果均匀地分布的话,对于B店来说就难以承载。
负载均衡策略
常见的负载均衡策略有
- 轮询:这也是loadbalance默认的负载均衡策略,就是将请求挨个散步在每个服务上
- 权重:顾名思义,权重较大的被分配得到的请求数就会更多
- ip哈希:根据客户端和服务端的ip,经过哈希算法得到一个字符串,它会缓存在服务端上,对于相同的ip,不管访问多少次都是同一个服务的实例
其他地负载均衡算法还有很多,这里只介绍这几种。