日志優化
- 默認是 用info 級別,最好不用openfire原生的打日志方式。
- 離線消息用存儲不打回方式,不要用打回方式 xmpp.offline.type=store_and_drop
- ConnectionHandler 收到異常,關閉鏈接的異常日志,用debug級別
- 會有很多debug級別的日志,打印出消息體。這種需要避免掉, 例如,logger.debug("msg resend by old session: " + msg.toXML()); 雖然是debug,不會打印日志,但是會執行msg.toXML(),而這個是個耗時操作。可改成如下寫法:
if(logger.isDebugEnabled()){
logger.debug("msg resend by old session: " + msg.toXML());
}
多人聊天優化
1:對於新加入的房間,不要顯示歷史聊天記錄history.type=none
2:多人聊天的聊天記錄不要存儲到db LocalMUCRoom.broadcast mucService.logConversation(this, message, senderAddress);
3 :對於多人聊天,創建房間,進入房間,退出房間頻率過高會導致內存占用過高。org.jivesoftware.openfire.event.GroupEventDispatcher

登錄優化
- 用最簡單的ssl驗證方式,也就是plain 用base64編碼 (replace into ofProperty values ("sasl.mechs","PLAIN");)
- 不用安全傳輸,也就是關掉tls(replace into ofProperty values ("xmpp.client.tls.policy","disabled");)
- xmpp登錄流程 有11步包交互,非常復雜和繁瑣。可以簡化登錄流程。運用快速登錄,現已經簡化到4步,只發stream包和auth包。
- 重寫AuthProvider,返回一定要快
離線消息
離線消息默認是用db存儲,並且可以設定策略限制每個用戶的存儲空間。在移到網絡下,會經常出現對方不在線,需要存離線的情況,並且每次登錄都需要取離線,所以是一個頻繁和耗時操作。
- 離線改用緩存存儲
- 設置離線消息用存儲不打回方式,會少一次網絡通信(通知發送方)
鏈接管理器
鏈接管理器的運用,可以極大緩解openfire的鏈接壓力。如果直連openfire,每個鏈接會占有一個文件句柄,每個socket會分配讀寫緩沖區。會占用大量內存資源。做過測試,當同時在線達到1W,
NIOConnection 將會占用3G多內存。是openfire占用內存最多的。如果用鏈接管理器,openfire只需要關注業務處理,不需要處理鏈接建立,釋放,最重要的是,只需要維護幾十個與鏈接管理器建立的長鏈接。當然官方的提供的鏈接管理器,版本非常老,網絡性能低下(鏈接管理器與openfire之間的通信竟然是直接用的socket,流式處理)。
1台openfire前面可以放多個鏈接管理器。
- 鏈接管理器與openfire之間通信方式改用mina。
- 鏈接管理器與openfire通信本身線程池優化,直接用mina提供的IoProcesser線程池
反空閑優化
設置idletime,防止空閑鏈接占用資源。openfire提供了好幾種方式
1. 客戶端主動發pingiq包,服務器會回包
2. 服務端主動發ping包,客戶端會回包
3. 運用mina本身提供的keep-alive機制
線程池優化
線程池優化的目的,是使系統使用較少的線程去高效的完成工作。具體分析需要用jstack來分析每一個線程。一般來講openfire維護幾百個線程(包括插件占用的)是比較正常的。
- 1~2個SocketAcceptor線程來處理建立鏈接,
- IoAcceptor用於監聽客戶端的連接,每監聽一個端口建立一個線程
- xmpp.processor.count=48 用48個線程處理SocketAcceptorIoProcessor,用來IO處理。
- xmpp.processor.threads.standard=64 xmpp.multiplex.processing.threads =64 用來做業務處理,也就是filter處理的過程
- openfire默認是用cpu核數個線程來處理epoll,如果cpu個數比較過多的話,可以減少線程數到8個,可用new NioSocketAcceptor( executor,processorCount );
網絡參數優化
-
net.ipv4.neigh.default.gc_stale_time = 120net.ipv4.conf.all.rp_filter = 0net.ipv4.conf.default.rp_filter = 0 //lvs內網通信需要net.ipv4.conf.default.arp_announce = 2net.ipv4.conf.all.arp_announce = 2net.ipv4.tcp_max_tw_buckets = 5000net.ipv4.tcp_syncookies = 1 //啟用syn cookiesnet.ipv4.tcp_max_syn_backlog = 8096 //同時,並發建立的鏈接不能及時處理的放到隊列的上限net.ipv4.tcp_synack_retries = 2 //syn ack沒有收到,最多做2次重試net.ipv6.conf.all.disable_ipv6 = 1net.ipv6.conf.default.disable_ipv6 = 1net.ipv6.conf.lo.disable_ipv6 = 1net.ipv4.conf.lo.arp_announce = 2net.ipv4.conf.eth0.rp_filter = 0net.ipv4.conf.eth1.rp_filter = 0net.ipv4.tcp_tw_reuse = 1net.ipv4.tcp_tw_recycle = 1net.ipv4.tcp_fin_timeout = 10 //對於進入fin狀態的,關掉鏈接的超時時間net.ipv4.ip_local_port_range = 1024 65000net.ipv4.tcp_rmem = 4096 16380 4194304 //讀緩沖區(tcp是雙工通道,讀寫通道是分離的)net.ipv4.tcp_wmem = 4096 16380 4194304 //寫緩沖區net.ipv4.tcp_mem = 678558 1004745 1457116 //總的內存vm.overcommit_memory = 1xmpp.socket.backlog=8096xmpp.socket.buffer.receive=-1(表示用系統設置)xmpp.socket.buffer.send=-1(表示用系統設置)xmpp.socket.linger=-1是否運用優雅關閉xmpp.socket.tcp-nodelay= true是否用Nagle算法vm.swappiness = 0net.ipv4.neigh.default.gc_stale_time=120net.ipv4.conf.all.rp_filter=0net.ipv4.conf.default.rp_filter=0net.ipv4.conf.default.arp_announce = 2net.ipv4.conf.all.arp_announce=2net.ipv4.tcp_max_tw_buckets = 5000net.ipv4.tcp_syncookies = 1net.ipv4.tcp_max_syn_backlog = 8192net.ipv4.tcp_synack_retries = 2net.ipv6.conf.all.disable_ipv6 = 1net.ipv6.conf.default.disable_ipv6 = 1net.ipv6.conf.lo.disable_ipv6 = 1net.ipv4.conf.lo.arp_announce=2net.ipv4.conf.eth0.rp_filter = 0net.core.rmem_default = 256960net.core.rmem_max = 513920net.core.wmem_default = 256960net.core.wmem_max = 513920net.core.netdev_max_backlog = 2000net.core.somaxconn = 8192net.core.optmem_max = 81920net.ipv4.tcp_mem = 131072 262144 624288net.ipv4.tcp_rmem = 8760 256960 6088000net.ipv4.tcp_wmem = 8760 256960 6088000net.ipv4.tcp_keepalive_time = 1800net.ipv4.tcp_keepalive_intvl = 30net.ipv4.tcp_keepalive_probes = 3net.ipv4.tcp_sack = 1net.ipv4.tcp_fack = 1net.ipv4.tcp_timestamps = 0net.ipv4.tcp_window_scaling = 1net.ipv4.tcp_tw_reuse = 1net.ipv4.tcp_tw_recycle = 0net.ipv4.tcp_fin_timeout = 30net.ipv4.ip_local_port_range = 1024 65535
StringBuilder優化
默認情況下,StringBuilder對象最多可容納16個字符之前自動調整大小本身。設置合適的長度,有助於防止JVM浪費時間申請內存和字符串復制。作為一個例子,因為我們知道stream header的大小為約500個字符,我們可以大小StringBuilder的恰如其分。
packet.createCopy
packet是用的DOM4J XML模型,所以復制很消耗性能。盡量避免用packet.createCopy
System.currentTimeMillis()
統計,打印日志,openfire本身會頻繁調用System..currentTimeMillis()。System.currentTimeMillis()的調用比new一個普通對象要耗時的多,因為會有一次系統調用。對於一些對時間精確度不是很高的情況下,可以改用內存存儲一小段時間內的值。
禁用roster花名冊、禁用presence包通訊
如果可能的話,可以禁用roster服務,改用http接口拉取好友列表,並且客戶端做緩存。
ping包優化
不管是服務端主動發ping,還是客戶端發ping包,不可否認的是,ping包占據openfire處理消息量最大一類。如下圖

差不多是消息發送的10倍左右,雖然ping包處理很簡單,耗時很短,但量大了,還是需要考慮優化的。我的優化方案就是把對於客戶端發的ping包處理移到鏈接管理器去。
A->server回執優化
由於我們的IM系統是做了送達,也就是服務端收到消息后,會顯示的給用戶發個回執包。和ping包優化機制一樣,鏈接管理器收到消息后,就直接返回回執包。
id序列號生成器
SequenceManager鎖的粒度比較大,並發比較高的時候,就經常會鎖住。需要重寫。