Add request received/handling stats for caps which are served by http poll handlers.

This adds explicit cap poll handler supporting to the Caps classes rather than relying on callers to do the complicated coding.
Other refactoring was required to get logic into the right places to support this.
cpu-performance
Justin Clark-Casey (justincc) 2013-07-15 23:22:39 +01:00
parent d06c85ea77
commit 1b7b664c86
11 changed files with 268 additions and 127 deletions

View File

@ -63,7 +63,11 @@ namespace OpenSim.Framework.Capabilities
public string CapsObjectPath { get { return m_capsObjectPath; } }
private CapsHandlers m_capsHandlers;
private Dictionary<string, string> m_externalCapsHandlers;
private Dictionary<string, PollServiceEventArgs> m_pollServiceHandlers
= new Dictionary<string, PollServiceEventArgs>();
private Dictionary<string, string> m_externalCapsHandlers = new Dictionary<string, string>();
private IHttpServer m_httpListener;
private UUID m_agentID;
@ -132,7 +136,6 @@ namespace OpenSim.Framework.Capabilities
m_agentID = agent;
m_capsHandlers = new CapsHandlers(httpServer, httpListen, httpPort, (httpServer == null) ? false : httpServer.UseSSL);
m_externalCapsHandlers = new Dictionary<string, string>();
m_regionName = regionName;
}
@ -147,6 +150,27 @@ namespace OpenSim.Framework.Capabilities
m_capsHandlers[capName] = handler;
}
public void RegisterPollHandler(string capName, PollServiceEventArgs pollServiceHandler)
{
m_pollServiceHandlers.Add(capName, pollServiceHandler);
m_httpListener.AddPollServiceHTTPHandler(pollServiceHandler.Url, pollServiceHandler);
// uint port = (MainServer.Instance == null) ? 0 : MainServer.Instance.Port;
// string protocol = "http";
// string hostName = m_httpListenerHostName;
//
// if (MainServer.Instance.UseSSL)
// {
// hostName = MainServer.Instance.SSLCommonName;
// port = MainServer.Instance.SSLPort;
// protocol = "https";
// }
// RegisterHandler(
// capName, String.Format("{0}://{1}:{2}{3}", protocol, hostName, port, pollServiceHandler.Url));
}
/// <summary>
/// Register an external handler. The service for this capability is somewhere else
/// given by the URL.
@ -163,13 +187,70 @@ namespace OpenSim.Framework.Capabilities
/// </summary>
public void DeregisterHandlers()
{
if (m_capsHandlers != null)
foreach (string capsName in m_capsHandlers.Caps)
{
foreach (string capsName in m_capsHandlers.Caps)
{
m_capsHandlers.Remove(capsName);
}
m_capsHandlers.Remove(capsName);
}
foreach (PollServiceEventArgs handler in m_pollServiceHandlers.Values)
{
m_httpListener.RemovePollServiceHTTPHandler("", handler.Url);
}
}
public bool TryGetPollHandler(string name, out PollServiceEventArgs pollHandler)
{
return m_pollServiceHandlers.TryGetValue(name, out pollHandler);
}
public Dictionary<string, PollServiceEventArgs> GetPollHandlers()
{
return new Dictionary<string, PollServiceEventArgs>(m_pollServiceHandlers);
}
/// <summary>
/// Return an LLSD-serializable Hashtable describing the
/// capabilities and their handler details.
/// </summary>
/// <param name="excludeSeed">If true, then exclude the seed cap.</param>
public Hashtable GetCapsDetails(bool excludeSeed, List<string> requestedCaps)
{
Hashtable caps = CapsHandlers.GetCapsDetails(excludeSeed, requestedCaps);
lock (m_pollServiceHandlers)
{
foreach (KeyValuePair <string, PollServiceEventArgs> kvp in m_pollServiceHandlers)
{
if (!requestedCaps.Contains(kvp.Key))
continue;
string hostName = m_httpListenerHostName;
uint port = (MainServer.Instance == null) ? 0 : MainServer.Instance.Port;
string protocol = "http";
if (MainServer.Instance.UseSSL)
{
hostName = MainServer.Instance.SSLCommonName;
port = MainServer.Instance.SSLPort;
protocol = "https";
}
//
// caps.RegisterHandler("FetchInventoryDescendents2", String.Format("{0}://{1}:{2}{3}", protocol, hostName, port, capUrl));
caps[kvp.Key] = string.Format("{0}://{1}:{2}{3}", protocol, hostName, port, kvp.Value.Url);
}
}
// Add the external too
foreach (KeyValuePair<string, string> kvp in ExternalCapsHandlers)
{
if (!requestedCaps.Contains(kvp.Key))
continue;
caps[kvp.Key] = kvp.Value;
}
return caps;
}
}
}
}

