MemCache 超详细解读

本贴最后更新于 2849 天前,其中的信息可能已经东海扬尘

MemCache 是什么

MemCache 是一个自由、源码开放、高性能、分布式的分布式内存对象缓存系统,用于动态 Web 应用以减轻数据库的负载。它通过在内存中缓存数据和对象来减少读取数据库的次数,从而提高了网站访问的速度。MemCaChe 是一个存储键值对的 HashMap,在内存中对任意的数据(比如字符串、对象等)所使用的 key-value 存储,数据可以来自数据库调用、API 调用,或者页面渲染的结果。MemCache 设计理念就是小而强大,它简单的设计促进了快速部署、易于开发并解决面对大规模的数据缓存的许多难题,而所开放的 API 使得 MemCache 能用于 Java、C/C++/C#、Perl、Python、PHP、Ruby 等大部分流行的程序语言。

另外,说一下 MemCache 和 MemCached 的区别:

1、MemCache 是项目的名称

2、MemCached 是 MemCache 服务器端可以执行文件的名称

MemCache 的官方网站为 http://memcached.org/

MemCache 访问模型

为了加深理解,我模仿着原阿里技术专家李智慧老师《大型网站技术架构 核心原理与案例分析》一书 MemCache 部分,自己画了一张图:

特别澄清一个问题,MemCache 虽然被称为"分布式缓存",但是 MemCache 本身完全不具备分布式的功能,MemCache 集群之间不会相互通信(与之形成对比的,比如 JBoss Cache,某台服务器有缓存数据更新时,会通知集群中其他机器更新缓存或清除缓存数据),所谓的"分布式",完全依赖于客户端程序的实现,就像上面这张图的流程一样。

同时基于这张图,理一下 MemCache 一次写缓存的流程:

1、应用程序输入需要写缓存的数据

2、API 将 Key 输入路由算法模块,路由算法根据 Key 和 MemCache 集群服务器列表得到一台服务器编号

3、由服务器编号得到 MemCache 及其的 ip 地址和端口号

4、API 调用通信模块和指定编号的服务器通信,将数据写入该服务器,完成一次分布式缓存的写操作

读缓存和写缓存一样,只要使用相同的路由算法和服务器列表,只要应用程序查询的是相同的 Key,MemCache 客户端总是访问相同的客户端去读取数据,只要服务器中还缓存着该数据,就能保证缓存命中。

这种 MemCache 集群的方式也是从分区容错性的方面考虑的,假如 Node2 宕机了,那么 Node2 上面存储的数据都不可用了,此时由于集群中 Node0 和 Node1 还存在,下一次请求 Node2 中存储的 Key 值的时候,肯定是没有命中的,这时先从数据库中拿到要缓存的数据,然后路由算法模块根据 Key 值在 Node0 和 Node1 中选取一个节点,把对应的数据放进去,这样下一次就又可以走缓存了,这种集群的做法很好,但是缺点是成本比较大。

一致性 Hash 算法

从上面的图中,可以看出一个很重要的问题,就是对服务器集群的管理,路由算法至关重要,就和负载均衡算法一样,路由算法决定着究竟该访问集群中的哪台服务器,先看一个简单的路由算法。

1、余数 Hash

比方说,字符串 str 对应的 HashCode 是 50、服务器的数目是 3,取余数得到 2,str 对应节点 Node2,所以路由算法把 str 路由到 Node2 服务器上。由于 HashCode 随机性比较强,所以使用余数 Hash 路由算法就可以保证缓存数据在整个 MemCache 服务器集群中有比较均衡的分布。

如果不考虑服务器集群的伸缩性(什么是伸缩性,请参见大型网站架构学习笔记),那么余数 Hash 算法几乎可以满足绝大多数的缓存路由需求,但是当分布式缓存集群需要扩容的时候,就难办了。

