其他

最后更新于:2022-04-01 21:50:07

RESTful登录设计(基于Spring及Redis的Token鉴权) http://www.scienjus.com/restful-token-authorization/
';

redis集群

最后更新于:2022-04-01 21:50:05

http://blog.csdn.net/canot/article/details/52702029 http://hot66hot.iteye.com/blog/2050676 http://www.cnblogs.com/yuanermen/p/5717885.html
';

Redis主从复制

最后更新于:2022-04-01 21:50:03

# Redis主从复制 ## 目标 1. 容灾 2. 负载均衡,读写分离 ## 特点 1. master/slave结构,master可读写,slave只能读。 2. master宕机,slave不会自动完成切换 3. master宕机后恢复,slave会自动与master再次建立复制关系 ## 实现 * 超级简单,在redis.conf里面配置加入 ``` slaveof masterauth <密码> #如果主节点配置了requirepass,这里要与主节点保持一致。 ``` 重启redis实例 * 其他方式 1. 在用redis-server启动命令后,执行slaveof 2. 命令行执行`slaveof ` 这两种都是动态的指定。 ## 验证(使用redis的info命令) * master实例 ``` # Replication role:master connected_slaves:1 slave0:ip=,port=6379,state=online,offset=183,lag=1 master_repl_offset:197 repl_backlog_active:1 repl_backlog_size:1048576 repl_backlog_first_byte_offset:2 repl_backlog_histlen:196 ``` * slave实例 ``` # Replication role:slave master_host: master_port:6379 master_link_status:up master_last_io_seconds_ago:7 master_sync_in_progress:0 slave_repl_offset:799 slave_priority:100 slave_read_only:1 connected_slaves:0 master_repl_offset:0 repl_backlog_active:0 repl_backlog_size:1048576 repl_backlog_first_byte_offset:0 repl_backlog_histlen:0 ``` ## 主从拓扑结构 #### 一主一从 当主节点出现故障,从节点提供服务 从节点可以开启AOF,保证数据安全,减轻持久化对主节点造成的压力 问题:但是在重启时因为主节点没有做持久化操作,导致主节点数据为空,导致子节点数据也被清空,所以再重启之前进行 slaveof no one断开与主节点的复制关系。 #### 一主多从 读写分离,适用读较多的场景,可以把读命令发送给从节点来分担主节点的压力。如keys、sort命令这种消耗性能的查询命令,可以交给一台从节点执行,防止对主节点造成阻塞。 问题:在高并发写入时,多个节点数据的同步会造成主节点性能的低下。 #### 树状主从 树形结构,一个节点既可以作为主节点又可以作为从节点。 通过数据复制中间层,数据向下传递,减少了主节点的压力,弥补了一主多从的缺憾。 缺点:占用了更多的服务器资源
';

redis数据持久化

最后更新于:2022-04-01 21:50:00

## 两种持久化方法的基本原理 * RDB持久化:将Reids在内存中的数据库记录定时dump到磁盘上的RDB持久化 * AOF定义:以日志的形式记录每个操作,将Redis执行过的所有指令全部记录下来(读操作不记录),只许追加文件但不可以修改文件.Redis启动时会读取AOF配置文件重构数据,换句话说,就是Redis重启就会根据日志内容从头到尾执行一次来完成数据的恢复工作。 >一.RDB与AOF同时开启 默认先加载AOF的配置文件 二.相同数据集,AOF文件要远大于RDB文件,恢复速度慢于RDB 三.AOF运行效率慢于RDB,但是同步策略效率好,不同步效率和RDB相同 ### RDB持久化基本配置实现 1. RDB持久化(以快照的方式) 策略(默认):   save 900 1 (15分钟变更一次)   save 300 10 (5分钟变更10次)   save 60 10000 (1分钟变更1万次) 2. RDB默认配置文件名称:   dbfilename dump.rdb > 如果关闭RDB持久化方式,就把配置注释掉 ### AOF持久化配置实现 1. 表示是否开启AOF持久化:   appendonly yes(默认no,关闭) 2. AOF持久化配置文件的名称:   appendfilename "appendonly.aof" 3. AOF持久化策略(默认每秒):   appendfsync always (同步持久化,每次发生数据变更会被立即记录到磁盘,性能差但数据完整性比较好)   appendfsync everysec (异步操作,每秒记录,如果一秒钟内宕机,有数据丢失,性能与数据安全折中,推荐)   appendfsync no (不同步) 4. AOF配置文件损坏修复方法:   进入redis安装路径 执行 redis-check-aof --fix AOF配置文件名称 5. AOF的Rewrite(重写) : * 定义:AOF采用文件追加的方式持久化数据,所以文件会越来越大,为了避免这种情况发生,增加了重写机制 * 当AOF文件的大小超过了配置所设置的阙值时,Redis就会启动AOF文件压缩,只保留可以恢复数据的最小指令集,可以使用命令bgrewriteaof * 原理:当AOF增长过大时,会fork出一条新的进程将文件重写(也是先写临时文件最后rename),遍历新进程的内存数据,每条记录有一条set语句。 * 重写AOF文件并没有操作旧的AOF文件,而是将整个内存中的数据内容用命令的方式重写了一个新的aof文件(有点类似快照) * 触发机制:Redis会记录上次重写时的AOF文件大小,默认配置时当AOF文件大小是上次rewrite后大小的一倍且文件大于64M时触发 ```      auto-aof-rewrite-percentage 100 (一倍)      auto-aof-rewrite-min-size 64mb ``` ## RDB与AOF的选择: * 做备份:当数据量大,且对恢复速度有要求,并且数据的一致性要求不高的话,可以只使用RDB * 只做缓存:不用开启任何的持久化方式 * 两者都开启的建议:RDB数据不实时,同时使用两者时服务器只会找AOF文件. > 可不可以只使用AOF?作者建议不要,因为RDB更适合备份数据库(AOF在不断变化,不好备份) ## 优化: ![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/ecc9a3b44bb80ba3da0f345486d0a875_966x270.png) * 建议一种使用方式:redis主从结构,只在slave上持久化RDB文件,而且只要15分钟备份一次就够了,只保留save 900 1 这条规则 * 在高并发大数据量写的情况下,不建议配置自动保存RDB。在RDB保存时会fock创建一个子进程,此操作过程父进程会阻塞! ## 参考    [redis数据丢失及解决](http://blog.csdn.net/xiangliangyu/article/details/8165644)
';

redis命令

最后更新于:2022-04-01 21:49:58

## Redis命令 ### redis数据结构 – strings ``` set mystr "hello world!" //设置字符串类型 get mystr //读取字符串类型 ``` >字符串类型的用法就是这么简单,因为是二进制安全的,所以你完全可以把一个图片文件的内容作为字符串来存储。 另外,我们还可以通过字符串类型进行数值操作: ``` 127.0.0.1:6379> set mynum "2" OK 127.0.0.1:6379> get mynum "2" 127.0.0.1:6379> incr mynum (integer) 3 127.0.0.1:6379> get mynum "3" ``` > 在遇到数值操作时,redis会将字符串类型转换成数值。 利用redis的INCR、INCRBY、DECR、DECRBY等指令来实现原子计数的效果. ### redis数据结构 – lists * redis中的lists在底层实现上并不是数组,而是链表,链表型lists的元素定位会比较慢。 * lists的常用操作包括LPUSH、RPUSH、LRANGE等。我们可以用LPUSH在lists的左侧插入一个新元素,用RPUSH在lists的右侧插入一个新元素,用LRANGE命令从lists中指定一个范围来提取元素。 ``` //新建一个list叫做mylist,并在列表头部插入元素"1" 127.0.0.1:6379> lpush mylist "1" (integer) 1 //返回当前mylist中的元素个数 127.0.0.1:6379> rpush mylist "2" //在mylist右侧插入元素"2" (integer) 2 127.0.0.1:6379> lpush mylist "0" //在mylist左侧插入元素"0" (integer) 3 127.0.0.1:6379> lrange mylist 0 1 //列出mylist中从编号0到编号1的元素 1) "0" 2) "1" 127.0.0.1:6379> lrange mylist 0 -1 //列出mylist中从编号0到倒数第一个元素 1) "0" 2) "1" 3) "2" ``` >lists的应用相当广泛,随便举几个例子: 1.我们可以利用lists来实现一个消息队列,而且可以确保先后顺序,不必像MySQL那样还需要通过ORDER BY来进行排序。 2.利用LRANGE还可以很方便的实现分页的功能。 3.在博客系统中,每片博文的评论也可以存入一个单独的list中。 ### redis数据结构 – 集合Set * redis的集合,是一种无序的集合,集合中的元素没有先后顺序。 * 集合是通过哈希表实现的,所以添加,删除,查找的复杂度都是O(1)。 * 集合相关的操作也很丰富,如添加新元素、删除已有元素、取交集、取并集、取差集等。 ``` 127.0.0.1:6379> sadd myset "one" //向集合myset中加入一个新元素"one" (integer) 1 127.0.0.1:6379> sadd myset "two" (integer) 1 127.0.0.1:6379> smembers myset //列出集合myset中的所有元素 1) "one" 2) "two" 127.0.0.1:6379> sismember myset "one" //判断元素1是否在集合myset中,返回1表示存在 (integer) 1 127.0.0.1:6379> sismember myset "three" //判断元素3是否在集合myset中,返回0表示不存在 (integer) 0 127.0.0.1:6379> sadd yourset "1" //新建一个新的集合yourset (integer) 1 127.0.0.1:6379> sadd yourset "2" (integer) 1 127.0.0.1:6379> smembers yourset 1) "1" 2) "2" 127.0.0.1:6379> sunion myset yourset //对两个集合求并集 1) "1" 2) "one" 3) "2" 4) "two" ``` 对于集合的使用,也有一些常见的方式,比如,QQ有一个社交功能叫做“好友标签”,大家可以给你的好友贴标签,比如“大美女”、“土豪”、“欧巴”等等,这时就可以使用redis的集合来实现,把每一个用户的标签都存储在一个集合之中。 ### redis数据结构 – 有序集合 * redis不但提供了无序集合(sets),还很体贴的提供了有序集合(sorted sets)。 * 有序集合中的每个元素都关联一个序号(score),这便是排序的依据。 * 很多时候,我们都将redis中的有序集合叫做zsets,这是因为在redis中,有序集合相关的操作指令都是以z开头的,比如zrange、zadd、zrevrange、zrangebyscore等等 ``` //新增一个有序集合myzset,并加入一个元素baidu.com,给它赋予的序号是1: 复制代码代码如下: 127.0.0.1:6379> zadd myzset 1 baidu.com (integer) 1 //向myzset中新增一个元素360.com,赋予它的序号是3 127.0.0.1:6379> zadd myzset 3 360.com (integer) 1 //向myzset中新增一个元素google.com,赋予它的序号是2 127.0.0.1:6379> zadd myzset 2 google.com (integer) 1 //列出myzset的所有元素,同时列出其序号,可以看出myzset已经是有序的了。 127.0.0.1:6379> zrange myzset 0 -1 with scores 1) "baidu.com" 2) "1" 3) "google.com" 4) "2" 5) "360.com" 6) "3" //只列出myzset的元素 127.0.0.1:6379> zrange myzset 0 -1 1) "baidu.com" 2) "google.com" 3) "360.com" ``` ### redis数据结构 – 哈希 最后要给大家介绍的是hashes,即哈希。哈希是从redis-2.0.0版本之后才有的数据结构。 hashes存的是字符串和字符串值之间的映射,比如一个用户要存储其全名、姓氏、年龄等等,就很适合使用哈希。 ``` //建立哈希,并赋值 127.0.0.1:6379> HMSET user:001 username antirez password P1pp0 age 34 OK //列出哈希的内容 127.0.0.1:6379> HGETALL user:001 1) "username" 2) "antirez" 3) "password" 4) "P1pp0" 5) "age" 6) "34" //更改哈希中的某一个值 127.0.0.1:6379> HSET user:001 password 12345 (integer) 0 //再次列出哈希的内容 127.0.0.1:6379> HGETALL user:001 1) "username" 2) "antirez" 3) "password" 4) "12345" 5) "age" 6) "34" ```
';

