在tigase内部处理(1):启动 里有体现http://my.oschina.net/greki/blog/275256
看tigase源码你会发现所有的tigase处理都是基于多线程,每个component都有自己的in和out处理线程,线程间的数据传输通过queue
总的流程大致就是:
这里主要介绍下,socket收到tcp报文转化为tigase内部的对象packet的过程;
主处理component:c2s:ClientConnectionManager
c2s在启动的时候创建了3类线程(创建过程参考启动篇):
socketReadThread():负责读socket的数据;
socketWriteThread():负责写socket;
ResultsListener:负责componet执行结果的处理;
processSocketData(); if ((receivedPackets() > 0) && (serviceListener != null)) { serviceListener.packetsReady(this);//serviceListener 这里是clientConnectManager;在connectionOpenTrhead里设置 }
while ((elem = elems.poll()) != null) { Packet pack = Packet.packetInstance(elem);//Message、Presence、IQ、其他Packet addReceivedPacket(pack);//puts processing results(packet) to queue(receivedPackets), sendAck(pack);//ack 模式,收到后发送ack }
if ((packet = filterPacket(packet, outgoing_filters)) != null) {//out_filter处理 processOutPacket(packet);// }
if (parent != null) {//parent指message_router,在初始化的时候设置
parent.addPacket(packet);---->clientConnectManager一定是走这里,把packet构建完成后,交给messageRouter 来处理,添加到它的in_queue; } else {
// It may happen for MessageRouter and this is intentional
addPacketNB(packet);
}
ServerComponent comp = getLocalComponent(packet.getTo());//根据to属性,查找local的component,普通聊天,一般是路由到SessionManager组件 if (comp != null) { Queue<Packet> results = new ArrayDeque<Packet>(); if (comp == this) { // This is addressed to the MessageRouter itself. Has to be processed // separately to avoid recurential calls by the packet processing // method. processPacketMR(packet, results); } else { // All other components process the packet the same way. comp.processPacket(packet, results);//调用component处理 } if (results.size() > 0) {//结果有返回继续,添回答到in_queue for (Packet res : results) { // No more recurrential calls!! addOutPacketNB(res); // processPacket(res); } // end of for () } // If the component is found the processing ends here as there can be // only one component with specific ID. return; } // This packet is not processed yet // The packet can be addressed to just a domain, one of the virtual hosts // The code below finds all components which handle packets addressed // to a virtual domains (implement VHostListener and return 'true' from // handlesLocalDomains() method call) String host = packet.getTo().getDomain(); ServerComponent[] comps = getComponentsForLocalDomain(host);//local找不到服务器组件,到对应的集群的其他host查询 if (comps == null) { // Still no component found, now the most expensive lookup. // Checking regex routings provided by the component. comps = getServerComponentsForRegex(packet.getTo().getBareJID().toString()); } if ((comps == null) &&!isLocalDomain(host)) { // None of the component want to process the packet. // If the packet is addressed to non-local domain then it is processed by // all components dealing with external world, like s2s comps = getComponentsForNonLocalDomain(host); } // Ok, if any component has been found then process the packet in a standard // way if (comps != null) { // Processing packet and handling results out Queue<Packet> results = new ArrayDeque<Packet>(); for (ServerComponent serverComponent : comps) { if (log.isLoggable(Level.FINEST)) { log.log(Level.FINEST, "2. Packet will be processed by: {0}, {1}", new Object[] { serverComponent.getComponentId(), packet }); } serverComponent.processPacket(packet, results); if (results.size() > 0) { for (Packet res : results) { // No more recurrential calls!! addOutPacketNB(res); // processPacket(res); } // end of for () } } } else { // No components for the packet, sending an error back if (log.isLoggable(Level.FINEST)) { log.finest("There is no component for the packet, sending it back"); } try { addOutPacketNB(Authorization.SERVICE_UNAVAILABLE.getResponseMessage(packet, "There is no service found to process your request.", true)); } catch (PacketErrorTypeException e) { // This packet is to local domain, we don't want to send it out // drop packet :-( log.warning("Can't process packet to local domain, dropping..." + packet .toStringSecure()); } }
参考:http://my.oschina.net/greki/blog/209588
ProcessorWorkerThread 调用
void process( Packet packet, XMPPResourceConnection session,
NonAuthUserRepository repo, Queue<Packet> results,
Map<String, Object> settings ) throws XMPPException;
Objects of this class carry a single XMPP packet (stanza). The XMPP stanza is carried as an XML element in DOM structure by the Packet object which contains some extra information and convenience methods to quickly access the most important stanza information. // packet是xmpp stanza封装对象 The stanza is accessible directly through the getElement() method and then it can be handles as an XML object. Please note! Even though the Packet object and carried the stanza Element is not unmodifiable it should be treated as such. This particular Packet can be processed concurrently at the same time in different components or plugins of the Tigase server. Modifying it may lead to unexpected and hard to diagnose behaviors. Every time you want to change or update the object you should obtaina a copy of it using one of the utility methods: copyElementOnly(), swapFromTo(...), errorResult(...), okResult(...), swapStanzaFromTo(...) //packet会被并发处理,修改都要先copy There are no public constructors for the class, instead you have to use factory methods: packetInstance(...) which return instance of one of the classes: Iq, Message or Presence. While creating a new Packet instance JIDs are parsed and processed through the stringprep. Hence some of the factory methods may throw TigaseStringprepException exception. You can avoid this by using the methods which accept preparsed JIDs. Reusing preparsed JIDs is highly recommended. //只能通过factory方法创建packetInstance //有3地址 There are 3 kinds of addresses available from the Packet object: PacketFrom/To, StanzaFrom/To and From/To. Stanza addresses are the normal XMPP addresses parsed from the XML stanza and as a convenience are available through methods as JID objects. This is not only convenient to the developer but also this is important for performance reasons as parsing JID and processing it through stringprep is quite expensive operation so it is better to do it once and reuse the parsed objects. Please note that any of them can be null. Note also. You should avoid parsing stanza JIDs from the XML element in your code as this may impact the server performance. Reuse the JIDs provided from the Packet methods. //1.xmpp 报文的jid, Packet addresses are also JID objects but they may contain a different values from the Stanza addresses. These are the Tigase internal addresses used by the server and they usually contain Tigase component source and destination address. In most cases they are used between connection managers and session managers and can be ignored by other code. One advantage of setting PacketFrom address to address of your component (getComponentId()) address is that if there is a packet delivery problem it will be returned back to the sender with apropriate error message. //2.tigase内部地址,组件路由 Simple From/To addresses contains values following the logic: If PacketFrom/To is not null then it contains PacketFrom/To values otherwise it contains StanzaFrom/To values. This is because the Tigase server tries always to deliver and process the Packet using PacketFrom/To addresses if they are null then Stanza addresses are used instead. So these are just convenience methods which allow avoiding extra IFs in the program code and also save some CPU cycles. Created: Tue Nov 22 07:07:11 2005 //3.如果PacketFrom/To不为空,就是PacketFrom/To;否则就是xmpp stanza的地址;目的是减少程序if else判断