From 032aeb8b5d05f5f5a8ef8c6e0fe572a321717c35 Mon Sep 17 00:00:00 2001 From: Teravus Ovares Date: Wed, 29 Jul 2009 02:15:45 +0000 Subject: * Adds the ability to have a thread efficient long poll service (such as the eventqueue) * If this doesn't melt the Http Server, this will significantly reduce the number of threads in use on regions with many users. * Adds AddPollServiceHTTPHandler, and RemovePollServiceHTTPHandler to BaseHttpServer * Generic enough to be used for many long poll services, not only the EventQueue. --- .../Framework/Servers/HttpServer/BaseHttpServer.cs | 95 +++++++++++++- .../Servers/HttpServer/Interfaces/IHttpServer.cs | 5 + .../Servers/HttpServer/PollServiceEventArgs.cs | 53 ++++++++ .../Servers/HttpServer/PollServiceHttpRequest.cs | 48 +++++++ .../HttpServer/PollServiceRequestManager.cs | 145 +++++++++++++++++++++ .../Servers/HttpServer/PollServiceWorkerThread.cs | 100 ++++++++++++++ OpenSim/Framework/Servers/Tests/OSHttpTests.cs | 1 + 7 files changed, 442 insertions(+), 5 deletions(-) create mode 100644 OpenSim/Framework/Servers/HttpServer/PollServiceEventArgs.cs create mode 100644 OpenSim/Framework/Servers/HttpServer/PollServiceHttpRequest.cs create mode 100644 OpenSim/Framework/Servers/HttpServer/PollServiceRequestManager.cs create mode 100644 OpenSim/Framework/Servers/HttpServer/PollServiceWorkerThread.cs (limited to 'OpenSim/Framework/Servers') diff --git a/OpenSim/Framework/Servers/HttpServer/BaseHttpServer.cs b/OpenSim/Framework/Servers/HttpServer/BaseHttpServer.cs index 4532e76..369d7d4 100644 --- a/OpenSim/Framework/Servers/HttpServer/BaseHttpServer.cs +++ b/OpenSim/Framework/Servers/HttpServer/BaseHttpServer.cs @@ -64,6 +64,9 @@ namespace OpenSim.Framework.Servers.HttpServer protected Dictionary m_HTTPHandlers = new Dictionary(); protected Dictionary m_agentHandlers = new Dictionary(); + protected Dictionary m_pollHandlers = + new Dictionary(); + protected uint m_port; protected uint m_sslport; protected bool m_ssl; @@ -72,6 +75,8 @@ namespace OpenSim.Framework.Servers.HttpServer protected IPAddress m_listenIPAddress = IPAddress.Any; + private PollServiceRequestManager m_PollServiceManager; + public uint SSLPort { get { return m_sslport; } @@ -189,6 +194,26 @@ namespace OpenSim.Framework.Servers.HttpServer return false; } + public bool AddPollServiceHTTPHandler(string methodName, GenericHTTPMethod handler, PollServiceEventArgs args) + { + bool pollHandlerResult = false; + lock (m_pollHandlers) + { + if (!m_pollHandlers.ContainsKey( methodName)) + { + m_pollHandlers.Add(methodName,args); + pollHandlerResult = true; + + } + } + + if (pollHandlerResult) + return AddHTTPHandler(methodName, handler); + + return false; + + } + // Note that the agent string is provided simply to differentiate // the handlers - it is NOT required to be an actual agent header // value. @@ -230,8 +255,19 @@ namespace OpenSim.Framework.Servers.HttpServer { IHttpClientContext context = (IHttpClientContext)source; IHttpRequest request = args.Request; + - OnHandleRequestIOThread(context,request); + PollServiceEventArgs psEvArgs; + if (TryGetPollServiceHTTPHandler(request.UriPath.ToString(), out psEvArgs)) + { + + m_PollServiceManager.Enqueue(new PollServiceHttpRequest(psEvArgs, context, request)); + //DoHTTPGruntWork(psEvArgs.NoEvents(),new OSHttpResponse(new HttpResponse(context, request))); + } + else + { + OnHandleRequestIOThread(context, request); + } } @@ -341,7 +377,7 @@ namespace OpenSim.Framework.Servers.HttpServer string requestBody = reader.ReadToEnd(); reader.Close(); - requestStream.Close(); + //requestStream.Close(); Hashtable keysvals = new Hashtable(); Hashtable headervals = new Hashtable(); @@ -527,6 +563,36 @@ namespace OpenSim.Framework.Servers.HttpServer } } + private bool TryGetPollServiceHTTPHandler(string handlerKey, out PollServiceEventArgs oServiceEventArgs) + { + string bestMatch = null; + + lock (m_pollHandlers) + { + foreach (string pattern in m_pollHandlers.Keys) + { + if (handlerKey.StartsWith(pattern)) + { + if (String.IsNullOrEmpty(bestMatch) || pattern.Length > bestMatch.Length) + { + bestMatch = pattern; + } + } + } + + if (String.IsNullOrEmpty(bestMatch)) + { + oServiceEventArgs = null; + return false; + } + else + { + oServiceEventArgs = m_pollHandlers[bestMatch]; + return true; + } + } + } + private bool TryGetHTTPHandler(string handlerKey, out GenericHTTPMethod HTTPHandler) { //m_log.DebugFormat("[BASE HTTP HANDLER]: Looking for HTTP handler for {0}", handlerKey); @@ -822,7 +888,7 @@ namespace OpenSim.Framework.Servers.HttpServer { response.Send(); response.OutputStream.Flush(); - response.OutputStream.Close(); + //response.OutputStream.Close(); } catch (IOException e) { @@ -1237,7 +1303,7 @@ namespace OpenSim.Framework.Servers.HttpServer } } - private static void DoHTTPGruntWork(Hashtable responsedata, OSHttpResponse response) + internal void DoHTTPGruntWork(Hashtable responsedata, OSHttpResponse response) { //m_log.Info("[BASE HTTP SERVER]: Doing HTTP Grunt work with response"); int responsecode = (int)responsedata["int_response_code"]; @@ -1261,7 +1327,7 @@ namespace OpenSim.Framework.Servers.HttpServer } //Even though only one other part of the entire code uses HTTPHandlers, we shouldn't expect this //and should check for NullReferenceExceptions - + if (string.IsNullOrEmpty(contentType)) { contentType = "text/html"; @@ -1402,6 +1468,8 @@ namespace OpenSim.Framework.Servers.HttpServer //m_workerThread.Start(); //ThreadTracker.Add(m_workerThread); StartHTTP(); + + } private void StartHTTP() @@ -1434,6 +1502,9 @@ namespace OpenSim.Framework.Servers.HttpServer m_httpListener2.RequestReceived += OnRequest; //m_httpListener.Start(); m_httpListener2.Start(64); + + // Long Poll Service Manager with 3 worker threads a 25 second timeout for no events + m_PollServiceManager = new PollServiceRequestManager(this, 3, 25000); HTTPDRunning = true; //HttpListenerContext context; @@ -1514,6 +1585,20 @@ namespace OpenSim.Framework.Servers.HttpServer } } + public void RemovePollServiceHTTPHandler(string httpMethod, string path) + { + lock (m_pollHandlers) + { + if (m_pollHandlers.ContainsKey(httpMethod)) + { + m_pollHandlers.Remove(httpMethod); + } + } + + RemoveHTTPHandler(httpMethod, path); + + } + public bool RemoveAgentHandler(string agent, IHttpAgentHandler handler) { try diff --git a/OpenSim/Framework/Servers/HttpServer/Interfaces/IHttpServer.cs b/OpenSim/Framework/Servers/HttpServer/Interfaces/IHttpServer.cs index c415dfb..9095831 100644 --- a/OpenSim/Framework/Servers/HttpServer/Interfaces/IHttpServer.cs +++ b/OpenSim/Framework/Servers/HttpServer/Interfaces/IHttpServer.cs @@ -74,6 +74,9 @@ namespace OpenSim.Framework.Servers.HttpServer /// bool AddHTTPHandler(string methodName, GenericHTTPMethod handler); + + bool AddPollServiceHTTPHandler(string methodName, GenericHTTPMethod handler, PollServiceEventArgs args); + /// /// Adds a LLSD handler, yay. /// @@ -114,6 +117,8 @@ namespace OpenSim.Framework.Servers.HttpServer /// /// void RemoveHTTPHandler(string httpMethod, string path); + + void RemovePollServiceHTTPHandler(string httpMethod, string path); bool RemoveLLSDHandler(string path, LLSDMethod handler); diff --git a/OpenSim/Framework/Servers/HttpServer/PollServiceEventArgs.cs b/OpenSim/Framework/Servers/HttpServer/PollServiceEventArgs.cs new file mode 100644 index 0000000..fed490e --- /dev/null +++ b/OpenSim/Framework/Servers/HttpServer/PollServiceEventArgs.cs @@ -0,0 +1,53 @@ +/* + * Copyright (c) Contributors, http://opensimulator.org/ + * See CONTRIBUTORS.TXT for a full list of copyright holders. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the OpenSimulator Project nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +using System; +using System.Collections; +using OpenMetaverse; +namespace OpenSim.Framework.Servers.HttpServer +{ + public delegate bool HasEventsMethod(UUID pId); + + public delegate Hashtable GetEventsMethod(UUID pId, string request); + + public delegate Hashtable NoEventsMethod(); + + public class PollServiceEventArgs : EventArgs + { + public HasEventsMethod HasEvents; + public GetEventsMethod GetEvents; + public NoEventsMethod NoEvents; + public UUID Id; + public PollServiceEventArgs(HasEventsMethod pHasEvents, GetEventsMethod pGetEvents, NoEventsMethod pNoEvents,UUID pId) + { + HasEvents = pHasEvents; + GetEvents = pGetEvents; + NoEvents = pNoEvents; + Id = pId; + } + } +} diff --git a/OpenSim/Framework/Servers/HttpServer/PollServiceHttpRequest.cs b/OpenSim/Framework/Servers/HttpServer/PollServiceHttpRequest.cs new file mode 100644 index 0000000..ff7c1e8 --- /dev/null +++ b/OpenSim/Framework/Servers/HttpServer/PollServiceHttpRequest.cs @@ -0,0 +1,48 @@ +/* + * Copyright (c) Contributors, http://opensimulator.org/ + * See CONTRIBUTORS.TXT for a full list of copyright holders. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the OpenSimulator Project nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +using System; +using HttpServer; + +namespace OpenSim.Framework.Servers.HttpServer +{ + + public class PollServiceHttpRequest + { + public readonly PollServiceEventArgs PollServiceArgs; + public readonly IHttpClientContext HttpContext; + public readonly IHttpRequest Request; + public readonly int RequestTime; + public PollServiceHttpRequest(PollServiceEventArgs pPollServiceArgs, IHttpClientContext pHttpContext, IHttpRequest pRequest) + { + PollServiceArgs = pPollServiceArgs; + HttpContext = pHttpContext; + Request = pRequest; + RequestTime = System.Environment.TickCount; + } + } +} diff --git a/OpenSim/Framework/Servers/HttpServer/PollServiceRequestManager.cs b/OpenSim/Framework/Servers/HttpServer/PollServiceRequestManager.cs new file mode 100644 index 0000000..7f632cf --- /dev/null +++ b/OpenSim/Framework/Servers/HttpServer/PollServiceRequestManager.cs @@ -0,0 +1,145 @@ +/* + * Copyright (c) Contributors, http://opensimulator.org/ + * See CONTRIBUTORS.TXT for a full list of copyright holders. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the OpenSimulator Project nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +using System; +using System.Collections; +using System.Threading; +using HttpServer; + +namespace OpenSim.Framework.Servers.HttpServer +{ + public class PollServiceRequestManager + { + private readonly BaseHttpServer m_server; + private static Queue m_requests = Queue.Synchronized(new Queue()); + private uint m_WorkerThreadCount = 0; + private Thread[] m_workerThreads; + private PollServiceWorkerThread[] m_PollServiceWorkerThreads; + private Thread m_watcherThread; + private bool m_running = true; + + + + public PollServiceRequestManager(BaseHttpServer pSrv, uint pWorkerThreadCount, int pTimeout) + { + m_server = pSrv; + m_WorkerThreadCount = pWorkerThreadCount; + m_workerThreads = new Thread[m_WorkerThreadCount]; + m_PollServiceWorkerThreads = new PollServiceWorkerThread[m_WorkerThreadCount]; + m_watcherThread = new Thread(ThreadStart); + + + //startup worker threads + for (uint i=0;i 0; tc++) + { + //Loop over number of requests each thread handles. + for (int i=0;i 0;i++) + { + try + { + m_PollServiceWorkerThreads[tc].Enqueue((PollServiceHttpRequest)m_requests.Dequeue()); + } + catch (InvalidOperationException) + { + // The queue is empty, we did our calculations wrong! + return; + } + + } + } + } + + } + + + + ~PollServiceRequestManager() + { + foreach (object o in m_requests) + { + PollServiceHttpRequest req = (PollServiceHttpRequest) o; + m_server.DoHTTPGruntWork(req.PollServiceArgs.NoEvents(), new OSHttpResponse(new HttpResponse(req.HttpContext, req.Request))); + } + + m_requests.Clear(); + + foreach (Thread t in m_workerThreads) + { + t.Abort(); + } + m_running = false; + } + } +} diff --git a/OpenSim/Framework/Servers/HttpServer/PollServiceWorkerThread.cs b/OpenSim/Framework/Servers/HttpServer/PollServiceWorkerThread.cs new file mode 100644 index 0000000..4c0be78 --- /dev/null +++ b/OpenSim/Framework/Servers/HttpServer/PollServiceWorkerThread.cs @@ -0,0 +1,100 @@ +using System; +using System.Collections; +using System.Collections.Generic; +/* + * Copyright (c) Contributors, http://opensimulator.org/ + * See CONTRIBUTORS.TXT for a full list of copyright holders. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the OpenSimulator Project nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +using System.IO; +using System.Text; +using HttpServer; +using OpenMetaverse; + +namespace OpenSim.Framework.Servers.HttpServer +{ + public delegate void ReQueuePollServiceItem(PollServiceHttpRequest req); + + public class PollServiceWorkerThread + { + public event ReQueuePollServiceItem ReQueue; + + private readonly BaseHttpServer m_server; + private BlockingQueue m_request; + private bool m_running = true; + private int m_timeout = 25000; + + + + public PollServiceWorkerThread(BaseHttpServer pSrv, int pTimeout) + { + m_request = new BlockingQueue(); + m_server = pSrv; + m_timeout = pTimeout; + } + + public void ThreadStart(object o) + { + Run(); + } + + public void Run() + { + while (m_running) + { + PollServiceHttpRequest req = m_request.Dequeue(); + if (req.PollServiceArgs.HasEvents(req.PollServiceArgs.Id)) + { + StreamReader str = new StreamReader(req.Request.Body); + + Hashtable responsedata = req.PollServiceArgs.GetEvents(req.PollServiceArgs.Id, str.ReadToEnd()); + m_server.DoHTTPGruntWork(responsedata, + new OSHttpResponse(new HttpResponse(req.HttpContext, req.Request))); + } + else + { + if ((Environment.TickCount - req.RequestTime) > m_timeout) + { + m_server.DoHTTPGruntWork(req.PollServiceArgs.NoEvents(), + new OSHttpResponse(new HttpResponse(req.HttpContext, req.Request))); + } + else + { + ReQueuePollServiceItem reQueueItem = ReQueue; + if (reQueueItem != null) + reQueueItem(req); + } + } + } + + + } + + internal void Enqueue(PollServiceHttpRequest pPollServiceHttpRequest) + { + m_request.Enqueue(pPollServiceHttpRequest); + } + } +} diff --git a/OpenSim/Framework/Servers/Tests/OSHttpTests.cs b/OpenSim/Framework/Servers/Tests/OSHttpTests.cs index f7f0afa..a6a90dc 100644 --- a/OpenSim/Framework/Servers/Tests/OSHttpTests.cs +++ b/OpenSim/Framework/Servers/Tests/OSHttpTests.cs @@ -67,6 +67,7 @@ namespace OpenSim.Framework.Servers.Tests public void Send(byte[] buffer) {} public void Send(byte[] buffer, int offset, int size) {} public void Respond(string httpVersion, HttpStatusCode statusCode, string reason, string body, string contentType) {} + public void Close() { } public event EventHandler Disconnected = delegate { }; /// -- cgit v1.1