从http协议的无状态性说起
HTTP是一种无状态协议。关于这个无状态之前我也不太理解,因为HTTP底层是TCP,既然是TCP,就是长连接,这个过程是保持连接状态的,又为什么说http是无状态的呢?先来搞清楚这两个概念:
无连接和无状态
无连接
每次连接只处理一个请求,服务端处理完客户端一次请求,等到客户端作出回应之后便断开连接;
无状态
是指服务端对于客户端每次发送的请求都认为它是一个新的请求,上一次会话和下一次会话没有联系;
无连接的维度是连接,无状态的维度是请求;http是基于tcp的,而从http1.1开始默认使用持久连接;在这个连接过程中,客户端可以向服务端发送多次请求,但是各个请求之间的并没有什么联系;这样来考虑,就很好理解无状态这个概念了。
持久连接
持久连接,本质上是客户端与服务器通信的时候,建立一个持久化的TCP连接,这个连接不会随着请求结束而关闭,通常会保持连接一段时间。`
现有的持久连接类型有两种:HTTP/1.0+的 keep-alive 和HTTP/1.1的 persistent 。
- HTTP/1.0+的keep-alive
先来开一张图:
这张图是请求www.baidu.com时的请求头信息。这里面我们需要注意的是:1
connection: keep-alive
我们每次发送一个HTTP请求,会附带一个connection:keep-alive,这个参数就是声明一个持久连接。
- HTTP/1.1的persistent
HTTP/1.1的持久连接默认是开启的,只有首部中包含connection:close,才会事务结束之后关闭连接。当然服务器和客户端仍可以随时关闭持久连接。
当发送了connection:close首部之后客户端就没有办法在那条连接上发送更多的请求了。当然根据持久连接的特性,一定要传输正确的content-length。
还有根据HTTP/1.1的特性,是不应该和HTTP/1.0客户端建立持久连接的。最后,一定要做好重发的准备。
http无状态
OK,首先来明确下,这个状态的主体指的是什么?应该是信息,这些信息是由服务端所维护的与客户端交互的信息(也称为状态信息);
因为HTTP本身是不保存任何用户的状态信息的,所以HTTP是无状态的协议。
如何保持状态信息
在聊这个这个问题之前,我们来考虑下为什么http自己不来做这个事情:也就是让http变成有状态的。
http本身来实现状态维护
从上面关于无状态的理解,如果现在需要让http自己变成有状态的,就意味着http协议需要保存交互的状态信息;暂且不说这种方式是否合适,但从维护状态信息这一点来说,代价就很高,因为既然保存了状态信息,那后续的一些行为必定也会受到状态信息的影响。
从历史角度来说,最初的http协议只是用来浏览静态文件的,无状态协议已经足够,这样实现的负担也很轻。但是随着web技术的不断发展,越来越多的场景需要状态信息能够得以保存;一方面是http本身不会去改变它的这种无状态的特性(至少目前是这样的),另一方面业务场景又迫切的需要保持状态;那么这个时候就需要来“装饰”一下http,引入一些其他机制来实现有状态。
cookie和session体系
通过引入cookie和session体系机制来维护状态信息。即用户第一次访问服务器的时候,服务器响应报头通常会出现一个Set-Cookie响应头,这里其实就是在本地设置一个cookie,当用户再次访问服务器的时候,http会附带这个cookie过去,cookie中存有sessionId这样的信息来到服务器这边确认是否属于同一次会话。
Cookie
cookie是由服务器发送给客户端(浏览器)的小量信息,以{key:value}的形式存在。
Cookie机制原理
客户端请求服务器时,如果服务器需要记录该用户状态,就使用response向客户端浏览器颁发一个Cookie。而客户端浏览器会把Cookie保存起来。当浏览器再请求 服务器时,浏览器把请求的网址连同该Cookie一同提交给服务器。服务器通过检查该Cookie来获取用户状态。
cookie 是存储于访问者的计算机中的变量。可以让我们用同一个浏览器访问同一个域名的时候共享数据。
HTTP 是无状态协议。简单地说,当你浏览了一个页面,然后转到同一个网站的另一个页面,服务器无法认识到这是同一个浏览器在访问同一个网站。每一次的访问,都是没有任何 关系的。
Koa Cookie 的使用
1、Koa 中设置 Cookie 的值1
ctx.cookies.set(name, value, [options])
通过options 设置 cookie name 的value :
options 名称 | options 值 |
---|---|
maxAge | 一个数字表示从 Date.now() 得到的毫秒数 |
expires | cookie 过期的Date |
path | cookie 路径, 默认是’/‘ |
domain | cookie 域名,可以访问该Cookie的域名。如果设置为“.baidu.com”,则所有以“baidu.com”结尾的域名都可以访问该Cookie;第一个字符必须为“.” |
secure | 安全 cookie 默认 false,设置成 true 表示只有 https 可以访问 |
httpOnly | 是否只是服务器可访问 cookie, 默认是true |
overwrite | 一个布尔值,表示是否覆盖以前设置的同名的 cookie (默认是 false). 如果是 true, 在同一个请求中设置相同名称的所有 Cookie(不管路径或域) 是否在设置此 Cookie 时从Set-Cookie 标头中过滤掉。 |
Cookie属性
name
cookie的名字,Cookie一旦创建,名称便不可更改
value
cookie值
comment
该Cookie的用处说明。浏览器显示Cookie信息的时候显示该说明
maxAge
Cookie失效的时间,单位秒。
正数,则超过maxAge秒之后失效。
负数,该Cookie为临时Cookie,关闭浏览器即失效,浏览器也不会以任何形式保存该Cookie。
为0,表示删除该Cookie。
path
该Cookie的使用路径。例如:
- path=/,说明本域名下contextPath都可以访问该Cookie。
- path=/app/,则只有contextPath为“/app”的程序可以访问该Cookie
path设置时,其以“/”结尾.
secure
该Cookie是否仅被使用安全协议传输。这里的安全协议包括HTTPS,SSL等。默认为false。
从请求中获取Cookie
1 | ctx.cookies.get('name'); |
Cookie同源与跨域
我们知道浏览器的同源策略:
URL由协议、域名、端口和路径组成,如果两个URL的协议、域名和端口相同,则表示他们同源。浏览器的同源策略,限制了来自不同源的”document”或脚本,对当前”document”读取或设置某些属性。
对于Cookie来说,Cookie的同源只关注域名,是忽略协议和端口的。所以一般情况下,https://localhost:80/和http://localhost:8080/的Cookie是共享的。
Cookie是不可跨域的;在没有经过任何处理的情况下,二级域名不同也是不行的。(wenku.baidu.com和baike.baidu.com)。
Cookie数量&大小限制及处理策略
– | IE6.0 | IE7.0/8.0 | Opera | FF | Safari | Chrome |
---|---|---|---|---|---|---|
个数/个 | 20/域 | 50/域 | 30/域 | 50/域 | 无限制 | 53/域 |
大小/Byte | 4095 | 4095 | 4096 | 4097 | 4097 | 4097 |
Koa 中设置中文 Cookie
1 | // 转换成 base64 字符串:aGVsbG8sIHdvcmxkIQ== |
eg
1 | var Koa=require('koa'), |
Session
session 是另一种记录客户状态的机制,不同的是 Cookie 保存在客户端浏览器中,而session 保存在服务器上。
Cookie机制弥补了HTTP协议无状态的不足。在Session出现之前,基本上所有的网站都采用Cookie来跟踪会话。
与Cookie不同的是,session是以服务端保存状态的。
session机制原理
当浏览器访问服务器并发送第一次请求时,服务器端会创建一个session 对象,生成一个类似于key,value 的键值对, 然后将key(cookie)返回到浏览器(客户)端,浏览器下次再访问时,携带 key(cookie),找到对应的 session(value)。 客户的信息都保存在session 中
当客户端请求创建一个session的时候,服务器会先检查这个客户端的请求里是否已包含了一个session标识 - sessionId,
- 如果已包含这个sessionId,则说明以前已经为此客户端创建过session,服务器就按照sessionId把这个session检索出来使用(如果检索不到,可能会新建一个)
- 如果客户端请求不包含sessionId,则为此客户端创建一个session并且生成一个与此session相关联的sessionId
sessionId的值一般是一个既不会重复,又不容易被仿造的字符串,这个sessionId将被在本次响应中返回给客户端保存。保存sessionId的方式大多情况下用的是cookie。
koa-session 的使用:
安装 express-session
1 | npm install koa-session --save |
引入express-session
1 | const session = require('koa-session'); |
设置官方文档提供的中间件
1 | app.keys = ['some secret hurr']; /*cookie的签名*/ |
使用
1 | //设置值 |
eg
1 | var Koa=require('koa'), |
Cookie 和 Session 区别
1、cookie 数据存放在客户的浏览器上,session 数据放在服务器上。
2、cookie 不是很安全,别人可以分析存放在本地的 COOKIE 并进行 COOKIE 欺骗考虑到安全应当使用 session。
3、session 会在一定时间内保存在服务器上。当访问增多,会比较占用你服务器的性能考虑到减轻服务器性能方面,应当使用 COOKIE。
4、单个 cookie 保存的数据不能超过 4K,很多浏览器都限制一个站点最多保存 20 个 cookie。
这两种是一样的;如果session不存在,就新建一个;如果是false的话,标识如果不存在就返回null;
生命周期
session的生命周期指的是创建session对象到销毁的过程。会依据session对象设置的存活时间,在达到session时间后将session对象销毁。session生成后,只要用户继续访问,服务器就会更新session的最后访问时间,并维护该session。
之前在单进程应用中,session我一般是存在内存中的,不会做持久化操作或者说使用三方的服务来存session信息,如redis。但是在分布式场景下,这种存在本机内存中的方式显然是不适用的,因为session无法共享。这个后面说。
session的有效期
session一般在内存中存放,内存空间本身大小就有一定的局限性,因此session需要采用一种过期删除的机制来确保session信息不会一直累积,来防止内存溢出的发生。
session的超时时间可以通过maxInactiveInterval属性来设置。
如果我们想让session失效的话,也可以当通过调用session的invalidate()来完成。
分布式session
首先是为什么会有这样的概念出现?
先考虑这样一个问题,现在我的应用需要部署在3台机器上。是不是出现这样一种情况,我第一次登陆,请求去了机器1,然后再机器1上创建了一个session;但是我第二次访问时,请求被路由到机器2了,但是机器2上并没有我的session信息,所以得重新登录。当然这种可以通过nginx的IP HASH负载策略来解决。对于同一个IP请求都会去同一个机器。
但是业务发展的越来越大,拆分的越来越多,机器数不断增加;很显然那种方案就不行了。那么这个时候就需要考虑是不是应该将session信息放在一个独立的机器上,所以分布式session要解决的问题其实就是分布式环境下的session共享的问题。
上图中的关于session独立部署的方式有很多种,可以是一个独立的数据库服务,也可以是一个缓存服务(redis,目前比较常用的一种方式,即使用Redis来作为session缓存服务器)。