redis数据结构

最后更新于:2022-04-01 21:49:56

## Redis支持的数据结构 ### 一.redis是一种高级的key:value存储系统,其中value支持五种数据类型: > 用java的数据结构去理解redis数据结构,就是Map * 字符串(strings),是二进制安全的,所以你完全可以把一个图片文件的内容作为字符串来存储。另外,支持字符串类型数值进行数值操作. > 用java的数据结构去理解redis数据结构,Map,key = String,Object = String * 列表(lists),底层实现上并不是数组,而是双链表,链表型lists的元素定位会相对慢。适合于取头或者尾部的N个元素。 > 用java的数据结构去理解redis数据结构,Map,key = String,Object = LinkedList * 集合(sets),是一种无序的集合,集合中的元素没有先后顺序。 集合是通过哈希表实现的,所以添加,删除,查找的复杂度都是O(1)。 > 用java的数据结构去理解redis数据结构,Map,key = String,Object = HsahSet * 有序集合(sorted sets),有序集合中的每个元素都关联一个序号(score),作为排序的依据 > 用java的数据结构去理解redis数据结构,Map,key = String,Object = TreeSet * 哈希(hashes),类似于java中的HashMap,适合存储一个对象的多个键值对属性。 > 用java的数据结构去理解redis数据结构,Map,key = String,Object = HashMap > 而关于key,有几个点要提醒大家: 1. key不要太长,尽量不要超过1024字节,这不仅消耗内存,而且会降低查找的效率; 2. key也不要太短,太短的话,key的可读性会降低; 3. 在一个项目中,key最好使用统一的命名模式,对象分类:id:属性,例如user:10000:passwd。 ### 二.java操作各种数据类型 #### 2.1Redis工具类 ```java package com.redis; import java.util.ArrayList; import java.util.Iterator; import java.util.List; import java.util.Set; import redis.clients.jedis.Jedis; import redis.clients.jedis.JedisPool; import redis.clients.jedis.JedisPoolConfig; import redis.clients.jedis.JedisShardInfo; import redis.clients.jedis.ShardedJedis; import redis.clients.jedis.ShardedJedisPool; import redis.clients.jedis.SortingParams; public class RedisClient { private Jedis jedis;//非切片额客户端连接 private JedisPool jedisPool;//非切片连接池 private ShardedJedis shardedJedis;//切片额客户端连接 private ShardedJedisPool shardedJedisPool;//切片连接池 public RedisClient() { initialPool(); initialShardedPool(); shardedJedis = shardedJedisPool.getResource(); jedis = jedisPool.getResource(); } /** * 初始化非切片池 */ private void initialPool() { // 池基本配置 JedisPoolConfig config = new JedisPoolConfig(); config.setMaxActive(20); config.setMaxIdle(5); config.setMaxWait(1000l); config.setTestOnBorrow(false); jedisPool = new JedisPool(config,"127.0.0.1",6379); } /** * 初始化切片池 */ private void initialShardedPool() { // 池基本配置 JedisPoolConfig config = new JedisPoolConfig(); config.setMaxActive(20); config.setMaxIdle(5); config.setMaxWait(1000l); config.setTestOnBorrow(false); // slave链接 List shards = new ArrayList(); shards.add(new JedisShardInfo("127.0.0.1", 6379, "master")); // 构造池 shardedJedisPool = new ShardedJedisPool(config, shards); } public void show() { KeyOperate(); StringOperate(); ListOperate(); SetOperate(); SortedSetOperate(); HashOperate(); jedisPool.returnResource(jedis); shardedJedisPool.returnResource(shardedJedis); } private void KeyOperate() { 。。。 } private void StringOperate() { 。。。 } private void ListOperate() { 。。。 } private void SetOperate() { 。。。 } private void SortedSetOperate() { 。。。 } private void HashOperate() { 。。。 } } ``` #### 2.2 Key功能 ```java private void KeyOperate() { System.out.println("======================key=========================="); // 清空数据 System.out.println("清空库中所有数据:"+jedis.flushDB()); // 判断key否存在 System.out.println("判断key999键是否存在:"+shardedJedis.exists("key999")); System.out.println("新增key001,value001键值对:"+shardedJedis.set("key001", "value001")); System.out.println("判断key001是否存在:"+shardedJedis.exists("key001")); // 输出系统中所有的key System.out.println("新增key002,value002键值对:"+shardedJedis.set("key002", "value002")); System.out.println("系统中所有键如下:"); Set keys = jedis.keys("*"); Iterator it=keys.iterator() ; while(it.hasNext()){ String key = it.next(); System.out.println(key); } // 删除某个key,若key不存在,则忽略该命令。 System.out.println("系统中删除key002: "+jedis.del("key002")); System.out.println("判断key002是否存在:"+shardedJedis.exists("key002")); // 设置 key001的过期时间 System.out.println("设置 key001的过期时间为5秒:"+jedis.expire("key001", 5)); try{ Thread.sleep(2000); } catch (InterruptedException e){ } // 查看某个key的剩余生存时间,单位【秒】.永久生存或者不存在的都返回-1 System.out.println("查看key001的剩余生存时间:"+jedis.ttl("key001")); // 移除某个key的生存时间 System.out.println("移除key001的生存时间:"+jedis.persist("key001")); System.out.println("查看key001的剩余生存时间:"+jedis.ttl("key001")); // 查看key所储存的值的类型 System.out.println("查看key所储存的值的类型:"+jedis.type("key001")); /* * 一些其他方法:1、修改键名:jedis.rename("key6", "key0"); * 2、将当前db的key移动到给定的db当中:jedis.move("foo", 1) */ } ``` 运行结果: ======================key========================== 清空库中所有数据:OK 判断key999键是否存在:false 新增key001,value001键值对:OK 判断key001是否存在:true 新增key002,value002键值对:OK 系统中所有键如下: key002 key001 系统中删除key002: 1 判断key002是否存在:false 设置 key001的过期时间为5秒:1 查看key001的剩余生存时间:3 移除key001的生存时间:1 查看key001的剩余生存时间:-1 查看key所储存的值的类型:string #### 2.3 String功能 ``` java private void StringOperate() { System.out.println("======================String_1=========================="); // 清空数据 System.out.println("清空库中所有数据:"+jedis.flushDB()); System.out.println("=============增============="); jedis.set("key001","value001"); jedis.set("key002","value002"); jedis.set("key003","value003"); System.out.println("已新增的3个键值对如下:"); System.out.println(jedis.get("key001")); System.out.println(jedis.get("key002")); System.out.println(jedis.get("key003")); System.out.println("=============删============="); System.out.println("删除key003键值对:"+jedis.del("key003")); System.out.println("获取key003键对应的值:"+jedis.get("key003")); System.out.println("=============改============="); //1、直接覆盖原来的数据 System.out.println("直接覆盖key001原来的数据:"+jedis.set("key001","value001-update")); System.out.println("获取key001对应的新值:"+jedis.get("key001")); //2、直接覆盖原来的数据 System.out.println("在key002原来值后面追加:"+jedis.append("key002","+appendString")); System.out.println("获取key002对应的新值"+jedis.get("key002")); System.out.println("=============增,删,查(多个)============="); /** * mset,mget同时新增,修改,查询多个键值对 * 等价于: * jedis.set("name","ssss"); * jedis.set("jarorwar","xxxx"); */ System.out.println("一次性新增key201,key202,key203,key204及其对应值:"+jedis.mset("key201","value201", "key202","value202","key203","value203","key204","value204")); System.out.println("一次性获取key201,key202,key203,key204各自对应的值:"+ jedis.mget("key201","key202","key203","key204")); System.out.println("一次性删除key201,key202:"+jedis.del(new String[]{"key201", "key202"})); System.out.println("一次性获取key201,key202,key203,key204各自对应的值:"+ jedis.mget("key201","key202","key203","key204")); System.out.println(); //jedis具备的功能shardedJedis中也可直接使用,下面测试一些前面没用过的方法 System.out.println("======================String_2=========================="); // 清空数据 System.out.println("清空库中所有数据:"+jedis.flushDB()); System.out.println("=============新增键值对时防止覆盖原先值============="); System.out.println("原先key301不存在时,新增key301:"+shardedJedis.setnx("key301", "value301")); System.out.println("原先key302不存在时,新增key302:"+shardedJedis.setnx("key302", "value302")); System.out.println("当key302存在时,尝试新增key302:"+shardedJedis.setnx("key302", "value302_new")); System.out.println("获取key301对应的值:"+shardedJedis.get("key301")); System.out.println("获取key302对应的值:"+shardedJedis.get("key302")); System.out.println("=============超过有效期键值对被删除============="); // 设置key的有效期,并存储数据 System.out.println("新增key303,并指定过期时间为2秒"+shardedJedis.setex("key303", 2, "key303-2second")); System.out.println("获取key303对应的值:"+shardedJedis.get("key303")); try{ Thread.sleep(3000); } catch (InterruptedException e){ } System.out.println("3秒之后,获取key303对应的值:"+shardedJedis.get("key303")); System.out.println("=============获取原值,更新为新值一步完成============="); System.out.println("key302原值:"+shardedJedis.getSet("key302", "value302-after-getset")); System.out.println("key302新值:"+shardedJedis.get("key302")); System.out.println("=============获取子串============="); System.out.println("获取key302对应值中的子串:"+shardedJedis.getrange("key302", 5, 7)); } ``` 运行结果: ======================String_1========================== 清空库中所有数据:OK =============增============= 已新增的3个键值对如下: value001 value002 value003 =============删============= 删除key003键值对:1 获取key003键对应的值:null =============改============= 直接覆盖key001原来的数据:OK 获取key001对应的新值:value001-update 在key002原来值后面追加:21 获取key002对应的新值value002+appendString =============增,删,查(多个)============= 一次性新增key201,key202,key203,key204及其对应值:OK 一次性获取key201,key202,key203,key204各自对应的值:[value201, value202, value203, value204] 一次性删除key201,key202:2 一次性获取key201,key202,key203,key204各自对应的值:[null, null, value203, value204] ======================String_2========================== 清空库中所有数据:OK =============新增键值对时防止覆盖原先值============= 原先key301不存在时,新增key301:1 原先key302不存在时,新增key302:1 当key302存在时,尝试新增key302:0 获取key301对应的值:value301 获取key302对应的值:value302 =============超过有效期键值对被删除============= 新增key303,并指定过期时间为2秒OK 获取key303对应的值:key303-2second 3秒之后,获取key303对应的值:null =============获取原值,更新为新值一步完成============= key302原值:value302 key302新值:value302-after-getset =============获取子串============= 获取key302对应值中的子串:302 #### 2.4 List功能 ```java private void ListOperate() { System.out.println("======================list=========================="); // 清空数据 System.out.println("清空库中所有数据:"+jedis.flushDB()); System.out.println("=============增============="); shardedJedis.lpush("stringlists", "vector"); shardedJedis.lpush("stringlists", "ArrayList"); shardedJedis.lpush("stringlists", "vector"); shardedJedis.lpush("stringlists", "vector"); shardedJedis.lpush("stringlists", "LinkedList"); shardedJedis.lpush("stringlists", "MapList"); shardedJedis.lpush("stringlists", "SerialList"); shardedJedis.lpush("stringlists", "HashList"); shardedJedis.lpush("numberlists", "3"); shardedJedis.lpush("numberlists", "1"); shardedJedis.lpush("numberlists", "5"); shardedJedis.lpush("numberlists", "2"); System.out.println("所有元素-stringlists:"+shardedJedis.lrange("stringlists", 0, -1)); System.out.println("所有元素-numberlists:"+shardedJedis.lrange("numberlists", 0, -1)); System.out.println("=============删============="); // 删除列表指定的值 ,第二个参数为删除的个数(有重复时),后add进去的值先被删,类似于出栈 System.out.println("成功删除指定元素个数-stringlists:"+shardedJedis.lrem("stringlists", 2, "vector")); System.out.println("删除指定元素之后-stringlists:"+shardedJedis.lrange("stringlists", 0, -1)); // 删除区间以外的数据 System.out.println("删除下标0-3区间之外的元素:"+shardedJedis.ltrim("stringlists", 0, 3)); System.out.println("删除指定区间之外元素后-stringlists:"+shardedJedis.lrange("stringlists", 0, -1)); // 列表元素出栈 System.out.println("出栈元素:"+shardedJedis.lpop("stringlists")); System.out.println("元素出栈后-stringlists:"+shardedJedis.lrange("stringlists", 0, -1)); System.out.println("=============改============="); // 修改列表中指定下标的值 shardedJedis.lset("stringlists", 0, "hello list!"); System.out.println("下标为0的值修改后-stringlists:"+shardedJedis.lrange("stringlists", 0, -1)); System.out.println("=============查============="); // 数组长度 System.out.println("长度-stringlists:"+shardedJedis.llen("stringlists")); System.out.println("长度-numberlists:"+shardedJedis.llen("numberlists")); // 排序 /* * list中存字符串时必须指定参数为alpha,如果不使用SortingParams,而是直接使用sort("list"), * 会出现"ERR One or more scores can't be converted into double" */ SortingParams sortingParameters = new SortingParams(); sortingParameters.alpha(); sortingParameters.limit(0, 3); System.out.println("返回排序后的结果-stringlists:"+shardedJedis.sort("stringlists",sortingParameters)); System.out.println("返回排序后的结果-numberlists:"+shardedJedis.sort("numberlists")); // 子串: start为元素下标,end也为元素下标;-1代表倒数一个元素,-2代表倒数第二个元素 System.out.println("子串-第二个开始到结束:"+shardedJedis.lrange("stringlists", 1, -1)); // 获取列表指定下标的值 System.out.println("获取下标为2的元素:"+shardedJedis.lindex("stringlists", 2)+"\n"); } ``` 运行结果: ======================list========================== 清空库中所有数据:OK =============增============= 所有元素-stringlists:[HashList, SerialList, MapList, LinkedList, vector, vector, ArrayList, vector] 所有元素-numberlists:[2, 5, 1, 3] =============删============= 成功删除指定元素个数-stringlists:2 删除指定元素之后-stringlists:[HashList, SerialList, MapList, LinkedList, ArrayList, vector] 删除下标0-3区间之外的元素:OK 删除指定区间之外元素后-stringlists:[HashList, SerialList, MapList, LinkedList] 出栈元素:HashList 元素出栈后-stringlists:[SerialList, MapList, LinkedList] =============改============= 下标为0的值修改后-stringlists:[hello list!, MapList, LinkedList] =============查============= 长度-stringlists:3 长度-numberlists:4 返回排序后的结果-stringlists:[LinkedList, MapList, hello list!] 返回排序后的结果-numberlists:[1, 2, 3, 5] 子串-第二个开始到结束:[MapList, LinkedList] 获取下标为2的元素:LinkedList #### 2.5 Set功能 ```java private void SetOperate() { System.out.println("======================set=========================="); // 清空数据 System.out.println("清空库中所有数据:"+jedis.flushDB()); System.out.println("=============增============="); System.out.println("向sets集合中加入元素element001:"+jedis.sadd("sets", "element001")); System.out.println("向sets集合中加入元素element002:"+jedis.sadd("sets", "element002")); System.out.println("向sets集合中加入元素element003:"+jedis.sadd("sets", "element003")); System.out.println("向sets集合中加入元素element004:"+jedis.sadd("sets", "element004")); System.out.println("查看sets集合中的所有元素:"+jedis.smembers("sets")); System.out.println(); System.out.println("=============删============="); System.out.println("集合sets中删除元素element003:"+jedis.srem("sets", "element003")); System.out.println("查看sets集合中的所有元素:"+jedis.smembers("sets")); /*System.out.println("sets集合中任意位置的元素出栈:"+jedis.spop("sets"));//注:出栈元素位置居然不定?--无实际意义 System.out.println("查看sets集合中的所有元素:"+jedis.smembers("sets"));*/ System.out.println(); System.out.println("=============改============="); System.out.println(); System.out.println("=============查============="); System.out.println("判断element001是否在集合sets中:"+jedis.sismember("sets", "element001")); System.out.println("循环查询获取sets中的每个元素:"); Set set = jedis.smembers("sets"); Iterator it=set.iterator() ; while(it.hasNext()){ Object obj=it.next(); System.out.println(obj); } System.out.println(); System.out.println("=============集合运算============="); System.out.println("sets1中添加元素element001:"+jedis.sadd("sets1", "element001")); System.out.println("sets1中添加元素element002:"+jedis.sadd("sets1", "element002")); System.out.println("sets1中添加元素element003:"+jedis.sadd("sets1", "element003")); System.out.println("sets1中添加元素element002:"+jedis.sadd("sets2", "element002")); System.out.println("sets1中添加元素element003:"+jedis.sadd("sets2", "element003")); System.out.println("sets1中添加元素element004:"+jedis.sadd("sets2", "element004")); System.out.println("查看sets1集合中的所有元素:"+jedis.smembers("sets1")); System.out.println("查看sets2集合中的所有元素:"+jedis.smembers("sets2")); System.out.println("sets1和sets2交集:"+jedis.sinter("sets1", "sets2")); System.out.println("sets1和sets2并集:"+jedis.sunion("sets1", "sets2")); System.out.println("sets1和sets2差集:"+jedis.sdiff("sets1", "sets2"));//差集:set1中有,set2中没有的元素 } ``` 运行结果 ======================set========================== 清空库中所有数据:OK =============增============= 向sets集合中加入元素element001:1 向sets集合中加入元素element002:1 向sets集合中加入元素element003:1 向sets集合中加入元素element004:1 查看sets集合中的所有元素:[element001, element002, element003, element004] =============删============= 集合sets中删除元素element003:1 查看sets集合中的所有元素:[element001, element002, element004] =============改============= =============查============= 判断element001是否在集合sets中:true 循环查询获取sets中的每个元素: element001 element002 element004 =============集合运算============= sets1中添加元素element001:1 sets1中添加元素element002:1 sets1中添加元素element003:1 sets1中添加元素element002:1 sets1中添加元素element003:1 sets1中添加元素element004:1 查看sets1集合中的所有元素:[element001, element002, element003] 查看sets2集合中的所有元素:[element002, element003, element004] sets1和sets2交集:[element002, element003] sets1和sets2并集:[element001, element002, element003, element004] sets1和sets2差集:[element001] #### 2.6SortedSet功能(有序集合) ```java private void SortedSetOperate() { System.out.println("======================zset=========================="); // 清空数据 System.out.println(jedis.flushDB()); System.out.println("=============增============="); System.out.println("zset中添加元素element001:"+shardedJedis.zadd("zset", 7.0, "element001")); System.out.println("zset中添加元素element002:"+shardedJedis.zadd("zset", 8.0, "element002")); System.out.println("zset中添加元素element003:"+shardedJedis.zadd("zset", 2.0, "element003")); System.out.println("zset中添加元素element004:"+shardedJedis.zadd("zset", 3.0, "element004")); System.out.println("zset集合中的所有元素:"+shardedJedis.zrange("zset", 0, -1));//按照权重值排序 System.out.println(); System.out.println("=============删============="); System.out.println("zset中删除元素element002:"+shardedJedis.zrem("zset", "element002")); System.out.println("zset集合中的所有元素:"+shardedJedis.zrange("zset", 0, -1)); System.out.println(); System.out.println("=============改============="); System.out.println(); System.out.println("=============查============="); System.out.println("统计zset集合中的元素中个数:"+shardedJedis.zcard("zset")); System.out.println("统计zset集合中权重某个范围内(1.0——5.0),元素的个数:"+shardedJedis.zcount("zset", 1.0, 5.0)); System.out.println("查看zset集合中element004的权重:"+shardedJedis.zscore("zset", "element004")); System.out.println("查看下标1到2范围内的元素值:"+shardedJedis.zrange("zset", 1, 2)); } ``` 运行结果: ======================zset========================== OK =============增============= zset中添加元素element001:1 zset中添加元素element002:1 zset中添加元素element003:1 zset中添加元素element004:1 zset集合中的所有元素:[element003, element004, element001, element002] =============删============= zset中删除元素element002:1 zset集合中的所有元素:[element003, element004, element001] =============改============= =============查============= 统计zset集合中的元素中个数:3 统计zset集合中权重某个范围内(1.0——5.0),元素的个数:2 查看zset集合中element004的权重:3.0 查看下标1到2范围内的元素值:[element004, element001] #### 2.7 哈希散列 ```java private void HashOperate() { System.out.println("======================hash=========================="); //清空数据 System.out.println(jedis.flushDB()); System.out.println("=============增============="); System.out.println("hashs中添加key001和value001键值对:"+shardedJedis.hset("hashs", "key001", "value001")); System.out.println("hashs中添加key002和value002键值对:"+shardedJedis.hset("hashs", "key002", "value002")); System.out.println("hashs中添加key003和value003键值对:"+shardedJedis.hset("hashs", "key003", "value003")); System.out.println("新增key004和4的整型键值对:"+shardedJedis.hincrBy("hashs", "key004", 4l)); System.out.println("hashs中的所有值:"+shardedJedis.hvals("hashs")); System.out.println(); System.out.println("=============删============="); System.out.println("hashs中删除key002键值对:"+shardedJedis.hdel("hashs", "key002")); System.out.println("hashs中的所有值:"+shardedJedis.hvals("hashs")); System.out.println(); System.out.println("=============改============="); System.out.println("key004整型键值的值增加100:"+shardedJedis.hincrBy("hashs", "key004", 100l)); System.out.println("hashs中的所有值:"+shardedJedis.hvals("hashs")); System.out.println(); System.out.println("=============查============="); System.out.println("判断key003是否存在:"+shardedJedis.hexists("hashs", "key003")); System.out.println("获取key004对应的值:"+shardedJedis.hget("hashs", "key004")); System.out.println("批量获取key001和key003对应的值:"+shardedJedis.hmget("hashs", "key001", "key003")); System.out.println("获取hashs中所有的key:"+shardedJedis.hkeys("hashs")); System.out.println("获取hashs中所有的value:"+shardedJedis.hvals("hashs")); System.out.println(); } ``` 运行结果: ======================hash========================== OK =============增============= hashs中添加key001和value001键值对:1 hashs中添加key002和value002键值对:1 hashs中添加key003和value003键值对:1 新增key004和4的整型键值对:4 hashs中的所有值:[value001, value002, value003, 4] =============删============= hashs中删除key002键值对:1 hashs中的所有值:[value001, value003, 4] =============改============= key004整型键值的值增加100:104 hashs中的所有值:[value001, value003, 104] =============查============= 判断key003是否存在:true 获取key004对应的值:104 批量获取key001和key003对应的值:[value001, value003] 获取hashs中所有的key:[key004, key003, key001] 获取hashs中所有的value:[value001, value003, 104]
';

