數(shù)據(jù)聚合
1.問題
微服務的難點:
微服務將單體服務中功能模塊按功能拆分成多個微服務項目硕旗,已達到功能解耦的目的肠槽。但倡蝙,由于拆分功能同時會將數(shù)據(jù)存儲也進行拆分(甚至有的時候不同的微服務底層使用的數(shù)據(jù)持久化方案不同佛纫,比如:Mysql残吩,MongoDB等)缰趋,前端請求數(shù)據(jù)時可能存證跨服務數(shù)據(jù)返回捧杉。
例:
微服務:訂單服務,用戶服務秘血,CRM服務
前端請求訂單數(shù)據(jù)需要包含訂單數(shù)據(jù)(訂單服務)味抖,訂單負責人信息(用戶服務),客戶信息(CRM服務)灰粮。
2.解決方案:
前端處理
前端請求數(shù)據(jù)后仔涩,根據(jù)數(shù)據(jù)再分別請求其他服務。
例如:
- 請求訂單數(shù)據(jù)
- 根據(jù)訂單數(shù)據(jù)中的負責人id粘舟,請求用戶服務數(shù)據(jù)
- 根據(jù)訂單數(shù)據(jù)中的客戶數(shù)據(jù)熔脂,請求CRM服務數(shù)據(jù)
缺點:
- 增加前端工作量
- 增加響應時間佩研。
后端處理
在服務中調用其他服務將數(shù)據(jù)補全,然后返回到前端
例如:
- 訂單服務查詢數(shù)據(jù)庫得到訂單數(shù)據(jù)
- 根據(jù)訂單數(shù)據(jù)中的負責人id霞揉,請求用戶服務數(shù)據(jù)
- 根據(jù)訂單數(shù)據(jù)中的客戶數(shù)據(jù)旬薯,請求CRM服務數(shù)據(jù)
- 返回數(shù)據(jù)
缺點:
- 增加后端工作量
BFF數(shù)據(jù)聚合(本項目采用的解決方案)
在前后端中間增加BFF數(shù)據(jù)聚合服務。
3.數(shù)據(jù)聚合
3.1數(shù)據(jù)聚合過程
sequenceDiagram
前端->>gateway:請求訂單數(shù)據(jù)
gateway->>聚合服務:請求訂單數(shù)據(jù)
聚合服務->>訂單服務:請求訂單數(shù)據(jù)
訂單服務->>聚合服務:返回訂單數(shù)據(jù)
聚合服務->>用戶服務:請求負責人數(shù)據(jù)
用戶服務->>聚合服務:返回負責人數(shù)據(jù)
聚合服務->>CRM服務:請求客戶數(shù)據(jù)
CRM服務->>聚合服務:返回客戶數(shù)據(jù)
聚合服務->>gateway:返回訂單數(shù)據(jù)(數(shù)據(jù)聚合后)
gateway->>前端:返回訂單數(shù)據(jù)(數(shù)據(jù)聚合后)
3.2注解:
Aggregation
- 使用在方法上适秩,只能使用在Web響應方法上绊序,表示該方法供數(shù)據(jù)聚合調用返回聚合數(shù)據(jù)
- 使用在集合屬性上,配合AggregationParam使用
- 使用在類上秽荞,表示類需要在聚合服務中聚合數(shù)據(jù)
@Target({ ElementType.TYPE, ElementType.FIELD, ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Aggregation {
/**
* 參數(shù)
* @return
*/
AggregationParam[] params() default {};
}
AggregationParam
配合Aggregation的屬性上使用
public @interface AggregationParam {
/**
* 查詢參數(shù)名
* @return
*/
String name();
/**
* 查詢參數(shù)引用或者常量值
* @return
*/
String value();
/**
* 是否是常量
* @return
*/
boolean constant() default false;
}
- name 參數(shù)名骤公,與聚合服務提供接口的參數(shù)一致
- value 取值
- 如果constant為true,則為字面值
- 如果constant為false蚂会,則使用當前對象的名字為value屬性值
例如:
需求側:
public class DepartmentDetail{
private String id; // 假設id值為D0001
private String name;
/** departmentId為服務提供接口的參數(shù)
* 因為constant為false
* value為id淋样,則調用服務提供接口時,參數(shù)為departmentId=D0001
*/
@Aggregation(params={@AggregationParam(name="departmentId",value="id")})
private List<UserAggregation> members;
}
提供側:
@Aggregation
public class UserAggregation {
private String id; // id需要和UserAggregationController的PathVariable對應
private String name;
// 省略getter setter
}
@RestController
@RequestMapping("aggregation/users")
public class UserAggregationController {
@Aggregation
@GetMapping()
public List<UserAggregation> getAggregation(
@RequestParam("departmentId")String departmentId){
// 返回UserAggregation數(shù)組
}
}
注意
如果是非空集合則不需要使用@Aggregation
例如:
@Aggregation public class UserAggregation { private String id; // id需要和UserAggregationController的PathVariable對應 private String name; // 省略getter setter constructure } @RestController @RequestMapping("aggregation/users") public class UserAggregationController { @Aggregation @GetMapping("{id}") public UserAggregation getAggregation(@PathVariable("id")String id){ // 省略返回UserAggregation } } public class DepartmentDetail{ private String id; // 假設id值為D0001 private String name; // members在邏輯中會被添加元素胁住,不需要使用@Aggregation // 會調用GET /aggregation/users/{id} private List<UserAggregation> members; } @RestController @RequestMapping("departments") public class DepartmentController{ @GetMapping("{id}") public DepartmentDetail getDepartment(@PathVariable("id")String id){ DepartmentDetail detail = new DepartmentDetail(); List<UserAggregation> list = new ArrayList<>(); list.add(new UserAggregation('U01')); list.add(new UserAggregation('U02')); list.add(new UserAggregation('U03')); detail.setMemebers(list); return detail; } }
3.3實現(xiàn)
wy-aggregation-service
實現(xiàn)使用的是Egg框架(node.js)。node 對于IO有好性能刊咳。同時彪见,由于js是弱類型語言,更容易對json數(shù)據(jù)處理娱挨。
3.4示例
用戶服務
@Aggregation
public class UserAggregation {
private String id; // id需要和UserAggregationController的PathVariable對應
private String name;
// 省略getter setter
}
@RestController
@RequestMapping("aggregation/users")
public class UserAggregationController {
@Aggregation
@GetMapping("{id}")
public UserAggregation getAggregation(@PathVariable("id")String id){
// 省略返回UserAggregation
}
@Aggregation
@GetMapping()
public List<UserAggregation> getAggregation(
@RequestParam("departmentId")String departmentId){
// 返回UserAggregation數(shù)組
}
}
CRM服務
@Aggregation
public class ClientAggregation {
private String id;
private String name;
// 省略getter setter
}
@RestController
@RequestMapping("aggregation/cleints")
public class CleintAggregationController {
@Aggregation
@GetMapping("{id}")
public ClientAggregation getAggregation(@PathVariable("id")String id){
// 省略返回ClientAggregation
}
}
訂單服務
public class Order{
private String id;
private String code;
private UserAggregation admin; // 負責人
private CleintAggregation client; // 客戶
// 省略getter setter
}
@RestController
@RequestMapping("orders")
public class OrderController {
@GetMapping("{id}")
public Order getOne(@PathVariable("id")String id){
Order order = new Order();
order.setAdmin(new UserAggregation('adminId'));// 需要設置adminId
order.setClient(new ClientAggregation('clientId'));//需要設置clientId
return order;
}
}
4.代碼
https://gitee.com/wenyu7980/wy-aggregation
https://gitee.com/wenyu7980/wy-aggregation-service