这节准备解析下tars的网络层和协议转换的源码,为了更好的理解这节,需要具备java网络层知识,主要是nio和Reactor模式。
nio的知识可以参考这个系列的文章
http://www.iteye.com/magazines/132-Java-NIO
这里简单说下Nio的概念,在bio模式下,因为socket读取数据是阻塞的,所以导致整个线程都卡在这里。在nio模式下,会用事件机制(表相是这样,底层是依赖操作系统的机制实现,linux的epoll,windows的iocp)用一个线程去托管n个socket,该线程不停的遍历所有socket的事件,如果某个socket读取好了数据发送通知通知该线程,该线程拿出该socket去处理(通俗的解释,不严格正确)
Reactor模式
https://www.cnblogs.com/doit8791/p/7461479.html
Reactor有几张图很重要,代表了几种模式
这3张图理解了,基本上Ractor模式就掌握了
上一节我们忽略了整个网络层的细节,对于上层来说,就是这段的代码
ServantAdapter.bind
public void bind(AppService appService) throws IOException {
this.skeleton = (ServantHomeSkeleton) appService;
ServerConfig serverCfg = ConfigurationManager.getInstance().getServerConfig();
boolean keepAlive = true;
Codec codec = createCodec(serverCfg);
Processor processor = createProcessor(serverCfg);
Executor threadPool = ServantThreadPoolManager.get(servantAdapterConfig);
Endpoint endpoint = this.servantAdapterConfig.getEndpoint();
if (endpoint.type().equals("tcp")) {
this.selectorManager = new SelectorManager(Utils.getSelectorPoolSize(), new ServantProtocolFactory(codec), threadPool, processor, keepAlive, "server-tcp-reactor", false);
this.selectorManager.setTcpNoDelay(serverCfg.isTcpNoDelay());
this.selectorManager.start();
System.out.println("[SERVER] server starting at " + endpoint + "...");
ServerSocketChannel serverChannel = ServerSocketChannel.open();
serverChannel.socket().bind(new InetSocketAddress(endpoint.host(), endpoint.port()), 1024);
serverChannel.configureBlocking(false);
selectorManager.getReactor(0).registerChannel(serverChannel, SelectionKey.OP_ACCEPT);
System.out.println("[SERVER] server started at " + endpoint + "...");
} else if (endpoint.type().equals("udp")) {
this.selectorManager = new SelectorManager(1, new ServantProtocolFactory(codec), threadPool, processor, false, "server-udp-reactor", true);
this.selectorManager.start();
System.out.println("[SERVER] server starting at " + endpoint + "...");
DatagramChannel serverChannel = DatagramChannel.open();
DatagramSocket socket = serverChannel.socket();
socket.bind(new InetSocketAddress(endpoint.host(), endpoint.port()));
serverChannel.configureBlocking(false);
this.selectorManager.getReactor(0).registerChannel(serverChannel, SelectionKey.OP_READ);
System.out.println("[SERVER] servant started at " + endpoint + "...");
}
}
先看 SelectorManager初始化
获取线程池的数目,我们大部分确定线程数一般是cpu+1,但是这里对于cpu大于8的,做了特殊处理,原因猜测是因为如果cpu大于8的时候,不想把cpu都占满?
public class Utils {
public static int getSelectorPoolSize() {
int processors = Runtime.getRuntime().availableProcessors();
return processors > 8 ? 4 + (processors * 5 / 8) : processors + 1;
}
我们看下SelectorManager成员变量
public final class SelectorManager {
private final AtomicLong sets = new AtomicLong(0);
//这里是前面说的reactor模式里的reactor
private final Reactor[] reactorSet;
//这里是codec的工厂,理解简单点就是codec,默认的情况是TarsCodec这个类
private ProtocolFactory protocolFactory = null;
//这个treadPool是处理业务的线程池
private Executor threadPool = null;
//request<-> response的处理逻辑 默认是TarsServantProcessor
private Processor processor = null;
//select的个数,基本就是reactor的个数
private final int selectorPoolSize;
private volatile boolean started;
private boolean keepAlive;
private boolean isTcpNoDelay = false;
selectManager的构造函数,主要是赋值,构建Reactors
public SelectorManager(int selectorPoolSize, ProtocolFactory protocolFactory, Executor threadPool,
Processor processor, boolean keepAlive, String reactorNamePrefix, boolean udpMode) throws IOException {
if (udpMode) selectorPoolSize = 1;
this.selectorPoolSize = selectorPoolSize;
this.protocolFactory = protocolFactory;
this.threadPool = threadPool;
this.processor = processor;
this.keepAlive = keepAlive;
reactorSet = new Reactor[selectorPoolSize];
for (int i = 0; i < reactorSet.length; i++) {
reactorSet[i] = new Reactor(this, reactorNamePrefix + "-" + protocolFactory.getClass().getSimpleName().toLowerCase() + "-" + String.valueOf(i), udpMode);
}
}
整个网络层,tars提供了一个单独的net包来封装
我们看下Reactor,可以看到Reactor是个Thread,里面包含nio的一个重要组件select
public final class Reactor extends Thread {
protected volatile Selector selector = null;
private volatile boolean crashed = false;
private final Queue
accpetor是处理网络事件的逻辑
public abstract class Acceptor {
protected SelectorManager selectorManager = null;
public Acceptor(SelectorManager selectorManager) {
this.selectorManager = selectorManager;
}
//处理网络OP_CONNECT的事件
public abstract void handleConnectEvent(SelectionKey key) throws IOException;
//处理网络OP_ACCEPT的事件
public abstract void handleAcceptEvent(SelectionKey key) throws IOException;
//处理网络OP_READ的事件
public abstract void handleReadEvent(SelectionKey key) throws IOException;
//处理网络OP_WRITE的事件
public abstract void handleWriteEvent(SelectionKey key) throws IOException;
}
我们稍后再看下TCPAcceptor具体的处理逻辑,回到上面
this.selectorManager.start();
这里启动了Reactor,其实就是调用Thread的run方法,这里基本属于nio的处理,可以简单理解,不停的循环遍历socket看看有没有事件抛出来,抛出来就处理。
public synchronized void start() {
if (this.started) {
return;
}
this.started = true;
for (Reactor reactor : this.reactorSet) {
reactor.start();
}
}
public void run() {
try {
while (!Thread.interrupted()) {
selector.select();
processRegister();
Iterator iter = selector.selectedKeys().iterator();
while (iter.hasNext()) {
SelectionKey key = iter.next();
iter.remove();
if (!key.isValid()) continue;
try {
//1. Update the last operation time
if (key.attachment() != null && key.attachment() instanceof Session) {
((Session) key.attachment()).updateLastOperationTime();
}
//2. Dispatch I/O event
dispatchEvent(key);
} catch (Throwable ex) {
disConnectWithException(key, ex);
}
}
processUnRegister();
}
} catch (Throwable e) {
crashed = true;
e.printStackTrace();
} finally {
try {
selector.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
抛出来的事件怎么处理,在dispatchEvent处理,其实是调用前面的acceptor去处理
private void dispatchEvent(final SelectionKey key) throws IOException {
if (key.isConnectable()) {
acceptor.handleConnectEvent(key);
} else if (key.isAcceptable()) {
acceptor.handleAcceptEvent(key);
} else if (key.isReadable()) {
acceptor.handleReadEvent(key);
} else if (key.isValid() && key.isWritable()) {
acceptor.handleWriteEvent(key);
}
}
先不急着看acceptor的逻辑,我们停下来想想,这里的reactor线程会发生什么?会走哪个逻辑?如果你熟悉网络编程的话,你会发现这个服务端还没绑定端口。回到上层
启动ractor之后,绑定端口,随后注册OP_ACCEPT消息
ServerSocketChannel serverChannel = ServerSocketChannel.open();
serverChannel.socket().bind(new InetSocketAddress(endpoint.host(), endpoint.port()), 1024);
serverChannel.configureBlocking(false);
selectorManager.getReactor(0).registerChannel(serverChannel, SelectionKey.OP_ACCEPT);
System.out.println("[SERVER] server started at " + endpoint + "...");
我们现在来看TcpAcceptor处理逻辑, 先看怎么处理OP_ACCEPT,如果一个客户端连接过来,建立一个TCPSession,然后注册到SessionManager里管理,同时使用一个新的reactor来处理这个connection的OP_READ的消息。
public void handleAcceptEvent(SelectionKey key) throws IOException {
//1. Accept TCP request
ServerSocketChannel server = (ServerSocketChannel) key.channel();
SocketChannel channel = server.accept();
channel.socket().setTcpNoDelay(selectorManager.isTcpNoDelay());
channel.configureBlocking(false);
Utils.setQosFlag(channel.socket());
//2. Create NioSession for each TCP connection
TCPSession session = new TCPSession(selectorManager);
session.setChannel(channel);
session.setStatus(SessionStatus.SERVER_CONNECTED);
session.setKeepAlive(selectorManager.isKeepAlive());
session.setTcpNoDelay(selectorManager.isTcpNoDelay());
//3. Register session
SessionManager.getSessionManager().registerSession(session);
//4. Register channel with the specified session
selectorManager.nextReactor().registerChannel(channel, SelectionKey.OP_READ, session);
}
这里符合我们前面讲的reactor第三张图,一个reactor去处理连接请求,连接上来让一个reactor组来处理读消息分散压力,本应该如此,但是我们看tars的nextReactor这个方法.假设我们有2个reactor,我们固定用第0个reactor去处理连接,但是这么写会导致我第0个reactor也会处理read消息,其实不是很符合reactor的标准设计思路。不知道tars这么设计的理由?
public final Reactor nextReactor() {
return this.reactorSet[(int) (this.sets.incrementAndGet() % this.selectorPoolSize)];
}
看下OP_READ消息处理,调用TCPSession的read方法
public void handleReadEvent(SelectionKey key) throws IOException {
TCPSession session = (TCPSession) key.attachment();
if (session == null) throw new RuntimeException("The session is null when reading data...");
session.read();
}
TCPSession也很好理解,就是一个connection。这里的处理都是nio的部分,如果对nio很熟悉,很好理解,简单来说
1. 把读取的二进制数据调用我们前面说的codec去处理获取request
2. 把request形成一个workTread丢到业务线程池里取处理
protected void read() throws IOException {
int ret = readChannel();
if (this.status == SessionStatus.CLIENT_CONNECTED) {
readResponse();
} else if (this.status == SessionStatus.SERVER_CONNECTED) {
readRequest();
} else {
throw new IllegalStateException("The current session status is invalid. [status:" + this.status + "]");
}
if (ret < 0) {
close();
return;
}
}
public void readRequest() throws IOException {
Request request = null;
IoBuffer tempBuffer = null;
try {
tempBuffer = readBuffer.duplicate().flip();
while (true) {
tempBuffer.mark();
if (tempBuffer.remaining() > 0) {
request = selectorManager.getProtocolFactory().getDecoder().decodeRequest(tempBuffer, this);
} else {
request = null;
}
if (request != null) {
try {
request.resetBornTime();
selectorManager.getThreadPool().execute(new WorkThread(request, selectorManager));
} catch (Exception ex) {
ex.printStackTrace();
}
} else {
tempBuffer.reset();
readBuffer = resetIoBuffer(tempBuffer);
break;
}
}
} catch (ProtocolException ex) {
close();
ex.printStackTrace();
}
}
我们先看codec的decodeRequest的逻辑,这里是在TarsCodec里,这里如果想看懂,需要对tars通信协议具备一定理解,我们可以不关心这个。
简单理解就是一个4字节的int去表明我这个消息长度是多少
public Request decodeRequest(IoBuffer buffer, Session session) throws ProtocolException {
//如果一个int都没有返回
if (buffer.remaining() < 4) {
return null;
}
//算下真个包体多长,如果包体太长,认为出错,buffer.getInt()就是这个包体的长度
int length = buffer.getInt() - TarsHelper.HEAD_SIZE;
if (length > TarsHelper.PACKAGE_MAX_LENGTH || length <= 0) {
throw new ProtocolException("the length header of the package must be between 0~10M bytes. data length:" + Integer.toHexString(length));
}
//如果接受的消息长度不足,说明还需要读取,返回继续读
if (buffer.remaining() < length) {
return null;
}
//这里就是把二进制数据按照协议的格式去填充进TarsServantRequest
byte[] reads = new byte[length];
buffer.get(reads);
TarsInputStream jis = new TarsInputStream(reads);
TarsServantRequest request = new TarsServantRequest(session);
try {
short version = jis.read(TarsHelper.STAMP_SHORT.shortValue(), 1, true);
byte packetType = jis.read(TarsHelper.STAMP_BYTE.byteValue(), 2, true);
int messageType = jis.read(TarsHelper.STAMP_INT.intValue(), 3, true);
int requestId = jis.read(TarsHelper.STAMP_INT.intValue(), 4, true);
String servantName = jis.readString(5, true);
String methodName = jis.readString(6, true);
request.setVersion(version);
request.setPacketType(packetType);
request.setMessageType(messageType);
request.setRequestId(requestId);
request.setServantName(servantName);
request.setFunctionName(methodName);
request.setInputStream(jis);
request.setCharsetName(charsetName);
} catch (Exception e) {
System.err.println(e);
request.setRet(TarsHelper.SERVERDECODEERR);
}
return request;
}
在我们开始看第二步之前,我们思考一个问题,我们不停的读取出来Request的,tars能不能保证同一个session执行的先后顺序。比如同一个客户端连续发送了消息abc,希望接受的顺序也是abc,tars能不能做到呢?
答案是no,tars做不到,为什么?因为消息abc是交由线程池处理,也许消息c先处理完就先返回了,所以同一个session是无法保证顺序的。
如果我们想保证一个session的消息严格按顺序返回,怎么做(hits:可以参看mina源码,mina是可以做到的)
做不到顺序,会对rpc有影响么?答案是no,具体答案在客户端实现里
从这里可以看到我们不能把Tars当做消息队列来使用,因为消息队列要严格顺序
我们再看第二步如何处理的session,主要是看WorkThread的执行方法
“`java
public void run() {
try {
if (udpSession != null) {
parseDatagramPacket();
udpBuffer = null;
}
if (req != null) {
req.setProcessTime(System.currentTimeMillis());
req.init();
Response res = selectorManager.getProcessor().process(req, req.getIoSession());
if (!res.isAsyncMode()) req.getIoSession().write(res);
} else if (resp != null) {
resp.init();
Ticket ticket = TicketManager.getTicket(resp.getTicketNumber());
if (ticket == null) {
String s = "failed to fetch request for this response. [from:" + resp.getSession().getRemoteIp() + ":" + resp.getSession().getRemotePort() + "]";
System.out.println(s);
return;
}
fillDitributedContext(ticket.request().getDistributedContext());
ticket.notifyResponse(resp);
ticket.countDown();
TicketManager.removeTicket(ticket.getTicketNumber());
}
} catch (Exception ex) {
ex.printStackTrace();
} finally {
clearDistributedContext();
}
}
“`
这里先调用req.init方法,这里会继续解析request消息。
这里为啥把request的解析分成2个部分,这里体现了设计的精巧,因为第一部分解析是在reactor线程里解析,所以需要尽量快,防止阻塞,而这里的body解析是在业务线程里解析。(深刻理解了Reactor模式)
public void init() {
((TarsCodec) this.session.getProtocolFactory().getDecoder()).decodeRequestBody(this);
// TarsCodecHelper.decodeRequestBody(this);
}
public ServantRequest decodeRequestBody(ServantRequest req) {
TarsServantRequest request = (TarsServantRequest) req;
if (request.getRet() != TarsHelper.SERVERSUCCESS) {
return request;
}
if (TarsHelper.isPing(request.getFunctionName())) {
return request;
}
TarsInputStream jis = request.getInputStream();
ClassLoader oldClassLoader = null;
try {
oldClassLoader = Thread.currentThread().getContextClassLoader();
Thread.currentThread().setContextClassLoader(resolveProtocolClassLoader());
String methodName = request.getFunctionName();
byte[] data = jis.read(TarsHelper.STAMP_BYTE_ARRAY, 7, true);//数据
int timeout = jis.read(TarsHelper.STAMP_INT.intValue(), 8, true);//超时时间
Map context = (Map) jis.read(TarsHelper.STAMP_MAP, 9, true);//Map context
Map status = (Map) jis.read(TarsHelper.STAMP_MAP, 10, true);
request.setTimeout(timeout);
request.setContext(context);
request.setStatus(status);
String servantName = request.getServantName();
//在启动的时候会把暴露的接口的方法和参数缓存起来,这里就是取出来
Map methodInfoMap = AnalystManager.getInstance().getMethodMapByName(servantName);
if (methodInfoMap == null || methodInfoMap.isEmpty()) {
request.setRet(TarsHelper.SERVERNOSERVANTERR);
throw new ProtocolException("no found methodInfo, the context[ROOT], serviceName[" + servantName + "], methodName[" + methodName + "]");
}
TarsMethodInfo methodInfo = methodInfoMap.get(methodName);
if (methodInfo == null) {
request.setRet(TarsHelper.SERVERNOFUNCERR);
throw new ProtocolException("no found methodInfo, the context[ROOT], serviceName[" + servantName + "], methodName[" + methodName + "]");
}
request.setMethodInfo(methodInfo);
List parametersList = methodInfo.getParametersList();
if (!CommonUtils.isEmptyCollection(parametersList)) {
Object[] parameters = new Object[parametersList.size()];
int i = 0;
//不同版本解析不同
if (TarsHelper.VERSION == request.getVersion()) {//request
parameters = decodeRequestBody(data, request.getCharsetName(), methodInfo);
} else if (TarsHelper.VERSION2 == request.getVersion() || TarsHelper.VERSION3 == request.getVersion()) {
//wup request
UniAttribute unaIn = new UniAttribute();
unaIn.setEncodeName(request.getCharsetName());
if (request.getVersion() == TarsHelper.VERSION2) {
unaIn.decodeVersion2(data);
} else if (request.getVersion() == TarsHelper.VERSION3) {
unaIn.decodeVersion3(data);
}
Object value = null;
for (TarsMethodParameterInfo parameterInfo : parametersList) {
if (TarsHelper.isHolder(parameterInfo.getAnnotations())) {
String holderName = TarsHelper.getHolderName(parameterInfo.getAnnotations());
if (!StringUtils.isEmpty(holderName)) {
value = new Holder(unaIn.getByClass(holderName, parameterInfo.getStamp()));
} else {
value = new Holder();
}
} else {
value = unaIn.getByClass(parameterInfo.getName(), parameterInfo.getStamp());
}
parameters[i++] = value;
}
} else {
request.setRet(TarsHelper.SERVERDECODEERR);
System.err.println("un supported protocol, ver=" + request.getVersion());
}
request.setMethodParameters(parameters);
}
} catch (Throwable ex) {
if (request.getRet() == TarsHelper.SERVERSUCCESS) {
request.setRet(TarsHelper.SERVERDECODEERR);
}
System.err.println(TarsUtil.getHexdump(jis.getBs()));
} finally {
if (oldClassLoader != null) {
Thread.currentThread().setContextClassLoader(oldClassLoader);
}
}
return request;
}
这里分三个版本
Verson1:
TarsInputStream的read方法,前面是如果参数是基本类型,用read解析,如果是复杂的对象类型调用TarsInputStreamExt.read处理
“`java
protected Object[] decodeRequestBody(byte[] data, String charset, TarsMethodInfo methodInfo) throws Exception {
TarsInputStream jis = new TarsInputStream(data);
List parametersList = methodInfo.getParametersList();
Object[] parameters = new Object[parametersList.size()];
int i = 0;
jis.setServerEncoding(charset);//set decode charset name
Object value = null;
for (TarsMethodParameterInfo parameterInfo : parametersList) {
if (TarsHelper.isHolder(parameterInfo.getAnnotations())) {
value = new Holder(jis.read(parameterInfo.getStamp(), parameterInfo.getOrder(), false));
} else {
value = jis.read(parameterInfo.getStamp(), parameterInfo.getOrder(), false);
}
parameters[i++] = value;
}
return parameters;
}
```java
public Object read(T o, int tag, boolean isRequire) {
if (o instanceof Byte) {
return Byte.valueOf(read((byte) 0x0, tag, isRequire));
} else if (o instanceof Boolean) {
return Boolean.valueOf(read(false, tag, isRequire));
} else if (o instanceof Short) {
return Short.valueOf(read((short) 0, tag, isRequire));
} else if (o instanceof Integer) {
int i = read((int) 0, tag, isRequire);
return Integer.valueOf(i);
} else if (o instanceof Long) {
return Long.valueOf(read((long) 0, tag, isRequire));
} else if (o instanceof Float) {
return Float.valueOf(read((float) 0, tag, isRequire));
} else if (o instanceof Double) {
return Double.valueOf(read((double) 0, tag, isRequire));
} else if (o instanceof String) {
return readString(tag, isRequire);
} else if (o instanceof Map) {
return readMap((Map) o, tag, isRequire);
} else if (o instanceof List) {
return readArray((List) o, tag, isRequire);
} else if (o instanceof TarsStructBase) {
return read((TarsStructBase) o, tag, isRequire);
} else if (o.getClass().isArray()) {
if (o instanceof byte[] || o instanceof Byte[]) {
return read((byte[]) null, tag, isRequire);
} else if (o instanceof boolean[]) {
return read((boolean[]) null, tag, isRequire);
} else if (o instanceof short[]) {
return read((short[]) null, tag, isRequire);
} else if (o instanceof int[]) {
return read((int[]) null, tag, isRequire);
} else if (o instanceof long[]) {
return read((long[]) null, tag, isRequire);
} else if (o instanceof float[]) {
return read((float[]) null, tag, isRequire);
} else if (o instanceof double[]) {
return read((double[]) null, tag, isRequire);
} else {
return readArray((Object[]) o, tag, isRequire);
}
} else {
return TarsInputStreamExt.read(o, tag, isRequire, this);
}
}
class="se-preview-section-delimiter">div>
public class TarsInputStreamExt {
public static T read(T e, int tag, boolean isRequire, TarsInputStream jis) {
TarsStructInfo info = TarsHelper.getStructInfo(e.getClass());
if (info == null) {
throw new TarsDecodeException("the JavaBean[" + e.getClass().getSimpleName() + "] no annotation Struct");
}
if (jis.skipToTag(tag)) {
HeadData hd = new HeadData();
jis.readHead(hd);
if (hd.type != TarsStructBase.STRUCT_BEGIN) {
throw new TarsDecodeException("type mismatch.");
}
T result = (T) CommonUtils.newInstance(e.getClass());
List list = info.getPropertyList();
if (!CommonUtils.isEmptyCollection(list)) {
for (TarsStrutPropertyInfo propertyInfo : list) {
Object value = jis.read(propertyInfo.getStamp(), propertyInfo.getOrder(), propertyInfo.isRequire());
BeanAccessor.setBeanValue(result, propertyInfo.getName(), value);
}
}
jis.skipToStructEnd();
return result;
} else if (isRequire) {
throw new TarsDecodeException("require field not exist.");
}
return null;
}
}
"se-preview-section-delimiter">
其中version2,3都是利用UniAttribute unaIn = new UniAttribute();
Version2:
数据data—>HashMap
```java
public class TarsInputStreamExt {
public static T read(T e, int tag, boolean isRequire, TarsInputStream jis) {
TarsStructInfo info = TarsHelper.getStructInfo(e.getClass());
if (info == null) {
throw new TarsDecodeException("the JavaBean[" + e.getClass().getSimpleName() + "] no annotation Struct");
}
if (jis.skipToTag(tag)) {
HeadData hd = new HeadData();
jis.readHead(hd);
if (hd.type != TarsStructBase.STRUCT_BEGIN) {
throw new TarsDecodeException("type mismatch.");
}
T result = (T) CommonUtils.newInstance(e.getClass());
List list = info.getPropertyList();
if (!CommonUtils.isEmptyCollection(list)) {
for (TarsStrutPropertyInfo propertyInfo : list) {
Object value = jis.read(propertyInfo.getStamp(), propertyInfo.getOrder(), propertyInfo.isRequire());
BeanAccessor.setBeanValue(result, propertyInfo.getName(), value);
}
}
jis.skipToStructEnd();
return result;
} else if (isRequire) {
throw new TarsDecodeException("require field not exist.");
}
return null;
}
}
"se-preview-section-delimiter">
其中version2,3都是利用UniAttribute unaIn = new UniAttribute();
Version2:
public void decode(byte[] buffer) {
_is.warp(buffer);
_is.setServerEncoding(encodeName);
HashMapbyte[]>> _tempdata = new HashMapbyte[]>>(1);
HashMapbyte[]> h = new HashMapbyte[]>(1);
h.put("", new byte[0]);
_tempdata.put("", h);
_data = (HashMapbyte[]>>) _is.readMap(_tempdata, 0, false);
}
其中version2,3都是利用UniAttribute unaIn = new UniAttribute();
Version2:
```java
public void decode(byte[] buffer) {
_is.warp(buffer);
_is.setServerEncoding(encodeName);
HashMapbyte[]>> _tempdata = new HashMapbyte[]>>(1);
HashMapbyte[]> h = new HashMapbyte[]>(1);
h.put("", new byte[0]);
_tempdata.put("", h);
_data = (HashMapbyte[]>>) _is.readMap(_tempdata, 0, false);
}
"se-preview-section-delimiter">
Version3:
public void decodeVersion3(byte[] buffer) {
_is.warp(buffer);
_is.setServerEncoding(encodeName);
HashMapbyte[]> _tempdata = new HashMapbyte[]>(1);
_tempdata.put("", new byte[0]);
_newData = (HashMapbyte[]>) _is.readMap(_tempdata, 0, false);
}
Version3:
```java
public void decodeVersion3(byte[] buffer) {
_is.warp(buffer);
_is.setServerEncoding(encodeName);
HashMapbyte[]> _tempdata = new HashMapbyte[]>(1);
_tempdata.put("", new byte[0]);
_newData = (HashMapbyte[]>) _is.readMap(_tempdata, 0, false);
}
"se-preview-section-delimiter">
最后解析的过程:
public T getByClass(String name, T proxy) throws ObjectCreateException {
if (null != _newData) {
if (!_newData.containsKey(name)) {
return null;
} else if (cachedData.containsKey(name)) {
return (T) cachedData.get(name);
} else {
byte[] data = _newData.get(name);
try {
Object o = decodeData(data, proxy);
if (null != o) {
saveDataCache(name, o);
}
return (T) o;
} catch (Exception ex) {
throw new ObjectCreateException(ex);
}
}
} else {
if (!_data.containsKey(name)) {
return null;
} else if (cachedData.containsKey(name)) {
return (T) cachedData.get(name);
} else {
HashMapbyte[]> pair = _data.get(name);
String className = null;
byte[] data = new byte[0];
for (Entrybyte[]> e : pair.entrySet()) {
className = e.getKey();
data = e.getValue();
break;
}
try {
_is.warp(data);
_is.setServerEncoding(encodeName);
Object o = _is.read(proxy, 0, true);
saveDataCache(name, o);
return (T) o;
} catch (Exception ex) {
throw new ObjectCreateException(ex);
}
}
}
}
"se-preview-section-delimiter">
其中saveDataCache是设立缓存,防止一次调用传递多次参数?(不是很懂???)
这样就把参数解析完毕
req.init();结束 这样所有的request全部解析完成
随后先调用process去处理request–>response转换,这里的process默认的是TarsServantProcessor
这里的核心就是找到ServantHomeSkeleton,然后调用真实的Method,执行invoke,获取返回值
在设计角度上看,在很多关键执行上,设计者提供了pre,post方法,比如在执行ServantHomeSkeleton.invoke 前会有preInvokeCapHomeSkeleton(),后会有postInvokeCapHomeSkeleton();
public Response process(Request req, Session session) {
AppContainer container = null;
TarsServantRequest request = null;
TarsServantResponse response = null;
ServantHomeSkeleton skeleton = null;
Object value = null;
AppContext appContext = null;
ClassLoader oldClassLoader = null;
int waitingTime = -1;
long startTime = req.getProcessTime();
String remark = "";
try {
oldClassLoader = Thread.currentThread().getContextClassLoader();
request = (TarsServantRequest) req;
//构建基础的response信息
response = createResponse(request, session);
response.setTicketNumber(req.getTicketNumber());
if (response.getRet() != TarsHelper.SERVERSUCCESS || TarsHelper.isPing(request.getFunctionName())) {
return response;
}
//判断开始处理消息的时间和接收消息的时间是否超时
int maxWaitingTimeInQueue = ConfigurationManager.getInstance().getServerConfig().getServantAdapterConfMap().get(request.getServantName()).getQueueTimeout();
waitingTime = (int) (startTime - req.getBornTime());
if (waitingTime > maxWaitingTimeInQueue) {
throw new TarsException("Wait too long, server busy.");
}
container = ContainerManager.getContainer(AppContainer.class);
Context, ?> context = ContextManager.registerContext(request, response);
context.setAttribute(Context.INTERNAL_START_TIME, startTime);
context.setAttribute(Context.INTERNAL_CLIENT_IP, session.getRemoteIp());
context.setAttribute(Context.INTERNAL_APP_NAME, container.getDefaultAppContext().name());
context.setAttribute(Context.INTERNAL_SERVICE_NAME, request.getServantName());
context.setAttribute(Context.INTERNAL_METHOD_NAME, request.getFunctionName());
context.setAttribute(Context.INTERNAL_SESSION_DATA, session);
//将request,response放到disDistributedContext里存储
DistributedContext distributedContext = DistributedContextManager.getDistributedContext();
distributedContext.put(DyeingSwitch.REQ, request);
distributedContext.put(DyeingSwitch.RES, response);
appContext = container.getDefaultAppContext();
if (appContext == null) throw new RuntimeException("failed to find the application named:[ROOT]");
// Thread.currentThread().setContextClassLoader(appContext.getAppContextClassLoader());
//
preInvokeSkeleton();
skeleton = appContext.getCapHomeSkeleton(request.getServantName());
if (skeleton == null) throw new RuntimeException("failed to find the servant named[" + request.getServantName() + "]");
value = skeleton.invoke(request.getMethodInfo().getMethod(), request.getMethodParameters());
response.setResult(value);
} catch (Throwable cause) {
System.out.println("ERROR: " + cause.getMessage());
if (response.isAsyncMode()) {
try {
Context context = ContextManager.getContext();
AsyncContext aContext = context.getAttribute(AsyncContext.PORTAL_CAP_ASYNC_CONTEXT_ATTRIBUTE);
if (aContext != null) aContext.writeException(cause);
} catch (Exception ex) {
System.out.println("ERROR: " + ex.getMessage());
}
} else {
response.setResult(null);
response.setCause(cause);
response.setRet(TarsHelper.SERVERUNKNOWNERR);
remark = cause.toString();
}
} finally {
if (oldClassLoader != null) {
Thread.currentThread().setContextClassLoader(oldClassLoader);
}
ContextManager.releaseContext();
if (!response.isAsyncMode()) {
printServiceFlowLog(flowLogger, request, response.getRet(), (System.currentTimeMillis() - startTime), remark);
}
postInvokeSkeleton();
OmServiceMngr.getInstance().reportWaitingTimeProperty(waitingTime);
reportServerStat(request, response, startTime);
}
return response;
}
"se-preview-section-delimiter">
回到上层,如果不是异步模式,直接发送走,这里压根没看到异步模式的处理代码
这里写的看起来服务器是有异步,其实是没有的,异步是在客户端做的,服务器虽然有这个属性,但是没用(不知道为什么这么写)
if (!res.isAsyncMode()) req.getIoSession().write(res);
"se-preview-section-delimiter">
public void write(Response response) throws IOException {
try {
IoBuffer buffer = selectorManager.getProtocolFactory().getEncoder().encodeResponse(response, this);
write(buffer);
} catch (ProtocolException ex) {
throw new IOException("protocol error:", ex);
}
}
"se-preview-section-delimiter">
这里又回到了codec,默认是TarsCodec方法
public IoBuffer encodeResponse(Response resp, Session session) throws ProtocolException {
TarsServantResponse response = (TarsServantResponse) resp;
if (response.getPacketType() == TarsHelper.ONEWAY) {
return null;
}
TarsOutputStream jos = new TarsOutputStream();
jos.setServerEncoding(charsetName);
try {
jos.getByteBuffer().putInt(0);
jos.write(response.getVersion(), 1);
jos.write(response.getPacketType(), 2);
if (response.getVersion() == TarsHelper.VERSION) {
jos.write(response.getRequestId(), 3);
jos.write(response.getMessageType(), 4);
jos.write(response.getRet(), 5);
jos.write(encodeResult(response, charsetName), 6);
if (response.getStatus() != null) {
jos.write(response.getStatus(), 7);
}
if (response.getRet() != TarsHelper.SERVERSUCCESS) {
jos.write(StringUtils.isEmpty(response.getRemark()) ? "" : response.getRemark(), 8);
}
} else if (TarsHelper.VERSION2 == response.getVersion() || TarsHelper.VERSION3 == response.getVersion()) {
jos.write(response.getMessageType(), 3);
jos.write(response.getTicketNumber(), 4);
String servantName = response.getRequest().getServantName();
jos.write(servantName, 5);
jos.write(response.getRequest().getFunctionName(), 6);
jos.write(encodeWupResult(response, charsetName), 7);
jos.write(response.getTimeout(), 8);
if (response.getContext() != null) {
jos.write(response.getContext(), 9);
}
if (response.getStatus() != null) {
jos.write(response.getStatus(), 10);
}
} else {
response.setRet(TarsHelper.SERVERENCODEERR);
System.err.println("un supported protocol, ver=" + response.getVersion());
}
} catch (Exception ex) {
if (response.getRet() == TarsHelper.SERVERSUCCESS) {
response.setRet(TarsHelper.SERVERENCODEERR);
}
}
ByteBuffer buffer = jos.getByteBuffer();
int datalen = buffer.position();
buffer.position(0);
buffer.putInt(datalen);
buffer.position(datalen);
return IoBuffer.wrap(jos.toByteArray());
}
"se-preview-section-delimiter">
这里需要注意写的时候,是先写入一个queue,这个queue是个线程安全的queue,回到线程模型,这个TCPSession其实是被多个业务线程处理,所以写入的时候可能是多个线程同时写入。写完queque,然后注册消息到select上
private Queue queue = new LinkedBlockingQueue(1024 * 8);
protected void write(IoBuffer buffer) throws IOException {
if (buffer == null) return;
if (channel == null || key == null) throw new IOException("Connection is closed");
if (!this.queue.offer(buffer.buf())) {
throw new IOException("The session queue is full. [ queue size:" + queue.size() + " ]");
}
if (key != null) {
key.interestOps(key.interestOps() | SelectionKey.OP_WRITE);
key.selector().wakeup();
}
}
"se-preview-section-delimiter">
回到TcpAcceptor
public void handleWriteEvent(SelectionKey key) throws IOException {
TCPSession session = (TCPSession) key.attachment();
if (session == null) throw new RuntimeException("The session is null when writing data...");
session.doWrite();
}
"se-preview-section-delimiter">
实际调用TcpSession的doWriter方法,这里是用nio去写入
protected synchronized int doWrite() throws IOException {
int writeBytes = 0;
while (true) {
ByteBuffer wBuf = queue.peek();
if (wBuf == null) {
key.interestOps(SelectionKey.OP_READ);
if (queue.peek() != null) {
key.interestOps(SelectionKey.OP_READ | SelectionKey.OP_WRITE);
}
key.selector().wakeup();
break;
}
int bytesWritten = ((SocketChannel) channel).write(wBuf);
if (bytesWritten == 0 && wBuf.remaining() > 0) // Socket buffer is full.
{
key.interestOps(SelectionKey.OP_READ | SelectionKey.OP_WRITE);
key.selector().wakeup();
break;
}
if (wBuf.remaining() == 0) {
writeBytes++;
queue.remove();
continue;
} else {
return -1;
}
}
if (!isKeepAlive()) close();
return writeBytes;
}
这样整个发送过程就结束了。
整个服务端解析就基本结束了。