1. 程式人生 > >smartFox錯誤記載發生session create, session remove問題

smartFox錯誤記載發生session create, session remove問題

● 遊戲伺服器偶爾出現如下針狀

2018-11-10 13:51:43,689 | INFO  | SocketReader | c.s.b.sessions.DefaultSessionManager | Session removed: { Id: 4, Type: DEFAULT, Logged: No, IP: 192.168.0.104:55975 }
2018-11-10 13:52:19,338 | INFO  | SocketReader | c.s.b.sessions.DefaultSessionManager | Session created: { Id: 5, Type: DEFAULT, Logged: No, IP: 192.168.0.104:55989 } on Server port: 9988 <---> 55989
2018-11-10 13:53:08,704 | INFO  | SocketReader | c.s.b.sessions.DefaultSessionManager | Session removed: { Id: 5, Type: DEFAULT, Logged: No, IP: 192.168.0.104:55989 }
2018-11-10 13:53:10,609 | INFO  | SocketReader | c.s.b.sessions.DefaultSessionManager | Session created: { Id: 6, Type: DEFAULT, Logged: No, IP: 192.168.0.104:55994 } on Server port: 9988 <---> 55994
2018-11-10 13:53:55,742 | INFO  | SocketReader | c.s.b.sessions.DefaultSessionManager | Session removed: { Id: 6, Type: DEFAULT, Logged: No, IP: 192.168.0.104:55994 }
2018-11-10 13:54:04,833 | INFO  | SocketReader | c.s.b.sessions.DefaultSessionManager | Session created: { Id: 7, Type: DEFAULT, Logged: No, IP: 192.168.0.104:56014 } on Server port: 9988 <---> 56014
2018-11-10 13:54:14,219 | INFO  | SocketReader | c.s.b.sessions.DefaultSessionManager | Session removed: { Id: 7, Type: DEFAULT, Logged: No, IP: 192.168.0.104:56014 }
2018-11-10 13:54:27,805 | INFO  | SocketReader | c.s.b.sessions.DefaultSessionManager | Session created: { Id: 8, Type: DEFAULT, Logged: No, IP: 192.168.0.104:56021 } on Server port: 9988 <---> 56021
2018-11-10 13:55:13,444 | INFO  | SocketReader | c.s.b.sessions.DefaultSessionManager | Session removed: { Id: 8, Type: DEFAULT, Logged: No, IP: 192.168.0.104:56021 }
2018-11-10 13:55:20,640 | INFO  | SocketReader | c.s.b.sessions.DefaultSessionManager | Session created: { Id: 9, Type: DEFAULT, Logged: No, IP: 192.168.0.104:56028 } on Server port: 9988 <---> 56028
2018-11-10 13:55:31,303 | INFO  | pool-2-thread-5 | c.s.v2.util.stats.CCULoggerTask | CCU stats: { Zone: --=={{{ AdminZone }}}==-- }, CCU: 0/0
2018-11-10 13:55:31,304 | INFO  | pool-2-thread-5 | c.s.v2.util.stats.CCULoggerTask | CCU stats: { Zone: COK1 }, CCU: 0/0
2018-11-10 13:55:31,304 | INFO  | pool-2-thread-5 | c.s.v2.util.stats.CCULoggerTask | CCU stats: CCU: 0/0
2018-11-10 13:56:05,317 | INFO  | SocketReader | c.s.b.sessions.DefaultSessionManager | Session removed: { Id: 9, Type: DEFAULT, Logged: No, IP: 192.168.0.104:56028 }
2018-11-10 13:56:12,304 | INFO  | SocketReader | c.s.b.sessions.DefaultSessionManager | Session created: { Id: 10, Type: DEFAULT, Logged: No, IP: 192.168.0.104:56036 } on Server port: 9988 <---> 56036
2018-11-10 13:57:43,277 | INFO  | SocketReader | c.s.b.sessions.DefaultSessionManager | Session removed: { Id: 10, Type: DEFAULT, Logged: No, IP: 192.168.0.104:56036 }
2018-11-10 13:58:13,233 | INFO  | SocketReader | c.s.b.sessions.DefaultSessionManager | Session created: { Id: 11, Type: DEFAULT, Logged: No, IP: 192.168.0.104:56050 } on Server port: 9988 <---> 56050
2018-11-10 13:58:18,775 | INFO  | SocketReader | c.s.b.sessions.DefaultSessionManager | Session removed: { Id: 11, Type: DEFAULT, Logged: No, IP: 192.168.0.104:56050 }
2018-11-10 13:58:36,421 | INFO  | SocketReader | c.s.b.sessions.DefaultSessionManager | Session created: { Id: 12, Type: DEFAULT, Logged: No, IP: 192.168.0.104:56058 } on Server port: 9988 <---> 56058
2018-11-10 13:58:57,724 | INFO  | SocketReader | c.s.b.sessions.DefaultSessionManager | Session removed: { Id: 12, Type: DEFAULT, Logged: No, IP: 192.168.0.104:56058 }
2018-11-10 13:59:06,673 | INFO  | SocketReader | c.s.b.sessions.DefaultSessionManager | Session created: { Id: 13, Type: DEFAULT, Logged: No, IP: 192.168.0.104:56062 } on Server port: 9988 <---> 56062