就假设 MemCache 服务器集群由 3 台变为 4 台吧,更改服务器列表,仍然使用余数 Hash,50 对 4 的余数是 2,对应 Node2,但是 str 原来是存在 Node1 上的,这就导致了缓存没有命中。如果这么说不够明白,那么不妨举个例子,原来有 HashCode 为 0~19 的 20 个数据,那么:

| HashCode | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 |
| 路由到的服务器 | 0 | 1 | 2 | 0 | 1 | 2 | 0 | 1 | 2 | 0 | 1 | 2 | 0 | 1 | 2 | 0 | 1 | 2 | 0 | 1 |

现在我扩容到 4 台,加粗标红的表示命中:

| HashCode | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 |
| 路由到的服务器 | 0 | 1 | 2 | 3 | 0 | 1 | 2 | 3 | 0 | 1 | 2 | 3 | 0 | 1 | 2 | 3 | 0 | 1 | 2 | 3 |

如果我扩容到 20+ 的台数,只有前三个 HashCode 对应的 Key 是命中的,也就是 15%。当然这只是个简单例子,现实情况肯定比这个复杂得多,不过足以说明,使用余数 Hash 的路由算法,在扩容的时候会造成大量的数据无法正确命中(其实不仅仅是无法命中,那些大量的无法命中的数据还在原缓存中在被移除前占据着内存)。这个结果显然是无法接受的,在网站业务中,大部分的业务数据度操作请求上事实上是通过缓存获取的,只有少量读操作会访问数据库,因此数据库的负载能力是以有缓存为前提而设计的。当大部分被缓存了的数据因为服务器扩容而不能正确读取时,这些数据访问的压力就落在了数据库的身上,这将大大超过数据库的负载能力,严重的可能会导致数据库宕机。

这个问题有解决方案,解决步骤为:

(1)在网站访问量低谷,通常是深夜,技术团队加班,扩容、重启服务器

(2)通过模拟请求的方式逐渐预热缓存,使缓存服务器中的数据重新分布

2、一致性 Hash 算法

一致性 Hash 算法通过一个叫做一致性 Hash 环的数据结构实现 Key 到缓存服务器的 Hash 映射,看一下我自己画的一张图:

具体算法过程为:先构造一个长度为 232 的整数环(这个环被称为一致性 Hash 环),根据节点名称的 Hash 值(其分布为[0, 232-1])将缓存服务器节点放置在这个 Hash 环上,然后根据需要缓存的数据的 Key 值计算得到其 Hash 值(其分布也为[0, 232-1]),然后在 Hash 环上顺时针查找距离这个 Key 值的 Hash 值最近的服务器节点,完成 Key 到服务器的映射查找。

就如同图上所示,三个 Node 点分别位于 Hash 环上的三个位置,然后 Key 值根据其 HashCode,在 Hash 环上有一个固定位置,位置固定下之后,Key 就会顺时针去寻找离它最近的一个 Node,把数据存储在这个 Node 的 MemCache 服务器中。使用 Hash 环如果加了一个节点会怎么样,看一下:

看到我加了一个 Node4 节点,只影响到了一个 Key 值的数据,本来这个 Key 值应该是在 Node1 服务器上的,现在要去 Node4 了。采用一致性 Hash 算法,的确也会影响到整个集群,但是影响的只是加粗的那一段而已,相比余数 Hash 算法影响了远超一半的影响率,这种影响要小得多。更重要的是,集群中缓存服务器节点越多,增加节点带来的影响越小,很好理解。换句话说,随着集群规模的增大,继续命中原有缓存数据的概率会越来越大,虽然仍然有小部分数据缓存在服务器中不能被读到,但是这个比例足够小,即使访问数据库,也不会对数据库造成致命的负载压力。

至于具体应用,这个长度为 232 的一致性 Hash 环通常使用二叉查找树实现,至于二叉查找树,就是算法的问题了,可以自己去查询相关资料。

MemCache 实现原理

