开发者

.NET 2 Serviced Component (COM+) efficiency problem

This is the first time I've create a .NET Serviced Component (COM+) and I'm having some issues when it comes to scalability. I've stress tested it up to 4000 users but it is still failing to exceed the performance of the original solution.

The basic idea behind it is the COM+ holds a static class containing a dictionary in memory and an ASP page calls the component to retrieve an object from the dictionary. There are between 6000 and 12000 items in the dictionary at any one time.

The static dictionary class looks like this:

public class Menus
{
    public static Dictionary<string, MenuPage> MenuPages { get; set; }
}

The ServicedComponent class which is exposed to COM+ looks like this:

public class MenuManager : ServicedComponent
{
    public MenuPage GetMenuPage(string PubCode, int Page)
    {
        MenuPage ReturnMenuPage;
        Menus.MenuPages.TryGetValue(String.Concat(PubCode, Page.ToString()), out ReturnMenuPage);
        return ReturnMenuPage;
    }
}

And the COM+ component is called from an ASP page like so...

Dim MenuManager : Set MenuManager = Server.CreateObject("MenuManager.MenuManager")
Dim MenuPage : Set MenuPage = MenuManager.GetMenuPage(UCase(sBook), iPage)

If (Not MenuPage Is Nothing) Then
    //Retrieve values from class
Else
    Set MenuPage = Nothing
    Set MenuManager = Nothing
    Throw404()
End If

Set MenuPage = Nothing
Set MenuManager = Nothing

The COM+ is running as a local service and the following options are set in AssemblyInfo:

[assembly: ApplicationActivation(ActivationOption.Server)]
[assembly: ApplicationAccessControl(false, AccessChecksLevel = AccessChecksLevelOption.ApplicationComponent)]

Under a single test instance, the current solution takes 0.03 seconds to resolve and the new co开发者_运维百科mponent takes 0.0003 seconds to resolve. However, under load it actually performs worse than the original.

What I really need to know is what on earth I am doing so wrongly that the performance is failing to improve as expected when scaled!?!?!

Any help would be appreciated.


Issue

I think what you are seeing is a byproduct of using a server application and returning a .NET class. When you invoke the MenuManager you are making an out of process call to return the MenuPage object. However the MenuPage is returned by reference -- the object is in memory inside your server application process. To verify this is true place some code in one of the MenuPage getter methods:

System.Diagnostics.Trace.TraceInformation(
  System.Diagnostics.Process.GetCurrentProcess().Id.ToString()
);

You will see that the process ID is the process ID of your server application.

For another test place a sleep after returning the MenuPage then after the sleep retrieve a property of the MenuPage. Before the sleep finishes manually shutdown the server application in COM+ Explorer. You will get a message saying that the remote server machine does not exist or is unavailable.

So for every page you will be making a remote out of process call to retrieve the MenuPage plus another out of process call for every property you invoke on MenuPage.

Improvements

Are you using a Server Application for security purposes? If not, then try to go to a library application so that the calls are not out of process. That could help.

I would also change my component to not return MenuPage but instead return primitive types (if possible) that can be marshaled by value (string, int etc.).

Finally, I would use JIT to deactivate the object immediately after the method returns. Since you are deactivating in a server process then you do not need to call Dispose from the ASP page.

I'm not sure what your MenuPage class looks like but the code would look something like:

[JustInTimeActivation(true)]
public class MenuManager : ServicedComponent
{
    public void GetMenuPageInfo(string PubCode, 
         int Page, 
         out string menuPageUrl, 
         out int menuCode)
    {
        ContextUtil.DeactivateOnReturn = true;

        menuPageUrl = null;
        menuCode = 0;

        MenuPage ReturnMenuPage;
        if (Menus.MenuPages.TryGetValue(
                String.Concat(PubCode, Page.ToString()), out ReturnMenuPage))
        {
            menuPageUrl = ReturnMenuPage.MenuPageUrl;
            menuCode = ReturnMenuPage.Code;
        }
    }
}

Hopefully that improves the scalability.

Reading

Look at Enterprise Services Design Considerations:

  • Use Enterprise Services only if you need to.
  • Use library applications if possible.
  • Consider DLL and class relationships.
  • Use distributed transactions only if you need to.
  • Use object pooling to reduce object creation overhead.
  • Design pooled objects based on calling patterns.
  • Use explicit interfaces.
  • Design less chatty interfaces.
  • Design stateless components.

Understanding Enterprise Services (COM+) in .NET is a great article on COM+.


Try to mark your COM+ with following attributes

[JustInTimeActivation]

and

[Transaction(TransactionOption.Disabled)]

I think if you disable COM+ transaction call to COM+ can works faster. Also i can suggest you to add logging to server and client parts. After this you can analyze where is the worse performance in COM+ call or Dictionary.TryGetValue


Improving Enterprise Services Performance


In my experience, you should set the COM to run "within the same process as client". it is one of the setting in the Component Service Console. The speed is at least thousand times faster.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