redis安装及基础知识

最后更新于:2022-04-01 21:49:53

## 1. Redis简介 Redis:REmote DIctionary Server(远程字典服务器) ### 1.1 Redis是什么? 是完全开源免费的,用C语言编写的,遵守BSD协议, 是一个高性能的(key/value)分布式内存数据库,基于内存运行 并支持持久化的NoSQL数据库,是当前最热门的NoSql数据库之一 ,也被人们称为数据结构服务器 Redis支持数据的备份,即master-slave模式的数据备份 Redis不仅仅支持简单的key-value类型的数据,同时还提供list,set,zset,hash等数据结构的存储 Redis支持数据的持久化,可以将内存中的数据保持在磁盘中,重启的时候可以再次加载进行使用 ### 1.2 能做什么? 定时器、计数器 发布、订阅消息系统(消息中间件) 模拟类似于HttpSession这种需要设定过期时间的功能 取最新N个数据的操作,如:可以将最新的10条评论的ID放在Redis的List里面 内存存储和持久化:redis支持异步将内存中的数据写到硬盘上,同时不影响继续服务 ### 1.3 官方网站 * 官网 http://redis.io/download * 中文网: http://www.redis.net.cn/ http://www.redis.cn/ ## 2.Redis安装 ### 2.1 安装过程 * 安装环境 ubuntu14.04 server ``` wget http://download.redis.io/releases/redis-3.2.5.tar.gz tar -xzvf ./redis-3.2.5.tar.gz cd redis-3.2.5/ ~/redis-3.2.5$ sudo apt-get install gcc ~/redis-3.2.5$ sudo apt-get install make ~/redis-3.2.5$ make MALLOC=libc ``` * 将可执行文件导入/usr/local/bin 目录 ``` ~/redis-3.2.5$ sudo make install cd src && make install make[1]: 正在进入目录 `/home/hanxt/redis-3.2.5/src' Hint: It's a good idea to run 'make test' ;) INSTALL install INSTALL install INSTALL install INSTALL install INSTALL install make[1]:正在离开目录 `/home/hanxt/redis-3.2.5/src' ``` 在执行了make install之后,查看默认安装目录:/usr/local/bin,包含如下文件: 1. redis-benchmark:性能测试工具,可以在自己本子运行,看看自己本子性能如何 服务启动起来后执行 2. redis-check-aof:修复有问题的AOF文件 3. redis-check-dump:修复有问题的dump.rdb文件 4. redis-cli:客户端,操作入口 5. redis-sentinel:redis集群使用 6. redis-server:Redis服务器启动命令 * 修改redis.conf的配置 ``` bind 192.168.1.152 127.0.0.1 #绑定ip,原始只有127.0.0.1 port 6379 daemonize yes #是否常驻进程运行,原始是no dir /home/aexit1/data/redis/6379 #数据文件持久化存储路径,原始是./ logfile /var/log/redis/redis.log requirepass <改为你自己的登陆密码> ``` * linux系统下面redis常用初始化脚本 ``` #!/bin/bash REDIS_PORT=6379 REDIS_ETC=/etc/redis REDIS_INITD=/etc/init.d sudo mkdir $REDIS_ETC sudo mkdir -p ~/data/redis/$REDIS_PORT sudo mkdir -p /var/log/redis sudo cp ./redis-3.2.5/utils/redis_init_script ${REDIS_INITD}/redis_${REDIS_PORT} sudo cp ./redis-3.2.5/redis.conf ${REDIS_ETC}/${REDIS_PORT}.conf ``` * 执行启动命令,守护进程 ``` sudo /etc/init.d/redis_6379 start (还可以stop) #redis-server ./redis.conf (这种启动方式,不是生产规范) ``` * 测试一下基础命令: ``` redis-cli -h 192.168.1.152 -p 6379 redis> set foo bar OK redis> get foo "bar" ``` ### 2.2.安装之后需要知道的基础知识 * redis是单进程工作,利用IO多路复用技术 * 默认16个数据库,类似数组下表从零开始,初始默认使用零号库,使用select命令切换数据库,如select 2 * Redis索引都是从零开始 * redis初始没有密码,可以使用统一密码管理,16个库都是同样密码,要么都OK要么一个也连接不上 * 默认端口是6379 ## 4.Redis常用命令 > 更多命令请参考 http://redisdoc.com/ ## 5.操作系统内核参数优化 ### Redis的启动过程日志 > 可以看到启动过程中,有许多的警告 ``` 22034:M 28 Nov 11:21:07.123 * Increased maximum number of open files to 10032 (it was originally set to 1024). _._ _.-``__ ''-._ _.-`` `. `_. ''-._ Redis 3.2.5 (00000000/0) 64 bit .-`` .-```. ```\/ _.,_ ''-._ ( ' , .-` | `, ) Running in standalone mode |`-._`-...-` __...-.``-._|'` _.-'| Port: 6379 | `-._ `._ / _.-' | PID: 22034 `-._ `-._ `-./ _.-' _.-' |`-._`-._ `-.__.-' _.-'_.-'| | `-._`-._ _.-'_.-' | http://redis.io `-._ `-._`-.__.-'_.-' _.-' |`-._`-._ `-.__.-' _.-'_.-'| | `-._`-._ _.-'_.-' | `-._ `-._`-.__.-'_.-' _.-' `-._ `-.__.-' _.-' `-._ _.-' `-.__.-' 22034:M 28 Nov 11:21:07.124 # WARNING: The TCP backlog setting of 511 cannot be enforced because /proc/sys/net/core/somaxconn is set to the lower value of 128. 22034:M 28 Nov 11:21:07.125 # Server started, Redis version 3.2.5 22034:M 28 Nov 11:21:07.125 # WARNING overcommit_memory is set to 0! Background save may fail under low memory condition. To fix this issue add 'vm.overcommit_memory = 1' to /etc/sysctl.conf and then reboot or run the command 'sysctl vm.overcommit_memory=1' for this to take effect. 22034:M 28 Nov 11:21:07.125 # WARNING you have Transparent Huge Pages (THP) support enabled in your kernel. This will create latency and memory usage issues with Redis. To fix this issue run the command 'echo never > /sys/kernel/mm/transparent_hugepage/enabled' as root, and add it to your /etc/rc.local in order to retain the setting after a reboot. Redis must be restarted after THP is disabled. 22034:M 28 Nov 11:21:07.125 * DB loaded from disk: 0.000 seconds 22034:M 28 Nov 11:21:07.125 * The server is now ready to accept connections on port 6379 ```
';

