SpringBoot基于AOP的本地/遠(yuǎn)程調(diào)用動態(tài)路由實踐指南
01為什么我們要“無縫切換”
MVP 階段:單體最快,寫完就可以上線。
流量暴漲后:微服務(wù)才能橫向擴展,但把本地 userService.findById() 改成 feignClient.findById() 真要命——全鏈路改一遍,風(fēng)險高、工期長。
理想狀態(tài):同一段業(yè)務(wù)代碼,今天跑本地,明天改一行配置就跑遠(yuǎn)程,零代碼侵入。

02核心設(shè)計一張圖
┌---------------┐
│ 業(yè)務(wù)代碼 │ ← 只認(rèn)接口 UserService
└-------┬-------┘
│ 注入
┌-------┴----------------┐
│ 統(tǒng)一抽象層 │
│ 根據(jù)配置動態(tài)選擇實現(xiàn) │
├------------┬-----------┤
│ UserServiceLocalImpl │ UserServiceRemoteImpl │
└------------┴-----------┘
03一步步落地
先畫好契約——接口層
// **統(tǒng)一接口** = 本地實現(xiàn) + 遠(yuǎn)程 Feign 共用
public interface UserService {
User getUserById(Long id);
List<User> listAllUsers();
User saveUser(User u);
void updateUser(User u);
void deleteUser(Long id);
}
本地實現(xiàn)
@Service
@ConditionalOnProperty(name = "service.mode", havingValue = "local", matchIfMissing = true)
public class UserServiceLocalImpl implements UserService {
@Autowired
private UserRepository repo; // 直連數(shù)據(jù)庫,不走網(wǎng)絡(luò)
@Override
public User getUserById(Long id) {
return repo.findById(id).orElse(null);
}
@Override
public List<User> listAllUsers() {
return repo.findAll();
}
/* 其余方法省略 */
}
注解
- @ConditionalOnProperty:Spring Boot 的條件裝配神器,配置文件里寫 local 就激活。
- 直接依賴 DAO,零網(wǎng)絡(luò)損耗,單元測試也能秒起。
遠(yuǎn)程實現(xiàn)——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 {
@Autowired
private UserServiceFeignClient feignClient; // 代理,真正發(fā) HTTP
@Override
public User getUserById(Long id) {
return feignClient.getUserById(id);
}
/* 其余方法省略 */
}
注解
- @FeignClient:Ribbon + Hystrix 自動集成,負(fù)載均衡、熔斷降級開箱即用。
- fallback:遠(yuǎn)程掛了直接走兜底邏輯,雪崩不存在的。
自動配置
@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 秒變微服務(wù)
進階玩法:細(xì)粒度路由 + 智能負(fù)載
按模塊單獨開關(guān)
service: user: local order: remote product: local
AOP 動態(tài)選路(偽代碼)
@Aspect
@Component
public class SmartRoutingAspect {
@Around("@annotation(SmartRouting)")
public Object route(ProceedingJoinPoint pjp) {
// 統(tǒng)計 RT、錯誤率,實時計算 local vs remote 分值
boolean goLocal = loadBalancingService.shouldGoLocal(pjp.getSignature());
return goLocal ? pjp.proceed() : feignInvoke(pjp);
}
}
04優(yōu)缺點

到此這篇關(guān)于SpringBoot基于AOP的本地/遠(yuǎn)程調(diào)用動態(tài)路由實踐指南的文章就介紹到這了,更多相關(guān)SpringBoot調(diào)用動態(tài)路由內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Apache?Hudi異步Clustering部署操作的掌握
這篇文章主要介紹了Apache?Hudi異步Clustering部署操作的掌握,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步早日升職加薪2022-03-03
mybatis-plus雪花算法增強idworker的實現(xiàn)
今天聊聊在mybatis-plus中引入分布式ID生成框架idworker,進一步增強實現(xiàn)生成分布式唯一ID,具有一定的參考價值,感興趣的小伙伴們可以參考一下2022-07-07
java如何實現(xiàn)自動生成數(shù)據(jù)庫設(shè)計文檔
以前我們還需要手寫數(shù)據(jù)庫設(shè)計文檔、現(xiàn)在可以通過引入screw核心包來實現(xiàn)Java?數(shù)據(jù)庫文檔一鍵生成。本文將具體介紹一下如何通過java自動生成數(shù)據(jù)庫設(shè)計文檔,需要的朋友可以參考下2021-11-11