● 一旦出現此bug,所有玩家全部登入不上,線上玩家不受影響.只要是玩家執行登入流程,全部卡在50%介面,死活登入不了.
● 經過N久的研究,最後發現是執行緒的原因.讓我們慢慢道來.
首先,這個smartFox原始碼中用於執行登入流程的有一個執行緒池,這個執行緒池比較坑,看如下原始碼

public final class SFSEventManager extends BaseCoreService implements ISFSEventManager {
    private int corePoolSize = 4;
    private int maxPoolSize = 5;
    private int threadKeepAliveTime = 60;
    private final ThreadPoolExecutor threadPool;
    private final Map<SFSEventType, Set<ISFSEventListener>> listenersByEvent;
    private final Logger logger;

    public SFSEventManager() {
        this.setName("SFSEventManager");
        this.logger = LoggerFactory.getLogger(SFSEventManager.class);
        this.threadPool = new ThreadPoolExecutor(this.corePoolSize, this.maxPoolSize, (long)this.threadKeepAliveTime, TimeUnit.SECONDS, new LinkedBlockingQueue());
        this.listenersByEvent = new ConcurrentHashMap();
    }

如上圖所示,這個執行緒池只有4個核心執行緒,只有4個核心執行緒,只有4個核心執行緒!!重要的事說三遍,最大執行緒數量為5,然並卵,執行緒的數量永遠永遠永遠只可能是4個,為什麼呢?請看執行緒使用的阻塞佇列LinkedBlockingQueue,我們來看下預設阻塞佇列的大小:

    public LinkedBlockingQueue() {
        this(Integer.MAX_VALUE);
    }

看到沒,阻塞對列的大小是int的最大,也就是21E多,這是什麼概念?對於我們小遊戲公司來說,玩家登入次數可能永遠達不到這麼多,也就是說可能永遠也觸發不了新的執行緒,換一種說法,執行緒池最大大小5永遠觸發不了,執行緒池的執行緒大小永遠是核心數量大小4.這是一個坑的地方!

● 坑二

 上面我們說過用於執行登入流程的執行緒數量只有4個,如果遊戲玩家不多且併發不高,那麼還算稍微正常點,但是一旦遇到一些特殊情況,如遊戲剛開服,或者遊戲維護結束之後,此時有大批玩家登入,這就出現問題了.
 登入流程載入東西過多,假設一個玩家耗費5秒,此時若是1000個玩家同時登入,後面的玩家只能慢慢等待前面玩家登入走完才能登入.4個執行緒執行,每個玩家耗時5秒,想想第1000個玩家得等待多久!!!

● 坑三

	坑三也是最坑,前面幾個坑忍忍也就過去了,這個坑直接讓遊戲完蛋.為什麼呢?
	前面說過執行登入執行緒4個,only 4個,也就要求你要保證這4個執行緒不能出一丁點意外,比如執行緒阻塞,一旦出現執行緒阻塞了,那麼玩完了,4個執行緒同時阻塞(只要出現一個阻塞,四個很快同樣阻塞),導致後面登入的玩家永遠獲取不到執行緒用來執行登入.這樣就會出現頻繁的session create, session remove.
	我們做了個實驗,在登入邏輯處給執行緒睡一下,用一個號登入遊戲,這時候,一個執行緒被佔用,一直睡在那,反覆執行四次,所有的執行緒都被耗盡了,此時登入執行緒池已經無能為力了,它已經沒有能力建立新的執行緒來執行登入了,因為阻塞佇列根本超不出佇列的長度.也就是說最大執行緒數量5是無效的.

● 解決方法
1.有能力的改原始碼.
2.找到執行緒阻塞的原因
3.個人認為最重要的是如何讓阻塞的執行緒釋放