asp.net core 从单机到集群

Intro

这篇文章主要以我的活动室预约的项目作为示例,看一下一个 asp.net core 应用从单机应用到分布式应用需要做什么。

示例项目

活动室预约提供了两个版本,集群版单机版

单机版方便部署,不依赖其他环境,数据库使用的是 sqlite,详细部署文档可以参考:https://github.com/WeihanLi/ActivityReservation/blob/dev/docs/deploy/standalone.md

集群版,目前依赖的组件有 mysql(数据库)/redis(缓存)/elasticsearch(日志)

日志

日志原来是输出到文件中的,单机部署没有什么问题,可以直接 ssh 到机器上查看文件内容,但是如果部署到集群上,日志再输出到文件的话,排查起来可就有点麻烦了,日志是分散在多台机器上,只看某一台机器上的日志可能并不能解决问题。

基于日志这个痛点让我把日志迁移到 elasticsearch 上,日志统一输出到 es,并通过 kibana 来搜索/分析日志。

日志组件一直用的 log4net,日志输出到 es ,自己写了一个 es 的 Appender, 但是后来越来越觉得 log4net使用起来不够灵活,后来日志组件换成了 serilog,使用 serilog 就可以方便的扩展,增加日志要记录的信息,关于自定义 serilog enricher 可以参考 Serilog 自定义 Enricher 来增加记录的信息

使用 es 来存储日志还有一个好处,就是搜索日志非常的快,而且借助 kibana 可以很方便的进行统计分析

拿上篇文章的图来借用一下,下面是 kibana 基于日志的 RequestIP 来绘制的前十个访问最多的 IP 地址

缓存

单机部署为了不增加系统复杂度,不引入外部依赖,单机版使用的是 MemoryCache

集群部署,就需要引入分布式缓存,我选择的是 redis,redis 组件是基于 StackExchange.Redis 的,自己在其基础上封装了一些功能。

在我的这个示例应用中 redis 不仅仅做缓存,我还用 redis 的 hash 实现了一个类似于 asp.net 里 Application 的服务,还有 redis 的发布订阅来实现一个 eventBus 来异步处理公告的浏览记录。

单机环境下,我们用 lock 或者用信号量来实现资源在某一段时间内只能被一个请求拿到

多台机器环境下,我们需要一个分布式锁,上面引入了 redis,就用 redis 来实现一个分布式锁,分布式锁详细实现可以参考:

RedLock

使用方式如下:

using (var redisLock = RedisManager.GetRedLockClient($"reservation:{reservation.ReservationPlaceId:N}:{reservation.ReservationForDate:yyyyMMdd}"))
{
if (redisLock.TryLock())
{
var reservationForDate = reservation.ReservationForDate;
if (!IsReservationForDateAvailable(reservationForDate, isAdmin, out msg))
{
return false;
} // ...
return true;
}
else
{
msg = "系统繁忙,请稍后重试!";
return false;
}
}

DataProtection

微软在 .net core 下引入了 DataProtection 来保护网站的数据,你也可以用它做一些数据保护,之前做了一个简单数据保护扩展,通过 Filter 来自动实现数据的加密/解密,详细信息可以参考 asp.net core 参数保护

默认 DataProtection 的key 是保存到文件的,可能你也注意到过在 asp.net core 应用启动的时候默认会有一条日志信息如下:

多台机器同时部署的话,key 基本上就是不一样的,这样数据就不会被认为是安全的。

举个栗子,我的应用有一个后台使用 cookie 认证,cookie 会使用 DataProtection 的 key 进行加密,使用默认的 DataProtection 时,多台机器上(实际是k8s的多个pod) 的key 是不一样的,这就导致我在后台登录了之后,进入后台之后刷新一下可能就又跳转到登录界面,这是因为生成的 cookie ,对于一个服务来说是有效的,但是对于其他服务来说是无效的(key 不同,没有办法解密成功,认证失败),所以集群部署的时候,DataProection 是必须要设置的,放在一个统一的地方管理,我们上面已经引入了 redis,所以就把 DataProtection 的 key 放在 redis 中去保存(redis 服务可以做高可用,即使 redis 服务挂了也会重新生成一个 key,不会有什么影响)

使用到的包 Microsoft.AspNetCore.DataProtection.StackExchangeRedis,配置方式:

// DataProtection persist in redis
services.AddDataProtection()
.SetApplicationName(ApplicationHelper.ApplicationName)
.PersistKeysToStackExchangeRedis(() => DependencyResolver.Current.ResolveService<IConnectionMultiplexer>().GetDatabase(5), "DataProtection-Keys")
;

获取用户IP

集群部署的时候,会有网关/反向代理去转发请求,这时候直接通过 HttpContext.Connection.RemoteIpAddress 获取到的 ip 地址就会是网关/反向代理的地址,并不是实际用户的地址,一般的反向代理软件会将真实的用户IP放在 X-Forwarded-For 请求头中,转发到下游真正的服务器地址,你可以从请求中直接获取 X-Forwarded-For 请求头的值,也可以使用微软提供的 ForwardedHeaders 中间件,配置方式:

public void ConfigureServices(IServiceCollection services)
{
// ...
services.Configure<ForwardedHeadersOptions>(options =>
{
options.KnownNetworks.Clear();
options.KnownProxies.Clear();
options.ForwardLimit = null;
options.ForwardedHeaders = Microsoft.AspNetCore.HttpOverrides.ForwardedHeaders.All;
});
} public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
app.UseForwardedHeaders();
// ...
}

具体参数配置可以参考文档:https://docs.microsoft.com/en-us/aspnet/core/host-and-deploy/proxy-load-balancer?view=aspnetcore-2.2

Memo

其他还有一些上面并未提到,

