前言:虽说公司app后端使用的是.net core+Redis+docker+k8s部署的,但是微信公众号后端使用的是IIS部署的,虽说公众号并发量不大,但领导还是使用了负载均衡,所以在介绍docker+k8s实现分布式Session共享之前,就先介绍一下IIS+nginx实现Session共享的方案,两者其实区别不大,所以这篇着重介绍方案,下篇介绍测试的区别以及填坑的方式。

1、环境准备

操作系统:Windows10

IIS:需要安装模块

VS2019、本地Redis数据库、ngnix(windows版)

2、Session共享的简易说明

下图简要说明了负载均衡通过轮询方式,将同一个客户端请求发送到不同的站点下,操作的Session应该是同一个。

3、添加测试项目

虽然个人认为本来WebApi中使用Session本身就是一种不合理的设计,但这是旧项目迁移需要保留的历史逻辑,所以只能硬着头皮寻找对应的解决方案了。

在VS2019中添加一个.net core 的WebApi项目,使用Session的话需要添加以下配置。

Startup.cs类中,ConfigureServices方法添加services.AddSession();  Configure方法中添加app.UseSession();  注意要放到UseMVC方法前面。

测试代码如下,添加testController类,在Get1方法中设置Session,记录当前时间,Get2方法中读取Session

[Route("[action]")]
[ApiController]
public class testController : ControllerBase
{
// GET: api/test
[HttpGet]
public IEnumerable<string> Get()
{
return new string[] { "value1", "value2", HttpContext.Connection.LocalIpAddress.ToString(), HttpContext.Connection.LocalPort.ToString()};
} // GET: api/test/5
[HttpGet]
public string Get1(int temp1)
{
if (string.IsNullOrEmpty(HttpContext.Session.GetString("qqq")))
{
HttpContext.Session.SetString("qqq", DateTime.Now.ToString());
}
return HttpContext.Connection.LocalIpAddress.ToString() + "|" + HttpContext.Connection.LocalPort.ToString();
} [HttpGet]
public string Get2(int temp1, int temp2)
{
return HttpContext.Connection.LocalIpAddress.ToString() + "|" + HttpContext.Connection.LocalPort.ToString() + "|" + HttpContext.Session.GetString("qqq");
}
}

4、发布.net core项目到IIS  

(1)右键项目点击发布

(2)记录下发布路径,并在IIS中新增两个站点,指向该路径,并设置不同的端口号

记得把应用程序池中改为无托管代码

5、nginx配置负载均衡

下载地址:http://nginx.org/

配置方式:

(1)找到nginx的安装路径,打开nginx.conf文件

(2)添加upstream配置,配置用于负载均衡轮询的站点,即上一步骤中添加的两个站点

(3)配置location节点,注意proxy_pass与upstream中配置的名称保持一致。

(4)启动ngnix,用cmd命令指定nginx的安装目录,然后start nginx

6、在没有做Session共享方案的情况下进行测试

浏览器分别输入http://localhost:7665/Get1与http://localhost:7665/Get2,由于ip是一样的,所以没有参考必要,不停刷新http://localhost:7665/Get1,最后看到的端口号在7666与7667之间不停的来回切换,说明nginx的轮询是成功的。当然这里只是为了实现session共享做的负载均衡,所以把负载均衡放在了同一台服务器上进行配置,感兴趣的同学可以使用不同服务器配置负载均衡,并用压力测试工具测试站点在配置负载均衡的吞吐能力,后面有机会我可以单独介绍这部分内容。

测试结果:

1、过程:  请求http://localhost:7665/Get1,请求分发到站点7667,设置了Session;

请求http://localhost:7665/Get2,请求分发到站点7666,读取不到该Session;

再次请求Get2,请求分发到站点7667,可以读取到Session。

结论:说明负载均衡的两个站点之间不会读取同一个Session,也就是说Session不会共享。

2、 过程: 再次请求Get1,请求分发到站点7666

再次请求Get2,请求分发到站点7667,读取不到该Session;

再次请求Get2,请求分发到站点7666,可以读取到Session,且session值被刷新。

