Redis Cluster安装配置

Redis集群介绍

  • Redis 集群是一个提供在多个Redis间节点间共享数据的程序集。
  • Redis集群并不支持处理多个keys的命令,因为这需要在不同的节点间移动数据,从而达不到像Redis那样的性能,在高负载的情况下可能会导致不可预料的错误.
  • Redis 集群通过分区来提供一定程度的可用性,在实际环境中当某个节点宕机或者不可达的情况下继续处理命令.
  • Redis 集群有16384个哈希槽,每个key通过CRC16校验后对16384取模来决定放置哪个槽.集群的每个节点负责一部分hash槽,举个例子,比如当前集群有3个节点,那么: 节点 A 包含 0 到 5500号哈希槽. 节点 B 包含5501 到 11000 号哈希槽. 节点 C 包含11001 到 16384号哈希槽.

Redis Cluster集群的优势:

  • 自动分割数据到不同的节点上。
  • 整个集群的部分节点失败或者不可达的情况下能够继续处理命令。
  • 客户端与redis节点直连,不需要中间proxy层.客户端不需要连接集群所有节点,连接集群中任何一个可用节点即可。
  • 支持主从复制模型。(slave->master 选举,集群容错)

Redis Cluster集群的主从复制模型:

为了使在部分节点失败或者大部分节点无法通信的情况下集群仍然可用,所以集群使用了主从复制模型,假如有A,B,C三个节点的集群,在没有复制模型的情况下,如果节点B失败了,那么整个集群就会以为缺少5501-11000这个范围的槽而不可用.
然而如果在集群创建的时候我们为每个节点添加一个从节点A1,B1,C1,那么整个集群便有三个master节点和三个slave节点组成,这样在节点B失败后,集群便会选举B1为新的主节点继续服务,整个集群便不会因为槽找不到而不可用了。

Redis Cluster模型图

Redis Cluster的官方文档介绍

1、Redis Cluster集群配置

环境:CentOS7,两台服务器:172.18.209.168、172.18.209.168
Redis的安装步骤可以参考之前的文章 Linux安装配置Redis
我们需要创建三个主节点和三个从节点:

172.18.209.168:7001
172.18.209.168:7002
172.18.209.168:7003
172.18.209.169:7004
172.18.209.169:7005
172.18.209.169:7006

为了方便起见,我这里在168这台机器上创建所有的节点。

#cd /usr/local/redis-4.0.1
#mkdir /data/cluster
#cd /data/cluster
#mkdir 7001 7002 7003 7004 7005 7006

复制默认的配置文件到7001端口对应的目录下。

#cp ../../redis.conf 7001/
修改配置文件:
#vim 7001/redis.conf
port 7001
daemonize yes
cluster-enabled yes
cluster-config-file nodes.conf
cluster-node-timeout 5000
appendonly yes

然后分别copy到其他端口对应的目录下,再打开配置文件修改一下端口号,其他内容不变。
进入对应的端口目录下启动实例:

#redis-server redis.conf

查看进程:

#ps -ef | grep redis
root 5371 1 0 Sep07 ? 00:01:35 redis-server *:6379
root 11891 1 0 10:35 ? 00:00:17 redis-server 172.18.209.168:7002 [cluster]
root 11914 1 0 10:37 ? 00:00:17 redis-server 172.18.209.168:7003 [cluster]
root 11921 1 0 10:37 ? 00:00:17 redis-server 172.18.209.168:7004 [cluster]
root 11926 1 0 10:37 ? 00:00:17 redis-server 172.18.209.168:7005 [cluster]
root 11931 1 0 10:37 ? 00:00:17 redis-server 172.18.209.168:7006 [cluster]

ps:我这里没有7001,是因为我创建后测试被我杀掉了。
接下来就是创建集群了,首先要安装依赖:

#yum install ruby rubygems -y

再安装gem-redis,下载gem-redis-4.0.0
下载gem-redis-4.0.0后执行命令安装

#gem install -l redis-4.0.0.gem

不出意外的话会报错:
ERROR: Error installing redis-4.0.0.gem:
redis requires Ruby version >= 2.2.2.
解决办法参考:redis requires Ruby version >= 2.2.2
为了方便执行命令,copy redis-trib 到/usr/bin目录下。

#cp redis-4.0.0/src/redis-trib.rb /usr/local/bin/redis-trib

接着开始创建集群,执行命令:

#redis-trib create --replicas 1 172.18.209.168:7001 172.18.209.168:7002 172.18.209.168:7003 172.18.209.169:7004 172.18.209.169:7005 172.18.209.169:7006

ps:redis-trib.rb 程序的命令是 create,这表示创建一个新的集群。
–replicas 1 表示为集群中的每个主节点创建一个从节点。
然后redis-trib 会打印出详细配置,确认没问题的话,输入yes确定:

