简介: RPC 拦截器机制在 preHandle、postHandle、exceptionHandle 以及 H5 等场景中的利用
mPaaS 挪动网关服务(Mobile Gateway Service,简称 MGS)作为 mPaas 最重要的组件之一,连贯了挪动客户端与服务端,简化了挪动端与服务端的数据协定和通信协定,从而可能显著晋升开发效率和网络通讯效率。
在咱们日常运维过程中发现,很多用户在应用客户端 RPC 组件的时候,有很多不同场景的诉求,比方拦挡申请增加业务申请标记,免登,返回后果模仿,异样解决,限流等。
本文旨在介绍通过利用 RPC 提供的拦截器机制,通过不同理论场景的形容,供业务参考应用。
RPC 调用原理
当 App 在挪动网关控制台接入后盾服务后,调用 RPC 的示例代码如下:
RpcDemoClient client = MPRpc.getRpcProxy(RpcDemoClient.class);
_// 设置申请_
GetIdGetReq req = new GetIdGetReq();
req.id = "123";
req.age = 14;
req.isMale = true;
_// 发动 rpc 申请_
String response = client.getIdGet(req);
值得好奇的是,整个调用过程中其实咱们并没有去实现 RpcDemoClient 这个接口,而是通过 MPRpc.getRpcProxy 获取了一个代理,通过代理对象实现了调用。在这里其实次要应用了 Java 动静代理的技术。
如下图所示,当调用 RPC 接口的时候,会通过动静代理的 RpcInvocationHandler,回调其实现的 invoke 办法,最终在 invoke 内实现数据的序列化解决最初通过网络库发到服务端。
public <T> T getRpcProxy(Class<T> clazz) {LogCatUtil.info("RpcFactory", "clazz=[" + clazz.getName() + "]");
return Proxy.newProxyInstance(clazz.getClassLoader(), new Class[]{clazz}, new RpcInvocationHandler(this.mConfig, clazz, this.mRpcInvoker));
}
在业务开发中,如果在某些状况下须要管制客户端的网络申请(拦挡网络申请,禁止拜访某些接口,或者限流),能够通过 RPC 拦截器实现。
RpcService rpcService = getMicroApplicationContext().findServiceByInterface(RpcService.class.getName());
rpcService.addRpcInterceptor(OperationType.class, new CommonInterceptor());
拦截器原理
RPC 目前采纳了拦截器机制实现 RPC 的自定义解决,如下图所示,业务能够通过设置自定义 RpcIntercept 实现在申请前,申请异样,申请返回三个阶段对 RPC 的定制解决。
preHandle 场景
1. 全局增加业务自定义申请 header
典型应用场景:业务增加自定义的业务全局标识或者其余统计字段。
@Override
public boolean preHandle(Object proxy,
ThreadLocal<Object> retValue,
byte[] retRawValue,
Class<?> aClass,
Method method,
Object[] args,
Annotation annotation,
ThreadLocal<Map<String, Object>> threadLocal)
throws RpcException {
_//Do something..._
RpcInvocationHandler handler = (RpcInvocationHandler) Proxy.getInvocationHandler(proxy);
handler.getRpcInvokeContext().addRequestHeader("header", "headerCustom");
return true;
}
2. 阻断以后申请 rpc 流程
典型应用场景:比方如果以后未登录,对须要登录的 rpc 先阻断,对立提醒登录。
@Override
public boolean preHandle(Object proxy,
ThreadLocal<Object> retValue,
byte[] retRawValue,
Class<?> aClass,
Method method,
Object[] args,
Annotation annotation,
ThreadLocal<Map<String, Object>> threadLocal)
throws RpcException {
_//Do something..._
String operationType = getOperationType(aClass, method, args);
if ("operationType1".equals(operationType)) {
boolean isLogin = false;
if (!isLogin) {Handler handler = new Handler(Looper.getMainLooper());
handler.post(new Runnable() {
@Override
public void run() {Toast.makeText(LauncherApplicationAgent.getInstance().getApplicationContext(),
"以后未登录,请登录", Toast.LENGTH_SHORT).show();}
});
_// 返回给下层调用登录失败的异样,下层做业务解决_
throw new RpcException(RpcException.ErrorCode.CLIENT_LOGIN_FAIL_ERROR, "login fail.");
}
}
return true;
}
private String getOperationType(Class<?> aClass, Method method, Object[] args) {if (aClass == null || null == method) return "";
OperationType operationType = method.getAnnotation(OperationType.class);
return operationType == null ? "" : operationType.value();}
postHandle 场景
1. 拦挡接口返回
典型应用场景:全局批改服务端的返回后果,比方 mock 服务端的数据。
@Override
public boolean postHandle(Object proxy,
ThreadLocal<Object> threadLocal,
byte[] retRawValue,
Class<?> aClass,
Method method,
Object[] args,
Annotation annotation) throws RpcException _{
//Do something...
// 场景:批改服务端返回的数据,比方 mock 数据,或者批改服务端数据
String operationType = getOperationType(aClass, method, args);
LoggerFactory.getTraceLogger().debug(TAG, "postHandle:" + operationType);
if ("operationType1".equals(operationType)) {String value = JSON.parse(retRawValue).toString();
LoggerFactory.getTraceLogger().debug(TAG, "postHandle 原始返回" + value);
String mockData = "{"img":"imgPath","User":{"name":" 我是 mock 的数据 ","age":18}_}";
Object mockObj = JSON.parseObject(mockData, method.getReturnType());
threadLocal.set(mockObj);
return true;
}
return true;
}
private String getOperationType(Class<?> aClass, Method method, Object[] args) _{if (aClass == null || null == method) return "";
OperationType operationType = method.getAnnotation(OperationType.class);
return operationType == null ? "" : operationType.value();}_
exceptionHandle 场景
1. 异样对立解决
比方登录态生效,服务端会对立返回 2000 的错误码,客户端能够在 exceptionHandle 里对立拦挡进行登录态免登操作。
@Override
public boolean exceptionHandle(Object proxy, ThreadLocal<Object> retValue, byte[] bytes, Class<?> aClass, Method method, Object[] objects,
RpcException rpcException, Annotation annotation) throws RpcException {String operationType = getOperationType(aClass, method, objects);
if (RpcException.ErrorCode.CLIENT_LOGIN_FAIL_ERROR == rpcException.getCode()
&& "operationType1".equals(operationType)) {
// 1. 去免登
hasLogin = true;
// 2. 免登后在帮下层重发申请,免登操作对下层业务无感知
try {LoggerFactory.getTraceLogger().debug(TAG, "exceptionHandle. Start resend rpc begin" + operationType);
// 重发申请
Object object = method.invoke(proxy, objects);
retValue.set(object);
LoggerFactory.getTraceLogger().debug(TAG, "exceptionHandle. Start resend rpc success");
return false;
} catch (Throwable e) {LoggerFactory.getTraceLogger().error(TAG, "resend rpc occurs illegal argument exception", e);
throw new RpcException(RpcException.ErrorCode.CLIENT_HANDLE_ERROR, e + "");
}
}
return true;
}
H5 场景
因为 H5 场景中应用的 jsapi 的 rpc,须要反对在 js 环境里传递到 native 环境,所以在设计上,是对立通过 operationType: alipay.client.executerpc 接口进行的转发,所以针对 H5 发送的 RPC 申请,须要做非凡判断,通过入参拿到实在的 operationType 接口,示例代码如下。
1. 获取 H5 申请的接口名称和入参
var params = [{"_requestBody":{"userName":"","userId":0}
}]
var operationType = 'alipay.mobile.ic.dispatch'
AlipayJSBridge.call('rpc', {
operationType: operationType,
requestData: params,
headers:{}}, function (result) {console.log(result);
});
如上图所示,业务通过 jsapi 去申请 rpc,如何获取 jsapi 申请的 rpc 名称,能够参考代码如下:
@Override
public boolean preHandle(Object o, ThreadLocal<Object> threadLocal, byte[] bytes, Class<?> aClass, Method method, Object[] objects, Annotation annotation, ThreadLocal<Map<String, Object>> threadLocal1) throws RpcException {String operationType = getOperationType(aClass, method, objects);
if ("alipay.client.executerpc".equals(operationType)) {
_// H5 的 rpc 名称_
String rpcName = (String) objects[0];
_// 入参_
String req = (String) objects[1];
LoggerFactory.getTraceLogger().debug(TAG, "operationType:" + rpcName + " " + req);
} else {_// Native 的 rpc_}
LoggerFactory.getTraceLogger().debug(TAG, "operationType:" + operationType);
return true;
}
原文链接
本文为阿里云原创内容,未经容许不得转载。