redis

最后更新于:2022-04-01 21:49:51

';

mysql性能

最后更新于:2022-04-01 21:49:49

操作系统内核参数 /etc/sysctl.conf ![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2bfc29ba4398806046e6396985251655_487x207.png) ![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/d602d3b79f65440d06a73e091ff8d440_523x157.png) 加快tcp链接回収速度,
';

MySQL 5.6 replicate原理与实践

最后更新于:2022-04-01 21:49:47

MySQL作为常用生产数据库中的一种,容灾及高可用方面设计是必须考虑的。而MySQL replicate是容灾、负载均衡、读写分离、高可用性等技术的基础,不仅使用广泛且易于扩展,是一项成熟的技术。成功的商业案例如:replicate+MHA;replicate+corbar;replicate+MMM等。 ## 1.MySQL replicate复制常见用途: * 数据容灾:复制是备份的扩展方案,但不能直接取代备份; * 负载均衡:通过复制可以将读操作分布到多个服务器上; * 高可用性:能避免单点故障,一个包含复制的良好设计系统能够显著缩短宕机时间; * MySQL升级:复制也是作为常用升级测试的解决方案。 ## 2.MySQL replicate复制原理: ![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/66a6c508c1adbf4e7c1ac972b5872931_554x352.png) * 在每次准备提交事务完成数据更新前,主库将数据更新的事件记录到二进制日志中。MySQL会按事务提交的顺序而非每条语句执行的顺序来记录二进制日志。在记录二进制日志后,主库会告诉存储引擎可以提交事务了。 * 备库将主库的二进制日志复制到本地中继日志中。备库会启动一个IO线程,IO线程跟主库建立一个普通的客户端连接,然后再主库上启动一个特殊的二进制(binlog dump)线程,这个二进制转储线程会读取主库上二进制日志中的事件。它不会对事件进行轮询。如果该线程追赶上了主库,它将进入睡眠状态,知道主库发送信号通知其有新的事件产生时才会被唤醒,备库IO线程会将接受到的事件记录到中继日志中。 * 备库启动SQL线程从中继日志中读取事件并在备库执行,从而实现备库数据的更新。当SQL线程追赶上IO线程时,中继日志通常已经在系统缓存中,所以中继日志开销很低。SQL线程执行的事件也可以通过配置选项来决定是否写入其自己的二进制日志中。 ## 3.MySQL replicate实现方式 * 环境 主库IP:192.168.1.75 从库IP:192.168.1.76 ### 3.1 追加主库my.cnf配置文件 ``` [mysqld] # 要体现出server的唯一性 server-id = 75 # 启用二进制日志 log_bin = /var/log/mysql/mysql-bin.log # 磁盘刷新指令.1表示每次写入时都将binlog与硬盘同步,影响效率 sync_binlog=1 # binlog日志格式,mysql默认采用statement,建议使用mixed binlog_format=mixed ``` 修改完成之后,重新启动主数据库 `service mysql restart` ### 3.2 在主库创建同步账号 ``` GRANT REPLICATION SLAVE ON *.* TO 'repl'@'192.168.1.76' IDENTIFIED BY 'repl'; flush privileges; ``` >注意:IP是从库所在的IP地址 ## 3.2 修改从库my.cnf配置文件 ``` [mysqld] # 服务器唯一ID,默认是1,一般取IP最后一段 server-id=76 # 启用二进制日志 log-bin=/var/log/mysql/mysql-bin.log ``` 修改完成之后,重新启动从数据库 ### 3.3 主库锁表备份 * 锁表 ``` #mysql -uroot -p mysql>FLUSH TABLES WITH READ LOCK; mysql> SHOW MASTER STATUS; +------------------+----------+--------------+------------------+-------------------+ | File | Position | Binlog_Do_DB | Binlog_Ignore_DB | Executed_Gtid_Set | +------------------+----------+--------------+------------------+-------------------+ | mysql-bin.000002 | 120 | | | | +------------------+----------+--------------+------------------+-------------------+ 1 row in set (0.00 sec) ``` 二进制日志文件是mysql-bin.000002,位置是120,记录下这两个值,稍后要用到。 * 备份主库数据 ``` # mysqldump -uroot -p -h127.0.0.1 -P3306 --all-databases --triggers --routines --events > ./all.sql ``` * 解锁主库锁表 ``` mysql>UNLOCK TABLES; ``` ### 3.4 从库导入快照 * 导入主库备份的数据 ``` mysql -uroot -p -h127.0.0.1 -P3306 < ./all.sql ``` * 开启从库同步 ``` #mysql -uroot -p mysql> CHANGE MASTER TO MASTER_HOST='192.168.1.75',MASTER_USER='repl',MASTER_PASSWORD='repl',MASTER_LOG_FILE='mysql-bin.000002',MASTER_LOG_POS=120; #然后启动从数据库的复制线程: mysql>START slave; # 接着查询数据库的slave状态: mysql> SHOW slave STATUS #如果下面两个参数都是Yes(不能是Running),则说明主从配置成功! Slave_IO_Running:Yes Slave_SQL_Running:Yes ``` > 注意:MASTER_LOG_FILE,MASTER_LOG_POS修改成主库3.3中的查询结果 ## 参考 http://www.cnblogs.com/kezf/p/mysql-slave.html http://blog.csdn.net/stuartjing/article/details/9719701
';

