實現(xiàn)要求:提供RPC上下文,客戶端可以透傳數(shù)據(jù)給服務端呐伞。
一個應用的基本設計包含幾個重要的角色:
- 實體域
- 會話域
- 服務域
實體域就是應用中抽象出來的組件为严,如Spring中的Bean宫屠,Mybatis中的MappedStatement等匪煌。會話域代表著一次交互過程,如Mybatis中的SqlSession裆悄,而服務域就是組件的功能集矛纹,負責會話域和實體域的生命周期管理,如Spring的ApplicationContext光稼,Shiro的SecurityManager等或南。
現(xiàn)在構造我們這個rpc demo中的會話域角色RpcContext。其主要職責就是負責在一次會話生命周期內(nèi)钟哥,保存迎献、傳遞相關參數(shù)數(shù)據(jù)。會話中的執(zhí)行體就是線程了腻贰,那么理所應當?shù)腞pcContext就應該和當前執(zhí)行線程綁定。很容易想到了ThreadLocal扒秸!
實現(xiàn)如下:
/**
* rpc上下文
*
* @author wqx
*
*/
public class RpcContext {
private static ThreadLocal<Map<String,Object>> context = new ThreadLocal<Map<String,Object> >(){
@Override
protected Map<String,Object> initialValue() {
Map<String,Object> m = new HashMap<String,Object>();
return m;
}
};
public static void addAttribute(String key, Object value){
context.get().put(key,value);
}
public static Object getAttribute(String key){
return context.get().get(key);
}
public static Map<String,Object> getAttributes(){
return Collections.unmodifiableMap(context.get());
}
}
RpcContext中的數(shù)據(jù)傳輸載體還是選擇RpcRequest播演,將其包在請求體中即可。如下:
public class RpcRequest implements Serializable
{
/**
*
*/
private static final long serialVersionUID = -7102839100899303105L;
//方法名
private String methodName;
//參數(shù)類型
private Class<?>[] parameterTypes;
//參數(shù)列表
private Object[] args;
//參數(shù)
private Map<String,Object> context;
RpcBuilder中在發(fā)送請求前伴奥,需要從當前上下文中獲取數(shù)據(jù)写烤。傳入RpcRequest對象。如下:
RpcRequest request = new RpcRequest(method.getName(), method.getParameterTypes(),args,RpcContext.getAttributes());
同理拾徙,在服務端接收到請求對象RpcRequest之后洲炊,需要將RpcRequest中傳來的數(shù)據(jù)和當前上下文進行關聯(lián)。Handler的run方法修改如下:
public void run() {
try{
ObjectInputStream in = null;
ObjectOutputStream out = null;
RpcResponse response = new RpcResponse();
try {
in = new ObjectInputStream(socket.getInputStream());
out = new ObjectOutputStream(socket.getOutputStream());
Object req = in.readObject();
if(req instanceof RpcRequest){
RpcRequest rpcRequest = (RpcRequest)req;
//關聯(lián)客戶端傳來的上下文數(shù)據(jù)
RpcContext.context.set(rpcRequest.getContext());
Method method = service.getClass().getMethod(rpcRequest.getMethodName(), rpcRequest.getParameterTypes());
//尼啡。暂衡。。
}
測試:
業(yè)務接口增加測試方法:
public interface UserService {
/**
* 上下文測試崖瞭,透明傳輸數(shù)據(jù)
*/
public Map<String,Object> rpcContextTest();
//狂巢。。书聚。
}
public class UserServiceImpl implements UserService {
@Override
public Map<String,Object> rpcContextTest() {
Map<String,Object> map = new HashMap<String,Object>();
map.put("server","hahaha");
if(RpcContext.getAttributes() != null )
map.putAll(RpcContext.getAttributes());
return map;
}
//唧领。藻雌。。
}
客戶端測試代碼:
@Test
public void testRpcContext(){
RpcContext.addAttribute("client","huhuhu");
Map<String, Object> res = userService.rpcContextTest();
Assert.assertEquals(res.get("server"),"hahaha");
Assert.assertEquals(res.get("client"),"huhuhu");
}