概述
通过本篇文章你可以了解到:
- Spring Cloud 微服务解决方案
- Spring Boot 整合 Dubbo
- Nacos 注册中心的使用
- 微服务之间调用
Spring Cloud
Spring Cloud 通常分为 Spring Cloud Netflix 和 Spring Cloud Alibaba 两套微服务解决方案。
Spring Cloud Netflix 是一个基于 Spring Cloud 的微服务架构解决方案,利用了多个 Netflix 开源项目来提供关键的微服务功能。其技术体系主要包括以下几个核心组件:
-
Eureka:服务注册与发现组件。服务提供者将自身注册到 Eureka Server,服务消费者可以从 Eureka Server 查询服务实例。Eureka 提供了高可用的服务注册中心。
-
Ribbon:客户端负载均衡组件。Ribbon 允许客户端根据配置的负载均衡策略选择合适的服务实例,支持多种负载均衡策略,如轮询、随机等。
-
Hystrix:熔断器组件。Hystrix 通过监控服务调用并在出现故障时快速失败,从而防止故障蔓延到整个系统。它提供了断路器模式、降级和熔断机制来增强系统的稳定性和可靠性。
-
Zuul:API 网关。Zuul 作为微服务的入口,负责请求路由、负载均衡、安全、监控等功能。它可以根据请求路径转发到不同的微服务,简化客户端的调用逻辑。
-
Feign:声明式 HTTP 客户端。Feign 提供了一种简单的方式来定义 RESTful 服务的客户端接口,通过注解来简化 HTTP 调用。
-
Archaius:配置管理组件。Archaius 提供了动态配置管理功能,可以在运行时更新配置而无需重启应用。
-
Turbine:监控组件。Turbine 聚合多个 Hystrix 的监控信息,方便进行集中监控和分析,适合用于微服务的健康检查和性能监控。
Spring Cloud Netflix 的这些组件结合起来,提供了一个完整的微服务架构解决方案,帮助开发者快速构建和管理微服务应用。
Spring Cloud Alibaba 是一套基于 Spring Cloud 的微服务解决方案,整合了阿里巴巴的一系列中间件和服务。其技术体系主要包括以下核心组件:
-
Nacos:服务发现与配置管理。Nacos 提供了服务注册、服务发现、动态配置和管理功能,支持 DNS 和基于 HTTP 的服务发现。它还具有分布式配置管理的能力,可以通过 Nacos 控制中心动态更新配置。
-
Sentinel:流量控制与熔断。Sentinel 提供了流量控制、熔断和降级功能,帮助开发者在高并发场景下保护系统稳定。它支持自定义规则和实时监控,可以通过控制台查看流量和异常数据。
-
Dubbo:高性能 RPC 框架。Dubbo 适用于微服务间的远程调用,支持多种协议(如 HTTP、Dubbo、REST)和序列化方式,提供服务治理、负载均衡和容错能力。
-
RocketMQ:分布式消息中间件。RocketMQ 提供可靠的消息传递和异步处理能力,适合处理高并发场景。它支持消息队列、发布/订阅模式以及事务消息。
-
Seata:分布式事务解决方案。Seata 提供了高性能的分布式事务管理,支持 TCC、AT、SAGA 等事务模式,帮助微服务在分布式环境下实现一致性。
-
Spring Cloud Gateway:API 网关。Spring Cloud Gateway 是一个提供动态路由、负载均衡、安全、监控等功能的 API 网关,支持过滤器和负载均衡策略。
-
Alibaba Cloud Integration:与阿里云服务的集成,包括云数据库、云存储、负载均衡等。通过与阿里云服务的无缝集成,开发者可以更加便捷地构建和管理微服务应用。
Spring Cloud Alibaba 通过整合这些组件,提供了丰富的功能和灵活的配置,能够满足各种场景下的微服务需求,尤其适合中国市场和阿里云生态。
为什么要换 Spring Cloud Alibaba
国内大部分公司选择切换到 Spring Cloud Alibaba,主要出于以下几个原因:
-
本地化支持:Spring Cloud Alibaba 针对中国市场进行了优化,能更好地满足本地开发者的需求,提供更贴合国内业务场景的解决方案。
-
与阿里云的深度集成:Spring Cloud Alibaba 与阿里云服务无缝集成,方便企业在构建微服务时使用阿里云的基础设施和服务,降低了运维复杂度。
-
丰富的功能组件:Spring Cloud Alibaba 提供了丰富的中间件组件(如 Nacos、Sentinel、Dubbo 等),可以解决微服务架构中常见的挑战,如服务治理、流量控制、分布式事务等,极大提高了开发效率。
-
稳定性和可靠性:阿里巴巴在大规模分布式系统的实际应用中积累了丰富的经验,Spring Cloud Alibaba 方案具有较高的稳定性和可靠性,能够支持企业级应用的需求。
-
社区和技术支持:Spring Cloud Alibaba 拥有活跃的社区和强大的技术支持,企业在使用过程中能够获得及时的帮助和更新。
-
简化的开发流程:Spring Cloud Alibaba 的组件通常更易于集成和使用,降低了微服务开发的门槛,帮助团队更快地上手和交付项目。
综上所述,Spring Cloud Alibaba 在满足国内市场需求、提供技术支持以及增强微服务架构的可靠性和可维护性等方面具有明显优势,因此许多公司选择切换到这一解决方案。其实核心原因还是 Spring Cloud Netflix 宣布 Eureka 项目闭源还有其他几个组件不再维护了。
Quick Start
接下来我们快速来体验一把 Spring Cloud Alibaba 微服务应用程序开发。
创建一个Maven父工程
创建一个 Maven 聚合项目,父项目的 POM 文件设置打包类型为 pom。因为这是一个多模块的工程,所以需要依赖管理配置,编写 dependencyManagement 依赖项管理配置。
<?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>
<packaging>pom</packaging>
<modules>
<module>demo-consumer</module>
<module>demo-producer</module>
</modules>
<groupId>org.codeart</groupId>
<artifactId>spring-cloud-dubbo-first</artifactId>
<version>1.0-SNAPSHOT</version>
<properties>
<maven.compiler.source>11</maven.compiler.source>
<maven.compiler.target>11</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<spring-boot-version>2.3.12.RELEASE</spring-boot-version>
<spring-cloud-alibaba-version>2.2.7.RELEASE</spring-cloud-alibaba-version>
</properties>
<dependencyManagement>
<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>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>${spring-boot-version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
</project>
可以看到编写了 spring-boot-dependencies 版本依赖管理,因为子工程会继承父工程,这样的话需要以导入依赖的方式创建 Spring Boot 工程。
创建consumer、producer子工程
分别创建 demo-consumer 和 demo-producer 子工程,然后分别导入依赖项:
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
</dependencies>
因为父工程已经做了依赖版本管理,所以子工程中不需要手动指定依赖。
创建并导出interface和service模块
在下面我们需要创建各个微服务模块的服务实现和接口定义子模块。因为其他微服务可能会调用当前的模块,那么假如当前微服务导出了定义的接口名称,远程的微服务就不用编写重复代码了,而且接口契约也得到了约束。
创建完毕大概像这样的目录结构:
然后在 demo-consumer-service 模块中导入 demo-consumer-api 和 demo-producer-api 模块。一个是为了实现自身微服务的 API,一个是为了调用其他微服务的 API。
<dependencies>
<dependency>
<groupId>org.codeart</groupId>
<artifactId>demo-consumer-api</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.codeart</groupId>
<artifactId>demo-producer-api</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
</dependencies>
同理在 demo-producer-service 模块中导入 demo-producer-api 模块:
<dependencies>
<dependency>
<groupId>org.codeart</groupId>
<artifactId>demo-producer-api</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
</dependencies>
编写SpringBoot引导类
producer-service 模块引导类如下:
@SpringBootApplication
public class ProducerApp {
public static void main(String[] args) {
SpringApplication.run(ProducerApp.class, args);
}
}
编写一个简单的 controller:
package org.codeart.producer.controller;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class HelloController {
@GetMapping("/hello")
public ResponseEntity<String> hello() {
return ResponseEntity.ok("hello world");
}
}
application.yml:
server:
port: 8100
consumer-service 模块也如法炮制,端口定为 8000。
访问一下浏览器 http://localhost:8100/hello,显示如下:
说明我们已经成功把 producer-service 模块启动起来了。
启动Nacos注册中心
微服务已经搭建完毕了,但是还需要注册中心来发现服务。这里我们使用 Nacos 作为注册中心,且使用 Docker 完成部署。
docker-compose.yml:
version: '3.8'
services:
nacos:
image: nacos/nacos-server:v2.4.3
container_name: nacos-server
environment:
- PREFER_HOST_MODE=hostname
- MODE=standalone
ports:
- "8848:8848"
volumes:
- ./data:/home/nacos/data
- ./conf:/home/nacos/conf
- ./logs:/home/nacos/logs
volumes:
nacos-data:
输入 docker compose up -d 启动 Nacos。
可以看到已经成功启动 Nacos 了,下面我们需要将各个微服务注册到 Nacos。
在 producer-service 模块导入依赖:
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
修改 application.yml:
server:
port: 8100
spring:
application:
name: producer-service
cloud:
nacos:
discovery:
server-addr: http://localhost:8848
修改引导类,添加 @EnableDiscoveryClient 注解:
@EnableDiscoveryClient
@SpringBootApplication
public class ProducerApp {
public static void main(String[] args) {
SpringApplication.run(ProducerApp.class, args);
}
}
然后重启微服务,可以看到已经成功注册到了 Nacos:
consumer-service 微服务也同理。
简单测试一下Restful接口
在本小节中,我们简单做一下 Restful 接口调用测试。首先,在父级 pom.xml 文件中设置 spring-cloud-dependencies 版本依赖管理:
<spring-cloud-version>Hoxton.SR12</spring-cloud-version>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>${spring-cloud-version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
在 demo-producer-api 模块中导入 spring-cloud-starter-openfeign 以及 spring-boot-starter-web 依赖:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
在 demo-producer-api 模块中创建 HelloApi 接口:
package org.codeart.producer.api;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
@FeignClient(name = "producer-service")
public interface HelloApi {
@GetMapping("/hello")
ResponseEntity<String> hello();
}
dmeo-producer-service 模块中 HelloController 实现 HelloApi 接口:
@RestController
public class HelloController implements HelloApi {
public ResponseEntity<String> hello() {
return ResponseEntity.ok("hello world");
}
}
demo-consumer-service 模块中在引导类上添加 @EnableFeignClients 注解:
package org.codeart.consumer;
import org.apache.dubbo.config.spring.context.annotation.EnableDubbo;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.openfeign.EnableFeignClients;
@EnableDubbo
// 多模块要复用接口代码那么就需要手动指定扫描的路径
@EnableFeignClients(basePackages = "org.codeart")
@SpringBootApplication
public class ConsumerApp {
public static void main(String[] args) {
SpringApplication.run(ConsumerApp.class, args);
}
}
在 demo-consumer-service 模块编写一个简单的 Controller:
package org.codeart.consumer.controller;
import org.apache.dubbo.config.annotation.DubboReference;
import org.codeart.producer.api.HelloApi;
import org.codeart.producer.dubbo.HelloService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class HelloController {
@Autowired
private HelloApi helloApi;
@GetMapping("/hello")
public ResponseEntity<String> hello() {
System.out.println("consumer calls producer");
return helloApi.hello();
}
}
通过浏览器访问一下 /hello 接口看是否会得到响应。可以看到成功地返回了结果。
集成Dubbo微服务调用框架
还有最后一步,集成 Dubbo 这个 RPC 框架来实现微服务之间调用。
导入Dubbo依赖
在本例中我们使用的是 Dubbo 3.x,在父级 pom.xml 文件中设置版本依赖管理:
<dubbo.version>3.3.1</dubbo.version>
<dependencies>
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-bom</artifactId>
<version>${dubbo.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-dependencies-bom</artifactId>
<version>${dubbo.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
在 demo-producer-service 模块中导入 dubbo 相关依赖:
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-spring-boot-starter</artifactId>
</dependency>
在引导类 ProducerApp 上添加 @EnableDubbo 注解:
@EnableDubbo
@EnableFeignClients
@EnableDiscoveryClient
@SpringBootApplication
public class ProducerApp {
public static void main(String[] args) {
SpringApplication.run(ProducerApp.class, args);
}
}
修改 application.yml 配置文件:
dubbo:
application:
name: producer-service
registry:
address: nacos://localhost:8848
创建Dubbo服务接口
在 demo-producer-api 模块创建 HelloService 接口用于定义 Dubbo 的实现类需要实现的远程调用方法:
package org.codeart.producer.dubbo;
public interface HelloService {
String hello();
}
在 demo-producer-service 模块中编写它的实现类,并添加 @DubboService 注解:
package org.codeart.producer.dubbo;
import org.apache.dubbo.config.annotation.DubboService;
@DubboService
public class HelloServiceImpl implements HelloService {
@Override
public String hello() {
return "Hello World";
}
}
consumer端调用Dubbo接口
在 demo-consumer-service 模块中也需要导入 dubbo-spring-boot-starter 依赖。
修改一下上面的 consumer 端的 HelloController 代码:
package org.codeart.consumer.controller;
import org.apache.dubbo.config.annotation.DubboReference;
import org.codeart.producer.api.HelloApi;
import org.codeart.producer.dubbo.HelloService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class HelloController {
@DubboReference
private HelloService helloService;
@GetMapping("/hello-dubbo")
public ResponseEntity<String> helloDubbo() {
System.out.println("consumer calls producer by dubbo");
return ResponseEntity.ok(helloService.hello());
}
}
访问 consumer 端的 /hello-dubbo http 接口看是否会得到响应。
同时在 Nacos 控制台上可以看到注册上来的 HelloService 接口,因为 Dubbo 默认是注册的是接口而不是服务名称。