Ubuntu14.04安装mysql5.6

最后更新于:2022-04-01 21:49:44

## 1.前提 * 此安装方法的前提是,链接互联网 ## 2.安装mysql * 更新源 ``` sudo apt-get update ``` * 安装MySQL-client-core-5.6 ``` sudo apt-get install MySQL-client-core-5.6 ``` * 安装mysql-client-5.6 ``` sudo apt-get install mysql-client-5.6 ``` * 安装mysql-server-5.6 ``` sudo apt-get install mysql-server-5.6 ``` ## 3.检查mysql状态 > 执行完成上面的命令之后,正常情况mysql服务会自启动 * 检查mysql的启动状态 ``` ## 查看进程 ps -ef | grep mysql ## 查看端口监听 netstat -tap | grep mysql ``` * 链接mysql进行状态检查 ``` mysql -h localhost -u root -p Enter password: mysql> status; -------------- mysql Ver 14.14 Distrib 5.6.33, for debian-linux-gnu (x86_64) using EditLine wrapper Connection id: 37 Current database: Current user: root@localhost SSL: Not in use Current pager: stdout Using outfile: '' Using delimiter: ; Server version: 5.6.33-0ubuntu0.14.04.1 (Ubuntu) Protocol version: 10 Connection: Localhost via UNIX socket Server characterset: latin1 Db characterset: latin1 Client characterset: utf8 Conn. characterset: utf8 UNIX socket: /var/run/mysqld/mysqld.sock Uptime: 25 min 27 sec Threads: 1 Questions: 115 Slow queries: 0 Opens: 87 Flush tables: 1 Open tables: 80 Queries per second avg: 0.075 -------------- ``` ## 4.修改必要的配置 sudo vim /etc/mysql/my.cnf * 修改远程访问 ``` # 为了可以远程访问,将[mysqld]下面的这行注释掉 # Instead of skip-networking the default is now to listen only on # localhost which is more compatible and is not less secure. # bind-address = 127.0.0.1 ``` * 修改字符编码,在client,mysqld,mysql配置段,分别追加如下编码信息 ``` [client] default-character-set = utf8 [mysqld] character-set-server=utf8 lower_case_table_names = 1 [mysql] default-character-set=utf8 ``` 再次查看status,变为utf8编码 ``` Server characterset: utf8 Db characterset: utf8 Client characterset: utf8 Conn. characterset: utf8 ``` * 新建数据库和用户 ``` create database `devicedb` default character set utf8 collate utf8_general_ci; CREATE USER 'device'@'%' IDENTIFIED BY '4rfv$RFV'; GRANT ALL ON devicedb.* TO 'device'@'%' IDENTIFIED BY '4rfv$RFV'; //只有执行了这一句才可以登陆 FLUSH PRIVILEGES; ``` ## 5.卸载旧版本 ``` #停止mysql的服务 sudo service mysql stop #删除mysql的数据文件(必要的情况下) sudo rm /var/lib/mysql/ -R #删除mqsql的配置文件 sudo rm /etc/mysql/ -R #删除mysql软件 sudo apt-get autoremove mysql* --purge ```
';