首先要说明一点,MemCache 的数据存放在内存中,存放在内存中个人认为意味着几点:

1、访问数据的速度比传统的关系型数据库要快,因为 Oracle、MySQL 这些传统的关系型数据库为了保持数据的持久性,数据存放在硬盘中,IO 操作速度慢

2、MemCache 的数据存放在内存中同时意味着只要 MemCache 重启了,数据就会消失

3、既然 MemCache 的数据存放在内存中,那么势必受到机器位数的限制,这个之前的文章写过很多次了,32 位机器最多只能使用 2GB 的内存空间,64 位机器可以认为没有上限

然后我们来看一下 MemCache 的原理,MemCache 最重要的莫不是内存分配的内容了,MemCache 采用的内存分配方式是固定空间分配,还是自己画一张图说明:

这张图片里面涉及了 slab_class、slab、page、chunk 四个概念,它们之间的关系是:

1、MemCache 将内存空间分为一组 slab

2、每个 slab 下又有若干个 page,每个 page 默认是 1M,如果一个 slab 占用 100M 内存的话,那么这个 slab 下应该有 100 个 page

3、每个 page 里面包含一组 chunk,chunk 是真正存放数据的地方,同一个 slab 里面的 chunk 的大小是固定的

4、有相同大小 chunk 的 slab 被组织在一起,称为 slab_class

MemCache 内存分配的方式称为 allocator,slab 的数量是有限的,几个、十几个或者几十个,这个和启动参数的配置相关。

MemCache 中的 value 过来存放的地方是由 value 的大小决定的,value 总是会被存放到与 chunk 大小最接近的一个 slab 中,比如 slab[1]的 chunk 大小为 80 字节、slab[2]的 chunk 大小为 100 字节、slab[3]的 chunk 大小为 128 字节(相邻 slab 内的 chunk 基本以 1.25 为比例进行增长,MemCache 启动时可以用-f 指定这个比例),那么过来一个 88 字节的 value,这个 value 将被放到 2 号 slab 中。放 slab 的时候,首先 slab 要申请内存,申请内存是以 page 为单位的,所以在放入第一个数据的时候,无论大小为多少,都会有 1M 大小的 page 被分配给该 slab。申请到 page 后,slab 会将这个 page 的内存按 chunk 的大小进行切分,这样就变成了一个 chunk 数组,最后从这个 chunk 数组中选择一个用于存储数据。

如果这个 slab 中没有 chunk 可以分配了怎么办,如果 MemCache 启动没有追加-M(禁止 LRU,这种情况下内存不够会报 Out Of Memory 错误),那么 MemCache 会把这个 slab 中最近最少使用的 chunk 中的数据清理掉,然后放上最新的数据。针对 MemCache 的内存分配及回收算法,总结三点:

1、MemCache 的内存分配 chunk 里面会有内存浪费,88 字节的 value 分配在 128 字节(紧接着大的用)的 chunk 中,就损失了 30 字节,但是这也避免了管理内存碎片的问题

2、MemCache 的 LRU 算法不是针对全局的,是针对 slab 的

3、应该可以理解为什么 MemCache 存放的 value 大小是限制的,因为一个新数据过来,slab 会先以 page 为单位申请一块内存,申请的内存最多就只有 1M,所以 value 大小自然不能大于 1M 了

再总结 MemCache 的特性和限制

上面已经对于 MemCache 做了一个比较详细的解读,这里再次总结 MemCache 的限制和特性:

1、MemCache 中可以保存的 item 数据量是没有限制的,只要内存足够

2、MemCache 单进程在 32 位机中最大使用内存为 2G,这个之前的文章提了多次了,64 位机则没有限制

3、Key 最大为 250 个字节,超过该长度无法存储

4、单个 item 最大数据是 1MB,超过 1MB 的数据不予存储

5、MemCache 服务端是不安全的,比如已知某个 MemCache 节点,可以直接 telnet 过去,并通过 flush_all 让已经存在的键值对立即失效