View File

@ -234,7 +234,7 @@ namespace OpenSim.Framework.Console
string uri = "/ReadResponses/" + sessionID.ToString() + "/";
m_Server.AddPollServiceHTTPHandler(
uri, new PollServiceEventArgs(null, HasEvents, GetEvents, NoEvents, sessionID,25000)); // 25 secs timeout
uri, new PollServiceEventArgs(null, uri, HasEvents, GetEvents, NoEvents, sessionID,25000)); // 25 secs timeout
XmlDocument xmldoc = new XmlDocument();
XmlNode xmlnode = xmldoc.CreateNode(XmlNodeType.XmlDeclaration,

View File

@ -387,6 +387,8 @@ namespace OpenSim.Framework.Servers.HttpServer
if (TryGetPollServiceHTTPHandler(request.UriPath.ToString(), out psEvArgs))
{
psEvArgs.RequestsReceived++;
PollServiceHttpRequest psreq = new PollServiceHttpRequest(psEvArgs, context, request);
if (psEvArgs.Request != null)

View File

@ -55,12 +55,26 @@ namespace OpenSim.Framework.Servers.HttpServer
Inventory = 2
}
public string Url { get; set; }
/// <summary>
/// Number of requests received for this poll service.
/// </summary>
public int RequestsReceived { get; set; }
/// <summary>
/// Number of requests handled by this poll service.
/// </summary>
public int RequestsHandled { get; set; }
public PollServiceEventArgs(
RequestMethod pRequest,
string pUrl,
HasEventsMethod pHasEvents, GetEventsMethod pGetEvents, NoEventsMethod pNoEvents,
UUID pId, int pTimeOutms)
{
Request = pRequest;
Url = pUrl;
HasEvents = pHasEvents;
GetEvents = pGetEvents;
NoEvents = pNoEvents;

View File

@ -26,13 +26,19 @@
*/
using System;
using System.Collections;
using System.Reflection;
using System.Text;
using HttpServer;
using log4net;
using OpenMetaverse;
namespace OpenSim.Framework.Servers.HttpServer
{
public class PollServiceHttpRequest
{
private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
public readonly PollServiceEventArgs PollServiceArgs;
public readonly IHttpClientContext HttpContext;
public readonly IHttpRequest Request;
@ -48,5 +54,44 @@ namespace OpenSim.Framework.Servers.HttpServer
RequestTime = System.Environment.TickCount;
RequestID = UUID.Random();
}
internal void DoHTTPGruntWork(BaseHttpServer server, Hashtable responsedata)
{
OSHttpResponse response
= new OSHttpResponse(new HttpResponse(HttpContext, Request), HttpContext);
byte[] buffer = server.DoHTTPGruntWork(responsedata, response);
response.SendChunked = false;
response.ContentLength64 = buffer.Length;
response.ContentEncoding = Encoding.UTF8;
try
{
response.OutputStream.Write(buffer, 0, buffer.Length);
}
catch (Exception ex)
{
m_log.Warn(string.Format("[POLL SERVICE WORKER THREAD]: Error ", ex));
}
finally
{
//response.OutputStream.Close();
try
{
response.OutputStream.Flush();
response.Send();
//if (!response.KeepAlive && response.ReuseContext)
// response.FreeContext();
}
catch (Exception e)
{
m_log.Warn(String.Format("[POLL SERVICE WORKER THREAD]: Error ", e));
}
PollServiceArgs.RequestsHandled++;
}
}
}
}

View File

@ -157,8 +157,7 @@ namespace OpenSim.Framework.Servers.HttpServer
{
foreach (PollServiceHttpRequest req in m_retryRequests)
{
DoHTTPGruntWork(m_server,req,
req.PollServiceArgs.NoEvents(req.RequestID, req.PollServiceArgs.Id));
req.DoHTTPGruntWork(m_server, req.PollServiceArgs.NoEvents(req.RequestID, req.PollServiceArgs.Id));
}
}
catch
@ -179,8 +178,8 @@ namespace OpenSim.Framework.Servers.HttpServer
try
{
wreq = m_requests.Dequeue(0);
DoHTTPGruntWork(m_server,wreq,
wreq.PollServiceArgs.NoEvents(wreq.RequestID, wreq.PollServiceArgs.Id));
wreq.DoHTTPGruntWork(
m_server, wreq.PollServiceArgs.NoEvents(wreq.RequestID, wreq.PollServiceArgs.Id));
}
catch
{
@ -214,7 +213,7 @@ namespace OpenSim.Framework.Servers.HttpServer
{
try
{
DoHTTPGruntWork(m_server, req, responsedata);
req.DoHTTPGruntWork(m_server, responsedata);
}
catch (ObjectDisposedException) // Browser aborted before we could read body, server closed the stream
{
@ -227,7 +226,7 @@ namespace OpenSim.Framework.Servers.HttpServer
{
try
{
DoHTTPGruntWork(m_server, req, responsedata);
req.DoHTTPGruntWork(m_server, responsedata);
}
catch (ObjectDisposedException) // Browser aborted before we could read body, server closed the stream
{
@ -242,8 +241,8 @@ namespace OpenSim.Framework.Servers.HttpServer
{
if ((Environment.TickCount - req.RequestTime) > req.PollServiceArgs.TimeOutms)
{
DoHTTPGruntWork(m_server, req,
req.PollServiceArgs.NoEvents(req.RequestID, req.PollServiceArgs.Id));
req.DoHTTPGruntWork(
m_server, req.PollServiceArgs.NoEvents(req.RequestID, req.PollServiceArgs.Id));
}
else
{
@ -258,46 +257,5 @@ namespace OpenSim.Framework.Servers.HttpServer
}
}
}
// DoHTTPGruntWork changed, not sending response
// do the same work around as core
internal static void DoHTTPGruntWork(BaseHttpServer server, PollServiceHttpRequest req, Hashtable responsedata)
{
OSHttpResponse response
= new OSHttpResponse(new HttpResponse(req.HttpContext, req.Request), req.HttpContext);
byte[] buffer = server.DoHTTPGruntWork(responsedata, response);
response.SendChunked = false;
response.ContentLength64 = buffer.Length;
response.ContentEncoding = Encoding.UTF8;
try
{
response.OutputStream.Write(buffer, 0, buffer.Length);
}
catch (Exception ex)
{
m_log.Warn(string.Format("[POLL SERVICE WORKER THREAD]: Error ", ex));
}
finally
{
//response.OutputStream.Close();
try
{
response.OutputStream.Flush();
response.Send();
//if (!response.KeepAlive && response.ReuseContext)
// response.FreeContext();
}
catch (Exception e)
{
m_log.Warn(String.Format("[POLL SERVICE WORKER THREAD]: Error ", e));
}
}
}
}
}
}

View File

@ -285,18 +285,7 @@ namespace OpenSim.Region.ClientStack.Linden
foreach (OSD c in capsRequested)
validCaps.Add(c.AsString());
Hashtable caps = m_HostCapsObj.CapsHandlers.GetCapsDetails(true, validCaps);
// Add the external too
foreach (KeyValuePair<string, string> kvp in m_HostCapsObj.ExternalCapsHandlers)
{
if (!validCaps.Contains(kvp.Key))
continue;
caps[kvp.Key] = kvp.Value;
}
string result = LLSDHelpers.SerialiseLLSDReply(caps);
string result = LLSDHelpers.SerialiseLLSDReply(m_HostCapsObj.GetCapsDetails(true, validCaps));
//m_log.DebugFormat("[CAPS] CapsRequest {0}", result);

View File

@ -377,7 +377,7 @@ namespace OpenSim.Region.ClientStack.Linden
// TODO: Add EventQueueGet name/description for diagnostics
MainServer.Instance.AddPollServiceHTTPHandler(
eventQueueGetPath,
new PollServiceEventArgs(null, HasEvents, GetEvents, NoEvents, agentID, 40000));
new PollServiceEventArgs(null, eventQueueGetPath, HasEvents, GetEvents, NoEvents, agentID, 40000));
// m_log.DebugFormat(
// "[EVENT QUEUE GET MODULE]: Registered EQG handler {0} for {1} in {2}",

View File

@ -77,7 +77,6 @@ namespace OpenSim.Region.ClientStack.Linden
private static WebFetchInvDescHandler m_webFetchHandler;
private Dictionary<UUID, string> m_capsDict = new Dictionary<UUID, string>();
private static Thread[] m_workerThreads = null;
private static DoubleQueue<aPollRequest> m_queue =
@ -114,7 +113,6 @@ namespace OpenSim.Region.ClientStack.Linden
return;
m_scene.EventManager.OnRegisterCaps -= RegisterCaps;
m_scene.EventManager.OnDeregisterCaps -= DeregisterCaps;
foreach (Thread t in m_workerThreads)
Watchdog.AbortThread(t.ManagedThreadId);
@ -134,7 +132,6 @@ namespace OpenSim.Region.ClientStack.Linden
m_webFetchHandler = new WebFetchInvDescHandler(m_InventoryService, m_LibraryService);
m_scene.EventManager.OnRegisterCaps += RegisterCaps;
m_scene.EventManager.OnDeregisterCaps += DeregisterCaps;
if (m_workerThreads == null)
{
@ -177,8 +174,8 @@ namespace OpenSim.Region.ClientStack.Linden
private Scene m_scene;
public PollServiceInventoryEventArgs(Scene scene, UUID pId) :
base(null, null, null, null, pId, int.MaxValue)
public PollServiceInventoryEventArgs(Scene scene, string url, UUID pId) :
base(null, url, null, null, null, pId, int.MaxValue)
{
m_scene = scene;
@ -308,40 +305,39 @@ namespace OpenSim.Region.ClientStack.Linden
if (m_fetchInventoryDescendents2Url == "")
return;
string capUrl = "/CAPS/" + UUID.Random() + "/";
// Register this as a poll service
PollServiceInventoryEventArgs args = new PollServiceInventoryEventArgs(m_scene, agentID);
PollServiceInventoryEventArgs args
= new PollServiceInventoryEventArgs(m_scene, "/CAPS/" + UUID.Random() + "/", agentID);
args.Type = PollServiceEventArgs.EventType.Inventory;
MainServer.Instance.AddPollServiceHTTPHandler(capUrl, args);
string hostName = m_scene.RegionInfo.ExternalHostName;
uint port = (MainServer.Instance == null) ? 0 : MainServer.Instance.Port;
string protocol = "http";
if (MainServer.Instance.UseSSL)
{
hostName = MainServer.Instance.SSLCommonName;
port = MainServer.Instance.SSLPort;
protocol = "https";
}
caps.RegisterPollHandler("FetchInventoryDescendents2", args);
caps.RegisterHandler("FetchInventoryDescendents2", String.Format("{0}://{1}:{2}{3}", protocol, hostName, port, capUrl));
m_capsDict[agentID] = capUrl;
// MainServer.Instance.AddPollServiceHTTPHandler(capUrl, args);
//
// string hostName = m_scene.RegionInfo.ExternalHostName;
// uint port = (MainServer.Instance == null) ? 0 : MainServer.Instance.Port;
// string protocol = "http";
//
// if (MainServer.Instance.UseSSL)
// {
// hostName = MainServer.Instance.SSLCommonName;
// port = MainServer.Instance.SSLPort;
// protocol = "https";
// }
//
// caps.RegisterHandler("FetchInventoryDescendents2", String.Format("{0}://{1}:{2}{3}", protocol, hostName, port, capUrl));
}
private void DeregisterCaps(UUID agentID, Caps caps)
{
string capUrl;
if (m_capsDict.TryGetValue(agentID, out capUrl))
{
MainServer.Instance.RemoveHTTPHandler("", capUrl);
m_capsDict.Remove(agentID);
}
}
// private void DeregisterCaps(UUID agentID, Caps caps)
// {
// string capUrl;
//
// if (m_capsDict.TryGetValue(agentID, out capUrl))
// {
// MainServer.Instance.RemoveHTTPHandler("", capUrl);
// m_capsDict.Remove(agentID);
// }
// }
private void DoInventoryRequests()
{
@ -355,4 +351,4 @@ namespace OpenSim.Region.ClientStack.Linden
}
}
}
}
}

