Linux之redis服务

redis简介

redis单纯程模型,支持主从模式,提高可用性,是一个开源项目,经常用来当一个数据结构服务器。其是内存级别的缓存服务器并可实现持久化功能. 据称一百万的变量存储(字串)占用100M内存空间,单台redis服务器可达到5万并发的能力。

redis与memcache的对比

redis的优势

  • 支持丰富的操作
  • 主从复制和集群
  • 就地更新操作
  • 支持持久化(磁盘),避免雪崩效应

memcache优势

  • 多线程,善用多核CPU,更少的阻塞操作
  • 更少的内存开销
  • 更少的内存分配压力
  • 可能有更少的内存碎片

redis的组件

  • redis-server
  • redis-cli
  • redis-benchmark
  • redis-check-dump & redis-check-aof

redis的工作端口

  • 6379/TCP

redis-cli命令的参数

  • -h HOST : 连接的主机地址或主机名
  • -p PORT :连接的端口
  • -s socket : 指定套接字
  • -a password : 指定连接密码
  • -r <repeat> : 指定命令运行多次

redis-cli中相关的命令

  • connection相关的命令
    • auth PASS : 认证
    • ping : 测试服务器是否在线
    • echo "string" : 显示string
    • quit : 退出
    • select # : 挑选指定的名称空间(即数据库)
    • help @connection : 获取与连接相关的命令帮助
  • 与服务器端支持的命令
    • help @server : 获取与服务器端相关的命令帮助

    • bgsave : 实现异步将数据集同步到磁盘上

    • client getname : 获取当前客户端的连接名

    • client kill IP:PORT : 指定IP:PORT可关闭相关的连接信息

    • client list : 查看客户端的连接信息

    172.16.36.70:6379> client list id=5 addr=172.16.36.70:57606 fd=8 name= age=904 idle=879 flags=N db=0 sub=0 psub=0 multi=-1 qbuf=0 qbuf-free=0 obl=0 oll=0 omem=0 events=r cmd=get
    ```
    • client setname CONNECTION-NAME: 设定当前连接的名称
    • info : 查看当前服务器的状态信息
    • info memory : 只显示memory段的相关信息
    • config resetstart : 重置info中所统计的数据
    • config set PARAMETER value : 运行时修改设定指定参数的值,只保存在内存中
    • config rewrite : 将修改在内存中的参数值同步到配置文件中
    • config get dir : 查看redis的文件保存目录
    • dbsize : 显示数据库中所有键的数量
    • lastsave : 用来获取最新一次save执行的时间戳
    • save : 保存数据到磁盘
    • monitor : 实时监控所接收到的请求
    • shutdown : 将所有数据从内存同步到磁盘,并安全关闭
    • shutdown [nosave][save] : 关闭程序并选择是否将数据同步到磁盘上
    • salveof HOST PORT :配置主从,当前节点将变成从节点
    • slowlog : 查看慢查询日志,需要开启慢查询日志功能
    • sync : 复制功能的内建命令
  • 与订阅相关的命令
    • help @pubsub : 获取与订阅相关的命令
    • psubscribe : 基于模式进行订阅
    • publish : 向频道发送消息
    • subscribe CHANNEL : 订阅一个频道

redis的认证功能

vim /etc/redis.conf
requirepass zhenping.com

重启redis服务
systemctl restart redis

连接redis并认证的方法
redis-cli -h 172.16.36.70
172.16.36.70:6379> select 0
(error) NOAUTH Authentication required.
172.16.36.70:6379> auth zhenping
OK

redis清空数据库

  • flushdb : 清空当前库

  • flushall : 清空所有库

    [root@Centos7 ~]# redis-cli -h 172.16.36.70
    172.16.36.70:6379> flushall
    OK
    172.16.36.70:6379> flushdb
    OK
    

redis的事务功能

通过multi,exec,watch等命令来实现事务功能,将多个命令打包,多个命令按顺序执行,并一次执行完成,并将执行结果一次性全部返回给客户端。redis事务不支持回滚操作,在事务中应避免发生错误(如命令写错等),事务了也将会执行失败。

  • multi : 启动一个事务
  • exec : 执行事务,一次性将事务中的所有操作执行完成后,返回给客户端
  • watch : 乐观锁机制,在EXEC命令执行之前 ,用于监视指定数据键,如果监视的某任意键数据被修改,服务器拒绝执行事务
####建立一个事务
[root@Centos7 ~]# redis-cli -h 172.16.36.70
172.16.36.70:6379> multi
OK
172.16.36.70:6379> set ip 172.16.36.70
QUEUED
172.16.36.70:6379> get ip
QUEUED
172.16.36.70:6379> set prot 8080
QUEUED
172.16.36.70:6379> exec
1) OK
2) "172.16.36.70"
3) OK

####使用watch机制监控键
[root@Centos7 ~]# redis-cli -h 172.16.36.70
172.16.36.70:6379> watch ip
OK
172.16.36.70:6379> multi
OK
172.16.36.70:6379> set ip 172.16.36.71
QUEUED
172.16.36.70:6379> get ip
QUEUED
172.16.36.70:6379> exec
(nil)

说明: 当watch ip指令发出后,其它的请求操作改变了IP键的值,此时新发起的事务中,如果也要改变其值, 此事务申请将会失败

redis的发布和订阅功能(publish/subscribe)

发布和订阅功能被广泛用于构建即时通信应用,比如:网络聊天室和实时广播、实时提醒等。 订阅发布功能就能帮你很轻松地实现通知、监控程序

  • subsciribe CHANNEL_NAME : 订阅一个频道
  • publish CHANNEL_NAME : 向频道发送一个消息
  • unsubscribe CHANNEL_NAME : 退订频道
  • psubscribe CHANNEL_NAME_PATTERN : 基于正则表达式模式定义多个频道
1、订阅一个频道:
72.16.36.70:6379> subscribe news
Reading messages... (press Ctrl-C to quit)
1) "subscribe"
2) "news"
3) (integer) 1

2、向频道发送消息
172.16.36.70:6379> publish news weizhenping
(integer) 1

3、频道会接收下来消息
[root@Centos7 ~]# redis-cli -h 172.16.36.70
172.16.36.70:6379> subscribe news
Reading messages... (press Ctrl-C to quit)
1) "subscribe"
2) "news"
3) (integer) 1
1) "message"
2) "news"
3) "weizhenping"   #此内容为频道发送过来的消息

基于模式的频道订阅示例
1、基于模式订阅频道
[root@Centos7 ~]# redis-cli -h 172.16.36.70
172.16.36.70:6379> PSUBSCRIBE news.i[to]    #订阅了news.io,news,io两个频道
Reading messages... (press Ctrl-C to quit)
1) "psubscribe"
2) "news.i[to]"
3) (integer) 1

2、向频道发送消息
172.16.36.70:6379> PUBLISH news.io hello
(integer) 1

3、频道会接收下来消息
[root@Centos7 ~]# redis-cli -h 172.16.36.70
172.16.36.70:6379> PSUBSCRIBE news.i[to]
Reading messages... (press Ctrl-C to quit)
1) "psubscribe"
2) "news.i[to]"
3) (integer) 1
1) "pmessage"
2) "news.i[to]"
3) "news.io"
4) "hello"

redis的持久化功能

  • 1、RDB(redisDB)

    • 工作原理

    RDB为snapshot(快照)存储机制,其也是redis默认的存储机制,按照事先定制的策略,周期性地将数据从内存中读取出来保存至磁盘,当到达save指令指定的时间,redis主进程将fork一个子进程,负责内存中的内容快照并保存到磁盘中。 Linux系统有写时复制机制,父进程与子进程会共享相同的物理页面,当父进程处理写请求时,操作系统为写的数据创建一个副本,因此子进程保存的数据一定是与时间点一致的数据。当子进程将快照写入临时文件后,会使用临时文件替换旧的文件,然后子进程完成退出 。保存的数据文件默认为dump.rdb. 如果在SAVE周期之前停电了,会造成部分数据丢失。

    • 数据保存机制
      • save : 在主线程中保存快照,保存时会阻塞所有客户端请求,每次将完整数据写至dump.rdb文件中,会带来大量的IO压力
      • bgsave : 异步保存机制,在后端自动保存,其不会阻塞窝客户端请求
    • RDB相关的配置参数(/etc/redis/redis.conf)
      • stop-writes-on-bgsave-error yes : 在基于快照备份时,一旦发生错误,是否停止写操作
      • rdbcompression yes : rdb文件是否压缩来节约空间
      • rdbchecksum yes : 是否对rdb的镜像文件做校验码检测,当redis启动时会根据事先保存好的校验码进行对比,保证数据的完整性
      • dbfilename dump.rdb : 保存的文件名
      • dir /var/lib/redis/ : 文件保存目录
      • save "" : 关闭rdb功能
  • 2、AOF(append only file)

    • 工作原理

    redis以顺序IO的方式附加在文件的尾部,将每一次的写命令操作都通过write函数追加到文件后面,其是比RDB更好的持久化方案,但文件会变得越来越大。当redis重启时,可通过执行文件中的命令在内存中重建数据库

    • 数据保存机制
      • bgrewriteaof : AOF文件重写,它不会读取正在使用的AOF文件,而是通过将内存中的数据以命令的方式保存至临时文件中,完成之后替换原来的AOF文件,这样可以减少AOF的大小
    • AOF重写过程
      • 1、redis主进程通过fork机制创建子线程
      • 2、子进程根据redis内存中现有的数据通过重建命令创建数据库于临时文件中
      • 3、父进程继续接收客户请求,并会把这些请求中的写操作继续追加到原来的AOF文件中,额外地,也将新的写请求放置于一个缓冲队列中
      • 4、子进程重写完成,会通知父进程,父进程把缓冲中的队列命令写到临时文件中
      • 5、父进程用临时文件替换老的AOF文件
    • AOF相关的配置参数(/etc/redis/redis.conf)
      • appendonly no : 是否启用AOF功能,yes表示启用
      • appendfilename "appendonly.aof" : aof存储文件
      • appendfsync always : 每次收到写命令,立即写入磁盘
      • appendfsync everysec : 每秒钟写一次,推荐操作
      • appendfsync no : append功能自己触发写操作,将所有操作都提交给OS,由操作系统决定什么时候写
      • no-appendfsync-no-rewrite no: 在重写的过程中是否调用fsync
      • auto-aof-rewrite-percentage 100 : 在aof文件已经是上次重写时的2倍大小,将自动启动重写操作
      • auto-aof-rewrite-min-size 64mb : 当文件达到64MB才执行重写操作
      RDB与AOF同时启用时:
      1、bgsave和bgrewriteaof不会同时执行
      2、在redis服务器启动用于恢复数据时,会优先使用AOF
      

redis的主从复制

  • 1、redis的主从复制特点

    • 一个master可以有多个slave
    • 支持链式复制
    • master以非阻塞方式同步数据至slave,意味着可同时与多个slave同步
  • 2、复制的工作原理

    • 1、主库会自己基于ping check机制来检查从库是否在线
    • 2、如果从库在线就同步数据文件至从服务器端
    • 3、从服务器也可以发送请求同步的请求,主库将启动一个线程,把内存中的数据同步给从库,从库将数据保存至文件中
    • 4、从库再把文件装载到内存中,从而完成复制功能
  • 3、主从相关配置(/etc/redis.conf)

    • slave-serve-stale-data yes : 当主服务器连接不上了,从服务器是否可以使用过期数据响应
    • repl-diskless-sync no : 是否基于diskless机制同步
    • slave-priority 100 : 指定从服务器优先级
    • min-slave-to-write 3 : 如果从节点小于三个,主服务器将拒绝写操作
    • min-slave-max-lag 10 : 从服务器不能晚于主服务器10秒钟,是否将停止复制

    如果master使用了requirepass开启了认证功能,从服务器要使用masterauth <password>来连接,使用指定的密码进行认证

    主从设置示例:
        172.16.36.70 : 主redis服务
        172.16.36.71 : 从redis服务
        
    172.16.36.70配置
    vim /etc/redis.conf
    bind 172.16.36.70
    
    172.16.36.71配置:
    vim /etc/redis.conf
    bind 172.16.36.71
    
    [root@Centos7 ~]# redis-cli -h 172.16.36.71
    172.16.36.71:6379> SLAVEOF 172.16.36.70 6379
    OK
    172.16.36.71:6379> get ip   #查看是否能获取到主库数据
    "172.16.36.70"
    172.16.36.71:6379> dbsize
    (integer) 3
    

redis高可用的实现

  • sentinel机制

    • 工作原理

      找一台专用的监控主机,即能提供监控又可以提供配置功能,如果发现master离线了,监控主机会从从节点选择新的主节点。为了不误判,setinel至少奇数个节点,同时监控。如果主节点不在线,多个setinel会协调一个新的主节点,以免发生误判,所有setinel每秒一次向所有服务器发送ping请求,判断节点是否在线。setinel是一个分布式系统,使用流言协议和投票协议来决定故障迁移。可以监控多组redis实例。

    • sentinel的功用
      • 用于管理多个redis服务,实现HA
      • 监控
      • 通知
      • 自动故障转移
    • 工作过程
      • 启用sentinel时, 首先服务器做自身初始化,运行redis-server中专用于sentinel功能中的代码
      • 初始化sentinel状态,根据给定的配置文件,初始化监控的master服务列表(可以根据master的配置,获取从服务器节点)
      • 创建连向Master的连接
    • sentinel下线机制
      • 主观下线 : 一个sentinel实例判断出某节点下线
      • 客观下线 : 多个sentinel节点协商后判断出某节点下线
  • setinel程序

    • redis-sentinel /path/to/sentinel.conf
    • redis-server /path/to/sentinel --sentinel
  • setinel的工作端口

    • 26379/TCP
  • 专用配置文件

    • /etc/redis-sentinel.conf
  • redis-sentinel.conf配置参数
    • prot 26379
    • dir /tmp
    • sentinel monitor mymaster 127.0.0.1 6379 2
    mymaster: sentinel要监控的实例名称,此名称可以随意取
    127.0.0.1 : 主节点的IP地址
    6379 : 主节点的端口
    2 : sentinel节点的票数,此值要大于sentinel节点数量的半数
    
    • sentinel down-after-milliseconds mymaster 30000 : 判断主节点不在线的默认超时时长,默认30秒
    • sentinel parallel-syncs mymaster 1 : 故障转移时最多能有多少个从服务器向主服务器发起同步请求
    • sentinel failover-timeout mymaster 180000 : 提升主节点的超时时长,表示提升新的主节点在3分钟内未完成,操作将失败
  • sentinel专用命令

    • sentinel masters : 列出所有主服务器
    • sentinel slaves <master name> : 获取所有当前redis实例中的从节点信息
    • sentinel get-master-addr-by name <master name> :直接获取当前redis实例主节点的IP地址及端口
    172.16.36.74:26379> sentinel get-master-addr-by-name mymaster
    1) "172.16.36.72"
    2) "6379"
    
    • sentinel reset : 重置服务器所有状态
    • sentinel failover <master name> : 手动实现故障转移
  • sentinel配置实例

实验环境说明:
    172.16.36.70 : redis主节点
    172.16.36.71 : redis从节点
    172.16.36.72 : redis从节点
    172.16.36.74 : sentinel节点1
    172.16.36.75 : sentinel节点2
    172.16.36.76 : sentinel节点3
    
####配置redis主节点
操作主机: 172.16.36.70
#vim /etc/redis.conf
bind 172.16.36.70
daemonize yes   
启动服务
#redis-server /etc/redis.conf
[root@Centos7 ~]# ss -tnl
State      Recv-Q Send-Q                                       Local Address:Port                                         Peer Address:Port
LISTEN     0      128                                           172.16.36.70:6379                                                    *:*


####配置redis从节点
操作主机: 172.16.36.71
#vim /etc/redis.conf
bind 172.16.36.71
daemonize yes

启动服务
#redis-server /etc/redis.conf
[root@Centos7 ~]# ss -tnl
State      Recv-Q Send-Q                                       Local Address:Port                                         Peer Address:Port
LISTEN     0      128                                           172.16.36.70:6379                                                    *:*

配置主节点信息
[root@Centos7 ~]# redis-cli -h 172.16.36.71 -p 6379
172.16.36.71:6379> SLAVEOF 172.16.36.70 6379
OK

操作主机: 172.16.36.72
#vim /etc/redis.conf
bind 172.16.36.72
daemonize yes

启动服务
#redis-server /etc/redis.conf
[root@Centos7 ~]# ss -tnl
State      Recv-Q Send-Q                                       Local Address:Port                                         Peer Address:Port
LISTEN     0      128                                           172.16.36.70:6379                                                    *:*

配置主节点信息
[root@Centos7 ~]# redis-cli -h 172.16.36.71 -p 6379
172.16.36.71:6379> SLAVEOF 172.16.36.70 6379
OK


####配置sentinel节点
操作主机: 172.16.36.74
# vim /etc/redis-sentinel.conf
port 26379
dir "/tmp"
daemonize yes
sentinel monitor mymaster 172.16.36.70 6379 2
sentinel parallel-syncs mymaster 3
sentinel down-after-milliseconds mymaster 30000
sentinel failover-timeout mymaster 180000

启动服务
redis-sentinel /etc/redis-sentinel.conf

查看服务启动状态
users:(("master",2112,14))
[root@Centos7 ~]# ss -tln
State      Recv-Q Send-Q                                       Local Address:Port                                         Peer Address:Port
LISTEN     0      128                                                      *:26379                                                   *:*


操作主机: 172.16.36.75
# vim /etc/redis-sentinel.conf
port 26379
dir "/tmp"
daemonize yes
sentinel monitor mymaster 172.16.36.70 6379 2
sentinel parallel-syncs mymaster 3
sentinel down-after-milliseconds mymaster 30000
sentinel failover-timeout mymaster 180000

启动服务
redis-sentinel /etc/redis-sentinel.conf

查看服务启动状态
users:(("master",2112,14))
[root@Centos7 ~]# ss -tln
State      Recv-Q Send-Q                                       Local Address:Port                                         Peer Address:Port
LISTEN     0      128                                                      *:26379                                                   *:*


操作主机: 172.16.36.76
# vim /etc/redis-sentinel.conf
port 26379
dir "/tmp"
daemonize yes
sentinel monitor mymaster 172.16.36.70 6379 2
sentinel parallel-syncs mymaster 3
sentinel down-after-milliseconds mymaster 30000
sentinel failover-timeout mymaster 180000

启动服务
redis-sentinel /etc/redis-sentinel.conf

查看服务启动状态
users:(("master",2112,14))
[root@Centos7 ~]# ss -tln
State      Recv-Q Send-Q                                       Local Address:Port                                         Peer Address:Port
LISTEN     0      128                                                      *:26379                                                   *:*



查看sentinel相关的日志信息
tailf /var/log/redis/redis-sentinel.log
[32524] 25 Mar 10:05:02.290 # Sentinel runid is a44025e518b65c512340c48535df119496f7a0d8
[32524] 25 Mar 10:05:02.291 # +monitor master mymaster 172.16.36.70 6379 quorum 2
[32524] 25 Mar 10:05:32.322 # +sdown slave 172.16.36.71:6379 172.16.36.71 6379 @ mymaster 172.16.36.70 6379
[32524] 25 Mar 10:05:46.311 * +sentinel sentinel 172.16.36.76:26379 172.16.36.76 26379 @ mymaster 172.16.36.70 6379
[32524] 25 Mar 10:05:57.071 * +sentinel sentinel 172.16.36.75:26379 172.16.36.75 26379 @ mymaster 172.16.36.70 6379
[32524] 25 Mar 10:09:03.727 # +sdown master mymaster 172.16.36.70 6379


将当前的Master节点shudown,查看sentinel的相关日志信息
[32524] 25 Mar 10:09:03.747 # +new-epoch 4
[32524] 25 Mar 10:09:03.749 # +vote-for-leader 90c011868befc3047a8527886efce8f8c6f9ea34 4
[32524] 25 Mar 10:09:03.836 # +odown master mymaster 172.16.36.70 6379 #quorum 3/2
[32524] 25 Mar 10:09:03.836 # Next failover delay: I will not start a failover before Fri Mar 25 10:15:04 2016
[32524] 25 Mar 10:09:04.854 # +config-update-from sentinel 172.16.36.75:26379 172.16.36.75 26379 @ mymaster 172.16.36.70 6379
[32524] 25 Mar 10:09:04.854 # +switch-master mymaster 172.16.36.70 6379 172.16.36.72 6379
[32524] 25 Mar 10:09:04.855 * +slave slave 172.16.36.71:6379 172.16.36.71 6379 @ mymaster 172.16.36.72 6379
[32524] 25 Mar 10:09:04.856 * +slave slave 172.16.36.70:6379 172.16.36.70 6379 @ mymaster 172.16.36.72 6379

对当前主节点监控sentinel的活跃探测信息
root@Centos7 ~]# redis-cli -h 172.16.36.72
172.16.36.72:6379> monitor
OK
1458878723.052705 [0 172.16.36.76:53255] "PING"
1458878723.402835 [0 172.16.36.75:38413] "PING"
1458878723.586806 [0 172.16.36.74:32824] "PING"
1458878723.859748 [0 172.16.36.75:38413] "PUBLISH" "__sentinel__:hello" "172.16.36.75,26379,90c011868befc3047a8527886efce8f8c6f9ea34,4,mymaster,172.16.36.72,6379,4"
1458878724.099837 [0 172.16.36.76:53255] "PING"
1458878724.152776 [0 172.16.36.76:53255] "PUBLISH" "__sentinel__:hello" "172.16.36.76,26379,21a0a795010287138b6efc636d03edcce66bcd56,4,mymaster,172.16.36.72,6379,4"
1458878724.454214 [0 172.16.36.75:38413] "PING"
1458878724.617819 [0 172.16.36.74:32824] "PING"
1458878724.693888 [0 172.16.36.74:32824] "PUBLISH" "__sentinel__:hello" "172.16.36.74,26379,a44025e518b65c512340c48535df119496f7a0d8,4,mymaster,172.16.36.72,6379,4"

使用info sentinel命令查看主从信息
172.16.36.74:26379> info sentinel
# Sentinel
sentinel_masters:1
sentinel_tilt:0
sentinel_running_scripts:0
sentinel_scripts_queue_length:0
master0:name=mymaster,status=ok,address=172.16.36.72:6379,slaves=2,sentinels=3

Redis Clustering

在redis3.0版本中引入clustering功能, 是去中心化的分布式数据库,通过分片机制进行数据分布,clustering内的每个节点仅有数据库一部分数据,每个节点都有全局元数据,通过查找元数据,即可查询到数据存放在哪台服务器

分布式解决方案

  • Twemproxy(Twitter)
    • 代理分片机制
    • 优点
      • 非常稳定,企业方案
    • 缺点
      • 单点故障
      • 需要依赖第三方软件,如Keeplived
      • 无法平滑地横向扩展
      • 没有后台界面
      • 代理分片机制引入更多的来回次数并提高延迟
      • 单核模式,无法充分利用多核,除非多实例
      • Twitter官方肉串不再继续使用
  • Codis(豌豆荚)
    • 代理分片机制
    • 2014年11月开源
    • 基于GO以及C语言开发
    • 优点
      • 非常稳定,企业级方案
      • 数据自动平衡
      • 高性能
      • 简单的测试显示较Twemproxy快一倍
      • 善用多核CPU
      • 简单
        • 没有paxos类的协调机制
        • 没有主从复制
      • 有后台界面
    • 缺点
      • 代理分片机制引入更多的来回次数并提高延迟
      • 需要第三方软件支持协调机制
        • 目前支持zookeeper及Etcd
      • 不支持主从复制 ,需要另外实现
      • Codis采用proxy方案,所有必然会带来单机性能的损失
        • 经测试,在不开Pipeline的情况下,大概会损失40%左右的性能
  • Redis Cluster(官方)
    • 官方实现
    • 需要3.0或更高版本
    • 优点
      • 无中心的P2P Gossip分散式模式
      • 更少的来回次数并降低延迟
      • 自动于多个redis节点进行分片
      • 不需要第三方软件支持协调机制
    • 缺点
      • 依赖于redis 3.0或更高版本
      • 需要时间验证其稳定性
      • 没有后台界面
      • 需要智能客户端
      • redis客户端必须支持redis cluster架构
      • 较Codis有更多的维护升级版本

推荐阅读更多精彩内容

  • 1.1 资料 ,最好的入门小册子,可以先于一切文档之前看,免费。 作者Antirez的博客,Antirez维护的R...
    JefferyLcm阅读 16,744评论 1 51
  • 本文将从Redis的基本特性入手,通过讲述Redis的数据结构和主要命令对Redis的基本能力进行直观介绍。之后概...
    kelgon阅读 59,759评论 24 624
  • Redis杂谈 Redis是近年来发展迅速的内存数据库,网上也已经有多Redis的文章。但不管是英文还是中文,多数...
    迷失于重逢阅读 1,292评论 0 14
  • 前面是黑暗 后面也是黑暗 那个穿拖鞋的小男孩 从走廊里过来 像灯一样,将门槛照亮
    诗人凤凰阅读 299评论 0 0
  • 看不得圆满 因为都已经支离破碎 看不得欢笑 因为都已经欲哭无泪 小时候,笑是一种心情 从滑梯快速滑落 我会笑 撞上...
    苏瑞旻阅读 296评论 0 1
  • H5单页面手势滑屏切换是采用HTML5 触摸事件(Touch) 和 CSS3动画(Transform,Transi...
    web蜗牛阅读 1,085评论 0 10
  • 爱的表现形式都是相似的,不爱则各有各的理由。 鲁迅的《两地书》里有这么一段话,虽不雅,却让我真真切切感受到他们之间...
    梧印墨迹阅读 393评论 0 2