mysql

最后更新于:2022-04-01 21:49:42

';

进阶篇

最后更新于:2022-04-01 21:49:40

## linux高并发或大数据应用内核参数的优化 http://www.cnblogs.com/zengkefu/p/5635081.html http://www.jianshu.com/p/9a8e383b5b49
';

基础篇

最后更新于:2022-04-01 21:49:38

## 1.基础一 ### 1.1 获取操作系统的基本信息 ``` $ uname -a Linux myself 4.2.0-27-generic #32~14.04.1-Ubuntu SMP Fri Jan 22 15:32:26 UTC 2016 x86_64 x86_64 x86_64 GNU/Linux ``` 上面的命令输出,有以下的信息 * Linux 14.04.1-Ubuntu 为操作系统版本 * linux 的内核版本为4.2.0-27-generic * x86_64说明是64位的操作系统 * 当前用户名是myself ### 1.2 新安装的ubuntu操作系统root密码 >新安装的ubuntu操作系统,在安装过程中建立了一个新的用户,如:hanxt。没有为root用户设置密码的步骤。 所以root密码是随机变化的。如果需要利用root用户,需要用hanxt用户登录,为root用户修改密码 ``` hanxt@ubuntu1:~$ sudo passwd [sudo] password for hanxt: (此处输入hanxt的密码) Enter new UNIX password: (此处输入root密码) Retype new UNIX password: (再次输入root密码) passwd: password updated successfully ``` ### 1.3 crontab服务使用 * 查看crontab服务是否启用,该命令ubuntu亲测可用 ``` service cron status ``` * crontab -e,每隔5分钟与ntp授时服务器同步时间,需要root用户(su root) ``` */5 * * * * /usr/sbin/ntpdate cn.pool.ntp.org > /home/aexit1/ntpdate.log ``` ``` touch /home/aexit1/ntpdate.log chown root:root /home/aexit1/ntpdate.log ``` ### 1.4 修改系统主机名 1.修改hostname为driver1 ``` hostname driver1 ``` 2.修改/etc/hosts ``` 192.168.1.32 driver1 ``` 重新登陆,hanxt@driver1:~$
';

linux运维

最后更新于:2022-04-01 21:49:35

';

煮饺子与mesos之间妙不可言的关系

最后更新于:2022-04-01 21:49:33

