Webim Series
In step-by-step webim creation (1), we have introduced how to implement a simple webim. However, this webim has a problem that every time a message listener is added, you must access the database once to check whether there are any messages. Obviously, if there are many users, the pressure on the database is quite high. One way to solve this problem is to first cache the message in the memory and do not immediately write the message to the database until the cache is full. This article describes how to implement message caching.
Basic Ideas
A message Cache Management class is implemented to cache all messages by users. Each user corresponds to a list <message> and stores the new messages received by the user, message Cache Management uses a hashtable to save the list <message> corresponding to all users.
ImplementationCodeAs follows:
Public class Messagecachemanagement { Static Messagecachemanagement M_instance = New Messagecachemanagement (); Static public Messagecachemanagement Instance { Get { Return M_instance ;}} Private Messagecachemanagement (){} Int32 M_count = 0; Hashtable M_cache = New Hashtable (); List < Message > Getusermessagecache ( String User ){ If (! M_cache.containskey (User) {m_cache.add (user, New List < Message > ());} Return M_cache [user] As List < Message > ;} /// <Summary> /// Clear Cache /// </Summary> Public void Clear (){ Lock (M_cache ){ List < Message > Msgs = New List < Message > (); Foreach ( Dictionaryentry Ent In M_cache) {(Ent. Value As List < Message >). Clear () ;}m_count = 0 ;}} /// <Summary> /// Retrieve All cached messages /// </Summary> /// <returns> </returns> Public List < Message > Getall (){ Lock (M_cache ){ List < Message > Msgs = New List < Message > (); Foreach ( Dictionaryentry Ent In M_cache ){ Foreach ( Message MSG In Ent. Value As List < Message >) {Msgs. Add (MSG );}} Return Msgs ;}} /// <Summary> /// Minimum Time for obtaining cached messages of a user /// </Summary> Public Nullable < Datetime > Getmincreatedtime ( String User ){ Lock (M_cache ){ List < Message > Usermsgs = getusermessagecache (User ); Return Usermsgs. Count = 0? Null : New Nullable < Datetime > (Usermsgs [0]. createdtime );}} /// <Summary> /// Insert a message into the cache /// </Summary> /// <Param name = "user"> </param> /// <Param name = "MSG"> </param> Public void Insert ( String User, Message MSG ){ List < Message > Usermsgs = Null ; Lock (M_cache) {usermsgs = getusermessagecache (User );} Lock (Usermsgs) {usermsgs. Add (MSG); m_count ++ ;}} /// <Summary> /// Find the message whose recipient is user and whose sending time is later than from in the cache /// </Summary> Public List < Message > Find ( String User, Datetime From ){ List < Message > Usermsgs = Null ; Lock (M_cache) {usermsgs = getusermessagecache (User );} Lock (Usermsgs ){ List < Message > Msgs = New List < Message > (); Int I = 0; While (I <usermsgs. Count & usermsgs [I]. createdtime <= from) I ++; While (I <usermsgs. Count) {msgs. Add (usermsgs [I]); I ++ ;} Return Msgs ;}} /// <Summary> /// Retrieve total messages /// </Summary> Public Int32 Count { Get { Return M_count ;}}}
Add message listener
After the message cache is added, the process for adding a message listener must also be modified. The specific idea is to first obtain the sending time of the earliest message sent by the message receiver in the cache. Obviously, if the listener's from is greater than or equal to the minimum sending time, you can directly access the cache without accessing the database. Modify the code:
/// <Summary> /// Add a message listener. If a message that meets the listener conditions is found, false is returned. No listener is added. /// If no message meeting the listener condition is found, true is returned, and the listener is added to m_listeners. /// </Summary> Public bool Addlistener ( String Aggreger, String Sender, Nullable < Datetime > From, Webim_asyncresult Asynresult ){ Messagelistener Listener = New Messagelistener (Aggreger, sender, from, asynresult ); Lock (M_lock ){ If (! M_listeners.containskey (receiver) {m_listeners.add (receiver, New List < Messagelistener > ());} List < Messagelistener > Listeners = m_listeners [Cycler] As List < Messagelistener >; // Obtain the minimum sending time of the message cached by the consumer Nullable < Datetime > Min = Messagecachemanagement . Instance. getmincreatedtime (Consumer ER ); List < Message > Messages = New List < Message > (); // When from> = the minimum time for messages cached in memory, you do not need to query the database If (Min = Null | From = Null | From. value <min. Value ){ // Query the database Messages. addrange (find (receiver er, sender, from ));} // Query in cache Messages. addrange ( Messagecachemanagement . Instance. Find (aggreger, from. Value )); If (Messages. Count = 0 ){ // Insert the listener Listeners. Add (listener );} Else { // Send a message Listener. Send (messages );} Return Messages. Count = 0 ;}}
Send message
After the message cache is added, the message sending process must also be modified. The specific idea is to save the message to the cache and then determine the total number of cached messages. If the maximum number of messages is exceeded, write messages to the database. Modify the code to (you can modify max_cache_count to modify the maximum number of cached messages ):
/// <Summary> /// Insert a new message. After the message is inserted, check whether there is a qualified listener in m_listeners. If yes, send the message. /// </Summary> Public Message Newmessage ( String Aggreger, String Sender, Datetime Createdtime, String Content ){ Lock (M_lock ){ Message Message = New Message (Sender, aggreger, content, createdtime, ++ m_maxkey ); List < Message > Messages = New List < Message > (); Messages. Add (Message ); If (M_listeners.containskey (receiver )){ List < Messagelistener > Listeners = m_listeners [Cycler] As List < Messagelistener >; List < Messagelistener > Removelisteners = New List < Messagelistener > (); Foreach ( Messagelistener Listener In Listeners ){ If (Listener. Sender = "*" | String . Compare (listener. Sender, sender, True ) = 0) & (listener. From = Null | Message. createdtime> listener. From) {listener. Send (messages); removelisteners. Add (listener); system. threading. Threadpool . Queueuserworkitem ( New System. threading. Waitcallback (Listener. Complete ));}} Foreach ( Messagelistener Listener In Removelisteners ){ // Remove the listener Listeners. Remove (listener );}} Messagecachemanagement . Instance. insert (receiver, message ); If ( Messagecachemanagement . Instance. Count> = max_cache_count ){// When the maximum value of the cache is exceeded, all messages in the cache are written to the database. // Start the transaction Sqlitetransaction Trans = m_conn.begintransaction (); Try { List < Message > Cachemsgs = Messagecachemanagement . Instance. getall (); Foreach ( Message MSG In Cachemsgs ){ Sqlitecommand Cmd = New Sqlitecommand ( "Insert into message (author er, sender, content, createdtime, key) values (?,?,?,?,?) " , M_conn); cmd. Parameters. Add ( "Cycler" , Dbtype . String). value = msg. Cycler; cmd. Parameters. Add ( "Sender" , Dbtype . String). value = msg. Sender; cmd. Parameters. Add ( "Content" , Dbtype . String). value = msg. content; cmd. Parameters. Add ( "Createdtime" , Dbtype . Datetime). value = msg. createdtime; cmd. Parameters. Add ( "Key" , Dbtype . Int64). value = MSG. Key; cmd. executenonquery ();} Trans. Commit ();} Catch {TRANS. rollback ();} Messagecachemanagement . Instance. Clear ();} Return Message ;}}
Source code download (this timeSource codeOnly the messagemanagement. CS file has been modified. If you have any questions, contact me via webim)
If this article is helpful to you, do not forget to recommend