>>> Creating cluster
>>> Performing hash slots allocation on 6 nodes...
Using 3 masters:
172.18.209.168:7001
172.18.209.169:7004
172.18.209.168:7002
Adding replica 172.18.209.169:7005 to 172.18.209.168:7001
Adding replica 172.18.209.168:7003 to 172.18.209.169:7004
Adding replica 172.18.209.169:7006 to 172.18.209.168:7002
M: 3e00a8c896fd0957e7d423e5b350415ba0572182 172.18.209.168:7001
slots:0-5460 (5461 slots) master
M: 28432a59f26a10f2c1eedad3f41c4ac47110d549 172.18.209.168:7002
slots:10923-16383 (5461 slots) master
S: 4806d7fb4c04880b3d7a6a20728088f243c52087 172.18.209.168:7003
replicates a59bef6cacd813b13742cdb22026bd167ef9aeaf
M: a59bef6cacd813b13742cdb22026bd167ef9aeaf 172.18.209.169:7004
slots:5461-10922 (5462 slots) master
S: f715ef8bcf25b1b7c0960c26b1e1b600c12ca09b 172.18.209.169:7005
replicates 3e00a8c896fd0957e7d423e5b350415ba0572182
S: dd39cd4b2b1ba8b34e0207023922f522ae72d2b6 172.18.209.169:7006
replicates 28432a59f26a10f2c1eedad3f41c4ac47110d549
Can I set the above configuration? (type 'yes' to accept):
>>> Nodes configuration updated
>>> Assign a different config epoch to each node
>>> Sending CLUSTER MEET messages to join the cluster
Waiting for the cluster to join...
>>> Performing Cluster Check (using node 172.18.209.168:7001)
M: 3e00a8c896fd0957e7d423e5b350415ba0572182 172.18.209.168:7001
   slots:0-5460 (5461 slots) master
   1 additional replica(s)
M: a59bef6cacd813b13742cdb22026bd167ef9aeaf 172.18.209.169:7004
   slots:5461-10922 (5462 slots) master
   1 additional replica(s)
S: 4806d7fb4c04880b3d7a6a20728088f243c52087 172.18.209.168:7003
   slots: (0 slots) slave
   replicates a59bef6cacd813b13742cdb22026bd167ef9aeaf
M: 28432a59f26a10f2c1eedad3f41c4ac47110d549 172.18.209.168:7002
   slots:10923-16383 (5461 slots) master
   1 additional replica(s)
S: dd39cd4b2b1ba8b34e0207023922f522ae72d2b6 172.18.209.169:7006
   slots: (0 slots) slave
   replicates 28432a59f26a10f2c1eedad3f41c4ac47110d549
S: f715ef8bcf25b1b7c0960c26b1e1b600c12ca09b 172.18.209.169:7005
   slots: (0 slots) slave
   replicates 3e00a8c896fd0957e7d423e5b350415ba0572182
[OK] All nodes agree about slots configuration.
>>> Check for open slots...
>>> Check slots coverage...
[OK] All 16384 slots covered.

这样就配置成功了。
可以使用命令验证:

#redis-cli -c -h 172.18.209.168 -p 7001
172.18.209.168:7001> set name "abswolf"
-> Redirected to slot [5798] located at 172.18.209.169:7004
OK
172.18.209.168:7001> get name
-> Redirected to slot [5798] located at 172.18.209.169:7004
"abswolf"
172.18.209.169:7004> 

ps:关于Redis集群的介绍来自Redis Cluster集群主从方案(附Jedis Cluster教程)

MySQL主从同步、主主同步配置

MySQL的 Replication 是一个异步的复制过程,从一个 MySQL Instance( Master)复制到另一个 MySQL Instance(Slave)。在 Master 与 Slave 之间的实现整个复制过程主要由三个线程来完成,其中两个线程(SQL线程和IO线程)在 Slave 端,另外一个线程(IO线程)在 Master 端。

软件环境:CentOS7,MySQL5.6.37 
服务器:192.168.1.100(Master),192.168.1.101(Slave)

1、配置192.168.1.100(Master)my.cnf

vim /etc/my.cnf
log-bin=mysql-bin #打开二进制日志(必须)
server-id=100 #设置唯一id,任意数。
binlog-do-db=test_db #设置要复制的数据库,多个数据库重复设置
#不需要复制的数据库,多个数据库重复设置,一般设置了bindlog-do-db,就不需要设置这个了。
binlog-ignore-db=mysql

注意:如果要实现复杂的主从同步:比如说,A->B->C,其中B是A的从服务器,同时B又是C的主服务器,那么B服务器除了需要打开log-bin之外,还需要打开log-slave-updates选项,可以在B上使用“show variables like ‘log%’;”来确认是否已经生效。

2、配置192.168.1.101(Slave)my.cnf

server-id=101
replicate-do-db=test_db 
log-bin=mysql-bin

3、在Master服务器中创建一个Slave可以登录的用户

GRANT ALL PRIVILEGES ON *.* TO 'abswolf'@'192.168.1.101' IDENTIFIED BY 'abswolf' WITH GRANT OPTION;