View File

@ -80,7 +80,7 @@ namespace OpenSim.Region.CoreModules.Framework
MainConsole.Instance.Commands.AddCommand(
"Comms", false, "show caps stats by user",
"show caps stats [<first-name> <last-name>]",
"show caps stats by user [<first-name> <last-name>]",
"Shows statistics on capabilities use by user.",
"If a user name is given, then prints a detailed breakdown of caps use ordered by number of requests received.",
HandleShowCapsStatsByUserCommand);
@ -285,27 +285,31 @@ namespace OpenSim.Region.CoreModules.Framework
if (SceneManager.Instance.CurrentScene != null && SceneManager.Instance.CurrentScene != m_scene)
return;
StringBuilder caps = new StringBuilder();
caps.AppendFormat("Region {0}:\n", m_scene.RegionInfo.RegionName);
StringBuilder capsReport = new StringBuilder();
capsReport.AppendFormat("Region {0}:\n", m_scene.RegionInfo.RegionName);
lock (m_capsObjects)
{
foreach (KeyValuePair<UUID, Caps> kvp in m_capsObjects)
{
caps.AppendFormat("** User {0}:\n", kvp.Key);
capsReport.AppendFormat("** User {0}:\n", kvp.Key);
Caps caps = kvp.Value;
for (IDictionaryEnumerator kvp2 = kvp.Value.CapsHandlers.GetCapsDetails(false, null).GetEnumerator(); kvp2.MoveNext(); )
for (IDictionaryEnumerator kvp2 = caps.CapsHandlers.GetCapsDetails(false, null).GetEnumerator(); kvp2.MoveNext(); )
{
Uri uri = new Uri(kvp2.Value.ToString());
caps.AppendFormat(m_showCapsCommandFormat, kvp2.Key, uri.PathAndQuery);
capsReport.AppendFormat(m_showCapsCommandFormat, kvp2.Key, uri.PathAndQuery);
}
foreach (KeyValuePair<string, string> kvp3 in kvp.Value.ExternalCapsHandlers)
caps.AppendFormat(m_showCapsCommandFormat, kvp3.Key, kvp3.Value);
foreach (KeyValuePair<string, PollServiceEventArgs> kvp2 in caps.GetPollHandlers())
capsReport.AppendFormat(m_showCapsCommandFormat, kvp2.Key, kvp2.Value.Url);
foreach (KeyValuePair<string, string> kvp3 in caps.ExternalCapsHandlers)
capsReport.AppendFormat(m_showCapsCommandFormat, kvp3.Key, kvp3.Value);
}
}
MainConsole.Instance.Output(caps.ToString());
MainConsole.Instance.Output(capsReport.ToString());
}
private void HandleShowCapsStatsByCapCommand(string module, string[] cmdParams)
@ -362,7 +366,16 @@ namespace OpenSim.Region.CoreModules.Framework
{
receivedStats[sp.Name] = reqHandler.RequestsReceived;
handledStats[sp.Name] = reqHandler.RequestsHandled;
}
}
else
{
PollServiceEventArgs pollHandler = null;
if (caps.TryGetPollHandler(capName, out pollHandler))
{
receivedStats[sp.Name] = pollHandler.RequestsReceived;
handledStats[sp.Name] = pollHandler.RequestsHandled;
}
}
}
);
@ -391,11 +404,9 @@ namespace OpenSim.Region.CoreModules.Framework
Caps caps = m_scene.CapsModule.GetCapsForUser(sp.UUID);
if (caps == null)
return;
return;
Dictionary<string, IRequestHandler> capsHandlers = caps.CapsHandlers.GetCapsHandlers();
foreach (IRequestHandler reqHandler in capsHandlers.Values)
foreach (IRequestHandler reqHandler in caps.CapsHandlers.GetCapsHandlers().Values)
{
string reqName = reqHandler.Name ?? "";
@ -410,6 +421,23 @@ namespace OpenSim.Region.CoreModules.Framework
handledStats[reqName] += reqHandler.RequestsHandled;
}
}
foreach (KeyValuePair<string, PollServiceEventArgs> kvp in caps.GetPollHandlers())
{
string name = kvp.Key;
PollServiceEventArgs pollHandler = kvp.Value;
if (!receivedStats.ContainsKey(name))
{
receivedStats[name] = pollHandler.RequestsReceived;
handledStats[name] = pollHandler.RequestsHandled;
}
else
{
receivedStats[name] += pollHandler.RequestsReceived;
handledStats[name] += pollHandler.RequestsHandled;
}
}
}
);
@ -468,12 +496,16 @@ namespace OpenSim.Region.CoreModules.Framework
if (caps == null)
return;
Dictionary<string, IRequestHandler> capsHandlers = caps.CapsHandlers.GetCapsHandlers();
List<CapTableRow> capRows = new List<CapTableRow>();
foreach (IRequestHandler reqHandler in capsHandlers.Values.OrderByDescending(rh => rh.RequestsReceived))
{
cdt.AddRow(reqHandler.Name, reqHandler.RequestsReceived, reqHandler.RequestsHandled);
}
foreach (IRequestHandler reqHandler in caps.CapsHandlers.GetCapsHandlers().Values)
capRows.Add(new CapTableRow(reqHandler.Name, reqHandler.RequestsReceived, reqHandler.RequestsHandled));
foreach (KeyValuePair<string, PollServiceEventArgs> kvp in caps.GetPollHandlers())
capRows.Add(new CapTableRow(kvp.Key, kvp.Value.RequestsReceived, kvp.Value.RequestsHandled));
foreach (CapTableRow ctr in capRows.OrderByDescending(ctr => ctr.RequestsReceived))
cdt.AddRow(ctr.Name, ctr.RequestsReceived, ctr.RequestsHandled);
sb.Append(cdt.ToString());
}
@ -505,6 +537,14 @@ namespace OpenSim.Region.CoreModules.Framework
totalRequestsReceived += reqHandler.RequestsReceived;
totalRequestsHandled += reqHandler.RequestsHandled;
}
Dictionary<string, PollServiceEventArgs> capsPollHandlers = caps.GetPollHandlers();
foreach (PollServiceEventArgs handler in capsPollHandlers.Values)
{
totalRequestsReceived += handler.RequestsReceived;
totalRequestsHandled += handler.RequestsHandled;
}
cdt.AddRow(sp.Name, sp.IsChildAgent ? "child" : "root", totalRequestsReceived, totalRequestsHandled);
}
@ -512,5 +552,19 @@ namespace OpenSim.Region.CoreModules.Framework
sb.Append(cdt.ToString());
}
private class CapTableRow
{
public string Name { get; set; }
public int RequestsReceived { get; set; }
public int RequestsHandled { get; set; }
public CapTableRow(string name, int requestsReceived, int requestsHandled)
{
Name = name;
RequestsReceived = requestsReceived;
RequestsHandled = requestsHandled;
}
}
}
}

