[asp.net] 방금 모든 ASP.Net 웹 사이트가 느린 이유를 발견했으며 그에 대한 조치를 취하려고합니다.

방금 ASP.Net 웹 응용 프로그램의 모든 요청이 요청 시작시 세션 잠금을 얻은 다음 요청 끝에서 해제한다는 것을 알았습니다.

처음에 나에게 있었던 것처럼 이것에 대한 의미가 당신에게 상실되는 경우, 이것은 기본적으로 다음을 의미합니다.

  • ASP.Net 웹 페이지를로드하는 데 시간이 오래 걸리는 경우 (데이터베이스 호출이 느리거나 기타로 인해) 사용자는 대기에 지쳐서 다른 페이지로 이동하기로 결정합니다. ASP.Net 세션 잠금은 새 요청이 원래 요청이 느리게로드를 완료 할 때까지 대기하도록합니다. 아아

  • UpdatePanel이 느리게로드 될 때마다 사용자는 UpdatePanel이 업데이트를 마치기 전에 다른 페이지로 이동하기로 결정합니다. ASP.net 세션 잠금은 새 요청이 원래 요청이 느리게로드를 완료 할 때까지 대기하도록합니다. 더블 아아!

옵션은 무엇입니까? 지금까지 나는 생각해 냈습니다.

  • ASP.Net이 지원하는 사용자 정의 SessionStateDataStore를 구현하십시오. 복사 할 곳이 너무 많지 않아 위험이 높고 엉망이되기 쉽습니다.
  • 진행중인 모든 요청을 추적하고 요청이 동일한 사용자로부터 온 경우 원래 요청을 취소하십시오. 극단적 인 것 같지만 작동합니다 (제 생각에).
  • 세션을 사용하지 마십시오! 사용자에게 일종의 상태가 필요할 때 대신 캐시를 사용하고 인증 된 사용자 이름의 키 항목 또는 그와 같은 것을 사용할 수 있습니다. 다시 한 번 극단적 인 것 같습니다.

ASP.Net Microsoft 팀이 버전 4.0의 프레임 워크에서 엄청난 성능 병목 현상을 겪었을 것입니다. 나는 분명한 것을 놓치고 있습니까? 세션에 ThreadSafe 컬렉션을 사용하는 것이 얼마나 어려울까요?



답변

페이지가 세션 변수를 수정하지 않으면 대부분의 잠금을 해제 할 수 있습니다.

<% @Page EnableSessionState="ReadOnly" %>

페이지에서 세션 변수를 읽지 않으면 해당 페이지에 대해이 잠금을 완전히 해제 할 수 있습니다.

<% @Page EnableSessionState="False" %>

페이지에서 세션 변수를 사용하지 않는 경우 web.config에서 세션 상태를 해제하십시오.

<sessionState mode="Off" />

궁금한 점이 있습니다. 잠금을 사용하지 않으면 “ThreadSafe 컬렉션”이 스레드로부터 안전 해지기 위해 어떻게 할 것이라고 생각하십니까?

편집 : 아마 “이 자물쇠의 대부분을 선택 해제”의 의미로 설명해야합니다. 지정된 세션에 대해 서로를 차단하지 않고 여러 읽기 전용 세션 또는 세션 없음 페이지를 동시에 처리 할 수 ​​있습니다. 그러나 읽기 / 쓰기 세션 페이지는 모든 읽기 전용 요청이 완료 될 때까지 처리를 시작할 수 없으며, 실행중인 동안 일관성을 유지하려면 해당 사용자의 세션에 독점적으로 액세스 할 수 있어야합니다. 한 페이지가 관련 값 집합을 그룹으로 변경하면 어떻게됩니까? 개별 값을 잠그면 작동하지 않습니다. 동시에 실행중인 다른 페이지가 사용자의 세션 변수를 일관되게 볼 수 있도록하려면 어떻게해야합니까?

가능하면 세션 변수가 일단 설정되면 수정을 최소화하는 것이 좋습니다. 이를 통해 대부분의 페이지를 읽기 전용 세션 페이지로 만들 수 있으므로 동일한 사용자의 여러 동시 요청이 서로 차단되지 않을 가능성이 높아집니다.


답변

자, 조엘 뮬러에게 그의 모든 의견에 대해 큰 제안을했습니다. 내 궁극적 인 해결책은이 MSDN 기사의 끝 부분에 자세히 설명 된 Custom SessionStateModule을 사용하는 것입니다.

