遠端呼叫——thrift協議
目標:介紹thrift協議的設計和實現,介紹dubbo-rpc-thrift的原始碼。
前言
dubbo整合thrift協議,是基於Thrift來實現的,Thrift是一種輕量級,與語言無關的軟體堆疊,具有用於點對點RPC的相關程式碼生成機制。Thrift為資料傳輸,資料序列化和應用程式級處理提供了清晰的抽象。程式碼生成系統採用簡單的定義語言作為輸入,並跨程式語言生成程式碼,使用抽象堆疊構建可互操作的RPC客戶端和伺服器。
原始碼分析
(一)MultiServiceProcessor
該類對輸入流進行操作並寫入某些輸出流。它實現了TProcessor介面,關鍵的方法是process。
@Override
public boolean process(TProtocol in, TProtocol out) throws TException {
// 獲得十六進位制的魔數
short magic = in.readI16();
// 如果不是規定的魔數,則列印錯誤日誌,返回false
if (magic != ThriftCodec.MAGIC) {
logger.error("Unsupported magic " + magic);
return false;
}
// 獲得三十二進位制魔數
in.readI32();
// 獲得十六進位制魔數
in.readI16();
// 獲得版本
byte version = in.readByte();
// 獲得服務名
String serviceName = in.readString();
// 獲得id
long id = in.readI64();
ByteArrayOutputStream bos = new ByteArrayOutputStream(1024);
// 建立基礎運輸TIOStreamTransport物件
TIOStreamTransport transport = new TIOStreamTransport(bos);
// 獲得協議
TProtocol protocol = protocolFactory.getProtocol(transport);
// 從集合中取出處理器
TProcessor processor = processorMap.get(serviceName);
// 如果處理器為空,則列印錯誤,返回false
if (processor == null) {
logger.error("Could not find processor for service " + serviceName);
return false;
}
// todo if exception
// 獲得結果
boolean result = processor.process(in, protocol);
ByteArrayOutputStream header = new ByteArrayOutputStream(512);
// 協議頭的傳輸器
TIOStreamTransport headerTransport = new TIOStreamTransport(header);
TProtocol headerProtocol = protocolFactory.getProtocol(headerTransport);
// 寫入16進位制的魔數
headerProtocol.writeI16(magic);
// 寫入32進位制的Integer最大值
headerProtocol.writeI32(Integer.MAX_VALUE);
// 寫入Short最大值的16進位制
headerProtocol.writeI16(Short.MAX_VALUE);
// 寫入版本號
headerProtocol.writeByte(version);
// 寫入服務名
headerProtocol.writeString(serviceName);
// 寫入id
headerProtocol.writeI64(id);
// 輸出
headerProtocol.getTransport().flush();
out.writeI16(magic);
out.writeI32(bos.size() + header.size());
out.writeI16((short) (0xffff & header.size()));
out.writeByte(version);
out.writeString(serviceName);
out.writeI64(id);
out.getTransport().write(bos.toByteArray());
out.getTransport().flush();
return result;
}
(二)RandomAccessByteArrayOutputStream
該類是隨機訪問陣列的輸出流,比較簡單,我就不多敘述,有興趣的可以直接看原始碼,不看影響也不大。
(三)ClassNameGenerator
@SPI(DubboClassNameGenerator.NAME)
public interface ClassNameGenerator {
/**
* 生成引數的類名
*/
public String generateArgsClassName(String serviceName, String methodName);
/**
* 生成結果的類名
* @param serviceName
* @param methodName
* @return
*/
public String generateResultClassName(String serviceName, String methodName);
}
該介面是是可擴充套件介面,定義了兩個方法。有兩個實現類,下面講述。
(四)DubboClassNameGenerator
該類實現了ClassNameGenerator介面,是dubbo相關的類名生成實現。
public class DubboClassNameGenerator implements ClassNameGenerator {
public static final String NAME = "dubbo";
@Override
public String generateArgsClassName(String serviceName, String methodName) {
return ThriftUtils.generateMethodArgsClassName(serviceName, methodName);
}
@Override
public String generateResultClassName(String serviceName, String methodName) {
return ThriftUtils.generateMethodResultClassName(serviceName, methodName);
}
}
(五)ThriftClassNameGenerator
該類實現了ClassNameGenerator介面,是Thrift相關的類名生成實現。
public class ThriftClassNameGenerator implements ClassNameGenerator {
public static final String NAME = "thrift";
@Override
public String generateArgsClassName(String serviceName, String methodName) {
return ThriftUtils.generateMethodArgsClassNameThrift(serviceName, methodName);
}
@Override
public String generateResultClassName(String serviceName, String methodName) {
return ThriftUtils.generateMethodResultClassNameThrift(serviceName, methodName);
}
}
以上兩個都呼叫了ThriftUtils中的方法。
(六)ThriftUtils
該類中封裝的方法比較簡單,就一些字串的拼接,有興趣的可以直接檢視我下面貼出來的註釋連線。
(七)ThriftCodec
該類是基於Thrift實現的編解碼器。 這裡需要大家看一下該類的註釋,關於協議的資料:
* |<- message header ->|<- message body ->|
* +----------------+----------------------+------------------+---------------------------+------------------+
* | magic (2 bytes)|message size (4 bytes)|head size(2 bytes)| version (1 byte) | header | message body |
* +----------------+----------------------+------------------+---------------------------+------------------+
* |<-
1.屬性
/**
* 訊息長度索引
*/
public static final int MESSAGE_LENGTH_INDEX = 2;
/**
* 訊息頭長度索引
*/
public static final int MESSAGE_HEADER_LENGTH_INDEX = 6;
/**
* 訊息最短長度
*/
public static final int MESSAGE_SHORTEST_LENGTH = 10;
public static final String NAME = "thrift";
/**
* 類名生成引數
*/
public static final String PARAMETER_CLASS_NAME_GENERATOR = "class.name.generator";
/**
* 版本
*/
public static final byte VERSION = (byte) 1;
/**
* 魔數
*/
public static final short MAGIC = (short) 0xdabc;
/**
* 請求引數集合
*/
static final ConcurrentMap<Long, RequestData> cachedRequest =
new ConcurrentHashMap<Long, RequestData>();
/**
* thrift序列號
*/
private static final AtomicInteger THRIFT_SEQ_ID = new AtomicInteger(0);
/**
* 類快取
*/
private static final ConcurrentMap<String, Class<?>> cachedClass =
new ConcurrentHashMap<String, Class<?>>();
2.encode
@Override
public void encode(Channel channel, ChannelBuffer buffer, Object message)
throws IOException {
// 如果訊息是Request型別
if (message instanceof Request) {
// Request型別訊息編碼
encodeRequest(channel, buffer, (Request) message);
} else if (message instanceof Response) {
// Response型別訊息編碼
encodeResponse(channel, buffer, (Response) message);
} else {
throw new UnsupportedOperationException("Thrift codec only support encode "
+ Request.class.getName() + " and " + Response.class.getName());
}
}
該方法是編碼的邏輯,具體的編碼操作根據請求型別不同分別呼叫不同的方法。
3.encodeRequest
private void encodeRequest(Channel channel, ChannelBuffer buffer, Request request)
throws IOException {
// 獲得會話域
RpcInvocation inv = (RpcInvocation) request.getData();
// 獲得下一個id
int seqId = nextSeqId();
// 獲得服務名
String serviceName = inv.getAttachment(Constants.INTERFACE_KEY);
// 如果是空的 則丟擲異常
if (StringUtils.isEmpty(serviceName)) {
throw new IllegalArgumentException("Could not find service name in attachment with key "
+ Constants.INTERFACE_KEY);
}
// 建立TMessage物件
TMessage message = new TMessage(
inv.getMethodName(),
TMessageType.CALL,
seqId);
// 獲得方法引數
String methodArgs = ExtensionLoader.getExtensionLoader(ClassNameGenerator.class)
.getExtension(channel.getUrl().getParameter(ThriftConstants.CLASS_NAME_GENERATOR_KEY, ThriftClassNameGenerator.NAME))
.generateArgsClassName(serviceName, inv.getMethodName());
// 如果是空,則丟擲異常
if (StringUtils.isEmpty(methodArgs)) {
throw new RpcException(RpcException.SERIALIZATION_EXCEPTION,
"Could not encode request, the specified interface may be incorrect.");
}
// 從快取中取出型別
Class<?> clazz = cachedClass.get(methodArgs);
if (clazz == null) {
try {
// 重新獲得型別
clazz = ClassHelper.forNameWithThreadContextClassLoader(methodArgs);
// 加入快取
cachedClass.putIfAbsent(methodArgs, clazz);
} catch (ClassNotFoundException e) {
throw new RpcException(RpcException.SERIALIZATION_EXCEPTION, e.getMessage(), e);
}
}
// 生成的Thrift物件的通用基介面
TBase args;
try {
args = (TBase) clazz.newInstance();
} catch (InstantiationException e) {
throw new RpcException(RpcException.SERIALIZATION_EXCEPTION, e.getMessage(), e);
} catch (IllegalAccessException e) {
throw new RpcException(RpcException.SERIALIZATION_EXCEPTION, e.getMessage(), e);
}
// 遍歷引數
for (int i = 0; i < inv.getArguments().length; i++) {
Object obj = inv.getArguments()[i];
if (obj == null) {
continue;
}
TFieldIdEnum field = args.fieldForId(i + 1);
// 生成set方法名
String setMethodName = ThriftUtils.generateSetMethodName(field.getFieldName());
Method method;
try {
// 獲得方法
method = clazz.getMethod(setMethodName, inv.getParameterTypes()[i]);
} catch (NoSuchMethodException e) {
throw new RpcException(RpcException.SERIALIZATION_EXCEPTION, e.getMessage(), e);
}
try {
// 呼叫下一個呼叫鏈
method.invoke(args, obj);
} catch (IllegalAccessException e) {
throw new RpcException(RpcException.SERIALIZATION_EXCEPTION, e.getMessage(), e);
} catch (InvocationTargetException e) {
throw new RpcException(RpcException.SERIALIZATION_EXCEPTION, e.getMessage(), e);
}
}
// 建立一個隨機訪問陣列輸出流
RandomAccessByteArrayOutputStream bos = new RandomAccessByteArrayOutputStream(1024);
// 建立傳輸器
TIOStreamTransport transport = new TIOStreamTransport(bos);
// 建立協議
TBinaryProtocol protocol = new TBinaryProtocol(transport);
int headerLength, messageLength;
byte[] bytes = new byte[4];
try {
// 開始編碼
// magic
protocol.writeI16(MAGIC);
// message length placeholder
protocol.writeI32(Integer.MAX_VALUE);
// message header length placeholder
protocol.writeI16(Short.MAX_VALUE);
// version
protocol.writeByte(VERSION);
// service name
protocol.writeString(serviceName);
// dubbo request id
protocol.writeI64(request.getId());
protocol.getTransport().flush();
// header size
headerLength = bos.size();
// 對body內容進行編碼
// message body
protocol.writeMessageBegin(message);
args.write(protocol);
protocol.writeMessageEnd();
protocol.getTransport().flush();
int oldIndex = messageLength = bos.size();
// fill in message length and header length
try {
TFramedTransport.encodeFrameSize(messageLength, bytes);
bos.setWriteIndex(MESSAGE_LENGTH_INDEX);
protocol.writeI32(messageLength);
bos.setWriteIndex(MESSAGE_HEADER_LENGTH_INDEX);
protocol.writeI16((short) (0xffff & headerLength));
} finally {
bos.setWriteIndex(oldIndex);
}
} catch (TException e) {
throw new RpcException(RpcException.SERIALIZATION_EXCEPTION, e.getMessage(), e);
}
buffer.writeBytes(bytes);
buffer.writeBytes(bos.toByteArray());
}
該方法是對request型別的訊息進行編碼。
4.encodeResponse
private void encodeResponse(Channel channel, ChannelBuffer buffer, Response response)
throws IOException {
// 獲得結果
RpcResult result = (RpcResult) response.getResult();
// 獲得請求
RequestData rd = cachedRequest.get(response.getId());
// 獲得結果的類名
String resultClassName = ExtensionLoader.getExtensionLoader(ClassNameGenerator.class).getExtension(
channel.getUrl().getParameter(ThriftConstants.CLASS_NAME_GENERATOR_KEY, ThriftClassNameGenerator.NAME))
.generateResultClassName(rd.serviceName, rd.methodName);
// 如果為空,則序列化失敗
if (StringUtils.isEmpty(resultClassName)) {
throw new RpcException(RpcException.SERIALIZATION_EXCEPTION,
"Could not encode response, the specified interface may be incorrect.");
}
// 獲得型別
Class clazz = cachedClass.get(resultClassName);
// 如果為空,則重新獲取
if (clazz == null) {
try {
clazz = ClassHelper.forNameWithThreadContextClassLoader(resultClassName);
cachedClass.putIfAbsent(resultClassName, clazz);
} catch (ClassNotFoundException e) {
throw new RpcException(RpcException.SERIALIZATION_EXCEPTION, e.getMessage(), e);
}
}
TBase resultObj;
try {
// 載入該類
resultObj = (TBase) clazz.newInstance();
} catch (InstantiationException e) {
throw new RpcException(RpcException.SERIALIZATION_EXCEPTION, e.getMessage(), e);
} catch (IllegalAccessException e) {
throw new RpcException(RpcException.SERIALIZATION_EXCEPTION, e.getMessage(), e);
}
TApplicationException applicationException = null;
TMessage message;
// 如果結果有異常丟擲
if (result.hasException()) {
Throwable throwable = result.getException();
int index = 1;
boolean found = false;
while (true) {
TFieldIdEnum fieldIdEnum = resultObj.fieldForId(index++);
if (fieldIdEnum == null) {
break;
}
String fieldName = fieldIdEnum.getFieldName();
String getMethodName = ThriftUtils.generateGetMethodName(fieldName);
String setMethodName = ThriftUtils.generateSetMethodName(fieldName);
Method getMethod;
Method setMethod;
try {
// 獲得get方法
getMethod = clazz.getMethod(getMethodName);
// 如果返回型別和異常型別一樣,則建立set方法,並且呼叫下一個呼叫鏈
if (getMethod.getReturnType().equals(throwable.getClass())) {
found = true;
setMethod = clazz.getMethod(setMethodName, throwable.getClass());
setMethod.invoke(resultObj, throwable);
}
} catch (NoSuchMethodException e) {
throw new RpcException(RpcException.SERIALIZATION_EXCEPTION, e.getMessage(), e);
} catch (InvocationTargetException e) {
throw new RpcException(RpcException.SERIALIZATION_EXCEPTION, e.getMessage(), e);
} catch (IllegalAccessException e) {
throw new RpcException(RpcException.SERIALIZATION_EXCEPTION, e.getMessage(), e);
}
}
if (!found) {
// 建立TApplicationException異常
applicationException = new TApplicationException(throwable.getMessage());
}
} else {
// 獲得真實的結果
Object realResult = result.getResult();
// result field id is 0
String fieldName = resultObj.fieldForId(0).getFieldName();
String setMethodName = ThriftUtils.generateSetMethodName(fieldName);
String getMethodName = ThriftUtils.generateGetMethodName(fieldName);
Method getMethod;
Method setMethod;
try {
// 建立get和set方法
getMethod = clazz.getMethod(getMethodName);
setMethod = clazz.getMethod(setMethodName, getMethod.getReturnType());
setMethod.invoke(resultObj, realResult);
} catch (NoSuchMethodException e) {
throw new RpcException(RpcException.SERIALIZATION_EXCEPTION, e.getMessage(), e);
} catch (InvocationTargetException e) {
throw new RpcException(RpcException.SERIALIZATION_EXCEPTION, e.getMessage(), e);
} catch (IllegalAccessException e) {
throw new RpcException(RpcException.SERIALIZATION_EXCEPTION, e.getMessage(), e);
}
}
if (applicationException != null) {
message = new TMessage(rd.methodName, TMessageType.EXCEPTION, rd.id);
} else {
message = new TMessage(rd.methodName, TMessageType.REPLY, rd.id);
}
RandomAccessByteArrayOutputStream bos = new RandomAccessByteArrayOutputStream(1024);
TIOStreamTransport transport = new TIOStreamTransport(bos);
TBinaryProtocol protocol = new TBinaryProtocol(transport);
int messageLength;
int headerLength;
//編碼
byte[] bytes = new byte[4];
try {
// magic
protocol.writeI16(MAGIC);
// message length
protocol.writeI32(Integer.MAX_VALUE);
// message header length
protocol.writeI16(Short.MAX_VALUE);
// version
protocol.writeByte(VERSION);
// service name
protocol.writeString(rd.serviceName);
// id
protocol.writeI64(response.getId());
protocol.getTransport().flush();
headerLength = bos.size();
// message
protocol.writeMessageBegin(message);
switch (message.type) {
case TMessageType.EXCEPTION:
applicationException.write(protocol);
break;
case TMessageType.REPLY:
resultObj.write(protocol);
break;
}
protocol.writeMessageEnd();
protocol.getTransport().flush();
int oldIndex = messageLength = bos.size();
try {
TFramedTransport.encodeFrameSize(messageLength, bytes);
bos.setWriteIndex(MESSAGE_LENGTH_INDEX);
protocol.writeI32(messageLength);
bos.setWriteIndex(MESSAGE_HEADER_LENGTH_INDEX);
protocol.writeI16((short) (0xffff & headerLength));
} finally {
bos.setWriteIndex(oldIndex);
}
} catch (TException e) {
throw new RpcException(RpcException.SERIALIZATION_EXCEPTION, e.getMessage(), e);
}
buffer.writeBytes(bytes);
buffer.writeBytes(bos.toByteArray());
}
該方法是對response型別的請求訊息進行編碼。
5.decode
@Override
public Object decode(Channel channel, ChannelBuffer buffer) throws IOException {
int available = buffer.readableBytes();
// 如果小於最小的長度,則還需要更多的輸入
if (available < MESSAGE_SHORTEST_LENGTH) {
return DecodeResult.NEED_MORE_INPUT;
} else {
TIOStreamTransport transport = new TIOStreamTransport(new ChannelBufferInputStream(buffer));
TBinaryProtocol protocol = new TBinaryProtocol(transport);
short magic;
int messageLength;
// 對協議頭中的魔數進行比對
try {
// protocol.readI32(); // skip the first message length
byte[] bytes = new byte[4];
transport.read(bytes, 0, 4);
magic = protocol.readI16();
messageLength = protocol.readI32();
} catch (TException e) {
throw new IOException(e.getMessage(), e);
}
if (MAGIC != magic) {
throw new IOException("Unknown magic code " + magic);
}
if (available < messageLength) {
return DecodeResult.NEED_MORE_INPUT;
}
return decode(protocol);
}
}
/**
* 解碼
* @param protocol
* @return
* @throws IOException
*/
private Object decode(TProtocol protocol)
throws IOException {
// version
String serviceName;
long id;
TMessage message;
try {
// 讀取協議頭中對內容
protocol.readI16();
protocol.readByte();
serviceName = protocol.readString();
id = protocol.readI64();
message = protocol.readMessageBegin();
} catch (TException e) {
throw new IOException(e.getMessage(), e);
}
// 如果是回撥
if (message.type == TMessageType.CALL) {
RpcInvocation result = new RpcInvocation();
// 設定服務名和方法名
result.setAttachment(Constants.INTERFACE_KEY, serviceName);
result.setMethodName(message.name);
String argsClassName = ExtensionLoader.getExtensionLoader(ClassNameGenerator.class)
.getExtension(ThriftClassNameGenerator.NAME).generateArgsClassName(serviceName, message.name);
if (StringUtils.isEmpty(argsClassName)) {
throw new RpcException(RpcException.SERIALIZATION_EXCEPTION,
"The specified interface name incorrect.");
}
// 從快取中獲得class類
Class clazz = cachedClass.get(argsClassName);
if (clazz == null) {
try {
// 重新獲得class型別
clazz = ClassHelper.forNameWithThreadContextClassLoader(argsClassName);
// 加入集合
cachedClass.putIfAbsent(argsClassName, clazz);
} catch (ClassNotFoundException e) {
throw new RpcException(RpcException.SERIALIZATION_EXCEPTION, e.getMessage(), e);
}
}
TBase args;
try {
args = (TBase) clazz.newInstance();
} catch (InstantiationException e) {
throw new RpcException(RpcException.SERIALIZATION_EXCEPTION, e.getMessage(), e);
} catch (IllegalAccessException e) {
throw new RpcException(RpcException.SERIALIZATION_EXCEPTION, e.getMessage(), e);
}
try {
args.read(protocol);
protocol.readMessageEnd();
} catch (TException e) {
throw new RpcException(RpcException.SERIALIZATION_EXCEPTION, e.getMessage(), e);
}
// 引數集合
List<Object> parameters = new ArrayList<Object>();
// 引數型別集合
List<Class<?>> parameterTypes = new ArrayList<Class<?>>();
int index = 1;
while (true) {
TFieldIdEnum fieldIdEnum = args.fieldForId(index++);
if (fieldIdEnum == null) {
break;
}
String fieldName = fieldIdEnum.getFieldName();
// 獲得方法名
String getMethodName = ThriftUtils.generateGetMethodName(fieldName);
Method getMethod;
try {
getMethod = clazz.getMethod(getMethodName);
} catch (NoSuchMethodException e) {
throw new RpcException(
RpcException.SERIALIZATION_EXCEPTION, e.getMessage(), e);
}
// 加入引數型別
parameterTypes.add(getMethod.getReturnType());
try {
parameters.add(getMethod.invoke(args));
} catch (IllegalAccessException e) {
throw new RpcException(
RpcException.SERIALIZATION_EXCEPTION, e.getMessage(), e);
} catch (InvocationTargetException e) {
throw new RpcException(
RpcException.SERIALIZATION_EXCEPTION, e.getMessage(), e);
}
}
// 設定引數
result.setArguments(parameters.toArray());
// 設定引數型別
result.setParameterTypes(parameterTypes.toArray(new Class[parameterTypes.size()]));
// 建立一個新的請求
Request request = new Request(id);
// 把結果放入請求中
request.setData(result);
// 放入集合中
cachedRequest.putIfAbsent(id,
RequestData.create(message.seqid, serviceName, message.name));
return request;
// 如果是丟擲異常
} else if (message.type == TMessageType.EXCEPTION) {
TApplicationException exception;
try {
// 讀取異常
exception = TApplicationException.read(protocol);
protocol.readMessageEnd();
} catch (TException e) {
throw new IOException(e.getMessage(), e);
}
// 建立結果
RpcResult result = new RpcResult();
// 設定異常
result.setException(new RpcException(exception.getMessage()));
// 建立Response響應
Response response = new Response();
// 把結果放入
response.setResult(result);
// 加入唯一id
response.setId(id);
return response;
// 如果型別是回應
} else if (message.type == TMessageType.REPLY) {
// 獲得結果的類名
String resultClassName = ExtensionLoader.getExtensionLoader(ClassNameGenerator.class)
.getExtension(ThriftClassNameGenerator.NAME).generateResultClassName(serviceName, message.name);
if (StringUtils.isEmpty(resultClassName)) {
throw new IllegalArgumentException("Could not infer service result class name from service name "
+ serviceName + ", the service name you specified may not generated by thrift idl compiler");
}
// 獲得class型別
Class<?> clazz = cachedClass.get(resultClassName);
// 如果為空,則重新獲取
if (clazz == null) {
try {
clazz = ClassHelper.forNameWithThreadContextClassLoader(resultClassName);
cachedClass.putIfAbsent(resultClassName, clazz);
} catch (ClassNotFoundException e) {
throw new RpcException(RpcException.SERIALIZATION_EXCEPTION, e.getMessage(), e);
}
}
TBase<?, ? extends TFieldIdEnum> result;
try {
result = (TBase<?, ?>) clazz.newInstance();
} catch (InstantiationException e) {
throw new RpcException(RpcException.SERIALIZATION_EXCEPTION, e.getMessage(), e);
} catch (IllegalAccessException e) {
throw new RpcException(RpcException.SERIALIZATION_EXCEPTION, e.getMessage(), e);
}
try {
result.read(protocol);
protocol.readMessageEnd();
} catch (TException e) {
throw new RpcException(RpcException.SERIALIZATION_EXCEPTION, e.getMessage(), e);
}
Object realResult = null;
int index = 0;
while (true) {
TFieldIdEnum fieldIdEnum = result.fieldForId(index++);
if (fieldIdEnum == null) {
break;
}
Field field;
try {
field = clazz.getDeclaredField(fieldIdEnum.getFieldName());
field.setAccessible(true);
} catch (NoSuchFieldException e) {
throw new RpcException(RpcException.SERIALIZATION_EXCEPTION, e.getMessage(), e);
}
try {
// 獲得真實的結果
realResult = field.get(result);
} catch (IllegalAccessException e) {
throw new RpcException(RpcException.SERIALIZATION_EXCEPTION, e.getMessage(), e);
}
if (realResult != null) {
break;
}
}
// 建立響應
Response response = new Response();
// 設定唯一id
response.setId(id);
// 建立結果
RpcResult rpcResult = new RpcResult();
// 用RpcResult包裹結果
if (realResult instanceof Throwable) {
rpcResult.setException((Throwable) realResult);
} else {
rpcResult.setValue(realResult);
}
// 設定結果
response.setResult(rpcResult);
return response;
} else {
// Impossible
throw new IOException();
}
}
該方法是對解碼的邏輯。對於訊息分為REPLY、EXCEPTION和CALL三種情況來分別進行解碼。
6.RequestData
static class RequestData {
/**
* 請求id
*/
int id;
/**
* 服務名
*/
String serviceName;
/**
* 方法名
*/
String methodName;
static RequestData create(int id, String sn, String mn) {
RequestData result = new RequestData();
result.id = id;
result.serviceName = sn;
result.methodName = mn;
return result;
}
}
該內部類是請求引數實體。
(八)ThriftInvoker
該類是thrift協議的Invoker實現。
1.屬性
/**
* 客戶端集合
*/
private final ExchangeClient[] clients;
/**
* 活躍的客戶端索引
*/
private final AtomicPositiveInteger index = new AtomicPositiveInteger();
/**
* 銷燬鎖
*/
private final ReentrantLock destroyLock = new ReentrantLock();
/**
* invoker集合
*/
private final Set<Invoker<?>> invokers;
2.doInvoke
@Override
protected Result doInvoke(Invocation invocation) throws Throwable {
RpcInvocation inv = (RpcInvocation) invocation;
final String methodName;
// 獲得方法名
methodName = invocation.getMethodName();
// 設定附加值 path
inv.setAttachment(Constants.PATH_KEY, getUrl().getPath());
// for thrift codec
inv.setAttachment(ThriftCodec.PARAMETER_CLASS_NAME_GENERATOR, getUrl().getParameter(
ThriftCodec.PARAMETER_CLASS_NAME_GENERATOR, DubboClassNameGenerator.NAME));
ExchangeClient currentClient;
// 如果只有一個連線的客戶端,則直接返回
if (clients.length == 1) {
currentClient = clients[0];
} else {
// 否則,取出下一個客戶端,迴圈陣列取
currentClient = clients[index.getAndIncrement() % clients.length];
}
try {
// 獲得超時時間
int timeout = getUrl().getMethodParameter(
methodName, Constants.TIMEOUT_KEY, Constants.DEFAULT_TIMEOUT);
RpcContext.getContext().setFuture(null);
// 發起請求
return (Result) currentClient.request(inv, timeout).get();
} catch (TimeoutException e) {
// 丟擲超時異常
throw new RpcException(RpcException.TIMEOUT_EXCEPTION, e.getMessage(), e);
} catch (RemotingException e) {
// 丟擲網路異常
throw new RpcException(RpcException.NETWORK_EXCEPTION, e.getMessage(), e);
}
}
該方法是thrift協議的呼叫鏈處理邏輯。
(九)ThriftProtocol
該類是thrift協議的主要實現邏輯,分別實現了服務引用和服務呼叫的邏輯。
1.屬性
/**
* 預設埠號
*/
public static final int DEFAULT_PORT = 40880;
/**
* 副檔名
*/
public static final String NAME = "thrift";
// ip:port -> ExchangeServer
/**
* 服務集合,key為ip:port
*/
private final ConcurrentMap<String, ExchangeServer> serverMap =
new ConcurrentHashMap<String, ExchangeServer>();
private ExchangeHandler handler = new ExchangeHandlerAdapter() {
@Override
public Object reply(ExchangeChannel channel, Object msg) throws RemotingException {
// 如果訊息是Invocation型別的
if (msg instanceof Invocation) {
Invocation inv = (Invocation) msg;
// 獲得服務名
String serviceName = inv.getAttachments().get(Constants.INTERFACE_KEY);
// 獲得服務的key
String serviceKey = serviceKey(channel.getLocalAddress().getPort(),
serviceName, null, null);
// 從集合中獲得暴露者
DubboExporter<?> exporter = (DubboExporter<?>) exporterMap.get(serviceKey);
// 如果暴露者為空,則丟擲異常
if (exporter == null) {
throw new RemotingException(channel,
"Not found exported service: "
+ serviceKey
+ " in "
+ exporterMap.keySet()
+ ", may be version or group mismatch "
+ ", channel: consumer: "
+ channel.getRemoteAddress()
+ " --> provider: "
+ channel.getLocalAddress()
+ ", message:" + msg);
}
// 設定遠端地址
RpcContext.getContext().setRemoteAddress(channel.getRemoteAddress());
return exporter.getInvoker().invoke(inv);
}
// 否則丟擲異常,不支援的請求訊息
throw new RemotingException(channel,
"Unsupported request: "
+ (msg.getClass().getName() + ": " + msg)
+ ", channel: consumer: "
+ channel.getRemoteAddress()
+ " --> provider: "
+ channel.getLocalAddress());
}
@Override
public void received(Channel channel, Object message) throws RemotingException {
// 如果訊息是Invocation型別,則呼叫reply,否則接收訊息
if (message instanceof Invocation) {
reply((ExchangeChannel) channel, message);
} else {
super.received(channel, message);
}
}
};
2.export
@Override
public <T> Exporter<T> export(Invoker<T> invoker) throws RpcException {
// can use thrift codec only
// 只能使用thrift編解碼器
URL url = invoker.getUrl().addParameter(Constants.CODEC_KEY, ThriftCodec.NAME);
// find server.
// 獲得服務地址
String key = url.getAddress();
// client can expose a service for server to invoke only.
// 客戶端可以為伺服器暴露服務以僅呼叫
boolean isServer = url.getParameter(Constants.IS_SERVER_KEY, true);
if (isServer && !serverMap.containsKey(key)) {
// 加入到集合
serverMap.put(key, getServer(url));
}
// export service.
// 得到服務key
key = serviceKey(url);
// 建立暴露者
DubboExporter<T> exporter = new DubboExporter<T>(invoker, key, exporterMap);
// 加入集合
exporterMap.put(key, exporter);
return exporter;
}
該方法是服務暴露的邏輯實現。
3.refer
@Override
public <T> Invoker<T> refer(Class<T> type, URL url) throws RpcException {
// 建立ThriftInvoker
ThriftInvoker<T> invoker = new ThriftInvoker<T>(type, url, getClients(url), invokers);
// 加入到集合
invokers.add(invoker);
return invoker;
}
該方法是服務引用的邏輯實現。
4.getClients
private ExchangeClient[] getClients(URL url) {
// 獲得連線數
int connections = url.getParameter(Constants.CONNECTIONS_KEY, 1);
// 建立客戶端集合
ExchangeClient[] clients = new ExchangeClient[connections];
// 建立客戶端
for (int i = 0; i < clients.length; i++) {
clients[i] = initClient(url);
}
return clients;
}
該方法是獲得客戶端集合。
5.initClient
private ExchangeClient initClient(URL url) {
ExchangeClient client;
// 加上編解碼器
url = url.addParameter(Constants.CODEC_KEY, ThriftCodec.NAME);
try {
// 建立客戶端
client = Exchangers.connect(url);
} catch (RemotingException e) {
throw new RpcException("Fail to create remoting client for service(" + url
+ "): " + e.getMessage(), e);
}
return client;
}
該方法是建立客戶端的邏輯。
6.getServer
private ExchangeServer getServer(URL url) {
// enable sending readonly event when server closes by default
// 加入只讀事件
url = url.addParameterIfAbsent(Constants.CHANNEL_READONLYEVENT_SENT_KEY, Boolean.TRUE.toString());
// 獲得服務的實現方式
String str = url.getParameter(Constants.SERVER_KEY, Constants.DEFAULT_REMOTING_SERVER);
// 如果該實現方式不是dubbo支援的方式,則丟擲異常
if (str != null && str.length() > 0 && !ExtensionLoader.getExtensionLoader(Transporter.class).hasExtension(str))
throw new RpcException("Unsupported server type: " + str + ", url: " + url);
ExchangeServer server;
try {
// 獲得伺服器
server = Exchangers.bind(url, handler);
} catch (RemotingException e) {
throw new RpcException("Fail to start server(url: " + url + ") " + e.getMessage(), e);
}
// 獲得實現方式
str = url.getParameter(Constants.CLIENT_KEY);
// 如果客戶端實現方式不是dubbo支援的方式,則丟擲異常。
if (str != null && str.length() > 0) {
Set<String> supportedTypes = ExtensionLoader.getExtensionLoader(Transporter.class).getSupportedExtensions();
if (!supportedTypes.contains(str)) {
throw new RpcException("Unsupported client type: " + str);
}
}
return server;
}
該方法是獲得server的邏輯實現。
後記
該部分相關的原始碼解析地址:https://github.com/CrazyHZM/i…
該文章講解了遠端呼叫中關於thrift協議實現的部分,要對Thrift。接下來我將開始對rpc模組關於webservice協議部分進行講解。