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

Spring Boot基于AOP的本地/远程调用动态路由实践

在这里插入图片描述

01为什么我们要“无缝切换”

• MVP 阶段:单体最快,写完就可以上线。
• 流量暴涨后:微服务才能横向扩展,但把本地 userService.findById() 改成 feignClient.findById() 真要命——全链路改一遍,风险高、工期长。
• 理想状态:同一段业务代码,今天跑本地,明天改一行配置就跑远程,零代码侵入。

02核心设计一张图

---------------┐
│  业务代码      │  ← 只认接口 UserService--------------┘│ 注入
┌-----------------------┐
│  统一抽象层             │
│  根据配置动态选择实现    │
├-----------------------┤
│ UserServiceLocalImplUserServiceRemoteImpl │
└-----------------------

03一步步落地

先画好契约——接口层

// **统一接口** = 本地实现 + 远程 Feign 共用
public interface UserService {User getUserById(Long id);List<User> listAllUsers();User saveUser(User u);void updateUser(User u);void deleteUser(Long id);
}

本地实现

@Service
@ConditionalOnProperty(name = "service.mode", havingValue = "local", matchIfMissing = true)
public class UserServiceLocalImpl implements UserService {@Autowiredprivate UserRepository repo;   // 直连数据库,不走网络@Overridepublic User getUserById(Long id) {return repo.findById(id).orElse(null);}@Overridepublic List<User> listAllUsers() {return repo.findAll();}/* 其余方法省略 */
}

注解

  1. @ConditionalOnProperty:Spring Boot 的条件装配神器,配置文件里写 local 就激活。
  2. 直接依赖 DAO,零网络损耗,单元测试也能秒起。

远程实现——Feign

// 1. 声明式 HTTP 客户端
@FeignClient(name = "user-service", fallback = UserServiceFallback.class)
public interface UserServiceFeignClient {@GetMapping("/api/users/{id}")User getUserById(@PathVariable("id") Long id);@GetMapping("/api/users")List<User> listAllUsers();@PostMapping("/api/users")User saveUser(@RequestBody User u);@PutMapping("/api/users")void updateUser(@RequestBody User u);@DeleteMapping("/api/users/{id}")void deleteUser(@PathVariable("id") Long id);
}
@Service
@ConditionalOnProperty(name = "service.mode", havingValue = "remote")
public class UserServiceRemoteImpl implements UserService {@Autowiredprivate UserServiceFeignClient feignClient;   // 代理,真正发 HTTP@Overridepublic User getUserById(Long id) {return feignClient.getUserById(id);}/* 其余方法省略 */
}

注解

  1. @FeignClient:Ribbon + Hystrix 自动集成,负载均衡、熔断降级开箱即用。
  2. fallback:远程挂了直接走兜底逻辑,雪崩不存在的。

自动配置

@Configuration
@EnableFeignClients(basePackages = "com.example.feign")
public class ServiceAutoConfiguration {@Bean@ConditionalOnProperty(name = "service.mode", havingValue = "remote")public UserService userServiceRemote(UserServiceFeignClient client) {return new UserServiceRemoteImpl(client);}@Bean@ConditionalOnProperty(name = "service.mode", havingValue = "local", matchIfMissing = true)public UserService userServiceLocal(UserRepository repo) {return new UserServiceLocalImpl(repo);}
}

配置

# application.yml
service:mode: local   # 改成 remote 秒变微服务进阶玩法:细粒度路由 + 智能负载
按模块单独开关
service:user: localorder: remoteproduct: local

AOP 动态选路(伪代码)

@Aspect
@Component
public class SmartRoutingAspect {@Around("@annotation(SmartRouting)")public Object route(ProceedingJoinPoint pjp) {// 统计 RT、错误率,实时计算 local vs remote 分值boolean goLocal = loadBalancingService.shouldGoLocal(pjp.getSignature());return goLocal ? pjp.proceed() : feignInvoke(pjp);}
}

05优缺点

在这里插入图片描述

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

相关文章:

  • 如何在银河麒麟桌面系统中启用 sudo 密码的星号反馈
  • QT之openGL使用(一)
  • 燕之屋打造多元化产品组合,引领行业迈向高质量发展新里程
  • 心通达OA知识管理平台:高效解决单位知识管理难题
  • 点晴模切ERP帮忙模切行业向数智化管理转型
  • Rocky Linux 9 源码包安装php7
  • 如何通过mac的前24bit,模糊确认是那一台什么样的设备
  • macOS 字体管理全攻略:如何查看已安装字体及常见字体格式区
  • 从基础到实战:.NET 反射机制的进阶用法与最佳实践
  • Tekla多部门协作,授权资源如何共享与调度?
  • 暑期算法训练.3
  • day29:零基础学嵌入式之线程1.0
  • HTML 极简个人介绍卡片(侧重语义化标签和响应式布局)
  • pytorch小记(三十二):深度解析 PyTorch 的 `torch.remainder`:向下取整余数运算
  • 【web安全】DVWA存储型XSS分析与利用
  • 第6天| openGauss中用户一次只能连接到一个数据库,没法访问其他数据库的对象
  • arping(ARP协议网络测试工具)
  • 【实时Linux实战系列】实时系统的安全性架构
  • MySQL如何解决事务并发的幻读问题
  • 从单线程到云原生:Redis 二十年演进全景与内在机理深剖
  • RuoYi-Cloud 定制微服务
  • 宝塔申请证书错误,提示 module ‘OpenSSL.crypto‘ has no attribute ‘sign‘
  • 有痛呻吟!!!
  • 09-three.js Materials
  • 任务4.1 谁做的好事
  • Nginx/OpenResty HTTP 请求处理阶段与 Lua 实践全解20250717
  • Python包测试全攻略:从单元测试到持续集成
  • Rabbitmq Direct Exchange(直连交换机)多个消费者,配置相同的key ,队列,可以保证只有一个消费者消费吗
  • 生成式AI干预下的认知依赖与批判性思维发展:基于ChatGPT辅助写作的纵向追踪
  • stl-string模拟