* 这篇原文文章转载自:http://www.aiweibang.com/yuedu/69570354.html. * 原文作者:小黑羊JoinWings 一直想写一篇介绍mesos的文章,文章可以不讲技术细节,但一定可以让一个外行一下子就明白什么是mesos.mesos能干什么. 很荣幸看到了,这样的一篇文章《煮饺子与mesos之间妙不可言的关系》,佩服的五体投地!此乃神人也!我觉得怎么样也不会比他写的更好了! 废话不多说!将原文奉上,原文网址是http://www.aiweibang.com/yuedu/69570354.html. 可惜不知道是因为什么,原文响应的速度非常慢.一字不改,原文转载!致敬小黑羊! # 煮饺子与mesos之间妙不可言的关系 你知道大型的饺子馆是怎么煮饺子的吗? ![](http://7xqnvr.com1.z0.glb.clouddn.com/%E7%85%AE%E9%A5%BA%E5%AD%90%E4%B8%8Emesos%E4%B9%8B%E9%97%B4%E5%A6%99%E4%B8%8D%E5%8F%AF%E8%A8%80%E7%9A%84%E5%85%B3%E7%B3%BB1.png) 是这样煮吗? ↓ ![](http://7xqnvr.com1.z0.glb.clouddn.com/%E7%85%AE%E9%A5%BA%E5%AD%90%E4%B8%8Emesos%E4%B9%8B%E9%97%B4%E5%A6%99%E4%B8%8D%E5%8F%AF%E8%A8%80%E7%9A%84%E5%85%B3%E7%B3%BB2.png) 那你要让顾客喝西北风了! 难道是很多个这种锅一起煮吗? ↓ ![](http://7xqnvr.com1.z0.glb.clouddn.com/%E7%85%AE%E9%A5%BA%E5%AD%90%E4%B8%8Emesos%E4%B9%8B%E9%97%B4%E5%A6%99%E4%B8%8D%E5%8F%AF%E8%A8%80%E7%9A%84%E5%85%B3%E7%B3%BB3.png) 呵呵,不是,你想赔死吗? 其实 他们用了一种神器 ↓ ![](http://7xqnvr.com1.z0.glb.clouddn.com/%E7%85%AE%E9%A5%BA%E5%AD%90%E4%B8%8Emesos%E4%B9%8B%E9%97%B4%E5%A6%99%E4%B8%8D%E5%8F%AF%E8%A8%80%E7%9A%84%E5%85%B3%E7%B3%BB4.png) 为什么会酱紫? 我们来分析一下大型饺子馆面对的需求 ↓ ![](http://7xqnvr.com1.z0.glb.clouddn.com/%E7%85%AE%E9%A5%BA%E5%AD%90%E4%B8%8Emesos%E4%B9%8B%E9%97%B4%E5%A6%99%E4%B8%8D%E5%8F%AF%E8%A8%80%E7%9A%84%E5%85%B3%E7%B3%BB5.png) ![](http://7xqnvr.com1.z0.glb.clouddn.com/%E7%85%AE%E9%A5%BA%E5%AD%90%E4%B8%8Emesos%E4%B9%8B%E9%97%B4%E5%A6%99%E4%B8%8D%E5%8F%AF%E8%A8%80%E7%9A%84%E5%85%B3%E7%B3%BB6.png) ![](http://7xqnvr.com1.z0.glb.clouddn.com/%E7%85%AE%E9%A5%BA%E5%AD%90%E4%B8%8Emesos%E4%B9%8B%E9%97%B4%E5%A6%99%E4%B8%8D%E5%8F%AF%E8%A8%80%E7%9A%84%E5%85%B3%E7%B3%BB7.png) 这种情况下 不像我们自己在家吃饺子 不可能把所有的饺子放在一个大锅里面煮 道理你懂的…… 但是,难道要每个订单一个锅来煮? 太浪费资源了吧 所以就有人发明了这种支持“虚拟化”锅 ![](http://7xqnvr.com1.z0.glb.clouddn.com/%E7%85%AE%E9%A5%BA%E5%AD%90%E4%B8%8Emesos%E4%B9%8B%E9%97%B4%E5%A6%99%E4%B8%8D%E5%8F%AF%E8%A8%80%E7%9A%84%E5%85%B3%E7%B3%BB8.png) 我们把煮水饺看成不同的任务 ↓ ![](http://7xqnvr.com1.z0.glb.clouddn.com/%E7%85%AE%E9%A5%BA%E5%AD%90%E4%B8%8Emesos%E4%B9%8B%E9%97%B4%E5%A6%99%E4%B8%8D%E5%8F%AF%E8%A8%80%E7%9A%84%E5%85%B3%E7%B3%BB9.png) 水饺锅里面不同的格子执行不同的任务 格子并不绝对封闭,汤是互通的 这样 一锅就可以满足很多客人了 如果客人再多了怎么办? 多来几只饺子锅组成集群! ↓ ![](http://7xqnvr.com1.z0.glb.clouddn.com/%E7%85%AE%E9%A5%BA%E5%AD%90%E4%B8%8Emesos%E4%B9%8B%E9%97%B4%E5%A6%99%E4%B8%8D%E5%8F%AF%E8%A8%80%E7%9A%84%E5%85%B3%E7%B3%BB10.png) 好强大的煮饺子集群 不管有多少客人 不管有多复杂的煮饺子组合 都可以在集群里面完成了 好拉轰啊 所有的格子都被池化 大厨根据点菜需求 灵活分配不同的格子来完成任务 任何一个格子都可以用来煮任何一种水饺 有同学问 客人还想吃煎饺或者蒸饺怎么办? 不要紧,更奇葩的锅已经有人发明了 ↓ ![](http://7xqnvr.com1.z0.glb.clouddn.com/%E7%85%AE%E9%A5%BA%E5%AD%90%E4%B8%8Emesos%E4%B9%8B%E9%97%B4%E5%A6%99%E4%B8%8D%E5%8F%AF%E8%A8%80%E7%9A%84%E5%85%B3%E7%B3%BB11.png) 不光可以煮,同时还可以煎和蒸 尼玛,黑科技真发达 “煮饺子集群”很牛逼 分配任务、具体操作的大厨更不简单 观摩一下他们是如何工作的吧 ↓ ![](http://7xqnvr.com1.z0.glb.clouddn.com/%E7%85%AE%E9%A5%BA%E5%AD%90%E4%B8%8Emesos%E4%B9%8B%E9%97%B4%E5%A6%99%E4%B8%8D%E5%8F%AF%E8%A8%80%E7%9A%84%E5%85%B3%E7%B3%BB12.gif) 好了,煮饺子的事情讲完了 这跟MESOS有毛线关系? ↓ ![](http://7xqnvr.com1.z0.glb.clouddn.com/%E7%85%AE%E9%A5%BA%E5%AD%90%E4%B8%8Emesos%E4%B9%8B%E9%97%B4%E5%A6%99%E4%B8%8D%E5%8F%AF%E8%A8%80%E7%9A%84%E5%85%B3%E7%B3%BB13.png) Mesos是Apache下的开源分布式资源管理框架,它被称为是分布式系统的内核。 小黑羊观点>> Mesos跟我们前面介绍的“煮饺子”体系有异曲同工之妙。 ↓ ↓ 数据中心有很多计算任务需要执行,比如Hadoop任务(饺子A)、MPI任务(饺子B)、Storm任务(饺子C)、Spark任务(饺子D)。 但是要想灵活的部署这些任务,达到呼之则来、挥之则去的效果,却没辣么简单。 人们都希望动态、灵活的利用数据中心的计算资源,更细粒度的分发任务,这就需要一套资源管理和调度框架来达到目的。 最早的时候,Google用自己开发的Borg来做这些事,可那是人家“私营饺子馆”专利,不可能随便拿来用的。 ![](http://7xqnvr.com1.z0.glb.clouddn.com/%E7%85%AE%E9%A5%BA%E5%AD%90%E4%B8%8Emesos%E4%B9%8B%E9%97%B4%E5%A6%99%E4%B8%8D%E5%8F%AF%E8%A8%80%E7%9A%84%E5%85%B3%E7%B3%BB14.png) 后来,就有活雷锋开发了Mesos来做和Borg一样的事儿,而且还开源了。 ↓ ![](http://7xqnvr.com1.z0.glb.clouddn.com/%E7%85%AE%E9%A5%BA%E5%AD%90%E4%B8%8Emesos%E4%B9%8B%E9%97%B4%E5%A6%99%E4%B8%8D%E5%8F%AF%E8%A8%80%E7%9A%84%E5%85%B3%E7%B3%BB15.png) 一张图看懂Mesos和煮饺子的关系 Mesos的角色分工与两层调度体系: * Mesos master相当于煮水饺的厨师长,当然有备份的副厨师长做替补(Standby master),他们之间的出勤制度由酒店老板负责监督(ZooKeeper)。 * Mesos slave驻留在每个饺子锅上(可认为是负责值守每个锅的厨工),负责具体任务分发。 * 具体的计算任务被按需分配在“锅集群”中执行,比如Hadoop、MPI、Spark等等,他们相当于不同的煮饺子需求。 * 每个“锅”提供一定的资源隔离能力,饺子锅用的是隔离网,Mesos方案目前比较流行采用Docker来进行隔离。 * 针对每种口味的饺子,都有点饺子、包饺子、煮饺子、上饺子的工作调度流程,被称为“Framework”,mesos为了可以适应各品种饺子的“FrameWork”流程 ![](http://7xqnvr.com1.z0.glb.clouddn.com/%E7%85%AE%E9%A5%BA%E5%AD%90%E4%B8%8Emesos%E4%B9%8B%E9%97%B4%E5%A6%99%E4%B8%8D%E5%8F%AF%E8%A8%80%E7%9A%84%E5%85%B3%E7%B3%BB16.png) 饺子菜单——Mesos可以配合调用的“FrameWork” MESOS的“煮饺子”任务调度过程: ① Slave 1(煮饺子师傅)向Master(厨师长)汇报其管理的饺子锅的空闲资源:4个CPU、4GB内存。 ② Master(厨师长)检查Framework1(猪肉韭菜)目前的订单需求。 ③ Framework1(猪肉韭菜)的当前订单需求是:煮两份饺子,分别是4两和3两,4两的需要用<2 CPUs, 1 GB RAM>的资源,3两的需要用<1 CPUs, 2 GB RAM>资源。 ④ 最后,Master(厨师长)向Slave(煮饺子师傅)下发任务,开工! ![](http://7xqnvr.com1.z0.glb.clouddn.com/%E7%85%AE%E9%A5%BA%E5%AD%90%E4%B8%8Emesos%E4%B9%8B%E9%97%B4%E5%A6%99%E4%B8%8D%E5%8F%AF%E8%A8%80%E7%9A%84%E5%85%B3%E7%B3%BB17.png) MESOS的工作流程 这个“煮饺子”的Mesos系统什么好处呢? ![](http://7xqnvr.com1.z0.glb.clouddn.com/%E7%85%AE%E9%A5%BA%E5%AD%90%E4%B8%8Emesos%E4%B9%8B%E9%97%B4%E5%A6%99%E4%B8%8D%E5%8F%AF%E8%A8%80%E7%9A%84%E5%85%B3%E7%B3%BB18.png) Mesos管理的是计算集群,调度的是任务,是当下最为流行的“数据中心操作系统”。它把集群资源池化,按需动态调配,提高资源使用效率和敏捷性,Mesos能让资源利用率提升5倍以上。 与容器技术结合,可以轻松部署海量计算资源和微服务,让上层业务的可伸缩性得到极大提高,Mesos可在数十秒内完成上万Docker容器部署。 模块化、插件式架构,轻松耦合多种Framework,这使得Mesos在其所支持的宽泛领域中,业务迅速增长。 专业人士认为,Mesos具备问鼎数据中心资源管理首选平台的能力。 ![](http://7xqnvr.com1.z0.glb.clouddn.com/%E7%85%AE%E9%A5%BA%E5%AD%90%E4%B8%8Emesos%E4%B9%8B%E9%97%B4%E5%A6%99%E4%B8%8D%E5%8F%AF%E8%A8%80%E7%9A%84%E5%85%B3%E7%B3%BB19.png) 目前Twitter、Airbnb、eBay、Netflix包括Apple家的Siri,都用了Mesos来“煮饺子”。 国内豆瓣是最早用Mesos的,去哪儿网、爱奇艺、小米、360也都在尝试Mesos。 爱奇艺煮饺子大师傅杨成伟语: “爱奇艺是国内互联网企业试用 Mesos 的先行者,最早将 Mesos 用于分布式转码服务,已经达到了800个节点。集群资源利用率已经高于40%,峰值时甚至超过了90%。” 而著名的创业公司Mesosphere是负责提供“煮饺子”解决方案的,跟他们类似的还有国内的初创公司数人科技。
';

博客文章集

最后更新于:2022-04-01 21:49:31

';

监控

最后更新于:2022-04-01 21:49:29