4、保持主从数据库的test_db数据库状态一致

数据量小的话可以用mysqldump,它有一个master-data参数很有用,通过使用此参数,导出的SQL文件里会自动包含CHANGE MASTER TO MASTER_LOG_FILE=’…’, MASTER_LOG_POS=…;,这样创建从服务器就更方便了。如果数据量大的话不太适合使用mysqldump(慢),如果是myisam表的话,加上–lock-all-tables参数,如果是innodb表的话,加上–single-transaction参数。
先在主服务器上锁定所有的表,以免在复制过程中数据发生变化:

mysql> flush tables with read lock;

然后在主服务器上查询当前二进制文件的文件名及偏移位置:

mysql > show master status;

然后停止主服务器上的MySQL服务:

shell> mysqladmin -u root shutdown

注意:如果仅是MyISAM的话,可以不停止MySQL服务,但要在复制数据文件的过程中保持只读锁,如果是InnoDB的话,必须停止MySQL服务。
再拷贝数据文件:

shell> tar -cvf /tmp/mysql-snapshot.tar .

拷贝完别忘了启动主服务上的MySQL服务了。
然后把数据文件应用到从服务器上,再次启动slave的时候使用,记得启动时加上skip-slave-start选项,使之不会立刻去连接master。

5、在Master服务器上执行命令mysql>show master status

+——————+———-+————–+——————+——————-+
| File | Position | Binlog_Do_DB | Binlog_Ignore_DB | Executed_Gtid_Set |
+——————+———-+————–+——————+——————-+
| mysql-bin.000001 | 120 | test_db | | |
+——————+———-+————–+——————+——————-+
1 row in set (0.00 sec)
记录File的名字和Position,下面从服务器上设置需要用到。

6、在Salve服务器上设置相关的二进制日志信息

mysql> stop slave;
mysql> CHANGE MASTER TO
    -> MASTER_HOST='192.168.1.100',
    -> MASTER_USER='abswolf',
    -> MASTER_PASSWORD='abswolf',
    -> MASTER_LOG_FILE='mysql-bin.000001',
    -> MASTER_LOG_POS=120;
mysql> start slave; #开启Salve数据库复制功能
mysql> show slave status\G #查看是否开启成功,

*************************** 1. row ***************************
Slave_IO_State: Waiting for master to send event
Master_Host: 192.168.1.100
Master_User: abswolf
Master_Port: 3306
Connect_Retry: 60
Master_Log_File: mysql-bin.000001
Read_Master_Log_Pos: 120
Relay_Log_File: mysqld-relay-bin.000002
Relay_Log_Pos: 283
Relay_Master_Log_File: mysql-bin.000001
Slave_IO_Running: Yes
Slave_SQL_Running: Yes

如果看到Slave_IO_Running和Slave_SQL_Running都是Yes的话,表示配置正确。

7、主主同步

主主同步就是两台数据库的数据进行变更的时候,另一台也会进行相应的变更。那么我们只需要把两个主从配置和起来就可以。需要不同的是一些主键的配置,分别在两台服务器的my.cnf配置文件中加上如下配置:

auto_increment_increment=2   #步进值auto_imcrement。一般有n台主MySQL就填n
auto_increment_offset=1   #起始值。一般填第n台主MySQL。此时为第一台主MySQL

然后在192.168.1.101中设置Master的日志信息。参考上面的步骤反过来做即可。

8、注意事项 参考(转)mysql主从同步 binlog-do-db replicate-do-db

应该保证从服务器上任何数据的修改都是通过从主服务器上复制操作获取的,换句话说,从服务器应该是只读的,如果不能保证这一点,则可能造成主从数据不一致。
可以在从服务器的my.cnf里加入read-only参数来实现这一点,唯一需要注意的一点事read-only仅对没有super权限的用户有效。所以最好核对一下连接从服务器的用户,确保其没有super权限。

从理想角度看,主从数据库应该无故障的运转下去,可以有时候还是会出现一些莫名其妙的问题,比如说即便从未在从服务器上手动更新过数据,但还是可能遇到“Error: 1062 Duplicate entry”错误,具体原因不详,可能是MySQL本身的问题。遇到这类问题的时候,从服务器会停止复制操作,我们只能手动解决问题,具体的操作步骤如下:

mysql> set global sql_slave_skip_counter = 1;
mysql> start slave;

同样的操作可能需要进行多次,也可以设置自动处理此类操作,在从服务器的my.cnf里设置: slave-skip-errors=1062

最后再唠叨一下日志的问题:时间长了,数据库服务器上的二进制文件会越来越多,清理是必要的,你可以设置自动清理,相关参数是expire_logs_days,也可以使用手动删除的方式,但这里说的手动不是指rm,而是指PURGE BINARY LOGS,删除任何日志前,最好在所有的从服务器上通过show slave status命令确认一下相关日志是否已经无用。