6、不能够遍历 MemCache 中所有的 item,因为这个操作的速度相对缓慢且会阻塞其他的操作

7、MemCache 的高性能源自于两阶段哈希结构:第一阶段在客户端,通过 Hash 算法根据 Key 值算出一个节点;第二阶段在服务端,通过一个内部的 Hash 算法,查找真正的 item 并返回给客户端。从实现的角度看,MemCache 是一个非阻塞的、基于事件的服务器程序

8、MemCache 设置添加某一个 Key 值的时候,传入 expiry 为 0 表示这个 Key 值永久有效,这个 Key 值也会在 30 天之后失效,见 memcache.c 的源代码:

#define REALTIME_MAXDELTA 606024*30
static rel_time_t realtime(const time_t exptime) { if (exptime == 0) return 0; if (exptime > REALTIME_MAXDELTA) { if (exptime <= process_started) return (rel_time_t)1; return (rel_time_t)(exptime - process_started);
} else { return (rel_time_t)(exptime + current_time);
}
}

这个失效的时间是 memcache 源码里面写的,开发者没有办法改变 MemCache 的 Key 值失效时间为 30 天这个限制

MemCache 指令汇总

上面说过,已知 MemCache 的某个节点,直接 telnet 过去,就可以使用各种命令操作 MemCache 了,下面看下 MemCache 有哪几种命令:

| 命 令 | 作 用 |
| get | 返回 Key 对应的 Value 值 |
| add | 添加一个 Key 值,没有则添加成功并提示 STORED,有则失败并提示 NOT_STORED |
| set | 无条件地设置一个 Key 值,没有就增加,有就覆盖,操作成功提示 STORED |
| replace | 按照相应的 Key 值替换数据,如果 Key 值不存在则会操作失败 |
| stats | 返回 MemCache 通用统计信息(下面有详细解读) |
| stats items | 返回各个 slab 中 item 的数目和最老的 item 的年龄(最后一次访问距离现在的秒数) |
| stats slabs | 返回 MemCache 运行期间创建的每个 slab 的信息(下面有详细解读) |
| version | 返回当前 MemCache 版本号 |
| flush_all | 清空所有键值,但不会删除 items,所以此时 MemCache 依旧占用内存 |
| quit | 关闭连接 |

stats 指令解读

stats 是一个比较重要的指令,用于列出当前 MemCache 服务器的状态,拿一组数据举个例子:

STAT pid 1023 STAT uptime 21069937 STAT time 1447235954 STAT version 1.4.5 STAT pointer_size 64 STAT rusage_user 1167.020934 STAT rusage_system 3346.933170 STAT curr_connections 29 STAT total_connections 21 STAT connection_structures 49 STAT cmd_get 49 STAT cmd_set 7458 STAT cmd_flush 0 STAT get_hits 7401 STAT get_misses 57 ..(delete、incr、decr、cas 的 hits 和 misses 数,cas 还多一个 badval)
STAT auth_cmds 0 STAT auth_errors 0 STAT bytes_read 22026555 STAT bytes_written 8930466 STAT limit_maxbytes 4134304000 STAT accepting_conns 1 STAT listen_disabled_num 0 STAT threads 4 STAT bytes 151255336 STAT current_items 57146 STAT total_items 580656 STAT evicitions 0

这些参数反映着 MemCache 服务器的基本信息,它们的意思是:

