首页 > 解决方案 > ASP.NET MVC 和 ASP.NET WebAPI 的负载平衡

问题描述

我正在从事两个项目。一种是使用 ASP.NET MVC,另一种是使用 ASP.NET WebAPI。我们的客户端想要实现负载均衡,但他们担心一旦完成,它可能会影响登录功能,因为用户的身份验证令牌可能不会在服务器之间共享。我被分配研究这个问题。谁能告诉我是否可以在服务器之间同步和共享用户的登录状态和身份验证令牌以达到负载平衡的目的?如果是这样,怎么做?这是 ASP.NET WebAPI 项目中登录功能的代码。

    public async Task<IHttpActionResult> Login(LoginModel model)
    {
        if (!ModelState.IsValid)
        {
            return BadRequest(ModelState);
        }

        // Invoke the "token" OWIN service to perform the login (POST /api/token)
        var requestParams = new List<KeyValuePair<string, string>>
        {
            new KeyValuePair<string, string>("grant_type", "password"),
            new KeyValuePair<string, string>("client_id", APP_CLIENT_ID),
            new KeyValuePair<string, string>("username", model.Username),
            new KeyValuePair<string, string>("password", model.Password)
        };

        var requestParamsFormUrlEncoded = new FormUrlEncodedContent(requestParams);
        var baseUrl = apiConfiguration.Host;
        var getTokenUrl = $"{baseUrl}/token";
        var tokenServiceResponse = await httpClient.PostAsync(getTokenUrl, requestParamsFormUrlEncoded);
        if (!tokenServiceResponse.IsSuccessStatusCode)
        {
            // Log response
            if (tokenServiceResponse.StatusCode == HttpStatusCode.NotFound)
            {
                log.ErrorFormat("Can not get token from url: {0}", getTokenUrl);
                return BadRequest(SERVER_ERROR_MESSAGE);
            }
            else
            {
                string content = await tokenServiceResponse.Content.ReadAsStringAsync();
                if (tokenServiceResponse.StatusCode == HttpStatusCode.InternalServerError)
                {
                    log.Error(content);
                    return BadRequest(SERVER_ERROR_MESSAGE);
                }
                else
                {
                    log.InfoFormat("Get token fail request fail | RESPONE - StatusCode: {0} - Reason: {1} - Content: {2}", tokenServiceResponse.StatusCode, tokenServiceResponse.ReasonPhrase, content);
                    return BadRequest(INVALID_USER_DATA_MESSAGE);
                }
            }
        }

        var result = ResponseMessage(tokenServiceResponse);
        var user = await userManager.FindByNameAsync(model.Username);
        if (user == null || user.IsDeleted)
        {
            log.InfoFormat("Invalid user data. Username: {0}", model.Username);
            return BadRequest(INVALID_USER_DATA_MESSAGE);
        }
        else if (user.AccountBlocked == true)
        {
            log.InfoFormat("The account {0} is blocked", model.Username);
            return BadRequest(ACCOUNT_IS_BLOCKED_MESSAGE);
        }

        var userId = user.Id;
        accountNotificationService.LogIn(userId, result.Response.IsSuccessStatusCode, APP_CLIENT_ID);

        return result;
    }

这是 ASP.NET MVC 项目中登录功能的代码。

    public async Task<IHttpActionResult> Login(LoginModel model)
    {
        if (model == null)
        {
            return this.BadRequest("Invalid user data");
        }
        // Invoke the "token" OWIN service to perform the login (POST /api/token)
        var requestParams = new List<KeyValuePair<string, string>>
        {
            new KeyValuePair<string, string>("grant_type", "password"),
            new KeyValuePair<string, string>("client_id", APP_CLIENT_ID),
            new KeyValuePair<string, string>("username", model.Username),
            new KeyValuePair<string, string>("password", model.Password)
        };

        try
        {
            var requestParamsFormUrlEncoded = new FormUrlEncodedContent(requestParams);
            var tokenServiceResponse = await new HttpClient().PostAsync(
                string.Format("{0}/Token", Config.Application.Host), requestParamsFormUrlEncoded);
            var result = this.ResponseMessage(tokenServiceResponse);
            if (!result.Response.IsSuccessStatusCode)
            {
                return this.BadRequest("Credentials are incorrect");
            }
            var user = result.Response.IsSuccessStatusCode ? await accountService.FindUser(model.Username, model.Password) : null;
            if (user == null)
            {
                return this.BadRequest("Credentials are invalid");
            }
            else if (user.IsDeleted)
            {
                return this.BadRequest("This user does not exists");
            }
            if (user.AccountBlocked == true)
            {
                return this.BadRequest("This account is blocked");
            }
            notificationService.LogIn(user?.Id, result.Response.IsSuccessStatusCode, APP_CLIENT_ID);


            return result;
        }
        catch (Exception ex)
        {
            ErrorLogging.LogError(ex, "Error in Login");
            return BasicResponse(new ResultModel
            {
                Success = false,
                ResponseCode = System.Net.HttpStatusCode.InternalServerError,
                Message = Constants.ErrorMessageCategory.General
            });
        }
    }

标签: c#asp.net-mvcasp.net-web-apiload-balancing

解决方案


当在负载均衡器后面运行机器时,负载均衡器上通常有一个 Affinity 设置,它将指示会话的“粘性”程度,负载均衡器应根据这些会话保持与同一服务器的连接。因此,根据道路平衡器的配置方式,这可能不是问题吗?特别是如果客户端(用户)在每次调用时发送会话信息或 API 令牌。

这是 Azure 负载均衡器分发模式的说明https://docs.microsoft.com/en-us/azure/load-balancer/load-balancer-distribution-mode


推荐阅读