补充:[ERROR] Error in Log_event::read_log_event(): ‘Event too big’ 在使用主从复制的时候,出现的问题多半是和日志(主服务器的二进制日志,从服务器的延迟日志)相关的。比如说加入你遇到了上面的错误,你可以根据错误日志的信息在主从数据库服务器上分别执行:

mysqlbinlog 日志文件 > /dev/null 查看错误,如果没有错误,则不会有任何输出,反之会输出错误信息,如果确定了错误是出现在主服务器二进制日志上,可以跳过适当的位置,再在从服务器上重新设定LOG_POS,如果确定了错误是出现在从服务器延迟日志上,则可以删除从服务器的延迟日志(使用CHANGE TO MASTER的时候,除非设定了延迟日志信息,否则会自动删除延迟日志),并在从服务器上重新设定LOG_POS。期间也可以考虑手动执行不能自动执行的SQL日志。

补充:配置的时候如果版本允许最好打开sync_binlog选项。

补充:有时候,从服务器延迟日志可能已经损坏,这时需要执行CHANGE MASTER TO设置新的日志文件信息,但是在从服务器上SHOW SLAVE STATUS会显示很多日志信息,他们的含义有所不同: Master_Log_File:Read_Master_Log_Pos 是IO相关的日志信息 Relay_Master_Log_File:Exec_Master_Log_Pos 是SQL相关的日志信息 从服务器需要设置的是SQL相关的日志信息: slave stop; change master to master_log_file=’(binlog name in relay_master_log_file)’, master_log_pos=(exec_master_log_pos number); slave start; 1) When you are using the master as a consistent snapshot, use SHOW MASTER STATUS to determine the position. 2) When you are using a slave as a consistent snapshot, use SHOW SLAVE STATUS and Exec_Master_Log_Pos.

MySQL 5.7 并行复制实现原理与调优

# slave
slave-parallel-type=LOGICAL_CLOCK
slave-parallel-workers=16
master_info_repository=TABLE
relay_log_info_repository=TABLE
relay_log_recovery=ON

JavaScript reduce 用法

reduce() 方法接收一个函数作为累加器,数组中的每个值(从左到右)开始缩减,最终计算为一个值。
reduce() 可以作为一个高阶函数,用于函数的 compose。
注意: reduce() 对于空数组是不会执行回调函数的。

语法

array.reduce(function(total, currentValue, currentIndex, arr), initialValue)

参数

参数 描述
total 必需。初始值, 或者计算结束后的返回值。
currentValue 必需。当前元素
currentIndex 可选。当前元素的索引
arr 可选。当前元素所属的数组对象。

initialValue可选。传递给函数的初始值

Flex布局语法介绍

指定使用Flex布局

.box{
  display: flex;
}

行内元素使用Flex布局

.box{
  display: inline-flex;
}

注意,设为 Flex 布局以后,子元素的float、clear和vertical-align属性将失效。

Flex的六个属性

    • flex-direction
    • flex-wrap
    • flex-flow
    • justify-content
    • align-items
    • align-content

flex-direction属性
flex-direction 设置项目的排列方向。

.box {
  flex-direction: row | row-reverse | column | column-reverse;
}

row(默认值):主轴为水平方向,起点在左端。
row-reverse:主轴为水平方向,起点在右端。
column:主轴为垂直方向,起点在上沿。
column-reverse:主轴为垂直方向,起点在下沿

See the Pen flex-direction: row by 06peng (@abswolf) on CodePen.

See the Pen flex-direction: row | row-reverse by 06peng (@abswolf) on CodePen.

See the Pen flex-direction: column by 06peng (@abswolf) on CodePen.

See the Pen flex-direction: column-reverse by 06peng (@abswolf) on CodePen.

flex-wrap属性
flex-wrap属性定义,如果一条轴线排不下,如何换行?它有三个值:
nowrap(默认):不换行。

See the Pen flex-wrap:nowrap by 06peng (@abswolf) on CodePen.


wrap:换行,第一行在上方。

See the Pen flex-wrap:wrap by 06peng (@abswolf) on CodePen.


wrap-reverse:换行,第一行在下方。

See the Pen flex-wrap:wrap-reverse by 06peng (@abswolf) on CodePen.

flex-flow属性
flex-flow属性是flex-direction属性和flex-wrap属性的简写形式,默认值为row和nowrap。

 
 .box { 
    flex-flow: <flex-direction> || <flex-wrap>; 
 }

justify-content属性
justify-content属性定义了项目水平对齐方式。

.box {
  justify-content: flex-start | flex-end | center | space-between | space-around;
}

flex-start(默认值):左对齐
flex-end:右对齐
center: 居中
space-between:两端对齐,项目之间的间隔都相等。
space-around:每个项目两侧的间隔相等。所以,项目之间的间隔比项目与边框的间隔大一倍。

See the Pen justify-content:flex-start by 06peng (@abswolf) on CodePen.

See the Pen justify-content:flex-end by 06peng (@abswolf) on CodePen.

See the Pen justify-content:center by 06peng (@abswolf) on CodePen.