结论:因为nginx每次都将请求分发到另外一站点,且session没有共享,所以string.IsNullOrEmpty(HttpContext.Session.GetString("qqq"))总是true,然后session都会被刷新,另一个站点的session就丢失了(这里的丢失应该是刷新session后产生了新的cookie值,导致原来的session无法读取,感兴趣的同学还可以用Fiddler跟踪记录下Get1产生cookie值,然后在Get2请求时带上cookie进行验证)。

7、使用Redis将Session存放在Redis服务器

(1)在Startup.cs文件中,ConfigureServices方法加入以下代码

services.Configure<CookiePolicyOptions>(options =>
{
// This lambda determines whether user consent for non-essential cookies is needed for a given request.
options.CheckConsentNeeded = context => false; //这里要改为false,默认是true,true的时候session无效
options.MinimumSameSitePolicy = Microsoft.AspNetCore.Http.SameSiteMode.None;
}); services.AddDataProtection(configure =>
{
configure.ApplicationDiscriminator = "wxweb";
})
.SetApplicationName("wxweb")
.AddKeyManagementOptions(options =>
{
//配置自定义XmlRepository
options.XmlRepository = new SessionShare();
}); #region 使用Redis保存Session
// 这里取连接字符串
services.AddDistributedRedisCache(option =>
{
//redis 连接字符串
option.Configuration = Configuration.GetValue<string>("RedisConnectionStrings");
//redis 实例名
option.InstanceName = "Wx_Session";
}); //添加session 设置过期时长分钟
//var sessionOutTime = con.ConnectionConfig.ConnectionRedis.SessionTimeOut;
services.AddSession(options =>
{
options.IdleTimeout = TimeSpan.FromSeconds(Convert.ToDouble( * * )); //session活期时间
options.Cookie.HttpOnly = true;//设为httponly
});
#endregion

简要说明:

services.Configure<CookiePolicyOptions>是为了可以使用cookie

SetApplicationName("wxweb")是为了保证不同站点下的应用程序名称是一致的。

options.XmlRepository = new SessionShare();是为了保证不同站点下应用程序使用的machinekey是一样的,详情见https://www.cnblogs.com/newP/p/6518918.html

AddDistributedRedisCache是一个官方的拓展组件,用户将session保存在redis中。

RedisConnectionStrings是Redis连接字符串

(2)SessionShare的实现如下

public class SessionShare : IXmlRepository
{
private readonly string keyContent =
@"自己的machinekey"; public virtual IReadOnlyCollection<XElement> GetAllElements()
{
return GetAllElementsCore().ToList().AsReadOnly();
} private IEnumerable<XElement> GetAllElementsCore()
{
yield return XElement.Parse(keyContent);
}
public virtual void StoreElement(XElement element, string friendlyName)
{
if (element == null)
{
throw new ArgumentNullException(nameof(element));
}
StoreElementCore(element, friendlyName);
} private void StoreElementCore(XElement element, string filename)
{
}
}

(3)再次进行发布

8、添加Session共享方案以后进行测试

测试结果:无论Get1刷新多少次,Get2都能拿到Session值,且Session没有被刷新(当前时间没有变化),即Session共享成功。

总结:以前总是看别人的文章了解Session共享,这次自己从配置负载均衡到解决Session共享从头到尾走了一遍,所以记录了下来。下篇文章我会介绍AddDistributedRedisCached在并发量较高时timeout的解决方案。

参考文章(同时感谢这些大佬的文章提供的帮助)

1、【nginx】配置Nginx实现负载均衡

2、Session分布式共享 = Session + Redis + Nginx