http://msdn.microsoft.com/en-us/library/system.web.sessionstate.sessionstateutility.aspx

이했다:

  • 구현이 매우 빠름 (실제로 공급자 경로를 이용하는 것보다 쉬웠습니다)
  • 많은 표준 ASP.Net 세션 처리를 SessionStateUtility 클래스를 통해 즉시 사용했습니다.

이것은 우리의 응용 프로그램에 대한 “스냅시 (snapiness)”느낌에 큰 차이를 가져 왔습니다. ASP.Net 세션의 사용자 지정 구현이 전체 요청에 대해 세션을 잠그는 것을 여전히 믿을 수 없습니다. 이것은 웹 사이트에 엄청난 양의 부진을 추가합니다. 내가해야 할 온라인 연구의 양 (그리고 실제로 경험이 많은 ASP.Net 개발자들과의 대화)을 고려할 때 많은 사람들이이 문제를 경험했지만 그 원인을 파악한 사람은 거의 없습니다. 아마도 Scott Gu에게 편지를 쓸 것입니다 …

나는 이것이 소수의 사람들에게 도움이되기를 바랍니다!


답변

AngiesList.Redis.RedisSessionStateModule을 사용하기 시작했습니다. 저장소 에 (매우 빠른) Redis 서버 를 사용하는 것 외에도 ( Windows 포트를 사용하고 있습니다 -MSOpenTech 포트 도 있지만 ) 세션에서 절대 잠금이 없습니다. .

내 의견으로는, 귀하의 응용 프로그램이 합리적인 방식으로 구성되어 있다면 이것은 문제가되지 않습니다. 실제로 세션의 일부로 고정 된 일관된 데이터가 필요한 경우에는 특별히 자체적으로 잠금 / 동시 검사를 구현해야합니다.

MS는 좋지 않은 응용 프로그램 디자인을 처리하기 위해 기본적으로 모든 ASP.NET 세션을 잠 가야한다고 결정하는 것은 잘못된 결정입니다. 특히 대부분의 개발자가 세션이 잠겨 있다는 사실을 알지 못하거나 알지 못하는 것처럼 보이기 때문에 앱을 구조화해야 할 필요는 없지만 가능한 한 읽기 전용 세션 상태를 최대한 많이 수행 할 수 있습니다 (가능한 경우 옵트 아웃) .


답변

이 스레드에 게시 된 링크를 기반으로 라이브러리를 준비했습니다. MSDN 및 CodeProject의 예제를 사용합니다. 제임스에게 감사합니다.

나는 또한 Joel Mueller의 조언을 수정했습니다.

코드는 다음과 같습니다.

https://github.com/dermeister0/LockFreeSessionState

해시 테이블 모듈 :

Install-Package Heavysoft.LockFreeSessionState.HashTable

스케일 아웃 StateServer 모듈 :

Install-Package Heavysoft.LockFreeSessionState.Soss

맞춤 모듈 :

Install-Package Heavysoft.LockFreeSessionState.Common

Memcached 또는 Redis 지원을 구현하려면이 패키지를 설치하십시오. 그런 다음 LockFreeSessionStateModule 클래스를 상속하고 추상 메소드를 구현하십시오.

코드는 아직 프로덕션 환경에서 테스트되지 않았습니다. 또한 오류 처리를 개선해야합니다. 현재 구현에서는 예외가 발생하지 않습니다.

Redis를 사용하는 일부 잠금없는 세션 제공자 :


답변

귀하의 응용 프로그램에 특별한 요구가 없다면, 두 가지 접근 방식이 있다고 생각합니다.

  1. 세션을 전혀 사용하지 마십시오
  2. joel이 언급 한대로 세션을 그대로 사용하고 미세 조정을 수행하십시오.

현재 요청이 완료 될 때까지 모든 세션 변수가 다른 활성 요청에서 변경되지 않음을 알 수있는 방식으로 세션은 스레드로부터 안전 할뿐만 아니라 상태에서 안전합니다. 이를 위해서는 현재 요청이 완료 될 때까지 세션이 잠기 도록 해야합니다 .

여러 가지 방법으로 동작과 같은 세션을 만들 수 있지만 현재 세션을 잠그지 않으면 ‘세션’이 아닙니다.

언급 한 특정 문제에 대해서는 HttpContext.Current.Response.IsClientConnected 확인해야한다고 생각합니다 . 이는 풀링 방식으로 만 비동기식으로 만 사용할 수 있기 때문에이 문제를 완전히 해결할 수는 없지만 불필요한 실행을 방지하고 클라이언트에서 대기하는 데 유용 할 수 있습니다.