See the Pen justify-content:space-between by 06peng (@abswolf) on CodePen.

See the Pen justify-content:space-around by 06peng (@abswolf) on CodePen.

align-items属性
align-items属性定义了项目垂直对齐方式。

.box {
  align-items: flex-start | flex-end | center | baseline | stretch;
}

flex-start:垂直起点对齐。
flex-end:垂直终点对齐。
center:垂直中间点对齐。
baseline: 项目的第一行文字的基线对齐。
stretch(默认值):如果项目未设置高度或设为auto,将占满整个容器的高度。

See the Pen align-items:flex-start by 06peng (@abswolf) on CodePen.

See the Pen align-items:flex-end by 06peng (@abswolf) on CodePen.

See the Pen align-items:center by 06peng (@abswolf) on CodePen.

See the Pen align-items:baseline by 06peng (@abswolf) on CodePen.

See the Pen align-items:stretch by 06peng (@abswolf) on CodePen.

align-content属性
align-content属性定义了不同方向的对齐方式(相当于换行,有横有竖)。如果项目只有单个方向,该属性不起作用。

.box {
  align-content: flex-start | flex-end | center | space-between | space-around | stretch;
}

flex-start:与起点对齐。
flex-end:与终点对齐。
center:与中中间点对齐。
space-between:与两端对齐,两端之间的间隔平均分布。
space-around:两侧的间隔都相等。间隔比轴线与边框的间隔大一倍。
stretch(默认值):占满整个空间。

See the Pen align-content:flex-start by 06peng (@abswolf) on CodePen.

See the Pen align-content:flex-end by 06peng (@abswolf) on CodePen.

See the Pen align-content:center by 06peng (@abswolf) on CodePen.

See the Pen align-content:space-between by 06peng (@abswolf) on CodePen.

See the Pen align-content:space-around by 06peng (@abswolf) on CodePen.

项目(item)的6个属性

    • order
    • flex-growp
    • flex-shrink
    • flex-basis
    • flex
    • align-self

order属性
order属性定义项目的排列顺序。数值越小,排列越靠前,默认为0

See the Pen flex-order by 06peng (@abswolf) on CodePen.


flex-grow属性
flex-grow属性定义项目的放大比例,默认为0,即如果存在剩余空间,也不放大。
如果所有项目的flex-grow属性都为1,则它们将等分剩余空间(如果有的话)。如果一个项目的flex-grow属性为2,其他项目都为1,则前者占据的剩余空间将比其他项多一倍。

See the Pen flex-grow by 06peng (@abswolf) on CodePen.


flex-shrink属性
flex-shrink属性定义了项目的缩小比例,默认为1,即如果空间不足,该项目将缩小。
如果所有项目的flex-shrink属性都为1,当空间不足时,都将等比例缩小。如果一个项目的flex-shrink属性为0,其他项目都为1,则空间不足时,前者不缩小。
负值对该属性无效。

See the Pen flex-shrink by 06peng (@abswolf) on CodePen.


flex-basis属性
flex-basis属性定义了在分配多余空间之前,项目占据的主轴空间(main size)。浏览器根据这个属性,计算主轴是否有多余空间。它的默认值为auto,即项目的本来大小。
它可以设为跟width或height属性一样的值(比如350px),则项目将占据固定空间。
flex属性
flex属性是flex-grow, flex-shrink 和 flex-basis的简写,默认值为0 1 auto。后两个属性可选。
该属性有两个快捷值:auto (1 1 auto) 和 none (0 0 auto)。
建议优先使用这个属性,而不是单独写三个分离的属性,因为浏览器会推算相关值。
align-self属性
align-self属性允许单个项目有与其他项目不一样的对齐方式,可覆盖align-items属性。默认值为auto,表示继承父元素的align-items属性,如果没有父元素,则等同于stretch。

.item {
  align-self: auto | flex-start | flex-end | center | baseline | stretch;
}

该属性可能取6个值,除了auto,其他都与align-items属性完全一致。

See the Pen align-self by 06peng (@abswolf) on CodePen.

SpringBoot应用安全停止

SpringBoot的启动我们可以在打包后用如下命令启动:

nohup java -jar springapplication.jar --spring.profiles.active=prod &

更多关于启动的参数请自行查阅。

SpringBoot的停止有两种方式,一种是通过http发送shutdown信号,另一种是通过service stop的方式来停止服务。

方式一:通过http发送shutdown信号
该方式主要依赖SpringBoot Actuator的endpoint特性,具体步骤如下:
添加依赖:

<dependency> 
    <groupId>org.springframework.boot</groupId> 
    <artifactId>spring-boot-starter-actuator</artifactId> 
</dependency>

application.properties中添加:

#启用shutdown
endpoints.shutdown.enabled=true
#禁用密码验证
endpoints.shutdown.sensitive=false

执行关闭命令:

curl -X POST host:port/shutdown
例如:$ curl -X POST http://localhost:8080/shutdown