| 参 数 名 | 作 用 |
| pid | MemCache 服务器的进程 id |
| uptime | 服务器已经运行的秒数 |
| time | 服务器当前的 UNIX 时间戳 |
| version | MemCache 版本 |
| pointer_size | 当前操作系统指针大小,反映了操作系统的位数,64 意味着 MemCache 服务器是 64 位的 |
| rusage_user | 进程的累计用户时间 |
| rusage_system | 进程的累计系统时间 |
| curr_connections | 当前打开着的连接数 |
| total_connections | 当服务器启动以后曾经打开过的连接数 |
| connection_structures | 服务器分配的连接构造数 |
| cmd_get | get 命令总请求次数 |
| cmd_set | set 命令总请求次数 |
| cmd_flush | flush_all 命令总请求次数 |
| get_hits | 总命中次数,重要,缓存最重要的参数就是缓存命中率,以 get_hits / (get_hits + get_misses)表示,比如这个缓存命中率就是 99.2% |
| get_misses | 总未命中次数 |
| auth_cmds | 认证命令的处理次数 |
| auth_errors | 认证失败的处理次数 |
| bytes_read | 总读取的字节数 |
| bytes_written | 总发送的字节数 |
| limit_maxbytes | 分配给 MemCache 的内存大小(单位为字节) |
| accepting_conns | 是否已经达到连接的最大值,1 表示达到,0 表示未达到 |
| listen_disabled_num | 统计当前服务器连接数曾经达到最大连接的次数,这个次数应该为 0 或者接近于 0,如果这个数字不断增长, 就要小心我们的服务了 |
| threads | 当前 MemCache 总线程数,由于 MemCache 的线程是基于事件驱动机制的,因此不会一个线程对应一个用户请求 |
| bytes | 当前服务器存储的 items 总字节数 |
| current_items | 当前服务器存储的 items 总数量 |
| total_items | 自服务器启动以后存储的 items 总数量 |

stats slab 指令解读

如果对上面的 MemCache 存储机制比较理解了,那么我们来看一下各个 slab 中的信息,还是拿一组数据举个例子:

1 STAT1:chunk_size 96
2 ...
3 STAT 2:chunk_size 144
4 STAT 2:chunks_per_page 7281
5 STAT 2:total_pages 7
6 STAT 2:total_chunks 50967
7 STAT 2:used_chunks 45197
8 STAT 2:free_chunks 1
9 STAT 2:free_chunks_end 5769
10 STAT 2:mem_requested 6084638
11 STAT 2:get_hits 48084
12 STAT 2:cmd_set 59588271
13 STAT 2:delete_hits 0
14 STAT 2:incr_hits 0
15 STAT 2:decr_hits 0
16 STAT 2:cas_hits 0
17 STAT 2:cas_badval 0
18 ... 19 STAT 3:chunk_size 216
20 ...

首先看到,第二个 slab 的 chunk_size(144)/第一个 slab 的 chunk_size(96)=1.5,第三个 slab 的 chunk_size(216)/第二个 slab 的 chunk_size(144)=1.5,可以确定这个 MemCache 的增长因子是 1.5,chunk_size 以 1.5 倍增长。然后解释下字段的含义:

| 参 数 名 | 作 用 |
| chunk_size | 当前 slab 每个 chunk 的大小,单位为字节 |
| chunks_per_page | 每个 page 可以存放的 chunk 数目,由于每个 page 固定为 1M 即 10241024 字节,所以这个值就是(10241024/chunk_size) |
| total_pages | 分配给当前 slab 的 page 总数 |
| total_chunks | 当前 slab 最多能够存放的 chunk 数,这个值是 total_pageschunks_per_page |
| used_chunks | 已经被分配给存储对象的 chunks 数目 |
| free_chunks | 曾经被使用过但是因为过期而被回收的 chunk 数 |
| free_chunks_end | 新分配但还没有被使用的 chunk 数,这个值不为 0 则说明当前 slab 从来没有出现过容量不够的时候 |
| mem_requested | 当前 slab 中被请求用来存储数据的内存空间字节总数,(total_chunks
chunk_size)-mem_requested 表示有多少内存在当前 slab 中是被闲置的,这包括未用的 slab+ 使用的 slab 中浪费的内存 |
| get_hits | 当前 slab 中命中的 get 请求数 |
| cmd_set | 当前 slab 中接收的所有 set 命令请求数 |
| delete_hits | 当前 slab 中命中的 delete 请求数 |
| incr_hits | 当前 slab 中命中的 incr 请求数 |
| decr_hits | 当前 slab 中命中的 decr 请求数 |
| cas_hits | 当前 slab 中命中的 cas 请求数 |
| cas_badval | 当前 slab 中命中但是更新失败的 cas 请求数 |