답변

업데이트 된 Microsoft.Web.RedisSessionStateProvider(에서 시작하여 3.0.2)을 사용하는 경우 web.config동시 세션을 허용하도록 이를 추가 할 수 있습니다 .

<appSettings>
    <add key="aspnet:AllowConcurrentRequestsPerSession" value="true"/>
</appSettings>

출처


답변

ASPNET MVC의 경우 다음을 수행했습니다.

  1. 기본적 SessionStateBehavior.ReadOnly으로 모든 컨트롤러의 동작을 재정 의하여 설정 합니다DefaultControllerFactory
  2. 세션 상태에 쓰기가 필요한 컨트롤러 작업에서 속성으로 표시하여 설정하십시오. SessionStateBehavior.Required

사용자 정의 ControllerFactory를 작성하고 대체하십시오 GetControllerSessionBehavior.

    protected override SessionStateBehavior GetControllerSessionBehavior(RequestContext requestContext, Type controllerType)
    {
        var DefaultSessionStateBehaviour = SessionStateBehaviour.ReadOnly;

        if (controllerType == null)
            return DefaultSessionStateBehaviour;

        var isRequireSessionWrite =
            controllerType.GetCustomAttributes<AcquireSessionLock>(inherit: true).FirstOrDefault() != null;

        if (isRequireSessionWrite)
            return SessionStateBehavior.Required;

        var actionName = requestContext.RouteData.Values["action"].ToString();
        MethodInfo actionMethodInfo;

        try
        {
            actionMethodInfo = controllerType.GetMethod(actionName, BindingFlags.IgnoreCase | BindingFlags.Public | BindingFlags.Instance);
        }
        catch (AmbiguousMatchException)
        {
            var httpRequestTypeAttr = GetHttpRequestTypeAttr(requestContext.HttpContext.Request.HttpMethod);

            actionMethodInfo =
                controllerType.GetMethods().FirstOrDefault(
                    mi => mi.Name.Equals(actionName, StringComparison.CurrentCultureIgnoreCase) && mi.GetCustomAttributes(httpRequestTypeAttr, false).Length > 0);
        }

        if (actionMethodInfo == null)
            return DefaultSessionStateBehaviour;

        isRequireSessionWrite = actionMethodInfo.GetCustomAttributes<AcquireSessionLock>(inherit: false).FirstOrDefault() != null;

         return isRequireSessionWrite ? SessionStateBehavior.Required : DefaultSessionStateBehaviour;
    }

    private static Type GetHttpRequestTypeAttr(string httpMethod)
    {
        switch (httpMethod)
        {
            case "GET":
                return typeof(HttpGetAttribute);
            case "POST":
                return typeof(HttpPostAttribute);
            case "PUT":
                return typeof(HttpPutAttribute);
            case "DELETE":
                return typeof(HttpDeleteAttribute);
            case "HEAD":
                return typeof(HttpHeadAttribute);
            case "PATCH":
                return typeof(HttpPatchAttribute);
            case "OPTIONS":
                return typeof(HttpOptionsAttribute);
        }

        throw new NotSupportedException("unable to determine http method");
    }

AcquisSessionLockAttribute

[AttributeUsage(AttributeTargets.Method)]
public sealed class AcquireSessionLock : Attribute
{ }

생성 된 컨트롤러 팩토리를 global.asax.cs

ControllerBuilder.Current.SetControllerFactory(typeof(DefaultReadOnlySessionStateControllerFactory));

이제 하나의 세션 상태 read-onlyread-write세션 상태를 모두 가질 수 있습니다 Controller.

public class TestController : Controller
{
    [AcquireSessionLock]
    public ActionResult WriteSession()
    {
        var timeNow = DateTimeOffset.UtcNow.ToString();
        Session["key"] = timeNow;
        return Json(timeNow, JsonRequestBehavior.AllowGet);
    }

    public ActionResult ReadSession()
    {
        var timeNow = Session["key"];
        return Json(timeNow ?? "empty", JsonRequestBehavior.AllowGet);
    }
}

참고 : ASPNET 세션 상태는 여전히 읽기 전용 모드로 기록 될 수 있으며 예외를 발생시키지 않습니다 (일관성을 보장하기 위해 잠그지 않음). 따라서 AcquireSessionLock세션 상태를 기록해야하는 컨트롤러의 동작에 주의를 기울여야합니다 .