View File

@ -235,7 +235,8 @@ namespace OpenSim.Region.CoreModules.Scripting.LSLHttp
string uri = "/lslhttp/" + urlcode.ToString() + "/";
PollServiceEventArgs args = new PollServiceEventArgs(HttpRequestHandler, HasEvents, GetEvents, NoEvents, urlcode, 25000);
PollServiceEventArgs args
= new PollServiceEventArgs(HttpRequestHandler, uri, HasEvents, GetEvents, NoEvents, urlcode, 25000);
args.Type = PollServiceEventArgs.EventType.LslHttp;
m_HttpServer.AddPollServiceHTTPHandler(uri, args);
@ -280,7 +281,8 @@ namespace OpenSim.Region.CoreModules.Scripting.LSLHttp
string uri = "/lslhttps/" + urlcode.ToString() + "/";
PollServiceEventArgs args = new PollServiceEventArgs(HttpRequestHandler, HasEvents, GetEvents, NoEvents, urlcode, 25000);
PollServiceEventArgs args
= new PollServiceEventArgs(HttpRequestHandler, uri, HasEvents, GetEvents, NoEvents, urlcode, 25000);
args.Type = PollServiceEventArgs.EventType.LslHttp;
m_HttpsServer.AddPollServiceHTTPHandler(uri, args);