看到这个命令的输出量很大,所有信息都很有作用。举个例子吧,比如第一个 slab 中使用的 chunks 很少,第二个 slab 中使用的 chunks 很多,这时就可以考虑适当增大 MemCache 的增长因子了,让一部分数据落到第一个 slab 中去,适当平衡两个 slab 中的内存,避免空间浪费。

MemCache 的 Java 实现实例

讲了这么多,作为一个 Java 程序员,怎么能不写写 MemCache 的客户端的实现呢?MemCache 的客户端有很多第三方 jar 包提供了实现,其中比较好的当属 XMemCached 了,XMemCached 具有效率高、IO 非阻塞、资源耗费少、支持完整的协议、允许设置节点权重、允许动态增删节点、支持 JMX、支持与 Spring 框架集成、使用连接池、可扩展性好等诸多优点,因而被广泛使用。这里利用 XMemCache 写一个简单的 MemCache 客户单实例,也没有验证过,纯属抛砖引玉:

public class MemCacheManager
{ private static MemCacheManager instance = new MemCacheManager(); /** XMemCache 允许开发者通过设置节点权重来调节 MemCache 的负载,设置的权重越高,该 MemCache 节点存储的数据越多,负载越大 /
private static MemcachedClientBuilder mcb =
new XMemcachedClientBuilder(AddrUtil.getAddresses("127.0.0.1:11211 127.0.0.2:11211 127.0.0.3:11211"), new int[]{1, 3, 5}); private static MemcachedClient mc = null; /
* 初始化加载客户端 MemCache 信息 */
static {
mcb.setCommandFactory(new BinaryCommandFactory()); // 使用二进制文件
mcb.setConnectionPoolSize(10); // 连接池个数,即客户端个数
try {
mc = mcb.build();
} catch (IOException e)
{
e.printStackTrace();
}

} private MemCacheManager()
{

} public MemCacheManager getInstance()
{ return instance;
} /** 向MemCache服务器设置数据 */
public void set(String key, int expiry, Object obj) throws Exception
{
    mc.set(key, expiry, obj);
} /** 从MemCache服务器获取数据 */
public Object get(String key) throws Exception
{ return mc.get(key);
} /** * MemCache通过compare and set即cas协议实现原子更新,类似乐观锁,每次请求存储某个数据都要附带一个cas值,MemCache
 * 比对这个cas值与当前存储数据的cas值是否相等,如果相等就覆盖老数据,如果不相等就认为更新失败,这在并发环境下特别有用 */
public boolean update(String key, Integer i) throws Exception
{
    GetsResponse result = mc.gets(key); long cas = result.getCas(); // 尝试更新key对应的value
    if (!mc.cas(key, 0, i, cas))
    { return false;
    } return true;
}

}

**==================================================================================

我不能保证写的每个地方都是对的,但是至少能保证不复制、不黏贴,保证每一句话、每一行代码都经过了认真的推敲、仔细的斟酌。每一篇文章的背后,希望都能看到自己对于技术、对于生活的态度。

我相信乔布斯说的,只有那些疯狂到认为自己可以改变世界的人才能真正地改变世界。面对压力,我可以挑灯夜战、不眠不休;面对困难,我愿意迎难而上、永不退缩。

**其实我想说的是,我只是一个程序员,这就是我现在纯粹人生的全部。

==================================================================================****

相关帖子

欢迎来到这里!