如果需要配置路径,则需要在application.properties中添加

management.context-path=/manage

则关闭命令变为

curl -X POST host:port/manage/shutdown

接下来说一下安全设置相关,远程执行命令的时候需要借助spring-boot-starter-security进行身份认证。
首先,添加security依赖:

<dependency> 
    <groupId>org.springframework.boot</groupId> 
    <artifactId>spring-boot-starter-security</artifactId> 
</dependency>

接着在application.properties中添加安全属性:

#开启shutdown的安全验证 
endpoints.shutdown.sensitive=true 
#验证用户名 
security.user.name=abswolf
#验证密码 
security.user.password=123456
#角色 
management.security.role=SUPERUSER

指定IP和端口

#指定管理端口和IP 
management.port=8080
management.address=127.0.0.1

执行命令

curl -u abswolf:123456 -X POST http://127.0.0.1:8081/shutdown

方式二:部署为Unix/Linux Service
该方式主要借助官方的spring-boot-maven-plugin创建”Fully executable” jar ,这中jar包内置一个shell脚本,可以方便的将该应用设置为Unix/Linux的系统服务(init.d service),官方对该功能在CentOS和Ubuntu进行了测试,对于OS X和FreeBSD,可能需要自定义。具体步骤如下:
引入插件:

<plugin> 
    <groupId>org.springframework.boot</groupId> 
    <artifactId>spring-boot-maven-plugin</artifactId> 
    <configuration> 
        <executable>true</executable> 
    </configuration> 
</plugin>

设置为系统服务:
将你的应用打成jar包,部署到服务器,假设部署路径为/var/app,包名为app.jar,通过如下方式将应该设置为一个系统服务:

sudo ln -s /var/app/app.jar /etc/init.d/app

赋予可执行权限:

chmod u+x app.jar

以系统服务的方式管理:

service foo start|stop|restart
默认PID文件路径:/var/run/appname/appname.pid
默认日志文件路径:/var/log/appname.log

自定义参数:
在这种方式下,我们还可以使用自定义的.conf文件来变更默认配置,方法如下:
1、在jar包相同路径下创建一个.conf文件,名称应该与.jar的名称相同,如appname.conf
2、在其中配置相关变量,如:

JAVA_HOME=/usr/local/jdk
JAVA_OPTS=-Xmx1024M
LOG_FOLDER=/custom/log

安全设置:
作为应用服务,安全性是一个不能忽略的问题,如下一些操作可以作为部分基础设置参考:
为服务创建一个独立的用户,同时最好将该用户的shell绑定为/usr/sbin/nologin
1、赋予最小范围权限:

chmod 500 app.jar

2、阻止修改:

sudo chattr +i app.jar

3、对.conf文件做类似的工作:

chmod 400 app.conf,sudo chown root:root app.conf

参考:正确、安全地停止SpringBoot应用服务

RxJava生命周期管理

当Activity或者fragment销毁的时候需要取消订阅,否则会消耗内存,导致OOM。这里推荐两种办法:
第一种:

 
CompositeDisposable mDisposables = new CompositeDisposable();
DisposableObserver mObserver = getObservable()//被观察者
        .subscribeOn(Schedulers.io())
        .observeOn(AndroidSchedulers.mainThread())
        .subscribe(mObserver);
mDisposables.add(mObserver);
当fragment或者activity销毁的时候,清空mDisposables.clear();

第二种使用rxlifecycle-components第三方框架

Activity需要继承RxAppcompatActivity,新建的Fragment则继承RxFragment。
myObservable
    .compose(RxLifecycle.bindUntilEvent(lifecycle, ActivityEvent.DESTROY))
    .subscribe();
具体可以查看官方GitHub

Git基本使用

一、先在第三方git平台上创建一个空的项目。

二、本地初始化项目

1、执行以下命令设置git的提交信息

git config --global user.name "your name"
git config --global user.email "your email"

2、在你的需要初始化版本库的文件夹中执行初始化命令

git init 
git remote add origin <你的项目地址>

3、执行以下命令提交本地代码

git pull origin master
git add .
git commit -m "first commit"
git push origin master

用AES加密时出现”java.security.InvalidKeyException: Illegal key size”异常

用AES加密时出现”java.security.InvalidKeyException: Illegal key size”异常。

原因:由于美国政策的原因,强加密技术是不允许出口给某些国家,所以sun只能在jdk里面实现一些加密,甚至加密强度比较弱,比如AES的密钥只允许128位,为了解锁限制只能下载其他的实现类去替换jdk默认的security provider

解决方法:
JDK7的下载地址: http://www.oracle.com/technetwork/java/javase/downloads/jce-7-download-432124.html
JDK8的下载地址: http://www.oracle.com/technetwork/java/javase/downloads/jce8-download-2133166.html
下载后解压,可以看到local_policy.jar和US_export_policy.jar以及readme.txt
如果安装了JRE,将两个jar文件放到%JRE_HOME%\lib\security目录下覆盖原来的文件
如果安装了JDK,还要将两个jar文件也放到%JDK_HOME%\jre\lib\security目录下覆盖原来文件

