在Asp.Net Core Nginx部署一文中,主要是讲述的如何利用Nginx来实现应用程序的部署,使用Nginx来部署主要有两大好处,第一是利用Nginx的负载均衡功能,第二是使用Nginx的反向代理来降低我们后端应用程序的压力。那除了以上两点之外,其实我们还可以利用代理服务器的缓存功能来进一步的降低后端应用程序的压力,提升系统的吞吐量(tps)。这一篇就来看一下具体应该如何去做吧。
之所以加这个目录是因为有一部分初学者对缓存的认知不够,特别是WEB中的缓存。
缓存它是一种空间换取时间的一种技术。
Web缓存(或HTTP缓存)是用于Web文档,如HTML页面和图像,减少带宽的使用,服务器的负载的一种信息技术。一个Web缓存系统存储通过Cache来传递的文件的副本;如果满足某些条件,则可以从缓存中得到后续的请求。
WEb缓存有几种方式:
1、服务端缓存
利用 Memcached,Redis,In-Memery 等缓存技术实现对数据的缓存。
2、代理服务器缓存
利用类似nginx的反向代理服务器,对请求的url对应的输出的进行缓存。这个缓存和应用程序实现的动态页面缓存类似,只不过用反向代理充当了应用程序的缓存实现。
3、客户端缓存
浏览器缓存,其实主要就是HTTP协议定义的缓存机制(如: Last-Modified,If-Modified-Since,Expires; Cache-control等)。
最简单的一种缓存,ASP.NET Core 提供了 IMemoryCache 接口来供我们使用。它存储在本地的 WEB 服务器内容中,注意是单机的 WEB 服务器,如果你需要部署的是一个服务器集群的话,那么你应该用分布式缓存,而不是选择这个。
就不详细介绍了,想了解的可以直接看官方文档。
随着云应用和服务器集群以及 docker 等技术的成熟,越来越多的应用程序开始考虑集群部署,因为它具有更好的性能和可伸缩可扩展性。那么这个时候就需要用到分布式缓存了。
在 ASP.NET Core应用中,已经对分布式缓存做了抽象,提供了 IDistributedCache 接口,该接口提供了添加,检索,删除等的同步和异步的方法。并且还默认提供了 Redis 和 SQLServer 的分布式缓存实现,我们也可以实现 IDistributedCache 接口来扩展自己的缓存系统。
需要说明的是Get,GetAsync
和Set,SetAsync
。 这两个接口方法默认是使用的byte[]
,之所以没有提供直接存储对象的方法是因为微软想把这个默认序列化的选择交给用户,因为每一个团队的偏好是不一样的,有些团队喜欢使用 XML,有些喜欢使用 JSON,有些喜欢使用 Protobuf 等,所以在 项目中,你可以根据自己的偏好来扩展想要的方法。
具体使用方法还是直接看官方文档好了。
关于使用也可以查看我的另外一篇博客: ASP.NET Core 使用 Redis 和 Protobuf 进行 Session 缓存。
在 ASP.NET Core中,有一种缓存叫做Response缓存,这个缓存主要是用来做代理服务器的缓存。它主要原理是在输出的HTTP Response的header里面添加指定的缓存标记。这些缓存标记用来让客户端或者代理服务器来识别需要缓存的内容。然后当客户端有请求到代理服务器的时候,代理服务器可以识别出一部分请求,然后直接把结果返回给浏览器,从而提高后端应用程序的性能和吞吐。
从这个图中看出来,在第一次的时候,一个客户端请求经过代理服务器请求的我们后端的WEB服务器上,然后WEB服务器在返回结果的META上添加了cache-control
标签,它的值为public
。
下面是cache-control
标签一些值的说明:
public
指示响应可被任何缓存区缓存。
private
指示对于单个用户的整个或部分响应消息,不能被共享缓存处理。这允许服务器仅仅描述当用户的部分响应消息,此响应消息对于其他用户的请求无效。
no-cache
指示请求或响应消息不能缓存(HTTP/1.0用Pragma的no-cache替换)根据什么能被缓存
max-age
指示客户机可以接收生存期不大于指定时间(以秒为单位)的响应。
min-fresh
指示客户机可以接收响应时间小于当前时间加上指定时间的响应。
max-stale
指示客户机可以接收超出超时期间的响应消息。如果指定max-stale消息的值,那么客户机可以接收超出超时期指定值之内的响应消息。
Expires 表示存在时间,允许客户端在这个时间之前不去检查(发请求),等同max-age的
效果。但是如果同时存在,则被Cache-Control的max-age覆盖。
格式:
Expires = "Expires" ":" HTTP-date
通过HTTP的META设置expires和cache-control
1 2 |
<span class="xml"><span class="hljs-tag"><<span class="hljs-name">meta</span> <span class="hljs-attr">http-equiv</span>=<span class="hljs-string">"Cache-Control"</span> <span class="hljs-attr">content</span>=<span class="hljs-string">"max-age=7200"</span> /></span> <span class="hljs-tag"><<span class="hljs-name">meta</span> <span class="hljs-attr">http-equiv</span>=<span class="hljs-string">"Expires"</span> <span class="hljs-attr">content</span>=<span class="hljs-string">"Mon, 20 Jul 2016 23:00:00 GMT"</span> /></span></span> |
在 ASP.NET Core MVC 中,提供了ResponseCache
这个特性用来做上面这些事情。它被作为一个Attribute添加的Controller的Action上。
Duration
指示缓存的过期时间,对应到Cache-Control 的 max-age 。
Location
有三个值Any
,Client
,None
分别对应到Cache-Control的 public,private,no-cache。
NoStore
设置值是否被存储。如果是true,它将设置Cache-Control为no-store
VaryByHeader
将在header中添加Vary标记。
CacheProfileName
使用的策略,在startup.cs中设置。
Order
在过滤器中的排序。
现在,我们已经知道了如果在Action中设置缓存标记了。
对于一些静态文件,比如程序用到的图片,css,js等,Nginx是可以直接处理的,只需要配置一下。
如果使用Nginx来处理静态文件的话,那么程序中startup.cs
就可以不用添加app.UseStaticFiles();
中间件了。
/etc/nginx/conf.d/nginx.conf
没有的话就新建一个。内容如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 |
<span class="hljs-attribute">proxy_temp_path</span> /usr/local/nginx/proxy_temp_dir <span class="hljs-number">1</span> <span class="hljs-number">2</span>; <span class="hljs-comment">#注:proxy_temp_path和proxy_cache_path指定的路径必须在同一分区</span> <span class="hljs-comment">#keys_zone=cache1:100m 表示这个zone(缓存区域)名称为cache1,分配的内存大小为100MB</span> <span class="hljs-comment">#/usr/local/nginx/proxy_cache_dir/cache1 表示cache1这个zone的文件要存放的目录</span> <span class="hljs-comment">#levels=1:2 表示缓存目录的第一级目录是1个字符,第二级目录是2个字符,即/usr/local/nginx/proxy_cache_dir/cache1/a/1b这种形式</span> <span class="hljs-comment">#inactive=1d 表示这个zone中的缓存文件如果在1天内都没有被访问,那么文件会被cache manager进程删除掉</span> <span class="hljs-comment">#max_size=10g 表示这个zone的硬盘容量为10GB</span> <span class="hljs-attribute">proxy_cache_path</span> /usr/local/nginx/proxy_cache_dir/cache1 levels=<span class="hljs-number">1</span>:<span class="hljs-number">2</span> keys_zone=cache1:<span class="hljs-number">100m</span> inactive=<span class="hljs-number">1d</span> max_size=<span class="hljs-number">10g</span>; <span class="hljs-comment">#upstream web-app {</span> <span class="hljs-comment"># server webapp1:5090;</span> <span class="hljs-comment"># server webapp2:5090;</span> <span class="hljs-comment">#}</span> <span class="hljs-section">server</span> { <span class="hljs-attribute">listen</span> <span class="hljs-number">80</span>; <span class="hljs-attribute">server_name</span> <span class="hljs-regexp">*.example.com</span>; <span class="hljs-comment">#在日志格式中加入$upstream_cache_status</span> <span class="hljs-attribute">log_format</span> format1 <span class="hljs-string">'<span class="hljs-variable">$remote_addr</span> - <span class="hljs-variable">$remote_user</span> [<span class="hljs-variable">$time_local</span>] '</span> <span class="hljs-string">'"<span class="hljs-variable">$request</span>" <span class="hljs-variable">$status</span> <span class="hljs-variable">$body_bytes_sent</span> '</span> <span class="hljs-string">'"<span class="hljs-variable">$http_referer</span>" "<span class="hljs-variable">$http_user_agent</span>" <span class="hljs-variable">$upstream_cache_status</span>'</span>; <span class="hljs-comment">#访问日志</span> <span class="hljs-attribute">access_log</span> log/access.log fomat1; <span class="hljs-comment">#$upstream_cache_status表示资源缓存的状态,有HIT MISS EXPIRED三种状态</span> <span class="hljs-attribute">add_header</span> X-Cache <span class="hljs-variable">$upstream_cache_status</span>; <span class="hljs-comment">#命中的正则表达式</span> <span class="hljs-attribute">location</span> <span class="hljs-regexp">~ .*\.(gif|jpg|jpeg|png|bmp|swf|js|css|html)$</span> { <span class="hljs-attribute">proxy_pass</span> http://127.0.0.1:5000; <span class="hljs-comment">#proxy_pass http://web-app; </span> <span class="hljs-comment">#proxy_set_header Host $host;</span> <span class="hljs-comment">#proxy_set_header X-Real-IP $remote_addr;</span> <span class="hljs-comment">#proxy_set_header X-Forwarded-For $remote_addr;</span> <span class="hljs-comment">#proxy_set_header Accept-Encoding "none";</span> <span class="hljs-comment">#设定proxy_set_header Accept-Encoding '';(或是后台服务器关闭gzip),这样这台机器才不会缓存被压缩的文件,造成乱码</span> <span class="hljs-comment">#proxy_set_header Accept-Encoding ""; 这个也可</span> <span class="hljs-comment">#如果后端的服务器返回502、504、执行超时等错误,自动将请求转发到upstream负载均衡池中的另一台服务器,实现故障转移。</span> <span class="hljs-comment">#proxy_next_upstream http_502 http_504 error timeout invalid_header;</span> <span class="hljs-comment">#设置资源缓存的zone</span> <span class="hljs-attribute">proxy_cache</span> cache1; <span class="hljs-comment">#设置缓存的key</span> <span class="hljs-attribute">proxy_cache_key</span> <span class="hljs-variable">$host</span><span class="hljs-variable">$uri</span><span class="hljs-variable">$is_args</span><span class="hljs-variable">$args</span>; <span class="hljs-comment">#设置状态码为200和304的响应可以进行缓存,并且缓存时间为10分钟</span> <span class="hljs-attribute">proxy_cache_valid</span> <span class="hljs-number">200</span> <span class="hljs-number">304</span> <span class="hljs-number">10m</span>; <span class="hljs-comment"># **!!!重要!!!** 这段配置加上后,proxy_cache就能支持后台设定的Cache-Control,Expires。 </span> <span class="hljs-attribute">proxy_ignore_headers</span> <span class="hljs-string">"Cache-Control"</span> <span class="hljs-string">"Expires"</span>; <span class="hljs-attribute">expires</span> <span class="hljs-number">30d</span>; } } |
上面有一个配置项在 ASP.NET Core 程序中比较重要,就是proxy_ignore_headers
这个配置项,它代表支持后台设定Cache-Control,Expires等。
其中upstream
节点是用来配置负载均衡的服务器的,proxy_pass
用来设置代理到upstream
节点,proxy_next_upstream
是用来配置故障转移。
使用sudo nginx -s reload
命令来重新加载配置。
关于缓存
缓存确实是提升应用程序性能最快也是效果最明显的方式之一,ASP.NET Core也为提供了很多种缓存方法。但是,在使用之前一定要了解每一种缓存的技术实现,切不可盲目使用。
关于部署
个人认为,在 ASP.NET Core 理想的分布式部署环境有两种:
第一种是基于云的部署,比如使用Azure,AWS,阿里云等,那么我们可以使用他们提供的负载均衡器来帮助我们拦截洪水般的请求,然后借助于云提供的高可用的实例集群或者Docker集群来降低应用程序的压力,提升吞吐。
比如我们项目现在使用的AWS的部署环境,借助于AWS来实现企业的私有云,包括高可用的Redis集群,弹性EC2集群,RDS集群,S3等,这个时候只需要专注于业务。
第二种是自己搭建集群环境,可以在服务器前端使用Nginx的负载均衡和缓存来拦截大部分的HTTP请求,然后后端使用Docker集群来做部署。
Docker部署可以参见本人的另外一篇文章:http://www.cnblogs.com/savorboard/p/dotnetcore-docker.html
在版本的快速迭代过程中,你还需要做的工作有如何提高部署的工作效率,那么可以使用一些Docker集群管理工具,后面会写一篇文章专门介绍Docker的集群管理和 ASP.NET Core的一键发布。
本文地址:http://www.cnblogs.com/savorboard/p/dotnetcore-nginx-cache.html
作者博客:Savorboard
欢迎转载,请在明显位置给出出处及链接