👀 分布式服务
作用:
分布式服务将应用程序的不同部分部署在多台服务器上,以提高可靠性、可用性和可扩展性。
使用场景:
- 高并发访问应用程序
- 需要高可用性和故障切换的应用程序
- 服务化、微服务架构的应用程序
优缺点:
- 优点: 可扩展、故障隔离、更好的资源利用
- 缺点: 网络延迟、复杂的服务协调和管理
名称 | 作用 | 使用场景 | 优缺点 | Redisson调用的方法 |
---|---|---|---|---|
分布式远程服务(Remote Service) | 允许方法在分布式环境中远程执行 | 跨节点执行方法 | 优点: 分布式执行、高可用性 缺点: 网络延迟、复杂性增加 | redissonClient.getRemoteService() |
分布式实时对象(Live Object)服务 | ORM服务,将Java对象映射到Redis | 存储和管理Java对象 | 优点: 简化Redis使用、自动序列化 缺点: ORM层性能开销 | redissonClient.getLiveObjectService() |
分布式执行服务(Executor Service) | 在分布式环境中执行、调度和取消任务 | 分布式任务执行 | 优点: 分布式任务、动态节点管理 缺点: 相对于单节点有更多网络开销 | redissonClient.getExecutorService() |
分布式调度任务服务(Scheduler Service) | 在分布式环境中计划和执行定时任务 | 定时任务、任务调度 | 优点: 分布式定时任务、自动任务分配 缺点: 相对于单节点有更多网络开销 | redissonClient.getScheduledExecutorService() |
分布式映射归纳服务(MapReduce) | 处理和生成大数据集 | 大数据分析、日志处理 | 优点: 处理大量数据、并行计算能力 缺点: 对于小数据集或简单计算任务可能过于复杂 | redissonClient.getMap("mapName").mapReduce() |
✌ 分布式远程服务(Remote Service)
作用:
分布式远程服务允许在不同的服务器或地理位置上的服务之间进行通信。
使用场景:
- 微服务之间的通信
- 跨地理位置的服务间通信
优缺点:
- 优点: 提高了服务的解耦,可以跨网络或地理位置进行通信
- 缺点: 网络延迟,序列化和反序列化的开销
🎷示例: 使用Spring Boot和Redisson实现分布式远程服务
🎯1. 创建Spring Boot项目并添加依赖
首先,我们需要创建一个Spring Boot项目并添加Redisson的依赖。
<dependencies>
<!-- Spring Boot Web Starter -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- Redisson -->
<dependency>
<groupId>org.redisson</groupId>
<artifactId>redisson-spring-boot-starter</artifactId>
<version>3.15.0</version> <!-- 使用最新的版本 -->
</dependency>
</dependencies>
🎯2. 配置Redisson
在src/main/resources
目录下,创建redisson.yaml
文件并配置Redisson:
singleServerConfig:
address: "redis://127.0.0.1:6379"
然后,在application.properties
中指定该配置文件:
spring.redisson.config=classpath:redisson.yaml
🎯3. 创建远程接口和实现
// Remote Interface
public interface MyRemoteInterface {
String processData(String data);
}
// Implementation
@Service
public class MyRemoteServiceImpl implements MyRemoteInterface {
@Override
public String processData(String data) {
// 实际处理逻辑
return "Processed: " + data;
}
}
🎯4. 创建Controller来使用远程服务
@RestController
@RequestMapping("/api")
public class MyController {
@Autowired
private RedissonClient redissonClient;
@GetMapping("/send")
public String sendData(@RequestParam String data) {
RRemoteService remoteService = redissonClient.getRemoteService();
MyRemoteInterface service = remoteService.get(MyRemoteInterface.class);
return service.processData(data);
}
}
🎯5. 启动并测试
运行Spring Boot应用并通过浏览器或工具访问http://localhost:8080/api/send?data=test
,您应该能看到“Processed: test”作为响应。
✍ 分布式远程服务工作流程
作用:
描述服务如何接收请求、处理请求和返回响应的过程。
使用场景:
- 当需要详细了解或优化服务通信时
优缺点:
- 优点: 提供了清晰的通信流程,有助于问题诊断
- 缺点: 如果设计不当,可能导致性能瓶颈或失败点
🎷示例: 使用Spring Boot和Redisson实现分布式远程服务工作流程
🎯1. 创建Spring Boot项目并添加依赖
这一步与上述示例相同,确保已经添加了Spring Boot和Redisson的依赖。
🎯2. 配置Redisson
同样,这一步与上述示例相同。
🎯3. 创建远程接口和实现
// Remote Interface
public interface MyRemoteInterface {
String processData(String data);
}
// Implementation
@Service
public class MyRemoteServiceImpl implements MyRemoteInterface {
@Override
public String processData(String data) {
// 实际处理逻辑
return "Processed: " + data;
}
}
🎯4. 创建Controller来使用远程服务
@RestController
@RequestMapping("/api")
public class MyController {
@Autowired
private RedissonClient redissonClient;
@GetMapping("/send")
public String sendData(@RequestParam String data) {
RRemoteService remoteService = redissonClient.getRemoteService();
// 步骤1: 获取远程服务接口的代理实例
MyRemoteInterface service = remoteService.get(MyRemoteInterface.class);
// 步骤2: 调用远程服务
String response = service.processData(data);
// 步骤3: 返回响应
return response;
}
}
🎯5. 工作流程解释
-
获取远程服务接口的代理实例: 当调用
remoteService.get(MyRemoteInterface.class)
时,Redisson会为我们提供一个代理实例,我们可以像调用本地方法一样调用它。 -
调用远程服务: 使用代理实例调用方法时,实际上是将调用及其参数序列化并发送到Redis服务器。然后,另一个Redisson客户端(在另一个应用实例或服务器上)会反序列化此调用并执行相应的方法。
-
返回响应: 方法的结果会被序列化并发送回原始的Redisson客户端,然后客户端会反序列化并返回给调用者。
✍ 发送即不管(Fire-and-Forget)模式和应答回执(Ack-Response)模式
作用:
定义消息发送和接收的方式。
使用场景:
- 发送即不管模式: 日志记录、统计数据上报等
- 应答回执模式: 金融交易、订单处理等
优缺点:
- 发送即不管模式:
- 优点: 快速,无需等待响应
- 缺点: 无法得知消息是否已处理或成功
- 应答回执模式:
- 优点: 可以确认消息的处理状态
- 缺点: 增加了通信的复杂性和延迟
🎷示例: 使用Spring Boot和Redisson实现发送即不管和应答回执模式
🎯1. 创建Spring Boot项目并添加依赖
与之前的示例相同,您需要添加Spring Boot和Redisson的依赖。
🎯2. 配置Redisson
与之前的示例相同,您需要配置Redisson。
🎯3. 创建消息发布和订阅的服务
@Service
public class MessageService {
@Autowired
private RedissonClient redissonClient;
// 发送即不管模式
public void fireAndForget(String message) {
RTopic<String> topic = redissonClient.getTopic("myTopic");
topic.publish(message);
}
// 应答回执模式
public CompletableFuture<String> ackResponse(String message) {
RTopic<String> topic = redissonClient.getTopic("ackTopic");
return topic.publishAsync(message);
}
@PostConstruct
public void subscribe() {
RTopic<String> topic = redissonClient.getTopic("myTopic");
topic.addListener((channel, msg) -> {
// 处理消息
System.out.println("Received: " + msg);
});
}
}
🎯4. 创建Controller来使用消息服务
@RestController
@RequestMapping("/message")
public class MessageController {
@Autowired
private MessageService messageService;
@PostMapping("/fire-and-forget")
public ResponseEntity<Void> fireAndForget(@RequestBody String message) {
messageService.fireAndForget(message);
return ResponseEntity.ok().build();
}
@PostMapping("/ack-response")
public ResponseEntity<String> ackResponse(@RequestBody String message) {
CompletableFuture<String> future = messageService.ackResponse(message);
try {
String response = future.get(); // 等待异步响应
return ResponseEntity.ok(response);
} catch (InterruptedException | ExecutionException e) {
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body("Error processing message");
}
}
}
🎯5. 启动并测试
- 对于“发送即不管”模式,您可以发送消息并不期望任何响应。
- 对于“应答回执”模式,您将发送消息并等待异步响应,确认消息已被处理。
使用相应的API端点发送请求,并观察消息如何被处理。
✍ 异步调用
作用:
允许发送者在不等待响应的情况下继续执行其他任务。
使用场景:
- 长时间运行的任务
- 后台处理任务,如图片上传、数据处理等
优缺点:
- 优点: 提高了系统的响应性和吞吐量
- 缺点: 需要处理并发、同步和错误恢复问题
当我们讨论异步调用时,我们通常指的是一个系统或服务发起的请求不需要立即等待结果。相反,它继续进行其他操作,并在稍后某个时间点(通常通过回调或事件)接收响应。
在这个示例中,我们将使用Spring Boot、Redisson和Java的CompletableFuture
来实现异步调用。
🎷示例: 使用Spring Boot和Redisson实现异步调用
🎯1. 创建Spring Boot项目并添加依赖
与之前的示例相同,您需要添加Spring Boot和Redisson的依赖。
🎯2. 配置Redisson
与之前的示例相同,您需要配置Redisson。
🎯3. 创建异步处理的服务
@Service
public class AsyncService {
@Autowired
private RedissonClient redissonClient;
// 异步处理数据并返回结果
public CompletableFuture<String> asyncProcessData(String data) {
return CompletableFuture.supplyAsync(() -> {
// 模拟数据处理
try {
Thread.sleep(2000); // 模拟长时间的处理
} catch (InterruptedException e) {
e.printStackTrace();
}
return "Processed: " + data;
});
}
}
🎯4. 创建Controller来使用异步服务
@RestController
@RequestMapping("/async")
public class AsyncController {
@Autowired
private AsyncService asyncService;
@GetMapping("/process")
public ResponseEntity<String> processData(@RequestParam String data) {
CompletableFuture<String> future = asyncService.asyncProcessData(data);
try {
String response = future.get(10, TimeUnit.SECONDS); // 最多等待10秒
return ResponseEntity.ok(response);
} catch (InterruptedException | ExecutionException | TimeoutException e) {
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body("Error processing data");
}
}
}
🎯5. 启动并测试
启动应用并访问http://localhost:8080/async/process?data=test
。您会注意到,尽管数据处理需要大约2秒的时间,但由于我们的调用是异步的,整个系统不会被阻塞。
✍ 取消异步调用
作用:
允许在异步调用还未完成时取消该调用。
使用场景:
- 用户取消了长时间运行的任务
- 系统资源紧张,需要优先处理其他任务
优缺点:
- 优点: 可以节省资源,避免不必要的处理
- 缺点: 需要额外的逻辑来处理取消操作,可能导致数据不一致
异步调用的取消通常涉及两个方面:
- 取消尚未开始的异步任务。
- 尝试中断正在运行的异步任务。
在这个示例中,我们将使用Spring Boot和Java的CompletableFuture
来实现取消异步调用。
🎷示例: 使用Spring Boot取消异步调用
🎯1. 创建Spring Boot项目并添加Web Starter依赖
🎯2. 创建异步处理的服务
@Service
public class AsyncService {
private final Executor executor = Executors.newFixedThreadPool(10);
private final Map<String, CompletableFuture<String>> tasks = new ConcurrentHashMap<>();
// 异步处理数据并返回任务ID
public String asyncProcessData(String data) {
String taskId = UUID.randomUUID().toString();
CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> {
try {
// 模拟长时间处理
Thread.sleep(10000);
return "Processed: " + data;
} catch (InterruptedException e) {
return "Task was cancelled.";
}
}, executor);
tasks.put(taskId, future);
return taskId;
}
// 取消异步任务
public String cancelTask(String taskId) {
CompletableFuture<String> future = tasks.get(taskId);
if (future != null && !future.isDone()) {
future.cancel(true);
return "Task cancelled.";
}
return "Task not found or already completed.";
}
}
🎯3. 创建Controller来使用异步服务
@RestController
@RequestMapping("/async")
public class AsyncController {
@Autowired
private AsyncService asyncService;
@PostMapping("/start")
public ResponseEntity<String> startTask(@RequestBody String data) {
String taskId = asyncService.asyncProcessData(data);
return ResponseEntity.ok("Task started with ID: " + taskId);
}
@PostMapping("/cancel/{taskId}")
public ResponseEntity<String> cancelTask(@PathVariable String taskId) {
String message = asyncService.cancelTask(taskId);
return ResponseEntity.ok(message);
}
}
🎯4. 启动并测试
- 启动应用并使用
POST
请求访问http://localhost:8080/async/start
来开始一个异步任务。你会得到一个任务ID作为响应。 - 在任务完成之前,使用该任务ID和
POST
请求访问http://localhost:8080/async/cancel/{taskId}
来取消异步任务。
✌ 分布式实时对象(Live Object)服务
✍ 介绍
Redisson Live Object Service是一个ORM(Object-Relational Mapping,对象关系映射)服务,可以将常见的Java对象映射到Redis的数据结构中。
作用:
- 提供了一种简单、直观的方式来使用Redis数据结构。
- 自动处理对象的序列化和反序列化。
使用场景:
- 当您需要将Java对象存储在Redis中,并希望通过简单的操作来管理它们时。
- 当您希望利用Redis的高性能和低延迟特性来管理实时数据。
优缺点:
- 优点: 简化了Redis数据结构的使用,提供了更高级的对象管理功能。
- 缺点: ORM层可能会引入一些额外的性能开销。
✍ 使用方法
要使用Redisson的Live Object服务,您首先需要定义一个接口,并为其添加@REntity
注解。接着,您可以使用RLiveObjectService
来获取或存储这些实时对象。
示例:
假设我们有一个User
对象,我们希望将其存储在Redis中:
@REntity
public interface User {
@RId
String getUsername();
void setUsername(String username);
String getEmail();
void setEmail(String email);
}
在Spring Boot应用中使用:
@Autowired
private RedissonClient redissonClient;
public void createUser() {
RLiveObjectService liveObjectService = redissonClient.getLiveObjectService();
User user = liveObjectService.create(User.class);
user.setUsername("john");
user.setEmail("john@example.com");
}
✍ 高级使用方法
Redisson的Live Object支持嵌套对象、索引和其他高级功能。
示例:
@REntity
public interface Order {
@RId
Long getId();
void setId(Long id);
@RIndex
User getUser();
void setUser(User user);
BigDecimal getTotal();
void setTotal(BigDecimal total);
}
在上述示例中,Order
对象有一个User
对象作为其属性,并为其创建了一个索引。
✍ 注解(Annotation)使用方法
Redisson提供了多个注解来帮助您定义和管理Live Objects:
@REntity
: 标记一个接口为实时对象。@RId
: 标记一个属性为实时对象的ID。@RIndex
: 标记一个属性为索引。
✍ 使用限制
- 实时对象只能是接口,不能是类。
- 所有的属性都需要有getter和setter方法。
- 不支持循环引用。
✌ 分布式执行服务(Executor Service)
✍ 分布式执行服务概述
Redisson的分布式执行服务(Executor Service)是一个强大的工具,允许您在分布式环境中执行、调度和取消任务。
作用:
- 执行分布式任务。
- 动态添加或删除工作节点。
- 使用Java的标准
ExecutorService
、ScheduledExecutorService
和Executor
接口。
使用场景:
- 当您需要在多个节点上均匀分配任务时。
- 当您需要在一个节点失败时,另一个节点接管并执行任务。
优缺点:
- 优点: 提供了一个熟悉的API来执行分布式任务,自动处理任务分配和失败转移。
- 缺点: 与单节点执行服务相比,可能会有更多的网络开销。
✍ 任务
您可以使用Java的标准Runnable
和callable
接口定义任务。
示例:
定义一个简单的任务:
public class MyTask implements Runnable {
@Override
public void run() {
// 执行任务逻辑
}
}
在Spring Boot应用中执行任务:
@Autowired
private RedissonClient redissonClient;
public void executeTask() {
RExecutorService executor = redissonClient.getExecutorService("myExecutor");
executor.execute(new MyTask());
}
✍ 取消任务
您可以取消已提交但尚未执行的任务。
示例:
@Autowired
private RedissonClient redissonClient;
public void cancelTask() {
RExecutorService executor = redissonClient.getExecutorService("myExecutor");
Future<?> future = executor.submit(new MyTask());
// 取消任务
future.cancel(true);
}
✌ 分布式调度任务服务(Scheduler Service)
✍ 分布式调度任务服务概述
Redisson的分布式调度任务服务允许您在分布式环境中计划和执行定时任务。
作用:
- 在分布式环境中执行定时任务。
- 动态添加或删除工作节点,而不会影响已计划的任务。
- 使用Java的标准
ScheduledExecutorService
接口。
使用场景:
- 当您需要在多个节点上均匀分配定时任务时。
- 当您需要在一个节点失败时,另一个节点接管并执行任务。
优缺点:
- 优点: 提供了一个熟悉的API来执行分布式定时任务,自动处理任务分配和失败转移。
- 缺点: 与单节点调度任务服务相比,可能会有更多的网络开销。
✍ 设定任务计划
您可以设定任务以固定的时间间隔或固定的延迟来执行。
示例:
定义一个简单的任务:
public class MyScheduledTask implements Runnable {
@Override
public void run() {
// 执行任务逻辑
}
}
在Spring Boot应用中设定任务计划:
@Autowired
private RedissonClient redissonClient;
public void scheduleTask() {
RScheduledExecutorService scheduler = redissonClient.getExecutorService("myScheduler");
scheduler.scheduleAtFixedRate(new MyScheduledTask(), 10, 30, TimeUnit.SECONDS); // 每30秒执行一次,首次执行延迟10秒
}
✍ 通过CRON表达式设定任务计划
Redisson还支持使用CRON表达式来计划任务。
示例:
在Spring Boot应用中使用CRON表达式设定任务计划:
@Autowired
private RedissonClient redissonClient;
public void scheduleTaskWithCron() {
RScheduledExecutorService scheduler = redissonClient.getExecutorService("myScheduler");
scheduler.schedule(new MyScheduledTask(), CronSchedule.of("0 */10 * * * ?")); // 每10分钟执行一次
}
✍ 取消计划任务
您可以取消已计划的任务。
示例:
@Autowired
private RedissonClient redissonClient;
public void cancelScheduledTask() {
RScheduledExecutorService scheduler = redissonClient.getExecutorService("myScheduler");
ScheduledFuture<?> future = scheduler.schedule(new MyScheduledTask(), 10, TimeUnit.SECONDS);
// 取消任务
future.cancel(true);
}
✌ 分布式映射归纳服务(MapReduce)
✍ 介绍
MapReduce是一个编程模型,用于处理和生成大数据集。它由两个主要步骤组成:映射(Map)步骤和归纳(Reduce)步骤。
作用:
- 分析、处理和聚合大量数据。
- 并行处理数据,提高性能。
使用场景:
- 大数据分析。
- 日志文件处理。
- 数据转换和清洗。
优缺点:
- 优点: 能够处理大量数据,提供并行计算能力。
- 缺点: 对于小数据集或简单的计算任务,可能过于复杂。
✍ 映射(Map)类型的使用范例
映射步骤接收数据并将其转换为一组中间键值对。
示例:
考虑一个简单的任务:我们有一个字符串列表,并希望计算每个单词出现的次数。
public class WordMapper implements RMapper<String, String, String, Integer> {
@Override
public void map(String key, String value, RCollector<String, Integer> collector) {
String[] words = value.split(" ");
for (String word : words) {
collector.emit(word, 1);
}
}
}
在Spring Boot应用中使用:
@Autowired
private RedissonClient redissonClient;
public void mapExample() {
RMap<String, String> map = redissonClient.getMap("myMap");
map.put("line1", "hello world");
map.put("line2", "hello redisson");
RMapReduce<String, String, String, Integer> mapReduce = map.mapReduce().mapper(new WordMapper());
// ... (后续的reduce操作)
}
✍ 集合(Collection)类型的使用范例
对于集合类型,您可以使用MapReduce来处理列表、集合或队列中的数据。
示例:
考虑同样的任务,但这次我们使用列表存储数据。
public void collectionExample() {
RList<String> list = redissonClient.getList("myList");
list.add("hello world");
list.add("hello redisson");
RMapReduce<String, String, String, Integer> mapReduce = list.mapReduce().mapper(new WordMapper());
// ... (后续的reduce操作)
}