-
Notifications
You must be signed in to change notification settings - Fork 0
/
content.json
1 lines (1 loc) · 321 KB
/
content.json
1
[{"title":"Vue 简介","date":"2018-07-09T09:43:56.651Z","path":"2018/07/09/vue_01/","text":"一、前端三大框架介绍 angular 09年诞生、Google公司 它的目的就是让我们开发单页面更加方便 它最主要的就是为前端带来了MVVM开发模式 MVVM(数据驱动模型),不需要操作DOM react(目前使用排名第一) Facebook公司开发的一个web框架 组件化 vue(在中国一些中小型公司用的比较多) 2014年2月,作者:尤雨溪(中国人) 早期由个人开发 借鉴angular与 react之所长,后起之秀 二、网站开发方式 传统开发方式 页面与服务端在一起,这种项目服务端比重大,因为绝大多数都是服务端技术 绝大多数网站都是这种方式开发 前后端分离 服务端只处理数据,不关心页面,只提供接口 传统开发方式页面与数据糅合在一起,很难维护 把服务端处理视图的业务转移到了客户 由原来的胖服务器变成瘦服务器,瘦客户端变成胖客户端 也是当前主流的方式 三、vue是什么 一款非常优秀的前端JavaScript框架 可以轻松的创建 SPA(Single Page Application) 应用程序 通过 指令 扩展了html,通过 表达式绑定到HTML 最大程度上解放了DOM操作 Vue 的特点: MVVM 双向数据绑定 组件化 渐进式","tags":[{"name":"Vue","slug":"Vue","permalink":"https://huankai.github.io/tags/Vue/"}]},{"title":"browsersync(浏览器同步工具)","date":"2018-07-09T09:43:56.641Z","path":"2018/07/09/Browsersync/","text":"配置使用 browsersync() 一、介绍官网地址:http://www.browsersync.cn/ Browsersync能让浏览器实时、快速响应您的文件更改(html、js、css、sass、less等)并自动刷新页面。更重要的是 Browsersync可以同时在PC、平板、手机等设备下进项调试。有了它,您不用在多个浏览器、多个设备间来回切换,频繁的刷新页面。更神奇的是您在一个浏览器中滚动页面、点击等行为也会同步到其他浏览器和设备中,这一切还可以通过可视化界面来控制。 1.1下载与安装:安装Node.js : http://nodejs.cn/download/ 安装 Browsersync : 全局安装: 打开终端执行 npm install -g browser-sync 在指定项目下安装:进入指定项目的根目录,执行 npm install –save-dev browser-sync 或执行npm i -D browser-sync ,安装完成后,会在项目根目录下创建 node_modules文件目录并生成安装的文件,还会在根目录下的 package.json 文件中添加如下内容:12345...\"devDependencies\": { \"browser-sync\": \"^2.23.6\" }... 上面 devDependencies 标签表示需要依赖的库,此文件中还有dependencies 标签也是依赖的库,两者区别就是 devDependencies 中所依赖的库并不是项目运行必须的,没有这些库项目也能运行起来,dependencies则是项目运行必须的库,不能少。 在 package.json文件中 scripts 标签添加如下内容:12345\"scripts\": { \"dev\":\"browser-sync start --server --files \\\"*.html,js/*.js,*.css\", \"start\":\"npm run dev\" } dev:此名称可以随便定义 ,dev的值表示使用browser-sync启动,并监听根目录下的所有html 、js/.js 、 .css 的内容,如果有修改,浏览器将自动刷新。 start :是npm 固定的一个参数,值的意思就是使用npm 运行定义的 dev ,其实这个可以不定义,如果不定义,使用 npm 运行时dev时就必须加入 run 指令:语法为: npm run dev,如果定义了,就可以以 start 的方式启动,如 : npm start ,执行这个命令和执行 npm run dev 一样,也和 npm run start 一样,简单的说,使用start可以省略 run , start就是dev的一个别名。 browser-sync 其它参数请查看: http://www.browsersync.cn/docs/options/","tags":[{"name":"Vue","slug":"Vue","permalink":"https://huankai.github.io/tags/Vue/"}]},{"title":"Redis 主从复制","date":"2018-07-09T09:43:35.113Z","path":"2018/07/09/Redis_06_主从复制(哨兵机制)/","text":"redis复制的非常重要特性(摘自官网): 一个Master可以有多个Slaves Slaves能过接受其他slave的链接,除了可以接受同一个master下面slaves的链接以外,还可以接受同一个结构图中的其他slaves的链接 redis复制是在master段是非阻塞的,这就意味着master在同一个或多个slave端执行同步的时候还可以接受查询 复制在slave端也是非阻塞的,假设你在redis.conf中配置redis这个功能,当slave在执行的新的同步时,它仍可以用旧的数据信息来提供查询,否则,你可以配置当redis slaves去master失去联系时,slave会给发送一个客户端错误 为了有多个slaves可以做只读查询,复制可以重复2次,甚至多次,具有可扩展性 通过复制可以避免master全量写硬盘的消耗:只要配置 master 的配置文件redis.conf来“避免保存”(注释掉所有”save”命令),然后连接一个用来持久化数据的slave即可。但是这样要确保masters 不会自动重启。 一主两从配置Redis主从复制:服务如下 master : 192.168.1.90 6379slaveof : 192.168.1.90 6380slaveof : 192.168.1.90 6381 1、 创建配置文件创建 redis_6380.conf 与 redis_6381.conf配置文件12345678910111213[root@huangkai conf]# pwd/usr/local/redis-4.0.1/conf[root@huangkai conf]# lltotal 60-rw-r--r--. 1 root root 57777 Oct 29 02:17 redis.conf[root@huangkai conf]# cp redis.conf redis_6380.conf[root@huangkai conf]# cp redis.conf redis_6381.conf [root@huangkai conf]# lltotal 180-rw-r--r--. 1 root root 57777 Nov 6 21:44 redis_6380.conf-rw-r--r--. 1 root root 57777 Nov 6 21:44 redis_6381.conf-rw-r--r--. 1 root root 57777 Oct 29 02:17 redis.conf[root@huangkai conf]# 2、修改配置文件redis.conf默认配置不用修改。 redis_6380.conf修改如下:1234567...port 6380pidfile /var/run/redis_6380.pidlogfile \"redis_6380.log\"dbfilename dump_6380.rdb# appendfilename \"appendonly_6380.aof\" appendonly持久化未开启,可不修改... redis_6381.conf修改如下:1234567...port 6381pidfile /var/run/redis_6381.pidlogfile \"redis_6381.log\"dbfilename dump_6381.rdb# appendfilename \"appendonly_6381.aof\" appendonly持久化未开启,可不修改... 3、启动三台Redis服务启动 6379 123456789101112[root@huangkai conf]# redis-server ./redis.conf 948:C 06 Nov 21:52:07.639 # oO0OoO0OoO0Oo Redis is starting oO0OoO0OoO0Oo948:C 06 Nov 21:52:07.639 # Redis version=4.0.1, bits=64, commit=00000000, modified=0, pid=948, just started948:C 06 Nov 21:52:07.639 # Configuration loaded[root@huangkai conf]# netstat -an|grep 6379tcp 0 0 0.0.0.0:6379 0.0.0.0:* LISTEN tcp6 0 0 :::6379 :::* LISTEN [root@huangkai conf]# [root@huangkai ~]# redis-cli -p 6379127.0.0.1:6379> 127.0.0.1:6379> 启动 6380123456789[root@huangkai conf]# redis-server ./redis_6380.conf [root@huangkai conf]# netstat -an|grep 6380tcp 0 0 0.0.0.0:6380 0.0.0.0:* LISTEN tcp6 0 0 :::6380 :::* LISTEN [root@huangkai conf]# [root@huangkai ~]# redis-cli -p 6380127.0.0.1:6380> 127.0.0.1:6380> 启动 638112345678910111213[root@huangkai conf]# redis-server ./redis_6381.conf [root@huangkai conf]# netstat -an|grep 6381 tcp 0 0 0.0.0.0:6381 0.0.0.0:* LISTEN tcp6 0 0 :::6381 :::* LISTEN [root@huangkai conf]#[root@huangkai ~]# redis-cli -p 6381127.0.0.1:6381> 127.0.0.1:6381> ``` 现在,三台服务已启动,并且都成功连接上,使用 ``INFO REPLICATION`` 命令查看信息如下:6379: 127.0.0.1:6379> INFO REPLICATION Replicationrole:masterconnected_slaves:0master_replid:d7f13a179d3808311c9dc4467a7a68619d3bf5b8master_replid2:0000000000000000000000000000000000000000master_repl_offset:0second_repl_offset:-1repl_backlog_active:0repl_backlog_size:1048576repl_backlog_first_byte_offset:0repl_backlog_histlen:0127.0.0.1:6379>126380: 127.0.0.1:6380> INFO REPLICATION Replicationrole:masterconnected_slaves:0master_replid:564db58a73b1dd19d262bd823bd590faaacec50emaster_replid2:0000000000000000000000000000000000000000master_repl_offset:0second_repl_offset:-1repl_backlog_active:0repl_backlog_size:1048576repl_backlog_first_byte_offset:0repl_backlog_histlen:0127.0.0.1:6380>126381: 127.0.0.1:6381> INFO REPLICATION Replicationrole:masterconnected_slaves:0master_replid:24a3159635d19972c822f86f024dc03fbad9c65dmaster_replid2:0000000000000000000000000000000000000000master_repl_offset:0second_repl_offset:-1repl_backlog_active:0repl_backlog_size:1048576repl_backlog_first_byte_offset:0repl_backlog_histlen:0127.0.0.1:6381>12从上面的信息可以看出,三台服务的 角色都 为 master,连接的 slaves为0,也就是这三台服务现在还没有任何关系,在任意一台服务中设置值,其它两台都不会同步数据。如:在 6379 设置 k1 为 k1 ,如下 127.0.0.1:6379> SET k1 k1OK127.0.0.1:6379> get k1“k1”127.0.0.1:6379>126380 获取k1的值,返回无结果,如下: 127.0.0.1:6380> get k1(nil)127.0.0.1:6380>126381 获取k1的值,返回也无结果,如下: 127.0.0.1:6381> get k1(nil)127.0.0.1:6381>12345678## 4、设置主从 ##### 4.1、最简单的主从配置 ###现在将 6379设置为master, 6380 与 6381 设置为slave,配置如下:6380: 127.0.0.1:6380> SLAVEOF 192.168.1.90 6379OK127.0.0.1:6380>1236381: 127.0.0.1:6381> SLAVEOF 192.168.1.90 6379OK127.0.0.1:6381>12345查看各服务信息:6379:由下可知: 此服务角色为 master,现在有两个连接的 slave,分别为 192.168.1.90:6380 与 192.168.1.90:6381,并且都为在线状态。 127.0.0.1:6379> INFO REPLICATION Replicationrole:masterconnected_slaves:2slave0:ip=192.168.1.90,port=6380,state=online,offset=112,lag=1slave1:ip=192.168.1.90,port=6381,state=online,offset=112,lag=0master_replid:7499fc3cfd327ecb9bf72c79e53cb5014f14230cmaster_replid2:0000000000000000000000000000000000000000master_repl_offset:112second_repl_offset:-1repl_backlog_active:1repl_backlog_size:1048576repl_backlog_first_byte_offset:1repl_backlog_histlen:112127.0.0.1:6379>1236380:由下可以,此时此服务的角色已变为 slave,它的master 主机为 192.168.1.90,端口号为 6379。 127.0.0.1:6380> INFO REPLICATION Replicationrole:slavemaster_host:192.168.1.90master_port:6379master_link_status:upmaster_last_io_seconds_ago:6master_sync_in_progress:0slave_repl_offset:140slave_priority:100slave_read_only:1connected_slaves:0master_replid:7499fc3cfd327ecb9bf72c79e53cb5014f14230cmaster_replid2:0000000000000000000000000000000000000000master_repl_offset:140second_repl_offset:-1repl_backlog_active:1repl_backlog_size:1048576repl_backlog_first_byte_offset:1repl_backlog_histlen:140127.0.0.1:6380>1236381:由下可以,此时此服务的角色也变为了 slave,它的master 主机为 192.168.1.90,端口号为 6379。 127.0.0.1:6381> INFO REPLICATION Replicationrole:slavemaster_host:192.168.1.90master_port:6379master_link_status:upmaster_last_io_seconds_ago:1master_sync_in_progress:0slave_repl_offset:168slave_priority:100slave_read_only:1connected_slaves:0master_replid:7499fc3cfd327ecb9bf72c79e53cb5014f14230cmaster_replid2:0000000000000000000000000000000000000000master_repl_offset:168second_repl_offset:-1repl_backlog_active:1repl_backlog_size:1048576repl_backlog_first_byte_offset:15repl_backlog_histlen:154127.0.0.1:6381>123之前在还没有配置slave时,我们在6379服务中配置了 k1 值,此时主从复制配置成功后, 6380与 6381是否会有 6379中配置的值呢?使用命令查看一下: 127.0.0.1:6380> get k1“k1”127.0.0.1:6380>12 127.0.0.1:6381> get k1“k1”127.0.0.1:6381>1234567如上,可以看到 6380 与 6381 都能获取到值。**结论一:配置主从后,从服务器(slave)会复制主服务(master)的所有值。**### 4.2、从服务器设置值 ###现在,我们在slave服务器上设置值,看看会出现什么情况 :6380: 127.0.0.1:6380> set k2 k2(error) READONLY You can’t write against a read only slave.127.0.0.1:6380>16381: 127.0.0.1:6381> set k3 v3(error) READONLY You can’t write against a read only slave.127.0.0.1:6381>1234567如上,在从服务器上设置值,抛出了一个错误,意思是 slave只能读取数据,不能写数据。**结论二:配置主从后,所有的写操作都只能在主服务器(master)进行,从服务器(slave)只能读数据不能写数据。如果需要在从服务器中支持写功能,可以在需要写的slave执行命令 ``config set slave-read-only no`` ,也就是使当前slave支持写功能。但是此种配置后,写入的数据只在当前slave中存在,不会同步其它的slave,更不会同步到master。 **### 4.3、主服务器宕机 ###现在,我们将主服务器(master(6379))停止服务,查看变化 127.0.0.1:6379> SHUTDOWNnot connected>1查看 6380 如下: 127.0.0.1:6380> info replication Replicationrole:slavemaster_host:192.168.184.128master_port:6379master_link_status:downmaster_last_io_seconds_ago:-1master_sync_in_progress:0slave_repl_offset:854master_link_down_since_seconds:26slave_priority:100slave_read_only:1connected_slaves:0master_replid:ca0c52c31eefe3567c0beb099b83e174080e054emaster_replid2:0000000000000000000000000000000000000000master_repl_offset:854second_repl_offset:-1repl_backlog_active:1repl_backlog_size:1048576repl_backlog_first_byte_offset:1repl_backlog_histlen:854127.0.0.1:6380>1查看 6381 如下: 127.0.0.1:6381> info replication Replicationrole:slavemaster_host:192.168.184.128master_port:6379master_link_status:downmaster_last_io_seconds_ago:-1master_sync_in_progress:0slave_repl_offset:854master_link_down_since_seconds:31slave_priority:100slave_read_only:1connected_slaves:0master_replid:ca0c52c31eefe3567c0beb099b83e174080e054emaster_replid2:0000000000000000000000000000000000000000master_repl_offset:854second_repl_offset:-1repl_backlog_active:1repl_backlog_size:1048576repl_backlog_first_byte_offset:15repl_backlog_histlen:840127.0.0.1:6381>1234567可以发现,slave并没有进行选举产生出master,现在它们的角色依然还是 slave。**结论三:当从服务器``SHUTDOWN``后,slave并没有通过选举产生新的master,此时会导致整个服务不可写。所以,在使用此种方式时,一定要将 master持久化,如果 master通过一个空数据集重启,slave中的数据也将被清空。**### 4.4、主服务器正常运行 ###现在启动 master,查看变化 not connected> quit[root@huangkai ~]# cd /usr/local/redis-4.0.1/conf/[root@huangkai conf]# redis-server ./redis.conf2196:C 07 Nov 11:44:58.813 # oO0OoO0OoO0Oo Redis is starting oO0OoO0OoO0Oo2196:C 07 Nov 11:44:58.813 # Redis version=4.0.1, bits=64, commit=00000000, modified=0, pid=2196, just started2196:C 07 Nov 11:44:58.813 # Configuration loaded[root@huangkai conf]#12连接 6379,查看信息如下: [root@huangkai conf]# redis-cli -p 6379127.0.0.1:6379> info replication Replicationrole:masterconnected_slaves:2slave0:ip=127.0.0.1,port=6381,state=online,offset=910,lag=0slave1:ip=127.0.0.1,port=6380,state=online,offset=910,lag=0master_replid:3fe903a1fab24acafb159f837c6bb1b7e1d423a5master_replid2:0000000000000000000000000000000000000000master_repl_offset:910second_repl_offset:-1repl_backlog_active:1repl_backlog_size:1048576repl_backlog_first_byte_offset:855repl_backlog_histlen:56127.0.0.1:6379> set k2 v2OK127.0.0.1:6379>126380 信息如下: 127.0.0.1:6380> info replication Replicationrole:slavemaster_host:192.168.184.128master_port:6379master_link_status:upmaster_last_io_seconds_ago:5master_sync_in_progress:0slave_repl_offset:924slave_priority:100slave_read_only:1connected_slaves:0master_replid:3fe903a1fab24acafb159f837c6bb1b7e1d423a5master_replid2:0000000000000000000000000000000000000000master_repl_offset:924second_repl_offset:-1repl_backlog_active:1repl_backlog_size:1048576repl_backlog_first_byte_offset:855repl_backlog_histlen:70127.0.0.1:6380> get k2“v2”127.0.0.1:6380> 126381 信息如下: 127.0.0.1:6381> info replication Replicationrole:slavemaster_host:192.168.184.128master_port:6379master_link_status:upmaster_last_io_seconds_ago:0master_sync_in_progress:0slave_repl_offset:938slave_priority:100slave_read_only:1connected_slaves:0master_replid:3fe903a1fab24acafb159f837c6bb1b7e1d423a5master_replid2:0000000000000000000000000000000000000000master_repl_offset:938second_repl_offset:-1repl_backlog_active:1repl_backlog_size:1048576repl_backlog_first_byte_offset:855repl_backlog_histlen:84127.0.0.1:6381> get k2“v2”127.0.0.1:6381>12345678910通过以上信息可知,当 master 重新恢复后,整个服务都可正常使用。### 4.5、既主既从 ###现在将 6380 设置为 6379的 slave ,并且设置为 6381的 master6379 不需要修改配置将6380设置为6379的slave如下: 127.0.0.1:6380> SLAVEOF 162.168.184.128 6379OK127.0.0.1:6380>12将6381设置为6380的 slave 如下: 127.0.0.1:6381> SLAVEOF 162.168.184.128 6380OK127.0.0.1:6381>12查看 6379信息如下: 127.0.0.1:6379> info replication Replicationrole:masterconnected_slaves:1slave0:ip=127.0.0.1,port=6380,state=online,offset=9180,lag=1master_replid:3fe903a1fab24acafb159f837c6bb1b7e1d423a5master_replid2:0000000000000000000000000000000000000000master_repl_offset:9180second_repl_offset:-1repl_backlog_active:1repl_backlog_size:1048576repl_backlog_first_byte_offset:855repl_backlog_histlen:8326127.0.0.1:6379>1查看 6380信息如下: 127.0.0.1:6380> info replication Replicationrole:slavemaster_host:192.168.184.128master_port:6379master_link_status:upmaster_last_io_seconds_ago:9master_sync_in_progress:0slave_repl_offset:9180slave_priority:100slave_read_only:0connected_slaves:0master_replid:3fe903a1fab24acafb159f837c6bb1b7e1d423a5master_replid2:0000000000000000000000000000000000000000master_repl_offset:9180second_repl_offset:-1repl_backlog_active:1repl_backlog_size:1048576repl_backlog_first_byte_offset:855repl_backlog_histlen:8326127.0.0.1:6380>1查看 6381 信息如下: 127.0.0.1:6381> info replication Replicationrole:slavemaster_host:162.168.184.128master_port:6380master_link_status:upmaster_last_io_seconds_ago:-1master_sync_in_progress:0slave_repl_offset:9166master_link_down_since_seconds:1510032310slave_priority:100slave_read_only:1connected_slaves:0master_replid:3fe903a1fab24acafb159f837c6bb1b7e1d423a5master_replid2:0000000000000000000000000000000000000000master_repl_offset:9166second_repl_offset:-1repl_backlog_active:1repl_backlog_size:1048576repl_backlog_first_byte_offset:855repl_backlog_histlen:8312127.0.0.1:6381>123456通过以上可知 :6379 角色为 master,slave 只有一个,为 63806380 角色为 slave,master 为 63796380 角色为 slave,master 为 63806379 设置值 : 127.0.0.1:6379> set k3 v3OK127.0.0.1:6379>126380 获取值 : 127.0.0.1:6380> get k3“v3”127.0.0.1:6380>126381 获取值 : 127.0.0.1:6381> get k3“v3”127.0.0.1:6381>12345678910111213141516171819由上可知,此种方式配置,也会同步 6381 服务的数据。### 4.6、反从为主 ###### 4.7、redis哨兵模式(sentinel) ###Redis 的 Sentinel 用于管理多个 Redis 服务器(instance), 该系统执行以下三个任务:- 监控(Monitoring): Sentinel 会不断地检查你的主服务器和从服务器是否运作正常。- 提醒(Notification): 当被监控的某个 Redis 服务器出现问题时, Sentinel 可以通过 API 向管理员或者其他应用程序发送通知。- 自动故障迁移(Automatic failover): 当一个主服务器不能正常工作时, Sentinel 会开始一次自动故障迁移操作, 它会将失效主服务器的其中一个从服务器升级为新的主服务器, 并让失效主服务器的其他从服务器改为复制新的主服务器; 当客户端试图连接失效的主服务器时, 集群也会向客户端返回新主服务器的地址, 使得集群可以使用新主服务器代替失效服务器。可以这样说,Redis的sentinel就是 [反从为主](#4.6-)的自动档。#### 4.7.1、sentinel 配置文件: ####安装完redis后,在redis根目录下,会有sentinel.conf 配置文件,该配置文件参数如下: #配置sentinel端口号,默认为 26379 port 26379 #sentinel目录 dir /tmp告诉sentinel去监听地址为ip:port的一个master,这里的master-name可以自定义,quorum是一个数字,#指明当有多少个sentinel认为一个master失效时,master才算真正失效。 #master-name只能包含英文字母,数字和“.-_”这三种字符,需要注意的是master-ip 要写真实的ip地址而不要用回环地址(127.0.0.1) #配置示例:sentinel monitor mymaster 192.168.0.5 6379 1 sentinel monitor #设置连接master和slave时的密码,注意的是sentinel不能分别为master和slave设置不同的密码,因此master和slave的密码应该设置相同。配置示例:sentinel auth-pass mymaster 0123passw0rd sentinel auth-pass #指定需要多少失效时间,一个master才会被这个sentinel主观地认为是不可用的。 单位是毫秒,默认为30秒 #配置示例: sentinel down-after-milliseconds mymaster 30000 sentinel down-after-milliseconds #指定在发生failover(主备切换时)最多可以有多少个slave同时对新的master进行同步,这个数字越小,完成failover所需的时间就越长,但是如果这个数字越大,就意味着越多的slave因为replication而不可用。可以通过将这个值设为 1 来保证每次只有一个slave 处于不能处理命令请求的状态。 #配置示例: sentinel parallel-syncs mymaster 1 sentinel parallel-syncs #failover-timeout 可以用在以下这些方面: 同一个sentinel对同一个master两次failover之间的间隔时间。 当一个slave从一个错误的master那里同步数据开始计算时间。直到slave被纠正为向正确的master那里同步数据时。 当想要取消一个正在进行的failover所需要的时间。 当进行failover时,配置所有slaves指向新的master所需的最大时间。不过,即使过了这个超时,slaves依然会被正确配置为指向master,但是就不按parallel-syncs所配置的规则来了。配置示例:sentinel failover-timeout mymaster1 20000sentinel failover-timeout sentinel的notification-script和reconfig-script是用来配置当某一事件发生时所需要执行的脚本,可以通过脚本来通知管理员,例如当系统运行不正常时发邮件通知相关人员。对于脚本的运行结果有以下规则:1.若脚本执行后返回1,那么该脚本稍后将会被再次执行,重复次数目前默认为102.若脚本执行后返回2,或者比2更高的一个返回值,脚本将不会重复执行。如果脚本在执行过程中由于收到系统中断信号被终止了,则同返回值为1时的行为相同。一个脚本的最大执行时间为60s,如果超过这个时间,脚本将会被一个SIGKILL信号终止,之后重新执行。 sentinel notification-script 通知型脚本:当sentinel有任何警告级别的事件发生时(比如说redis实例的主观失效和客观失效等等),将会去调用这个脚本,这时这个脚本应该通过邮件,SMS等方式去通知系统管理员关于系统不正常运行的信息。调用该脚本时,将传给脚本两个参数,一个是事件的类型,一个是事件的描述。如果sentinel.conf配置文件中配置了这个脚本路径,那么必须保证这个脚本存在于这个路径,并且是可执行的,否则sentinel无法正常启动成功。配置示例:sentinel notification-script mymaster /var/redis/notify.sh sentinel client-reconfig-script 当一个master由于failover而发生改变时,这个脚本将会被调用,通知相关的客户端关于master地址已经发生改变的信息。以下参数将会在调用脚本时传给脚本: 目前总是“failover”, 是“leader”或者“observer”中的一个。 参数 from-ip, from-port, to-ip, to-port是用来和旧的master和新的master(即旧的slave)通信的。这个脚本应该是通用的,能被多次调用,不是针对性的。配置示例:sentinel client-reconfig-script mymaster /var/redis/reconfig.sh123456789101112131415161718#### 4.7.2、一主双从三sentinel ####通过配置一主双从三sentinel实现redis 监控与自动故障迁移。ip地址分配如下:master:192.168.1.90 6379slave1:192.168.1.90 6380slave2:192.168.1.90 6381sentinel1 : 192.168.1.90 26379sentinel2 : 192.168.1.90 26389sentinel3 : 192.168.1.90 26399一主三从配置见 [修改配置文件](#2-)sentinel配置如下:sentinel1: #将配置文件复制到conf目录,保留出厂配置[root@huangkai redis-4.0.1]#cp sentinel.conf conf/ #修改配置文件,内容如下 port 26379 #端口号,daemonize yes # 是否以守护进程启动,默认此文件中没有此配置,默认值为 no,如果不添加,启动sentinel时,进程会直接在前台跑,一退出sentinel进程就关了,还一种方式是以 nohub 来启动logfile “/var/log/sentinel_26379.log” #日志文件12sentinel2: [root@huangkai conf]# cp sentinel.conf sentinel_26389.conf[root@huangkai conf]# #修改配置文件,内容如下port 26389 #端口号,daemonize yeslogfile “/var/log/sentinel_26389.log”12sentinel3: [root@huangkai conf]# cp sentinel.conf sentinel_26399.conf[root@huangkai conf]# #修改配置文件,内容如下port 26399 #端口号,daemonize yeslogfile “/var/log/sentinel_26399.log”12启动 sentinel1 [root@huangkai conf]# redis-sentinel ./sentinel.conf[root@huangkai conf]# #连接[root@huangkai ~]# redis-cli -p 26379127.0.0.1:26379> sentinel master mymaster 1) “name” 2) “mymaster” 3) “ip” 4) “127.0.0.1” 5) “port” 6) “6379” 7) “runid” 8) “334ab6dba2c2e236929d925fc21298649736bcae” 9) “flags”10) “master”11) “link-pending-commands”12) “0”13) “link-refcount”14) “1”15) “last-ping-sent”16) “0”17) “last-ok-ping-reply”18) “876”19) “last-ping-reply”20) “876”21) “down-after-milliseconds”22) “30000”23) “info-refresh”24) “1992”25) “role-reported”26) “master”27) “role-reported-time”28) “674576”29) “config-epoch”30) “0”31) “num-slaves”32) “2”33) “num-other-sentinels”34) “0”35) “quorum”36) “2”37) “failover-timeout”38) “180000”39) “parallel-syncs”40) “1”127.0.0.1:26379>12启动 sentinel2 [root@huangkai conf]# redis-sentinel ./sentinel_26389.conf #连接[root@huangkai ~]# redis-cli -p 26389127.0.0.1:26389> sentinel master mymaster 1) “name” 2) “mymaster” 3) “ip” 4) “127.0.0.1” 5) “port” 6) “6379” 7) “runid” 8) “334ab6dba2c2e236929d925fc21298649736bcae” 9) “flags”10) “master”11) “link-pending-commands”12) “0”13) “link-refcount”14) “1”15) “last-ping-sent”16) “0”17) “last-ok-ping-reply”18) “122”19) “last-ping-reply”20) “122”21) “down-after-milliseconds”22) “30000”23) “info-refresh”24) “1972”25) “role-reported”26) “master”27) “role-reported-time”28) “122532”29) “config-epoch”30) “0”31) “num-slaves”32) “2”33) “num-other-sentinels”34) “0”35) “quorum”36) “2”37) “failover-timeout”38) “180000”39) “parallel-syncs”40) “1”127.0.0.1:26389>12启动 sentinel3 [root@huangkai conf]# redis-sentinel ./sentinel_26399.conf #连接[root@huangkai ~]# redis-cli -p 26399127.0.0.1:26399> sentinel master mymaster 1) “name” 2) “mymaster” 3) “ip” 4) “127.0.0.1” 5) “port” 6) “6379” 7) “runid” 8) “334ab6dba2c2e236929d925fc21298649736bcae” 9) “flags”10) “master”11) “link-pending-commands”12) “0”13) “link-refcount”14) “1”15) “last-ping-sent”16) “0”17) “last-ok-ping-reply”18) “442”19) “last-ping-reply”20) “442”21) “down-after-milliseconds”22) “30000”23) “info-refresh”24) “9464”25) “role-reported”26) “master”27) “role-reported-time”28) “119978”29) “config-epoch”30) “0”31) “num-slaves”32) “2”33) “num-other-sentinels”34) “0”35) “quorum”36) “2”37) “failover-timeout”38) “180000”39) “parallel-syncs”40) “1”127.0.0.1:26399>1234到此,一主双从三sentinel配置完成。停止 6379(master),查看变化: 127.0.0.1:6379> info replication Replicationrole:masterconnected_slaves:2slave0:ip=127.0.0.1,port=6381,state=online,offset=133321,lag=1slave1:ip=127.0.0.1,port=6380,state=online,offset=133454,lag=1master_replid:3fe903a1fab24acafb159f837c6bb1b7e1d423a5master_replid2:0000000000000000000000000000000000000000master_repl_offset:133587second_repl_offset:-1repl_backlog_active:1repl_backlog_size:1048576repl_backlog_first_byte_offset:855repl_backlog_histlen:132733127.0.0.1:6379> SHUTDOWNnot connected>` 主从复制的缺点:由于所有的写操作都是在master上进行,然后同步到slave,master复制到 slave会有一定的延时,当系统繁忙时,可能延时会更加严重,slave机器增多也会使这个问题更严重。","tags":[{"name":"Redis","slug":"Redis","permalink":"https://huankai.github.io/tags/Redis/"}]},{"title":"其它改进","date":"2018-07-09T09:43:03.546Z","path":"2018/07/09/10_其它改进/","text":"一、过时的APIJava 9 废弃或者移除了几个不常用的功能,其中最主要的是Applet API,现在是标记为废弃的,随着对安全要求的提高,主流浏览器已取消对Java浏览器插件的支持。HTML5的出现也进一步加速了它的消亡。开发者现在可以使用像Java Web Start这样的技术来代替 Applet,它可以实现从浏览器启动应用程序或者安装应该程序。 同时,appletviewer工具也标记为废弃 二、Java 编译工具改进智能Java编译工具(sjavac (Smart java complier)) 用于在多核处理器下提升JDK编译的速度,并取代了之前的JDK编译工具 javac ,继而成为Java环境默认的通用的智能编译工具。同时,JDK 9 还更新了javac编译工具,才兼容低版本的JDK。 三、统一的JVM日志系统日志是解决问题的唯一有效途径,曾经很难知道导致JVM性能问题和JVM崩溃的原因,不同的JVM的日志碎片化和日志选项(如JVM组件对于日志使用的是不同机制和规则),这使得JVM难以调试。解决该问题最有效的方法:对所有的JVM组件引入一个单一的系统,这些JVM组件支持细粒度和易配置的JVM日志。 四、javadoc的 HTML5生成可生成html5格式的javadoc。 五、javascript 引擎升级(Nashorn)Nashorn 项目在JDK9中得到改进 ,它为Java 提供轻量级的Javascript运行时。Nashorn项目跟随Netscape 的Rhino 项目,目的是为了在java中实现一个高性能、轻量级的Javascript运行时,Nashorn 使得Java能够嵌入到Javascript,它在JDK 8 中为Java提供一个Javasciprt引擎。JDK 9 中包含一个用于解析Nashorn的 ECMAScript 语法树的API,这个API使得IDE 和服务端框架不再需要依赖 Nashorn项目的内部实现类就可以分析 ECMAScript代码。 六、java动态编译器Oracle 一直在努力提高Java启动和运行时的性能,希望其它能够在更多广泛的场景达到或接近本地语言的性能。但是,直到今天,谈到Java,很多C/C++开发者还是会不屑地评价为启动慢、吃内存等。 简单说,这主要是因为Java 编译器产生的类文件是Java虚拟机可以理解的二进制代码,而不是真正可执行的本地代码,需要Java虚拟机解释和编译,这带来了额外的开销。 JIT(Just-in-time) 编辑器可以在运行时将热点编译成本地代码,速度很快。但是Java项目现在变得很大很复杂,因此JIT 编译器需要花费较长时间 才能热身完成,而且有些Java方法还没法编译。性能方面也会下降。AOT 编辑器就是为了解决这些问题而生的 在JDK9中,AOT(AHead of Time Compliation) 作为实验特性被引入进来,开发者可以利用新的 jaotc 工具将重点代码转换成类似库一样的文件。虽然仍处于实验阶段,但这个功能使得Java应用在被虚拟机启动之前能够先将Java类编译为原生代码。此功能旨在改进小于和大型应用程序的启动时间,同时对峰值性能的影响很小。 但是Java技术供应商 Excelsior 的营销总监Dmitry Leskov 担心AOT编译技术不够成熟,希望Oracle 能够等到Java 10 时有个更稳定的版本才发布。 别外,JVMCI(Java Level JVM Compiler Interface) 等特性,对于整个编译器语言的发展,可能都具有非常重要的意义。目前GRaal COre API已被集成进入了JAVA 9 ,虽然还只是一小步,但完全用Java语言实现的可靠的、高性能的动态编译器似乎不再遥不可及,这是Java 虚拟机开发工程师的福音。 与此同时,随着 Truffle 框架 和 Substrate VM 的发展,已经让个另信心满满的工程师高呼 “One VM to Rule Them All !”,也许就在不远的将来 Ploygot 以一种另类的方式成为现实。 七、其它改进 I/O流新特性 java.io.InputStream 中增加了新的方法来读取和复制 InputStream 中包含的数据。 12345readAllBytes():读取 InputStream 中的所有剩余字节。readNBytes(byte[] b,int off,int len): 从 InputStream 中读取指定数量的字节到数组中。transferTo(OutputStream out):读取 InputStream 中的全部字节并写入到指定的 OutputStream 中 。 多版本兼容 jar包:参考资料:http://openjdk.java.net/jeps/238https://segmentfault.com/a/1190000013584354 垃圾收集机制Java 9 移除了在 Java 8 中 被废弃的垃圾回收器配置组合,同时把G1设为默认的垃圾回收器实现。替代了之前默认使用的Parallel GC,这项变更是很重要的,因为相对于Parallel来说,G1会在应用线程上做更多的事情,而Parallel几乎没有在应用线程上做任何事情,它基本上完全依赖GC线程完成所有的内存管理。这意味着切换到G1将会为应用线程带来额外的工作,从而直接影响到应用的性能。 改进列表如下 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687102: Process API Updates110: HTTP 2 Client143: Improve Contended Locking158: Unified JVM Logging165: Compiler Control193: Variable Handles197: Segmented Code Cache199: Smart Java Compilation, Phase Two200: The Modular JDK201: Modular Source Code211: Elide Deprecation Warnings on Import Statements212: Resolve Lint and Doclint Warnings213: Milling Project Coin214: Remove GC Combinations Deprecated in JDK 8215: Tiered Attribution for javac216: Process Import Statements Correctly217: Annotations Pipeline 2.0219: Datagram Transport Layer Security (DTLS)220: Modular Run-Time Images221: Simplified Doclet API222: jshell: The Java Shell (Read-Eval-Print Loop)223: New Version-String Scheme224: HTML5 Javadoc225: Javadoc Search226: UTF-8 Property Files227: Unicode 7.0228: Add More Diagnostic Commands229: Create PKCS12 Keystores by Default231: Remove Launch-Time JRE Version Selection232: Improve Secure Application Performance233: Generate Run-Time Compiler Tests Automatically235: Test Class-File Attributes Generated by javac236: Parser API for Nashorn237: Linux/AArch64 Port238: Multi-Release JAR Files240: Remove the JVM TI hprof Agent241: Remove the jhat Tool243: Java-Level JVM Compiler Interface244: TLS Application-Layer Protocol Negotiation Extension245: Validate JVM Command-Line Flag Arguments246: Leverage CPU Instructions for GHASH and RSA247: Compile for Older Platform Versions248: Make G1 the Default Garbage Collector249: OCSP Stapling for TLS250: Store Interned Strings in CDS Archives251: Multi-Resolution Images252: Use CLDR Locale Data by Default 253: Prepare JavaFX UI Controls & CSS APIs for Modularization254: Compact Strings255: Merge Selected Xerces 2.11.0 Updates into JAXP256: BeanInfo Annotations257: Update JavaFX/Media to Newer Version of GStreamer258: HarfBuzz Font-Layout Engine259: Stack-Walking API260: Encapsulate Most Internal APIs261: Module System262: TIFF Image I/O263: HiDPI Graphics on Windows and Linux264: Platform Logging API and Service265: Marlin Graphics Renderer266: More Concurrency Updates267: Unicode 8.0268: XML Catalogs269: Convenience Factory Methods for Collections270: Reserved Stack Areas for Critical Sections271: Unified GC Logging272: Platform-Specific Desktop Features273: DRBG-Based SecureRandom Implementations274: Enhanced Method Handles275: Modular Java Application Packaging276: Dynamic Linking of Language-Defined Object Models277: Enhanced Deprecation278: Additional Tests for Humongous Objects in G1279: Improve Test-Failure Troubleshooting 280: Indify String Concatenation281: HotSpot C++ Unit-Test Framework282: jlink: The Java Linker283: Enable GTK 3 on Linux284: New HotSpot Build System285: Spin-Wait Hints287: SHA-3 Hash Algorithms288: Disable SHA-1 Certificates289: Deprecate the Applet API 290: Filter Incoming Serialization Data292: Implement Selected ECMAScript 6 Features in Nashorn294: Linux/s390x Port295: Ahead-of-Time Compilation","tags":[{"name":"JDK9","slug":"JDK9","permalink":"https://huankai.github.io/tags/JDK9/"}]},{"title":"全新的HTTP API","date":"2018-07-09T09:43:03.541Z","path":"2018/07/09/09_http改进/","text":"HTTP(超文体传输协议),早上1997年被采用在目前的 HTTP1.1版本中,直到 2015年,HTTP 2 才成为标准。 HTTP 1.1 和 HTTP 2 主要区别是如何在客户端和服务端之间构建和传输数据。HTTP1.1依赖于请求/响应周期,HTTP 2 允许服务器 push数据 ,它可以发送比客户端请求更多的数据,这使得它可以优先处理并发送首先加载页面至关重要的数据。 JDK 9 中有新的方式来处理HTTP调用 ,它提供了一个新的HttpClient 替代旧的,blocking 模式的HttpURLConnection(HTTP1.0时代,并使得了协议无关的方法),并提供了对 HTTP2 和 Websocket的支持。此外,HTTPClient 还提供了处理HTTP2的相关特性,比如流和服务器推送等功能。 全新的HTTPClient 需要在 module-info.java 中添加: requires jdk.incubator.httpclient;12345678public static void main(String[] args) throws IOException, InterruptedException { HttpClient httpClient = HttpClient.newHttpClient(); HttpRequest httpRequest = HttpRequest.newBuilder(URI.create(\"https://www.baidu.com\")).GET().build(); HttpResponse<String> response = httpClient.send(httpRequest, HttpResponse.BodyHandler.asString()); System.out.println(response.statusCode()); System.out.println(response.version().name()); System.out.println(response.body()); }","tags":[{"name":"JDK9","slug":"JDK9","permalink":"https://huankai.github.io/tags/JDK9/"}]},{"title":"API改进_Stream API","date":"2018-07-09T09:43:03.532Z","path":"2018/07/09/08_Stream改进/","text":"Java 的Stream API 是 JDK 8 中添加的新功能,可以让开发者更快的运算,有效的利用数据进行并行计算,能够利用多核架构进行声明式的数据处理。 在JDK9 中,Stream API 变得更加友好: 1、Stream 接口中新添加 4 个方法:takeWhile 、dropWhile 、ofNullable 、iterate 重载方法,可以让你提供一个Predicate(判断条件)让你决定什么时候停止循环。 takeWhile takeWhile用于从Stream中获取部分数据,接收一个Predicate进行选择,在有序的Stream列表中,takeWile返回从头开始尽量多的元素 123 Stream<Integer> stream = Stream.of(2, 34, 56, 23, 12, 44, 63, 745);//从头开始,只要迭代的元素小于50就不再迭代,返回已迭代的元素 // 如只返回 2 和 34stream.takeWhile(x -> x < 50).forEach(System.out::println); dropWhile dorpWhile 和 takeWhile刚好相反 123Stream<Integer> stream = Stream.of(2, 34, 56, 23, 12, 44, 63, 745);//和 takeWhile相反,从头开始,只要迭代的元素小于50都不要,返回未迭代的元素 // 如只返回 56, 23, 12, 44, 63, 745stream.dropWhile(x -> x < 50).forEach(System.out::println); ofNullable 123456789101112//对于只有单个元素的 Stream,如果这个元素为 null,会抛出 NullPointerException.//Stream<Object> stream = Stream.of(null);//System.out.println(stream.count());// 如果有多个元素,即使存在空元素,也不会抛出 NullPointerExceptionStream.of(3, 5, null).forEach(System.out::println);System.out.println();//如果只有单个元素,可以使用 ofNullable 方法Stream<Object> stream = Stream.ofNullable(null);System.out.println(stream.count()); iterate 1234567//在jdk 8 中,使用 iterate 方法创建的Stream ,要使用 Limit 方法限制产生的元素个数Stream.iterate(0, x -> x + 1).limit(10).forEach(System.out::println);System.out.println();// 在JDK 9 中,可以使用iterate 的重载方法,接受一个 Predicate 参数,达到指定条件就不再产生元素。Stream.iterate(0, x -> x < 10, x -> x + 1).forEach(System.out::println); 2、Optional 类中添加转换为 Stream 的方法123456789101112List<String> list = new ArrayList<>();list.add(\"MM\");list.add(\"JJ\");list.add(\"GG\");Optional<List<String>> optionalStrings = Optional.of(list);//只有一个元素,这个元素是 listoptionalStrings.ifPresent(System.out::println);// 如果要获取 list 中的每个元素,可以使用 flatMapStream<String> stringStream = optionalStrings.stream().flatMap(Collection::stream);stringStream.forEach(System.out::println);","tags":[{"name":"JDK9","slug":"JDK9","permalink":"https://huankai.github.io/tags/JDK9/"}]},{"title":"API改进_集合","date":"2018-07-09T09:43:03.530Z","path":"2018/07/09/07_集合改进/","text":"创建只读集合 JDK8 12345678//创建一个只读集合List<String> list = new ArrayList<>();list.add(\"MM\");list.add(\"JJ\");list.add(\"DD\");list = Collections.unmodifiableList(list);list.forEach(System.out::println);// list.add(\"GG\");// 不能再添加,会抛出异常 JDK9 List 、 Set、Map 接口都添加了 of 静态方法,Map接口还添加了ofEntries方法,都是创建不可变的集合。123//此时的list是只读的,相应的Set 、 Map 都有对应的of方法,// Map 还有 ofEntries方法来创建不可变的Map。List<Integer> list = List.of(1,2,3);","tags":[{"name":"JDK9","slug":"JDK9","permalink":"https://huankai.github.io/tags/JDK9/"}]},{"title":"语法改进_标识符&String","date":"2018-07-09T09:43:03.518Z","path":"2018/07/09/06_标识符&String/","text":"一、标识符:在 jdk8之前 ,可以使用下划线(_)声明变量,在jdk9 中,单独的下划线有特殊的含义,不能被用于标识符。 1String _ = \"test\"; //在jdk8 之前编译通过,jdk9中编译不通过。 二、String在JDK8之前,String 使用的是 char类型的数组,在JDK9中,改为了byte类型的数组(encoding flag)。 char类型使用的是两个字节,在多数情况下,String类型在堆存储时大多数是Latin字符,而Latin字符只需要一个byte就可以,如果使用 char类型,就浪费了一半的空间。我们知道对于中文,使用是三个字节,但是在UTF-16中,只暂用两个字节。","tags":[{"name":"JDK9","slug":"JDK9","permalink":"https://huankai.github.io/tags/JDK9/"}]},{"title":"语法改进_try","date":"2018-07-09T09:43:03.513Z","path":"2018/07/09/05_异常处理/","text":"在 jdk7及以前: 123456789101112131415InputStreamReader reader = null;try { reader = new InputStreamReader(System.in); reader.read();} catch (IOException e) { e.printStackTrace();}finally { if( null != reader){ try { reader.close(); } catch (IOException e) { e.printStackTrace(); } }} 在 JDK 8 中: 可以在 try 后面的小括号中声明对象的实例化,此对象会提升为final类型,可以不使用 finally显示关闭流,会自动将资源关闭。 12345try(InputStreamReader reader = new InputStreamReader(System.in)) { reader.read();} catch (IOException e) { e.printStackTrace();} 在 JDK9 中:可以将reader 声明在 try之前,reader也会自动提升为 final类型。 123456InputStreamReader reader = new InputStreamReader(System.in);try(reader) { reader.read();} catch (IOException e) { e.printStackTrace();} 如果有多个需要关闭的资源,可以将这些资源以英文逗号分隔,如: 1234567InputStreamReader reader = new InputStreamReader(System.in);OutputStreamWriter writer = new OutputStreamWriter(System.out);try(reader;out) { reader.read();} catch (IOException e) { e.printStackTrace();}","tags":[{"name":"JDK9","slug":"JDK9","permalink":"https://huankai.github.io/tags/JDK9/"}]},{"title":"语法改进_钻石操作符(泛型 [Diamond operator])","date":"2018-07-09T09:43:03.512Z","path":"2018/07/09/04_钻石操作符/","text":"我们可以与匿名实现类共同使用钻石操作符 在java8 中以下语法会报错: 1Set<String> sets = new HashSet<>(){};//继承于HashSet的子类匿名对象 在 jdk9中,以上语法不会报错。","tags":[{"name":"JDK9","slug":"JDK9","permalink":"https://huankai.github.io/tags/JDK9/"}]},{"title":"语法改进_接口私有方法","date":"2018-07-09T09:43:03.507Z","path":"2018/07/09/03_接口私有方法/","text":"在JDK7以前,接口中只能声明抽象(abstract)方法和静态(static)常量,在JDK8中,接口可以声明静态方法(static)和默认(default)方法,而在JDK9中,接口还可以声明私有(private)方法。 在JDK7及以前123456789101112public interface MyInterface { //定义常量,默认修饰符为 public static final ,可以省略 String CONTANT = \"Contant\"; //定义抽象方法. void metod(); void method2(); // ...} 在JDK 8中123456789101112131415161718192021public interface MyInterface { String CONTANT = \"Contant\"; void metod(); //定义静态方法,静态方法只能通过类名调用,如: MyInterface.staticMethod(); //默认修饰符为 public static void staticMethod(){ System.out.println(\"我是静态方法方法\"); } //定义默默认方法,默认方法只能通过对象调用,默认方法子类可以重写(Override) //如果子类重写了接口中的默认方法,并且在默认方法中需要调用接口定义的默认方法,不能使用 super.DefaultMethod()这样调用 ,必须要加 接口名.super.默认方法来调用,如 MyInterface.super.defaultMethod()。 //默认修饰符为 public default void defaultMethod(){ System.out.println(\"我是默认方法\"); } // ...} 在JDK 9中12345678910111213141516171819202122232425public interface MyInterface { String CONTANT = \"Contant\"; void metod(); //定义静态方法,静态方法只能通过类名调用,如: MyInterface.staticMethod(); //默认修饰符为 public static void staticMethod(){ System.out.println(\"我是静态方法方法\"); } //定义默默认方法,默认方法只能通过对象调用,默认方法子类可以重写(Override) //如果子类重写了接口中的默认方法,并且在默认方法中需要调用接口定义的默认方法,不能使用 super.DefaultMethod()这样调用 ,必须要加 接口名.super.默认方法来调用,如 MyInterface.super.defaultMethod()。 //默认修饰符为 public default void defaultMethod(){ System.out.println(\"我是默认方法\"); } private void privateMethod(){ System.out.println(\"我是私有方法\"); } // ...} 一道很经典的面试题:抽象类与接口有什么不同? 相同点都不能被实例化对象,必须使用子类或匿名类实例化,以多态的方式使用。 不同点 1、抽象类使用 abstract class 声明 ,接口使用 interface 声明;2、抽象类只能单继承、接口可以多实现;3、在jdk7 及以前:接口中只能有抽象方法和静态常量,在 jdk8 中,接口中可以有抽象方法、静态方法和默认方法、静态常量。在jdk9中,接口中还可以声明私有方法。","tags":[{"name":"JDK9","slug":"JDK9","permalink":"https://huankai.github.io/tags/JDK9/"}]},{"title":"java REPL 工具(jshell)","date":"2018-07-09T09:43:03.505Z","path":"2018/07/09/02_jshell/","text":"一、产生背景像 Pyton 、Scala 之类的语言很早就有了交互式编程环境REPL(Read evaluate print loop) ,以交互式的方式对语句和表达式求值,开发者只需要输入一些代码,就可以的编译前获取对程序的反馈。而之前的java版本,想要执行代码,必须先创建文件、声明类、提供测试方法才能实现。 二、设计理念即写即得,快速运行 三、使用jshell ${JAVA_HOME}/bin 目录下有个 jshell.exe的运行程序,也可以在终端直接运行 jshell 进入java的shell 模式: 使用jshell 声明变量并运行 12345678jshell> int i = 10i ==> 10jshell> int j = 20j ==> 20jshell> System.out.println(i + j);30 使用 jshell声明方法并调用 1234567jshell> public void add(int i ,int j){ ...> System.out.println(i+j); ...> }| 已创建 方法 add(int,int)jshell> add(1,20)21 注意:如果再次声明相同的方法,会覆盖前面的方法. 查看默认导入的包如果使用以下这些包中的类,可以不使用 import 导入。 12345678910111213jshell> /import| import java.io.*| import java.math.*| import java.net.*| import java.nio.file.*| import java.util.*| import java.util.concurrent.*| import java.util.function.*| import java.util.prefs.*| import java.util.regex.*| import java.util.stream.*jshell> 查看声明的变量 12345jshell> /vars| int i = 10| int j = 20jshell> 查看声明的方法 1234jshell> /methods | void add(int,int)jshell> 执行外部文件 假设 /Users/huangkai/HelloTest.java文件内容为: 1234void printHello(){ System.out.println(\"Hello world!\");}printHello(); 使用 jshell 执行:1234jshell> /open /Users/huangkai/HelloTest.javaHello world!jshell> 使用文本编辑器:如要编辑 add方法, 12jshell> /edit add#会打开编辑器,可以编辑add方法内容 没有非RuntimeException 的检查 12jshell> URL url = new URL(\"https://www.baidu.com\");url ===> https://www.baidu.com 以上这句话,会抛出MalformedURLException,但在 jshell环境不需要throws 或 try catch,内部隐藏实现了。 退出jshell 1jshell> /exit","tags":[{"name":"JDK9","slug":"JDK9","permalink":"https://huankai.github.io/tags/JDK9/"}]},{"title":"模块化","date":"2018-07-09T09:43:03.496Z","path":"2018/07/09/01_模块化/","text":"一、产生的背景及意义谈到java9大家第一个想到的应该是Jigsaw 项目,众所周知,Java已发布了20多年,Java和相关的生态在不断丰富的同时,也暴露出一些问题: Java运行环境的膨胀和臃肿每次JVM启动时,至少需要 30~ 60M 的内存加载,主要原因是JVM需要加载 rt.jar ,不管其中的类是否有被ClassLoader加载,整个Jar都会被加载到JVM中去。(而使用模块化可以根据需要只加载程序运行需要的class) 当代码库越来越庞大,越来越复杂,不同版本的类库交叉依赖很让人头疼,这些阻碍了Java的开发和运行效率。 很难真正的对代码进行封装,而系统并没有对不同的jar之间的关系有个明确的概念,每一个公共的类都可以被任意类路径下的其它公共类所访问到,这样也导致无意中使用了并不想被公开的API 类路径本身也存在一定的问题,你怎么知道所需要的jar都有了?或者是否会有重复的包呢? 同时,由于兼容性等各方面的掣肘,对java大刀阔斧的革新也越来越难,Jigsaw 从 Java 7 就开始筹备,Java 8 进行了大量的工作,终于在Java 9 落地。 作为Java 9 平台最大的一个特性,随着Java 平台模块化系统的落地,开发人员无需再为不断膨胀的Java 平台苦恼,例如,你可以使用 jlink工具,根据需要定制运行环境,这对于拥有大量镜像的容器应用场景或复杂关系的大型应用等,都具有非常重要的意义 。 本质上讲,模块(module)的概念,其实就是在 package 外再包裹一层。也就是说,用 module 来管理包,通过声明某些包暴露,不声明默认就是隐藏。因此,模块化使得代码组织更加安全,因为他可以指定哪里包可以暴露,哪些可以隐藏。 二、实现目标 减少了内存的开销 只需要 requires 模块,而非全部jdk,可简化各种类库和大型应用的开发 改进java SE 平台,使其可以适用不同大小的平台设备 改进安全性、可维护性、提高性能 三、使用举例:https://github.com/huankai/jdk9","tags":[{"name":"JDK9","slug":"JDK9","permalink":"https://huankai.github.io/tags/JDK9/"}]},{"title":"Hadoop - HDFS工作机制","date":"2018-07-09T09:42:47.015Z","path":"2018/07/09/04_HDFS工作机制/","text":"NameNode负责管理整个文件系统元数据,DataNode负责具体的文件数据块存储,Secondary NameNode 主要协助NameNode进行元数据的备份。 HDFS的内部工作机制对客户端都是透明的,客户端请求访问HDFS 都需要通过NameNode申请来访问。","tags":[{"name":"Hadoop","slug":"Hadoop","permalink":"https://huankai.github.io/tags/Hadoop/"}]},{"title":"Hadoop - DataNode 介绍","date":"2018-07-09T09:42:47.005Z","path":"2018/07/09/03_Hadoop_DataNode/","text":"DataNode负责将实际数据存储到HDFS中。 DataNode也称为 Slave。 NameNode 与 DataNode会不断保持心跳。 DataNode启动时,会将自己发布到NameNode并汇报负责自己持有的块列表。 当某个DataNode关闭时,它不会影响数据他集群的可用性,NameNode将安排由其他DataNode管理的块进行副本复制。 DataNode所在机器通常会配置有大的磁盘空间,因为实际数据存储在DataNode中。 DataNode会定期(dfs.heartbeat.internal 配置项,默认为3秒)向 NameNode发送心跳,如果NameNode长时间没有接收到DataNode的心跳,就会认为该DataNode失效。 block汇报时间间隔取参数值 dfs.blockreport.internalMsec ,默认时间为6小时。","tags":[{"name":"Hadoop","slug":"Hadoop","permalink":"https://huankai.github.io/tags/Hadoop/"}]},{"title":"Hadoop - NameNode 介绍","date":"2018-07-09T09:42:47.000Z","path":"2018/07/09/02_Hadoop_NameNode/","text":"NameNode 是Hadoop 的核心。 NameNode 也称为 Master。 NameNode仅存储HDFS的元数据、文件系统中所有的目录树、并跟踪整个集群中的文件。 NameNode不存储实际的数据或数据集,数据本身是存储在每个DataNode节点中。 NameNode知道HDFS中任何给定文件列表的块的列表及其位置,使用此信息,NameNode知道如何从块中构建文件。 NameNode并不会持久化存储每个文件中各个块所在DataNode的位置信息,这些信息会在系统启动时从数据节点重建。 NameNode对HDFS至关重要,当NameNode节点无法使用时,HDFS/Hadoop整个集群都无法使用。 NameNode所在机器通常会配置大内存。","tags":[{"name":"Hadoop","slug":"Hadoop","permalink":"https://huankai.github.io/tags/Hadoop/"}]},{"title":"Hadoop简介与安装","date":"2018-07-09T09:42:46.978Z","path":"2018/07/09/01_Hadoop介绍与集群配置/","text":"一、Hadoop介绍Hadoop 是Apache 旗下用java语言实现的开源软件框架,是一个开发和运行处理大规模数据的软件平台,允许使用简单的编程模型在大量的计算机集群上对大型数据进行分布式处理。 Hadoop目前有1.x / 2.x / 3.x 三个大版本: 1.x : 主要由 HDFS(分布式文件系统(redundant reliable storage)) 和 MapReduce(分布式运算变成框架(cluster resource management & data processing)) 组件构成。 2.x : 主要有 HDFS 、YARN(作业调度和集群资源管理框架) 、 MapReduce Or Others 组件构成。在1.x版本中,MapReduce主要是集群资源管理,在2.x版本中YARN做集群资源管理和作业调度,MapReduce只用于数据处理,也可以使用其它的一些技术做数据管理。 3.x : 刚出来的新版本。 Hadoop是 Apache Lucene 创始人Dou Cutting 创建的,最早起源于Nutch,它是Lucene(全文检索框架)的子项目,Nutch的设计目标是构建一个大型的全网搜索引擎,包括网页抓取、索引、查询等功能。但随着抓取网页数量的增加,遇到了严重的可扩展性问题,如何解决数十亿网页的存储问题和索引问题。2003年Google发表了一篇论文为该问题提供了可行的解决方案,论文中描述的是谷歌的产品架构,该架构称为:谷歌分布式文件系统(GFS),可以解决在网页爬取和索引过程中产生的超大文件的存储需求。2004年,Google发表论文向全世界介绍了谷歌版的MapReduce系统。同时期,Nutch的开发人员完成了相应的开源实现HDFS和 MapReduce,并从Nutch中剥离出来成为独立的Hadoop项目,到2008年1月,Hadoop成为Apache顶级项目,迎来了它的快速发展期。2006年Google发表了论文是关于BigTable的,这促使了后来的Hbase的发展。因此,Hadoop极其生态圈的发展离不开Google的贡献。 二、Hadoop的特性 扩容能力:Hadoop是在可用的计算机集群之间分配数据并完成计算任务的,这些集群可以方便的扩展到数以千计的节点中; 成本低:Hadoop通过普通廉价的机器组成服务器集群来分发以及处理数据,以至于成本很低; 高效率:通过并发数据,Hadoop可以在节点之间动态并行的移动数据,使得速度非常快; 可靠性:能自动维护数据的多份复制,并且在任务失败后能自动重新部署(redeploy) 计算任务,所以Hadoop的按位存储和处理数据的能力值得信赖。 三、集群安装集群介绍:Hadoop集群具体来说包括两个集群:HDFS集群和YARN集群,两者逻辑上分离(HDFS的运行不影响YARN,YARN的运行也不影响HDFS),但物理上常部署在一起(HDFS和YARN可以部署在同一服务器上)。 HDFS 集群负责海量数据的存储,集群中的角色主要有:NameNode、DataNode、SecondaryNameNode(相当于NameNode的秘书) YARN集群负责海量数据运算时的资源调度,集群中的角色主要有:ResourceManager、NodeManager Mapreduce 其实是一个分布式运算编程的框架,是应用程序开发包,由用户按照编程规范进行程序开发,打包运行在HDFS集群上并且受到YARN集群的资源调度管理。 Hadoop 常用的集群方式有三种: Standlone mode(独立模式),只在单机部署,仅一台机器运行一个java进程 ,主要用于调试; Pseude-Distributed mode(伪分布模式),只在单机部署,一台机器运行HDFS的NameNode、DataNode、YARN的ResourceManager、NodeManager,但分别启用单独的java进程 ,主要用于调试; Cluster mode(集群模式):生产环境部署,会使用N 台主机组成一个Hadoop集群,主节点与从节点会分开部署在不同服务器上。 3.1、安装前配置操作系统: CentOS Linux release 7.3.1611 (Core) JDK版本: 1.8.0_144 Hadoop版本: hadoop-2.9.1关闭防火墙: systemctl stop firewalld设置防火墙开机禁止启动: systemctl disable firewalld配置主机名(三台机器都需要配置): vim /etc/hosts123192.168.64.128 hadoop-node-1192.168.64.129 hadoop-node-2192.168.64.130 hadoop-node-3 重启网卡: systemctl restart network使用 ping 命令检查是否配置成功(三台机器都需要配置成功),这一步必须不能出错123456789101112131415161718192021222324252627282930[root@sjq-02 ~]# ping -c 3 hadoop-node-1PING hadoop-node-1 (192.168.64.128) 56(84) bytes of data.64 bytes from brokerserver1 (192.168.64.128): icmp_seq=1 ttl=64 time=0.523 ms64 bytes from brokerserver1 (192.168.64.128): icmp_seq=2 ttl=64 time=1.71 ms64 bytes from brokerserver1 (192.168.64.128): icmp_seq=3 ttl=64 time=1.69 ms--- hadoop-node-1 ping statistics ---3 packets transmitted, 3 received, 0% packet loss, time 2001msrtt min/avg/max/mdev = 0.523/1.309/1.713/0.555 ms[root@sjq-02 ~]# [root@sjq-02 ~]# ping -c 3 hadoop-node-2PING hadoop-node-2 (192.168.64.129) 56(84) bytes of data.64 bytes from brokerserver2 (192.168.64.129): icmp_seq=1 ttl=64 time=0.013 ms64 bytes from brokerserver2 (192.168.64.129): icmp_seq=2 ttl=64 time=0.024 ms64 bytes from brokerserver2 (192.168.64.129): icmp_seq=3 ttl=64 time=0.024 ms--- hadoop-node-2 ping statistics ---3 packets transmitted, 3 received, 0% packet loss, time 1999msrtt min/avg/max/mdev = 0.013/0.020/0.024/0.006 ms[root@sjq-02 ~]# [root@sjq-02 ~]# ping -c 3 hadoop-node-3PING hadoop-node-3 (192.168.64.130) 56(84) bytes of data.64 bytes from hadoop-node-3 (192.168.64.130): icmp_seq=1 ttl=64 time=0.846 ms64 bytes from hadoop-node-3 (192.168.64.130): icmp_seq=2 ttl=64 time=1.36 ms64 bytes from hadoop-node-3 (192.168.64.130): icmp_seq=3 ttl=64 time=0.975 ms--- hadoop-node-3 ping statistics ---3 packets transmitted, 3 received, 0% packet loss, time 2004msrtt min/avg/max/mdev = 0.846/1.060/1.360/0.219 ms[root@sjq-02 ~]# 配置免密登陆(128可以使用 ssh 免密登陆128、129和130三台服务器注意:128也需要配置):在128服务器上执行如下:123456789101112#第一步:生成公钥私钥[root@sjq-01 ~]# ssh-keygen -t rsa#输入以上命令,按下三个回车。#第二步:按锁头#命令语法为: ssh-copy-id -i ~/.ssh/id_rsa.pub <romte_ip>#如果你的ssh端口不是默认的22,可以使用 -p 参数指定端口号,如:(ssh-copy-id -i ~/.ssh/id_rsa.pub -p 24 <romte_ip>)#分别执行 128、129、130[root@sjq-01 ~]# ssh-copy-id -i ~/.ssh/id_rsa.pub 192.168.64.128#回车输入密码即可使用 (ssh 192.168.64.128) 登陆到128服务器,#配置免密登陆129和130服务器也是如此。 部署环境: 主机 IP 角色分配 hadoop-node-1 192.168.64.128 NameNode、DataNode、ResourceManager hadoop-node-2 192.168.64.129 DataNode、NodeManager、SecondaryNameNode hadoop-node-3 192.168.64.130 DataNode、NodeManager 3.2、安装JDK 1.8安装方式网上一大把,这里略过。 3.3、安装Hadoop下载 Hadoop : http://hadoop.apache.org/releases.html#Download 解压到指定的目录: tar -zxvf hadoop-2.9.1.tar.gz -C /usr/local/ 配置环境变量: vim /etc/profile ,在末尾添加如下内容:123HADOOP_HOME=/usr/local/hadoop-2.9.1#注意,hadoop的bin 和sbin都配置成环境变量,sbin就是super bin的简写,其实也是调用了bin目录中的相关脚本。export PATH=$HADOOP_HOME/bin:$HADOOP_HOME/sbin:$PATH 重新使配置文件生效: source /etc/profile 四、Hadoop配置文件修改(每台机器执行)3.1、 hadoop-env.sh该文件设置的是Hadoop运行时需要的环境变量,JAVA_HOME是必须设置的,即使当前系统环境变量中配置了JAVA_HOME,它也是不能识别的,因为Hadoop即使在本机上运行,它也会把当前的执行环境当成远程服务器。 12# vim ${HADOOP_HOME}/etc/hadoop/hadoop-env.shexport JAVA_HOME= JAVA_HOME的绝对路径 3.2、core-site.xmlhadoop的核心配置文件,有默认的配置项目 core-default.xml 12345678910<!-- 指定 Hadoop 所使用的文件系统schema(URI),HDFS的老大(NameNode)的地址,协议可以是 hdfs:// tfs:// file:// gfs:// 等 --><property> <name>fs.defaultFS</name> <value>hdfs://hadoop-node-1:9000</value></property><!-- 指定 Hadoop 运行时产生文件的存储目录,默认为 /tmp/hadoop-${user.name} --><property> <name>hadoop.tmp.dir</name> <value>/data/hadoop/tmp</value></property> 3.3、hdfs-site.xml1234567891011<!-- 指定 HDFS副本备份数量,默认为3 --><property> <name>dfs.replication</name> <value>2</value></property><!-- 指定 Hadoop secondaryNameNode 节点地址 --><property> <name>dfs.namenode.secondary.http-address</name> <value>hadoop-node-2:50090</value></property> 3.4、mapred-site.xml进入 ${HADOOP_HOME}/etc/hadoop目录下,你会发现并没有 mapred-site.xml文件,而是有一个mapred-site.xml.template文件,这是 mapred-site.xml 的模板文件,使用 mv 命令重命名:1[root@sjq-01 hadoop]# mv mapred-site.xml.template mapred-site.xml 修改配置内容:vim mapred-site.xml12345<!-- 指定 mapreduce 运行时的框架,这里指定 yarn ,默认是 local --><property> <name>mapreduce.frameworkname</name> <value>yarn</value></property> 3.5、yarn-site.xml12345678910<!-- 指定YARN 的老大 (ResourceManager)地址 --><property> <name>yarn.resourcemanager.hostname</name> <value>hadoop-node-1</value></property><!-- NodeManager上运行的附属服务,需配置成 mapreduce_shuffle 才可以运行Mapreduce程序,默认值为 mapreduce.shuffle --><property> <name>yarn.nodemanager.aux-services</name> <value>mapreduce_shuffle</value></property> 3.6、slaves此文件写上从节点的主机名,每一行写一个主机名123hadoop-node-1hadoop-node-2hadoop-node-3 3.7、关于Hadoop 的配置文件以上我们主要修改的都 ***-site.xml配置文件,在Hadoop中,有这些默认的只读的配置文件, core-default.xml, hdfs-default.xml, yarn-default.xml and mapred-default.xm 配置文件,这里面配置了hadoop的默认配置选项,如果用户没有修改,这些文件中的配置将会生效; ***-site.xml 这些配置了用户自定义的配置选项site 配置选项的优先级会大于 default 文件中的配置,如果有配置的话,就会覆盖 ***-default.xml 中的配置选项。 所有的配置文件选项,可以在官网文档中查询:core-default.xml : http://hadoop.apache.org/docs/r2.9.1/hadoop-project-dist/hadoop-common/core-default.xmlhdfs-default.xml : http://hadoop.apache.org/docs/r2.9.1/hadoop-project-dist/hadoop-hdfs/hdfs-default.xmlmapred-default.xml : http://hadoop.apache.org/docs/r2.9.1/hadoop-mapreduce-client/hadoop-mapreduce-client-core/mapred-default.xmlyarn-default.xml : http://hadoop.apache.org/docs/r2.9.1/hadoop-yarn/hadoop-yarn-common/yarn-default.xml与上个版本相比过时的配置 : http://hadoop.apache.org/docs/r2.9.1/hadoop-project-dist/hadoop-common/DeprecatedProperties.html 3.8 、每台服务器复制配置以上6个配置文件修改完成后,将 128上的 ${HADOOP_HOME}目录上传到 129 和 130两台服务器上:12[root@sjq-02 local]# scp -r /usr/local/hadoop-2.9.1 [email protected]:/usr/local[root@sjq-02 local]# scp -r /usr/local/hadoop-2.9.1 [email protected]:/usr/local 修改 129 和 130 两台服务器上的环境变量(分别在129 和 130上执行以下命令):123456[root@sjq-02 ~]# vim /etc/profile# 添加如下内容HADOOP_HOME=/usr/local/hadoop-2.9.1export PATH=$HADOOP_HOME/bin:$HADOOP_HOME/sbin:$PATH[root@sjq-02 ~]# source /etc/profile 五、Hadoop 集群启动5.1、启动方式要启动Hadoop集群,需要启动HDFS 和 YARN两个集群。注意:首次启动HDFS时,必须对其进行格式化操作。本质上是一些清理和准备工作,因为此时的HDFS在物理上还是不存在的。后续不再需要格式化。格式化的操作在HDFS集群的主角色(namenode) 所在服务器上操作即可。 1[root@sjq-01 ~]# hdfs namenode -format # 或者hadoop namenode -format 单节点依次启动: 在主节点上使用以下命令启动HDFS NameNode: 1[root@sjq-01 ~]# hadoop-daemon.sh start namenode 在每个从节点上使用以下命令启动HDFS DataNode: 1[root@sjq-01 ~]# hadoop-daemon.sh start datanode 在主节点上使用以下命令启动YARN ResourceManager: 1[root@sjq-01 ~]# yarn-daemon.sh start resourcemanager 在每个从节点上使用以下命令启动YARN nodemanager: 1[root@sjq-01 ~]# yarn-daemon.sh start nodemanager 以上脚本位于 ${HADOOP_HOME}/sbin 目录 下,如果想要停止某个角色,只需要把命令中的 start 改为 stop 即可。","tags":[{"name":"Hadoop","slug":"Hadoop","permalink":"https://huankai.github.io/tags/Hadoop/"}]},{"title":"GitLab 搭建及配置","date":"2018-07-09T09:42:08.905Z","path":"2018/07/09/Git_04_GitLab/","text":"一、GitLab 简介GitLab 是一个利用Ruby on Rails 开发的开源版本控制系统,实现一个自托管的Git项目仓库,可通过Web界面进行访问公开的或者私人项目。 它拥有与GitHub类似的功能,能够浏览源代码,管理缺陷和注释。可以管理团队对仓库的访问,它非常易于浏览提交过的版本并提供一个文件历史库。团队成员可以利用内置的简单聊天程序(Wall)进行交流。它还提供一个代码片段收集功能可以轻松实现代码复用,便于日后有需要的时候进行查找。 开源中国代码托管平台 码云 就是基于GitLab项目搭建。 GitLab 分为 GitLab Community Edition(CE) 社区版 和 GitLab Enterprise Edition(EE) 专业版。社区版免费,专业版收费,两个版本在功能上的差异对比,可以参考官方对比说明 二、GitLab 安装和配置安装社区版,GitLab CE 版本:10.8.1这此省略 2.1、GitLab安装安装要求 操作系统:windows 暂不支持,不受支持的 Unix衍生版有:Arch Linux、Fedora、FreeBSD、Gentoo、macOS 内存: 必须不少于 4GB,否则在安装过程中会出现不可预知的问题,这也是官网的推荐。 Ruby版本:2.3以上。 CPU: 2核心,可支持500用户,这也是官网的推荐 数据库: PostgreSQL (官网推荐) 、MySQL/MariaDB (有些功能会受限) 更多要求,请查看官网文档 通过GitLab官方提供的Omnibus安装包来安装,相对方便。Omnibus安装包套件整合了大部分的套件(Nginx、ruby on rails、git、redis、postgresql等),再不用额外安装这些软件,减轻了绝大部分安装量。 GitLab官方安装文档 :CentOS7.x系统 安装依赖包,并配置postfix服务为GitLab邮件服务12345[root@sjq-09 ~]# yum install curl openssh-server openssh-clients postfix cronie[root@sjq-09 ~]# systemctl start postfix [root@sjq-09 ~]# systemctl enable postfix # 配置开机启动# 关闭防火墙[root@sjq-09 ~]# systemctl stop firewalld 两种安装源: 从官方镜像源安装添加GitLab仓库并安装到服务器上 123[root@sjq-09 ~]# curl https://packages.gitlab.com/install/repositories/gitlab/gitlab-ee/script.rpm.sh | sudo bash[root@sjq-09 ~]# yum install gitlab-ce # 自动安装最新版本 从第三方镜像源安装官方镜像源在国外,国外安装会很慢,甚至有时因网络问题会无法安装。国内推荐使用 清华大学开源软件镜像源。使用 vim /etc/yum.repos.d/gitlab-ce.repo创建文件,内容如下: 12345[gitlab-ce]name=Gitlab CE Repositorybaseurl=https://mirrors.tuna.tsinghua.edu.cn/gitlab-ce/yum/el$releasever/gpgcheck=0enabled=1 保存、退出vim 后,再执行:12[root@sjq-09 ~]# yum makecache # 更新本地YUM缓存[root@sjq-09 ~]# yum install gitlab-ce # 自动安装最新版本 修改配置文件vim /etc/gitlab/gitlab.rb,绑定域名1external_url 'http://xxx.com' #也可以写ip地址 启动GitLab,使得配置生效1[root@sjq-09 ~]# gitlab-ctl reconfigure 使用浏览器访问GitLab首次访问GitLab,系统会让你重新设置管理员的密码,设置成功后会返回登录界面。默认的管理员账号是root,如果你想更改默认管理员账号,请输入上面设置的新密码登录系统后修改帐号名. GitLab相关目录及配置 12345主配置文件: /etc/gitlab/gitlab.rbGitLab 文档根目录: /opt/gitlab默认存储库位置: /var/opt/gitlab/git-data/repositoriesGitLab Nginx 配置文件路径: /var/opt/gitlab/nginx/conf/gitlab-http.confPostgresql 数据目录: /var/opt/gitlab/postgresql/data GitLab由以下服务构成 nginx: 静态web服务器 gitlab-shell: 用于处理Git命令和修改authorized keys列表 gitlab-workhorse: 轻量级的反向代理服务器 logrotate:日志文件管理工具 postgresql:数据库、 redis:缓存数据库 sidekiq:用于在后台执行队列任务(异步执行) unicorn:An HTTP server for Rack applications,GitLab Rails应用是托管在这个服务器上面的。 2.2、配置:2.2.1、配置SMTP服务如果你不想用服务器自带的postfix服务来发邮件,可以改用SMTP服务。修改GitLab邮件服务配置(gitlab.rb文件),使用腾讯企业邮箱的SMTP服务器,填写账号和密码:vim /etc/gitlab/gitlab.rb1234567gitlab_rails['smtp_address'] = \"smtp.exmail.qq.com\"gitlab_rails['smtp_port'] = 25gitlab_rails['smtp_user_name'] = \"xxx\"gitlab_rails['smtp_password'] = \"xxx\"gitlab_rails['smtp_domain'] = \"smtp.qq.com\"gitlab_rails['smtp_authentication'] = 'plain'gitlab_rails['smtp_enable_starttls_auto'] = true 使配置生效:12[root@sjq-09 ~]# gitlab-ctl reconfigure[root@sjq-09 ~]# gitlab-rake cache:clear RAILS_ENV=production # 清除缓存 2.2.2、GitLab配置HTTPSGitLab默认是使用HTTP的,可以手动配置为HTTPS上传SSL证书,创建 SSL目录,用于存放SSL证书12[root@sjq-09 ~]# mkdir -p /etc/gitlab/ssl[root@sjq-09 ~]# chmod 0700 /etc/gitlab/ssl 上传证书并修改证书权限1[root@sjq-09 ~]# chmod 600 /etc/gitlab/ssl/* 修改GitLab的主配置文件vim /etc/gitlab/gitlab.rb 1234external_url \"https://xxx.com\"nginx['redirect_http_to_https'] = truenginx['ssl_certificate'] = \"/etc/gitlab/ssl/xxx.com.crt\"nginx['ssl_certificate_key'] = \"/etc/gitlab/ssl/xxx.com.key\" 重建配置,使其生效1[root@sjq-09 ~]# gitlab-ctl reconfigure 以上操作后,GitLab自带的Nginx服务的配置文件 /var/opt/gitlab/nginx/conf/gitlab-http.conf 会被重新修改: 12345678server { listen *:80; server_name xxx.com; server_tokens off; ## Don't show the nginx version number, a security best practice return 301 https://xxx.com:443$request_uri; access_log /var/log/gitlab/nginx/gitlab_access.log gitlab_access; error_log /var/log/gitlab/nginx/gitlab_error.log;} 不用额外再配置,HTTP 会自动跳转到 HTTPS 。开放443端口,因为防火墙未开启,此处省略。 三、gitLab服务管理3.1、服务管理1234567891011121314151617# 启动所有 gitlab 组件:gitlab-ctl start# 停止所有 gitlab 组件:gitlab-ctl stop# 停止所有 gitlab postgresql 组件:gitlab-ctl stop postgresql# 停止相关数据连接服务gitlab-ctl stop unicorngitlab-ctl stop sidekiq# 重启所有 gitlab 组件:gitlab-ctl restart# 重启所有 gitlab gitlab-workhorse 组件:gitlab-ctl restart gitlab-workhorse# 查看服务状态gitlab-ctl status# 生成配置并启动服务gitlab-ctl reconfigure 3.2、日志管理1234567891011121314151617181920212223# 实时查看所有日志gitlab-ctl tail# 实时检查redis的日志gitlab-ctl tail redis # 实时检查postgresql的日志gitlab-ctl tail postgresql # 检查gitlab-workhorse的日志gitlab-ctl tail gitlab-workhorse # 检查logrotate的日志gitlab-ctl tail logrotate # 检查nginx的日志gitlab-ctl tail nginx # 检查sidekiq的日志gitlab-ctl tail sidekiq # 检查unicorn的日志gitlab-ctl tail unicorn 3.3、备份3.3.1、手动备份:gitLab默认备份的目录为 /var/opt/gitlab/backups,如果想改备份目录,可修改/etc/gitlab/gitlab.rb: 1gitlab_rails['backup_path'] = '/data/backups' 修改文件后,需要1[root@sjq-09 ~]# gitlab-ctl reconfigure 备份命令:1[root@sjq-09 ~]# gitlab-rake gitlab:backup:create 该命令会在备份目录(默认:/var/opt/gitlab/backups/)下创建一个tar压缩包xxxxxxxx_gitlab_backup.tar,其中开头的xxxxxx是备份创建的时间戳,这个压缩包包括GitLab整个的完整部分。 3.3.2、使用 crontab实现自动备份 1234[root@sjq-09 ~]# crontab -e# 每天2点备份gitlab数据0 2 * * * /usr/bin/gitlab-rake gitlab:backup:create 可设置只保留最近7天的备份,编辑配置文件 /etc/gitlab/gitlab.rb12# 数值单位:秒gitlab_rails['backup_keep_time'] = 604800 重新加载gitlab配置文件1[root@sjq-09 ~]# gitlab-ctl reconfigure 3.4、恢复假设配置文件为: /var/opt/gitlab/backups/1499444722_2018_05_28_7.4.7_gitlab_backup.tar 停止 unicorn 和 sidekiq ,保证数据库没有新的连接,不会有写数据情况。 1234567891011# 停止相关数据连接服务[root@sjq-09 ~]# gitlab-ctl stop unicorn[root@sjq-09 ~]# gitlab-ctl stop sidekiq# 指定恢复文件,会自动去备份目录找。确保备份目录中有这个文件。# 指定文件名的格式类似:1499444722_2018_05_28_7.4.7,程序会自动在文件名后补上:“_gitlab_backup.tar”# 一定按这样的格式指定,否则会出现 The backup file does not exist! 的错误[root@sjq-09 ~]# gitlab-rake gitlab:backup:restore BACKUP=1499444722_2018_05_28_7.4.7# 启动Gitlab[root@sjq-09 ~]# gitlab-ctl start 3.5、开机自启vim /etc/rc.d/rc.local 添加如下内容1/bin/gitlab-ctl start 3.6、数据库操作连接数据库12345678910111213141516[root@sjq-09 data]# gitlab-rails dbconsolepsql (9.6.8)Type \"help\" for help.gitlabhq_production=> \\l #查看所有数据库 List of databases Name | Owner | Encoding | Collate | Ctype | Access privileges ---------------------+-------------+----------+-------------+-------------+--------------------------------- gitlabhq_production | gitlab | UTF8 | en_US.UTF-8 | en_US.UTF-8 | postgres | gitlab-psql | UTF8 | en_US.UTF-8 | en_US.UTF-8 | template0 | gitlab-psql | UTF8 | en_US.UTF-8 | en_US.UTF-8 | =c/\"gitlab-psql\" + | | | | | \"gitlab-psql\"=CTc/\"gitlab-psql\" template1 | gitlab-psql | UTF8 | en_US.UTF-8 | en_US.UTF-8 | =c/\"gitlab-psql\" + | | | | | \"gitlab-psql\"=CTc/\"gitlab-psql\"gitlabhq_production=> \\q # 退出 默认情况下,postgresql只允许本机访问,配置访问权限 vim /var/opt/gitlab/postgresql/data/pg_hba.conf 添加如下内容1host all all 0.0.0.0/0 trust vim /var/opt/gitlab/postgresql/data/postgresql.conf 找到 listen_addresses 设置为’*’1listen_addresses = '*' 重启 postgresql服务: 1gitlab-ctl restart postgresql","tags":[{"name":"Git","slug":"Git","permalink":"https://huankai.github.io/tags/Git/"}]},{"title":"Git 多账号配置SSH","date":"2018-07-09T09:42:08.887Z","path":"2018/07/09/Git_03_多账号配置SSH/","text":"Windows 下Git多账号配置SSH-key的管理 所有执行命令的地方都是在管理员模式下进行,即打开cmd,也可以使用Git Bash客户端用管理员身份运行程序。 1、生成github.com对应的私钥与公钥在任意目录打开 cmd 或 Git Bash ,执行命令 ssh-keygen -t rsa -C youEmail 创建github对应的sshKey,命令为id_rsa_github 会在当前目录下生成 id_rsa_github 和 id_rsa_github.pub 两个文件,将这两个文件Copy 到 C:\\Users\\用户名\\.ssh 目录中。 以同样的方式生成另一个git的私钥与公钥,(邮箱地址可以相同,也可以不同,在 2 处输入生成的文本不同),也将生成的两个文件Copy 到C:\\Users\\用户名\\.ssh 目录中。 2、把github对应的公钥和另一个生成的git对应的公钥上传到服务器。GitHub SSH 生成位置在 User -> Settings -> SSH and GPG keys -> New SSH Key 将对应的github 的 id_rsa_github.pub文件内容复制到 Key文本框中。 另一个git的 SSH我使用的个人服务器,所以就没有截图,如果使用的是 gitee.com 这个网站,也和github类似。 3、在.ssh目录创建 config 文件并配置(最核心的地方)每个账号单独配置一个Host,每个Host要取一个别名,每个Host主要配置HostName和IdentityFile两个属性即可 Host的名字可以取为自己喜欢的名字,不过这个会影响git相关命令,例如:Host mygit 这样定义的话,命令如下,即git@后面紧跟的名字改为 mygitgit clone git@mygit:huankai/hk-core.git HostName 这个是真实的域名地址IdentityFile 这里是id_rsa的地址PreferredAuthentications 配置登录时用什么权限认证–可设为publickey,password keyboard-interactive等User 配置使用用户名 12345678910111213# 配置github.comHost github.com HostName github.com IdentityFile C:\\\\Users\\\\huangkai\\\\.ssh\\\\id_rsa_github PreferredAuthentications publickey User huankai# 配置个人服务器Host 192.168.1.100 HostName 192.168.1.100 IdentityFile C:\\\\Users\\\\huangkai\\\\.ssh\\\\id_rsa_my PreferredAuthentications publickey User huangkai 4、打开Git Bash客户端执行测试命令测试是否配置成功(会自动在.ssh目录生成known_hosts文件把私钥配置进去)12345sjq-278@DESKTOP-V9JFELK MINGW64 /e$ ssh -T [email protected] huankai! You've successfully authenticated, but GitHub does not provide shell access.sjq-278@DESKTOP-V9JFELK MINGW64 /e 如上,测试成功后,就可以使用不同的git ssh操作了,互相不会影响。如果你使用的了TortoiseGit 管理工具,在自动 Load Putty Key 时,要选择对应的 ppk文件,ppk文件的生成,请点击 这里 查看。","tags":[{"name":"Git","slug":"Git","permalink":"https://huankai.github.io/tags/Git/"}]},{"title":"ETL 利器Kettle实战应用解析系列 ————【生成作业并发布到定时任务中】","date":"2018-07-09T09:40:46.138Z","path":"2018/07/09/05_生成作业并发布到定时任务中/","text":"生成作业并发布到定时任务中快捷键 Ctrl + ALT + N 创建一个作业。 1、Start该组件表示一个作业开始,双击可定义定时执行。 2、转换定义转换,可以有多个。 3、发送邮件成功后,可以发送邮件通知 4、成功作业成功。 5、最终流程图","tags":[{"name":"ETL","slug":"ETL","permalink":"https://huankai.github.io/tags/ETL/"}]},{"title":"ETL 利器Kettle实战应用解析系列 ————【生成日志记录到数据库表中】","date":"2018-07-09T09:40:46.135Z","path":"2018/07/09/04_kettle生成日志记录到数据库表中/","text":"以部门数据处理为例生成日志并保存到数据库中:在部门处理的转换文件中,双击 插入/更新中的步骤(也可以是其它步骤),弹出转换属性对话框,切换到日志面板,在转换项中输入日志数据库连接 与 日志表 参数,下面还可以定义 日志表的字段 ,点击下方的 SQL 按键,会弹出要执行的sql 语句,你可以点击下方的执行,会在对应的数据库中创建指定的日志表。确定保存后再执行这个转换文件,当任务执行完后,数据库中会生成相应的日志信息。 如果你想生成每个步骤的日志信息,还可以切换到 步骤 项中,按照上面的依次配置执行即可。","tags":[{"name":"ETL","slug":"ETL","permalink":"https://huankai.github.io/tags/ETL/"}]},{"title":"ETL 利器Kettle实战应用解析系列 ————【学生数据处理】","date":"2018-07-09T09:40:46.131Z","path":"2018/07/09/03_Kettle实战应用_学生数据处理/","text":"学生数据处理学生数据的处理需要根据实际的业务来处理,这里只是根据个人的业务,项目开发中还得具体情况具体分析。 1、多输入源、排序、记录连接:学生数据处理需要从多个数据源中获取输入源,将这些数据源的数据通过 排序 —> 记录连接(相当于sql 中的多表join 查询) 合并数据。 此步骤需要连接三个数据库获取输入的数据,主要就是配置 表输入 —> 排序记录 —> 记录集连接 这三步,你还可以根据业务添加分组等其他组件。 2、Java代码设置参数值:此组件可以根据你的需求编写相关的 java代码,如果内置的一些方法不能满足你的业务需求,可以将你的相关jar放在 ${KETTLE_HOme}/lib 目录中,然后重启ektte工具,就可以在 此组件中使用自定义的java类。 此组件主要是根据需求重写 processRow 这个方法,并需要导入 相关的包,如下:生成用户密码、设置用户拼音、设置生日转换为日期、生成用户UUID等。 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253import com.sjq.core.utils.DigestUtils;import com.sjq.core.utils.PinyinUtils;import com.sjq.core.utils.DateUtils;import java.sql.Timestamp;import org.apache.commons.lang3.StringUtils;import java.util.UUID;public boolean processRow(StepMetaInterface smi, StepDataInterface sdi) throws KettleException { Object[] r = getRow(); if (r == null) { setOutputDone(); return false; } //设置密码 String passWord = get(Fields.In, \"password\").getString(r); //输入参数 if(StringUtils.isEmpty(passWord)){ String stuNo = get(Fields.In, \"学号\").getString(r); //输入参数 passWord = DigestUtils.encryptPassword(stuNo); } get(Fields.Out, \"password\").setValue(r, passWord); //设置拼音 String stuName = get(Fields.In, \"姓名\").getString(r); //输入参数 get(Fields.Out, \"spellName\").setValue(r, PinyinUtils.converterToFirstSpell(stuName)); //设置毕业时间 String endDate = get(Fields.In, \"毕业时间\").getString(r); //输入参数 get(Fields.Out, \"毕业时间\").setValue(r, new Timestamp(DateUtils.parseDate(endDate).getTime())); //设置生日 String brithDay = get(Fields.In, \"出生日期\").getString(r); //输入参数 if(StringUtils.isNotEmpty(brithDay)){ get(Fields.Out, \"出生日期\").setValue(r, new Timestamp(DateUtils.parseDate(brithDay).getTime())); } //设置用户id String userId = get(Fields.In, \"user_id\").getString(r); //输入参数 if(StringUtils.isEmpty(userId)){ userId = UUID.randomUUID().toString(); } get(Fields.Out, \"user_id\").setValue(r, userId); //设置用户Infoid String userInfoId = get(Fields.In, \"user_info_id\").getString(r); //输入参数 if(StringUtils.isEmpty(userInfoId)){ userInfoId = UUID.randomUUID().toString(); } get(Fields.Out, \"user_info_id\").setValue(r, userInfoId); // Send the row on to the next step. putRow(data.outputRowMeta, r); return true; } 3、增加常量:定义你需要常量数据。可见第二节相关内容。 4、值映射:可以将值映射成你想要的值。如性别 为 男的映射为 1,为女的映射为 2. 5、生成随机数:定义你需要常量数据。可见第二节相关内容。 6、获取系统信息:定义获取系统信息,可见第二节相关内容。 7、插入/更新 sp_user表以上几步都配置成功后,在 sp_user 表中所有字段都能在流中找到,定义输出的表、执行插入与更新的条件、流字段与输出表字段的映射关系。即可插入或更新此表。 8、阻塞数据直到步骤都完成:添加此步骤的目的是其它表(这里指的是 sp_user_info、sp_user_role、bas_student、bas_student_class 这四张表)中在插入数据时,与上一步中的 sp_user表中添加了主外键关联,因此,如果sp_user表中没有用户信息,其它表中在插入数据时就会抛出主键外错误,这也证实了,以上你所画出的流程并不是从头到尾按步执行的,这应该是该工具内部优化性能的一种机制。为了保证sp_user表中的数据插入完后,再执行其它表中数据的插入或更新,此组件就可以实现这样的功能。 9、插入/更新 其它表:定义输出的表、执行插入与更新的条件、流字段与输出表字段的映射关系。 10、执行:定义好上面的步骤后,按第二节的执行步骤方式执行。 11、最终流程与日志截图 :","tags":[{"name":"ETL","slug":"ETL","permalink":"https://huankai.github.io/tags/ETL/"}]},{"title":"ETL 利器Kettle实战应用解析系列 ————【部门与班级数据处理】","date":"2018-07-09T09:40:46.128Z","path":"2018/07/09/02_Kettle实战应用_部门与班级数据处理/","text":"#一、部门数据处理流程如下:# 1、创建一个转换:打开ETL工具后,使用快捷键 Ctrl + N 创建一个转换。 2、表输入:2.1、点击左边 核心对象 面板中,打到 表输入 组件,直接拖拽到右边的编辑区; 2.2、双击编辑区的 表输入 组件,弹出配置对话框,在此可以编辑步骤名称、数据库连接配置、要查询的 sql,配置好后,可以点击下方的预览 查看sql的执行结果。 3、增加常量:可以定义一些常量数据。点击左边 核心对象 面板中,打到 常量数据 组件,直接拖拽到右边的编辑区;按住 Shift 键,将 表输入 组件与 增加常量 组件相连。 4、生成随机数:生成一些主键UUID,点击左边 核心对象 面板中,打到 生成随机数 组件,直接拖拽到右边的编辑区;按住 Shift 键,将 增加常量 组件与 生成随机数 组件相连。 5、获取系统信息:获取系统的一些相关系统,如当前时间。点击左边 核心对象 面板中,打到 获取系统信息 组件,直接拖拽到右边的编辑区;按住 Shift 键,将 生成随机数 组件与 获取系统信息 组件相连。 6、插入/更新数据定义输出的表、执行插入与更新的条件、流字段与输出表字段的映射关系。点击左边 核心对象 面板中,打到 插入/更新 组件,直接拖拽到右边的编辑区;按住 Shift 键,将 获取系统信息 组件与 插入/更新 组件相连,注意:这里有两个需要 插入/更新 的组件,当连接两个相同的组件时,会提示是 分发 还是 复制,这里选择 复制 即可。 7、执行以上步骤都定义好之后,启动查看执行结果,查看日志,如果有错误需要解决。 8、最终流程与日志截图 通过上图日志可知:此次执行总共更新 49条记录(为什么是更新?因为我已执行过一次,根据第 7 步中的查询的条件来判断,如果成立则会执行更新,不成立才执行插入)。 #二、班级数据处理流程:# 班级数据的处理步骤比上面的更简单,上面的输出有两张表,班级输出只有一张表,和上面的从表输入 到 获取系统信息的步骤一样,这里不再列出。","tags":[{"name":"ETL","slug":"ETL","permalink":"https://huankai.github.io/tags/ETL/"}]},{"title":"Linux常用命令(top)","date":"2018-04-23T08:28:59.521Z","path":"2018/04/23/常用命令_top/","text":"top 命令是 Linux 下常用的性能分析工具,能够实时显示系统中各个进程的资源占用状况,类似于 Windows 的任务管理器。 top用于动态监控进程所占系统资源,每隔3秒变一次。这个命令的特点是把占用系统资源(CPU,内存,磁盘IO等)最高的进程放到最前面。top命令打印出了很多信息,包括系统负载(loadaverage)、进程数(Tasks)、cpu使用情况、内存使用情况以及交换分区使用情况。 命令内容详解:在命令行输入 top 显示如下 第一行系统信息: 字符 描述 top - 15:35:56 当前系统时间 up 1:30 系统已运行时间 2 users 在线用户,包含系统用户 load average: 0.01, 0.03, 0.05 系统负载。三个数值分别为 1分钟、5分钟、15分钟前到现在的平均值。 第二行系统进程信息 字符 描述 Tasks: 92 total 总进程数 2 running 运行进程数 91 sleeping 睡眠进程数 0 stopped 停止进程数 0 zombie 僵尸进程数 僵尸进程 :一个子进程在其父进程没有调用wait()或waitpid()的情况下,这个进程就是僵尸进程,如果其父进程还存在一直不调用wait(),则该僵尸进程将无法回收,等到父进程退出后该进程被init回收。 第三行Cpu使用信息 字符 描述 %Cpu(s): 0.0 us cpu占用率(%),用户进程占用cpu百分率 0.7 sy 系统占用CPU百分比 0.0 ni 用户进程空间内改变过优先级的进程占用CPU百分比 99.3 id cpu空闲率 0.0 wa 等待IO的CPU时间百分比 0.0 hi 硬中断(Hardware IRQ)占用CPU的百分比 0.0 si 软中断(Software Interrupts)占用CPU的百分比 第四行内存使用信息 字符 描述 KiB Mem : 999936 total 内存总量 391636 free 系统空闲内存大小 170252 used 系统使用内存大小 438048 buff/cache 缓存大小 第五行Swap内存信息 字符 描述 KiB Swap: 1048572 total Swap 总量 1048572 free Swap空闲量 0 used Swap使用量 第六行进程信息 字符 描述 PID 进程pid USER 进程所属用户 PR 进程优先级 NI nice值。越小优先级越高,最小-20,最大20(用户设置最大19) VIRT 进程使用的虚拟内存总量,单位kb。VIRT=SWAP+RES RES 进程使用的、未被换出的物理内存大小,单位kb。RES=CODE+DATA SHR 共享内存大小,单位kb S 进程状态。D=不可中断的睡眠状态 R=运行 S=睡眠 T=跟踪/停止 Z=僵尸进程 %CPU 进程占用cpu百分比 %MEM 进程占用内存百分比 TIME+ 进程运行时间 COMMAND 进程名称 top 常用交互命令(进入top后使用): c 换显示命令名称和完整命令行 M 根据内存大小排序 P 根据CPU大小排序 T 根据时间大小排序 u 输入指定用户名后,查看输入用户名的进程 q 退出top 1 注意:是数字1,不是字母l,监控每个逻辑CPU的状况(对于多核CPU时) b 打开/关闭高亮效果 i 忽略闲置和僵死进程。这是一个开关式命令 k 终止指定的进程。按下k键–>再输入要杀死的进程的pid–>按enter键–>(选择信号类型,以数字标示,默认15为杀死)本步可省略按enter键(常用为-9) kill 信号大全详见: https://www.2cto.com/os/201202/119425.html 扩展:比 top 更好使用的工具: htop: https://huankai.github.io/2018/04/23/%E5%B8%B8%E7%94%A8%E5%91%BD%E4%BB%A4_htop/","tags":[{"name":"Linux","slug":"Linux","permalink":"https://huankai.github.io/tags/Linux/"}]},{"title":"htop","date":"2018-04-23T08:28:59.509Z","path":"2018/04/23/常用命令_htop/","text":"htop 的界面字符解释和 top 差不多,可以参照: https://huankai.github.io/2018/04/23/常用命令_top 安装安装环境 :Centos 7 安装扩展源:sudo yum install epel-release安装top:sudo yum install htop 安装完成后,在命令行执行 htop 如下: 字符 描述 F1 Help 帮助 F2 Setup 设置(可以设置显示的参数,方式等 F3 Search 搜索(光标跳到含有输入字符的行) F4 Filter 筛选(只保留完全匹配输入字符的行) F5 Tree 查看进程数(和pstree差不多) F6 Sorted 排序 F7 Nice - 增加优先级 F8 Nice + 减少优先级 F9 Kill 杀死进程,默认信号15 F10 Quit 退出","tags":[{"name":"Linux","slug":"Linux","permalink":"https://huankai.github.io/tags/Linux/"}]},{"title":"Git 环境搭建","date":"2018-03-21T00:39:27.874Z","path":"2018/03/21/Git_01_安装/","text":"安装环境:操作系统: CentOS Linux release 7.3.1611 (Core) 安装:1、Linux 做为服务器端系统,Windows 作为客户端系统,分别安装 Git服务器端:1[root@huangkai ~]# yum install -y git 安装完后,查看 Git 版本12[root@huangkai ~]# git --versiongit version 1.8.3.1 客户端:下载 Git for Windows,地址:https://git-for-windows.github.io ,或 点击这里 下载安装完之后,可以使用 Git Bash 作为命令行客户端。也可以使用 tortoiseGit 客户端来管理项目: 点击这里 下载查看 Git 版本:123huangkai@DESKTOP-MEKIV7C MINGW64 ~/Desktop$ git --versiongit version 2.14.2.windows.2 自报家门:这里要配置的是你个人的用户名称和电子邮件地址。这两条配置很重要,每次 Git 提交时都会引用这两条信息,说明是谁提交了更新,所以会随更新内容一起被永久纳入历史记录 123huangkai@DESKTOP-MEKIV7C MINGW64 ~/Desktop$ git config --global user.name \"huangkai\"$ git config --global user.email \"[email protected]\" 2、创建 git用户,用来管理 git 服务12345678[root@huangkai ~]# useradd git[root@huangkai ~]# passwd gitChanging password for user git.New password: BAD PASSWORD: The password is shorter than 8 charactersRetype new password: passwd: all authentication tokens updated successfully.[root@huangkai ~]# 3、创建 Git 仓库设置 /home/git/test.git 为 Git 裸仓库(一般以 .git结尾,裸仓库是没有工作区的,纯粹为了共享),然后把 Git 仓库的 拥有者 修改为 git用户1234567[root@huangkai ~]# cd /home/git/[root@huangkai git]# git --bare init test.gitInitialized empty Git repository in /home/git/test.git/[root@huangkai git]# chown -R git:git test.git[root@huangkai git]# lltotal 0drwxr-xr-x. 2 git git 6 Feb 12 22:10 test.git 4、客户端 clone 远程仓库 进入 Git Bash 命令行客户端,创建项目地址(设置在 d:/gittest)并进入此目录,然后从 Linux git 服务器上 clone 项目:12huangkai@DESKTOP-MEKIV7C MINGW64 /d/gittest$ git clone [email protected]:/home/git/test.git 如果SSH用的不是默认的22端口,则需要使用以下的命令(假设SSH端口号是7700):12huangkai@DESKTOP-MEKIV7C MINGW64 /d/gittest$ git clone [email protected]:7700/home/git/test.git 当第一次连接到目标 Git 服务器时会得到一个提示:123The authenticity of host '192.168.1.90 (192.168.1.90)' can't be established.ECDSA key fingerprint is SHA256:HxWUbnmfhcxBEnkYtXPyN4xwA2OCNsE8WZzOsvhMaCY.Are you sure you want to continue connecting (yes/no)? 选择 yes:1Warning: Permanently added '192.168.1.90' (ECDSA) to the list of known hosts. 此时 C:/Users/用户名/.ssh 下会多出一个文件 known_hosts,以后在这台电脑上再次连接目标 Git 服务器时不会再提示上面的语句。后面提示要输入密码,可以采用 SSH 公钥来进行验证. 5、客户端创建 SSH 公钥和私钥123456789101112131415161718192021222324huangkai@DESKTOP-MEKIV7C MINGW64 /d/gittest$ ssh-keygen -t rsa -C \"[email protected]\"Generating public/private rsa key pair.Enter file in which to save the key (/c/Users/huangkai/.ssh/id_rsa):Enter passphrase (empty for no passphrase):Enter same passphrase again:Your identification has been saved in /c/Users/huangkai/.ssh/id_rsa.Your public key has been saved in /c/Users/huangkai/.ssh/id_rsa.pub.The key fingerprint is:SHA256:dXmadhishVapUGRtsrpr2QLpxLv04Yg7WQ22n6H85ww [email protected] key's randomart image is:+---[RSA 2048]----+| o+... || ...++. || .+=* . || o oo+ * || o =S.. = . || B + . . || *.=E* || oo=+B+o || ooo+==o |+----[SHA256]-----+huangkai@DESKTOP-MEKIV7C MINGW64 /d/gittest 输入回车、此时 C:/Users/用户名/.ssh 下会多出两个文件 id_rsa 和 id_rsa.pub id_rsa 是私钥 id_rsa.pub 是公钥 6、服务器端 Git 打开 RSA 认证进入 /etc/ssh 目录,编辑 sshd_config,打开以下三个配置的注释:123RSAAuthentication yesPubkeyAuthentication yesAuthorizedKeysFile .ssh/authorized_keys 保存并重启 sshd 服务:1[root@huangkai gittest]# systemctl restart sshd 由 AuthorizedKeysFile 得知公钥的存放路径是 .ssh/authorized_keys,实际上是 $Home/.ssh/authorized_keys,由于管理 Git 服务的用户是 git,所以实际存放公钥的路径是 /home/git/.ssh/authorized_keys 在 /home/git/ 下创建目录 .ssh,将.ssh 文件夹的 owner 修改为 git1234567891011121314151617181920[root@huangkai git]# mkdir .ssh[root@huangkai git]# ls -latotal 12drwx------. 4 git git 89 Feb 12 22:29 .drwxr-xr-x. 4 root root 33 Feb 12 22:06 ..-rw-r--r--. 1 git git 18 Aug 3 2016 .bash_logout-rw-r--r--. 1 git git 193 Aug 3 2016 .bash_profile-rw-r--r--. 1 git git 231 Aug 3 2016 .bashrcdrwxr-xr-x. 3 git git 18 Feb 12 22:11 gittestdrwxr-xr-x. 2 root root 6 Feb 12 22:29 .ssh[root@huangkai git]# chown -R git:git .ssh[root@huangkai git]# ls -latotal 12drwx------. 4 git git 89 Feb 12 22:29 .drwxr-xr-x. 4 root root 33 Feb 12 22:06 ..-rw-r--r--. 1 git git 18 Aug 3 2016 .bash_logout-rw-r--r--. 1 git git 193 Aug 3 2016 .bash_profile-rw-r--r--. 1 git git 231 Aug 3 2016 .bashrcdrwxr-xr-x. 3 git git 18 Feb 12 22:11 gittestdrwxr-xr-x. 2 git git 6 Feb 12 22:29 .ssh 将客户端公钥导入服务器端 /home/git/.ssh/authorized_keys 文件 回到 Git Bash下,导入文件,输入服务器端 git 用户的密码(就是git用户的密码) 123456huangkai@DESKTOP-MEKIV7C MINGW64 /d/gittest$ ssh [email protected] 'cat >> .ssh/authorized_keys' < ~/.ssh/[email protected]'s password:huangkai@DESKTOP-MEKIV7C MINGW64 /d/gittest$ 回到服务器端,查看 .ssh 下是否存在 authorized_keys 文件:123456[root@huangkai .ssh]# ls -latotal 4drwxr-xr-x. 2 git git 29 Feb 12 22:43 .drwx------. 4 git git 89 Feb 12 22:39 ..-rw-rw-r--. 1 git git 397 Feb 12 22:43 authorized_keys[root@huangkai .ssh]# 可以通过 cat 查看authorized_keys 的内容 重要:修改 .ssh 目录的权限为 700 修改 .ssh/authorized_keys 文件的权限为 600 123456[root@huangkai git]# chmod 700 .ssh/[root@huangkai git]# cd .ssh/[root@huangkai .ssh]# lltotal 4-rw-rw-r--. 1 git git 397 Feb 12 22:43 authorized_keys[root@huangkai .ssh]# chmod 600 authorized_keys 客户端再次 clone :12huangkai@DESKTOP-MEKIV7C MINGW64 /d/gittest$ git clone [email protected]:/home/git/gittest/.git 7、禁止 git 用户 ssh 登录服务器之前在服务器端创建的 git 用户将此用户不允许 ssh 登录服务器 编辑 /etc/passwd 找到:1git:x:1001:1001::/home/git:/bin/bash 修改为1git:x:1001:1001::/home/git:/bin/git-shell 此时 git 用户可以正常通过 ssh 使用 git,但无法通过 ssh 登录系统。 参考资料: https://www.liaoxuefeng.com/wiki/0013739516305929606dd18361248578c67b8067c8c017b000https://git-scm.com/docs","tags":[{"name":"Git","slug":"Git","permalink":"https://huankai.github.io/tags/Git/"}]},{"title":"Git 基本使用(TortoiseGit)","date":"2018-03-20T09:34:50.486Z","path":"2018/03/20/Git_02_基本使用/","text":"一、 安装与基本配置1.1、下载与安装: ##.Git for windows 下载地址:https://github.com/git-for-windows/git/tags选择需要的版本下载,下载完成后,双击安装,使用默认的安装即可,安装完成后,右键菜单就会出现 Git GUI 和 Git Bash 两个快捷菜单,Git GUI 我们一般不用,它没命令方式强大,一般情况我们会使用Git Bash 操作。 1.2、自报家门:Git 安装完成后,需要配置你个人的用户名和电子邮箱,这两个配置很重要,每次Git 提交时都会引用这两个配置信息,指明是谁提交了,也会随更新内容一起被永久纳入历史记录。 在你电脑的任意位置,右键 –> Git Bash ,会弹出 Git 命令框,在命令框中设置用户名与邮箱如下: 此配置会在你的电脑 C:\\Users\\huangkai目录下生成 .gitconfig 文件,没错,这个文件的内容就是存储以上两条命令的结果。注意,这个目录是当前登陆用户名的目录 ,我登陆的账号就是 huangkai ,如果你是用 administrator 登陆的,那这个文件应该在 C:\\Users\\administrator 这个目录下。 还可以使得git config –list 查看Git 的参数,如下,就有上面配置的用户名与邮箱: 二、基本操作(使用 TortoiseGit)2.1、安装TortoiseGIt:tortoiseGit 客户端下载地址:https://tortoisegit.org/download/ 打开此页面后,会显示最新版本下载,这里可根据你本机安装的git版本进行下载,如果你安装的是Git 版本是2.15 ,可点击 PREVIEW RELEASE 查看之前的版本进行下载,下载完成后,直接下一步,安装即可。 语言包支持:在此下载页面下方会有各语言包支持,如果你有英文恐惧症,还是资深患者,可以根据你的系统位数下载对应的中文包。不过我强烈建议尤其是搞技术的同学们使用英文版。 下载语言包后,安装完成,然后右键 TortoiseGit Settings 中设置对应的语言包,如下图所示,不知道为什么,我的windows 10 没有安装成功,之前在windows 7上有安装成功,可能是偶尔的人品问题,不过我还是喜欢用英文版本的,就不纠结这个了。 2.2、克隆远程到本地库需要输入的参数:URL : 克隆的地址Directory: 保存在本地的目录Branch: 分支名称,默认为 masterLoad Putty Key:自动加载 ppk文件,如果不选择,你需要启动 pageant这个程序,并将 ppk文件导入到pageant中,建议选中此项,并选择ppk文件所在目录。 如何生成ppk文件? 1、先生成 RSA 公钥与私钥:右键打开git bash,在控制台中输入以下命令:1$ ssh-keygen -t rsa -C \"[email protected]\" 然后按几次回车,就会在你电脑的C:\\Users\\huangkai.ssh 目录下生成 id_rsa (私钥)与 id_rsa.pub(公钥)这两个文件。 2、在开始菜单中搜索,PuttyKen 双击打形式这个程序Conversions –> ImportKey –> 选择生成RSA 私钥的文件–> Save private key ,保存这个生成的文件到指定的目录即可。 2.3、提交当本地文件有修改,需提交到远程服务器时,进入项目根目录,右键点击Git Commit ->’branchName’ 这个菜单,branchName 是你当前所在分支的名称,输入提交的消息,选择需要提交的文件,然后点击下面的 Commit & Push 提交并推送到远程服务器。 2.4、更新右键 TortoiseGit –> Pull 或者 右键 TortoiseGit –> Fetch 两者区别:Pull: 拉下更新后会自动合并本地分支,其实是 Fetch 与 merge两个命令的合并。 Fetch: 先把更新拉下来,此时工作区不会自动合并,在用merge或rebase进行合并。 2.5、删除如果你要删除一个没有加入到Git管理的文件或文件夹,那你可以直接删除即可,如果你要删除一个已加入到Git管理的文件或文件夹,可以先直接删除这个文件,然后提交,比如,删除readme.md这个文件,如下,可以看出这个文件的状态为 missing 你也可以选择需要删除的文件,右键 TortoiseGit –> Delete –> Remove ,此时,选中的文件就会从本地工作区删除,再执行提交操作如下:可以看出该文件的状态为Deleted. 2.6、版本回退在项目根目录下右键TortoiseGit –> Show log ,弹出对话框如下,显示的是所有提交的历史记录 每点击每个提交的记录,在下方会有本次提交的修改,如果你要回退到这个版本,可以选择这次提交的记录后 右键 Reset “master” to this 弹出如下: reset Type: soft: 软重置,不会改变工作区和索引 mixed: 混合,保持工作区不变,重置索引文件 hard:重置工作区和索引,会丢弃在本地的变更 选择Hard –> Ok ,此时,你的工作区应该就回退到此版本了。如果你再使用 TortoiseGit –> Show log 查看日志记录,只会显示到回退版本的提交记录。注意,此时你要提交修改到远程服务器,会提交失败,因为你的工作区不是最新的版本如果你又想回到最后提交的版本,在项目根目录下 右键 TortoiseGit –> Show reflog,会显示各种版本号,选择你需要指定退回的版本号id ,右键 –> Reset “master” to this ,此时,你的工作区又是当前最新的版本。 2.7、撤消修改选择需要撤消修改的文件,右键 TortoiseGit –> Revert注意,此操作会丢失本地的修改,无法找回,请慎用!!! 2.8、解决冲突测试例子:Readme.md文件内容如下:1SDFSDFADFADFS UserA 修改readme.md文件,并提交12SDFSDFADFADFSabceda UserB 也修改了readme.md文件12SDFSDFADFADFShello 此时,用户B想要提交文件,先更新项目,更新项目时,会报错,如下: 解决办法: 1、先把自己的代码保存一个版本隐藏起来右键 TortoiseGit –> Stash Save 输入消息后保存。保存成功后,此时你项目的所有文件都应该是绿色的勾 2、重新更新项目右键TortoiseGit –> pull此时更新下的项目就是UserA 服务器上最新的项目 3、把隐藏的代码取回来右键 TortoiseGit –> Stash Pop此时会弹出框告诉你代码有冲突,有冲突的文件也变成了黄色感叹号,并询问是否需要解决它 4、编辑冲突上一步点击是后,会弹出如下冲突列表对象框,你需要双击每个文件依次解决冲突 双击readme.md文件,弹出冲突编辑对话框如下 如上,红色的行表示为有冲突的地方,你可以光标指定批有冲突的地方,右键会弹出菜单:Use this text block: 使用此文本块Use this whole file: 使用整个文件Use text block from right before left 优先使用左边文本块Use text block from left before right 优先右边文本块 可根据你的选择进行编辑冲突,最后要保证下面合并后的内容不能有红色的行,然后点击菜单栏中的的 mark as resolved(标记为解决)。关闭此窗口,冲突解决完成。然后,你就可以继续提交你本地的修改了。","tags":[{"name":"Git","slug":"Git","permalink":"https://huankai.github.io/tags/Git/"}]},{"title":"Nginx 配置文件","date":"2018-03-16T06:24:07.358Z","path":"2018/03/16/Nginx_02_配置文件/","text":"Nginx 配置文件: 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110# 定义Nginx运行的用户和用户组user nginx nginx ;# 启动进程,通常设置成和cpu的数量相等,cpu信息可以使用 cat /proc/cpuinfo 查看worker_processes 8;# 为每个进程分配cpu,上例中将8个进程分配到8个cpu,当然可以写多个,或者将一个进程分配到多个cpuworker_cpu_affinity 00000001 00000010 00000100 00001000 00010000 00100000 01000000 10000000;# 这个指令是指当一个nginx进程打开的最多文件描述符数目,理论值应该是最多打开文件数(ulimit -n)与nginx进程数相除,但是nginx分配请求并不是那么均匀所以最好与ulimit -n的值保持一致worker_rlimit_nofile 102400;#错误日志定义等级[debug/info/notice/warn/error/crit]error_log /var/log/nginx/error.log warn;#pid文件路径pid /var/run/nginx.pid;#一个nginx进程打开的最多文件描述符数目,理论值应该是最多打开文件数(系统的值ulimit -n)与nginx进程数相除,但是nginx分配请求并不均匀.所以建议与ulimit -n的值保持一致worker_rlimit_nofile 65535; #工作模式及连接数上限events { #poll是多路复用IO(I/O Multiplexing)中的一种方式,但是仅用于linux2.6以上内核,可以大大提高nginx的性能 use epoll; #单个后台worker process进程的最大并发链接数 (最大连接数=连接数*进程数) worker_connections 102400; #尽可能多的接受请求 multi_accept on;}#设定http服务器,利用它的反向代 理功能提供负载均衡支持http { #设定mime类型,类型由mime.type文件定义 include mime.types; default_type application/octet-stream; #设定日志格式 access_log /usr/local/nginx/log/nginx/access.log; #sendfile 指令指定 nginx 是否调用 sendfile 函数(zero copy 方式)来输出文件,对于普通应用必须设为 on #如果用来进行下载等应用磁盘IO重负载应用,可设置为 off,以平衡磁盘与网络I/O处理速度,降低系统的uptime. sendfile on; #开启目录列表访问,合适下载服务器,默认关闭。 #autoindex on; #防止网络阻塞 tcp_nopush on; #keepalive超时时间,客户端到服务器端的连接持续有效时间,当出现对服务器的后继请求时,keepalive-timeout功能可避免建立或重新建立连接 keepalive_timeout 60; #提高数据的实时响应性 tcp_nodelay on; #开启gzip压缩 gzip on; gzip_min_length 1k; gzip_buffers 4 16k; gzip_http_version 1.1; gzip_comp_level 4; #压缩级别大小,最大为9,值越小,压缩后比例越小,CPU处理更快 gzip_types text/plain application/x-javascript text/css application/xml; gzip_vary on; client_max_body_size 10m; #允许客户端请求的最大单文件字节数 client_body_buffer_size 128k; #缓冲区代理缓冲用户端请求的最大字节数 proxy_connect_timeout 120; #nginx跟后端服务器连接超时时间(代理连接超时) proxy_send_timeout 120; #后端服务器数据回传时间(代理发送超时) proxy_read_timeout 120; #连接成功后,后端服务器响应时间(代理接收超时) proxy_buffer_size 4k; #设置代理服务器(nginx)保存用户头信息的缓冲区大小 proxy_buffers 4 32k; #proxy_buffers缓冲区,网页平均在32k以下的话,这样设置 proxy_busy_buffers_size 64k; #高负荷下缓冲大小(proxy_buffers*2) large_client_header_buffers 4 4k;#设定请求缓冲 #客户端请求头部的缓冲区大小,这个可以根据你的系统分页大小来设置,一般一个请求的头部大小不会超过1k #不过由于一般系统分页都要大于1k,所以这里设置为分页大小。分页大小可以用命令getconf PAGESIZE取得 client_header_buffer_size 4k; #这个将为打开文件指定缓存,默认是没有启用的,max指定缓存数量,建议和打开文件数一致,inactive是指经过多长时间文件没被请求后删除缓存。 open_file_cache max=102400 inactive=20s; #将为打开文件指定缓存,默认是没有启用的,max指定缓存数量,建议和打开文件数一致,inactive是指经过多长时间文件没被请求后删除缓存。 open_file_cache_valid 30s; #这个是指多长时间检查一次缓存的有效信息。 open_file_cache_min_uses 1; #open_file_cache指令中的inactive参数时间内文件的最少使用次数,如果超过这个数字,文件描述符一直是在缓存中打开的,如上例,如果有一个文件在inactive #包含其它配置文件,如自定义的虚拟主机 include vhosts.conf;}","tags":[{"name":"Linux","slug":"Linux","permalink":"https://huankai.github.io/tags/Linux/"},{"name":"Nginx","slug":"Nginx","permalink":"https://huankai.github.io/tags/Nginx/"}]},{"title":"Redis 集群","date":"2018-03-16T02:13:44.567Z","path":"2018/03/16/Redis_07_集群/","text":"一、安装环境:操作系统: Centos Linux 7.3.1611 (Core)Redis版本:4.0.1Redis集群配置: 主机名 Ip地址 端口号 集群端口号(默认为端口号 + 10000) sjq-01 192.168.64.128(master) 6379 16379 sjq-01 192.168.64.128(master) 6380 16380 sjq-01 192.168.64.128(slave) 6381 16381 sjq-02 192.168.64.129(master) 6379 16379 sjq-02 192.168.64.129(slave) 6380 16380 sjq-02 192.168.64.129(slave) 6381 16381 其中192.168.64.128:6379(master) 的 slave 为 192.168.64.129:6380 (slave)192.168.64.129:6379 (master) 的 slave 为 192.168.64.128:6381 (slave)192.168.64.128:6380 (master) 的 slave 为 192.168.64.129:6381 (slave) 二、安装Redis请 点击这里 查看Redis的安装 三、创建集群3.1、创建集群每台服务器安装 ruby 和 rubyge ms使用 yum 安装 : yum -y install ruby rubygems查看ruby 版本: ruby -v gem 安装 redis ruby 接口 : gem install redis 如果出现以上错误,表示redis 4.0.1需要的ruby版本至少大于等2.2.2,而上面通过yum安装的ruby版本为2.0 。解决办法如下: 1、安装 curl :yum -y install curl 2、安装RVM:先执行:gpg2 --keyserver hkp://keys.gnupg.net --recv-keys D39DC0E3再执行:curl -L get.rvm.io | bash -s stable在安装rvm时,可能会有连接不到远程服务器的问题,这要就要看你的人品了,有时候不会出现这样的问题,目录没有找到解决办法,等一会再执行,好像就可以了。 3、加载配置文件:source /usr/local/rvm/scripts/rvm 4、查看rvm库中已知的ruby版本:rvm list known 5、安装一个ruby版本: rvm install 2.3.3 ,这里我安装的为2.3.3,只要大于2.2.2就可以 6、使用一个ruby版本:rvm use 2.3.3 7、卸载一个已知版本:rvm remove 2.0.0 8、查看ruby 版本: ruby -v ,此时 ruby 版本成了 2.3.3 9、再使用 gem install redis 安装ruby redis接口,就不会有上面的错误信息了 配置Redis集群节点信息(每台服务器执行):1234567891011[root@sjq-02 redis-4.0.1]# mkdir -p conf/cluster/6379[root@sjq-02 redis-4.0.1]# mkdir -p conf/cluster/6380[root@sjq-02 redis-4.0.1]# mkdir -p conf/cluster/6381[root@sjq-02 redis-4.0.1]# cp redis.conf conf/cluster/6379/[root@sjq-02 redis-4.0.1]# cp redis.conf conf/cluster/6380/[root@sjq-02 redis-4.0.1]# cp redis.conf conf/cluster/6381/[root@sjq-02 redis-4.0.1]# cd conf/cluster/6379/[root@sjq-02 6379]# lltotal 60-rw-r--r--. 1 root root 57764 Mar 14 11:14 redis.conf[root@sjq-02 6379]# 使用vim修改 6379目录 下的redis.conf配置信息如下: 参数名称 参数值 描述 bind 192.168.64.129 Bind ip protected-mode yes 开启保护模式 port 6379 redis端口号 daemonize yes 守护进程启动 pidfile /var/run/redis_6379.pid pid文件 dir /usr/local/redis-4.0.1/conf/cluster/6379 数据存放目录 databases 1 默认为16,集群时设置一个即可 cluster-enabled yes 开启集群 cluster-config-file /usr/local/redis-4.0.1/config/cluster/6379/nodes.conf 集群配置文件(启动时自动生成在当前目录下,建议在6379目录下启动redis节点) cluster-node-timeout 15000 节点互联超时时间,毫秒 cluster-migration-barrier 1 数据迁移的副本临界数,这个参数表示一个主节点在拥有多少个从节点时就要割让一个从节点给另一个没有任何从节点的主节点 使用vim修改 6380目录 下的redis.conf配置信息如下: 参数名称 参数值 描述 bind 192.168.64.129 Bind ip protected-mode yes 开启保护模式 port 6380 redis端口号 daemonize yes 守护进程启动 pidfile /var/run/redis_6380.pid pid文件 dir /usr/local/redis-4.0.1/conf/cluster/6380 数据存放目录 databases 1 默认为16,集群时设置一个即可 cluster-enabled yes 开启集群 cluster-config-file /usr/local/redis-4.0.1/config/cluster/6380/nodes.conf 集群配置文件(启动时自动生成在当前目录下,建议在6379目录下启动redis节点) cluster-node-timeout 15000 节点互联超时时间,毫秒 cluster-migration-barrier 1 数据迁移的副本临界数,这个参数表示一个主节点在拥有多少个从节点时就要割让一个从节点给另一个没有任何从节点的主节点 使用vim修改 6381目录 下的redis.conf配置信息如下: 参数名称 参数值 描述 bind 192.168.64.129 Bind ip protected-mode yes 开启保护模式 port 6381 redis端口号 daemonize yes 守护进程启动 pidfile /var/run/redis_6381.pid pid文件 dir /usr/local/redis-4.0.1/conf/cluster/6380 数据存放目录 databases 1 默认为16,集群时设置一个即可 cluster-enabled yes 开启集群 cluster-config-file /usr/local/redis-4.0.1/config/cluster/6381/nodes.conf 集群配置文件(启动时自动生成在当前目录下,建议在6379目录下启动redis节点) cluster-node-timeout 15000 节点互联超时时间,毫秒 cluster-migration-barrier 1 数据迁移的副本临界数,这个参数表示一个主节点在拥有多少个从节点时就要割让一个从节点给另一个没有任何从节点的主节点 分别在每台服务器上启动redis服务:123[root@sjq-02 6379]# /usr/local/redis-4.0.1/bin/redis-server ./redis.conf[root@sjq-02 6380]# /usr/local/redis-4.0.1/bin/redis-server ./redis.conf[root@sjq-02 6381]# /usr/local/redis-4.0.1/bin/redis-server ./redis.conf 可以使用 ps aux|grep redis 查看服务是否正常启动。 执行Redis 集群创建命令(只需要在其中一个节点上执行一次即可)12345678910111213141516171819202122232425[root@sjq-02 ~]$ cd /usr/local/redis-4.0.1/[root@sjq-02 redis-4.0.1]# cp /usr/local/redis-4.0.1/src/redis-trib.rb /usr/local/bin/redis-trib[root@sjq-01 6379]# redis-trib create --replicas 1 192.168.64.128:6379 192.168.64.129:6379 192.168.64.128:6380 192.168.64.129:6380 192.168.64.128:6381 192.168.64.129:6381>>> Creating cluster>>> Performing hash slots allocation on 6 nodes...Using 3 masters:192.168.64.128:6379192.168.64.129:6379192.168.64.128:6380Adding replica 192.168.64.129:6380 to 192.168.64.128:6379Adding replica 192.168.64.128:6381 to 192.168.64.129:6379Adding replica 192.168.64.129:6381 to 192.168.64.128:6380M: f1a1123ca63ed4864a166b34252568ae68b7f79c 192.168.64.128:6379 slots:0-5460 (5461 slots) masterM: fd8a224b4f220fd4f944fd1b3c743c548ac7eeb3 192.168.64.129:6379 slots:5461-10922 (5462 slots) masterM: e0a135a0708e9c2ec51dff0080cffe9ba4983fe2 192.168.64.128:6380 slots:10923-16383 (5461 slots) masterS: c4f8d1ef105b8285132003e0bc6067c5e5195bc4 192.168.64.129:6380 replicates f1a1123ca63ed4864a166b34252568ae68b7f79cS: c5861fd86db135dd19bf5b48f7f15343b1844833 192.168.64.128:6381 replicates fd8a224b4f220fd4f944fd1b3c743c548ac7eeb3S: 380b1f8cd50ab6f295a8f53ef25f46f424ae8c4d 192.168.64.129:6381 replicates e0a135a0708e9c2ec51dff0080cffe9ba4983fe2Can I set the above configuration? (type 'yes' to accept): yes 如上所示:主节点(M)分别为:192.168.64.128:637 ,哈希槽为(0-5460)192.168.64.129:6379 ,哈希槽为(5461-10922)192.168.64.128:6380 ,哈希槽为(10923-16383)从节点(S)分别为:192.168.64.129:6380、192.168.64.128:6381、192.168.64.129:6381 创建集群时会自动分配主从节点,如果你觉得上面的主从节点不是你想要的,可以 ctrl + c 退出交互模式,交换上面创建集群命令的ip地址的位置,如果是你想要的,输入 yes ,回车,开始创建集群,信息如下:12345678910111213141516171819202122232425262728Can I set the above configuration? (type 'yes' to accept): yes>>> Nodes configuration updated>>> Assign a different config epoch to each node>>> Sending CLUSTER MEET messages to join the clusterWaiting for the cluster to join..>>> Performing Cluster Check (using node 192.168.64.128:6379)M: f1a1123ca63ed4864a166b34252568ae68b7f79c 192.168.64.128:6379 slots:0-5460 (5461 slots) master 1 additional replica(s)S: c4f8d1ef105b8285132003e0bc6067c5e5195bc4 192.168.64.129:6380 slots: (0 slots) slave replicates f1a1123ca63ed4864a166b34252568ae68b7f79cS: 380b1f8cd50ab6f295a8f53ef25f46f424ae8c4d 192.168.64.129:6381 slots: (0 slots) slave replicates e0a135a0708e9c2ec51dff0080cffe9ba4983fe2M: e0a135a0708e9c2ec51dff0080cffe9ba4983fe2 192.168.64.128:6380 slots:10923-16383 (5461 slots) master 1 additional replica(s)M: fd8a224b4f220fd4f944fd1b3c743c548ac7eeb3 192.168.64.129:6379 slots:5461-10922 (5462 slots) master 1 additional replica(s)S: c5861fd86db135dd19bf5b48f7f15343b1844833 192.168.64.128:6381 slots: (0 slots) slave replicates fd8a224b4f220fd4f944fd1b3c743c548ac7eeb3[OK] All nodes agree about slots configuration.>>> Check for open slots...>>> Check slots coverage...[OK] All 16384 slots covered. 3.2、创建集群过程说明: 给定的redis-trib 程序的命令为 create ,这表示创建一个新的集群; --replicas 1 表示每个主节点下有一个从节点; 之后跟着其它参数则是实例的地址列表,程序会使用这些地址的实例创建集群。接着,redis-trib会打印一些预想中的配置给你看,如果你觉得没问题,就可以输入 yes,redis-trib 就会将这份配置应用到集群中。 查看集群状(可以在任意一台节点上查看):从上图可以看出,每个集群的主、从节点、以及每个master节点的哈希槽 redis-cluster把所有的物理节点映射到 【0-16383】个slot(哈希槽)上,cluster负责维护 nodeslotvalue 3.3、集群高可用测试:3.3.1、重建集群步骤网上的说法是删除每个节点的nodes.conf、appendonly.aof、dump.rdb文件,再重启每个节点,使用 redis-trib create 命令创建集群,此种方式不能在生产环境中使用,如果删除这些文件, 持久化的数据都不存在,这在生产环境是非常危险的。正确做法是关闭所有集群节点,直接启动每个节点即可,不要删除nodes.conf 、appendonly.aof 、dump.rdb文件,重启redis后将自动加入到集群中。 3.3.2、查看当前集群各节点的状态: 3.3.3、redis-trib.rb命令参数说明: call : 执行redis命令 create:创建一个新的集群 add-node:将一个节点添加到集群中,第一个是新节点ip:port ,第二个是任意一个已存在节点 ip:port,如将新节点192.168.64.135:6379添加到集群中:redis-trib add-node 192.168.64.135:6379 192.168.64.128:6379 reshard:重新分片 check:查看集群信息 del-node:移除一个节点 四、为集群添加新节点:在 192.168.64.135上安装 redis,步骤如上,并启动两个redis实例:192.168.64.135:6379(master) 和192.168.64.135:6380(slave) 4.1、添加主节点在任意一台redis集群服务器上使用 add-node 将一个节点添加到集群中1/usr/local/redis-4.0.1/src/redis-trib.rb add-node 192.168.64.135:6379 192.168.64.128:6379 执行上面的命令,就会添加新的节点到集群中,新的节点不包含任何数据,因为它没有分配 slot(哈希槽),默认新添加的节点是master(主)节点,当集群需要将某个从节点升级为新的主节点时,这个新节点不会被选中。 为新节点添加slot(哈希槽):只需要指定集群中一个节点的地址,redis-trib 就会自动找到集群中的其他节点信息,目前,redis-trib只能在管理员的协助下完成重新分片的工作,命令如下:1/usr/local/redis-4.0.1/src/redis-trib.rb reshard 192.168.64.129:6379 上面的会询问你需要移动多少个哈希槽(slot) ,从 1 – 16384,这里输入 500。 上面又会询问你接收的节点 id是哪个,很显然这个节点id为刚添加的新节点id,可以连接redis集群查看如下: 新添加的节点为 红色框部分,解读如下: 参数 描述 63f13eba35c50dc8503bebad513f4265368c7aa4 节点id 192.168.64.135:6379@16379 主机ip:端口号@集群链接端口号 master 表示为主节点 - 如果该节点为master,以 – 表示,如果为 slave(从节点), 则为该节点的master id connected 0-5460 如果connected后面有数字,表示为主节点的哈希槽,只有master的connected才有可能有哈希槽的值,slave不会有 很显然,在上面应该输入新增加节点的id 即: 63f13eba35c50dc8503bebad513f4265368c7aa4 又会询问,需要从哪些节点分配 ,如果输入all ,会将每个 master节点分配500个哈希槽到新的节点上,你也可以指定输入想要从哪些节点移动到新节点的master id,然后输入 done 完成。这里我输入 all 。然后就给你列出一些移动哈希槽的计划列表,如果没有问题,输入yes 即可 再查看集群节点信息如下:我们可以发现此时 192.168.64.135:6379 节点下有哈希槽了,主节点添加成功。 4.2、添加从节点先在任意一台中集群节点服务上执行以下命令将新的节点添加到集群中:1# /usr/local/redis-4.0.1/src/redis-trib.rb add-node 192.168.64.135:6380 192.168.64.128:6379 查看节点信息如下: 新添加的节点默认为主节点,将新节点设置为192.168.64.135:6379的 从节点,操作如下:使用redis-cli 连接上新节点的shell,输入命令:cluster replicate 对应master的节点id1234[root@sjq-09 6379]# /usr/local/redis-4.0.1/bin/redis-cli -c -h 192.168.64.135 -p 6380192.168.64.135:6380> cluster replicate 63f13eba35c50dc8503bebad513f4265368c7aa4OK192.168.64.135:6380> 查看集群节点信息如下,此时192.168.64.135:6380(slave)的主节点为192.168.64.135:6379 注意:在线添加slave时,需要 dump 整个master进程,并传递到slave,再由slave加载rdb文件到内存,rdb传输过程中master可能无法提供服务,整个过程消耗大量IO,要小心操作,最好在服务器访问量小的时候进行。 五、为集群删除节点5.1 删除slave节点:使用 redis-trib del-node 节点ip:port 节点id 删除:删除192.168.64.135:6380节点,删除节点后,也会将该节点服务停用。 查看集群节点信息如下,可知删除的节点已不在集群中: 5.2、删除master节点:删除主节点之前,需要先使用 reshard移除该 master 的全部slot(哈希槽),然后再删除当前节点,(目前只能将删除的master的slot迁移到一个节点上),操作方式与配置slot类似,指定具体的Source node即可。下面以删除192.168.64.135:6379节点为例说明: 再次查看节点信息如下,发现 192.168.64.135:6379没有了哈希槽(slot) 此时,我们就可以删除节点了 再次查看节点信息,发现 192.168.64.135:6379 这个节点已不在集群中。 六、为集群添加密码授权方式一:为集群添加密码授权在每个redis服务的redis.conf 添加内容如下:1requirepass 1234567 说明:这种方式需要重新启动各节点 方式二: 进入各个实例进行设置123./redis-cli -c -h 192.168.64.128 -p 6379config set requirepass 1234567 config rewrite 注意:各个节点密码都必须一致,否则Redirected就会失败,推荐这种方式,这种方式会把密码写入到redis.conf里面去,且不用重启。 设置密码之后如果需要使用redis-trib.rb的各种命令,如下:12redis-trib.rb check 192.168.64.128[ERR] Sorry, can’t connect to node 192.168.64.128:6379 上面会报错,解决方法: vim /usr/local/rvm/gems/ruby-2.3.3/gems/redis-4.0.1/lib/redis/client.rb ,找到password,并修改password为上面配置的密码即可1234567891011121314151617181920212223242526272829303132class Redis class Client DEFAULTS = { :url => lambda { ENV[\"REDIS_URL\"] }, :scheme => \"redis\", :host => \"127.0.0.1\", :port => 6379, :path => nil, :timeout => 5.0, :password => \"1234567\", :db => 0, :driver => nil, :id => nil, :tcp_keepalive => 0, :reconnect_attempts => 1, :inherit_socket => false } attr_reader :options def scheme @options[:scheme] end def host @options[:host] end def port @options[:port] end 网上找资料说还需要在每个节点上配置 masterauth 的密码,其实不用,本人亲测,masterauth 这个参数有没有都不会影响到集群,就算每个节点配置的masterauth 不一样也能正常使用,但 requirepass 这个就必须得一样了,并且修改vim /usr/local/rvm/gems/ruby-2.3.3/gems/redis-4.0.1/lib/redis/client.rb这个文件的password的值也是配置文件中 requirepass 参数的值。 官网说的很详细,masterauth 这个参数是配置主从复制中需要认证的参数。","tags":[{"name":"Redis","slug":"Redis","permalink":"https://huankai.github.io/tags/Redis/"}]},{"title":"Docker","date":"2018-02-28T09:07:16.080Z","path":"2018/02/28/Docker/","text":"一、介绍1.1、镜像 Docker镜像(Image)就是一个只读的模板,可以用来创建Docker容器,一个镜像可以启动多个Docker容器。 1.2、容器Docker利用容器(Container)来运行应用,容器是从镜像创建的运行实利,它可以启动、停止、删除等。每个容器都是相互隔离的、保证安全的平台。可以把容器看成是一个简单的Linux环境(包括ROOT用户权限、进程空间、用户空间、网络等)和运行在其中的应用程序。 1.3、 RegistryRegistry(仓库)是集中存放镜像文件的场所。Registry对其中的镜像进行分类管理。 一个Registry 会有多个Repository 一个Registry会有多个不同的tag与Image 比如名称为 centos 的repository 下,有tag 为 6 和 7 的镜像。 Registry 分为公有与私有的两种形式: 最大的公有Registry是Docker Hub,存放了大量的镜像供用户下载 国内的公有Registry有USTC、网易云、aliCloud、DaoCloud等,可供大陆用户更加稳定的访问 用户可以在本地创建一个私有Registry 用户创建了自己的镜像之后就可以使用push 命令上传到公有Registry或私有Registry中,这样下次在另一台机器使用这个镜像时就只需要从Registry pull下来就可以运行了。 二、安装2.1、安装环境:操作系统:CentOS Linux release 7.3.1611 (Core)Docker版本:Docker version 1.12.6, build 3e8e77d/1.12.6 查看是否有安装Docker12345[root@sjq01 ~]# yum list installed|grep dockerdocker.x86_64 2:1.12.6-71.git3e8e77d.el7.centos.1 @extras docker-client.x86_64 2:1.12.6-71.git3e8e77d.el7.centos.1 @extras docker-common.x86_64 2:1.12.6-71.git3e8e77d.el7.centos.1 @extras [root@sjq01 ~]# 如果上面命令执行有信息出来,执行 yum -y remove xxx 删除,xxx指定的显示的信息:123[root@sjq01 ~]# yum -y remove docker.x86_64[root@sjq01 ~]# yum -y remove docker-client.x86_64[root@sjq01 ~]# yum -y remove docker-common.x86_64 yum 安装1[root@sjq01 ~]$ yum -y install docker 2.2、启动 :1[root@sjq01 ~]$ systemctl start docker.service 2.4、停止 :1[root@sjq01 ~]$ systemctl stop docker.service 2.4、配置国内镜像:Docker默认的镜像为 https://hub.docker.com/ ,从此镜像下载会非常慢,可根据如下配置使用国内镜像下载123456[root@sjq01 ~]$ vim /etc/docker/daemon.json# 添加内容如下:{\"registry-mirrors\": [\"https://docker.mirrors.ustc.edu.cn\"]} 重启Docker:配置完之后执行下面的命令,以使docker的配置文件生效1[root@sjq01 ~]$systemctl restart docker 三、常用操作3.1、列出镜像使用 docker images 命令列出 镜像,如下,表示有 nexus镜像123[root@sjq01 ~]# docker imagesREPOSITORY TAG IMAGE ID CREATED SIZEdocker.io/sonatype/nexus latest f7d8039f8626 2 weeks ago 454.6 MB 3.2、删除镜像docker rmi (名称:tag) 或 docker rmi (image id)名称与tag中间用英文冒号分隔,可以确定一个镜像,也可以直接使用 image id1[root@sjq01 ~]# docker rmi docker.io/sonatype/nexus:latest 或使用镜像id删除1[root@sjq01 ~]# docker rmi f7d8039f8626 3.3、导入/导出镜像 导入将本机文件导入到docker中执行命令的语法:docker load < 完整的文件路径名 1[root@sjq01 ~]# docker load < /root/nexus.tar.gz 导出将Docker镜像导出到本机文件中执行命令的语法:docker save (名称:tag) > /root/文件名.tar.gz会将指定的镜像导出到/root目录下,如导出nexus 到本地文件 1[root@sjq01 ~]# docker save docker.io/sonatype/nexus:latest > /root/nexus.tar.gz 四、Docker容器操作4.1、启动容器 以交互式启动:docker run -it –name 容器名称 镜像 /bin/bash1[root@sjq01 ~]# docker run -it --name my-nexus docker.io/sonatype/nexus:latest /bin/bash 容器名称可以任意指定,必须唯一退出交互模式,直接输入 exit 退出 以守护进程方式启动:docker run -d –name 容器名称 镜像1[root@sjq01 ~]# docker run -d --name my-nexus docker.io/sonatype/nexus:latest 容器名称必须唯一 4.2、停止容器docker stop 容器名称或容器Id1[root@sjq01 ~]# docker stop my-nexus 4.3、删除容器删除指定容器 :docker rm 容器名称或容器Id删除所有容器: docker rm 'docker ps -a -q' 4.4、查看容器使用 docker ps -a 查看:123456[root@sjq01 ~]# docker ps -aCONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMESd5579464bcef f7d8039f8626 \"/bin/sh -c '${JAVA_H\" 12 minutes ago Up 12 minutes 0.0.0.0:8081->8081/tcp nexus2b2ca50cb7f8 docker.io/sonatype/nexus:latest \"-p 8081:8081 --name \" 15 hours ago Created 8081/tcp stupefied_borgcb71f72f94b9 docker.io/sonatype/nexus:latest \"-p 8081:8081 --name\" 15 hours ago Created 8081/tcp prickly_lichterman[root@sjq01 ~]# 五、Docker 搭建Tomcat服务使用docker pull tomcat 下载镜像 启动Docker Tomcat服务 :docker run -d --name my-tomcat -p 8888:8080 镜像这里的-p 8888:8080 指的是宿主机的8888端口号映射到docker tomcat容器的8080端口号,此时如果启动成功,访问宿主机的8888服务,其实就是访问 docker tomcat的8080服务。 进入到 Docker Tomcat运行环境:docker exec -it my-tomcat /bin/bash 将 war 应用上传到 Docker Tomcat webapps目录中:docker cp 应用程序war包 my-tomcat:/usr/local/tomcat/webapps 回车上传完成后,tomcat 会自动加载上传的war,不需要手动重启。","tags":[{"name":"Docker","slug":"Docker","permalink":"https://huankai.github.io/tags/Docker/"}]},{"title":"Spring Cloud Eureka 服务发现","date":"2018-02-22T09:02:04.439Z","path":"2018/02/22/SpringCloud_01_Eureka/","text":"Spring Cloud是一套基于Spring Boot 实现的微服务开发工具,微服务(也称微服务架构),简单地说,就是将一个系统按照一定的规则有效的拆分成多个不同的服务,每个服务都能独立的进行开发、管理、扩展、维护。服务与服务之间可以通过Restful API 等方式进行相互调用。 Spring Cloud并没有重复制造轮子,它只是将业界多个开源的微服务框架集成起来,再通过Spring Boot进行包装屏蔽复杂的配置和实现原理,目的就是给开发者一套简单易懂、易部署与维护的分布式系统开发工具包。它提供了微服务开发所需要的配置管理、服务发现、负载均衡、断路器、智能路由、微代理、控制总线等组件。 1、EurekaEureka是一种基于REST 的服务,主要用于定位服务,以实现中间层服务器的负载均衡和故障转移。它是由 Spring Cloud Netflix(Spring Cloud 子项目)项目提供的。 1.1、Spring Cloud Netflix它主要是对 Netflix 开源的一系列产品进行包装,为 Spring Boot 应用程序提供自动配置的 Netflix OSS 集成。通过一些简单的注解,就能快速启用并构建大型的分布式系统。它提供的模块有:服务发现(Eureka)、断路器(Hystrix)、智能路由(Zuul)、客户端负载均衡(Ribbon)。 1.2、样例项目结构 1.3、服务注册中心在 pom.xml 中声明使用spring-cloud-starter-eureka-server启动器(本示例对应的项目是eureka-server)12345678910<dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-eureka-server</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-log4j2</artifactId> </dependency></dependencies> 使用@EnableEurekaServer注解将应用声明为 Eureka 服务器端(Eureka Server),从而启动 Eureka 服务注册中心的组件,对外提供服务注册和发现的功能。123456789@EnableEurekaServer@SpringBootApplicationpublic class EurekaServerApplication { public static void main(String[] args) { SpringApplication.run(EurekaServerApplication.class, args); }} 在默认的模式中,Eureka 服务器端也充当客户端并向给定的 serviceUrl 注册自己。在生产环境中,我们通常会有多台服务器端应用,但是为了简单起见,本示例使用单台服务器,因此需要禁掉 Eureka 服务器端应用的客户端行为: 1234567891011### 坐标: ${hk-eureka-simple}/src/main/resources/application.yml ###server: port: 8761eureka: instance: hostname: localhost client: register-with-eureka: false fetch-registry: false service-url: defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka 配置项 默认值 说明 eureka.instance.hostname - 实例的主机名 eureka.client.register-with-eureka true 该实例是否向 Eureka 服务器注册自己,以供外部应用发现自己。在某些情况下,你可能不希望当前的应用被外部的其他应用发现,而只是想从服务器发现其他服务的实例,此时你可以将此值设为 false eureka.client.fetch-registry true 该实例是否向 Eureka 服务器获取所有的注册信息表 eureka.client.service-url.defaultZone - 该实例与 Eureka 服务器通讯的 URL 地址列表。如果 Eureka 服务器地址不止一个,则使用英文的逗号分隔 Eureka 服务器默认监听 8761 端口来接收服务注册,除此之外它还提供一个可视化的直观页面,可以方便的查看注册的服务。EurekaServerApplication,访问:http://localhost:8761 从上图可以看到,此时还没有任何服务注册到 Eureka 服务器。 1.4、客户端(服务提供者)在 pom.xml 中声明使用spring-cloud-starter-eureka启动器(本示例对应的项目是user-service): 12345678910<dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-eureka</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-log4j2</artifactId> </dependency></dependencies> 使用@EnableEurekaClient或@EnableDiscoveryClient注解可以将应用声明为 Eureka 客户端(Eureka Client)。当客户端向 Eureka 服务器注册时,它会提供关于自身的一些元数据,例如主机和端口,健康指示符 URL,主页等信息。 123456789@SpringBootApplication@EnableEurekaClientpublic class UserServiceApplication { public static void main(String[] args) { SpringApplication.run(UserServiceApplication.class, args); }} 还需要配置才能找到 Eureka 服务器:123456789101112### 坐标: ${hk-user-service}/src/main/resources/application.yml ###server: port: 8881spring: application: name: user-serviceeureka: client: service-url: defaultZone: http://localhost:8761/eureka spring.application.name是 Eureka 客户端向服务器注册的服务ID和虚拟主机的名称。在 Eureka 服务器中,服务ID相同的实例将集群在一起。UserServiceApplication,再次访问:http://localhost:8761 从上图可以看到,客户端应用程序已经成功被注册了。 1.5、高可用以上示例都是单点运行的,不适合于生产环境。Eureka 官方给出的应用部署架构图是这样的: 下面对这个架构图来作一些解读,希望能帮助你更好的理解。 1.5.1、Region 和 Zone 在 Eureka 中有 Region(区域)和 Zone(Availability Zone,可用区)的区分。这是由于 Netflix 开源的 Eureka 旨在 AWS(Amazon Web Services,现在通常称为云计算)中运行,因此使用了一些 AWS 特有的概念术语。在非 AWS 的环境下,我们可以简单的将 Region 理解成大区或地域(如阿里云服务器的华南、华北地区),Zone 可以简单的理解成机房。如需了解更多相关信息,可参考:AWS的区域和可用区概念解释上图是一个 Eureka 集群的部署架构图,它面有 3 个 Zone(us-east-1c、us-east-1d、us-east-1e),它们都属于 us-east-1 这个 Region。 1.5.2、Eureka Server 每个 Zone 至少有一个 Eureka Server,能够对外提供服务发现和处理区域故障。在 Eureka Server 集群中,没有 Master/Slave 的区分,每个Eureka Server都是对等(Peer)的。它们除了可以作为服务注册中心外,也可以充当客户端向其它 Eureka Server 注册自己,并会从他们对等的节点(由 erueka.client.service-url.defaultZone配置指定)中复制(replicate) 所有服务注册表信息以达到同步的目的,如果因为某种原因导致失败,默认等待 5 分钟(可以通过 erueka.server.wait-time-in-ms-when-sync-empt配置),在这期间,它不向客户端提供服务注册信息。并且默认失败重试 5 次(可以通过eureka.server.number-of-replication-retries配置)。 1.5.3、Eureka ClientEureka 客户端应用分为两种,Application Service(服务提供者) 与 Application Client(服务消费者)。Application Service 通常需要向给定 serviceUrl 对应的Eureka Server 来注册自己,以供外部应用可以发现自己。其注册信息包括主机名和端口信息等元数据。然后默认以每隔30秒的频率向Erueka Server发送一次心跳(可以通过eureka.instance.lease-renewal-interval-in-seconds 配置)来续约服务。 Eureka Server默认为 90 秒内如果没有收到客户端的心跳,则它会将该客户端实例从它的注册表中剔除,以禁止该实例的流量(可以通过erueka.instance.lease-expiration-duration-in-seconds配置,注意:如果该值设置过大,即使实例不存在了,流量也可以路由到该实例;如果设置过小可能因为网络原因导致实例被服务器剔除;该值至少应该比发送心跳频率的间隔值要大)。 Eureka客户端默认会从注册的 Eureka Server中获取所有的服务注册表信息(可以通过eureka.client.fetch-registry配置),默认是以每隔30秒获取注册表信息(可以通过eureka.client.registry-fetch-interval-seconds配置)。 Application Client 可以不向任何Eureka Server注册自己,它可以只从Eureka Server获取注册过的服务列表,通过RESTFUL API 的方式远程调用服务提供者。 1.5.4、 Eureka Server 高可用样例本示例在同一主机运行多个Eureka Server 实例,由于Eureka 会过滤同一主机的相同主机名(详见 com.netflix.eureka.cluster.PeerEurekaNodes#isThisMyUrl),但它不检查端口,因此需要先定义至少两个不同的主机名,并使它们映射到127.0.0.1,这里采用修改 hosts文件的方式,Windows系统 hosts文件路径在C:\\Windows\\System32\\drivers\\etc\\hosts 打开此文件,在最后一行添加:1127.0.0.1 peer1 peer2 peer3 复制 hk-eureka-simple 为 hk-eureka-highavailability,修改 ${hk-eureka-highavailability}/src/main/resources/application.yml配置如下:1234567891011121314151617181920212223242526272829303132333435363738394041424344spring: application: name: eureka-server-highavailability profiles: active: - peer1 logging: config: classpath:log4j2.xml ---spring: profiles: peer1server: port: 8761 eureka: instance: hostname: peer1 client: service-url: defaultZone: http://peer2:8762/eureka,http://peer3.8763/eureka---spring: profiles: peer2server: port: 8762eureka: instance: hostname: peer2 client: service-url: defaultZone: http://peer1:8761/eureka,http://peer3.8763/eureka---spring: profiles: peer3server: port: 8763eureka: instance: hostname: peer3 client: service-url: defaultZone: http://peer1:8761/eureka,http://peer2.8762/eureka 这里配置了3 个Eureka Server实例 ,每个实例与其它两个实例分别进行两两相互注册关系图如下: 需要注意的是:Eureka Server的服务注册信息不能二次传播,如下图的实例关系配置是不可取的 上图的每个Eureka Server实例是单向向另外一个实例注册,如果现有一个新的客户端实例C 向 Server1 注册, Server1 与 Server2中都会有C 的注册信息,但是Server3 中没有C的注册信息(详见:com.netflix.eureka.registry.PeerAwareInstanceRegistryImpl#replicateToPeers)。 启动3个Eureka Server实例: 使用 Jar方式启动先将项目用maven打成 jar包,cmd 进入jar所在目录, 123456# 启动 eureka 实例1java -jar hk-eureka-highavailability-0.0.1-SNAPSHOT.jar# 启动 eureka 实例2java -jar -Dspring.profiles.active=peer2 hk-eureka-highavailability-0.0.1-SNAPSHOT.jar# 启动 eureka 实例3java -jar -Dspring.profiles.active=peer3 hk-eureka-highavailability-0.0.1-SNAPSHOT.jar 使用 STS/Eclipse 启动 123456# 启动 eureka 实例1右键 -> Run As -> Spring Boot App# 启动 eureka 实例2右键 -> Run As -> Run Configurations -> 左边 Spring Boot App ->右键 -> NEW -> 右边 Spring Boot 选项卡指定 project 与 Main type ,Arguments 选项卡中的 Program arguments 中添加 --spring.profiles.active=peer2 -> 右下角点击 Run 运行# 启动 eureka 实例3右键 -> Run As -> Run Configurations -> 左边 Spring Boot App ->右键 -> NEW -> 右边 Spring Boot 选项卡指定 project 与 Main type ,Arguments 选项卡中的 Program arguments 中添加 --spring.profiles.active=peer3 -> 右下角点击 Run 运行 启动 eureka 实例1与实例2时,可能会有连接的错误信息,因为无法连接上实例3,当实例1与实例2都启动成功后,在启动实例3时不会有错误信息存在。 浏览器访问 http://localhost:8761: 1.5.5、 Eureka Client 高可用样例 复制 hk-user-service 为 hk-user-service-highavailability,修改 ${hk-user-service-highavailability}/src/main/resources/application.yml配置如下:12345678910111213141516171819spring: application: name: user-service-highavailability profiles: active: client1eureka: client: service-url: defaultZone: http://peer1:8761/eureka/---spring: profiles: client1server: port: 8881---spring: profiles: client2server: port: 8882 客户端的eureka.client.service-url.defaultZone指定为当前 Zone 中任意一台服务注册中心的地址就可以,因为上例中配置的每台服务注册中心的服务注册表是两两相互进行复制的。启动 2 个 Eureka Client 实例:1234# 启动 client 实例1右键 -> Run As -> Spring Boot App# 启动 client 实例2右键 -> Run As -> Run Configurations -> 左边 Spring Boot App ->右键 -> NEW -> 右边 Spring Boot 选项卡指定 project 与 Main type ,Arguments 选项卡中的 Program arguments 中添加 --spring.profiles.active=client2 -> 右下角点击 Run 运行 浏览器重新刷新http://localhost:8761: 1.6、自我保护模式Eureka 默认开启了自我保护模式(可以通过eureka.server.enable-self-preservation配置)。该模式被激活的条件是:在 1 分钟后,Renews (last min)<Renews threshold。你可以在 Eureka Server 首页的右上侧可以看到: 参数名 描述 Renews threshold Eureka Server 期望每分钟收到客户端实例续约的总数 Renews (last min) Eureka Server 最后 1 分钟收到客户端实例续约的总数 1.6.1、服务器端续约阀值的计算源码(Renews threshold) 123# com.netflix.eureka.registry.PeerAwareInstanceRegistryImpl#openForTraffic #this.expectedNumberOfRenewsPerMin = count * 2;this.numberOfRenewsPerMinThreshold = (int) (this.expectedNumberOfRenewsPerMin * serverConfig.getRenewalPercentThreshold()); 其中,count 为 服务器的数量。数值 2 表示每 30 秒 1 个心跳,每分钟 2 个心跳的固定频率因子。 归纳公式:2M * renewalPercentThreshold。其中,M 为服务器的个数,计算结果只保留整数位。 renewalPercentThreshold 默认是 0.85(可以通过eureka.server.renewal-percent-threshold配置)。 其实这就是个固定值,因为对于每个 Eureka Server 来说,M 只能取 1。这段代码达到的效果是: 1.expectedNumberOfRenewsPerMin 重置为固定值 2;2.numberOfRenewsPerMinThreshold 的值被设置为 1; 1.6.2、客户器端续约阀值的计算源码(Renews threshold) 12345# com.netflix.eureka.registry.AbstractInstanceRegistry#register #if (this.expectedNumberOfRenewsPerMin > 0) { this.expectedNumberOfRenewsPerMin = this.expectedNumberOfRenewsPerMin + 2; this.numberOfRenewsPerMinThreshold = (int) (this.expectedNumberOfRenewsPerMin * serverConfig.getRenewalPercentThreshold());} 注:上面贴出的 PeerAwareInstanceRegistryImpl 继承自 AbstractInstanceRegistry。它们共享 expectedNumberOfRenewsPerMin 和 numberOfRenewsPerMinThreshold 属性,具体可自行翻阅源码。 设有 N 个客户端,服务器端先启动,expectedNumberOfRenewsPerMin 被重置为固定值 2。接着客户端依次启动:N = 1–>(2 + 2) * renewalPercentThresholdN = 2–>(2 + 2 + 2) * renewalPercentThresholdN = 3–>(2 + 2 + 2 + 2) * renewalPercentThreshold归纳公式:2(N + 1) * renewalPercentThreshold,计算结果只保留整数位。即,如果只有 1 个 Eureka Server 或者有多个 Eureka Server 但它们之间没有相互注册: 当 N = 0 时,只计算服务器端。Renews threshold= 1。由于没有客户端向服务器发送心跳,Renews (last min)<Renews threshold,Eureka 自我保护模式被激活;当 N ≠ 0 时,服务器端的计算结果被客户端覆盖,即只计算客户端;当 N = 2 时,Renews threshold= 2(N + 1) * renewalPercentThreshold = 2 * 3 * 0.85 = 5。2 个客户端以每 30 秒发送 1 个心跳,1 分钟后总共向服务器发送 4 个心跳,Renews (last min)<Renews threshold,Eureka 自我保护模式被激活;所以如果 N < 3,在 1 分钟后,服务器端收到的客户端实例续约的总数总是小于期望的阀值,因此 Eureka 的自我保护模式自动被激活。首页会出现警告信息: EMERGENCY! EUREKA MAY BE INCORRECTLY CLAIMING INSTANCES ARE UP WHEN THEY’RE NOT. RENEWALS ARE LESSER THAN THRESHOLD AND HENCE THE INSTANCES ARE NOT BEING EXPIRED JUST TO BE SAFE. 这种情况下,由于 Eureka Server 没有对等的节点,同步不到服务注册信息,默认需等待 5 分钟(可以通过eureka.server.wait-time-in-ms-when-sync-empty配置)。即 5 分钟后你应该看到此信息。 为避免这种情况发生,你可以: 关闭自我保护模式(eureka.server.enable-self-preservation设为 false) 降低 renewalPercentThreshold 的比例(eureka.server.renewal-percent-threshold设置为 0.5 以下,比如 0.49) 部署多个 Eureka Server 并开启其客户端行为(eureka.client.register-with-eureka不要设为 false,默认为 true) 如果是采取部署多个 Eureka Server 并开启其客户端行为使其相互注册。假设有 M 个 Eureka Server,那么,每个 Eureka Server 每分钟可以额外收到 2 (M - 1) 个心跳。例如:当 M = 1,N = 2 时,Renews threshold= 2(N + 1) renewalPercentThreshold = 2 3 0.85 = 5,2 个客户端以每 30 秒发送 1 个心跳,1 分钟后总共向服务器发送 4 个心跳,Renews (last min)Renews threshold; Eureka 的自我保护模式是有意义的,该模式被激活后,它不会从注册列表中剔除因长时间没收到心跳导致租期过期的服务,而是等待修复,直到心跳恢复正常之后,它自动退出自我保护模式。这种模式旨在避免因网络分区故障导致服务不可用的问题。例如,两个客户端实例 C1 和 C2 的连通性是良好的,但是由于网络故障,C2 未能及时向 Eureka 发送心跳续约,这时候 Eureka 不能简单的将 C2 从注册表中剔除。因为如果剔除了,C1 就无法从 Eureka 服务器中获取 C2 注册的服务,但是这时候 C2 服务是可用的。所以,Eureka 的自我保护模式最好还是开启它。 1.7、Eureka 与 Zookeeper 的区别Eureka 最大程度上保证 AP(Availability,可用性;Partition-tolerance,分区容错性),而 Zookeeper 保证的是 CP(Consistency,一致性;Partition-tolerance,分区容错性)。如果因为网络分区故障导致服务器(master 节点)无法与其它节点联系,对于 Zookeeper 来说,这是不能容忍的。它会对剩下的节点重新进行 leader 选举,在这期间,整个 Zookeeper 集群是不可用的,这就直接导致了所有注册服务瘫痪的现象。而对于 Eureka 来说,每个节点都是对等的,失去了一个节点,就自动切换到其它节点,只要还有一个 Eureka 节点存在,就能正常对外提供注册服务。Eureka 可以很好的应对因网络分区故障而导致的部分节点失去联系的状况。","tags":[{"name":"Spring Cloud","slug":"Spring-Cloud","permalink":"https://huankai.github.io/tags/Spring-Cloud/"}]},{"title":"Thymeleaf 教程","date":"2018-02-22T07:15:02.821Z","path":"2018/02/22/Thymeleaf_01/","text":"Thymeleaf 是一个服务器端 Java 模板引擎,能够处理 HTML、XML、CSS、JAVASCRIPT 等模板文件。Thymeleaf 模板可以直接当作静态原型来使用,它主要目标是为开发者的开发工作流程带来优雅的自然模板,也是 Java 服务器端 HTML5 开发的理想选择。 1. 创建模板文件创建一个 HTML 模板文件: 12345678910<!DOCTYPE HTML><html xmlns:th=\"http://www.thymeleaf.org\"><head> <title>Index Page</title> <meta http-equiv=\"Content-Type\" content=\"text/html; charset=UTF-8\" /></head><body> <p th:text=\"${message}\">Welcome to BeiJing!</p></body></html> 通过引入 Thymeleaf 命名空间。th:text用于处理p标签体的文本内容。该模板文件直接在任何浏览器中正确显示,浏览器会自动忽略它们不能理解的属性th:text。但这不是一个真正有效的 HTML5 文档,因为 HTML5 规范是不允许使用th:*这些非标准属性的。我们可以切换到 Thymeleaf 的data-th-*语法,以此来替换th:*语法: 12345678910<!DOCTYPE HTML><html xmlns:th=\"http://www.thymeleaf.org\"><head> <title>Index Page</title> <meta http-equiv=\"Content-Type\" content=\"text/html; charset=UTF-8\" /></head><body> <p data-th-text=\"${message}\">Welcome to BeiJing!</p></body></html> HTML5 规范是允许data-*这样自定义的属性的。th:*和data-th-*这两个符号是完全等效且可以互换的。但为了简单直观和代码的紧凑性,本文采用th:*的表示形式。 2. 标准表达式语法Thymeleaf 提供了非常丰富的标准表达式语法,总共有 8 大类: 简单表达式 字面值 文本操作 算术运算 布尔运算 比较和相等 条件运算 无操作符 2.1 简单表达式 语法 名称 描述 作用 ${…} Variable Expressions 变量表达式 取出上下文变量的值 *{…} Selection Variable Expressions 选择变量表达式 取出选择的对象的属性值 #{…} Message Expressions 消息表达式 使文字消息国际化,I18N @{…} Link URL Expressions 链接表达式 用于表示各种超链接地址 ~{…} Fragment Expressions 片段表达式 引用一段公共的代码片段 2.1.1 ${…}1234567@GetMapping(\"/standard-expression-syntax/variables\")public String variables(ModelMap model, HttpSession session) { model.put(\"now\", new Date()); model.put(\"message\", \"Welcome to BeiJing!\"); session.setAttribute(\"user\", new User(\"test\", \"男\", 24)); ... ...} 通过变量表达式${}取出上下文环境中的message变量:12<!-- Welcome to BeiJing! --><p th:text=\"${message}\"></p> 它相当于:1ctx.getVariable(\"message\"); 2.1.2 *{…}变量表达式${}是面向整个上下文的,而选择变量表达式*{}的上下文是父标签(th:object)所选择的对象:12345<div th:object=\"${session.user}\"> <p th:text=\"*{name}\"></p> <p th:text=\"*{sex}\"></p> <p th:text=\"*{age}\"></p></div> 它相当于:12345<div> <p th:text=\"${session.user.name}\"></p> <p th:text=\"${session.user.sex}\"></p> <p th:text=\"${session.user.age}\"></p></div> 如果对象没有被选择,那么,*{}和${}表达式所达到的效果是完全相同的:12<p th:text=\"*{session.user.name}\"></p><p th:text=\"${session.user.name}\"></p> 2.1.3 #{…}消息表达式可用于国际化文字信息。首先我们来了解一下 i18n 资源文件的命名规则: basename.properties basename_language.properties basename_language_country.properties basename 是自定义的资源文件名称,language和country必须是 Java 支持的语言和国家。basename.properties是缺省加载的资源文件,当客户端根据本地语言查找不到相关的资源文件时,则使用该配置文件。 创建文件 src/main/resources/messages.properties,内容如下:1welcome.message = 北京欢迎你! 创建文件src/main/resources/messages_en_US.properties,内容如下:1welcome.message = Welcome to BeiJing! messages是 Spring Boot 加载资源文件默认采用的名称(basename),如果你所使用的资源文件名称不是以messages命名或所使用的资源文件不是在src/main/resources根目录,你可以通过spring.messages.basename属性来做具体的配置。如,资源文件messages.properties和messages_en_US.properties假设它们所在的目录位置是src/main/resources/i18n。 application.properties 配置示例:1spring.messages.basename:i18n/messages application.yml 配置示例:123spring messages basename: i18n/messages 静态文本消息示例:12<!-- 北京欢迎你! --><p th:text=\"#{welcom.message}\"></p> 消息表达式#{}是不允许直接处理非静态的文本消息的,但是你可以在资源文件中通过使用占位符{}来处理非静态的文本消息:messages.properties 配置示例: 1welcome.user.message = {0}, 北京欢迎你! messages_en_US.properties 配置示例:1welcome.user.message = {0}, Welcome to BeiJing! 非静态文本消息,以参数的形式传递变量的值:12<!-- test, 北京欢迎你! --><p th:text=\"#{welcome.user.message(${session.user.name})}\"></p> 2.1.4 @{…}链接表达式@{}是专门用来处理 URL 链接地址的。绝对地址示例:12<!-- https://huankai.github.io --><p th:text=\"@{https://huankai.github.io}\"></p> 页面相对地址示例:12<!-- commons/base.html --><p th:text=\"@{commons/base.html}\"></p> 上下文相对地址(相对于当前的服务)示例:12<!-- /css/mian.css --><p th:text=\"@{/css/mian.css}\"></p> 服务器相对地址(相对于部署在同一个服务器中的不同服务)示例:12<!-- /image/upload --><p th:text=\"@{~/image/upload}\"></p> 参数使用示例:12345678<!-- /css/mian.css?v=1.0 --><p th:text=\"@{/css/mian.css(v=1.0)}\"></p><!-- /user/order?username=fanlychie --><p th:text=\"@{/user/order(username=${session.user.name})}\"></p><!-- /user/order?username=fanlychie&status=PAIED --><p th:text=\"@{/user/order(username=${session.user.name},status='PAIED')}\"></p><!-- /user/fanlychie/info --><p th:text=\"@{/user/{username}/info(username=${session.user.name})}\"></p> 2.1.5 ~{…}片段表达式~{}可以用来引用一段公共的 HTML 代码片段。 语法 描述 ~{templatename} 引用整个模板文件的代码片段 ~{templatename :: selector} selector 可以是 th:fragment 指定的名称或其他选择器。如类选择器、ID选择器等 ~{::selector} 相当于 ~{this :: selector},表示引用当前模板定义的代码片段 在 Thymeleaf 模板文件中,你可以使用th:fragment属性来定义一段公共的代码片段,然后你可以通过使用th:insert、th:replace、th:include(Thymeleaf 3.0 开始不再推荐使用,本文也将不再介绍它)属性来将这些公共的代码片段引入到模板文件中来。src/main/resources/templates/base.html,通过th:fragment属性定义一段公共的代码片段: 1<div id=\"footer\" th:fragment=\"footerFragment\">&copy; 2017 huangkai</div> src/main/resources/templates/index.html,通过th:insert属性引用一段公共的代码片段:1<div th:insert=\"~{base :: footerFragment}\"></div> 其中,~{}是可选的,我们可以去掉这层的包裹:1<div th:insert=\"base :: footerFragment\"></div> 若 index.html 与 base.html 不在同级目录,如 templates/commons/base.html:1<div th:insert=\"~{commons/base :: footerFragment}\"></div> 使用th:fragment属性定义代码片段时,你还可以声明一组参数:123456789<div th:fragment=\"crumbs(parent, child)\"> <i th:text=\"${parent}\"></i> <i th:text=\"${child}\"></i></div> <!--<i>用户中心</i><i>我的订单</i>--><div th:insert=\"::crumbs('用户中心', '我的订单')\"></div> 此外,我们还可以通过类选择器、ID选择器等来引用公共的代码片段:1<div th:insert=\"~{base :: #footer}\"></div> 除了th:insert属性th:replace也可以用来引用公共的代码片段。不同的是,th:insert是直接将代码片段插入到标签体内,而th:replace则是用代码片段直接替换标签体内容。1234567891011<!--<div> <div id=\"footer\">© 2017 huangkai</div></div>--><div th:insert=\"~{base :: footerFragment}\"></div> <!--<div id=\"footer\">© 2017 huangkai</div>--><div th:replace=\"~{base :: footerFragment}\"></div> 2.1.6 内置对象 对象 描述 #ctx 上下文对象 #vars 同 #ctx,表示上下文变量 #locale 上下文本地化(特定的地理区域)变量,可参考 java.util.Locale #request HttpServletRequest 对象,可参考 javax.servlet.http.HttpServletRequest #response HttpServletResponse 对象,可参考 javax.servlet.http.HttpServletResponse #session HttpSession 对象,可参考 javax.servlet.http.HttpSession #servletContext ServletContext 对象,可参考 javax.servlet.ServletContext #ctx示例:123456<!-- zh_CN --><p th:text=\"${#ctx.getLocale()}\"></p><!-- Welcome to BeiJing! --><p th:text=\"${#ctx.getVariable('message')}\"></p><!-- true --><p th:text=\"${#ctx.containsVariable('message')}\"></p> #vars示例:123456<!-- zh_CN --><p th:text=\"${#vars.getLocale()}\"></p><!-- Welcome to BeiJing! --><p th:text=\"${#vars.getVariable('message')}\"></p><!-- true --><p th:text=\"${#vars.containsVariable('message')}\"></p> #locale示例:123456789101112<!-- zh_CN --><p th:text=\"${#locale}\"></p><!-- CN --><p th:text=\"${#locale.country}\"></p><!-- 中国 --><p th:text=\"${#locale.displayCountry}\"></p><!-- zh --><p th:text=\"${#locale.language}\"></p><!-- 中文 --><p th:text=\"${#locale.displayLanguage}\"></p><!-- 中文 (中国) --><p th:text=\"${#locale.displayName}\"></p> #request示例:12345678910111213141516171819202122<!-- HTTP/1.1 --><p th:text=\"${#request.protocol}\"></p><!-- http --><p th:text=\"${#request.scheme}\"></p><!-- localhost --><p th:text=\"${#request.serverName}\"></p><!-- 8080 --><p th:text=\"${#request.serverPort}\"></p><!-- GET --><p th:text=\"${#request.method}\"></p><!-- /standard-expression-syntax/variables --><p th:text=\"${#request.requestURI}\"></p><!-- http://localhost:8080/standard-expression-syntax/variables --><p th:text=\"${#request.requestURL}\"></p><!-- /standard-expression-syntax/variables --><p th:text=\"${#request.servletPath}\"></p><!-- java.util.Collections$3@203646fe --><p th:text=\"${#request.parameterNames}\"></p><!-- {q=[Ljava.lang.String;@3308c69f} --><p th:text=\"${#request.parameterMap}\"></p><!-- q=expression --><p th:text=\"${#request.queryString}\"></p> 注意,请求地址的 URL 参数直接通过#request.x是取不出来的,需要使用param.x语法来取出。如,URL:/standard-expression-syntax/variables?q=expression,取出 q 参数的正确姿势:1<p th:text=\"${param.q}\"></p #response示例:12345678<!-- 200 --><p th:text=\"${#response.status}\"></p><!-- 8192 --><p th:text=\"${#response.bufferSize}\"></p><!-- UTF-8 --><p th:text=\"${#response.characterEncoding}\"></p><!-- text/html;charset=UTF-8 --><p th:text=\"${#response.contentType}\"></p> #session示例:123456<!-- 2BCB2A0EACFF2D9D249D9799431B5127 --><p th:text=\"${#session.id}\"></p><!-- 1499786693244 --><p th:text=\"${#session.lastAccessedTime}\"></p><!-- huangkai --><p th:text=\"${#session.getAttribute('user').name}\"></p> 注意,放到会话里面的对象直接通过#session.x是取不出来的,需要使用session.x语法来取出。如,取出会话里面的 user 对象的正确姿势:1<p th:text=\"${session.user.name}\"></p> 2.1.7 工具类 对象 描述 #messages 消息工具类,与 #{…} 作用相同 #uris 地址相关的工具类 #conversions 对象转换工具类 #dates 日期时间工具类 #calendars 日历工具类 #numbers 数字工具类 #strings 字符串工具类 #objects 对象工具类 #bools 布尔工具类 #arrays 数组工具类 #lists List 工具类 #sets Set 工具类 #maps Map 工具类 1234<!-- false --><p th:text=\"${#strings.isEmpty(message)}\"></p><!-- 2017-07-27 14:37:25 --><p th:text=\"${#dates.format(now, 'yyyy-MM-dd HH:mm:ss')}\"></p> 2.2 字面值所谓字面值,首先它不是一个变量,它是一个具体的确切的值,通常这些值是比较简单的,例如:18、’welcome’等,它们没有名称,以至于我们只能用值来称呼它们,因此我们称其为字面值。 2.2.1 文字字面值文字字面值是用单引号引起来的任何字符内容,如果字符内容里面含有单引号,则需要进行转义:1234<!-- Welcome to BeiJing! --><p th:text=\"'Welcome to BeiJing!'\"></p><!-- 'Welcome to BeiJing!' --><p th:text=\"'\\'Welcome to BeiJing!\\''\"></p> 2.2.2 数字字面值1234<!-- 2017 --><p th:text=\"2017\"></p><!-- 2018 --><p th:text=\"2017 + 1\"></p> 2.2.3 布尔字面值1234<!-- false --><p th:text=\"1 > 2\"></p><!-- 否 --><p th:text=\"1 > 2 ? '是' : '否'\"></p> 2.2.4 空字面值12<!-- false --><p th:text=\"${user == null}\"></p> 2.2.5 字面令牌字面令牌(Literal Tokens)的内容只能含有(不能含有空格、特殊符号等): 大写或小写的字母、中文等不含空格和特殊符号的文本 0 到 9 的数字 中括号 下划线 连字符(-) 点符号(.) 实际上,数字、布尔和空字面值都是字面令牌的特殊情况。字面令牌能够用来对标准表达式语法进行简化,我们可以将包裹它的内容的单引号去掉:123<p th:text=\"Welcome to BeiJing!\"></p>它等效于:<p th:text=\"'Welcome to BeiJing!'\"></p> 2.3 文本操作我们可以对文本内容进行两种常用的操作,它们分别为字符串连接和字符串替换。 2.3.1 字符串连接不管是字面值还是表达式的结果,我们都可以使用+符号将它们连接起来:12<!-- Welcome to BeiJing! --><p th:text=\"'Welcome to ' + ${location} + '!'\"></p> 2.3.2 字面值替换符号||可以用来将字面值和表达式包裹起来,这样就能方便的替换变量的值,而不需要使用+连接符:12<!-- Welcome to BeiJing! --><p th:text=\"|Welcome to ${location}!|\"></p> 2.4 算术运算支持+(加)、-(减)、*(乘)、/(除)、%(模)运算:1234567891011121314<!-- 6 --><p th:text=\"4 + 2\"></p><!-- 2 --><p th:text=\"4 - 2\"></p><!-- 8 --><p th:text=\"4 * 2\"></p><!-- 2 --><p th:text=\"4 / 2\"></p><!-- 0 --><p th:text=\"4 % 2\"></p><!-- 2 --><p th:text=\"${pagination.page + 1}\"></p><!-- 2 --><p th:text=\"${pagination.page} + 1\"></p> 2.5 布尔运算支持and(且)、or(或)、!(非)、not(非)运算:1234<p th:text=\"${user.online and user.vip}\"></p><p th:text=\"${user.online or user.vip}\"></p><p th:text=\"${!user.online}\"></p><p th:text=\"${not user.online}\"></p> 2.6 比较和相等支持<(lt)、>(gt)、<=(le)、>=(ge)、==(eq)、!=(ne):123456<p th:text=\"${user.age < 60}\"></p><p th:text=\"${user.age <= 60}\"></p><p th:text=\"${user.age > 18}\"></p><p th:text=\"${user.age >= 18}\"></p><p th:text=\"${user.age == 18}\"></p><p th:text=\"${user.age != 18}\"></p> 2.7 条件运算三元运算符:(if) ? (then) : (else)12<p th:text=\"${user.online ? '在线' : '离线'}\"></p><p th:text=\"${user.online ? (user.vip ? 'VIP用户在线' : '普通用户在线') : '离线'}\"></p> 二元运算符:(value) ?: (defaultValue)。其中,value非空(null)即真,条件为真时输出value,否则输出defaultValue。假设token = null,user.email = [email protected]<!-- 你还没有登录,请先登录 --><p th:text=\"${token} ?: '你还没有登录,请先登录'\"></p><!-- [email protected] --><p th:text=\"${user.email} ?: '你还没有绑定邮箱'\"></p> 2.8 无操作符当模板运行在服务器端时,Thymeleaf 会解析th:*属性的具体值替换标签体的内容。无操作符(_)则允许你使用原型标签体的内容作为默认值:12<!-- 你还没有登录,请先登录 --><p th:text=\"${token} ?: _\">你还没有登录,请先登录</p> 3. 使用文本首先介绍两个最基础的th:*属th:text和th:utext,它们都是用于处理文本消息内容 3.1 th:text标签体中展示表达式评估结果的文本内容:1<p th:text=\"${message}\"></p> 使用外部化的文本内容:1<p th:text=\"${message}\">Welcome to BeiJing!</p> 当它作为静态文件直接运行时,浏览器会自动忽略它不能识别的th:text属性,而显示 p 标签体的文本内容Welcome to BeiJing!当它作为模板文件运行在服务器端时,th:text属性的具体值将会替换 p 标签体的文本内容。 3.2 th:utext属性th:utext与th:text的区别在于: th:text默认会对含有 HTML 标签的内容进行字符转义; th:utext(Unescaped Text)则不会对含有 HTML 标签的内容进行字符转义; 假设:message = “Welcome to BeiJing!“。使用th:text属性:1<p th:text=\"${message}\"></p> th:text效果:<b>Welcome to BeiJing!</b> 使用th:utext属性:1<p th:utext=\"${message}\"></p> th:utext效果:Welcome to BeiJing! 4. 设置属性值在 Thymeleaf 模板文件中,你可以使用th:*(或者使用th:attr属性)来设置任意的 HTML5 标签属性的值。不仅如此,你还可以th:*-*来同时为多个不同的标签属性设置相同的一个值,甚至你可以使用th:attrappend和th:attrprepend来追加新的值到现有的标签属性值中。 4.1 th:attr这种方式是不被推荐的,了解一下就行。下面是用th:attr=”href=…”来设置标签href属性的值:1<a th:attr=\"href=@{https://www.google.com.hk}\">谷歌一下你就知道</a> 4.2 th:*显然th:attr=”href=@{http://www.baidu.com}”不够简洁,我们更推荐下面的这种语法:1<a th:href=\"@{https://www.google.com.hk}\">谷歌一下你就知道</a> 其中th:*中的 * 可以是 HTML5 支持的任意属性名称,甚至这些属性名称可以是自定义的:12<!-- <div item-id=\"1001\">Welcome to BeiJing!</div> --><div th:item-id=\"${user.id}\">Welcome to BeiJing!</div> 4.3 th:*-*如果想要同时为标签的多个不同属性设置相同的一个值,可以使用th:*-*的语法:1<img src=\"logo.png\" th:alt-title=\"LOGO图片\"> 它相当于:1<img src=\"logo.png\" th:alt=\"LOGO图片\" th:title=\"LOGO图片\"> 4.4 th:attrappend & th:attrprependth:attrappend和th:attrprepend可以将表达式的结果分别追加到指定的属性值之后和之前。1234<!-- <button class=\"btn enable\">购买</button> --><button class=\"btn\" th:attrappend=\"class=${outOfStock} ? ' enable' : ' disable'\">购买</button><!-- <button class=\"enable btn\">购买</button> --><button class=\"btn\" th:attrprepend=\"class=${outOfStock} ? 'enable ' : 'disable '\">购买</button> 另外,还有两个常用的具体附加属性th:classappend=”…”和th:styleappend=””。它们分别用来代替th:attrappend=”class=…”和th:attrappend=”style=…”。 12<!-- <button class=\"btn enable\">购买</button> --><button class=\"btn\" th:classappend=\"${outOfStock} ? ' enable' : ' disable'\">购买</button> 4.5 布尔属性在 HTML 中有些属性是布尔属性,布尔属性是指没有值的属性,如readonly、checked、selected等。它们若存在那就意味着值为 true。12345678910<input type=\"checkbox\" name=\"rememberme\" checked /> 记住我<input type=\"radio\" name=\"sex\" value=\"male\" checked> 男<input type=\"radio\" name=\"sex\" value=\"female\"> 女<input type=\"text\" name=\"appId\" value=\"J123654\" readonly><select> <option selected>北京</option> <option>上海</option> <option>广州</option> <option>深圳</option></select> Thymeleaf 也允许我们通过th:*(这里的*表示任意的布尔属性) 来选择是否使用这些布尔属性。1<input type=\"checkbox\" name=\"rememberme\" ch:checked=\"${rememberme}\" /> 记住我 正如你所见,如果表达式的结果为true,则自动勾选复选框,若为false,则不会自动勾选。 5. 遍历遍历(迭代)的语法th:each=”自定义的元素变量名称 : ${集合变量名称}”: 123456<div> <spn>你所在城市:</spn> <select name=\"mycity\"> <option th:each=\"city : ${cities}\" th:text=\"${city.name}\"></option> </select></div> 属性th:each提供了一个用于跟踪迭代的状态变量,它包含以下几个属性: 属性 类型 描述 index int 当前迭代的索引,从 0 开始 count int 当前迭代的计数,从 1 开始 size int 集合中元素的总个数 current int 当前的元素对象 even boolean 当前迭代的计数是否是偶数 odd boolean 当前迭代的计数是否是奇数 first boolean 当前元素是否是集合的第一个元素 last boolean 当前元素是否是集合的最后一个元素 状态变量的使用语法:th:each=”自定义的元素变量名称, 自定义的状态变量名称 : ${集合变量名称}”:123456<div> <spn>所在城市:</spn> <select name=\"mycity\"> <option th:each=\"city, status : ${cities}\" th:text=\"${city.name}\" th:item-index=\"${status.count}\"></option> </select></div> 不管什么时候,Thymeleaf 始终会为每个th:each创建一个状态变量,默认的状态变量名称就是自定义的元素变量名称后面加Stat字符串组成:123456<div> <spn>所在城市:</spn> <select name=\"mycity\"> <option th:each=\"city : ${cities}\" th:text=\"${city.name}\" th:item-index=\"${cityStat.count}\"></option> </select></div> 6. 条件判断条件判断语句有三种,分别是:th:if、th:unless、th:swith。 6.1 th:if当表达式的评估结果为真时则显示内容,否则不显示: 1<a th:href=\"@{/user/order(uid=${user.id})}\" th:if=\"${user != null}\">我的订单</a> 真假评估的依据: 当表达式的值不为空(null)时 如果表达式的值是一个布尔类型,且值为true评估为真,否则为假 如果表达式的值是一个数字类型,且值为非0评估为真,否则为假 如果表达式的值是一个字符类型,且值为非0评估为真,否则为假 如果表达式的值是一个字符串类型,且值为非”false”、”off”、”no”评估为真,否则为假 如果表达式的值不是一个布尔、数字、字符或字符串评估为真 当表达式的值为空(null)时,评估结果为假 因此,上面代码我们也可以简写成:1<a th:href=\"@{/user/order(uid=${user.id})}\" th:if=\"${user}\">我的订单</a> 但是,为了代码的可读性,我们并不建议这样使用。 6.2 th:unlessth:unless与th:if判断恰好相反,当表达式的评估结果为假时则显示内容,否则不显示:1<a th:href=\"@{/user/order(uid=${user.id})}\" th:unless=\"${user == null}\">我的订单</a> 6.3 th:swith多路选择语句,它需要搭配th:case来使用: 1234<div th:switch=\"${user.role}\"> <p th:case=\"admin\">管理员</p> <p th:case=\"user\">普通用户</p></div> 7. 定义局部变量使用th:with属性可以定义局部变量:123<p th:with=\"name='fanlychie'\"> <span th:text=\"${name}\"></span></p> 同时定义多个局部变量时,用英文,号分隔开: 123<p th:with=\"name=${user.name},age={user.age}\"> ......</p> 8. 注释下面介绍常见的两种注释: 8.1 标准注释语法:<!-- ... --->,注释的代码块会在文件源代码中显示出来。 8.1.1 单行注释1<!-- <span>${message}</span> ---> 8.1.2 多行注释123456<!--<div th:switch=\"${user.role}\"> <p th:case=\"admin\">管理员</p> <p th:case=\"user\">普通用户</p></div>---> 8.2 解析器级注释语法:<!--/* ... */-->,注释的代码块会在引擎解析的时候抹去。 8.2.1 单行注释:1<!--/* <span>${message}</span> */--> 8.2.2 多行注释123456<!--/*--><div th:switch=\"${user.role}\"> <p th:case=\"admin\">管理员</p> <p th:case=\"user\">普通用户</p></div><!--*/--> 9. 内联表达式内联表达式允许我们直接在 HTML 文本中使用标准表达式,而不需要使用th:*标签属性。 9.1 [[…]][[]]相当于th:text,对含有 HTML 标签的内容自动进行字符转义。1<p>The message is : [[${htmlContent}]]</p> 9.2 [(…)][()]相当于th:utext,对含有 HTML 标签的内容不进行字符转义。1<p>The message is : [(${htmlContent})]</p> 9.3 th:inline我们已经了解到,使用[[]]和[()]语法可以直接在 HTML 文本中使用标准表达式,如果想要使用更多高级的功能,需要使用th:inline属性来激活,它的取值如下: 值 描述 none 禁止内联表达式,可以原样输出 [[]] 和 [()] 字符串 text 文本内联,可以使用 th:each 等高级语法 css 样式内联,如:<style th:inline="css"> javascript 脚本内联,如:<style th:inline="javascript"> 9.3.1 none12<!-- [[1, 2], [3, 4]] --><p th:inline=\"none\">[[1, 2], [3, 4]]</p> 9.3.2 text123456<!-- 北京 上海 广州 深圳 --><p th:inline=\"text\"> [# th:each=\"city : ${cities}\"] [(${city.name})] [/]</p> 9.3.3 css12345<style th:inline=\"css\"> body { background-color:[[${bgColor}]]; }</style> 9.3.4 javascript1234<script th:inline=\"javascript\"> var user = [[${user}]]; alert(\"用户名:\" + user.name);</script> 参考文档文献链接 : http://www.thymeleaf.org/doc/tutorials/3.0/usingthymeleaf.html","tags":[{"name":"Thymeleaf","slug":"Thymeleaf","permalink":"https://huankai.github.io/tags/Thymeleaf/"}]},{"title":"Linux Svn 服务器搭建","date":"2018-02-22T07:15:02.811Z","path":"2018/02/22/Svn服务器搭建/","text":"1. 检查svn版本是否有安装1rpm -qa subversion 执行上面的命令,没有任何显示,表示没有安装,如果有,执行 yum remove subversion 删除。 2. 执行安装1yum -y install subversion 等待安装完成即可 3. 配置和启动svn服务器 创建目录 : 12mkdir -p /data/svn/data # svn数据保存目录 -p 参数表示如果子目录不存在,也会级联创建目录mkdir -p /data/svn/passwd #svn公共用户密码与权限目录 启动 svn服务: 123svnserve -d -r /data/svn/data/-d 守护进程启动 -r 指定svn目录 查看服务进程: 123ps -ef|grep svnroot 1748 1 0 21:44 ? 00:00:00 svnserve -d -r /data/svn/data/root 1771 1401 0 21:45 pts/0 00:00:00 grep --color=auto svn 如上,表示svn服务已启动 查看端口号: svn默认使用的端口号为 3690123lsof -i:3690COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAMEsvnserve 1748 root 3u IPv4 24804 0t0 TCP *:svn (LISTEN) 或者使用如下方式查看12netstat -lntup|grep 3690tcp 0 0 0.0.0.0:3690 0.0.0.0:* LISTEN 1748/svnserve 4. 创建svn仓库123456789101112131415161718[root@huangkai200 data]# svnadmin create hk #创建仓库, hk 是仓库名称[root@huangkai200 data]# lshk[root@huangkai200 data]# cd hk/[root@huangkai200 hk]# ll # 查看仓库目录结构total 16drwxr-xr-x 2 root root 51 Mar 12 21:58 conf # svn仓库配置文件 drwxr-sr-x 6 root root 4096 Mar 12 21:58 db #数据存放文件-r--r--r-- 1 root root 2 Mar 12 21:58 format drwxr-xr-x 2 root root 4096 Mar 12 21:58 hooksdrwxr-xr-x 2 root root 39 Mar 12 21:58 locks-rw-r--r-- 1 root root 229 Mar 12 21:58 README.txt[root@huangkai200 hk]# cd conf/ [root@huangkai200 conf]# ll total 12-rw-r--r-- 1 root root 1080 Mar 12 21:58 authz #权限管理文件 -rw-r--r-- 1 root root 309 Mar 12 21:58 passwd #用户与密码文件 -rw-r--r-- 1 root root 3090 Mar 12 21:58 svnserve.conf #主配置文件,包含上面authz 与 passwd文件 5、修改 svnserver.conf 主配置文件123456789101112131415161718192021222324252627282930313233343536373839404142[root@huangkai200 conf]# vi svnserve.conf### This file controls the configuration of the svnserve daemon, if you### use it to allow access to this repository. (If you only allow### access through http: and/or file: URLs, then this file is### irrelevant.)### Visit http://subversion.apache.org/ for more information. [general]### The anon-access and auth-access options control access to the### repository for unauthenticated (a.k.a. anonymous) users and### authenticated users, respectively.### Valid values are \"write\", \"read\", and \"none\".### Setting the value to \"none\" prohibits both reading and writing;### \"read\" allows read-only access, and \"write\" allows complete### read/write access to the repository.### The sample settings below are the defaults and specify that anonymous### users have read-only access to the repository, while authenticated### users have read and write access to the repository.# anon-access = read #将此项目默认配置改为 none,svn默认对未认证的用户有只读权限,我们要将此用户的权限设置为 none,然后将 20行 的 auth-access 的注解删除anon-access = none auth-access = write### The password-db option controls the location of the password### database file. Unless you specify a path starting with a /,### the file's location is relative to the directory containing### this configuration file.### If SASL is enabled (see below), this file will NOT be used.### Uncomment the line below to use the default password file.# svn默认每个仓库会指定密码文件 ,为了多个仓库统一密码管理 ,将此选项设置为公共的密码文件password-db = /data/svn/passwd/passwd### The authz-db option controls the location of the authorization### rules for path-based access control. Unless you specify a path### starting with a /, the file's location is relative to the the### directory containing this file. If you don't specify an### authz-db, no path-based access control is done.### Uncomment the line below to use the default authorization file.authz-db = /data/svn/passwd/authz #和密码一样,将此选项设置为公共的权限文件### This option specifies the authentication realm of the repository.### If two repositories have the same authentication realm, they should### have the same password database, and vice versa. The default realm### is repository's uuid.# realm = My First Repository 6、拷贝authz 和 passwd 文件 到 公共用户与密码的目录1234567891011121314[root@huangkai200 hk]# cp authz passwd /data/svn/passwd[root@huangkai200 passwd]# ll # 可以看到,如下两个文件的权限为 644(文件所有者可读可写,所在组可读,其它组可读,这样,其它用户就可以看到这两个文件 ,文件的安全性就降低了)total 8-rw-r--r-- 1 root root 1080 Mar 12 22:27 authz-rw-r--r-- 1 root root 329 Mar 12 22:25 passwd[root@huangkai200 passwd]# 修改文件权限:[root@huangkai200 passwd]# chmod 600 * #(给文件所有者可读写权限,其它用户没有权限)[root@huangkai200 passwd]# lltotal 8-rw------- 1 root root 1080 Mar 12 22:27 authz-rw------- 1 root root 329 Mar 12 22:25 passwd 7、SVN添加账户在 passwd文件中[users]下按例子添加账户12345678910[root@huangkai200 passwd]# vi passwd### This file is an example password file for svnserve.### Its format is similar to that of svnserve.conf. As shown in the### example below it contains one section labelled [users].### The name and password for each user follow, one account per line.[users]# harry = harryssecret# sally = sallyssecrethuangkai = huangkai #添加账号,等号前为用户名,等号后为 密码 8、修改authz配置文件123456789101112131415161718192021222324252627282930313233343536373839[root@huangkai200 passwd]# vi authz### This file is an example authorization file for svnserve.### Its format is identical to that of mod_authz_svn authorization### files.### As shown below each section defines authorizations for the path and### (optional) repository specified by the section name.### The authorizations follow. An authorization line can refer to:### - a single user,### - a group of users defined in a special [groups] section,### - an alias defined in a special [aliases] section,### - all authenticated users, using the '$authenticated' token,### - only anonymous users, using the '$anonymous' token,### - anyone, using the '*' wildcard.###### A match can be inverted by prefixing the rule with '~'. Rules can### grant read ('r') access, read-write ('rw') access, or no access### ('').[aliases]# joe = /C=XZ/ST=Dessert/L=Snake City/O=Snake Oil, Ltd./OU=Research Institute/CN=Joe Average[groups]# harry_and_sally = harry,sally# harry_sally_and_joe = harry,sally,&joe# [/foo/bar]# harry = rw# &joe = r# * =# [repository:/baz/fuz]# @harry_and_sally = rw# * = r[hk:/]huangkai = rw#权限配置规则#[版本库:/项目/目录]#用户名 = rw(可写可读) | r (只读)#也可以指定组,一个组对应一个或多个用户多个用户用 逗号隔开# gruop_1 = huangkai,...#为组配置权限规则,如 : @ 组名# @gruop_1 = rw 修改 authz 与 passwd文件 不需要重启svn服务器,但修改 svnserver.conf 必须重启使配置生效,authz 与 passwd 配置文件不能写错,否则服务可能不能正常启动。 启动服务: 12345678910[root@huangkai200 passwd]# lsof -i:3690 #先查看服务是否启动COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAMEsvnserve 1748 root 3u IPv4 24804 0t0 TCP *:svn (LISTEN)root@huangkai200 passwd]# kill -9 1748 #如果有启动,先关闭[root@huangkai200 passwd]# ps -ef|grep svnroot 3252 1401 0 22:58 pts/0 00:00:00 grep --color=auto svn[root@huangkai200 passwd]# svnserve -d -r /data/svn/data/ #启动服务 9、svn 开机启动首先:编写一个启动脚本svn_startup.sh,我放在/data/svn/svn_startup.sh12#!/bin/bash/usr/bin/svnserve -d -r /data/svn/data/ 这里的svnserve路径保险起见,最好写绝对路径,因为启动的时候,环境变量也许没加载。绝对路径怎么查?使用 which svnserve 查看:12[root@huangkai200 hk]# which svnserve /usr/bin/svnserve 然后修改该脚本的执行权限:1[root@huangkai200 svn]# chmod 700 svn_startup.sh 最后:加入自动运行12[root@huangkai200 svn]#vi /etc/rc.d/rc.local在末尾添加脚本的路径,如:/data/svn/svn_startup.sh 看看 /etc/rc.d/rc.local的权限,如果没有可执行权限,则需要设置 , chmod 711 /etc/rc.d/rc.local 10、svn 客户端连接 :确保防火墙关闭或开放 3690 端口,现在,你可以重启一下试试了1svn://ip地址:3690/hk","tags":[{"name":"Linux","slug":"Linux","permalink":"https://huankai.github.io/tags/Linux/"},{"name":"Svn","slug":"Svn","permalink":"https://huankai.github.io/tags/Svn/"}]},{"title":"Spring Boot 快速入门 - 1 分钟搭建 Web 应用","date":"2018-02-22T07:15:02.803Z","path":"2018/02/22/Spring_Boot_01/","text":"Spring Boot 不是一个新的框架,它是提供一种使我们更易于创建基于 Spring 的最小或零配置的独立应用和服务的方式。 Spring 对于 Java 开发者来说一定都并不陌生,它作为目前非常流行的一个 Java 应用开发的基础框架,应用非常广泛。然而,由于其配置繁杂,各样格式的XML配置文件,着实让人头疼。 Spring Boot 的出现,可以让我们只需要非常简单的几步就可以搭建起一个基于 Spring 框架的 Web 应用程序。Spring Boot的主要目标: 为所有的Spring开发提供一个更快,更广泛的入门体验; 开箱即用,以最小或零配置的方式,使我们更专注于解决应用程序的功能需求; 提供一些非功能性的常见的大型项目类特性(如内嵌服务器、安全、度量、健康检查、外部化配置); 绝对没有代码生成,也不需要XML配置,可以完全避免XML配置; 为了避免定义更多的注释配置(它将一些现有的 Spring 框架注释组合成一个简单的单个注释); 提供一些默认值,以便在任何时间内快速启动新项目。 1. 项目依赖1234567891011121314151617181920212223242526272829<project xmlns=\"http://maven.apache.org/POM/4.0.0\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd\"> <modelVersion>4.0.0</modelVersion> <groupId>com.hk</groupId> <artifactId>spring-boot-quick-start</artifactId> <packaging>jar</packaging> <version>0.0.1-SNAPSHOT</version> <name>spring-boot-quick-start</name> <url>http://maven.apache.org</url> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>1.5.7.RELEASE</version> </parent> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build></project> 2. 控制器12345678@RestControllerpublic class HelloWorldController { @GetMapping(\"/\") public String sayHello() { return \"Hello, Spring Boot!\"; }} 3. 主应用程序类Spring Boot 建议我们将主应用程序类置于其他类之上的根包名之下。这样就相当于隐式的的定义了注解扫描的基础搜索包名,而不需要指定 scanBasePackages 属性。12345678@SpringBootApplicationpublic class Application { public static void main(String[] args) { SpringApplication.run(Application.class); } } 4. 运行直接运行 Application 中的 main 方法:123456789101112131415161718192021222324252627282930 . ____ _ __ _ _ /\\\\ / ___'_ __ _ _(_)_ __ __ _ \\ \\ \\ \\( ( )\\___ | '_ | '_| | '_ \\/ _` | \\ \\ \\ \\ \\\\/ ___)| |_)| | | | | || (_| | ) ) ) ) ' |____| .__|_| |_|_| |_\\__, | / / / / =========|_|==============|___/=/_/_/_/ :: Spring Boot :: (v1.5.7.RELEASE)2017-10-12 13:02:06.791 INFO 7188 --- [ main] com.hk.Application : Starting Application on FANLYCHIE-PC with PID 7188 (F:\\dev\\workspace\\idea\\spring-boot-hello-world\\target\\classes started by fanlychie in F:\\dev\\workspace\\idea\\spring-boot-hello-world)2017-10-12 13:02:06.793 INFO 7188 --- [ main] com.hk.Application : No active profile set, falling back to default profiles: default2017-10-12 13:02:06.844 INFO 7188 --- [ main] ationConfigEmbeddedWebApplicationContext : Refreshing org.springframework.boot.context.embedded.AnnotationConfigEmbeddedWebApplicationContext@50de0926: startup date [Sun Apr 02 13:02:06 CST 2017]; root of context hierarchy2017-10-12 13:02:08.030 INFO 7188 --- [ main] s.b.c.e.t.TomcatEmbeddedServletContainer : Tomcat initialized with port(s): 8080 (http)2017-10-12 13:02:08.041 INFO 7188 --- [ main] o.apache.catalina.core.StandardService : Starting service Tomcat2017-10-12 13:02:08.042 INFO 7188 --- [ main] org.apache.catalina.core.StandardEngine : Starting Servlet Engine: Apache Tomcat/8.5.112017-10-12 13:02:08.121 INFO 7188 --- [ost-startStop-1] o.a.c.c.C.[Tomcat].[localhost].[/] : Initializing Spring embedded WebApplicationContext2017-10-12 13:02:08.122 INFO 7188 --- [ost-startStop-1] o.s.web.context.ContextLoader : Root WebApplicationContext: initialization completed in 1284 ms2017-10-12 13:02:08.254 INFO 7188 --- [ost-startStop-1] o.s.b.w.servlet.ServletRegistrationBean : Mapping servlet: 'dispatcherServlet' to [/]2017-10-12 13:02:08.259 INFO 7188 --- [ost-startStop-1] o.s.b.w.servlet.FilterRegistrationBean : Mapping filter: 'characterEncodingFilter' to: [/*]2017-10-12 13:02:08.259 INFO 7188 --- [ost-startStop-1] o.s.b.w.servlet.FilterRegistrationBean : Mapping filter: 'hiddenHttpMethodFilter' to: [/*]2017-10-12 13:02:08.259 INFO 7188 --- [ost-startStop-1] o.s.b.w.servlet.FilterRegistrationBean : Mapping filter: 'httpPutFormContentFilter' to: [/*]2017-10-12 13:02:08.259 INFO 7188 --- [ost-startStop-1] o.s.b.w.servlet.FilterRegistrationBean : Mapping filter: 'requestContextFilter' to: [/*]2017-10-12 13:02:08.502 INFO 7188 --- [ main] s.w.s.m.m.a.RequestMappingHandlerAdapter : Looking for @ControllerAdvice: org.springframework.boot.context.embedded.AnnotationConfigEmbeddedWebApplicationContext@50de0926: startup date [Sun Apr 02 13:02:06 CST 2017]; root of context hierarchy2017-10-12 13:02:08.597 INFO 7188 --- [ main] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped \"{[/],methods=[GET]}\" onto public java.lang.String com.hk.controller.HelloWorldController.sayHello()2017-10-12 13:02:08.599 INFO 7188 --- [ main] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped \"{[/error]}\" onto public org.springframework.http.ResponseEntity<java.util.Map<java.lang.String, java.lang.Object>> org.springframework.boot.autoconfigure.web.BasicErrorController.error(javax.servlet.http.HttpServletRequest)2017-10-12 13:02:08.599 INFO 7188 --- [ main] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped \"{[/error],produces=[text/html]}\" onto public org.springframework.web.servlet.ModelAndView org.springframework.boot.autoconfigure.web.BasicErrorController.errorHtml(javax.servlet.http.HttpServletRequest,javax.servlet.http.HttpServletResponse)2017-10-12 13:02:08.624 INFO 7188 --- [ main] o.s.w.s.handler.SimpleUrlHandlerMapping : Mapped URL path [/webjars/**] onto handler of type [class org.springframework.web.servlet.resource.ResourceHttpRequestHandler]2017-10-12 13:02:08.624 INFO 7188 --- [ main] o.s.w.s.handler.SimpleUrlHandlerMapping : Mapped URL path [/**] onto handler of type [class org.springframework.web.servlet.resource.ResourceHttpRequestHandler]2017-10-12 13:02:08.661 INFO 7188 --- [ main] o.s.w.s.handler.SimpleUrlHandlerMapping : Mapped URL path [/**/favicon.ico] onto handler of type [class org.springframework.web.servlet.resource.ResourceHttpRequestHandler]2017-10-12 13:02:08.826 INFO 7188 --- [ main] o.s.j.e.a.AnnotationMBeanExporter : Registering beans for JMX exposure on startup2017-10-12 13:02:08.866 INFO 7188 --- [ main] s.b.c.e.t.TomcatEmbeddedServletContainer : Tomcat started on port(s): 8080 (http)2017-10-12 13:02:08.869 INFO 7188 --- [ main] com.hk.Application : Started Application in 2.338 seconds (JVM running for 2.673)","tags":[{"name":"Spring-Boot","slug":"Spring-Boot","permalink":"https://huankai.github.io/tags/Spring-Boot/"}]},{"title":"Spring Boot 添加 Thymeleaf 支持","date":"2018-02-22T07:15:02.791Z","path":"2018/02/22/Spring_Boot_02/","text":"Spring Boot 对 Thymeleaf 模板引擎提供了自配置的良好支持。Spring Boot 1.5.7.RELEASE 版本默认使用的是 Thymeleaf 2.0+,本文使用 Thymeleaf 3.0+ 版本,在 pom.xml 中添加以下声明: 1234<properties> <thymeleaf.version>3.0.5.RELEASE</thymeleaf.version> <thymeleaf-layout-dialect.version>2.2.1</thymeleaf-layout-dialect.version></properties> 然后在 dependencies 添加 Thymeleaf 依赖声明: 1234<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-thymeleaf</artifactId></dependency> 控制器:123456789@Controllerpublic class WelcomeController { @GetMapping(\"/\") public String welcome(ModelMap model) { model.put(\"message\", \"Hello Thymeleaf!\"); return \"index\"; }} 模板文件: Spring Boot 对 Thymeleaf 模板引擎提供了自动配置的支持(详见 ThymeleafProperties)。我们只需遵循约定,在/src/main/resources/templates/目录创建相应的页面模板文件(*.html)即可 #src/main/resources/templates/index.html 1234567891011<!DOCTYPE HTML><html xmlns:th=\"http://www.thymeleaf.org\"><head> <title>首页</title> <meta http-equiv=\"Content-Type\" content=\"text/html; charset=UTF-8\"/> <link rel=\"stylesheet\" type=\"text/css\" th:href=\"@{/css/main.css}\"></head><body> <h1 th:text=\"${message}\"></h1></body></html> 静态文件:Spring Boot 默认将静态资源文件映射到类路径下的目录包括(详见 ResourcesProperties): /META-INF/resources/ /resources/ /static/ /public/ 因此我们可以将 css、js、images 等静态资源文件放在/src/main/resources/static/目录下. #src/main/resources/static/css/main.css123456789body { padding: 0; color: #444; width: 280px; margin: 100px auto; font-family: SimSun; background-color: #FBFBFB; text-shadow: rgba(50,50,50,0.3) 2px 2px 3px;} 主应用程序类: 12345678@SpringBootApplicationpublic class Application { public static void main(String[] args) { SpringApplication.run(Application.class); } } 模板文件和静态资源文件的缓存问题: 当修改 css、js 等静态资源文件的内容或模板文件的内容时,刷新客户端浏览器,发现内容还是老的,说明 Spring Boot 内置的 Servelt 容器并没有实时重新加载修改过的文件内容。你只能在每次修改静态资源文件时,虽然不需要重启服务,但是你要重新编译一次,IntelliJ IDEA 中按一次 Ctrl + F9 即可。 有关 Thymeleaf 基本使用可查看 Thymeleaf 教程","tags":[{"name":"Spring-Boot","slug":"Spring-Boot","permalink":"https://huankai.github.io/tags/Spring-Boot/"}]},{"title":"SecureCRT 8.1 安装与破解","date":"2018-02-22T07:15:02.783Z","path":"2018/02/22/SecureCRT_8_1/","text":"一、安装下载地址: 百度云盘链接 ,密码 : dfit 安装直接按步骤下一步完成即可,这里略过 二、破解先不要启动程序,将 keygen.exe与 fx.exe文件复制到安装目录下 右键 keygen.exe 以管理员运行如下 : 点击 patch,弹出对话框,选中 SecureCRT.exe 文件,再点击 Generate,选择 LicenseHelper.exe 文件,破解 SecureCRT。 右键 fx.exe 以管理员运行如下 :123Name 输入上面的 name,如: ygeRCompany 输入上面的 Company,如: TEAM ZWT点击Patch --> Generate 然后打开SecureCRT 和 SecureFX,将上面生成的信息输入分别到指定的输入框中,完成破解。注意:keygen.exe生成的信息是破解 SecureCRT 的,fx.exe生成的信息是破解 SecureFX 的。 三、设置编码 SecureCRT编码设置:打开 SecureCRT软件,Options –> Session Options : 如上,设置为UTF-8 SecureFX编码设置:打开 SecureFX软件,Options –> Session Options : 还需要修改配置文件:Options -> Global Options -> General -> Configuraition Paths : 找到配置文件所在目录的Sesseion目录 修改Default.ini配置内容找到 Use UTF8”=00000000 ,将00000000 改为 00000001 接下来新创建的连接就是以UTF8编码的了,如果之前已创建的连接,还是有乱码问题,找到此目录下指定的ip地址的 ini文件,如 192.168.1.244.ini 文件,查看是否 Use UTF8 行值为 00000000,改为 00000001","tags":[{"name":"Tool","slug":"Tool","permalink":"https://huankai.github.io/tags/Tool/"}]},{"title":"Redis 事务","date":"2018-02-22T07:15:02.777Z","path":"2018/02/22/Redis_05_事务/","text":"1、介绍:redis 事务 :可以一次执行多个命令,本质上是一组命令的集合。具有如下两个特性: 一个事务中的所有命令都会被序列化,按顺序串行化执行, 在执行的过程中,不会被其它客户端发送的命令所打断。 事务是一个原子操作,要么全部执行,要么全部不执行。 EXEC 命令负责触发并执行事务中的所有命令: 如果客户端的使用 MULTI 开启一个事务后,因为断开而没有成功执行EXEC,那么事务中的所有命令都不会被执行; 如果客户端在开启事务之后,执行EXEC,那么事务中的所有命令都会执行。 当使用 AOF 做持久化时,Redis 会使用单个Write(2)命令将事务写入到磁盘中。如果Redis服务器因为某种原因被管理员杀死,或者遇到某种故障,可能只有部分事务命令被写入磁盘中。 2、事务常用命令: 命令 开始版本 描述 MULTI 1.2.0 标记一个事务块的开始。 随后的指令将在执行EXEC时作为一个原子执行 EXEC 1.2.0 执行事务中所有在排队等待的指令并将链接状态恢复到正常,当使用WATCH 时,只有当被监视的键没有被修改,且允许检查设定机制时,EXEC会被执行 DISCARD 2.0.0 刷新一个事务中所有在排队等待的指令,并且将连接状态恢复到正常 WATCH 2.2.0 标记所有指定的key 被监视起来,在事务中有条件的执行(乐观锁) UNWATCH 2.2.0 刷新一个事务中已被监视的所有key 2.1、正常执行1234567891011121314151617181920[root@huangkai ~]# redis-cli 127.0.0.1:6379> 127.0.0.1:6379> 127.0.0.1:6379> 127.0.0.1:6379> KEYS *(empty list or set)127.0.0.1:6379> 127.0.0.1:6379> MULTIOK127.0.0.1:6379> set k1 k1QUEUED127.0.0.1:6379> set k2 k2QUEUED127.0.0.1:6379> get k2QUEUED127.0.0.1:6379> EXEC1) OK2) OK3) \"k2\"127.0.0.1:6379> 2.2、放弃事务使用 DISCARD 放弃事务后,EXEC 将抛出错误12345678910111213141516171819127.0.0.1:6379> FLUSHDBOK127.0.0.1:6379> KEYS *(empty list or set)127.0.0.1:6379> MULTIOK127.0.0.1:6379> set k1 k1QUEUED127.0.0.1:6379> set k2 k2QUEUED127.0.0.1:6379> get k2QUEUED127.0.0.1:6379> DISCARDOK127.0.0.1:6379> EXEC(error) ERR EXEC without MULTI127.0.0.1:6379> get k1(nil)127.0.0.1:6379> 2.3、全体连坐当执行错误的命令时,redis抛出异常信息,再执行 EXEC 时,也会抛出执行失败的错误信息,此时事务会回滚。1234567891011121314151617127.0.0.1:6379> FLUSHDBOK127.0.0.1:6379> MULTIOK127.0.0.1:6379> set k1 k1QUEUED127.0.0.1:6379> set k2 k2QUEUED127.0.0.1:6379> sets k3(error) ERR unknown command 'sets'127.0.0.1:6379> set k4 k4QUEUED127.0.0.1:6379> EXEC(error) EXECABORT Transaction discarded because of previous errors.127.0.0.1:6379> get k1(nil)127.0.0.1:6379> 2.4、冤头债主先设置 k1 值为 k1,k2 值为 k2,再对 k1执行原子加1操作,显示 (k1 + 1)无法运算,但是在进行 INCR 时,并没有抛出异常,而在 EXEC 时有异常,此时事务并没有回滚,只是异常出现的地方没有执行而已。可知 Redis事务并不是很严谨,可以说Redis只是支持部分事务。123456789101112131415161718127.0.0.1:6379> FLUSHDBOK127.0.0.1:6379> MULTIOK127.0.0.1:6379> set k1 k1QUEUED127.0.0.1:6379> set k2 k2QUEUED127.0.0.1:6379> INCR k1QUEUED127.0.0.1:6379> get k1QUEUED127.0.0.1:6379> EXEC1) OK2) OK3) (error) ERR value is not an integer or out of range4) \"k1\"127.0.0.1:6379> 2.5、Watch监控实现乐观锁WATCH 命令可以为 Redis 事务提供 check-and-set (CAS)行为。被 WATCH 的键会被监视,并会发觉这些键是否被改动过了。 如果有至少一个被监视的键在 EXEC 执行之前被修改了, 那么整个事务都会被取消。 什么是悲观锁? 悲观锁(Pessimistic Lock): 就是很悲观,每次去拿数据时都会认识别人会修改,所以每次去拿数据时都会加锁,这样别人拿到数据就会block直到它拿到锁。传统的关系型数据库就用到很多这种锁机制,如表锁,行锁,读锁,写锁等,都是在操作之前先上锁。常用场景如备份数据库表时,锁整个表。 什么是乐观锁? 乐观锁(Optimistic Lock) :就是很乐观,每次去拿数据时都会认识别人不会修改,所以不会加锁,但是在更新的时候,会判断别人在此期间是否有更新数据,可以使用版本号等机会,乐观锁适用于多读的应用类型,这样可以提高系统吞吐量。乐观锁策略:提交版本号必须大于记录当前版本号才能执行更新。 CAS(Check And Set)这里引用官网的例子说明, 假设我们需要原子性地为某个值进行增 1 操作(假设 INCR 不存在)首先我们可能会这样做:123val = GET mykeyval = val + 1SET mykey val 上面的这个实现在只有一个客户端的时候可以很好地执行。 但是, 当多个客户端同时对同一个键进行这样的操作时, 就会产生竞争条件。举个例子, 如果客户端 A 和 B 都读取了键原来的值, 比如 10 , 那么两个客户端都会将键的值设为 11 , 但正确的结果应该是 12 才对。有了 WATCH , 我们就可以轻松地解决这类问题了: 123456WATCH mykeyval = GET mykeyval = val + 1MULTISET mykey valEXEC 使用上面的代码, 如果在 WATCH 执行之后, EXEC 执行之前, 有其他客户端修改了 mykey 的值, 那么当前客户端的事务就会失败。 程序需要做的, 就是不断重试这个操作, 直到没有发生碰撞为止。这种形式的锁被称作乐观锁, 它是一种非常强大的锁机制。 并且因为大多数情况下, 不同的客户端会访问不同的键, 碰撞的情况一般都很少, 所以通常并不需要进行重试。 WATCH 使得 EXEC 命令需要有条件地执行: 事务只能在所有被监视键都没有被修改的前提下执行, 如果这个前提不能满足的话,事务就不会被执行。WATCH 命令可以被调用多次。 对键的监视从 WATCH 执行之后开始生效, 直到调用 EXEC 为止。用户还可以在单个 WATCH 命令中监视任意多个键, 就像这样:12127.0.0.1:6379> WATCH key1 key2 OK 当 EXEC 被调用时, 不管事务是否成功执行, 对所有键的监视都会被取消。 另外, 当客户端断开连接时, 该客户端对键的监视也会被取消。 使用无参数的 UNWATCH 命令可以手动取消对所有键的监视。 对于一些需要改动多个键的事务, 有时候程序需要同时对多个键进行加锁, 然后检查这些键的当前值是否符合程序的要求。 当值达不到要求时, 就可以使用 UNWATCH 命令来取消目前对键的监视, 中途放弃这个事务, 并等待事务的下次尝试。","tags":[{"name":"Redis","slug":"Redis","permalink":"https://huankai.github.io/tags/Redis/"}]},{"title":"Redis 发布与订阅","date":"2018-02-22T07:15:02.770Z","path":"2018/02/22/Redis_04_发布与订阅/","text":"Redis发布与订阅 订阅,取消订阅和发布实现了发布/订阅消息范式,发送者(发布者)不是计划发送消息给特定的接收者(订阅者)。而是发布的消息分到不同的频道,不需要知道什么样的订阅者订阅。订阅者对一个或多个频道感兴趣,只需接收感兴趣的消息,不需要知道什么样的发布者发布的。这种发布者和订阅者的解耦合可以带来更大的扩展性和更加动态的网络拓扑。 Redis的发布与订阅功能说白了就是消息,它支持消息功能 ,但是在企业开发中一般不会选择使用Redis的发布与订阅,而会选择消息中间件,如 ActiveMq、RabbitMq 、RocketMq 、Kafka等。在企业开发中Redis的主要还是作为分布式内存缓存使用。 发布与订阅命令: 命令 语法 开始版本 描述 PSUBSCRIBE PSUBSCRIBE pattern [pattern …] 2.0.0 订阅给定的模式(patterns) PUBSUB PUBSUB subcommand [argument [argument …]] 2.8.0 PUBSUB是一个内省命令,允许检查Pub / Sub子系统的状态。它由单独记录的子命令组成 PUBLISH PUBLISH channel message 2.0.0 将信息 message 发送到指定的频道 channe PUNSUBSCRIBE PUNSUBSCRIBE [pattern [pattern …]] 2.0.0 停止发布到匹配给定模式的渠道的消息监听 SUBSCRIBE SUBSCRIBE channel [channel …] 2.0.0 订阅给指定频道的信息 UNSUBSCRIBE UNSUBSCRIBE [channel [channel …]] 2.0.0 停止频道监听 1、SUBSCRIBE(订阅频道)一个客户端订阅 频道 channel1 与 channel2 2、PUBLISH(发布消息)在 channel1 发布消息 hello 此时订阅了 channel1频道的客户端将收到发送的消息,见 第一张图片 3、PSUBSCRIBE(通配符订阅模式)通配符订阅以 new 开头的频道 发送消息到 new1 和 new13 频道,上面可以收到信息","tags":[{"name":"Redis","slug":"Redis","permalink":"https://huankai.github.io/tags/Redis/"}]},{"title":"Redis 持久化","date":"2018-02-22T07:15:02.763Z","path":"2018/02/22/Redis_03_持久化/","text":"请查看官网 http://doc.redisfans.com/topic/persistence.html","tags":[{"name":"Redis","slug":"Redis","permalink":"https://huankai.github.io/tags/Redis/"}]},{"title":"Redis配置文件","date":"2018-02-22T07:15:02.757Z","path":"2018/02/22/Redis_02_配置文件/","text":"Redis 的主配置文件在 Redis安装根目录下redis.conf,文件详细配置如下: 配置文件单位说明: 1k => 1000 bytes 1kb => 1024 bytes 1m => 1000000 bytes 1mb => 1024*1024 bytes 1g => 1000000000 bytes 1gb => 102410241024 bytes如上可知, 1g 与 1gb所表示的大小是有区别的,单位大小写不敏感,1gb = 1Gb = 1gB = 1GB 1、includes载入其它配置文件信息比如说当你有多个server,而有一些配置项是它们公用的,那么你可以将这些公用的配置项写进一个配置文件common.conf里,然后这些server再include这个配置文件,这些server自己的配置项则分别写在自己的配置文件里。示例:include /path/to/common.conf 2、modules模块系统,是Redis 4.0新功能之一。这个系统可以让用户通过自己编写的代码来扩展和实现 Redis 本身并不具备的功能, 具体使用方法可以参考 antirez 的博文《Redis Loadable Module System》: http://antirez.com/news/106 因为模块系统是通过高层次 API 实现的, 它与 Redis 内核本身完全分离、互不干扰, 所以用户可以在有需要的情况下才启用这个功能, 以下是 redis.conf 中记载的模块载入方法:1234567################################## MODULES ###################################### Load modules at startup. If the server is not able to load modules# it will abort. It is possible to use multiple loadmodule directives.## loadmodule /path/to/my_module.so# loadmodule /path/to/other_module.so 目前已经有人使用这个功能开发了各种各样的模块, 比如 Redis Labs 开发的一些模块就可以在 http://redismodules.com 看到, 此外 antirez 自己也使用这个功能开发了一个神经网络模块: https://github.com/antirez/neural-redis模块功能使得用户可以将 Redis 用作基础设施, 并在上面构建更多功能, 这给 Redis 带来了无数新的可能性。 3、network123456789################################## NETWORK #####################################bind 127.0.0.1 192.168.1.90protected-mode yesport 6379tcp-backlog 511# unixsocket /tmp/redis.sock# unixsocketperm 700timeout 0tcp-keepalive 300 3.1、bind默认情况下,bind接口是127.0.0.1,也就是本地回环地址,这样,Redis只能通过本机客户端连接,而无法通过远程连接,这样可以避免将redis服务暴露于危险的网络环境中,防止一些不安全的人随随便便通过远程连接到redis服务。如果bind选项为空的话,并且 protected-mode 为 no,会接受所有来自于可用网络接口的连接。如果bind选项为空的话,并且 protected-mode 为 yes,会选择默认的连接,即只能通过 127.0.0.1连接语法如下: 1bind ip1 ip2 ... 如果redis安装服务器ip为 192.168.1.90 ,在 192.168.1.4机器 上需要连接 redis服务,那么 bind配置应该如下123#注意,后面的ip并不是配置 192.168.1.4,应该配置redis可接受的ip 192.168.1.90bind 127.0.0.1 192.168.1.90protected-mode yes 或者如下:1protected-mode no 3.2、protected-mode保护模式:默认为 yes,只接受指定 bind的连接,如果设置为 no ,bind的ip不会生效,接受所有可用的网络连接。生产环境严格建议设置为 yes 3.3、portredis配置服务的端口号参数 3.4、timeout客户端连接超时时间,单位为秒,0 为禁止 4、GENERAL12345678910################################# GENERAL #####################################daemonize yessupervised nopidfile /var/run/redis_6379.pidloglevel noticelogfile \"\"syslog-enabled nosyslog-ident redissyslog-facility local0databases 16 4.1、daemonizeredis是否以守护进程启动,默认为 no,可选值 yes | no 4.2、supervised可以通过upstart和systemd管理Redis守护进程,这个参数是和具体的操作系统相关的 4.3、pidfileredis启动后的pid文件目录,默认为 /var/run/redis_6379.pid 4.4、loglevel日志级别:有四个等级 :debug < verbose < notice < warning ,默认为 notice 4.5、logfile日志文件目录,当指定为空字符串时,为标准输出,如果redis已守护进程模式运行,那么日志将会输出到 /dev/null 4.6、 syslog-enabled是否把日志记录到系统日志。默认为 no 4.7、syslog-ident设置系统日志的id 4.8、syslog-facility指定syslog设备(facility),必须是user或则local0到local7 4.9、databasesredis database 个数,默认为 16 个,从 0 开始 ,到 15 5、SNAPSHOTTING快照配置12345678save 900 1save 300 10save 60 10000stop-writes-on-bgsave-error yesrdbcompression yesrdbchecksum yesdbfilename dump.rdbdir ./ 5.1、saverdb持久化每隔 多少 秒内有 多少个写操作,执行持久化操作,可以配置多个save ,如果要禁rdb,这里的三个 save都注释就可以了. 5.2、stop-writes-on-bgsave-errorrdb持久化,redis会在持久化的时候,开启一个新的进程进行持久化,如果在持久化的时候出错,如磁盘空间不足,为保证数据的完整性,此参数控制持久化出错时停止在内存写入数据。默认值就好。 5.3、rdbcompressionrdb 持久化时是否压缩,默认yes 5.4、rdbchecksum重启redis时,检查 rdb文件是否有损坏,默认 yes 5.5、dbfilename存的 rdb文件名,默认为 dump.rdb 5.6、dir导出的rdb文件放在哪个目录,默认为当前目录 6、REPLICATION主从复制12345678910111213141516slaveof <masterip> <masterport>masterauth <master-password>slave-serve-stale-data yesslave-read-only yesrepl-diskless-sync norepl-diskless-sync-delay 5repl-ping-slave-period 10repl-timeout 60repl-disable-tcp-nodelay norepl-backlog-size 1mbrepl-backlog-ttl 3600slave-priority 100min-slaves-to-write 3min-slaves-max-lag 10slave-announce-ip 5.5.5.5slave-announce-port 1234 6.1、slaveof设置本机为slave服务。格式:slaveof 。设置master服务的IP地址及端口,在Redis启动时,它会自动从master进行数据同步。1slave 192.168.1.90 6378 6.2、masterauth当master服务设置了密码保护时,slav服务连接master的密码。 6.3、slave-serve-stale-data当一个slave与master失去联系时,或者复制正在进行的时候,slave应对请求的行为:1) 如果为 yes(默认值) ,slave 仍然会应答客户端请求,但返回的数据可能是过时,或者数据可能是空的在第一次同步的时候;2) 如果为 no ,在你执行除了 info 和 salveof 之外的其他命令时,slave 都将返回一个 “SYNC with master in progress” 的错误。 6.4、slave-read-only设置slave是否是只读的。从2.6版起,slave默认是只读的。 6.5、repl-diskless-sync主从数据复制是否使用无硬盘复制功能。 6.6、repl-diskless-sync-delay当启用磁盘复制时,可以配置延迟时间,以秒为单位,默认为 5 秒,如果设置为 0 ,表示禁用。 6.7、repl-ping-slave-period指定slave定期ping master的周期,默认10秒钟。 6.8、repl-timeout设置主库批量数据传输时间或者ping回复时间间隔,默认值是60秒 。 6.9、repl-disable-tcp-nodelay指定向slave同步数据时,是否禁用socket的NO_DELAY选项。若配置为“yes”,则禁用NO_DELAY,则TCP协议栈会合并小包统一发送,这样可以减少主从节点间的包数量并节省带宽,但会增加数据同步到 slave的时间。若配置为“no”,表明启用NO_DELAY,则TCP协议栈不会延迟小包的发送时机,这样数据同步的延时会减少,但需要更大的带宽。 通常情况下,应该配置为no以降低同步延时,但在主从节点间网络负载已经很高的情况下,可以配置为yes 6.10、repl-backlog-size设置主从复制backlog容量大小。这个 backlog 是一个用来在 slaves 被断开连接时存放 slave 数据的 buffer,所以当一个 slave 想要重新连接,通常不希望全部重新同步,只是部分同步就够了,仅仅传递 slave 在断开连接时丢失的这部分数据。这个值越大,salve 可以断开连接的时间就越长。 6.11、repl-backlog-ttl配置当master和slave失去联系多少秒之后,清空backlog释放空间。当配置成0时,表示永远不清空。 6.12、slave-priority当 master 不能正常工作的时候,Redis Sentinel 会从 slaves 中选出一个新的 master,这个值越小,就越会被优先选中,但是如果是 0 , 那是意味着这个 slave 不可能被选中。 默认优先级为 100。 6.13、min-slaves-to-write首先在本机启动两台redis服务192.168.1.90 6379(master)192.168.1.90 6378(slave)1234567891011121314151617[root@huangkai ~]# redis-cli 127.0.0.1:6379> 127.0.0.1:6379> 127.0.0.1:6379> INFO replication# Replicationrole:masterconnected_slaves:1slave0:ip=127.0.0.1,port=6378,state=online,offset=182,lag=1master_replid:5af7d4dbd0be4052451940ed582c66244dee38b3master_replid2:0000000000000000000000000000000000000000master_repl_offset:182second_repl_offset:-1repl_backlog_active:1repl_backlog_size:1048576repl_backlog_first_byte_offset:1repl_backlog_histlen:182127.0.0.1:6379> 在master服务中使用info查看信息如上:可以看到有一个slave,端口号为 6378,在一般情况下,lag的值应该在0秒或者1秒之间跳动,如果超过1秒的话,那么说明主从服务器之间的连接出现了故障。 Redis的min-slaves-to-write和min-slaves-max-lag两个选项可以防止主服务器在不安全的情况下执行写命令12min-slaves-to-write 3min-slaves-max-lag 10 那么在从服务器的数量少于3个,或者三个从服务器的延迟(lag)值都大于或等于10秒时,主服务器将拒绝执行写命令,这里的延迟值就是上面提到的INFO replication命令的lag值。默认情况下 min-slaves-to-write 值为 0 (禁用) ,min-slaves-max-lag 值为 10. 6.14、min-slaves-max-lag见 6.13 6.15、slave-announce-ipRedis master能够以不同的方式列出所连接slave的地址和端口。例如,“INFO replication”部分提供此信息,除了其他工具之外,Redis Sentinel还使用该信息来发现slave实例。此信息可用的另一个地方在masterser的“ROLE”命令的输出中。通常由slave报告的列出的IP和地址,通过以下方式获得:IP:通过检查slave与master连接使用的套接字的对等体地址自动检测地址。端口:端口在复制握手期间由slavet通信,并且通常是slave正在使用列出连接的端口。然而,当使用端口转发或网络地址转换(NAT)时,slave实际上可以通过(不同的IP和端口对)来到达。 slave可以使用以下两个选项,以便向master报告一组特定的IP和端口,以便INFO和ROLE将报告这些值。如果你需要仅覆盖端口或IP地址,则没必要使用这两个选项。 6.16、slave-announce-port见 6.15 7、SECURITY安全配置12requirepass foobaredrename-command CONFIG \"\" 7.1、requirepass设置redis连接密码,生产环境强烈建议设置密码。 7.2、rename-command将命令重命名。为了安全考虑,可以将某些重要的、危险的命令重命名。当你把某个命令重命名成空字符串的时候就等于取消了这个命令。 8、CLIENTS客户端配置1maxclients 10000 此类只有一个参数,maxclients ,设置客户端最大并发连接数,默认无限制,Redis可以同时打开的客户端连接数为Redis进程可以打开的最大文件描述符数-32(redis server自身会使用一些),如果设置 maxclients 0,表示不作限制。当客户端连接数到达限制时,Redis会关闭新的连接并向客户端返回max number of clients reached错误信息。 9、 MEMORY MANAGEMENT内存管理123maxmemory <bytes>maxmemory-policy noevictionmaxmemory-samples 5 9.1、maxmemory指定Redis最大内存限制,Redis在启动时会把数据加载到内存中,达到最大内存后,Redis会先尝试清除已到期或即将到期的Key,当此方法处理 后,仍然到达最大内存设置,将无法再进行写入操作,但仍然可以进行读取操作。Redis新的vm机制,会把Key存放内存,Value会存放在swap区,格式:maxmemory 。1maxmemory 1024000 9.2、maxmemory-policy当内存使用达到最大值时,redis使用的清除策略。有以下几种可以选择: volatile-lru -> 利用LRU算法移除设置过过期时间的key (LRU:最近最少使用 Least Recently Used ,也就是首先淘汰最长时间未被使用的key) allkeys-lru -> 利用LRU算法移除任何key volatile-lfu -> 利用LFU算法移除设置过过期时间的key(LFU:最近最不常用 Least Frequently Used,也就是淘汰一定时期内被访问次数最少的 key) allkeys-lfu -> 利用LFU算法移除任何key volatile-random -> 移除设置过过期时间的随机key allkeys-random -> 移除随机key volatile-ttl -> 移除即将过期的key(minor TTL) noeviction -> 不移除任何key,只是返回一个写错误 。默认选项 9.3、maxmemory-samplesLRU、LFU 和 minimal TTL 算法都不是精准的算法,但是相对精确的算法(为了节省内存),随意你可以选择样本大小进行检测。redis默认选择5个样本进行检测,你可以通过maxmemory-samples进行设置 样本数 10、 LAZY FREEING1234lazyfree-lazy-eviction nolazyfree-lazy-expire nolazyfree-lazy-server-del noslave-lazy-flush no 11、APPEND ONLY MODEAOF 持久化,将每执行的命令写入文件中,默认为 no,可以和rdb一起使用使用 aof有一个问题:当对同一个key多次更改时,其实我们只需要最后一次更新的值,之前执行的命令也会保存起来,这样会导致aof文件过大。12345678appendonly noappendfilename \"appendonly.aof\"appendfsync everysecno-appendfsync-on-rewrite noauto-aof-rewrite-percentage 100auto-aof-rewrite-min-size 64mbaof-load-truncated yesaof-use-rdb-preamble no 11.1、appendonly是否启用aof持久化方式 。即是否在每次更新操作后进行日志记录,默认配置是no,即在采用异步方式把数据写入到磁盘。可选值 no | yes 11.2、appendfilenameaof持久化文件名,默认为 appendonly.aof 11.3、appendfsyncaof文件刷新的频率。有三种: always :在每个命令都会同步到 aof文件中,安全,速度慢,因为要经常操作磁盘; everysec : 择中方案,每秒写一次,最多也只会丢失1秒的数据,默认值; no : 由操作系统判断缓存大小写入到磁盘,同步频率低,速度快。 11.4、no-appendfsync-on-rewrite正在导出 rdb 快照的过程中要不要停止同步aof 默认为 no,默认值即可 11.5、auto-aof-rewrite-percentageaof重写参数对于使用aof持久化时,操作同一个Key导致aof文件过大的问题,可以使用auto-aof-rewrite-percentage 与 auto-aof-rewrite-min-size 两个参数来设定。 aof 文件大小比上次重写时的大小,增长率达到 100%时重写1auto-aof-rewrite-percentage 100 11.6、auto-aof-rewrite-min-sizeaof文件会从 0 M 增长,前期 aof会进行大量的重写,在此时间没有必须这么频繁的重写,这里指定aof文件超过 64M时才重写,和上面的 auto-aof-rewrite-percentage 参数是并的关系1auto-aof-rewrite-min-size 64mb 11.7、aof-load-truncatedredis在启动时可以加载被截断的AOF文件,而不需要先执行 redis-check-aof 工具1aof-load-truncated yes 11.8、aof-use-rdb-preambleRedis 4.0 新增了 RDB-AOF 混合持久化格式这是一个可选的功能, 在开启了这个功能之后, AOF 重写产生的文件将同时包含 RDB 格式的内容和 AOF 格式的内容, 其中 RDB 格式的内容用于记录已有的数据, 而 AOF 格式的内存则用于记录最近发生了变化的数据, 这样 Redis 就可以同时兼有 RDB 持久化和 AOF 持久化的优点 —— 既能够快速地生成重写文件, 也能够在出现问题时, 快速地载入数据。这个功能可以通过此参数进行开启,默认为 no 12、 LUA SCRIPTINGLUA脚本1lua-time-limit 5000 此类只有一个参数 lua-time-limit,一个Lua脚本最长的执行时间,单位为毫秒,如果为0或负数表示无限执行时间,默认为5000。 13、REDIS CLUSTERRedis集群配置123456cluster-enabled yescluster-config-file nodes-6379.confcluster-node-timeout 15000cluster-slave-validity-factor 10cluster-migration-barrier 1cluster-require-full-coverage yes 13.1、cluster-enabled集群开关,默认不开启集群模式 13.2、cluster-config-file集群配置文件的名称,每个节点都有一个集群相关的配置文件,持久化保存集群的信息。这个文件并不需要手动配置,这个配置文件有Redis生成并更新,每个Redis集群节点需要一个单独的配置文件请确保与实例运行的系统中配置文件名称不冲突1cluster-config-file nodes-6379.conf 13.3、cluster-node-timeout节点互连超时的阀值,集群节点超时毫秒数 13.4、cluster-slave-validity-factor在进行故障转移的时候,全部slave都会请求申请为master,但是有些slave可能与master断开连接一段时间了,导致数据过于陈旧,这样的slave不应该被提升为master。该参数就是用来判断slave节点与master断线的时间是否过长。判断方法是:比较slave断开连接的时间和(node-timeout * slave-validity-factor) + repl-ping-slave-period,如果节点超时时间为三十秒, 并且slave-validity-factor为10,,假设默认的repl-ping-slave-period是10秒,即如果超过310秒slave将不会尝试进行故障转移 13.5、cluster-migration-barriermaster的slave数量大于该值,slave才能迁移到其他孤立master上,如这个参数若被设为2,那么只有当一个主节点拥有2 个可工作的从节点时,它的一个从节点会尝试迁移。 13.6、cluster-require-full-coverage默认情况下,集群全部的slot有节点负责,集群状态才为ok,才能提供服务。设置为no,可以在slot没有全部分配的时候提供服务。不建议打开该配置,这样会造成分区的时候,小分区的master一直在接受写请求,而造成很长时间数据不一致 14、 CLUSTER DOCKER/NAT support兼容 NAT 和 Docker,4.0 新功能123cluster-announce-ip 10.1.1.5cluster-announce-port 6379cluster-announce-bus-port 6380 15、SLOW LOGslowlog是redis用于记录记录慢查询执行时间的日志系统。由于slowlog只保存在内存中,因此slowlog的效率很高,完全不用担心会影响到redis的性能。12slowlog-log-slower-than 10000slowlog-max-len 128 15.1、slowlog-log-slower-thanslowlog的划定界限,只有query执行时间大于slowlog-log-slower-than的才会定义成慢查询,才会被slowlog进行记录。slowlog-log-slower-than设置的单位是微妙,默认是10000微妙,也就是10ms 15.2、slowlog-max-len慢查询最大的条数,当slowlog超过设定的最大值后,会将最早的slowlog删除,是个FIFO队列 16、LATENCY MONITOR延迟监控延迟监控功能是用来监控redis中执行比较缓慢的一些操作,用LATENCY打印redis实例在跑命令时的耗时图表。只有一个参数 latency-monitor-threshold ,只记录大于等于设置的值的操作,0的话,就是关闭监视1latency-monitor-threshold 0 17、EVENT NOTIFICATION事件通知1notify-keyspace-events Elg 17.1、notify-keyspace-events键空间通知使得客户端可以通过订阅频道或模式,来接收那些以某种方式改动了 Redis 数据集的事件。因为开启键空间通知功能需要消耗一些 CPU ,所以在默认配置下,该功能处于关闭状态。notify-keyspace-events 的参数可以是以下字符的任意组合,它指定了服务器该发送哪些类型的通知: K 键空间通知,所有通知以 __keyspace@__ 为前缀 E 键事件通知,所有通知以 __keyevent@__ 为前缀 g DEL、 EXPIRE 、 RENAME 等类型无关的通用命令的通知 $ 字符串命令的通知 l 列表命令的通知 s 集合命令的通知 h 哈希命令的通知 z 有序集合命令的通知 x 过期事件:每当有过期键被删除时发送 e 驱逐(evict)事件:每当有键因为 maxmemory 政策而被删除时发送 A 参数 g$lshzxe 的别名 输入的参数中至少要有一个 K 或者 E,否则的话,不管其余的参数是什么,都不会有任何 通知被分发。示例如下 :1notify-keyspace-events Elg 18、 ADVANCED CONFIG高级配置 1234567891011121314hash-max-ziplist-entries 512hash-max-ziplist-value 64list-max-ziplist-size -2list-compress-depth 0set-max-intset-entries 512zset-max-ziplist-entries 128zset-max-ziplist-value 64hll-sparse-max-bytes 3000activerehashing yesclient-output-buffer-limit normal 0 0 0client-output-buffer-limit slave 256mb 64mb 60client-output-buffer-limit pubsub 32mb 8mb 60hz 10aof-rewrite-incremental-fsync yes 18.1、hash-max-ziplist-entrieshash类型的数据结构在编码上可以使用ziplist和hashtable。ziplist的特点就是文件存储(以及内存存储)所需的空间较小,在内容较小时,性能和hashtable几乎一样。因此redis对hash类型默认采取ziplist。如果hash中条目的条目个数或者value长度达到阀值,将会被重构为hashtable。这个参数指的是ziplist中允许存储的最大条目个数,,默认为512,建议为1281hash-max-ziplist-entries 128 18.2、hash-max-ziplist-valueziplist中允许条目value值最大字节数,默认为64,建议为10241hash-max-ziplist-value 1024 18.3、list-max-ziplist-size当取正值的时候,表示按照数据项个数来限定每个quicklist节点上的ziplist长度。比如,当这个参数配置成5的时候,表示每个quicklist节点的ziplist最多包含5个数据项。当取负值的时候,表示按照占用字节数来限定每个quicklist节点上的ziplist长度。这时,它只能取-1到-5这五个值,每个值含义如下: -5 每个quicklist节点上的ziplist大小不能超过64 Kb。(注:1kb => 1024 bytes) -4 每个quicklist节点上的ziplist大小不能超过32 Kb。 -3 每个quicklist节点上的ziplist大小不能超过16 Kb。 -2 每个quicklist节点上的ziplist大小不能超过8 Kb。(-2是Redis给出的默认值) -1 每个quicklist节点上的ziplist大小不能超过4 Kb 18.4、list-compress-depth这个参数表示一个quicklist两端不被压缩的节点个数。注:这里的节点个数是指quicklist双向链表的节点个数,而不是指ziplist里面的数据项个数。实际上,一个quicklist节点上的ziplist,如果被压缩,就是整体被压缩的。参数list-compress-depth的取值含义如下: 0: 是个特殊值,表示都不压缩。这是Redis的默认值。 1: 表示quicklist两端各有1个节点不压缩,中间的节点压缩。 2: 表示quicklist两端各有2个节点不压缩,中间的节点压缩。 3: 表示quicklist两端各有3个节点不压缩,中间的节点压缩。依此类推… 由于0是个特殊值,很容易看出quicklist的头节点和尾节点总是不被压缩的,以便于在表的两端进行快速存取 18.4、set-max-intset-entriesset允许存储的最大条目个数小于等于set-max-intset-entries用intset,大于set-max-intset-entries用set 18.5、zset-max-ziplist-entrieszset中允许存储的最大条目个数小于等于zset-max-ziplist-entries用ziplist,大于zset-max-ziplist-entries用zset 18.6、zset-max-ziplist-valuezset中允许的value值最大字节数小于等于zset-max-ziplist-value用ziplist,大于zset-max-ziplist-value用zset 18.7、hll-sparse-max-bytes设置的值小于等于hll-sparse-max-bytes使用稀疏数据结构(sparse)大于hll-sparse-max-bytes使用稠密的数据结构(dense),取值范围在 0 ~ 15000,建议的value大概为3000。如果对CPU要求不高,对空间要求较高的,建议设置到10000左右 18.8、activerehashingRedis将在每100毫秒时使用1毫秒的CPU时间来对redis的hash表进行重新hash,可以降低内存的使用。当你的使用场景中,有非常严格的实时性需要,不能够接受Redis时不时的对请求有2毫秒的延迟的话,把这项配置为no。如果没有这么严格的实时性要求,可以设置为yes,以便能够尽可能快的释放内存 18.9、client-output-buffer-limit normal对客户端输出缓冲进行限制可以强迫那些不从服务器读取数据的客户端断开连接,用来强制关闭传输缓慢的客户端。1client-output-buffer-limit normal 0 0 0 对于normal client,第一个0表示取消hard limit,第二个0和第三个0表示取消soft limit,normal client默认取消限制,因为如果没有寻问,他们是不会接收数据的 18.10、client-output-buffer-limit1client-output-buffer-limit slave 256mb 64mb 60 对于slave client和MONITER client,如果client-output-buffer一旦超过256mb,又或者超过64mb持续60秒,那么服务器就会立即断开客户端连接 18.11、client-output-buffer-limit1client-output-buffer-limit pubsub 32mb 8mb 60 对于pubsub client,如果client-output-buffer一旦超过32mb,又或者超过8mb持续60秒,那么服务器就会立即断开客户端连接 18.12、hz1hz 10 redis执行任务的频率为1s除以hz ,如 1/10 18.13、aof-rewrite-incremental-fsync1aof-rewrite-incremental-fsync yes 在aof重写的时候,如果打开了aof-rewrite-incremental-fsync开关,系统会每32MB执行一次fsync。这对于把文件写入磁盘是有帮助的,可以避免过大的延迟峰值 19、ACTIVE DEFRAGMENTATION活跃的碎片整理注意,此功能在 此系列教程使用的版本中只是实验性的,您也可以无视它。然而,即使是在生产过程中,它也经受了压力测试,并由多名工程师手工测试了一段时间。 什么是活跃的碎片整理?活动(联机)碎片整理允许Redis服务器压缩内存中数据的小分配和deal位置之间的空间,从而恢复内存。 碎片化是一个自然过程,每一个分配器和某些工作负载都会发生。通常需要重新启动服务器以降低碎片,或者至少清除所有数据并重新创建它。然而,由于Oran Agra实现了Redis 4.0的这个特性,这个过程在运行时可以在运行时以“热”的方式运行。 在redis运行的情况下,此功能默认是禁用的,只有在编译Redis时才会开启。123456activedefrag yesactive-defrag-ignore-bytes 100mbactive-defrag-threshold-lower 10active-defrag-threshold-upper 100active-defrag-cycle-min 25active-defrag-cycle-max 75 19.1、activedefrag1activedefrag yes 启用碎片整理 19.1、active-defrag-ignore-bytes碎片垃圾开始主动整理的最低大小 19.2、active-defrag-threshold-lower碎片开始自动整理的最小百分比 19.3、active-defrag-threshold-upper最大化碎片率 19.4、active-defrag-cycle-min最小整理磁盘碎片的CPU百分比 19.5、active-defrag-cycle-max最大整理磁盘碎片的CPU百分比","tags":[{"name":"Redis","slug":"Redis","permalink":"https://huankai.github.io/tags/Redis/"}]},{"title":"Redis安装","date":"2018-02-22T07:15:02.750Z","path":"2018/02/22/Redis_01_安装教程/","text":"安装环境 系统版本:CentOS Linux release 7.3.1611 (Core) Redis版本:4.0.1 下载下载地址: 请点击 官网 下载 ,下载完成后,上传到 Linux某个目录中。 也可以直接使用 wget 命令下载: 12345678910111213[huangkai@sjq01 soft]$ wget http://download.redis.io/releases/redis-4.0.1.tar.gz--2017-10-20 14:10:53-- http://download.redis.io/releases/redis-4.0.1.tar.gzResolving download.redis.io (download.redis.io)... 109.74.203.151Connecting to download.redis.io (download.redis.io)|109.74.203.151|:80... connected.HTTP request sent, awaiting response... 200 OKLength: 1711660 (1.6M) [application/x-gzip]Saving to: ‘redis-4.0.1.tar.gz’100%[===============================================================================================================================================================================================>] 1,711,660 585KB/s in 2.9s 2017-10-20 14:10:57 (585 KB/s) - ‘redis-4.0.1.tar.gz’ saved [1711660/1711660][huangkai@sjq01 soft]$ lltotal 1672-rw-rw-r--. 1 huangkai huangkai 1711660 Jul 24 21:59 redis-4.0.1.tar.gz 12345678910111213[huangkai@sjq01 soft]$ wget http://download.redis.io/releases/redis-4.0.1.tar.gz--2017-10-20 14:10:53-- http://download.redis.io/releases/redis-4.0.1.tar.gzResolving download.redis.io (download.redis.io)... 109.74.203.151Connecting to download.redis.io (download.redis.io)|109.74.203.151|:80... connected.HTTP request sent, awaiting response... 200 OKLength: 1711660 (1.6M) [application/x-gzip]Saving to: ‘redis-4.0.1.tar.gz’100%[===============================================================================================================================================================================================>] 1,711,660 585KB/s in 2.9s 2017-10-20 14:10:57 (585 KB/s) - ‘redis-4.0.1.tar.gz’ saved [1711660/1711660][huangkai@sjq01 soft]$ lltotal 1672-rw-rw-r--. 1 huangkai huangkai 1711660 Jul 24 21:59 redis-4.0.1.tar.gz 安装1、解压文件解压文件到指定目录 (/usr/local/) 1234567891011121314151617181920212223[huangkai@sjq01 soft]$ sudo tar xzf redis-4.0.1.tar.gz -C /usr/local/[sudo] password for huangkai: [huangkai@sjq01 soft]$ cd /usr/local/redis-4.0.1[huangkai@sjq01 redis-4.0.1]$ lltotal 268-rw-rw-r--. 1 huangkai huangkai 127778 Jul 24 21:58 00-RELEASENOTES-rw-rw-r--. 1 huangkai huangkai 53 Jul 24 21:58 BUGS-rw-rw-r--. 1 huangkai huangkai 1815 Jul 24 21:58 CONTRIBUTING-rw-rw-r--. 1 huangkai huangkai 1487 Jul 24 21:58 COPYINGdrwxrwxr-x. 6 huangkai huangkai 124 Jul 24 21:58 deps-rw-rw-r--. 1 huangkai huangkai 11 Jul 24 21:58 INSTALL-rw-rw-r--. 1 huangkai huangkai 151 Jul 24 21:58 Makefile-rw-rw-r--. 1 huangkai huangkai 4223 Jul 24 21:58 MANIFESTO-rw-rw-r--. 1 huangkai huangkai 20530 Jul 24 21:58 README.md-rw-rw-r--. 1 huangkai huangkai 57764 Jul 24 21:58 redis.conf-rwxrwxr-x. 1 huangkai huangkai 271 Jul 24 21:58 runtest-rwxrwxr-x. 1 huangkai huangkai 280 Jul 24 21:58 runtest-cluster-rwxrwxr-x. 1 huangkai huangkai 281 Jul 24 21:58 runtest-sentinel-rw-rw-r--. 1 huangkai huangkai 7606 Jul 24 21:58 sentinel.confdrwxrwxr-x. 3 huangkai huangkai 4096 Jul 24 21:58 srcdrwxrwxr-x. 10 huangkai huangkai 167 Jul 24 21:58 testsdrwxrwxr-x. 8 huangkai huangkai 4096 Jul 24 21:58 utils[huangkai@sjq01 redis-4.0.1]$ 如上所示, tar 命令 -C 参数,表示把 tar.gz包解压到 /usr/local目录中,上面需要输入密码,是因为/usr/local目录需要root用户才能访问,此时并不是root用户登陆。2、赋予权限(可选操作) 给当前用户redis操作的权限(可选,如果你安装的目录普通用户有权限访问,或者你使用的是root用户,此操作不需要,如果没有权限,后面的操作可能都会报没有权限的错误) 将redis-4.0.1目录及其子目录的所有者给 huangkai 1234567891011121314[root@sjq01 local]# chown -R huangkai:huangkai redis-4.0.1[root@sjq01 local]# lltotal 4drwxr-xr-x. 2 root root 6 Nov 5 2016 bindrwxr-xr-x. 2 root root 6 Nov 5 2016 etcdrwxr-xr-x. 2 root root 6 Nov 5 2016 gamesdrwxr-xr-x. 2 root root 6 Nov 5 2016 includedrwxr-xr-x. 2 root root 6 Nov 5 2016 libdrwxr-xr-x. 2 root root 6 Nov 5 2016 lib64drwxr-xr-x. 2 root root 6 Nov 5 2016 libexecdrwxrwxr-x. 6 huangkai huangkai 4096 Jul 24 21:58 redis-4.0.1drwxr-xr-x. 2 root root 6 Nov 5 2016 sbindrwxr-xr-x. 5 root root 49 Aug 16 09:08 sharedrwxr-xr-x. 2 root root 6 Nov 5 2016 src 3、创建安装目录 (bin 和 conf):1234567891011121314151617181920212223[huangkai@sjq01 redis-4.0.1]$ mkdir bin conf[huangkai@sjq01 redis-4.0.1]$ lltotal 268-rw-rw-r--. 1 huangkai huangkai 127778 Jul 24 21:58 00-RELEASENOTESdrwxrwxr-x. 2 huangkai huangkai 6 Oct 20 15:36 bin-rw-rw-r--. 1 huangkai huangkai 53 Jul 24 21:58 BUGS-rw-rw-r--. 1 huangkai huangkai 1815 Jul 24 21:58 CONTRIBUTING-rw-rw-r--. 1 huangkai huangkai 1487 Jul 24 21:58 COPYINGdrwxrwxr-x. 6 huangkai huangkai 124 Jul 24 21:58 depsdrwxrwxr-x. 2 huangkai huangkai 6 Oct 20 15:36 conf-rw-rw-r--. 1 huangkai huangkai 11 Jul 24 21:58 INSTALL-rw-rw-r--. 1 huangkai huangkai 151 Jul 24 21:58 Makefile-rw-rw-r--. 1 huangkai huangkai 4223 Jul 24 21:58 MANIFESTO-rw-rw-r--. 1 huangkai huangkai 20530 Jul 24 21:58 README.md-rw-rw-r--. 1 huangkai huangkai 57764 Jul 24 21:58 redis.conf-rwxrwxr-x. 1 huangkai huangkai 271 Jul 24 21:58 runtest-rwxrwxr-x. 1 huangkai huangkai 280 Jul 24 21:58 runtest-cluster-rwxrwxr-x. 1 huangkai huangkai 281 Jul 24 21:58 runtest-sentinel-rw-rw-r--. 1 huangkai huangkai 7606 Jul 24 21:58 sentinel.confdrwxrwxr-x. 3 huangkai huangkai 4096 Jul 24 21:58 srcdrwxrwxr-x. 10 huangkai huangkai 167 Jul 24 21:58 testsdrwxrwxr-x. 8 huangkai huangkai 4096 Jul 24 21:58 utils[huangkai@sjq01 redis-4.0.1]$ 3、编译安装 编译: make 编译安装:sudo make install PREFIX=/usr/local/redis-4.0.1/ ,会在 /usr/local/redis-4.0.1/bin目录中创建Redis脚本文件12345678910111213141516171819202122232425262728293031[huangkai@sjq01 redis-4.0.1]$ make----------一系列编译与安装信息----------[huangkai@sjq01 redis-4.0.1]$ sudo make install PREFIX=/usr/local/redis-4.0.1/----日 志 信 息 开 始------cd src && make installmake[1]: Entering directory `/usr/local/redis-4.0.1/src' CC Makefile.depmake[1]: Leaving directory `/usr/local/redis-4.0.1/src'make[1]: Entering directory `/usr/local/redis-4.0.1/src'Hint: It's a good idea to run 'make test' ;) INSTALL install INSTALL install INSTALL install INSTALL install INSTALL installmake[1]: Leaving directory `/usr/local/redis-4.0.1/src'----日 志 信 息 结 束------[huangkai@sjq01 redis-4.0.1]$ [huangkai@sjq01 bin]$ pwd/usr/local/redis-4.0.1/bin[huangkai@sjq01 bin]$ lltotal 21768-rwxr-xr-x. 1 root root 2450880 Oct 20 15:55 redis-benchmark-rwxr-xr-x. 1 root root 5740808 Oct 20 15:55 redis-check-aof-rwxr-xr-x. 1 root root 5740808 Oct 20 15:55 redis-check-rdb-rwxr-xr-x. 1 root root 2605168 Oct 20 15:55 redis-clilrwxrwxrwx. 1 root root 12 Oct 20 15:55 redis-sentinel -> redis-server-rwxr-xr-x. 1 root root 5740808 Oct 20 15:55 redis-server[huangkai@sjq01 bin]$ 4、复制配置文件12345[huangkai@sjq01 redis-4.0.1]$ cp redis.conf ./conf/[huangkai@sjq01 redis-4.0.1]$ cd conf/[huangkai@sjq01 conf]$ lltotal 60-rw-rw-r--. 1 huangkai huangkai 57764 Oct 20 16:02 redis.conf 5、启动服务 编译完成后二进制文件是在src目录下,通过下面的命令启动Redis服务:123456789101112131415161718192021222324252627282930[huangkai@sjq01 redis-4.0.1]$ ./bin/redis-server ./conf/redis.conf5290:C 20 Oct 14:26:05.100 # oO0OoO0OoO0Oo Redis is starting oO0OoO0OoO0Oo5290:C 20 Oct 14:26:05.100 # Redis version=4.0.1, bits=64, commit=00000000, modified=0, pid=5290, just started5290:C 20 Oct 14:26:05.100 # Warning: no config file specified, using the default config. In order to specify a config file use ./redis-server /path/to/redis.conf5290:M 20 Oct 14:26:05.101 # You requested maxclients of 10000 requiring at least 10032 max file descriptors.5290:M 20 Oct 14:26:05.101 # Server can't set maximum open files to 10032 because of OS error: Operation not permitted.5290:M 20 Oct 14:26:05.101 # Current maximum open files is 4096. maxclients has been reduced to 4064 to compensate for low ulimit. If you need higher maxclients increase 'ulimit -n'. _._ _.-``__ ''-._ _.-`` `. `_. ''-._ Redis 4.0.1 (00000000/0) 64 bit .-`` .-```. ```\\/ _.,_ ''-._ ( ' , .-` | `, ) Running in standalone mode |`-._`-...-` __...-.``-._|'` _.-'| Port: 6379 | `-._ `._ / _.-' | PID: 5290 `-._ `-._ `-./ _.-' _.-' |`-._`-._ `-.__.-' _.-'_.-'| | `-._`-._ _.-'_.-' | http://redis.io `-._ `-._`-.__.-'_.-' _.-' |`-._`-._ `-.__.-' _.-'_.-'| | `-._`-._ _.-'_.-' | `-._ `-._`-.__.-'_.-' _.-' `-._ `-.__.-' _.-' `-._ _.-' `-.__.-' 5290:M 20 Oct 14:26:05.103 # WARNING: The TCP backlog setting of 511 cannot be enforced because /proc/sys/net/core/somaxconn is set to the lower value of 128.5290:M 20 Oct 14:26:05.103 # Server initialized5290:M 20 Oct 14:26:05.103 # 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.5290:M 20 Oct 14:26:05.103 # 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.5290:M 20 Oct 14:26:05.103 * Ready to accept connections 如果控制台出现如上信息,表示Redis已启动成功。 连接测试你可以使用内置的客户端命令redis-cli进行使用,新开一个连接窗口1234567[root@sjq01 ~]# cd /usr/local/redis-4.0.1/bin/[root@sjq01 bin]# ./redis-cli 127.0.0.1:6379> SET name huangkaiOK127.0.0.1:6379> GET name\"huangkai\"127.0.0.1:6379> 关闭服务方式1、使用 kill 命令关闭进程(一般不建议此方式) 12345[root@sjq01 bin]# ps aux|grep redishuangkai 5290 0.3 0.7 145248 7968 pts/0 Sl+ 14:26 0:04 ./redis-server *:6379root 5370 0.0 0.0 112648 964 pts/1 R+ 14:50 0:00 grep --color=auto redis[root@sjq01 src]# kill -9 5290[root@sjq01 src]# 方式2、优雅关闭Redis 使用 SHUTDOWN 命令1234[root@sjq01 bin]# ./redis-cli 127.0.0.1:6379> SHUTDOWNnot connected> quit[root@sjq01 src]# WARNING FIX从上面redis启动之后的控制台打出的信息来看,有3条WARNING信息。他们分别如下: 1、WARNING: The TCP backlog setting of 511 cannot be enforced because /proc/sys/net/core/somaxconn is set to the lower value of 128. 解决方式:12345678910第一种:无需重启系统即可生效;但重启以后信息丢失[huangkai@sjq01 redis-4.0.1]$ su - root #切换到root用户Password: Last login: Fri Oct 20 14:26:20 CST 2017 from 192.168.184.1 on pts/1[root@sjq01 ~]# echo 511 >/proc/sys/net/core/somaxconn #修改配置----------第二种:即可生效,重启不会丢失[root@sjq01 ~]# vim /etc/sysctl.conf #打开配置文件,在其后追加net.core.somaxconn = 511[root@sjq01 ~]# sysctl -p #使配置文件sysctl.conf生效 原理:对于一个TCP连接,Server与Client需要通过三次握手来建立网络连接.当三次握手成功后,我们可以看到端口的状态由LISTEN转变为ESTABLISHED,接着这条链路上就可以开始传送数据了.每一个处于监听(Listen)状态的端口,都有自己的监听队列.监听队列的长度,与如下两方面有关:一个是 somaxconn参数;另一个是使用该端口的程序中listen()函数.故而somaxconn会限制了接收新 TCP 连接侦听队列的大小。对于一个经常处理新连接的高负载 web服务环境来说,默认的 128 太小了。大多数环境这个值建议增加到 1024 或者更多。 服务进程会自己限制侦听队列的大小,常常在它们的配置文件中有设置队列大小的选项。大的侦听队列对防止拒绝服务 DoS 攻击也会有所帮助。 redis配置文件中有个参数,tcp-backlog默认值是511,而系统默认的somaxconn是128,所以redis启动以后报出这个warnning 2、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. 解决办法:12345678910第一种:无需重启系统即可生效;但重启以后信息丢失[huangkai@sjq01 redis-4.0.1]$ su - root #切换到root用户Password: Last login: Fri Oct 20 14:26:20 CST 2017 from 192.168.184.1 on pts/1[root@sjq01 ~]# echo 1 > /proc/sys/vm/overcommit_memory #修改配置----------第二种:即可生效,重启不会丢失[root@sjq01 ~]# vim /etc/sysctl.conf #打开配置文件,在其后追加vm.overcommit_memory=1[root@sjq01 ~]# sysctl -p #使配置文件sysctl.conf生效 原理: 内核参数overcommit_memory 确定了内存分配策略,可选值为[0、1、2]0: 表示内核将检查是否有足够的可用内存供应用进程使用;如果有足够的可用内存,内存申请允许;否则,内存申请失败,并把错误返回给应用进程。1: 表示内核允许分配所有的物理内存,而不管当前的内存状态如何。2: 表示内核允许分配超过所有物理内存和交换空间总和的内存 进程通常调用malloc()函数来请求分配内存,Linux支持超量分配内存,以允许分配比可用RAM加上交换内存的请求。Linux对大部分申请内存的请求都回复”yes”,以便能跑更多更大的程序。因为申请内存后,并不会马上使用内存。这种技术叫做Overcommit。当linux发现内存不足时,会发生OOM killer(OOM=out-of-memory)。它会选择杀死一些进程(用户态进程,不是内核线程),以便释放内存。当oom-killer发生时,linux会选择杀死哪些进程?选择进程的函数是oom_badness函数(在mm/oom_kill.c中),该函数会计算每个进程的点数(0~1000)。点数越高,这个进程越有可能被杀死。每个进程的点数跟oom_score_adj有关,而且oom_score_adj可以被设置(-1000最低,1000最高)。 3、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. 意思是 你使用的是透明大页,可能导致redis延迟和内存使用问题解决办法:12345678910111213141516171819202122232425262728293031323334353637第一种方法:重启生效,不会丢失[huangkai@sjq01 redis-4.0.1]$ su - root #切换到root用户Password: Last login: Fri Oct 20 14:26:20 CST 2017 from 192.168.184.1 on pts/1[root@sjq01 ~]# vim /etc/rc.d/rc.local #打开配置文件,在其后追加if test -f /sys/kernel/mm/transparent_hugepage/enabled; thenecho never > /sys/kernel/mm/transparent_hugepage/enabledfiif test -f /sys/kernel/mm/transparent_hugepage/defrag; thenecho never > /sys/kernel/mm/transparent_hugepage/defragfi[root@sjq01 ~]# cat /etc/rc.d/rc.local #查看修改后的内容 #!/bin/bash# THIS FILE IS ADDED FOR COMPATIBILITY PURPOSES## It is highly advisable to create own systemd services or udev rules# to run scripts during boot instead of using this file.## In contrast to previous versions due to parallel execution during boot# this script will NOT be run after all other services.## Please note that you must run 'chmod +x /etc/rc.d/rc.local' to ensure# that this script will be executed during boot.touch /var/lock/subsys/localif test -f /sys/kernel/mm/transparent_hugepage/enabled; thenecho never > /sys/kernel/mm/transparent_hugepage/enabledfiif test -f /sys/kernel/mm/transparent_hugepage/defrag; thenecho never > /sys/kernel/mm/transparent_hugepage/defragfi[root@sjq01 ~]# [root@sjq01 ~]# reboot #重启系统----------第二种方法:及时生效,重启丢失[root@sjq01 ~]# echo never > /sys/kernel/mm/transparent_hugepage/enabled 原理: 正常来说,有两种方式来增加内存,可以管理的内存大小:1.增大硬件内存管理单元的大小。2.增大page的大小。第一个方法不是很现实,现代的硬件内存管理单元最多只支持数百到上千的page表记录,并且,对于数百万page表记录的维护算法必将与目前的数百条记录的维护算法大不相同才能保证性能,目前的解决办法是,如果一个程序所需内存page数量超过了内存管理单元的处理大小,操作系统会采用软件管理的内存管理单元,但这会使程序运行的速度变慢。 从redhat 6(centos,sl,ol)开始,操作系统开始支持 Huge Pages,也就是大页。简单来说, Huge Pages就是大小为2M到1GB的内存page,主要用于管理数千兆的内存,比如1GB的page对于1TB的内存来说是相对比较合适的。 THP(Transparent Huge Pages)是一个使管理Huge Pages自动化的抽象层。目前需要注意的是,由于实现方式问题,THP会造成内存锁影响性能,尤其是在程序不是专门为大内内存页开发的时候,简单介绍如下:操作系统后台有一个叫做khugepaged的进程,它会一直扫描所有进程占用的内存,在可能的情况下会把4kpage交换为Huge Pages,在这个过程中,对于操作的内存的各种分配活动都需要各种内存锁,直接影响程序的内存访问性能,并且,这个过程对于应用是透明的,在应用层面不可控制,对于专门为4k page优化的程序来说,可能会造成随机的性能下降现象。 将Redis命令加入环境变量123[rootoot@sjq01 bin]# vim + /etc/profile #打开配置文件,添加如下内容到最后export PATH=$PATH:/usr/local/redis-4.0.1/bin[root@sjq01 bin]# source /etc/profile #使配置文件生效","tags":[{"name":"Redis","slug":"Redis","permalink":"https://huankai.github.io/tags/Redis/"}]},{"title":"Nginx Linux 安装","date":"2018-02-22T07:15:02.742Z","path":"2018/02/22/Nginx_01_安装/","text":"一、什么是 nginx? nginx是一种高性能的HTTP和反向代理服务器 ,同时也是一个代理邮件服务器,我们在nginx上可以发布网站,也可以实现负载均衡的功能 ,还可以作为邮件服务器实现收发邮件等功能 。所谓负载均衡是指当同时有多个用户访问服务器的时候,为了减轻服务器压力,我们需要将用户分别引入各服务器,分担服务器的压力。 nginx 可以实现高并发、部署简单、 内存消耗少、成本低等优点。 二、nginx 安装 下载点击 此处 下载下载完后,将软件包上传到linux指定目录(也可以使用 wget下载,如下): 123456[root@huangkai200 src]# wget http://nginx.org/download/nginx-1.10.3.tar.gz[root@huangkai200 src]# pwd/usr/src[root@huangkai200 src]# lltotal 910812-rw-r--r--. 1 root root 910812 Dec 24 21:45 nginx-1.10.3.tar.gz 添加用户 123[root@huangkai200 src]# useradd nginx -s /sbin/nologin -M -s 表示指定用户登陆所使用的 shell,nologin表示此用户不能登陆 -M 表示不创建用户home目录 安装 在安装过程中,可能会遇到一些错误 nginx 安装需要 openssl、gcc、pcre 的支持 可先执行如下命令: yum install openssl openssl-devel -y yum install pcre pcre-devel gcc-c++ -y 123456789[root@huangkai200 src]# tar -zxvf nginx-1.10.3.tar.gz # 解压文件[root@huangkai200 src]# ./configure --user=nginx --group=nginx --prefix=/usr/local/soft/nginx --with-http_stub_status_module --with-http_ssl_module--user 指定用户--group 指定组--prefix 指定安装路径--with-http_stub_status_model 激活状态信息--with-http_ssl_module 激活SSL功能 [root@huangkai200 src]# make[root@huangkai200 src]# makeinstall nginx 查看版本 进入nginx根目录 ${nginx}/sbin/nginx -v 三、启动、关闭、重启 nginxnginx 默认端口号为 80 ,在 Linux 中,默认 0 —— 1024 号端口需要 root用户才能启动,所以需要切换到 root用户,或者更改nginx 默认端口号。1234567[huangkai@huangkai200 sbin]$ su - root # 切换到root用户Password: Last login: Sat Mar 18 15:02:28 CST 2017 on pts/0[root@huangkai200 ~]# cd /usr/local/soft/nginx/[root@huangkai200 nginx]# ./sbin/nginx -t # 检查nginx 配置nginx: the configuration file /usr/local/soft/nginx/conf/nginx.conf syntax is oknginx: configuration file /usr/local/soft/nginx/conf/nginx.conf test is successful 启动nginx123456[root@huangkai200 nginx]# ./sbin/nginx [root@huangkai200 nginx]# ps -ef|grep nginxgdm 1338 1218 0 13:21 ? 00:00:00 /usr/libexec/ibus-engine-simpleroot 6597 1 0 15:20 ? 00:00:00 nginx: master process ./sbin/nginxnginx 6598 6597 0 15:20 ? 00:00:00 nginx: worker processroot 6610 6474 0 15:20 pts/0 00:00:00 grep --color=auto ngin 如上,表示 nginx 启动成功,确保防火墙打开或开放 80 端口的情况下,使用浏览器访问 http://ip地址 nginx 平滑启动进入 ${nginx}/sbin目录,执行 ./sbin nginx -s reload ,平滑启动必须是nginx已运行状态 nginx 停止进入 ${nginx}/sbin目录,执行 ./nginx stop","tags":[{"name":"Linux","slug":"Linux","permalink":"https://huankai.github.io/tags/Linux/"},{"name":"Nginx","slug":"Nginx","permalink":"https://huankai.github.io/tags/Nginx/"}]},{"title":"Mac 常用快捷键","date":"2018-02-22T07:15:02.735Z","path":"2018/02/22/Mac快捷键/","text":"前往文件夹:在Finder 下按 command + shift + G 可以开启“前往文件夹”对话框,可以输入路径来快速访问Finder目录位置。 在打开或保存对话框窗口显示隐藏文件command+shift+句号 显示隐藏文件 在任何位置直接开启图片全屏幻灯片模式在Finder的任何位置,选中所有图片,然后按 command + option + Y ,就可以直接开启图片全屏幻灯片模式 快速进入聚焦模式,桌面只显示当前程序窗口command + option + H ,可以只保留当前程序窗口,将其他的隐藏 隐藏当前程序窗口有人来了不想给他看你在干什么,那么快用 command + H ,这时当前活跃的窗口会隐藏 直接锁定屏幕Control + Shift + Eject (开机键) ,可以直接锁定屏幕,进入屏幕保护模式,如果需要,还可以设置开启密码锁定,这样,需要密码才可以登陆了 直接关机 重启 睡眠关机:command + option + control +eject(开机键) 重启:command + control + eject 睡眠:command + option +eject Finder 快捷键command + z :撤销之前的操作,比如删除一个文件想找回来,这时不必去废纸篓找,按这个就好了。 command + 向上箭头 :前往上层文件夹 光标在文本中的行首和行尾行首 : command + 左方向键 行尾 :command + 右方向键 强制退出当前响应的应用command + option + shift + esc 一到两秒,直到程序被强制退出 刷新command + r","tags":[{"name":"Mac","slug":"Mac","permalink":"https://huankai.github.io/tags/Mac/"}]},{"title":"Mac MySql安装","date":"2018-02-22T07:15:02.728Z","path":"2018/02/22/Mac安装Mysql/","text":"安装从官方 下载 mysql for mac 下载后双击.dmg文件 , 先安装 mysql-5.5.23-osx10.6-x86.pkg (mysql主安装包) 再安装 MySQL.prefPane (将mysql安装在系统偏好设置中) 再安装 MySQLStartupItem.pkg (mysql自动启动包) 以上三个都安装完后,mysql就安装完成。 mysql 环境变量配置:123打开终端,输入 cd ~ ,再输入open -e .base_profile 或者 nano .base_profile 在这个文件中加入 export PATH=${path}:/usr/local/mysql/bin然后 control + c (保存)—> Y(确认保存) —> Enter (保存并退出) 修改mysql默认密码: 安装完成后的默认root密码为空,这样很不安全,需要修改mysql密码方式一: 打开终端,将mysql的密码设置为root ,在终端输入 mysqladmin -u root -p root ,会提示输入密码,由于默认密码为空,直接回车即可完成修改,到此mysql 安装完成。 方式二: 先用root登陆 mysql -u root -p , 直接回车即可,执行以下命令:123use mysql;UPDATE user SET Password = PASSWORD(‘新密码') WHERE user = 'root';FLUSH PRIVILEGES; 修改mysql 编码mysql安装好后,需要保存数据, 默认mysql的编码为latin,在保存中文时就会有乱码问题,所以需要修改编码设置。先关闭Mysql服务 ,再执行以下命令:123456789101112进入mysql目录: cd /usr/local/mysql新建文件夹etc : sudo mkdir etc复制文件my-default.cnf 到指目录并修改文件名为my.cnf: sudo cp /usr/local/mysql/support-files/my-default.cnf /usr/local/mysql/etc/my.cnf在my.cnf 中的[mysqld]部分加入 character-set-server=utf8在最后加入 [client] port=3306 default-character-set=utf8 注意:在mysql 5.7.18版本中,在 support-files 目录中没有 my-default.cnf文件,只需要创建 etc目录后,新建 my.cnf文件,写入如下即可123456789101112131415进入Mysql根目录 cd /usr/local/mysql(如果该目录下没有etc文件夹,则创建) mkdir etc #创建目录 cd etc #进入目录创建文件: vim my.cnf写入如下内容: [client] default-character-set=utf8 [mysqld] port=3306 character_set_server=utf8 basedir=/usr/local/mysql #mysql根目录 datadir=/usr/local/mysql/data #mysql data目录 mysql 删除mac 下的mysql 的dmg只有安装文件 ,没有卸载文件 ,只能手动输入命令来删除在删除操作之前,要停止Mysql的有关进程 ,打开终端,依次输入如下命令123456789sudo rm /usr/local/mysqlsudo rm -rf /usr/local/mysql*sudo rm -rf /Library/StartupItems/MySQLCOMsudo rm -rf /Library/PreferencePanes/My*vim /etc/hostconfig (and removed the line MYSQLCOM=-YES-)rm -rf ~/Library/PreferencePanes/My*sudo rm -rf /Library/Receipts/mysql*sudo rm -rf /Library/Receipts/MySQL*sudo rm -rf /var/db/receipts/com.mysql.* 到此,mysql删除完成","tags":[{"name":"Mac","slug":"Mac","permalink":"https://huankai.github.io/tags/Mac/"},{"name":"MySql","slug":"MySql","permalink":"https://huankai.github.io/tags/MySql/"}]},{"title":"Linux 常用命令","date":"2018-02-22T07:15:02.721Z","path":"2018/02/22/Linux常用命令/","text":"","tags":[{"name":"Linux","slug":"Linux","permalink":"https://huankai.github.io/tags/Linux/"}]},{"title":"JPQL语法","date":"2018-02-22T07:15:02.715Z","path":"2018/02/22/JPQL/","text":"JPQL(Java Persistence Query Language,Java 持久化查询语言)和 SQL 之间有很多相似之处,它们之间主要的区别在于前者处理 JPA 实体类,而后者则直接涉及关系数据。在 JPQL 中,可以使用SELECT、UPDATE和DELETE语法来定义查询。 1. 查询语法:SELECT ... FROM ... [WHERE ...] [GROUP BY ... [HAVING ...]] [ORDER BY ...] FROM 子句通过声明一个或多个标识符变量来定义查询的范围 WHERE 子句 用于限制查询到的对象或值的条件表达式 GROUP BY 子句根据一组属性对查询结果进行分组 HAVING 子句配合GROUP BY子句使用,以根据条件表达式进一步限制查询结果 ORDER BY 子句对查询结果进行排序 1234567891011121314151617//部门@Entity(name = \"department\")public class Department { @Id @GeneratedValue private Long id; private String name; @OneToMany(cascade = CascadeType.ALL, orphanRemoval = true) @JoinColumn(name = \"department_id\") private Set<Employee> employees; // getters and setters } 123456789101112131415161718192021222324252627//员工@Entity(name = \"employee\")public class Employee { @Id @GeneratedValue private Long id; private String name; @Enumerated(EnumType.STRING) private Sex sex; private Integer age; private Boolean married; private Double salary; private Date hireDate; @ManyToOne(fetch = FetchType.LAZY) private Department department; // getters and setters } 1234567891011121314151617181920public enum Sex { MALE(\"男\"), FEMALE(\"女\"), ; private final String displayText; private Sex(String displayText) { this.displayText = displayText; } @Override public String toString() { return displayText; } } 1.1 基础查询语法:SELECT 标识符变量 FROM 实体名称 [AS] 标识符变量示例:查询所有的雇员信息12@Query(\"SELECT E FROM Employee E\")List<Employee> selectAll(); 1.2 查询参数JPQL 支持两种查询参数,它们分别是命名参数和位置参数 1.2.1 命名参数语法::自定义的参数名称示例:按性别和薪资范围查找雇员信息12@Query(\"SELECT E FROM Employee E WHERE E.sex = :sex AND E.salary > :salary\")List<Employee> selectByNamedParams(@Param(\"sex\") Sex sex, @Param(\"salary\") Double salary); 在方法的参数列表中,需要使用@Param注解标注每个参数的名称,使之与查询语句参数名称匹配 1.2.2 位置参数语法:?位置编号的数值示例:按姓名和性别查找雇员信息 12@Query(\"SELECT E FROM Employee E WHERE E.sex = ?1 AND E.salary > ?2\")List<Employee> selectByPositionalParams(Sex sex, Double salary); 在方法的参数列表中,参数的顺序需要与查询语句中参数标注的编号依次对应起来。 1.3 关联查询通过使用关键字[LEFT|INNER] JOIN联接关系属性查询 1.3.1 单值关联查询语法:SELECT 标识符变量 FROM 实体名称 [AS] 标识符变量 JOIN 实体名称.单值关联字段 [AS] 标识符变量2 ... 示例:按部门名称查找该部门所有的雇员信息12@Query(\"SELECT E FROM Employee E JOIN E.department D WHERE D.name = ?1\")List<Employee> selectByDeptName(String deptName); 1.3.2 多值关联查询语法1:SELECT 标识符变量 FROM 实体名称 [AS] 标识符变量 JOIN 实体名称.多值关联字段 [AS] 标识符变量2 ...示例:查询薪资大于10000的所有雇员所属的部门信息12@Query(\"SELECT D FROM Department D JOIN D.employees E WHERE E.salary > 10000\")List<Department> selectByMultRelatedField(); 语法2:SELECT 标识符变量 FROM 实体名称 [AS] 标识符变量, IN(实体名称.多值关联字段) [AS] 标识符变量2 ...12@Query(\"SELECT D FROM Department D, IN(D.employees) E WHERE E.salary > 10000\")List<Department> selectByMultRelatedCollection(); 1.4 去重查询语法:SELECT DISTINCT 标识符变量 FROM 实体名称 [AS] 标识符变量 ...示例:查询薪资大于10000的所有雇员所属的部门信息,并消除查询结果中的重复的部门 12@Query(\"SELECT DISTINCT D FROM Department D JOIN D.employees E WHERE E.salary > 10000\")List<Department> selectByMultRelatedFieldDistinct(); 1.5 字面值JPQL 支持的字面值有以下的4种,它们分别是:字符串、数字、布尔、枚举。 1.5.1 字符串语法:'字符串'示例:查询给定名字的雇员信息 12@Query(\"SELECT E FROM Employee E WHERE E.name = '张三'\")Employee selectByLiteralString(); 如果字符串中含有单引号,则用两个单引号来表示。如:Li’Si -> Li’’Si 12@Query(\"SELECT E FROM Employee E WHERE E.name = 'Li''Si'\")Employee selectByLiteralStringWithQuote(); 1.5.2 数字整数类型:如24、+24、-24、24L,支持 Java Long 范围的数值。浮点类型:如24.、24.6、+24.6、-24.6、24.6F、24.6D,支持 Java Double 范围的数值。示例:查询薪资大于10000的所有雇员12@Query(\"SELECT E FROM Employee E WHERE E.salary > 10000.0\")List<Employee> selectByLiteralNumber(); 1.5.3 布尔布尔类型的可选值为:TRUE或FALSE,它们不区分大小写。示例:查找已婚的所有雇员 12@Query(\"SELECT E FROM Employee E WHERE E.married = TRUE\")List<Employee> selectByLiteralBool(); 1.5.4 枚举枚举类名必须指定为完全限定类名。示例:查询所有女性的雇员12@Query(\"SELECT E FROM Employee E WHERE E.sex = org.fanlychie.enums.Sex.FEMALE\")List<Employee> selectByLiteralEnum(); 1.6 模糊查询 表达式 匹配 不匹配 E.name LIKE ‘张%’ 张三 小张伟 E.name LIKE ‘张_’ 张三 张三丰 E.name LIKE ‘张_% 张_三 张三 示例:查询张性的所有雇员12@Query(\"SELECT E FROM Employee E WHERE E.name LIKE '张%'\")List<Employee> selectByLikeLiteralString(); 1.7 空集合查询通过使用关键字IS [NOT] EMPTY来查找关联的属性集合的值为空的记录。示例:查找尚无雇员的所有部门12@Query(\"SELECT D FROM Department D WHERE D.employees IS EMPTY\")List<Department> selectByEmpty(); 1.8 构造器查询结果的类型如果不是持久化的实体类,必须使用该类的完全限定名。语法:SELECT NEW 类的完全限定名(参数1, 参数2, …) …示例:查询所有的雇员信息12@Query(\"SELECT NEW com.hk.model.SimpleEmployee(E.name, E.sex) FROM Employee E\")List<SimpleEmployee> selectSimpleEmployees(); 12345678910111213141516package com.hk.model; public class SimpleEmployee { private String name; private Sex sex; public SimpleEmployee(String name, Sex sex) { this.name = name; this.sex = sex; } // getters and setters } 2. 更新示例:更新某个雇员的婚姻状态和薪资信息 1234@Modifying@Transactional@Query(\"UPDATE Employee SET married = ?2, salary = ?3 WHERE id = ?1\")int update(Long id, Boolean married, Double salary); @Query无法进行 DML(Data Manipulation Language 数据操控语言,主要语句有 INSERT、DELETE、UPDATE)操作,如需更新数据库表的数据需要标注@Modifying注解,并且需要使用支持事务的@Transactional注解。 3. 删除示例:删除没有雇员的部门信息 1234@Modifying@Transactional@Query(\"DELETE FROM Department D WHERE D.employees IS EMPTY\")int delete(); 参考文档文献链接:https://docs.oracle.com/javaee/7/tutorial/persistence-querylanguage.htm 、https://docs.oracle.com/html/E13946_04/ejb3_langref.html","tags":[{"name":"JPA","slug":"JPA","permalink":"https://huankai.github.io/tags/JPA/"}]},{"title":"JPA 一对多与多对一","date":"2018-02-22T07:15:02.708Z","path":"2018/02/22/JPA_一对多、多对一/","text":"1、OneToMany@OneToMany 是属性或方法级别的注解,用于定义源实体与目标实体是一对多的关系。 参数 类型 描述 targetEntity Class 源实体关联的目标实体类型,默认是该成员属性对应的集合类型的泛型的参数化类型 mappedBy String 用在双向关联中。如果关系是双向的,则需定义此参数(与 @JoinColumn 互斥,如果标注了 @JoinColumn 注解,不需要再定义此参数) cascade CascadeType[] 定义源实体和关联的目标实体间的级联关系。当对源实体进行操作时,是否对关联的目标实体也做相同的操作。默认没有级联操作。该参数的可选值有:CascadeType.PERSIST(级联新建)CascadeType.REMOVE(级联删除)CascadeType.REFRESH(级联刷新)CascadeType.MERGE(级联更新)CascadeType.ALL(包含以上四项) fetch FetchType 定义关联的目标实体的数据的加载方式。可选值:FetchType.LAZY(延迟加载,默认)FetchType.EAGER(立即加载) orphanRemoval boolean 当源实体关联的目标实体被断开(如给该属性赋予另外一个实例,或该属性的值被设为 null。被断开的实例称为孤值,因为已经找不到任何一个实例与之发生关联)时,是否自动删除断开的实例(在数据库中表现为删除表示该实例的行记录),默认为 false 1.1、 一对多外键关联1234567891011121314151617@Entity(name = \"user\")public class User implements Serializable { @Id @GeneratedValue private Long id; private String username; private String password; @OneToMany(cascade = CascadeType.ALL, orphanRemoval = true) private Set<Address> addresses; // getters and setters } 1234567891011121314151617181920@Entity(name = \"address\")public class Address implements Serializable { @Id @GeneratedValue private Long id; private String name; private String province; private String city; private String area; private String detail; // getters and setters } 产生的 DDL 语句(MySQL):12345678910111213141516171819202122232425CREATE TABLE `user` ( `id` bigint(20) NOT NULL AUTO_INCREMENT, `password` varchar(255) DEFAULT NULL, `username` varchar(255) DEFAULT NULL, PRIMARY KEY (`id`)) ENGINE=InnoDB DEFAULT CHARSET=utf8; CREATE TABLE `address` ( `id` bigint(20) NOT NULL AUTO_INCREMENT, `area` varchar(255) DEFAULT NULL, `city` varchar(255) DEFAULT NULL, `detail` varchar(255) DEFAULT NULL, `name` varchar(255) DEFAULT NULL, `province` varchar(255) DEFAULT NULL, PRIMARY KEY (`id`)) ENGINE=InnoDB DEFAULT CHARSET=utf8; CREATE TABLE `user_addresses` ( `user_id` bigint(20) NOT NULL, `addresses_id` bigint(20) NOT NULL, PRIMARY KEY (`user_id`,`addresses_id`), UNIQUE KEY `UK_i5lp1fvgfvsplfqwu4ovwpnxs` (`addresses_id`), CONSTRAINT `FKfm6x520mag23hvgr1oshaut8b` FOREIGN KEY (`user_id`) REFERENCES `user` (`id`), CONSTRAINT `FKth1icmttmhhorb9wiarm73i06` FOREIGN KEY (`addresses_id`) REFERENCES `address` (`id`)) ENGINE=InnoDB DEFAULT CHARSET=utf8; Hibernate @OneToMany 默认会产生一张中间表,如上例的 user_addresses 表。为了避免这种情况,你可以在一的一方使用 @JoinColumn 注解: 123@OneToMany(cascade = CascadeType.ALL, orphanRemoval = true)@JoinColumn(name = \"user_id\")private Set<Address> addresses; 产生的 DDL 语句(MySQL):12345678910111213141516171819CREATE TABLE `user` ( `id` bigint(20) NOT NULL AUTO_INCREMENT, `password` varchar(255) DEFAULT NULL, `username` varchar(255) DEFAULT NULL, PRIMARY KEY (`id`)) ENGINE=InnoDB DEFAULT CHARSET=utf8; CREATE TABLE `address` ( `id` bigint(20) NOT NULL AUTO_INCREMENT, `area` varchar(255) DEFAULT NULL, `city` varchar(255) DEFAULT NULL, `detail` varchar(255) DEFAULT NULL, `name` varchar(255) DEFAULT NULL, `province` varchar(255) DEFAULT NULL, `user_id` bigint(20) DEFAULT NULL, PRIMARY KEY (`id`), KEY `FKda8tuywtf0gb6sedwk7la1pgi` (`user_id`), CONSTRAINT `FKda8tuywtf0gb6sedwk7la1pgi` FOREIGN KEY (`user_id`) REFERENCES `user` (`id`)) ENGINE=InnoDB DEFAULT CHARSET=utf8; 这样一来,多的一方通过外键直接与一的一方发生关联,不需要中间表。 2、@ManyToOne@ManyToOne 是属性或方法级别的注解,用于定义源实体与目标实体是多对一的关系,属性参数见 @OneToMany 。 2.1、 多对一外键关联123456789101112131415@Entity(name = \"user\")public class User implements Serializable { @Id @GeneratedValue private Long id; private String username; private String password; // getters and setters } 1234567891011121314151617181920212223@Entity(name = \"address\")public class Address implements Serializable { @Id @GeneratedValue private Long id; private String name; private String province; private String city; private String area; private String detail; @ManyToOne(optional = false) private User user; // getters and setters } 产生的 DDL 语句(MySQL): 12345678910111213141516171819CREATE TABLE `user` ( `id` bigint(20) NOT NULL AUTO_INCREMENT, `password` varchar(255) DEFAULT NULL, `username` varchar(255) DEFAULT NULL, PRIMARY KEY (`id`)) ENGINE=InnoDB DEFAULT CHARSET=utf8; CREATE TABLE `address` ( `id` bigint(20) NOT NULL AUTO_INCREMENT, `area` varchar(255) DEFAULT NULL, `city` varchar(255) DEFAULT NULL, `detail` varchar(255) DEFAULT NULL, `name` varchar(255) DEFAULT NULL, `province` varchar(255) DEFAULT NULL, `user_id` bigint(20) NOT NULL, PRIMARY KEY (`id`), KEY `FKda8tuywtf0gb6sedwk7la1pgi` (`user_id`), CONSTRAINT `FKda8tuywtf0gb6sedwk7la1pgi` FOREIGN KEY (`user_id`) REFERENCES `user` (`id`)) ENGINE=InnoDB DEFAULT CHARSET=utf8; 3、 @OneToMany & @ManyToOne一对多 & 多对一双向外键关联示例: 123456789101112131415161718@Entity(name = \"user\")public class User implements Serializable { @Id @GeneratedValue private Long id; private String username; private String password; @OneToMany(cascade = CascadeType.ALL, orphanRemoval = true) @JoinColumn(name = \"user_id\") private Set<Address> addresses; // getters and setters } 1234567891011121314151617181920212223@Entity(name = \"address\")public class Address implements Serializable { @Id @GeneratedValue private Long id; private String name; private String province; private String city; private String area; private String detail; @ManyToOne(optional = false) private User user; // getters and setters } 产生的 DDL 语句(MySQL): 12345678910111213141516171819CREATE TABLE `user` ( `id` bigint(20) NOT NULL AUTO_INCREMENT, `password` varchar(255) DEFAULT NULL, `username` varchar(255) DEFAULT NULL, PRIMARY KEY (`id`)) ENGINE=InnoDB DEFAULT CHARSET=utf8; CREATE TABLE `address` ( `id` bigint(20) NOT NULL AUTO_INCREMENT, `area` varchar(255) DEFAULT NULL, `city` varchar(255) DEFAULT NULL, `detail` varchar(255) DEFAULT NULL, `name` varchar(255) DEFAULT NULL, `province` varchar(255) DEFAULT NULL, `user_id` bigint(20) NOT NULL, PRIMARY KEY (`id`), KEY `FKda8tuywtf0gb6sedwk7la1pgi` (`user_id`), CONSTRAINT `FKda8tuywtf0gb6sedwk7la1pgi` FOREIGN KEY (`user_id`) REFERENCES `user` (`id`)) ENGINE=InnoDB DEFAULT CHARSET=utf8;","tags":[{"name":"JPA","slug":"JPA","permalink":"https://huankai.github.io/tags/JPA/"}]},{"title":"JPA 多对多","date":"2018-02-22T07:15:02.700Z","path":"2018/02/22/JPA_多对多/","text":"@ManyToMany 是属性或方法级别的注解,用于定义源实体与目标实体是多对多的关系。 参数 类型 描述 targetEntity Class 源实体关联的目标实体类型,默认是该成员属性对应的集合类型的泛型的参数化类型 mappedBy String 用在双向关联中。如果关系是双向的,则需定义此参数(与 @JoinColumn 互斥,如果标注了 @JoinColumn 注解,不需要再定义此参数) cascade CascadeType[] 定义源实体和关联的目标实体间的级联关系。当对源实体进行操作时,是否对关联的目标实体也做相同的操作。默认没有级联操作。该参数的可选值有:CascadeType.PERSIST(级联新建)CascadeType.REMOVE(级联删除)CascadeType.REFRESH(级联刷新)CascadeType.MERGE(级联更新)CascadeType.ALL(包含以上四项) fetch FetchType 定义关联的目标实体的数据的加载方式。可选值:FetchType.LAZY(延迟加载,默认)FetchType.EAGER(立即加载) 1. 多对多单向外键关联123456789101112Entity(name = \"course\")public class Course { @Id @GeneratedValue private Long id; private String name; // getters and setters } 1234567891011121314@Entity(name = \"student\")public class Student { @Id private String no; private String name; @ManyToMany(cascade = CascadeType.ALL) private Set<Course> courses; // getters and setters } 产生的 DDL 语句(MySQL): 1234567891011121314151617181920CREATE TABLE `course` ( `id` bigint(20) NOT NULL AUTO_INCREMENT, `name` varchar(255) DEFAULT NULL, PRIMARY KEY (`id`)) ENGINE=InnoDB DEFAULT CHARSET=utf8; CREATE TABLE `student` ( `no` varchar(255) NOT NULL, `name` varchar(255) DEFAULT NULL, PRIMARY KEY (`no`)) ENGINE=InnoDB DEFAULT CHARSET=utf8; CREATE TABLE `student_courses` ( `student_no` varchar(255) NOT NULL, `courses_id` bigint(20) NOT NULL, PRIMARY KEY (`student_no`,`courses_id`), KEY `FKlwviiijdg10oc2ui4yl7adh1o` (`courses_id`), CONSTRAINT `FKa6x7sxxnd9c1pat349a01bsow` FOREIGN KEY (`student_no`) REFERENCES `student` (`no`), CONSTRAINT `FKlwviiijdg10oc2ui4yl7adh1o` FOREIGN KEY (`courses_id`) REFERENCES `course` (`id`)) ENGINE=InnoDB DEFAULT CHARSET=utf8; 2. @JoinTable与 @Table 注解相类似,不同的是,@JoinTable 注解是用于定义关联表,它只能标注在实体类型的成员属性或方法上,常用于多对多或多对一的关联映射。如果没有声明,则使用该注解的默认值。 参数 类型 描述 name String 连接表的名称 catalog String 默认为数据库系统缺省的 catalog schema String 默认为用户缺省的 schema joinColumns JoinColumn[] 连接表中的外键列,通过使用 @JoinColumn 注解来声明,该外键参照源实体的主键 inverseJoinColumns JoinColumn[] 与 joinColumns 参数作用类似,只不过该外键参照的是目标实体的主键 uniqueConstraints UniqueConstraint[] 表的唯一约束(除了由 @Column 和 @JoinColumn 注解指定的约束以及主键的约束之外的约束),通过使用 @UniqueConstraint 注解来声明,仅在允许自动更新数据库表结构的场景中起到作用,默认没有其他额外的约束条件 indexes Index[] 表的索引,通过使用 @Index 注解来声明,仅在允许自动更新数据库表结构的场景中起到作用,默认没有其他额外的索引 foreignKey ForeignKey 用于生成表时定义 joinColumns 参数的外键约束 inverseForeignKey ForeignKey 用于生成表时定义 inverseJoinColumns 参数的外键约束 Course 定义不变,Student 定义改为:1234567891011121314151617@Entity(name = \"student\")public class Student { @Id private String no; private String name; @ManyToMany(cascade = CascadeType.ALL) @JoinTable(name = \"student_course\", joinColumns = @JoinColumn(name = \"sno\"), inverseJoinColumns = @JoinColumn(name = \"cid\")) private Set<Course> courses; // getters and setters } 产生的 DDL 语句(MySQL):1234567891011121314151617181920CREATE TABLE `course` ( `id` bigint(20) NOT NULL AUTO_INCREMENT, `name` varchar(255) DEFAULT NULL, PRIMARY KEY (`id`)) ENGINE=InnoDB DEFAULT CHARSET=utf8; CREATE TABLE `student` ( `no` varchar(255) NOT NULL, `name` varchar(255) DEFAULT NULL, PRIMARY KEY (`no`)) ENGINE=InnoDB DEFAULT CHARSET=utf8; CREATE TABLE `student_course` ( `sno` varchar(255) NOT NULL, `cid` bigint(20) NOT NULL, PRIMARY KEY (`sno`,`cid`), KEY `FKkx4bkddvbfs0ese9v7hc5rycg` (`cid`), CONSTRAINT `FKrfibef5g98fllv2tlxuiii0lu` FOREIGN KEY (`sno`) REFERENCES `student` (`no`), CONSTRAINT `FKkx4bkddvbfs0ese9v7hc5rycg` FOREIGN KEY (`cid`) REFERENCES `course` (`id`)) ENGINE=InnoDB DEFAULT CHARSET=utf8; 3. 多对多双向外键关联类 Student 定义不变,Course 类的定义改为: 123456789101112131415@Entity(name = \"course\")public class Course { @Id @GeneratedValue private Long id; private String name; @ManyToMany(mappedBy = \"courses\") private Set<Student> students; // getters and setters } 产生的 DDL 语句与多对多单向外键关联产生的一致。","tags":[{"name":"JPA","slug":"JPA","permalink":"https://huankai.github.io/tags/JPA/"}]},{"title":"JPA @Transient 注解","date":"2018-02-22T07:15:02.692Z","path":"2018/02/22/JPA_Transient/","text":"@Transient 是属性或方法级别的注解,该注解没有参数,用于标注属性是瞬态而非持久的。 示例:123456789101112131415@Entity(name = \"student\")public class Student implements Serializable { @Id @GeneratedValue private Long id; private String name; @Transient private String mail; // getters and setters } 产生的 DDL 语句(MySQL):12345CREATE TABLE `student` ( `id` bigint(20) NOT NULL AUTO_INCREMENT, `name` varchar(255) DEFAULT NULL, PRIMARY KEY (`id`)) ENGINE=InnoDB DEFAULT CHARSET=utf8;","tags":[{"name":"JPA","slug":"JPA","permalink":"https://huankai.github.io/tags/JPA/"}]},{"title":"JPA @Temporal 注解","date":"2018-02-22T07:15:02.685Z","path":"2018/02/22/JPA_Temporal/","text":"@Temporal 是属性或方法级别的注解,用于声明属性持久化到数据库时所使用的时间精度。该注解可以应用于任何以下类型的实体类属性: java.util.Date java.util.Calendar 参数 类型 描述 value TemporalType 存储的类型,可选值:TemporalType.DATE(日期)TemporalType.TIME(时间)TemporalType.TIMESTAMP(日期和时间) 示例:12345678910111213141516171819@Entity(name = \"user\")public class User implements Serializable { @Id @GeneratedValue private Long id; @Temporal(TemporalType.DATE) private Date birthday; @Temporal(TemporalType.TIMESTAMP) private Date lastLoginTime; @Temporal(TemporalType.TIME) private Date tokenExpiredTime; // getters and setters } 产生的 DDL 语句(MySQL):1234567CREATE TABLE `user` ( `id` bigint(20) NOT NULL AUTO_INCREMENT, `birthday` date DEFAULT NULL, `last_login_time` datetime DEFAULT NULL, `token_expired_time` time DEFAULT NULL, PRIMARY KEY (`id`)) ENGINE=InnoDB DEFAULT CHARSET=utf8; 数据值样例:12345+----+------------+---------------------+--------------------+| id | birthday | last_login_time | token_expired_time |+----+------------+---------------------+--------------------+| 1 | 2017-05-14 | 2017-05-14 15:12:49 | 15:12:49 |+----+------------+---------------------+--------------------+","tags":[{"name":"JPA","slug":"JPA","permalink":"https://huankai.github.io/tags/JPA/"}]},{"title":"JPA @Table 注解","date":"2018-02-22T07:15:02.675Z","path":"2018/02/22/JPA_Table/","text":"@Table 是类级别的注解,用于声明实体映射到数据库中的具体的表。 参数 类型 描述 name String 表的名称,默认为实体名称(参考 @Entity 注解的 name 参数说明),因此如果实体名称与映射的表名称一致时,@Table 注解常常可以省略 catalog String 默认为数据库系统缺省的 catalog schema String 默认为用户缺省的 schema indexes Index[] 表的索引,通过使用 @Index 注解来声明,仅在允许自动更新数据库表结构的场景中起到作用,默认没有其他额外的索引 uniqueConstraints UniqueConstraint[] 表的唯一约束(除了由 @Column 和 @JoinColumn 注解指定的约束以及主键的约束之外的约束),通过使用 @UniqueConstraint 注解来声明,仅在允许自动更新数据库表结构的场景中起到作用,默认没有其他额外的约束条件 1、catalog 和 schema 的区别catalog 和 schema 主要用来解决数据库系统命名冲突的问题。一个数据库系统可以包含多个 catalog,每个 catalog 可以包含多个 schema,而每个 schema 又可以包含多个数据库对象(表、视图等)。不同的数据库系统对 catalog 和 schema 的支持方式有所不同,常见的数据库系统: 数据库系统 catalog schema MySQL 不支持 数据库名 Oracle 不支持 用户 ID SQLServer 数据库名 对象属主名 DB2 指定数据库对象时,Catalog 可以省略 Catalog 属主名 Sybase 数据库名 数据库属主名 2、 唯一约束和索引的区别唯一约束是用来确保数据的正确性,它不允许表中存在重复的数据,若新插入的数据在表中已经存在,则更新操作失败。在数据库系统中,创建一个唯一约束的同时,也会为该约束所指定的所有列创建一个唯一索引,即约束包含索引。 索引是用来优化数据库表数据的检索性能的。通常,出现在查询 SQL 的 WHERE 子句和 JOIN 子句中的列可以考虑为其建立索引。 3、 @UniqueConstraint用于声明表的唯一约束,这些仅在允许自动更新数据库表结构的场景中起到作用。 参数 类型 描述 name String 约束名称,如果不指定,默认使用数据库提供商所生成的值 columnNames String[] 约束的列名称 4、 @Index用于声明表的索引,这些仅在允许自动更新数据库表结构的场景中起到作用。另外,不需要为表的主键指定索引,因为主键索引会自动被创建。 参数 类型 描述 name String 索引名称,如果不指定,默认使用数据库提供商所生成的值 columnList String 要包含在索引中的列名称 unique boolean 索引是否唯一,默认为 false 5、@Table5.1 唯一约束name 列、mail 列的值必须是唯一的,不允许出现重复的值:123456789101112131415161718@Entity(name = \"user\")@Table(uniqueConstraints = { @UniqueConstraint(name = \"unique_name\", columnNames = \"name\"), @UniqueConstraint(name = \"unique_mail\", columnNames = \"mail\")})public class User implements Serializable { @Id @GeneratedValue(strategy = GenerationType.AUTO) private Long id; private String name; private String mail; // getters and setters } 产生的 DDL 语句(MySQL):12345678CREATE TABLE `user` ( `id` bigint(20) NOT NULL AUTO_INCREMENT, `mail` varchar(255) DEFAULT NULL, `name` varchar(255) DEFAULT NULL, PRIMARY KEY (`id`), UNIQUE KEY `unique_name` (`name`), UNIQUE KEY `unique_mail` (`mail`)) ENGINE=InnoDB DEFAULT CHARSET=utf8; 5.2。 联合唯一约束多列联合唯一约束,name 列和 mail 列不能同时出现相同的值: 123456789101112131415@Entity(name = \"user\")@Table(uniqueConstraints = @UniqueConstraint(name = \"unique_name_mail\", columnNames = {\"name\", \"mail\"}))public class User implements Serializable { @Id @GeneratedValue(strategy = GenerationType.AUTO) private Long id; private String name; private String mail; // getters and setters } 产生的 DDL 语句(MySQL):1234567CREATE TABLE `user` ( `id` bigint(20) NOT NULL AUTO_INCREMENT, `mail` varchar(255) DEFAULT NULL, `name` varchar(255) DEFAULT NULL, PRIMARY KEY (`id`), UNIQUE KEY `unique_name_mail` (`name`,`mail`)) ENGINE=InnoDB DEFAULT CHARSET=utf8; 5.3、 单列索引为 name 列和 mail 列分别建立索引 123456789101112131415161718@Entity(name = \"user\")@Table(indexes = { @Index(name = \"index_name\", columnList = \"name\"), @Index(name = \"index_mail\", columnList = \"mail\")})public class User implements Serializable { @Id @GeneratedValue(strategy = GenerationType.AUTO) private Long id; private String name; private String mail; // getters and setters } 产生的 DDL 语句(MySQL):12345678CREATE TABLE `user` ( `id` bigint(20) NOT NULL AUTO_INCREMENT, `mail` varchar(255) DEFAULT NULL, `name` varchar(255) DEFAULT NULL, PRIMARY KEY (`id`), KEY `index_name` (`name`), KEY `index_mail` (`mail`)) ENGINE=InnoDB DEFAULT CHARSET=utf8; 5.4、 多列索引为 name 列和 mail 列建立多列索引: 123456789101112131415@Entity(name = \"user\")@Table(indexes = @Index(name = \"index_name_mail\", columnList = \"name,mail\"))public class User implements Serializable { @Id @GeneratedValue(strategy = GenerationType.AUTO) private Long id; private String name; private String mail; // getters and setters } 产生的 DDL 语句(MySQL): 1234567CREATE TABLE `user` ( `id` bigint(20) NOT NULL AUTO_INCREMENT, `mail` varchar(255) DEFAULT NULL, `name` varchar(255) DEFAULT NULL, PRIMARY KEY (`id`), KEY `index_name_mail` (`name`,`mail`)) ENGINE=InnoDB DEFAULT CHARSET=utf8; 5.5、 单列索引和多列索引的区别当 SQL 查询条件中包含 name 和 mail 时: 1SELECT * FROM user WHERE name = 'admin' AND mail = '[email protected]' 如果为 name 和 mail 列分别建立索引,当执行查询时,MySQL 只能使用一个索引。如果发现有多个单列索引可用,MySQL 会试图选择一个限制最严格的索引来检索,而其他索引则利用不上。使用分析器分析查询 SQL: 1EXPLAIN SELECT * FROM user WHERE name = 'admin' AND mail = '[email protected]' 结果如下: 12345+--------+------+-----------------------+------------+---------+-------+------+-------------+| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |+----+-------------+--------+------+-----------------------+------------+---------+-------+------+-------------+| 1 | SIMPLE | user | ref | index_name,index_mail | index_mail | 768 | const | 1 | Using where |+----+-------------+--------+------+-----------------------+------------+---------+-------+------+-------------+ MySQL 优化器如果发现可以使用多个索引查找后的交集/并集定位数据,那么 MySQL 优化器就会尝试使用 index merge(索引合并)的方式来查询: 1EXPLAIN SELECT * FROM user WHERE name = 'admin' AND mail = '[email protected]' 结果如下:12345+----+-------------+--------+-------------+-----------------------+-----------------------+---------+------+------+------------------------------------------------------------------+| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |+----+-------------+--------+-------------+-----------------------+-----------------------+---------+------+------+------------------------------------------------------------------+| 1 | SIMPLE | user | index_merge | index_name,index_mail | index_name,index_mail | 768,768 | NULL | 1 | Using intersect(index_name,index_mail); Using where; Using index |+----+-------------+--------+-------------+-----------------------+-----------------------+---------+------+------+------------------------------------------------------------------+ 对于多列索引,由于索引文件以B树的数据结构存储,MySQL 能够快速转到合适的 name,然后再转到合适的 mail。在建立多列索引时,应该将严格的索引放在前面,这样筛选数据的时候力度会更大,效率更高。 使用分析器分析查询 SQL:1EXPLAIN SELECT * FROM user WHERE name = 'admin' AND mail = '[email protected]' 结果如下: 12345+----+-------------+--------+------+-----------------+-----------------+---------+-------------+------+--------------------------+| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |+----+-------------+--------+------+-----------------+-----------------+---------+-------------+------+--------------------------+| 1 | SIMPLE | user | ref | index_name_mail | index_name_mail | 1536 | const,const | 2 | Using where; Using index |+----+-------------+--------+------+-----------------+-----------------+---------+-------------+------+--------------------------+","tags":[{"name":"JPA","slug":"JPA","permalink":"https://huankai.github.io/tags/JPA/"}]},{"title":"JPA @OneToOne 注解","date":"2018-02-22T07:15:02.667Z","path":"2018/02/22/JPA_OneToOne/","text":"@OneToOne 是属性或方法级别的注解,用于定义源实体与目标实体是一对一的关系。 参数 类型 描述 targetEntity Class 源实体关联的目标实体类型,默认是该成员属性对应的类型,因此该参数通常可以缺省 mappedBy String 用在双向关联中。如果关系是双向的,只能有一方作为主体端,另一方则需声明此参数以表明将表间的这种关联关系转交给对方来维护 cascade CascadeType[] 定义源实体和关联的目标实体间的级联关系。当对源实体进行操作时,是否对关联的目标实体也做相同的操作。默认没有级联操作。该参数的可选值有:CascadeType.PERSIST(级联新建)CascadeType.REMOVE(级联删除)CascadeType.REFRESH(级联刷新)CascadeType.MERGE(级联更新)CascadeType.ALL(包含以上四项) fetch FetchType 定义关联的目标实体的数据的加载方式。可选值:FetchType.LAZY(延迟加载)FetchType.EAGER(立即加载,默认)延迟加载:只有在第一次访问源实体关联的目标实体的时候才去加载。立即加载:在加载源实体数据的时候同时去加载好关联的目标实体的数据 optional boolean 源实体关联的目标实体是否允许为 null,默认为 true orphanRemoval boolean 当源实体关联的目标实体被断开(如给该属性赋予另外一个实例,或该属性的值被设为 null。被断开的实例称为孤值,因为已经找不到任何一个实例与之发生关联)时,是否自动删除断开的实例(在数据库中表现为删除表示该实例的行记录),默认为 false 注:目标实体是指被关系注解(如:@OneToOne)标注的属性或方法所对应的类,源实体是指该属性或方法所属的类。 1. 一对一单向外键关联123456789101112131415@Entity(name = \"person\")public class Person implements Serializable { @Id @GeneratedValue private Long id; private String name; @OneToOne(cascade = CascadeType.ALL, fetch = FetchType.LAZY) private IdCard idCard; // getters and setters } 123456789101112@Entity(name = \"idcard\")public class IdCard { @Id private String no; @Temporal(TemporalType.DATE) private Date expiryDate; // getters and setters } 产生的 DDL 语句(MySQL): 1234567891011121314CREATE TABLE `person` ( `id` bigint(20) NOT NULL AUTO_INCREMENT, `name` varchar(255) DEFAULT NULL, `id_card_no` varchar(255) DEFAULT NULL, PRIMARY KEY (`id`), KEY `FKar03p8ob32rgj1axxy2q507v5` (`id_card_no`), CONSTRAINT `FKar03p8ob32rgj1axxy2q507v5` FOREIGN KEY (`id_card_no`) REFERENCES `idcard` (`no`)) ENGINE=InnoDB DEFAULT CHARSET=utf8; CREATE TABLE `idcard` ( `no` varchar(255) NOT NULL, `expiry_date` date DEFAULT NULL, PRIMARY KEY (`no`)) ENGINE=InnoDB DEFAULT CHARSET=utf8; 2. orphanRemoval 与 CascadeType.REMOVE 的区别CascadeType.REMOVE(或包含 CascadeType.REMOVE 的 CascadeType.ALL)表示级联删除,只有对源实例做删除操作时,才会级联删除关联的目标实例。如上例,删除 id=1 的 Person 实例,那么,该实例所关联的 IdCard 实例也将被删除。示例代码片段: 1personRepository.delete(1L); 假设 person.idCard = idCard1,如果 person.idCard 属性被赋予了另外一个 IdCard 实例:person.idCard = idCard2 或被设为 null:person.idCard = null。此时,身份证 idCard1 已经找不到任何一个人和它发生关联,这样的值我们成为孤值,它已经失去了存在的意义。如果程序中设置了 orphanRemoval = true,那么,当更新 person 实例时,idCard1 实例将会被自动删除。示例代码片段: 123Person person = personRepository.findOne(1L);person.setIdCard(null);personRepository.save(person); 3. 一对一双向外键关联类 Person 定义不变,IdCard 类的定义改为: 123456789101112131415@Entity(name = \"idcard\")public class IdCard { @Id private String no; @Temporal(TemporalType.DATE) private Date expiryDate; @OneToOne(mappedBy = \"idCard\") private Person person; // getters and setters } 产生的 DDL 语句与一对一单向外键关联产生的一致。 4. @JoinColumn 参数 类型 描述 name String 外键列的名称,默认为:属性的名称 + _ + 属性对应的实体的主键列的名称(Hibernate 映射列时,若遇到驼峰拼写,会自动添加 _ 连接并将大写字母改成小写)。 unique boolean 外键列的值是否是唯一的。这是 @UniqueConstraint 注解的一个快捷方式,实质上是在声明唯一约束。默认值为 false nullable boolean 外键列的值是否允许为 null。默认为 true insertable boolean 外键列是否包含在 INSERT 语句中,默认为 true updatable boolean 外键列是否包含在 UPDATE 语句中,默认为 true columnDefinition String 生成外键列的 DDL 时使用的 SQL 片段。默认使用推断的类型来生成 SQL 片段以创建此列 table String 外键列所属的表的名称。默认值:如果是外键 @OneToOne 或 @ManyToOne 关联,则为源实体的表的名称;如果是单向外键 @OneToMany 关系,则为目标实体的表的名称;如果是 @ManyToMany、@OneToOne、双向 @ManyToOne、双向 @OneToMany 关联,则为连接表的名称 若修改 Person 类的定义为:12345678910111213141516@Entity(name = \"person\")public class Person implements Serializable { @Id @GeneratedValue private Long id; private String name; @OneToOne(cascade = CascadeType.ALL, fetch = FetchType.LAZY) @JoinColumn(name = \"idcard_no\") private IdCard idCard; // getters and setters } 产生的 DDL 语句(MySQL):12345678CREATE TABLE `person` ( `id` bigint(20) NOT NULL AUTO_INCREMENT, `name` varchar(255) DEFAULT NULL, `idcard_no` varchar(255) DEFAULT NULL, PRIMARY KEY (`id`), KEY `FK56rk440ff4uyhc5vdis0jeiut` (`idcard_no`), CONSTRAINT `FK56rk440ff4uyhc5vdis0jeiut` FOREIGN KEY (`idcard_no`) REFERENCES `idcard` (`no`)) ENGINE=InnoDB DEFAULT CHARSET=utf8;","tags":[{"name":"JPA","slug":"JPA","permalink":"https://huankai.github.io/tags/JPA/"}]},{"title":"JPA @MappedSuperclass 注解","date":"2018-02-22T07:15:02.658Z","path":"2018/02/22/JPA_MappedSuperclass/","text":"@MappedSuperclass 是类级别注解,该注解没有任何参数,被该注解标注的类不会映射到数据库中单独的表,但该类所拥有的属性都将映射到其子类的数据库表的列中。 1、 示例12345678910111213@MappedSuperclasspublic class BaseEntity { @Id @GeneratedValue(strategy = GenerationType.AUTO) protected Long id; @Column(name = \"create_time\") protected Date createTime; // getters and setters } 12345678910@Entity(name = \"student\")public class Student extends BaseEntity implements Serializable { private String name; private String mail; // getters and setters } 产生的 DDL 语句(MySQL):1234567CREATE TABLE `student` ( `id` bigint(20) NOT NULL AUTO_INCREMENT, `create_time` datetime DEFAULT NULL, `mail` varchar(255) DEFAULT NULL, `name` varchar(255) DEFAULT NULL, PRIMARY KEY (`id`)) ENGINE=InnoDB DEFAULT CHARSET=utf8; 2、 @AttributeOverride对于被 @MappedSuperclass 注解标注的类派生出来的子类,可以使用 @AttributeOverride 注解重新定义以覆盖父类中的映射信息。 参数 类型 描述 name String 属性名称 column Column 列信息 1234567891011@Entity(name = \"student\")@AttributeOverride(name = \"createTime\", column = @Column(name = \"create_date\"))public class Student extends BaseEntity implements Serializable { private String name; private String mail; // getters and setters } 产生的 DDL 语句(MySQL):1234567CREATE TABLE `student` ( `id` bigint(20) NOT NULL AUTO_INCREMENT, `create_date` datetime DEFAULT NULL, `mail` varchar(255) DEFAULT NULL, `name` varchar(255) DEFAULT NULL, PRIMARY KEY (`id`)) ENGINE=InnoDB DEFAULT CHARSET=utf8; 3、 @AttributeOverrides如果需要重新定义父类中的多个映射信息,需要使用 @AttributeOverrides 注解。 参数 类型 描述 value AttributeOverride[] @AttributeOverride 注解列表 1234567891011121314@Entity(name = \"student\")@AttributeOverrides({ @AttributeOverride(name = \"id\", column = @Column(name = \"student_id\")), @AttributeOverride(name = \"createTime\", column = @Column(name = \"create_date\"))})public class Student extends BaseEntity implements Serializable { private String name; private String mail; // getters and setters } 产生的 DDL 语句(MySQL): 1234567CREATE TABLE `student` ( `student_id` bigint(20) NOT NULL AUTO_INCREMENT, `create_date` datetime DEFAULT NULL, `mail` varchar(255) DEFAULT NULL, `name` varchar(255) DEFAULT NULL, PRIMARY KEY (`person_id`)) ENGINE=InnoDB DEFAULT CHARSET=utf8;","tags":[{"name":"JPA","slug":"JPA","permalink":"https://huankai.github.io/tags/JPA/"}]},{"title":"JPA @Id 注解","date":"2018-02-22T07:15:02.650Z","path":"2018/02/22/JPA_Id/","text":"@Id 是属性或方法级别的注解,该注解没有参数,用于标注实体的主键(映射到数据库表的主键)。 示例:12345678910111213@Entity(name = \"student\")public class Student implements Serializable { @Id private Long id; private String name; private String mail; // getters and setters } 产生的 DDL 语句(MySQL): 123456CREATE TABLE `student` ( `id` bigint(20) NOT NULL, `mail` varchar(255) DEFAULT NULL, `name` varchar(255) DEFAULT NULL, PRIMARY KEY (`id`)) ENGINE=InnoDB DEFAULT CHARSET=utf8;","tags":[{"name":"JPA","slug":"JPA","permalink":"https://huankai.github.io/tags/JPA/"}]},{"title":"JPA @GeneratedValue 注解","date":"2018-02-22T07:15:02.642Z","path":"2018/02/22/JPA_GeneratedValue/","text":"@GeneratedValue 是属性或方法级别的注解,它结合 @Id 注解为主键的值提供生成策略的规范。 参数 类型 描述 strategy GenerationType 主键生成策略。可选值:GenerationType.TABLEGenerationType.SEQUENCEGenerationType.IDENTITYGenerationType.AUTO 默认是 GenerationType.AUTO generator String 主键生成器的名称。该名称为 @TableGenerator 或 @SequenceGenerator 注解中 name 参数的值。默认为持久化提供者(如 Hibernate)提供的id生成器 1、 @GeneratedValue1234567891011121314@Entity(name = \"student\")public class Student implements Serializable { @Id @GeneratedValue private Long id; private String name; private String mail; // getters and setters } 产生的 DDL 语句(MySQL): 123456CREATE TABLE `student` ( `id` bigint(20) NOT NULL AUTO_INCREMENT, `mail` varchar(255) DEFAULT NULL, `name` varchar(255) DEFAULT NULL, PRIMARY KEY (`id`)) ENGINE=InnoDB DEFAULT CHARSET=utf8; 1.1、 GenerationType.TABLE该策略使用一个特殊的数据库表来为各个实体分配主键,并确保主键值的唯一性。它不依赖环境和数据库系统的具体实现,在不同数据库之间可以很容易的进行移植。 1234567891011121314@Entity(name = \"student\")public class Student implements Serializable { @Id @GeneratedValue(strategy = GenerationType.TABLE) private Long id; private String name; private String mail; // getters and setters } 在 Hibernate + MySQL 环境产生的 DDL 语句: 123456789101112CREATE TABLE `student` ( `id` bigint(20) NOT NULL, `mail` varchar(255) DEFAULT NULL, `name` varchar(255) DEFAULT NULL, PRIMARY KEY (`id`)) ENGINE=InnoDB DEFAULT CHARSET=utf8; CREATE TABLE `hibernate_sequences` ( `sequence_name` varchar(255) NOT NULL, `sequence_next_hi_value` bigint(20) DEFAULT NULL, PRIMARY KEY (`sequence_name`)) ENGINE=InnoDB DEFAULT CHARSET=utf8; 其中,hibernate_sequences 表就是用来为各个实体分配主键的,sequence_name 用于存储各个实体名称,sequence_next_hi_value 则是用于存储各个实体的下一个主键的值。 每次保存 Student 实体对象的数据的时候,首先会到 hibernate_sequences 表中查询该实体名称是否已经存在,若不存在,则向 hibernate_sequences 表插入一行该实体名称的记录,若已经存在,则直接取出 sequence_next_hi_value 的值作为本次 Student 数据的主键值,接着更新 hibernate_sequences 表的记录,使该实体名称的记录的 sequence_next_hi_value 值加1。最后保存 Student 实体对象的记录。 1.1.1、 @TableGenerator表生成器,GenerationType.TABLE 策略通常结合该注解一起使用。 参数 类型 描述 name String 生成器的名称,它可以被一个或多个类引用为主键的生成器 table String 生成器表的名称。默认为持久化提供者(如 Hibernate)提供的名称 catalog String 生成器表的 catalog,默认为数据库系统缺省的 catalog schema String 生成器表的 schema,默认为用户缺省的 schema pkColumnName String 生成器表的主键列的名称,默认为持久化提供者(如 Hibernate)提供的名称 valueColumnName String 生成器表存储生成的值的列的名称,默认为持久化提供者(如 Hibernate)提供的名称 pkColumnValue String 生成器表中的主键值,用于将不同的实体区分开来。默认为实体名称(@Entity 注解的 name 参数的值) initialValue int 用于初始化生成器生成的初始值。默认值是0。在 Hibernate 环境中,需要开启 hibernate.id.new_generator_mappings=true(Spring JPA 配置为:spring.jpa.hibernate.use-new-id-generator-mappings=true),否则此参数无效。并且如果实体记录已经存在生成器表中,此参数也无效(即只有当实体记录第一次写入生成器表中时此参数生效) allocationSize int 每次分配的值的数量大小,用完之后再分配此数量的值,默认为50 uniqueConstraints UniqueConstraint[] 表的唯一约束 indexes Index[] 表的索引 123456789101112131415161718192021@Entity(name = \"student\")public class Student implements Serializable { @Id @GeneratedValue(strategy = GenerationType.TABLE, generator = \"pkGenerator\") @TableGenerator( name = \"pkGenerator\", table = \"pk_sequences\", pkColumnName = \"entity_name\", valueColumnName = \"sequence_value\", allocationSize = 10 ) private Long id; private String name; private String mail; // getters and setters } 12345678910111213141516171819@Entity(name = \"user\")public class User implements Serializable { @Id @GeneratedValue(strategy = GenerationType.TABLE, generator = \"pkGenerator\") @TableGenerator( name = \"pkGenerator\", table = \"pk_sequences\", pkColumnName = \"entity_name\", valueColumnName = \"sequence_value\", allocationSize = 10 ) private Long id; private String name; // getters and setters } 产生的 DDL 语句(MySQL):123456789101112131415161718CREATE TABLE `student` ( `id` bigint(20) NOT NULL, `mail` varchar(255) DEFAULT NULL, `name` varchar(255) DEFAULT NULL, PRIMARY KEY (`id`)) ENGINE=InnoDB DEFAULT CHARSET=utf8; CREATE TABLE `user` ( `id` bigint(20) NOT NULL, `name` varchar(255) DEFAULT NULL, PRIMARY KEY (`id`)) ENGINE=InnoDB DEFAULT CHARSET=utf8; CREATE TABLE `pk_sequences` ( `entity_name` varchar(255) NOT NULL, `sequence_value` bigint(20) DEFAULT NULL, PRIMARY KEY (`entity_name`)) ENGINE=InnoDB DEFAULT CHARSET=utf8; 1.2、 GenerationType.SEQUENCE某些数据库系统(如:Oracle、DB2、PostgreSQL 等)底层支持使用序列对象来为表的主键提供唯一值,而对于不支持序列对象的数据库(如:MySQL、SQLServer),则不应使用此策略。 1234567891011121314@Entity(name = \"student\")public class Student implements Serializable { @Id @GeneratedValue(strategy = GenerationType.SEQUENCE) private Long id; private String name; private String mail; // getters and setters } 1234567891011121314151617181920212223242526@Entity(name = \"user\")public class User implements Serializable { @Id @GeneratedValue(strategy = GenerationType.SEQUENCE) private Long id; private String name; public Long getId() { return id; } public void setId(Long id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } } 这种方式是多个实体共用同一个序列对象,这将导致各个实体分配到的序列值不连续,并且消耗加快 1.2.1、 @SequenceGenerator序列生成器,GenerationType.SEQUENCE 策略通常结合该注解一起使用。 参数 类型 描述 name String 生成器的名称,它可以被一个或多个类引用为主键的生成器 sequenceName String 序列对象的名称,默认为持久化提供者(如 Hibernate)提供的名称 catalog String 序列生成器的 catalog schema String 序列生成器的 schema initialValue int 用于初始化序列对象生成的初始值。默认值是1 allocationSize int 每次分配的值的数量大小,用完之后再分配此数量的值,默认为50 12345678910111213141516171819@Entity(name = \"student\")public class Student implements Serializable { @Id @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = \"personSeqGenerator\") @SequenceGenerator( name = \"personSeqGenerator\", sequenceName = \"PERSON_SEQ\", allocationSize = 10 ) private Long id; private String name; private String mail; // getters and setters } 1234567891011121314151617@Entity(name = \"user\")public class User implements Serializable { @Id @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = \"userSeqGenerator\") @SequenceGenerator( name = \"userSeqGenerator\", sequenceName = \"USER_SEQ\", allocationSize = 10 ) private Long id; private String name; // getters and setters } 1.3、 GenerationType.IDENTITY大部分数据库系统(如:MySQL、SQLServer、DB2、Sybase、HypersonicSQL 等)底层支持表的主键自增长,而对于不支持主键自增长的数据库(如:Oracle),则不能使用此策略。 1234567891011121314@Entity(name = \"student\")public class Student implements Serializable { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; private String name; private String mail; // getters and setters } 产生的 DDL 语句(MySQL):123456CREATE TABLE `person` ( `id` bigint(20) NOT NULL AUTO_INCREMENT, `mail` varchar(255) DEFAULT NULL, `name` varchar(255) DEFAULT NULL, PRIMARY KEY (`id`)) ENGINE=InnoDB DEFAULT CHARSET=utf8; 1.4、 GenerationType.AUTO自动为特定的数据库选择适当的策略,这是比较常用的策略。 1234567891011121314@Entity(name = \"student\")public class Student implements Serializable { @Id @GeneratedValue(strategy = GenerationType.AUTO) private Long id; private String name; private String mail; // getters and setters } 由于 @GeneratedValue 注解默认采用的策略就是 GenerationType.AUTO,因此可以简写成:1234567891011121314@Entity(name = \"student\")public class Student implements Serializable { @Id @GeneratedValue private Long id; private String name; private String mail; // getters and setters }","tags":[{"name":"JPA","slug":"JPA","permalink":"https://huankai.github.io/tags/JPA/"}]},{"title":"JPA @Entity 注解","date":"2018-02-22T07:15:02.633Z","path":"2018/02/22/JPA_Entity/","text":"@Entity 是类级别注解,用于声明标注的类是持久的,我们把这样的类称为实体类。每个实体类映射到数据库中的一张表,实体类所拥有的属性将映射成数据库表的列。并由 JPA 负责将对实体的操作转换为对数据库表的操作。 参数 类型 描述 name String 实体名称,在 JPQL 中用于引用该实体类。默认为该类的简单类名称 示例 123456@Entity(name = \"person\")public class Person implements Serializable { ... ... }","tags":[{"name":"JPA","slug":"JPA","permalink":"https://huankai.github.io/tags/JPA/"}]},{"title":"JPA @Column 注解","date":"2018-02-22T07:15:02.625Z","path":"2018/02/22/JPA_Column/","text":"@Column 是属性或方法级别的注解,用于指定持久化属性映射到数据库表的列。如果没有指定列注释,则使用其默认值。 参数 类型 描述 name String 列的名称,默认为属性的名称(Hibernate 映射列时,若遇到驼峰拼写,会自动添加 _ 连接并将大写字母改成小写) unique boolean 列的值是否是唯一的。这是 @UniqueConstraint 注解的一个快捷方式, 实质上是在声明唯一约束。默认值为 false nullable boolean 列的值是否允许为 null。默认为 true insertable boolean 列是否包含在 INSERT 语句中,默认为 true updatable boolean 列是否包含在 UPDATE 语句中,默认为 true columnDefinition String 生成列的 DDL 时使用的 SQL 片段。默认使用推断的类型来生成 SQL 片段以创建此列 table String 当前列所属的表的名称 length int 列的长度,仅对字符串类型的列生效。默认为255 precision int 列的精度,仅对十进制数值有效,表示有效数值的总位数。默认为0 scale int 列的精度,仅对十进制数值有效,表示小数位的总位数。默认为0 1、 示例12345678910111213141516171819202122232425@Entity(name = \"user\")public class User implements Serializable { @Id @GeneratedValue private Long id; @Column(nullable = false, length = 32) private String name; @Column(length = 128) private String mail; @Column(columnDefinition = \"char(11) NOT NULL\") private String phone; @Column(precision = 5, scale = 2) private BigDecimal salary; @Column(precision = 5, scale = 2) private double assets; // getters and setters } 产生的 DDL 语句(MySQL): 123456789CREATE TABLE `user` ( `id` bigint(20) NOT NULL AUTO_INCREMENT, `assets` double NOT NULL, `mail` varchar(128) DEFAULT NULL, `name` varchar(32) NOT NULL, `phone` char(11) NOT NULL, `salary` decimal(5,2) DEFAULT NULL, PRIMARY KEY (`id`)) ENGINE=InnoDB DEFAULT CHARSET=utf8; 可以看出,salary 字段的精度控制生效了,但对于 double 类型的 assets 字段的精度控制没有生效,为了使其生效,将代码修改为:12@Column(columnDefinition = \"double(5, 2)\")private double assets; 产生的 DDL 语句(MySQL):123456789CREATE TABLE `user` ( `id` bigint(20) NOT NULL AUTO_INCREMENT, `assets` double(5,2) DEFAULT NULL, `mail` varchar(128) DEFAULT NULL, `name` varchar(32) NOT NULL, `phone` char(11) NOT NULL, `salary` decimal(5,2) DEFAULT NULL, PRIMARY KEY (`id`)) ENGINE=InnoDB DEFAULT CHARSET=utf8;","tags":[{"name":"JPA","slug":"JPA","permalink":"https://huankai.github.io/tags/JPA/"}]},{"title":"JPA @Basic 注解","date":"2018-02-22T07:15:02.609Z","path":"2018/02/22/JPA_Basic/","text":"@Basic 是属性或方法级别的注解,该注解可以应用于任何以下类型的实体类属性: Java 原始类型 原始类型的包装类型 String java.math.BigInteger java.math.BigDecimal java.util.Date java.util.Calendar java.sql.Date java.sql.Time java.sql.Timestamp byte[] Byte[] char[] Character[] 枚举 任意实现 java.io.Serializable 接口的类型 在实体类中,对以上这些类型的属性,如果没有标注 @Basic 注解,则将使用 @Basic 注解的默认值 参数 类型 描述 fetch FetchType 属性值的加载策略。可选值:FetchType.EAGER:即时加载;FetchType.LAZY:延迟加载,当第一次访问属性时才进行数据的加载;默认为 FetchType.EAGER。 optional boolean 是否允许为 null,默认为 true 示例:123456789101112131415@Entity(name = \"student\")public class Student implements Serializable { @Id @GeneratedValue(strategy = GenerationType.AUTO) private Long id; private String name; @Basic(optional = false, fetch = FetchType.LAZY) private String mail; // getters and setters } 产生的 DDL 语句(MySQL): 123456CREATE TABLE `student` ( `id` bigint(20) NOT NULL AUTO_INCREMENT, `mail` varchar(255) NOT NULL, `name` varchar(255) DEFAULT NULL, PRIMARY KEY (`id`)) ENGINE=InnoDB DEFAULT CHARSET=utf8;","tags":[{"name":"JPA","slug":"JPA","permalink":"https://huankai.github.io/tags/JPA/"}]},{"title":"Idea 使用","date":"2018-02-22T07:15:02.587Z","path":"2018/02/22/Idea_01/","text":"下载官网下载:点击 官网 下载 如下,有windows版本 、maxOS版本、Linux版本,windows版本又分为安装版与 zip版本,可以下载zip版后直接解压到本地磁盘某个目录即可。 安装zip版本的只需要解压,没有安装过程。exe版本的根据个性化设置走完安装向导即可。 配置1、全局编码配置:1.1、先进入idea安装目录 的bin目录,目录文件如下:Idea.exe与 idea.exe.vmoptions为 32位系统启动程序与启动配置文件信息idea64.ext与idea64.exe.vmoptions为 64位系统启动程序 与配置文件信息,根据操作系统的位数修改对应的配置文件,在配置文件新一行加入-Dfile.encoding=UTF-8 1.2、启动idea,进入设置界面,配置如下: 上面要将Transparent native-to-ascii conversion选项勾选,否则项目中properties文件中的中文会显示成Unicode编码值。 2、项目配置 2.1、添加SDK:如下,选择你本地安装的JAVA_HOME目录即可 Project SDK 配置:如上:1处是指定 SDK,如果没有,可以new 一个2 处是指定项目指定的语言等级,jdk1.8 可指定 8,如下 2.2、忽略大小写提示:Idea默认的提示是区分大小写的,对于使用过eclipse的来说很不友好,设置如下:将First letter改为 None即可。 2.3、方法体不自动折叠:默认情况下,打开java文件后方法体内容是折叠成一行了,如下图所示: 设置如下:File ->settings -> Editor -> Code Folding ,将 One-line-methods 去掉勾选。 2.4、取消显示形参名:默认情况下,idea调用的方法会有形参名显示,如下 设置如下:File ->settings -> Editor -> Appearance ,将 show parameter name hints 去掉勾选 2.5、警告配置:Idea 有很多黄色的警告信息,并与 eclipse的警告信息甚至有些相反,eclipse有的idea没有,看起来很不习惯。 如下所有警告设置都在 File -> setting -> Editor -> Inspections Access can be private 出现此种警告,表示定义的方法(包含public)没有被外部调用才出现的警告,设置如下:去掉所选择的复选框 方法注释没有定义描述出现此种警告,表示方法有文档注释的标签,却没有描述,如下,@return 没有描述设置如下: 去掉勾选框 泛型右边也指定具体类型 相同代码下滑线提示Idea会检查项目中是否有相同的代码,如果有,会以下划线提示,如果不需要提示,配置如下: 关闭Typo检查 默认注解参数值警告当在类、属性或方法上使用注解时,如果注解的某些属性有默认值,而此时你标注的注解属性也是默认值,idea也发会出警告,如果想取消警告,配置如下: 类未被使用警告将创建一个类后,如果这个类一直未被使用,会有警告,如果想取消警告,配置如下: 参考文档:https://www.jetbrains.com/idea/documentation/http://wiki.jikexueyuan.com/project/intellij-idea-tutorial/ 快捷键:http://blog.csdn.net/djcken/article/details/16362629","tags":[{"name":"Idea","slug":"Idea","permalink":"https://huankai.github.io/tags/Idea/"}]}]