Mac Android Studio离线配置gradle

Android Studio每次大更新,基本Gradle也需要大更新。每次通过studio都下载半天,甚至下载不了。所以通过在Gradle官网下载并手动配置,几分钟就可以搞定。

Gradle官网下载

1、首先要知道你需要下载的Gradle版本号。

在项目目录下,找到该文件:

2、下载gradle-4.1-all.zip之后,Copy 到Gradle所在的目录。

这里推荐用命令行直接打开:

#open .gradle

把gradle-4.1-all.zip直接放到gradle-4.1-all目录下的那一长串目录下。然后重启Android Studio就可以了。

JedisCluster应用详解

Redis集群的搭建参考Redis Cluster安装配置

本文主要介绍JedisCluster客户端结合spring注解的使用。

1、rediscluster.properties

#redis的服务器地址
redis.host=172.18.209.168
#redis的服务端口
redis.port=6379
#密码
redis.password=×××××××
#最大空闲数
redis.maxIdle=100
#最大连接数
redis.maxActive=300
#最大建立连接等待时间
redis.maxWait=1000
#客户端超时时间单位是毫秒
redis.timeout=100000
redis.maxTotal=1000
redis.minIdle=8
#明是否在从池中取出连接前进行检验,如果检验失败,则从池中去除连接并尝试取出另一个
redis.testOnBorrow=true
#连接耗尽时是否阻塞  false报异常,ture阻塞直到超时
redis.blockWhenExhausted=false
#当设置为true,且服务开启的jmx服务时,使用jconsole等工具将看到连接池的状态
redis.jmxEnabled=true
#向连接池归还链接时,是否检测链接对象的有效性
redis.testOnReturn=false
#对象空闲多久后逐出,当空闲时间>该值 且 空闲连接>最大空闲数 时直接逐出
redis.softMinEvictableIdleTimeMillis=10000

#jediscluster
cluster1.host.port=172.18.209.168:7001
cluster2.host.port=172.18.209.168:7002
cluster3.host.port=172.18.209.168:7003
cluster4.host.port=172.18.209.169:7004
cluster5.host.port=172.18.209.169:7005
cluster6.host.port=172.18.209.169:7006

#rediscluster
spring.redis.cluster.nodes=172.18.209.168:7001,172.18.209.168:7002,172.18.209.168:7003,172.18.209.169:7004,172.18.209.169:7005,172.18.209.169:7006
spring.redis.cluster.max-redirects=3  

2、spring-redis.xml

<bean id="propertyConfigurer"
      class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
    <property name="location" value="classpath:rediscluster.properties" />
</bean>

<!-- 基础参数配置 -->
<bean id="jedisClusterPoolConfig" class="redis.clients.jedis.JedisPoolConfig">
    <!-- 连接池最大允许连接数 -->
    <property name="maxTotal" value="${redis.maxTotal}"/>
    <!-- 对象空闲多久后逐出,当空闲时间>该值 且 空闲连接>最大空闲数 时直接逐出 -->
    <property name="softMinEvictableIdleTimeMillis" value="${redis.softMinEvictableIdleTimeMillis}"/>
    <!-- 向连接池归还链接时,是否检测链接对象的有效性 -->
    <property name="testOnReturn" value="${redis.testOnReturn}"/>
    <!-- 当设置为true,且服务开启的jmx服务时,使用jconsole等工具将看到连接池的状态 -->
    <property name="jmxEnabled" value="${redis.jmxEnabled}"/>
    <!-- 连接耗尽时是否阻塞  false报异常,ture阻塞直到超时 -->
    <property name="blockWhenExhausted" value="${redis.blockWhenExhausted}"/>
    <!--最大空闲数-->
    <property name="maxIdle" value="${redis.maxIdle}" />
    <!--最大建立连接等待时间-->
    <property name="maxWaitMillis" value="${redis.maxWait}" />
    <!--是否在从池中取出连接前进行检验,如果检验失败,则从池中去除连接并尝试取出另一个-->
    <property name="testOnBorrow" value="${redis.testOnBorrow}" />
</bean>

<bean id="resourcePropertySource" class="org.springframework.core.io.support.ResourcePropertySource">
    <constructor-arg name="name" value="rediscluster.properties"/>
    <constructor-arg name="resource" value="classpath:rediscluster.properties"/>
</bean>

<bean id="redisClusterConfiguration" class="org.springframework.data.redis.connection.RedisClusterConfiguration">
    <constructor-arg name="propertySource" ref="resourcePropertySource"/>
</bean>

<!-- JedisCluster -->
<bean id="jedisCluster" class="*.*.jedis.JedisClusterFactory">
    <property name="addressConfig" value="classpath:rediscluster.properties" />
    <property name="addressKeyPrefix" value="cluster" />
    <property name="timeout" value="300000" />
    <property name="maxRedirections" value="6" />
    <property name="genericObjectPoolConfig" ref="jedisClusterPoolConfig" />
