当前位置: 首页 > news >正文

SpringBoot学习日记 Day6:解锁微服务与高效任务处理

一、开篇:从单体到微服务的思维转变

刚开始接触微服务时,我总习惯把所有功能写在一个项目里。直到项目越来越臃肿,每次修改都要全量部署,才意识到微服务架构的价值。今天我们就来探索SpringBoot在微服务场景下的强大能力!

二、多模块项目:微服务的雏形

1. 创建父工程(pom.xml关键配置)

<packaging>pom</packaging>
<modules><module>weather-service</module><module>user-service</module><module>common</module>
</modules><!-- 统一依赖管理 -->
<dependencyManagement><dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-dependencies</artifactId><version>2.7.3</version><type>pom</type><scope>import</scope></dependency></dependencies>
</dependencyManagement>

2. 子模块示例:common模块

common/
├── src/
│   ├── main/
│   │   ├── java/com/example/common/
│   │   │   ├── exception/  # 公共异常类
│   │   │   ├── utils/      # 工具类
│   │   │   └── config/     # 公共配置
│   ├── resources/
├── pom.xml

子模块pom.xml特点:

<parent><groupId>com.example</groupId><artifactId>parent-project</artifactId><version>1.0.0</version>
</parent><dependencies><!-- 模块间依赖 --><dependency><groupId>com.example</groupId><artifactId>common</artifactId><version>${project.version}</version></dependency>
</dependencies>

三、REST客户端:服务间通信的桥梁

1. RestTemplate基础用法

@Service
public class WeatherService {private final RestTemplate restTemplate;// 推荐使用构造器注入public WeatherService(RestTemplateBuilder builder) {this.restTemplate = builder.rootUri("https://api.weather.com").setConnectTimeout(Duration.ofSeconds(3)).build();}public WeatherData getWeather(String city) {String url = "/v1/current?city={city}";try {return restTemplate.getForObject(url, WeatherData.class, city);} catch (RestClientException e) {throw new BusinessException("天气服务调用失败", e);}}
}

2. WebClient响应式调用

@Service
public class ReactiveWeatherService {private final WebClient webClient;public ReactiveWeatherService(WebClient.Builder builder) {this.webClient = builder.baseUrl("https://api.weather.com").build();}public Mono<WeatherData> getWeatherAsync(String city) {return webClient.get().uri("/v1/current?city={city}", city).retrieve().bodyToMono(WeatherData.class).timeout(Duration.ofSeconds(2)).onErrorResume(e -> Mono.error(new BusinessException("天气服务异常")));}
}

3. 重试机制增强健壮性

@Bean
public RestTemplate restTemplate() {return new RestTemplateBuilder().interceptors(new RetryableRestTemplateInterceptor()).build();
}// 自定义拦截器
public class RetryableRestTemplateInterceptor implements ClientHttpRequestInterceptor {@Overridepublic ClientHttpResponse intercept(HttpRequest request, byte[] body, ClientHttpRequestExecution execution) throws IOException {int retryCount = 0;ClientHttpResponse response;while (retryCount < 3) {try {response = execution.execute(request, body);if (response.getStatusCode().is5xxServerError()) {throw new IOException("Server error");}return response;} catch (IOException e) {retryCount++;if (retryCount >= 3) {throw e;}Thread.sleep(1000 * retryCount);}}throw new IOException("Request failed after retries");}
}

四、定时任务:后台执行的瑞士军刀

1. 基础定时任务

@Slf4j
@Component
@EnableScheduling
public class WeatherDataSyncTask {// 每30分钟执行一次@Scheduled(fixedRate = 30 * 60 * 1000)public void syncWeatherData() {log.info("开始同步天气数据...");// 业务逻辑}// 每天凌晨1点执行@Scheduled(cron = "0 0 1 * * ?")public void clearOldData() {log.info("清理过期数据...");}
}

2. 动态定时任务(数据库配置触发时间)