比较常用的如 Session,如果要上集群的话,也应该有相应的分布式 session,这个应用没有用到 session,所以上面没有提(之前用极验验证码的时候有用,后来换成腾讯的验证码服务之后去掉了session)

文件上传,如果是存在本地的话,也不太合适,可能需要存在一个集中的文件服务器或者云端存储如 Azure Blob。。(网站里的公告模块的图片上传还没改,,,打算基于 github 或者 开源中国的码云实现一个 storage )

其他暂时没想到了,想到了再补充吧。

Reference

asp.net core 从单机到集群的更多相关文章

  1. Kubernetes初探[1]:部署你的第一个ASP.NET Core应用到k8s集群

    Kubernetes简介 Kubernetes是Google基于Borg开源的容器编排调度引擎,作为CNCF(Cloud Native Computing Foundation)最重要的组件之一,它的 ...

  2. (视频)asp.net core系列之k8s集群部署视频

    0.前言 应许多网友的要求,特此录制一下k8s集群部署的视频.在录制完成后发现视频的声音存在一点瑕疵,不过不影响大家的观感. 一.视频说明 1.视频地址: 如果有不懂,或者有疑问的欢迎留言.视频分为两 ...

  3. Greenplum源码编译安装(单机及集群模式)完全攻略

    公司有个项目需要安装greenplum数据库,让我这个gp小白很是受伤,在网上各种搜,结果找到的都是TMD坑货帖子,但是经过4日苦战,总算是把greenplum的安装弄了个明白,单机及集群模式都部署成 ...

  4. 玩转nodeJS系列:使用cluster创建nodejs单机多核集群(多进程)

    前言: nodejs提供了cluster集群(支持端口共享的多进程),cluster基于child_process,process二次封装,方便我们使用该功能实现单机nodejs的web集群. 1.c ...

  5. Zookeeper单机伪集群

    Zookeeper单机伪集群 1.配置 zookeeper下载地址:http://apache.mirrors.lucidnetworks.net/zookeeper/ 可以选择需要的版本,我下载的是 ...

  6. 【运维技术】Zookeeper单机以及集群搭建教程

    Zookeeper单机以及集群搭建教程 单机搭建 单机安装以及启动 安装zookeeper的前提是必须有java环境 # 选择目录进行下载安装 cd /app # 下载zk,可以去官方网站下载,自己上 ...

  7. redis在Windows下以后台服务一键搭建集群(单机--伪集群)

    redis在Windows下以后台服务一键搭建集群(单机--伪集群) 一.概述 此教程介绍如何在windows系统中同一台机器上布置redis伪集群,同时要以后台服务的模式运行.布置以脚本的形式,一键 ...

  8. Spark Tachyon编译部署(含单机和集群模式安装)

    Tachyon编译部署 编译Tachyon 单机部署Tachyon 集群模式部署Tachyon 1.Tachyon编译部署 Tachyon目前的最新发布版为0.7.1,其官方网址为http://tac ...

  9. 原创 | 手摸手带您学会 Elasticsearch 单机、集群、插件安装(图文教程)

    欢迎关注笔者的公众号: 小哈学Java, 每日推送 Java 领域干货文章,关注即免费无套路附送 100G 海量学习.面试资源哟!! 个人网站: https://www.exception.site/ ...

随机推荐

  1. Linux日志系统分析:rsyslog、syslog和klog

    参考博客: https://blog.csdn.net/lidonghat/article/details/55004280 https://blog.csdn.net/u012247418/arti ...

  2. Lockey的沙雕低错集锦(未完待续~自己提升用)

      (嘿嘿 (^~ ^)) 1. bitset<2100>a[2100]    a[x]|=(1<<x)   (1<=x<=2100)  使用时忘了x的范围,额,应 ...

  3. WinForm控件之【DateTimePicker】

    基本介绍 时间控件应用较为广泛,属性设置项也比较完善是非常好用的控件. 常设置属性.事件 CustomFormat:当Format属性设置为自定义类型时可自定义控件时间的显示格式: Enabled:指 ...

  4. 数据库触发器_trigger

    部门表_删除: USE [test] GO /****** Object: Trigger [dbo].[部门_Delete] Script Date: 2015/8/31 16:41:46 **** ...

  5. HttpClient多文件上传代码及普通参数中文乱码问题解决

    该随笔记录了在实际项目中使用HttpClient调用外部api,需上传文件和普通参数的代码. 笔者在使用 HttpClient 调用 http api 接口时,需要服务端上传文件和一些普通参数给 ht ...

  6. elasticsearch5.4集群超时

    四个节点,有两个是新增加的节点,两个老节点间组成集群没有问题,新增加了两个节点,无论是四个组成集群 # --------------------------------- Discovery ---- ...

  7. youku_androidid

    youku_androidid = 1310; imei screenwidth screenhight

  8. jmeter_遍历转换浮点时间戳

    概述 近期帮朋友解决了一个浮点时间戳转换的问题,在这里记录一下. 具体场景是有一个十位浮点时间戳的list,需要遍历转换为当前的标准时间. list如下: 实现步骤 实现步骤其实很简单,只需要一个fo ...

  9. 编译Tomcat9源码及tomcat乱码问题解决

    因工作原因,需要从根本上优化tomcat的配置,故准备从源码入手,看看可以做哪些工作. 1. tomcat下载 tomcat最新的版本为9,下载源码的方式有3种: 1/ 官方网站 https://to ...

  10. 成为高级 React 开发你需要知道的知识点

    简评:除了常见的 HOC 和 RenderProp 技巧,作者介绍了 7 个有用的知识点. 使用 Fragment 而不是 div 很多时候我们想要处理多个 component,但是 render 只 ...