</bean>

<!-- 配置redis connection -->
<bean id="redisConnectionFactory" class="org.springframework.data.redis.connection.jedis.JedisConnectionFactory">
    <constructor-arg ref="redisClusterConfiguration"/>
    <constructor-arg ref="jedisClusterPoolConfig"/>
</bean>

<!-- 数据以字符串存储  -->
<bean id="stringRedisSerializer" class="org.springframework.data.redis.serializer.StringRedisSerializer"/>

<!-- 数据以字节流存储 -->
<bean id="jdkRedisSerializer" class="org.springframework.data.redis.serializer.JdkSerializationRedisSerializer"/>

<!--  redis String类型 访问模版本 -->
<bean id="stringRedisTemplate" class="org.springframework.data.redis.core.StringRedisTemplate">
    <property name="connectionFactory" ref="redisConnectionFactory"/>
</bean>

<!-- redis 访问模版 -->
<bean id="redisTemplate" class="org.springframework.data.redis.core.RedisTemplate">
    <property name="connectionFactory" ref="redisConnectionFactory"/>
    <!-- 在hash数据结构中,hash-key的序列化策略 -->
    <property name="hashKeySerializer" ref="stringRedisSerializer"/>
    <!-- 对于普通K-V操作时,key采取的序列化策略 -->
    <property name="keySerializer" ref="stringRedisSerializer"/>
    <!-- value采取的序列化策略 -->
    <property name="valueSerializer" ref="jdkRedisSerializer"/>
</bean>

3、JedisClusterFactory.java

public class JedisClusterFactory implements FactoryBean, InitializingBean {

    private Resource addressConfig;
    private String addressKeyPrefix;

    private JedisCluster jedisCluster;
    private Integer timeout;
    private Integer maxRedirections;
    private GenericObjectPoolConfig genericObjectPoolConfig;

    private Pattern p = Pattern.compile("^.+[:]\\d{1,5}\\s*$");

    @Override
    public JedisCluster getObject() throws Exception {
        return jedisCluster;
    }

    @Override
    public Class<? extends JedisCluster> getObjectType() {
        return (this.jedisCluster != null ? this.jedisCluster.getClass() : JedisCluster.class);
    }

    @Override
    public boolean isSingleton() {
        return true;
    }

    private Set parseHostAndPort() throws Exception {
        try {
            Properties prop = new Properties();
            prop.load(this.addressConfig.getInputStream());
            Set haps = new HashSet<>();
            for (Object key : prop.keySet()) {
                if (!((String) key).startsWith(addressKeyPrefix)) {
                    continue;
                }
                String val = (String) prop.get(key);
                boolean isIpPort = p.matcher(val).matches();
                if (!isIpPort) {
                    throw new IllegalArgumentException("ip 或 port 不合法");
                }
                String[] ipAndPort = val.split(":");
                HostAndPort hap = new HostAndPort(ipAndPort[0], Integer.parseInt(ipAndPort[1]));
                haps.add(hap);
            }

            return haps;
        } catch (IllegalArgumentException ex) {
            throw ex;
        } catch (Exception ex) {
            throw new Exception("解析jedis配置文件失败", ex);
        }
    }

    @Override
    public void afterPropertiesSet() throws Exception {
        Set haps = this.parseHostAndPort();
        jedisCluster = new JedisCluster(haps, timeout, maxRedirections, genericObjectPoolConfig);
    }

    public void setAddressConfig(Resource addressConfig) {
        this.addressConfig = addressConfig;
    }

    public void setTimeout(int timeout) {
        this.timeout = timeout;
    }

    public void setMaxRedirections(int maxRedirections) {
        this.maxRedirections = maxRedirections;
    }

    public void setAddressKeyPrefix(String addressKeyPrefix) {
        this.addressKeyPrefix = addressKeyPrefix;
    }

    public void setGenericObjectPoolConfig(GenericObjectPoolConfig genericObjectPoolConfig) {
        this.genericObjectPoolConfig = genericObjectPoolConfig;
    }
}

redis requires Ruby version >= 2.2.2

在做redis集群,安装redis-gem的时候出现该错误:

#gem install -l redis-4.0.0.gem
ERROR:  Error installing redis-4.0.0.gem:
redis requires Ruby version >= 2.2.2.

原来是因为CentOS7 yum库中ruby的版本支持到 2.0.0,可gem 安装redis需要最低是2.2.2。那么只能手动更新了。
解决步骤:

1、安装RVM

gpg2 --keyserver hkp://keys.gnupg.net --recv-keys D39DC0E3
curl -L get.rvm.io | bash -s stable
source /usr/local/rvm/scripts/rvm

2、安装Ruby新版本

rvm install 2.3.3

3、设置默认使用新版本

rvm use 2.3.3 --default

4、卸载旧版本

rvm remove 2.0.0