.Net Core Web Api实践(二).net core+Redis+IIS+nginx实现Session共享的更多相关文章

  1. net core+Redis+IIS+nginx实现Session共享

    .Net Core Web Api实践(二).net core+Redis+IIS+nginx实现Session共享   前言:虽说公司app后端使用的是.net core+Redis+docker+ ...

  2. .Net Core Web Api实践(三).net core+Redis+docker实现Session共享

    前言:上篇文章介绍了.net core+Redis+IIS+nginx实现Session共享,本来打算直接说明后续填坑过程,但毕竟好多坑是用docker部署后出现的,原计划简单提一下.net core ...

  3. .Net Core Web Api实践(四)填坑连接Redis时Timeout performing EVAL

    前言:前两篇文章.net core+Redis+IIS+nginx实现Session共享中,介绍了使用Microsoft.Extensions.Caching.Redis实现Session共享的方法, ...

  4. .Net Core Web Api实践之中间件的使用(一)

    前言:从2019年年中入坑.net core已半年有余,总体上来说虽然感觉坑多,但是用起来还是比较香的.本来我是不怎么喜欢写这类实践分享或填坑记录的博客的,因为初步实践坑多,文章肯定也会有各种错误,跟 ...

  5. ASP.NET Core Web API 索引 (更新Identity Server 4 视频教程)

    GraphQL 使用ASP.NET Core开发GraphQL服务器 -- 预备知识(上) 使用ASP.NET Core开发GraphQL服务器 -- 预备知识(下) [视频] 使用ASP.NET C ...

  6. ASP.NET Core Web API

    1.简单介绍 ASP.NET Core Web API 是 ASP.NET Core MVC 的一个功能.ASP.NET Core MVC 包含了对 Web API 的支持.可以构建多种客户端的 HT ...

  7. .net core web api + Autofac + EFCore 个人实践

    1.背景 去年时候,写过一篇<Vue2.0 + Element-UI + WebAPI实践:简易个人记账系统>,采用Asp.net Web API + Element-UI.当时主要是为了 ...

  8. ASP.NET Core Web API下事件驱动型架构的实现(二):事件处理器中对象生命周期的管理

    在上文中,我介绍了事件驱动型架构的一种简单的实现,并演示了一个完整的事件派发.订阅和处理的流程.这种实现太简单了,百十行代码就展示了一个基本工作原理.然而,要将这样的解决方案运用到实际生产环境,还有很 ...

  9. 使用 ASP.NET Core MVC 创建 Web API(二)

    使用 ASP.NET Core MVC 创建 Web API 使用 ASP.NET Core MVC 创建 Web API(一) 六.添加数据库上下文 数据库上下文是使用Entity Framewor ...

随机推荐

  1. linux服务器时间更新

    yum install ntpdate ntpdate ntp1.aliyun.com(阿里云服务器时间)

  2. Android Animation动画详解(一): 补间动画

    前言 你有没有被一些APP中惊艳的动画效果震撼过,有没有去思考,甚至研究过这些动画是如何实现的呢? 啥?你没有思考,更没有研究过? 好吧,那跟着我一起来学习下如何去实现APP中那些让我们惊羡的动画特效 ...

  3. docker 使用总结

    docker run asn@hadoop1:~/Desktop$ docker run --help Usage: docker run [OPTIONS] IMAGE [COMMAND] [ARG ...

  4. web与原生交互+活动

    <!doctype html> <html lang="en"> <head> <meta charset="UTF-8&quo ...

  5. Java反射机制(一):认识Class类

    一. 认识Class类 1.1 正常我们再使用一个类时,大多情况是先获取类的对象,然后通过对象去操作类中的属性或方法. 那,大家有没有想过,如果我们已经有了一个类的对象,我能否通过该对象去获取到类的信 ...

  6. RBF神经网络的matlab简单实现

    径向基神经网络 1.径向基函数 (Radial Basis Function,RBF) 神经网络是一种性能良好的前向网络,具有最佳逼近.训练简洁.学习收敛速度快以及克服局部最小值问题的性能,目前已经证 ...

  7. Python--day47--内容回顾

    1.什么是数据库

  8. php 变量名前加一个下划线含义

    https://segmentfault.com/q/1010000006467833 一个下划线是私有变量以及私有方法两个下划线是PHP内置变量. 以下划线开头,表示为类的私有成员. 这只是个不成文 ...

  9. 2019-8-4-自动更新所有-Git-仓库

    title author date CreateTime categories 自动更新所有 Git 仓库 lindexi 2019-08-04 14:44:59 +0800 2019-08-01 2 ...

  10. ASP.NET MVC 实现页落网资源分享网站+充值管理+后台管理(9)之系统登录

    前面我们已经做好了一个文章管理功能模块,接下来,我们回头来做登录窗口,登录不仅涉及到登录验证还涉及到登录日志还有缓存时长等. 对于缓存的相关设置,我们已经写好封装在Bobo.Utilities.dll ...