@Service
public class DynamicTaskService {@Autowiredprivate ThreadPoolTaskScheduler taskScheduler;private ScheduledFuture<?> future;public void startTask(String taskId, Runnable task, String cron) {stopTask(taskId); // 先停止已有任务future = taskScheduler.schedule(task, new CronTrigger(cron));}public void stopTask(String taskId) {if (future != null) {future.cancel(true);}}
}// 配置线程池
@Configuration
public class TaskConfig {@Beanpublic ThreadPoolTaskScheduler taskScheduler() {ThreadPoolTaskScheduler scheduler = new ThreadPoolTaskScheduler();scheduler.setPoolSize(5);scheduler.setThreadNamePrefix("task-");return scheduler;}
}

五、异步处理:释放系统吞吐潜力

1. 快速启用异步

@SpringBootApplication
@EnableAsync
public class MyApp {public static void main(String[] args) {SpringApplication.run(MyApp.class, args);}
}// 自定义线程池
@Configuration
public class AsyncConfig {@Bean(name = "asyncExecutor")public Executor asyncExecutor() {ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();executor.setCorePoolSize(5);executor.setMaxPoolSize(10);executor.setQueueCapacity(100);executor.setThreadNamePrefix("Async-");executor.initialize();return executor;}
}

2. 异步方法实践

@Service
public class LogService {@Async("asyncExecutor")public CompletableFuture<Void> saveLogAsync(Log log) {// 模拟耗时操作Thread.sleep(1000);logRepository.save(log);return CompletableFuture.completedFuture(null);}@Asyncpublic void processInBackground() {// 无返回值的异步方法}
}// 调用示例
@RestController
public class LogController {@PostMapping("/logs")public Result<Void> addLog(@RequestBody Log log) {logService.saveLogAsync(log); // 异步执行return Result.success();}
}

六、实战项目:天气服务系统

1. 系统架构

weather-service (主模块)
├── 调用外部天气API
├── 定时缓存数据
├── 提供REST接口
│
common (公共模块)
├── 异常处理
├── 工具类

2. 核心代码片段

天气数据缓存:

@Cacheable(value = "weather", key = "#city")
public WeatherData getCachedWeather(String city) {return externalApiClient.getWeather(city);
}@Scheduled(fixedRate = 30 * 60 * 1000)
@CacheEvict(value = "weather", allEntries = true)
public void refreshCache() {log.info("清空天气缓存");
}

异步日志记录:

@Async
public void asyncAddAccessLog(HttpServletRequest request) {AccessLog log = new AccessLog();log.setPath(request.getRequestURI());log.setIp(request.getRemoteAddr());log.setCreateTime(LocalDateTime.now());logRepository.save(log);
}

七、避坑指南

1. 跨模块扫描问题:
- 主类添加@ComponentScan("com.example")
- 确保包结构有共同父包

2. RestTemplate单例问题:
- 推荐通过RestTemplateBuilder创建
- 为不同服务创建不同的RestTemplate实例

3. 定时任务阻塞:
- 默认使用单线程,长时间任务会影响其他任务
- 务必配置线程池

4. 异步失效场景:
- 同事务问题:自调用或private方法无效
- 异常处理:主线程无法捕获异步方法异常

八、明日计划

1. 学习SpringBoot Actuator监控
2. 集成Prometheus+Grafana
3. 实现健康检查与指标暴露
4. 探索SpringBoot Admin

思考题:在微服务架构下,如果天气服务和用户服务需要频繁通信,RestTemplate和WebClient哪种方式更适合?为什么?欢迎在评论区分享你的见解!

如果觉得这篇学习日记有帮助,请点赞收藏支持~完整天气服务示例代码可通过私信获取。在实际开发中遇到异步或定时任务问题也欢迎留言讨论!

http://www.dtcms.com/a/323290.html

相关文章:

  • .NET程序跨平台ARM电脑上发布的程序格式是,so还是DLL?
  • AWT 基本组件深入浅出:Button/Label/TextField/Checkbox/Choice/List 全面实战与性能优化
  • GPT-4 vs GPT-5 深度分析
  • 逻辑回归详解:原理、应用与实践
  • n沟道增强型mos管
  • 支持 UMD 自定义组件与版本控制:从 Schema 到动态渲染
  • Beelzebub靶机通关教程
  • java 中 @NotBlank 和 @NotNull 的区别
  • 【LLM实战|llamaIndex】llamaIndex介绍和RAG
  • dnSpy:设置断点
  • Docker 容器中运行昇腾(Ascend)AI 环境
  • Vitalik谈以太坊:ETH财库储备策略“有益且有价值”
  • SELinux 入门指南
  • vue+flask大模型写诗诗词推荐与可视化系统
  • 代理人工智能的隐藏威胁
  • 【渲染流水线】[几何阶段]-[图元装配]以UnityURP为例
  • Pandas 分层索引
  • AI 大模型企业级应用落地挑战与解决方案
  • 机器翻译:需要了解的数学基础详解
  • BPMN编辑器技术实现总结AI时代的工作流编辑器
  • Ubuntu系统忘记密码怎么办?
  • 【机器学习深度学习】模型选型:如何根据现有设备选择合适的训练模型
  • 安全合规3--防火墙
  • 知识蒸馏 - 大语言模型知识蒸馏LLM-KD-Trainer 源码分析 KnowledgeDistillationTrainer类
  • 【动态数据源】⭐️@DS注解实现项目中多数据源的配置
  • 【QT】常⽤控件详解(六)多元素控件 QListWidget Table Widget Tree Widget
  • 【Avalonia】无开发者账号使用iOS真机调试跨平台应用
  • C++四种类型转换
  • Tiger任务管理系统-12
  • SpringBoot学习日记(二)