一、生产者和消费者的定义
在 Spring Cloud 中,术语 “生产者” 和 “消费者” 用于描述微服务架构中的两种基本角色。
角色 | 定义 |
生产者 Provider |
生产者是提供具体服务或功能的模块。它将业务逻辑封装成服务,供其他模块调用。生产者向服务注册中心注册自己提供的服务,使其他模块可以通过服务注册中心发现并调用这些服务。 |
消费者 Consumer |
消费者是通过调用生产者提供的服务来完成特定功能的模块。消费者从服务注册中心获取生产者的信息,然后调用生产者的服务接口。消费者在运行时动态发现并连接到可用的生产者。 |
示例:一个在线商城系统中,订单服务可以被视为生产者,提供创建订单、查询订单等服务。购物车服务可以是一个消费者,它调用订单服务的创建订单服务来完成用户购物车中商品的下单。
简单在线商城购物流程服务结构图:
稍微复杂一点的在线商城系统的购物流程服务结构图:
二、生产者和消费者代码演示
1、创建父工程
在构建微服务项目时,首先需要创建一个父工程,以便统一管理依赖版本和项目属性。
我们来新建项目 SpringCloudTest。
父工程不需要太多引入,勾选 spring web 这一项即可。
删除多余内容:
pom.xml 新增 packaging 标签
2、创建服务注册中心
微服务架构中,服务注册中心是整个系统的核心,负责服务的注册与发现。使用 Spring Cloud 的Eureka 组件,可以轻松搭建一个高可用的服务注册中心。在创建 Eureka 注册中心时,需要在项目中引入相应的依赖,并通过注解标记该服务为 Eureka Server。
新建模块 euraka-server。
勾选 eureka server。
创建完成后,我们修改 eureka-server 的 pom.xml,使它的 标签里的内容对应父工程。
父工程:
eureka-server 的 pom.xml:
com.example
SpringCloudTest
0.0.1-SNAPSHOT
同时,我们需要在父工程的 pom.xml 里添加子模块的依赖关系。
eureka-server
创建 eureka-server 的 application.properties 配置文件重命名为 application.yml。
内容修改为:
#服务端口
server:
port: 8081
#服务名称
spring:
application:
name: eureka-server
#eureka地址
eureka:
client:
service-url:
defaultZone: http://127.0.0.1:8081/eureka/
register-with-eureka: false
fetch-registry: false
启动类上添加 @EnableEurekaServer 注解。
测试启动 eurake 服务,选中 EurekaServerApplication 右键运行,启动 eurake-server 服务。确认控制台正常加载,在控制台上能够看到服务实例的信息,表示 Eureka 注册中心已成功启动。
访问我们设置的 eurake-server 服务的地址:localhost:8081,可以看到 Eureka 服务界面。
3、创建生产者
生产者是负责提供服务的模块,它将特定的业务逻辑封装成服务,供其他模块调用。在创建生产者时,需要定义服务接口,并使用 @RestController 或 @Service 等注解将业务逻辑发布为 RESTful API 或 RPC 服务。生产者将服务注册到 Eureka 注册中心,使消费者能够发现并调用这些服务。
① 创建生产者模块
父工程下创建模块 eureka-provider:
勾选 Eureka Discovery Client,用于注册到 eureka-server 服务注册中心。
类似于 eureka-server 服务注册中心的创建,我们需要在 pom.xml 里引入父类标签,可以参考上面eureka-server 服务注册中心创建时的写法,内容是一样的,都指向父工程。
com.example
SpringCloudTest
0.0.1-SNAPSHOT
同理,为父工程增加新的子模块:eureka-provider:
eureka-server
eureka-provider
在主类上使用 @EnableEurekaClient 注解,注册该服务到 Eureka Server。
② 添加消费者配置
将 eureka-provider 的 application.properties 配置文件重命名为 application.yml。
配置 eureka-provider 的相关属性,如服务端口、注册中心地址等。
# 服务器端口
server:
port: 8082
# 配置发布服务地址
spring:
application:
name: eureka-provider
eureka:
client:
service-url:
defaultZone: http://localhost:8081/eureka
③ 编写生产者代码
生产者模块需要将特定的业务逻辑封装成服务,供其他模块调用。所以我们需要创建实体类并且写一些供我们调用的接口。
这里是创建 eureka-provider 模块时勾选 Eureka Discovery Client 自带的代码,包含两个控制器接口类和一个实体类,我们直接拿来用就可以。
实体类 User 如下:
控制器基类 BaseController:
④ 测试生产者服务可用
接着,我们运行应用,根据我们刚刚的配置,运行地址是 localhost:8082,我们看看访问这个地址能否显示 Hello World 的主页。如果可以访问,说明项目运行成功,我们再来访问 Eureka Server 的控制台,确保 eureka-provider 成功注册到服务注册中心。
4、创建消费者
消费者是通过调用生产者提供的服务来完成特定功能的模块。在创建消费者时,需要引入 Eureka 客户端依赖,以便消费者能够从服务注册中心发现并调用生产者提供的服务。通过使用 @LoadBalanced 注解配置 RestTemplate,可以实现基于服务名称的负载均衡。
① 创建消费者模块
父工程下创建模块 eureka-consumer:
勾选 Eureka Discovery Client,用于注册到 eureka-server 服务注册中心。
类似于 eureka-server 服务注册中心的创建,我们需要在 pom.xml 里引入父类标签,可以参考上面eureka-server 服务注册中心创建时的写法,内容是一样的,都指向父工程。
com.example
SpringCloudTest
0.0.1-SNAPSHOT
同理,为父工程增加新的子模块:eureka-consumer:
eureka-server
eureka-provider
eureka-consumer
在主类上使用 @EnableEurekaClient 注解,注册该服务到 Eureka Server。
② 添加消费者配置
将 eureka-consumer 的 application.properties 配置文件重命名为 application.yml。
配置 eureka-consumer 的相关属性,如服务端口、注册中心地址等。
# 服务器端口
server:
port: 8083
# 配置发布服务地址
spring:
application:
name: eureka-consumer
eureka:
client:
service-url:
defaultZone: http://localhost:8081/eureka
③ 编辑消费者代码
使用 RestTemplate 或 Feign 等方式调用生产者提供的服务,确保服务的负载均衡。
这里我们使用的是 RestTemplate。
启动类里添加:
@Bean // 交给spring容器管理
@LoadBalanced // 支持使用服务名称发现服务进行调用,且支持负载
public RestTemplate getRestTemplate() {
return new RestTemplate();
}
创建调用生产者服务的接口类 UserController。
UserController:
package com.example.eurekaconsumer.demos.web;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;
import java.util.List;
@RestController
public class UserController {
@Autowired
private RestTemplate restTemplate;
@RequestMapping("/showUser")
@ResponseBody
public User showUser() {
String baseUrl = "http://" + "eureka-provider" + "/user";
User userInfo = restTemplate.getForObject(baseUrl, User.class);
return userInfo;
}
}
我们这里的接口调用地址是 /showUser,调用的是生产者服务的 /user 接口。
④ 测试消费者服务可用
接着,我们运行应用,根据我们刚刚的配置,运行地址是 localhost:8083,我们看看访问这个地址能否显示 Hello World 的主页。如果可以访问,说明项目运行成功,我们再来访问 Eureka Server 的控制台,确保 eureka-consumer 成功注册到服务注册中心。
5、消费者调用生产者服务
生产者和消费者模块准备完成之后,我们就来演示消费者调用生产者服务的过程。
首先,我们启动项目,按照 eureka-server、eureka-provier 、eureka-consumer 的顺序启动。
然后,我们来直接调用生产者 eureka-provier 的用户服务接口 /user。访问地址:localhost:8082/user,可以看到成功返回给我们一个 User 对象。
接着,我们来通过消费者 eureka-consumer 来调用生产者 eureka-provier 的用户服务接口,访问地址:localhost:8083/showUser,可以看到也是成功返回给我们一个 User 对象。这里的对象就是eureka-provier 的用户服务接口 /user 返回给我们的。
以上就是消费者服务调用生产者服务的简单示例。
三、实现服务调用的负载均衡
在上面代码的演示中,我们已经用到了负载均衡的措施,这一节,我就再系统描述一下 Spring Cloud 中基于 RestTemplate 实现负载均衡的具体步骤。
负载均衡是一种将网络或计算负载分配到多个服务器或网络路径的技术,以实现资源的均匀利用和提高系统的可用性。在微服务架构中,负载均衡起到了关键的作用,确保请求能够被均匀分发到多个服务实例上,避免某个实例过载而影响系统性能。
在 Spring Cloud 中,RestTemplate 通过整合 Ribbon 负载均衡器,可以实现在服务消费者端的负载均衡。以下是使用 RestTemplate 进行负载均衡的基本用法:
1、引入Ribbon依赖
在消费者项目的 pom.xml 中引入 Spring Cloud Netflix Ribbon 依赖:
org.springframework.cloud
spring-cloud-starter-netflix-ribbon
2、创建RestTemplate
在消费者的配置类或者项目启动类中创建一个带有 @LoadBalanced 注解的 RestTemplate Bean,该注解启用了 Ribbon 的负载均衡功能:
启动类里添加方法:
@Bean // 交给spring容器管理
@LoadBalanced // 支持使用服务名称发现服务进行调用,且支持负载
public RestTemplate getRestTemplate() {
return new RestTemplate();
}
配置类例子:
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.client.RestTemplate;
@Configuration
public class AppConfig {
@Bean
@LoadBalanced
public RestTemplate restTemplate() {
return new RestTemplate();
}
}
3. 使用RestTemplate进行服务调用
在消费者的服务类中使用 RestTemplate 进行服务调用。使用服务名称而不是硬编码的 URL,Ribbon 会根据服务名称自动选择可用的实例:
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;
@Service
public class ConsumerService {
private final RestTemplate restTemplate;
@Autowired
public ConsumerService(RestTemplate restTemplate) {
this.restTemplate = restTemplate;
}
public String consumeService() {
// 使用服务名称而不是具体的URL
String serviceUrl = "http://producer-service/produce";
return restTemplate.getForObject(serviceUrl, String.class);
}
}
上述代码中,http://producer-service 是服务提供者的服务名称,而不是具体的服务实例的URL。Ribbon 会根据负载均衡策略选择一个可用的服务实例进行调用。
通过这种方式,RestTemplate 在消费者端实现了对服务提供者的负载均衡。你可以根据具体的业务需求选择不同的负载均衡策略,例如轮询、随机、权重等。
四、生产者和消费者知识总结
生产者(Provider):
生产者是提供具体服务或功能的模块,将业务逻辑封装成服务,供其他模块调用。
要点 | 内容 |
服务注册 | 生产者将自身注册到服务注册中心,使其他模块能够发现并调用这些服务。 |
服务标识 | 生产者有唯一的标识,通常是服务的名称,通过服务注册中心进行标识和发现。 |
服务提供 | 实现具体的服务接口,可以是RESTful API或RPC接口,对外提供服务功能。 |
负载均衡 | 通过服务注册中心和负载均衡器,使请求能够均匀分布到多个生产者实例,提高性能和可用性。 |
消费者(Consumer):
消费者是通过调用生产者提供的服务来完成特定功能的模块,使用其他服务以满足业务需求。
要点 | 内容 |
服务发现 | 消费者通过服务注册中心获取生产者的实例信息,实现动态发现服务。 |
负载均衡 | 使用负载均衡器决定选择哪个生产者实例进行服务调用,确保请求分发均衡。 |
服务调用 | 通过 HTTP RESTful API 或 RPC 调用生产者的服务接口,完成特定的业务需求。 |
动态感知 | 消费者能够动态感知生产者实例的上线和下线,保持与服务注册中心的实时同步。 |
负载均衡与服务注册中心:
要点 | 内容 |
Ribbon 负载均衡器 |
作为客户端负载均衡器,通过配置策略决定选择哪个生产者实例进行服务调用。 |
Eureka 服务注册中心 |
用于生产者注册和消费者服务发现,提供服务实例信息和状态。 |
服务发现 与注册 |
生产者通过 Eureka 注册服务,消费者通过 Eureka 发现服务,实现解耦的服务调用。 |
动态感知 与适应性 |
负载均衡器和服务注册中心能够动态感知实例变化,保持对系统变化的及时适应性。 |
以上知识点总结了生产者和消费者在微服务架构中的基本概念和操作,强调了服务注册中心和负载均衡在实现服务发现和调用过程中的关键作用。这些概念为搭建稳健、高性能的微服务系统提供了基础。