一. 业务场景说明
微服务开发,有两个独立的应用服务:a 和 b,a是聚合入口服务,单向调用关系,即 a -> b。又有一个开放平台服务c,需要先调用a,然后再a中回调通知b。但开发平台回调通知的时间有很多,在a中不想对每一个通知时间都写一个方法rest接口来接收,而是想用一个rest方法统一收归开放平台的各种通知,然后根据通知类型分派到b中不同的api里,这样保证了a作为聚合入口服务的纯粹性,开放平台也只需要和a的这一个统一收归的接口交互,真正的业务处理放到b中,各自职责明确。那么如何实现呢?
二. 通过注解动态获取类,并反射调用api
a服务中:
- 自定义注解
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Component
public @interface Event {
OpenPlatformEventEnum type();
}
- 开放平台通知枚举定义
/**
* 开放平台event枚举类
*/
public enum OpenPlatformEventEnum {
/**
* 删除货源
*/
DELETE_CARGO("ymm.cargo.delete");
private String code;
OpenPlatformEventEnum(String code) {
this.code = code;
}
public String getCode() {
return code;
}
public static OpenPlatformEventEnum getEventByCode(String code){
OpenPlatformEventEnum[] values = values();
for (OpenPlatformEventEnum value : values) {
if (value.getCode().equals(code)) {
return value;
}
}
return null;
}
}
- 通知事件处理器定义
public interface EventHandler {
Response invoke(CallBackResultDto callBackResultDto);
}
@Event(type= OpenPlatformEventEnum.DELETE_CARGO)
public class DeleteCargoEventHandlerImpl implements EventHandler {
// b服务的RPC api类
@Autowired
private CallBackFacade callBackFacade;
@Override
public Response invoke(CallBackResultDto callBackResultDto) {
BaseResponse baseResponse = callBackFacade.deleteCargo(callBackResultDto.getEvent(), callBackResultDto.getData());
if (!baseResponse.isSuccess()) {
return Response.newFailResponse(Integer.valueOf(ErrorEnum.SERVER_ERROR.getCode()), baseResponse.getMsg());
}
return Response.SUCCESS;
}
}
- 事件处理管理器定义
@Component
public class EventHandlerManager implements BeanPostProcessor {
private Map<OpenPlatformEventEnum,EventHandler> eventHandlerMap = Maps.newConcurrentMap();
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
return bean;
}
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
// 实例化阶段,过滤掉非EventHandler
if (!(bean instanceof EventHandler)) {
return bean;
}
// 扫描注解,在项目启动阶段检查必要信息是否缺失
Event event = AnnotationUtils.findAnnotation(bean.getClass(), Event.class);
if (event == null) {
throw new BeanCreationException("EventHandler must mark @Event annotation");
}
OpenPlatformEventEnum eventType = event.type();
EventHandler eventHandler = eventHandlerMap.get(eventType);
if(eventHandler == null){
eventHandlerMap.put(eventType,(EventHandler) bean);
}
return bean;
}
public EventHandler getEventHandlerByType(OpenPlatformEventEnum type){
if(type == null){
return null;
}
return eventHandlerMap.get(type);
}
}
- Rest接口api定义
/**
* 开放平台回调处理类
*/
@RestController
@Slf4j
public class OpenPlatformController {
@Autowired
private EventHandlerManager eventHandlerManager;
/**
* 开放平台回调处理方法
* 开放平台的各种通知统一收归到一个入口
* @param request 请求对象
* @return 处理结果
*/
@RequestMapping(value = "/openPlatform/dealCallBack", method = RequestMethod.POST, produces = "application/json; charset=UTF-8")
public OpenPlatformResult dealCallBack(@RequestBody DealCallBackRequest request) {
OpenPlatformResult response = new OpenPlatformResult();
if (request == null || StringUtils.isEmpty(request.getResult())) {
log.error("开放平台返回的请求为空");
return response;
}
String result = request.getResult();
if (request.getEncrypt() == 1) {
try {
result = AESHexUtil.decryptBase64DecorateAES(result, OpenPlatformLionConfig.getOpenPlatformDecryptKey());
} catch (Exception e) {
log.error("开放平台解密失败");
return response;
}
}
CallBackResultDto callBackResultDto = JSON.parseObject(result, CallBackResultDto.class);
log.info("开放平台回调的请求,{}", JSON.toJSONString(callBackResultDto));
if (callBackResultDto == null || StringUtils.isEmpty(callBackResultDto.getEvent())) {
log.error("开放平台转换后的请求对象为空");
return response;
}
// 从枚举类中获取反射的类
EventHandler eventHandler = eventHandlerManager.getEventHandlerByType(OpenPlatformEventEnum.getEventByCode(callBackResultDto.getEvent()));
if (eventHandler == null) {
log.warn("根据开放平台的eventCode没有找到对应的处理类,eventCode:{}", callBackResultDto.getEvent());
return response;
}
// invoke调用各自的业务类
if (reflectCall(callBackResultDto, eventHandler)) {
return response;
}
response.setResult("success");
return response;
}
/**
* 反射到子类调用是否抛异常
*
* @param callBackResultDto 请求参数
* @return 调用结果
*/
private boolean reflectCall(CallBackResultDto callBackResultDto, EventHandler eventHandler) {
try {
Response response = eventHandler.invoke(callBackResultDto);
if (!response.success()) {
log.error("调用失败", response.getMsg());
return true;
}
} catch (Exception e) {
log.error("调用失败", e);
return true;
}
return false;
}
}
a服务与b服务之间是通过RPC调用的。
<bean id="callBackFacade" class="com.dianping.dpsf.spring.ProxyBeanFactory" init-method="init">
<property name="serviceName" value="http://service.tms.com/pigeon/callBackFacade_1.0.0"/>
<property name="iface" value="com.saas.tms.trans.api.facade.CallBackFacade"/>
<property name="serialize" value="hessian"/>
<property name="callMethod" value="sync"/>
<property name="timeout" value="5000"/>
</bean>
b服务中
- 回调处理类
public interface CallBackFacade{
/**
* 删除货源通知
*/
BaseResponse deleteCargo(String event,String data);
}