## Mesos 可观测指标 本文档描述了由mesos master和agent 节点提供的可观测指标.同时提供了一些关于发现集群指标异常方面的指导. ### 概述 Mesos master 和 agent 节点报告了一系列的统计指标,这样你可以监控资源使用量和及早的探测到异常情况.这些信息是由mesos报告,细节包括可用资源,已使用资源,注册应用框架,活动状态的agent,和任务状态.你可以使用这些信息创建自动告警和在监控仪表盘中绘制不同时间指标图形. ### 指标类型 Mesos 提供了2种不同的指标: counters(计数值) and gauges(计量值). **计数值**:是对零散事件的跟踪,并且是**单调递增的**.该指标值通常是自然数.比如:失败任务的次数,已注册的agent的数量。对于这种类型的指标,变化率比数值本身更有用处. **计量值**:代表了一个瞬时的度量采样值.比如:集群已使用的内存,在线的agent的数量.对于这种类型的一些指标,通常被用于在一段时间内监控该值是在一个阀值之上还是之下. 本文档中表格中说明了每一个指标的类型 ### Master 节点 每一个master节点的指标可以访问http端点 [/metrics/snapshot](http://mesos.apache.org/documentation/latest/endpoints/metrics/snapshot/)获取.响应内容为一个包含了键值对指标的JSON对象. #### 可观测指标 这章节列出所有的Mesos master节点的分类可用指标. ##### **资源类别** 下面的指标提供了集群所有可用资源的信息和当前使用量.持续的资源使用量高可能说明你需要增加集群的资源能力或者某一个应用框架异常. > 译者注:已被分配的可回收的:是指该资源被分配给某个容器或任务,但是没有被使用,可以被回收的部分. |指标|描述|类型| |--|--|--| |master/cpus_percent| 已分配的CPU的比例| 计量值| |master/cpus_used |已分配的CPU的数量| 计量值| |master/cpus_total |CPU的总数 |计量值| |master/cpus_revocable_percent| 已分配但可回收的CPU的比例| 计量值| |master/cpus_revocable_total| 可回收的CPU的总量| 计量值| |master/cpus_revocable_used|已分配但可回收的CPU的数量| 计量值| |master/disk_percent|已分配的磁盘空间比例| 计量值| |master/disk_used|已分配的磁盘空间(MB) |计量值| |master/disk_total| 磁盘空间总量(MB)| 计量值| |master/disk_revocable_percent |已分配但可回收的磁盘空间比例|计量值| |master/disk_revocable_total| 可回收的磁盘空间总量(MB)| 计量值| |master/disk_revocable_used |已分配但可回收的磁盘空间(MB)| 计量值| |master/gpus_percent |已经被分配的GPU比例| 计量值| |master/gpus_used| 已被分配的GPU数量 |计量值| |master/gpus_total| GPU总数| 计量值| |master/gpus_revocable_percent| 已被分配的可回收GPU比例| 计量值| |master/gpus_revocable_total| 可被回收的GPU总量| 计量值| |master/gpus_revocable_used| 已分配但可回收GPU数量| 计量值| |master/mem_percent |已分配的内存的比例 |计量值| |master/mem_used| 已分配的内存(MB)| 计量值| |master/mem_total| 内存总量(MB)| 计量值| |master/mem_revocable_percent|已分配但可回收的内存比例| 计量值| |master/mem_revocable_total|可回收的内存总量(MB) |计量值| |master/mem_revocable_used| 已分配但可回收的内存量(MB)| 计量值|
';

Docker Containerizer

最后更新于:2022-04-01 21:49:26

## Docker Containerizer Mesos 0.20.0 新增了对使用Docker镜像启动任务的支持,同时支持了一部分Docker参数设置,未来计划支持更多! 使用者可以将Docker 镜像作为一个任务 或 一个执行器来启动. 下面的章节将结合对Docker的支持来描述 API的变化,同时说明如何设置Docker. ### 设置 为了让agent使用Docker Containerizer,你必须在启动agent的时候设置containerizers 参数值为docker. 如: `mesos-agent --containerizers=docker,mesos` 每一个具有Docker containerizer的agent 都需要安装Docker 命令行客户端(版本 >= 1.0.0) 如果你在agent允许iptables运行,请确保iptables允许所有的docker 桥接接口的网络流量通过.添加如下规则: ``` iptables -A INPUT -s 172.17.0.0/16 -i docker0 -p tcp -j ACCEPT ``` ### 如何使用 Docker Containerizer? 0.20.0版本之前的TaskInfo,只支持2种方式运行任务:一种是通过设置CommandInfo,在bash命令行运行任务;或者设置ExecutorInfo启动一个自定义的执行器来运行任务. 0.20.0开始在TaskInfo和ExecutorInfo增加了ContainerInfo字段,用来配置类似于Docker的Containerizer去运行任务和执行器. 为了启动一个Docker镜像作为任务运行,需要在TaskInfo 中设置command和container字段,因为Docker Containerizer会使用它们作为辅助命令去启动docker 镜像.ContainerInfo的类型设置为docker,在DockerInfo中说明要被启动的docker镜像. 为了启动一个个Docker镜像作为执行器运行,需要在TaskInfo中设置ExecutorInfo,它包含了一个type为docker的ContainerInfo用来启动执行器.注意一旦Docker 镜像作为mesos 执行器启动,将向agent进行注册. ### Docker Containerizer的作用是什么? Docker Containerizer将任务/执行器的启动与停止请求,转换为Docker命令行. 当任务启动的时候,Docker Containerizer将做如下事情: 1. 将CommandInfo中涉及到的所有文件拉取到sandbox。 2. 从远程仓库拉取Docker 镜像. 3. 使用docker 执行器运行docker 镜像,将sandbox 目录映射到docker 容器,并且设置 MESOS_SANDBOX环境变量的映射目录.执行器将容器日志输出到sandbox中的stdout/stderr文件. 4. 当容器退出或者销毁的时候,停止或删除docker容器. The Docker Containerizer launches all containers with the mesos- prefix plus the agent id (ie: mesos-agent1-abcdefghji), and also assumes all containers with the mesos- prefix is managed by the agent and is free to stop or kill the containers. When launching the docker image as an Executor, the only difference is that it skips launching a command executor but just reaps on the docker container executor pid. Note that we currently default to host networking when running a docker image, to easier support running a docker image as an Executor. The containerizer also supports optional force pulling of the image. It is set disabled as default, so the docker image will only be updated again if it’s not available on the host. To enable force pulling an image, force_pull_image has to be set as true. ### 私有Docker仓库 To run an image from a private repository, one can include the uri pointing to a .dockercfg that contains login information. The .dockercfg file will be pulled into the sandbox the Docker Containerizer set the HOME environment variable pointing to the sandbox so docker cli will automatically pick up the config file. Starting from 0.29, we provide an alternative way to specify docker config file for pulling images from private registries. We allow operators to specify a shared docker config file using an agent flag. This docker config file will be used to pull images from private registries for all containers. See configuration documentation for detail. Operators can either use a local docker config file (need to manually configure .docker/config.json or .dockercfg on each agent), or specify the flag as a JSON-formatted string. For example: `--docker_config=file:///home/vagrant/.docker/config.json` or as a JSON object, ``` --docker_config="{ \ \"auths\": { \ \"https://index.docker.io/v1/\": { \ \"auth\": \"xXxXxXxXxXx=\", \ \"email\": \"username@example.com\" \ } \ } \ }" ``` ### CommandInfo to run Docker images A docker image currently supports having an entrypoint and/or a default command. To run a docker image with the default command (ie: docker run image), the CommandInfo’s value must not be set. If the value is set then it will override the default command. To run a docker image with an entrypoint defined, the CommandInfo’s shell option must be set to false. If shell option is set to true the Docker Containerizer will run the user’s command wrapped with /bin/sh -c which will also become parameters to the image entrypoint. ### Recover Docker containers on agent recovery The Docker containerizer supports recovering Docker containers when the agent restarts, which supports both when the agent is running in a Docker container or not. With the --docker_mesos_image flag enabled, the Docker containerizer assumes the containerizer is running in a container itself and modifies the mechanism it recovers and launches docker containers accordingly.
';

Containerizer

最后更新于:2022-04-01 21:49:24

## Containerizer ### 动机 Containerizer的作用是在容器中运行任务,包含以下几方面: * 将任务与其他运行时任务隔离 * 容器内部任务处在一个运行时资源限制的环境 * 程序控制任务的独立资源 * 在打包的文件系统镜像运行软件,可以运行在不同的环境中 ### Containerizer的类型 mesos与现有的容器技术有很好的兼容性(比如:docker), 并且提供了自己的容器技术.他还支持不同容器技术的组合(比如:docker和mesos) Mwsos 实现了如下的一些containerizer: * [Composing]()(组合) * [Docker]() * [Mesos]() (默认) * External (已废弃) 通过设置agent参数 `--containerizers`,用户可以指定去使用哪种类型的containerizer #### Composing containerizer 这个特性允许多种容器技术混合使用.当你使用agent参数--containerizers 配置逗号分隔的多个独立容器名称的时候,该特性被激活(如:--containerizers=mesos,docker).逗号分隔的列表的顺序很重要,因为第一个containerizer,如果支持某个任务运行的容器配置,将被用于启动该任务. 用例: * 对于不同类型的资源隔离的测试任务。因为mesos 的 containerizers有多种资源隔离的能力,一个应用框架可以使用组合containerizer进行测试,一个任务用mesos containerizer的控制环境,与此同时只需要修改容器参数就可以使用docker容器测试另一个任务. ### Docker containerizer Docker containerizer将任务运行在docker 容器.设置agent参数`--containerizers=docker`就可以使用这种containerizer. 用例: * 如果任务的运行需要一些docker 中的工具包 * 如果 Mesos agent运行于docker 容器中 更多细节, 请查看 [Docker Containerizer](). ### Mesos containerizer 这种containerizer将任务运行在Mesos提供的一组插件式的隔离环境中.这是一个Mesos原生的containerizer解决方案,设置agent参数`--containerizers=mesos`就可以使用它. 用例: * 不需要以来其他的容器技术,Mesos自己来控制任务的运行时环境 * 需要细粒度的操作系统控制(如:linux提供的cgroups/namespaces) * 需要Mesos最新的容器技术特性 * 需要额外的资源控制,比如:其他的容器技术不能提供的磁盘使用量限制 * 需要添加自定义任务的隔离性 更多细节, 请查看 [Mesos Containerizer](). ### References * [Containerizer Internals]() for implementation details of containerizers.
';