在了解连接池之前,我们需要对长、短链接建立初步认识。我们都知道,网络通信大部分都是基于 TCP/IP 协议,数据传输之前,双方通过“ 三次握手 ”建立连接,当数据传输完成之后,又通过“ 四次挥手 ”释放连接,以下是“三次握手”与“四次挥手”示意图:
三次握手建立连接示意图:
四次挥手释放连接示意图:
长、短连接是相对通信时间而言的。长连接相对短连接而言,多了一个 保持连接 的过程,可以在一个连接上可以连续发送多个数据包,在连接保持期间,如果没有数据包发送,需要双方发链路检测包。
短连接的 *** 作步骤是:
建立连接——数据传输——关闭连接…建立连接——数据传输——关闭连接
client向server发起连接请求,server接到请求,然后双方建立连接。client向server发送消息,server回应client,然后一次请求就完成了。这时候双方任意都可以发起close *** 作,不过一般都是client先发起close *** 作。上述可知,短连接一般只会在 client/server间传递一次请求 *** 作。
短连接的优点是:管理起来比较简单,存在的连接都是有用的连接,不需要额外的控制手段。
长连接的 *** 作步骤是:
建立连接——数据传输…(保持连接)…数据传输——关闭连接
client向server发起连接,server接受client连接,双方建立连接,client与server完成一次请求后,它们之间的连接并不会主动关闭,后续的读写 *** 作会继续使用这个连接。
TCP长连接保持的两种办法:
自定义心跳消息头.,一般客户端主动发送到服务端,服务器接收后进行回应(也可以不回应),以便能够侦测连接是否异常断开。
通过设置TCP keepalive的属性,并设置发送底层心跳包的时间间隔。TCP keepalive是在底层定时发送心跳报文,服务器端接收到底层的心跳报文直接丢弃,不关心其内容。
HTTP协议是无状态的,在HTTP/1.0中默认使用短连接,客户端和服务器每进行一次HTTP *** 作,浏览器就会重新建立一个HTTP会话。
而从HTTP/1.1起,默认使用长连接,用以保持连接特性,使用长连接的HTTP协议,会在响应头加入这行代码:
在使用长连接的情况下,当一个网页打开完成后,客户端和服务器之间用于传输HTTP数据的TCP连接不会关闭,客户端再次访问这个服务器时,会继续使用这一条已经建立的连接。Keep-Alive不会永久保持连接,它有一个保持时间,可以在不同的服务器软件中设定这个时间。实现长连接需要客户端和服务端都支持长连接。
HTTP协议的长连接和短连接,实质上是TCP协议的长连接和短连接。
基于TCP/IP协议,我们可以知道,频繁的连接创建和销毁都需要消耗资源,而连接池是将已经创建好的连接保存在池中,当有请求来时,直接使用已经创建好的连接进行访问,这样省略了创建连接和销毁连接的过程。这样性能上得到了提高。
以数据库连接池为例,基本原理如下:
连接池技术带来的好处:
由于连接得到重用,避免了频繁创建、释放连接引起的大量性能开销。在减少系统消耗的基础上,另一方面也增进了系统运行环境的平稳性(减少内存碎片以及临时进程/线程的数量)。
连接池在初始化过程中,往往已经创建了若干连接置于池中备用。此时连接的初始化工作均已完成。对于业务请求处理而言,直接利用现有可用连接,避免了连接初始化和释放过程的时间开销,从而缩减了系统整体响应时间。
在较为完备的连接池实现中,可根据预先的连接占用超时设定,强制收回被占用连接。从而避免了常规连接 *** 作中可能出现的资源泄漏。
以PHP开发为例,基于PHP-FPM机制实现的Web服务,并不容易实现连接池,而常驻内存的开发框架,例如workerman、swoole 则可以简单实现连接池功能。PHP-FPM机制下的连接池需要借助第三方Proxy实现,例如:
其实长连接是相对于通常的短连接而说的,也就是长时间保持客户端与服务端的连接状态。通常的短连接 *** 作步骤是:
连接-》数据传输-》关闭连接;
而长连接通常就是:
连接-》数据传输-》保持连接-》数据传输-》保持连接-》…………-》关闭连接;
这就要求长连接在没有数据通信时,定时发送数据包,以维持连接状态,短连接在没有数据传输时直接关闭就行了
什么时候用长连接,短连接?
长连接主要用于在少数客户端与服务端的频繁通信,因为这时候如果用短连接频繁通信常会发生Socket出错,并且频繁创建Socket连接也是对资源的浪费。
但是对于服务端来说,长连接也会耗费一定的资源,需要专门的线程(unix下可以用进程管理)来负责维护连接状态。
总之,长连接和短连接的选择要视情况而定。
短链接,通俗来说,就是将长的URL网址,通过程序计算等方式,转换为简短的网址字符串。
微博和Twitter都有140字数的限制,如果分享一个长网址,很容易就超出限制。
营销短信,字数的限制,当字数过长: 1.不美观 2.超出字符额外收费。
生成二维码的原始链接,当原始链接过长时,生成的二维码过于复杂,导致一些像素较低的手机无法扫描.
功能要求:
非功能性要求:
扩展要求:
可以使用 REST API 来公开我们服务的功能。以下可能是用于创建和删除 URL 的 API 的定义:
createURL (api_dev_key, original_url, custom_alias=None, user_name=None, expire_date=None)
参数:
api_dev_key(string):注册账号的API开发者密钥。除其他外,这将用于根据分配的配额限制用户。
original_url(字符串):要缩短的原始 URL。
custom_alias(字符串):URL 的可选自定义键。
user_name(字符串):在编码中使用的可选用户名。
expire_date (string): 缩短 URL 的可选过期日期。
返回 :(字符串)
成功插入会返回缩短的 URL;否则,它会返回错误代码。
deleteURL (api_dev_key, url_key)
其中“url_key”是一个字符串,表示要检索的缩短的 URL;成功删除会返回“已删除 URL”。
如何发现和防止滥用?恶意用户可以通过使用当前设计中的所有 URL 密钥使我们破产。为了防止滥用,我们可以通过他们的 api_dev_key 限制用户。每个 api_dev_key 可以限制为每个时间段内特定数量的 URL 创建和重定向(每个开发者密钥可以设置为不同的持续时间)。
结合储存数据设计:
数据库架构:
我们需要两张表:一张用于存储有关 URL 映射的信息,另一张用于创建短链接的用户数据。
应该使用什么样的数据库?由于我们预计存储数十亿行,并且我们不需要使用对象之间的关系——NoSQL 选择更容易扩展
在第 1 节的示例中,缩短的 URL 是“https://tinyurl.com/vzet59pa”。这个 URL 的最后八个字符构成了我们要生成的短链。讨论以下两种解决方案: 摘要算法、自增序列算法
方案一:摘要算法
这种算法,虽然会生成4个,但是仍然存在重复几率
方案二: 自增序列算法
设置 id 自增,一个 10进制 id 对应一个 62进制的数值,1对1,也就不会出现重复的情况。这个利用的就是低进制转化为高进制时,字符数会减少的特性。
第一种算法的好处就是简单好理解,永不重复。但是短码的长度不固定,随着 id 变大从一位长度开始递增。如果非要让短码长度固定也可以就是让 id 从指定的数字开始递增就可以了。百度短网址用的这种算法。
为了扩展我们的数据库,我们需要对其进行分区,以便它可以存储有关数十亿个 URL 的信息。因此,我们需要开发一种分区方案,将我们的数据划分并存储到不同的数据库服务器中。
一个基于范围的分区: 我们可以根据哈希键的第一个字母将 URL 存储在单独的分区中。因此,我们将所有以字母“A”(和“a”)开头的 URL 哈希键保存在一个分区中,将那些以字母“B”开头的 URL 哈希键保存在另一个分区中,依此类推。这种方法称为基于范围的分区。我们甚至可以将某些不太频繁出现的字母组合到一个数据库分区中。因此,我们应该开发一种静态分区方案,以始终以可预测的方式存储/查找 URL。
这种方法的主要问题是它可能导致数据库服务器不平衡。例如,我们决定将所有以字母“E”开头的 URL 放入 DB 分区,但后来我们意识到我们有太多以字母“E”开头的 URL。
另外基于散列的分区: 在这个方案中,我们获取我们正在存储的对象的散列。然后我们根据哈希计算要使用的分区。在我们的例子中,我们可以使用“键”或短链接的哈希值来确定我们存储数据对象的分区。
我们的散列函数会将 URL 随机分布到不同的分区中(例如,我们的散列函数总是可以将任何“键”映射到 [1…256] 之间的数字)。这个数字将代表我们存储对象的分区。
这种方法仍然会导致分区过载,这可以使用一致哈希解决。
可以缓存经常访问的 URL,结合缓存中间件例如 Memcached、redis,它可以存储完整的 URL 及其各自的哈希值。因此,应用服务器在访问后端存储之前,可以快速检查缓存是否具有所需的 URL。
我们应该有多少缓存内存? 我们可以从每天 20% 的流量开始,根据客户的使用模式,我们可以调整我们需要多少缓存服务器。如上所述,我们需要 170GB 的内存来缓存 20% 的日常流量。由于现代服务器可以拥有 256GB 内存,我们可以轻松地将所有缓存放入一台机器中。或者,我们可以使用几个较小的服务器来存储所有这些热门 URL。
哪种缓存驱逐策略最适合我们的需求? 当缓存已满,并且我们想用更新/更热的 URL 替换链接时,我们将如何选择?最近最少使用 (LRU) 可能是我们系统的合理策略。根据此政策,会首先丢弃最近最少使用的 URL,可以使用 Linked Hash Map 或类似的数据结构来存储我们的 URL 和哈希,这也将跟踪最近访问过的 URL。
如何更新每个缓存副本? 每当缓存未命中时,我们的服务器就会访问后端数据库。每当发生这种情况时,我们都可以更新缓存并将新条目传递给所有缓存副本。每个副本都可以通过添加新条目来更新其缓存。如果副本已经有该条目,它可以简单地忽略它。
我们可以在系统的三个地方添加负载均衡层:
条目应该永远存在,还是应该被清除?如果达到用户指定的过期时间,链接会发生什么?
如果我们选择不断搜索过期链接来删除它们,这会给我们的数据库带来很大的压力。相反,我们可以慢慢删除过期链接并进行惰性清理。我们的服务会确保只删除过期的链接。
用户能否创建私有 URL 或允许一组特定用户访问 URL?
可以将权限级别(公共/私有)与数据库中的每个 URL 一起存储,还可以创建一个单独的表来存储有权查看特定 URL 的 UserID。如果用户没有权限并尝试访问 URL,可以发回错误 (HTTP 401)。鉴于我们将数据存储在像 Cassandra 这样的 NoSQL 宽列数据库中,表存储权限的键将是“哈希”(或 KGS 生成的“键”)。这些列将存储那些有权查看 URL 的用户的用户 ID。
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)