数据聚合
1. 问题
微服务的难点:
微服务将单体服务中功能模块按性能拆分成多个微服务项目,已达到性能解耦的目标。但,因为拆分性能同时会将数据存储也进行拆分(甚至有的时候不同的微服务底层应用的数据长久化计划不同,比方:Mysql,MongoDB 等),前端申请数据时可能存证跨服务数据返回。
例:
微服务:订单服务,用户服务,CRM 服务
前端申请订单数据须要蕴含订单数据(订单服务),订单负责人信息(用户服务),客户信息(CRM 服务)。
2. 解决方案:
前端解决
前端申请数据后,依据数据再别离申请其余服务。
例如:
- 申请订单数据
- 依据订单数据中的负责人 id,申请用户服务数据
- 依据订单数据中的客户数据,申请 CRM 服务数据
毛病:
- 减少前端工作量
- 减少响应工夫。
后端解决
在服务中调用其余服务将数据补全,而后返回到前端
例如:
- 订单服务查询数据库失去订单数据
- 依据订单数据中的负责人 id,申请用户服务数据
- 依据订单数据中的客户数据,申请 CRM 服务数据
- 返回数据
毛病:
- 减少后端工作量
BFF 数据聚合 (本我的项目采纳的解决方案)
在前后端两头减少 BFF 数据聚合服务。
3. 数据聚合
3.1 数据聚合过程
sequenceDiagram
前端 ->>gateway: 申请订单数据
gateway->> 聚合服务: 申请订单数据
聚合服务 ->> 订单服务: 申请订单数据
订单服务 ->> 聚合服务: 返回订单数据
聚合服务 ->> 用户服务: 申请负责人数据
用户服务 ->> 聚合服务: 返回负责人数据
聚合服务 ->>CRM 服务: 申请客户数据
CRM 服务 ->> 聚合服务: 返回客户数据
聚合服务 ->>gateway: 返回订单数据(数据聚合后)gateway->> 前端: 返回订单数据(数据聚合后)
3.2 注解:
Aggregation
- 应用在办法上,只能应用在 Web 响应办法上,示意该办法供数据聚合调用返回聚合数据
- 应用在汇合属性上,配合 AggregationParam 应用
- 应用在类上,示意类须要在聚合服务中聚合数据
@Target({ElementType.TYPE, ElementType.FIELD, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Aggregation {
/**
* 参数
* @return
*/
AggregationParam[] params() default {};}
AggregationParam
配合 Aggregation 的属性上应用
public @interface AggregationParam {
/**
* 查问参数名
* @return
*/
String name();
/**
* 查问参数援用或者常量值
* @return
*/
String value();
/**
* 是否是常量
* @return
*/
boolean constant() default false;}
- name 参数名,与聚合服务提供接口的参数统一
-
value 取值
- 如果 constant 为 true,则为字面值
- 如果 constant 为 false,则应用以后对象的名字为 value 属性值
例如:
需要侧:
public class DepartmentDetail{
private String id; // 假如 id 值为 D0001
private String name;
/** departmentId 为服务提供接口的参数
* 因为 constant 为 false
* value 为 id,则调用服务提供接口时,参数为 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 数组}
}
留神
如果是非空集合则不须要应用 @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 实现
wy-aggregation-service
实现应用的是 Egg 框架(node.js)。node 对于 IO 有好性能。同时,因为 js 是弱类型语言,更容易对 json 数据处理。
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 数组}
}
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/w…
https://gitee.com/wenyu7980/w…