我们正在构建一个小众社区,大家在这里相互信任,以平等 • 自由 • 奔放的价值观进行分享交流。最终,希望大家能够找到与自己志同道合的伙伴,共同成长。

注册 关于
请输入回帖内容 ...

推荐标签 标签

  • Ubuntu

    Ubuntu(友帮拓、优般图、乌班图)是一个以桌面应用为主的 Linux 操作系统,其名称来自非洲南部祖鲁语或豪萨语的“ubuntu”一词,意思是“人性”、“我的存在是因为大家的存在”,是非洲传统的一种价值观,类似华人社会的“仁爱”思想。Ubuntu 的目标在于为一般用户提供一个最新的、同时又相当稳定的主要由自由软件构建而成的操作系统。

    126 引用 • 169 回帖
  • Thymeleaf

    Thymeleaf 是一款用于渲染 XML/XHTML/HTML5 内容的模板引擎。类似 Velocity、 FreeMarker 等,它也可以轻易的与 Spring 等 Web 框架进行集成作为 Web 应用的模板引擎。与其它模板引擎相比,Thymeleaf 最大的特点是能够直接在浏览器中打开并正确显示模板页面,而不需要启动整个 Web 应用。

    11 引用 • 19 回帖 • 364 关注
  • SendCloud

    SendCloud 由搜狐武汉研发中心孵化的项目,是致力于为开发者提供高质量的触发邮件服务的云端邮件发送平台,为开发者提供便利的 API 接口来调用服务,让邮件准确迅速到达用户收件箱并获得强大的追踪数据。

    2 引用 • 8 回帖 • 486 关注
  • 导航

    各种网址链接、内容导航。

    42 引用 • 175 回帖
  • CentOS

    CentOS(Community Enterprise Operating System)是 Linux 发行版之一,它是来自于 Red Hat Enterprise Linux 依照开放源代码规定释出的源代码所编译而成。由于出自同样的源代码,因此有些要求高度稳定的服务器以 CentOS 替代商业版的 Red Hat Enterprise Linux 使用。两者的不同在于 CentOS 并不包含封闭源代码软件。

    238 引用 • 224 回帖
  • Hprose

    Hprose 是一款先进的轻量级、跨语言、跨平台、无侵入式、高性能动态远程对象调用引擎库。它不仅简单易用,而且功能强大。你无需专门学习,只需看上几眼,就能用它轻松构建分布式应用系统。

    9 引用 • 17 回帖 • 612 关注
  • 微软

    微软是一家美国跨国科技公司,也是世界 PC 软件开发的先导,由比尔·盖茨与保罗·艾伦创办于 1975 年,公司总部设立在华盛顿州的雷德蒙德(Redmond,邻近西雅图)。以研发、制造、授权和提供广泛的电脑软件服务业务为主。

    8 引用 • 44 回帖 • 1 关注
  • 星云链

    星云链是一个开源公链,业内简单的将其称为区块链上的谷歌。其实它不仅仅是区块链搜索引擎,一个公链的所有功能,它基本都有,比如你可以用它来开发部署你的去中心化的 APP,你可以在上面编写智能合约,发送交易等等。3 分钟快速接入星云链 (NAS) 测试网

    3 引用 • 16 回帖 • 6 关注
  • LeetCode

    LeetCode(力扣)是一个全球极客挚爱的高质量技术成长平台,想要学习和提升专业能力从这里开始,充足技术干货等你来啃,轻松拿下 Dream Offer!

    209 引用 • 72 回帖
  • OpenShift

    红帽提供的 PaaS 云,支持多种编程语言,为开发人员提供了更为灵活的框架、存储选择。

    14 引用 • 20 回帖 • 632 关注
  • 开源

    Open Source, Open Mind, Open Sight, Open Future!

    407 引用 • 3578 回帖 • 1 关注
  • Ruby

    Ruby 是一种开源的面向对象程序设计的服务器端脚本语言,在 20 世纪 90 年代中期由日本的松本行弘(まつもとゆきひろ/Yukihiro Matsumoto)设计并开发。在 Ruby 社区,松本也被称为马茨(Matz)。

    7 引用 • 31 回帖 • 216 关注
  • OAuth

    OAuth 协议为用户资源的授权提供了一个安全的、开放而又简易的标准。与以往的授权方式不同之处是 oAuth 的授权不会使第三方触及到用户的帐号信息(如用户名与密码),即第三方无需使用用户的用户名与密码就可以申请获得该用户资源的授权,因此 oAuth 是安全的。oAuth 是 Open Authorization 的简写。

    36 引用 • 103 回帖 • 17 关注
  • JSON

    JSON (JavaScript Object Notation)是一种轻量级的数据交换格式。易于人类阅读和编写。同时也易于机器解析和生成。

    52 引用 • 190 回帖
  • 国际化

    i18n(其来源是英文单词 internationalization 的首末字符 i 和 n,18 为中间的字符数)是“国际化”的简称。对程序来说,国际化是指在不修改代码的情况下,能根据不同语言及地区显示相应的界面。

    8 引用 • 26 回帖 • 1 关注
  • PostgreSQL

    PostgreSQL 是一款功能强大的企业级数据库系统,在 BSD 开源许可证下发布。

    22 引用 • 22 回帖 • 1 关注
  • TGIF

    Thank God It's Friday! 感谢老天,总算到星期五啦!

    288 引用 • 4485 回帖 • 663 关注
  • 宕机

    宕机,多指一些网站、游戏、网络应用等服务器一种区别于正常运行的状态,也叫“Down 机”、“当机”或“死机”。宕机状态不仅仅是指服务器“挂掉了”、“死机了”状态,也包括服务器假死、停用、关闭等一些原因而导致出现的不能够正常运行的状态。

    13 引用 • 82 回帖 • 59 关注
  • 友情链接

    确认过眼神后的灵魂连接,站在链在!

    24 引用 • 373 回帖
  • WebClipper

    Web Clipper 是一款浏览器剪藏扩展,它可以帮助你把网页内容剪藏到本地。

    3 引用 • 9 回帖 • 4 关注
  • etcd

    etcd 是一个分布式、高可用的 key-value 数据存储,专门用于在分布式系统中保存关键数据。

    5 引用 • 26 回帖 • 528 关注
  • 服务器

    服务器,也称伺服器,是提供计算服务的设备。由于服务器需要响应服务请求,并进行处理,因此一般来说服务器应具备承担服务并且保障服务的能力。

    125 引用 • 588 回帖
  • C++

    C++ 是在 C 语言的基础上开发的一种通用编程语言,应用广泛。C++ 支持多种编程范式,面向对象编程、泛型编程和过程化编程。

    107 引用 • 153 回帖
  • 创业

    你比 99% 的人都优秀么?

    85 引用 • 1399 回帖 • 1 关注
  • JWT

    JWT(JSON Web Token)是一种用于双方之间传递信息的简洁的、安全的表述性声明规范。JWT 作为一个开放的标准(RFC 7519),定义了一种简洁的,自包含的方法用于通信双方之间以 JSON 的形式安全的传递信息。

    20 引用 • 15 回帖 • 6 关注
  • 架构

    我们平时所说的“架构”主要是指软件架构,这是有关软件整体结构与组件的抽象描述,用于指导软件系统各个方面的设计。另外还有“业务架构”、“网络架构”、“硬件架构”等细分领域。

    142 引用 • 442 回帖 • 1 关注
  • 强迫症

    强迫症(OCD)属于焦虑障碍的一种类型,是一组以强迫思维和强迫行为为主要临床表现的神经精神疾病,其特点为有意识的强迫和反强迫并存,一些毫无意义、甚至违背自己意愿的想法或冲动反反复复侵入患者的日常生活。

    15 引用 • 161 回帖 • 2 关注