Redis
安装Redis安装单机RedisWindows安装RedisDocker安装RedisLinux源码安装RedisReleases · tporadowski/redis12345678docker run \ -p 6379:6379 \ --name myredis \ -v $PWD/redis.conf:/etc/redis/redis.conf \ -v $PWD/data:/data \ -d redis:3.2 redis-server /etc/redis/redis.conf \ --restart=always \ --appendonly yes 命令说明: -name myredis : 指定容器名称,这个最好加上,不然在看docker进程的时候会很尴尬。 p 6699:6379 : 端口映射,默认redis启动的是6379,外部端口(6699)。 v $PWD/redis.conf:/etc/redis/redis.conf : 将主机中当前目录下的redis.conf配置文件映射。 v $PWD/data:/data -d redis:latest: 将主机中当前目录下的data挂载到容器的/data -redis-server --appendonly yes :在容器执行redis-server启动命令,并打开redis持久化配置 -restart=always:自动启动 注意事项: 如果不需要指定配置,v $PWD/redis.conf:/etc/redis/redis.conf 可以不用 redis-server 后面的那段 /etc/redis/redis.conf 也可以不用。 $PWD 在window系统下貌似不能用,可以用相对路径/ 客户端连接 1234# 先查询到myredis容器的ip地址。docker inspect myredis | grep IP# 连接到redis容器。然后就进入redis命令行了。docker run -it redis:latest redis-cli -h 192.168.42.321234567891011121314151617# Cetnos安装需要的软件yum -y install gcc gcc-c++ kernel-devel make# Ubunt 安装需要的软件sudo apt install gcc g++ make linux-kernel-headers kernel-package# ubuntu 新内核版本要用到的sudo apt install gcc g++ make linux-libc-dev# 下载rediswget http://download.redis.io/releases/redis-5.0.5.tar.gztar -zxvf redis-5.0.5.tar.gzcd redis-5.0.5# 安装redissudo make && make instal# 注意make的时候可能会报错: #include <jemalloc/jemalloc.h>,使用下面的命令make MALLOC=libc 修改redis.cnf 123456# 设置后台运行daemonize yes# 设置log文件路径logfile /var/log/redis/redis-server.log# 设置持久化文件存放路径dir /var/lib/redis 12345678910# 创建日志存放目录mkdir /var/log/redis/# 创建持久化文件存放目录mkdir /var/log/redis/# 创建存放配置的文件夹mkdir /etc/redis# 拷贝配置文件并改名cp redis.conf /etc/redis/6379.conf# 拷贝自启动脚本文件cp /usr/local/redis-6.0.3/utils/redis_init_script /etc/init.d/redisd 将启动脚本复制到/etc/init.d目录下,本例将启动脚本命名为redisd(通常都以d结尾表示是后台自启动服务) 1234567891011# Centos#设置为开机自启动服务器cd /etc/init.d/chkconfig redisd on# Ubuntu#设置服务脚本有执行权限sudo chmod +x /etc/init.d/redisd#注册服务cd /etc/init.d/sudo update-rc.d redisd defaults 此处直接配置开启自启动 chkconfig redisd on 将报错误: service does not support chkconfig 123#!/bin/sh# chkconfig: 2345 90 10# description: Redis is a persistent key-value database 通用命令 123456#启动Redis服务sudo service redisd start#关闭服务sudo service redisd stop#重启服务:sudo service redisd restart 安装集群RedisDocker安装集群源码安装集群12345678910111213141516171819202122232425262728293031323334353637383940#!/bin/bashecho "start install redis-cluster..."if [ ! -d /opt/docker-redis/7001/ ];then mkdir -p /opt/docker-redis/700{1,2,3,4,5,6}/data/ficd /opt/docker-redis/7001/wget http://download.redis.io/redis-stable/redis.conf -O /opt/docker-redis/7001/redis7001.conf#sed -i '/^#/d;/^$/d' redis7001.conf #取出空行和注释行sed -i 's/bind/#bind/g;s/appendonly no/appendonly yes/g;s/protected-mode yes/protected-mode no/g' redis7001.conf #开启持久化,注释监听ip#echo '#集群配置' >> /opt/docker-redis/7001/redis7001.confecho 'cluster-enabled yes' >>/opt/docker-redis/7001/redis7001.confecho 'cluster-config-file nodes-7001.conf' >>/opt/docker-redis/7001/redis7001.confecho 'cluster-node-timeout 15000' >>/opt/docker-redis/7001/redis7001.conf#echo 'cluster-announce-ip 192.168.92.135' >>/opt/docker-redis/7001/redis7001.confecho 'cluster-announce-port 7001' >>/opt/docker-redis/7001/redis7001.confecho 'cluster-announce-bus-port 17001' >>/opt/docker-redis/7001/redis7001.conf#for port in `seq 7002 7006`;do cp redis7001.conf ../${port}/redis${port}.conf echo "cluster-config-file nodes-${port}.conf" >>/opt/docker-redis/${port}/redis${port}.conf echo "cluster-announce-port ${port}" >>/opt/docker-redis/${port}/redis${port}.conf echo "cluster-announce-bus-port 1${port}" >>/opt/docker-redis/${port}/redis${port}.confdone#for port in `seq 7001 7006`;do# sed -i "s/logfile \"\"/logfile \"\/usr\/local\/docker\/redis-cluster\/log\/redis.log\"/g" redis${port}.conf sed -i "s/port 6379/port ${port}/g" /opt/docker-redis/${port}/redis${port}.conf docker run --restart always --name redis-cluster-${port} --net host --privileged=true -v /opt/docker-redis/${port}/redis${port}.conf:/usr/local/docker/redis-cluster/${port}/redis${port}.conf -v \ /opt/docker-redis/${port}/data/:/usr/local/docker/redis-cluster/data/ \ -d redis redis-server /usr/local/docker/redis-cluster/${port}/redis${port}.confdonedocker pssleep 2sss -tnulp|grep redis#创建集群#redis-cli --cluster create 192.168.92.135:7001 192.168.92.135:7002 192.168.92.135:7003 192.168.92.135:7004 192.168.92.135:7005 192.168.92.135:7006 --cluster-replicas 1下载,解压,编译安装 用一台虚拟机模拟6个节点,创建出3 master、3 salve 环境。 创建文件前,先编译好Redis程序 创建节点创建配置redis.conf 12345678910111213141516# 端口7000 7001 7002 7003 7004 7005port 7000# 自己建议修改为0.0.0.0bind 0.0.0.0# redis后台运行daemonize yes# pidfile文件对应7000 7001 7002 7003 7004 7005pidfile /var/run/redis_7000.pid# 开启集群 把注释#去掉cluster-enabled yes# 集群的配置,配置文件首次启动自动生成7000 7001 7002 7003 7004 7005cluster-config-file nodes_7000.conf# 请求超时 默认15秒,可自行设置cluster-node-timeout 15000# AOF日志开启appendonly yes 创建文件夹 12345678910cd /usr/localmkdir redis_clustercd redis_clustermkdir 7000 7001 7002 7003 7004 7005cp /usr/local/redis-5.0.5/redis.conf /usr/local/redis_cluster/7000/cp /usr/local/redis-5.0.5/redis.conf /usr/local/redis_cluster/7001/cp /usr/local/redis-5.0.5/redis.conf /usr/local/redis_cluster/7002/cp /usr/local/redis-5.0.5/redis.conf /usr/local/redis_cluster/7003/cp /usr/local/redis-5.0.5/redis.conf /usr/local/redis_cluster/7004/cp /usr/local/redis-5.0.5/redis.conf /usr/local/redis_cluster/7005/ 分别修改三个文件夹里的配置文件,修改如下内容 启动节点的redis/usr/local/bin/redis-server 123456/usr/local/bin/redis-server /usr/local/redis_cluster/7000/redis.conf/usr/local/bin/redis-server /usr/local/redis_cluster/7001/redis.conf/usr/local/bin/redis-server /usr/local/redis_cluster/7002/redis.conf/usr/local/bin/redis-server /usr/local/redis_cluster/7003/redis.conf/usr/local/bin/redis-server /usr/local/redis_cluster/7004/redis.conf/usr/local/bin/redis-server /usr/local/redis_cluster/7005/redis.conf 检查 redis 启动情况 1234567ps -ef | grep rediroot 61020 1 0 02:14 ? 00:00:01 redis-server 0.0.0.0:7000 [cluster]root 61024 1 0 02:14 ? 00:00:01 redis-server 0.0.0.0:7001 [cluster]root 61029 1 0 02:14 ? 00:00:01 redis-server 0.0.0.0:7002 [cluster]root 61029 1 0 02:14 ? 00:00:01 redis-server 0.0.0.0:7002 [cluster]root 61029 1 0 02:14 ? 00:00:01 redis-server 0.0.0.0:7002 [cluster]root 61029 1 0 02:14 ? 00:00:01 redis-server 0.0.0.0:7002 [cluster] 启动集群 装的redis是5.x的版本,这里没有应用到redis-trib.rb,所以就不需要装ruby 这里启动的时候可能会有报错,因为默认开启了protected-mode 12345cd /usr/local/binredis-cli --cluster create 192.168.0.100:7003 192.168.0.100:7004 192.168.0.100:7005 192.168.0.179:7000 192.168.0.179:7001 192.168.0.179:7002 --cluster-replicas 1Can I set the above configuration? (type 'yes' to accept): yesyes 校验,等运行完成 1234567891011121314151617181920212223242526272829[root@worker1 src]# redis-cli --cluster check 192.168.0.179:7000192.168.0.179:7000 (27bce53b...) -> 0 keys | 5462 slots | 1 slaves.192.168.0.100:7004 (6b0173d9...) -> 0 keys | 5461 slots | 1 slaves.192.168.0.100:7003 (9f15a932...) -> 0 keys | 5461 slots | 1 slaves.[OK] 0 keys in 3 masters.0.00 keys per slot on average.>>> Performing Cluster Check (using node 192.168.0.179:7000)M: 27bce53bda92341ca4a5c82c2361ab99f24c0b27 192.168.0.179:7000 slots:[5461-10922] (5462 slots) master 1 additional replica(s)S: c7ebcd900fb7d9afb1980797acba45518cb7d877 192.168.0.100:7005 slots: (0 slots) slave replicates 27bce53bda92341ca4a5c82c2361ab99f24c0b27S: ed5256f8db1bf556a8dadbe8f2b07699507e17d9 192.168.0.179:7001 slots: (0 slots) slave replicates 6b0173d925f70807a9081b7bc09bcd37be857342S: 758609eaea88bac25b864f2badbab2171a68089b 192.168.0.179:7002 slots: (0 slots) slave replicates 9f15a9329a9d0ec5c7fcb5abbba817730f0942f9M: 6b0173d925f70807a9081b7bc09bcd37be857342 192.168.0.100:7004 slots:[10923-16383] (5461 slots) master 1 additional replica(s)M: 9f15a9329a9d0ec5c7fcb5abbba817730f0942f9 192.168.0.100:7003 slots:[0-5460] (5461 slots) master 1 additional replica(s)[OK] All nodes agree about slots configuration.>>> Check for open slots...>>> Check slots coverage...[OK] All 16384 slots covered. Cluster配置 1234567891011# 设置加入cluster,成为其中的节点cluster-enabled yes|no# cluster配置文件名,该文件属于自动生成,仅用于快速查找文件并查询文件内容cluster-config-file < filename># 节点服务响应超时时间,用于判定该节点是否下线或切换为从节点cluster-node-timeout < milliseconds># master连接的slave最小数量cluster-migration-barrier < count> Cluster节点操作命令 1234567891011121314151617181920212223242526# 连接到集群,加一个-c就行redis-cli -c# 查看集群节点信息cluster nodes# 进入一个从节点redis,切换其主节点cluster replication <master-id># 发现一个新节点,新增主节点cluster meet ip:port# 忽略一个没有solt的节点cluster forget# 手动故障转移cluster failover# 计算键 key 应该被放置在哪个槽上cluster keyslot <key># 返回槽 slot 目前包含的键值对数量cluster countkeysinslot <slot># 返回 count 个 slot 槽中的键cluster getkeysinslot <slot> <count> 配置12345# requirepass foobared 注释去掉并写入要设置的密码 , 需要重启:systemctl restart redisrequirepass 123456# 将 bind 设置为允许所有 IP 地址的远程访问,需要重启:systemctl restart redisbind 0.0.0.0 Redis的数据类型redis自身是一个Map,其中所有的数据都是采用key:value的形式存储 数据类型指的是存储的数据的类型,也就是value部分的类型,key部分永远都是字符串 string –> String hash –> Hashmap list –> LinkList set –> HashSet sorted_set –> TreeSet String redis所有的操作都是原子性的,采用单线程处理所有业务,命令是一个一个执行的 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586# 设值# 格式 set <key> <value>192.168.245.129:0>set k1 anthony"OK"# 取值# 格式 get <key>192.168.245.129:0>get k1"anthony"# 删除值,成功返回1,不成功返回0192.168.245.129:0>del k1"1"192.168.245.129:0>del k1234"0"# 一次性存入多个值192.168.245.129:0>mset k1 v1 k2 v2 k3 v3"OK"# 一次性取出多个值192.168.245.129:0>mget k1 k2 k3 1) "v1" 2) "v2" 3) "v3"# 打印值的长度192.168.245.129:1>set name anthony"OK"192.168.245.129:1>get name"anthony"192.168.245.129:1>strlen name"7"# 追加# 有数据就追加192.168.245.129:1>append name 666"10"192.168.245.129:1>get name"anthony666"# 没数据就新建192.168.245.129:1>append othername frankie"7"192.168.245.129:1>get othername"frankie"# 递增递减,小数不行192.168.245.129:1>set num 1"OK"192.168.245.129:1>incr num"2"192.168.245.129:1>incr num"3"192.168.245.129:1>decr num"2"192.168.245.129:1>decr num"1"# 递增递减指定值,小数不行192.168.245.129:1>incrby num 4"6"192.168.245.129:1>decrby num 2"4"# 小数不行192.168.245.129:1>incrby num 1.5"ERR value is not an integer or out of range"# 递增指定小数,貌似没有递减192.168.245.129:1>incrbyfloat num 1.5"5.5"# 指定过期时间# EX 用于指定 key 的过期时间。EX(秒),PX(毫秒),KEEPTTL 保持原有的过期时间不变。# PX 毫秒-设置指定的到期时间(以毫秒为单位)。# NX 没有key的时候才能set成功,XX key存在的时候才能set成功SET key value [EX seconds|PX milliseconds|KEEPTTL] [NX|XX] [GET]# 指定过期时间# time,以秒为单位。SETEX key time value# 功能等价NX, SET if Not eXistsredis> SETNX mykey "Hello"(integer) 1redis> SETNX mykey "World"(integer) 0 Hash graph LR; Key; Key-->Field1--> Value1; Key-->Field2--> Value2; Key-->Field3--> Value3; subgraph 相当于Value Field1; Value1; Field2; Value2; Field3; Value3; end; 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748# 设值/修改值 hset key filed1 value192.168.245.129:0>HSET user name zhangsan"1"192.168.245.129:0>hset user age 38"1"# 取一个属性值192.168.245.129:0>hget user age"38"# 取多个属性值192.168.245.129:0>hmget user age name 1) "45" 2) "zhangsan"# 取一个key192.168.245.129:0>hgetall user 1) "name" 2) "zhangsan" 3) "age" 4) "38" # 删除一个属性值192.168.245.129:0>hdel user name"1"192.168.245.129:0>hgetall user 1) "age" 2) "38" # 查看有多少个属性 192.168.245.129:0>hlen user"3"# 获取所有的属性192.168.245.129:0>hkeys user 1) "age" 2) "name" 3) "sex"# 获取所有的属性值192.168.245.129:0>hvals user 1) "45" 2) "zhangsan" 3) "n" # 指定属性增加指定值 192.168.245.129:0>hincrby user age 1"46" List flowchart LR subgraph 顺序表/数组 id0(头指针) id1[华为] id2(苹果) id3(微软) end subgraph 单向链表 direction LR id00[头指针] --> id4[[华为]] id4[[华为]] --> id5[[苹果]] id5[[苹果]] --> id6[[微软]] end subgraph 双向链表 direction LR id000[头指针] <--> id7[[华为]] id7[[华为]] <--> id8[[苹果]] id8[[苹果]] <--> id9[[微软]] end 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758# 先插入huawei192.168.245.129:0>lpush list1 huawei"1"# 再插入apple192.168.245.129:0>lpush list1 apple"2"# 最后插入Microsoft192.168.245.129:0>lpush list1 microsoft"3"# 从左边取192.168.245.129:0>lrange list1 0 2 1) "microsoft" 2) "apple" 3) "huawei" # 一次性插入多条数据192.168.245.129:0>rpush list2 a b c"3"# 从左边取# lrange key start stop# lindex key index 取指定索引的值# llen key 取长度# 没有rrange192.168.245.129:0>lrange list2 0 2 1) "a" 2) "b" 3) "c" # 从左边取的第二钟方法192.168.245.129:0>lrange list2 0 -1 1) "a" 2) "b" 3) "c"# lpop从左边删,rpop从右边删192.168.245.129:0>lpush list3 a b c"3"192.168.245.129:0>lpop list3"c"----------------------------------------------------------------------------------------------------# 阻塞取值,从运行命令开始,如果有数据,取出来,立马返回,如果没有数据,就等指定的时间20s,有就立马返回结束,如果没有,就一直等到时间结束192.168.245.129:0>blpop list4 20 1) "list4" 2) "32"# 删除指定数据# lrem key count value# count是删除多少个value192.168.245.129:0>lpush dianzan a b c d"4"192.168.245.129:0>lrem dianzan 1 c"1"192.168.245.129:0>lrange dianzan 0 -1 1) "d" 2) "b" 3) "a" Set 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263# 添加192.168.245.129:0>sadd users zs"1"192.168.245.129:0>sadd users lisi"1"192.168.245.129:0>sadd users ww"1"# 查列表192.168.245.129:0>smembers users 1) "lisi" 2) "ww" 3) "zs" # 查数量192.168.245.129:0>scard users"3"# 判断是否有指定数据192.168.245.129:0>sismember users ls"0"192.168.245.129:0>sismember users ww"1"192.168.245.129:0>smembers users 1) "lisi" 2) "ww" 3) "zs" # 删除指定数据192.168.245.129:0>srem users ww"1"192.168.245.129:0>smembers users 1) "lisi" 2) "zs" # 随机获取集合中指定数量的数据,获取之后,原来的队列数据不变 srandmember key count # 随机获取集合中的某个数据并讲该数据移除集合 spop key -------------------------------------------------------------------------------------- > sadd u1 a11> sadd u1 a21> sadd u1 a31> sadd u2 a11> sadd u2 a21# 交集> sinter u1 u2a2a1# 并集> sunion u1 u2a1a2a3# 差集 (u1,u2)顺序不一样,结果不一样> sdiff u1 u2a3 redis应用于随机推荐类信息检索,例如热点歌单推荐,热点新闻推荐,热点旅游线路,应用APP推荐,大V推荐等 sort_set12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364# 添加数据> zadd scores 100 zhangsan1> zadd scores 90 lisi1> zadd scores 95 wangwu1# 获取全部数据> zrange scores 0 -1lisiwangwuzhangsan# 获取全部数据> zrange scores 0 -1 withscoreslisi90wangwu95zhangsan100# 反向获取全部数据> zrevrange scores 0 -1 withscoreszhangsan100wangwu95lisi90# 删除数据> zrem scores zhangsan1> zrange scores 0 -1 withscoreslisi90wangwu95# 按条件获取数据,可以用作分页zrangebyscore key min max [WITHSCORES] [LIMIT]zrevrangebyscore key max min [WITHSCORES]# 条件删除zremrangebyrank key start stopzremrangebyscore key min max-----------------------------------------排名-------------------------------------------------------# 添加模拟数据> zadd movies 143 aa 97 bb 201 cc3# 获取数据对应的索引(排名)> zrank movies bb0# 反向获取> zrevrank movies bb2# score 值获取与修改zscore key memberzincrby key increment member Redis 通用命令1234567891011121314151617181920212223# 删除del key# 获取key是否存在exists key# 获取key的类型type key# 设置指定有效期,秒expire key second# 设置指定有效期,毫秒pexpire key milliseconds# 有效期是时间戳expireat key timestamp# 获取key的有效期ttl keypttl key 毫秒# 集群环境下,模糊查询key# 要是查不出来数据,99999要换成一个很大的值SCAN 0 MATCH * count 99999 持久化很多时候我们需要持久化数据也就是将内存中的数据写入到硬盘里面,大部分原因是为了之后重用数据(比如重启机器、机器故障之后恢复数据),或者是为了防止系统故障而将数据备份到一个远程位置。 Redis不同于Memcached的很重一点就是,Redis支持持久化,而且支持两种不同的持久化操作。 Redis的一种持久化方式叫快照(snapshotting,RDB) 另一种方式是只追加文件(append-only file,AOF) 这两种方法各有千秋,下面我会详细这两种持久化方法是什么,怎么用,如何选择适合自己的持久化方法。 RDB(快照)save 会生成rdb文件 save指令相关配置 配置项 描述 dbfilename dump.rdb 指定本地数据库文件名,默认值为 dump.rdb dir ./ 指定本地数据库存放目录 注意:Redis是单线程的,所有命令都会在类似队列中排好队,不建议使用save指令,因为save指令的执行会阻塞当前Redis服务器,直到当前RDB过程完成位置,有可能会造成长时间阻塞,线上环境不建议使用 bgsave指令手动启动后台保存操作,但不是立即执行 执行成功了不会在控制台输出,可以在日志中看到 1234558142:M 07 Aug 2020 07:23:17.355 * Starting BGSAVE for SYNC with target: disk58142:M 07 Aug 2020 07:23:17.355 * Background saving started by pid 5818358183:C 07 Aug 2020 07:23:17.357 * DB saved on disk58183:C 07 Aug 2020 07:23:17.357 * RDB: 0 MB of memory used by copy-on-write58142:M 07 Aug 2020 07:23:17.456 * Background saving terminated with success bgsave命令是针对save阻塞问题做的优化。Redis内部所有涉及到RDB操作都采用bgsave的方式,save命令可以放弃使用 save配置1234567# second:监控时间范围# changes:监控key的变化量save second changessave 900 1 #在900秒(15分钟)之后,如果至少有1个key发生变化,Redis就会自动触发BGSAVE命令创建快照。save 300 10 #在300秒(5分钟)之后,如果至少有10个key发生变化,Redis就会自动触发BGSAVE命令创建快照。save 60 10000 #在60秒(1分钟)之后,如果至少有10000个key发生变化,Redis就会自动触发BGSAVE命令创建快照。 save配置原理 **注意:**save配置要根据实际业务情况进行设置,频度过高或过低都会出现性能问题,结果可能是灾难性的save配置中对second与changes设置通常具有互补对应关系,尽量不要设置成包含性关系save配置启动后执行的是bgsave操作 对比 RDB启动方式 全量复制在主从复制中会提到 服务器运行过程中重启debug reload 关闭服务器时指定保存数据shutdown save RDB 优缺点RDB优点 RDB是一个紧凑压缩的二进制文件,存储效率较高 RDB内部存储的是redis在某个时间点的数据快照,非常适合用于数据备份,全量复制等场景 RDB恢复数据的速度要比AOF快很多 应用:服务器中每X小时执行bgsave备份,并将RDB文件拷贝到远程己气中,用于灾难恢复 RDB缺点 RDB方式无论是执行指令还是利用配置,无法做到实时持久化,具体较大的可能性丢失数据 bgsave指令每次运行要执行fork操作创建子进程,要牺牲掉一些性能 Redis的众多版本中未进行RDB文件格式的版本统一,有可能出现个版本服务之间数据格式无法兼容现象 RDB存储的弊端 存储数据量较大,效率较低——基于快照思想,每次读写都是全部数据,当数据量巨大时,效率非常低 大数据量下的IO性能较低 基于fork创建子进程,内存产生额外消耗 宕机带来的数据丢失风险 解决思路 不写全数据,仅记录部分数据 改记录数据未记录操作过程 对所有操作均进行记录,排除丢失数据的风险 这也就是AOF的引入 AOFAOF写数据过程 AOF写数据的三种策略123appendfsync always #每次有数据修改发生时都会写入AOF文件,这样会严重降低Redis的速度appendfsync everysec #每秒钟同步一次,显示地将多个写命令同步到硬盘 **(默认的)**appendfsync no #让操作系统决定何时进行同步 AOF功能开启和相关配置1234567891011# 是否开启APF持久化功能,默认为不开启appendonly yes|no# AOF写数据策略appendfsync always|everysec|no# AOF持久化文件名,默认文件名为appendonly.aof,建议配置为appendonly-端口号.aofappendfilename filename# AOF持久化文件保存路径,与RDB持久化文件保持一致即可dir 重写随着命令的不断写入AOF,文件会越来越大,为了解决这个问题,Redis引入AOF重写机制压缩文件体积,AOF文件重写是将Redis进程内的数据转换为写命令同步到新AOF文件的过程,简单说就是将同样一个数据的若干个命令执行结果转换为最终结果数据对应的指令进行记录 AOF重写作用 降低磁盘占用量,提高磁盘利用路 提高持久化效率,降低持久化写时间,提高IO性能 降低数据恢复用时,提高数据恢复效率 AOF重写规则 进程内已超时的数据不再写入文件 忽略无效指令,重写时使用进程内数据直接生成,这样新的AOF文件只保留最终数据的写入命令 如del key1,hdel key2,srem key3,set key 222等 对统一数据的多条命令合并为一条命令如 lpush list1 a ,lpush list1 b,lpush list1 c可以转化为lpush list1 a b c为防止数据量过大造成客户端缓冲区溢出,对list,set,hash,set等类型,每条指令最多写入64个元素 重写方式123456# 手动重写,在命令行执行,会覆盖原来的aof文件,但是文件更小bgrewriteaof# 自动重写auto-aof-rewrite-min-size sizeauto-aof-rewrite-percentage percentage 手动重写流程: 自动重写的触发条件: 1234567# 自动重写触发条件设置auto-aof-rewrite-min-sizeauto-aof-rewrite-percentage percent# 自动重写触发对比参数(运行指令info Persistence获取具体信息)aof_current_sizeaof_base_size AOF和RDB的区别 RDB和AOF的选择之感 对数据非常敏感,建议使用默认的AOF持久化方案AOF持久化策略使用erverysecond,每秒钟fsync一次。该策略redis任然可以保持很好的处理性能,当出现问题时,最多丢失0-1秒中的数据。注意:由于AOF文件存储体积较大,且恢复数据较慢 数据呈现阶段有效性,建议使用RDB持久化方案数据可以良好的做到阶段内无丢失(该阶段是开发者或运维人工手工维护的),且恢复速度较快,阶段点数据恢复通常采用RDB方案注意:利用RDB实现紧凑的数据持久化会使Redis降得很低 综合对比 RDB与AOF得选择实际上是在做一种权衡,每种都有利弊 如不能承受数分钟以内得数据丢失,对业务数据非常敏感,选用AOF 如能承受数分钟以内数据丢失,且追求大数据集得恢复速度,选用RDB灾难恢复选用RDB 双保险策略,同时开启RDB和AOF,重启后,Redis优先使用AOF来恢复数据,降低丢失数据的量 Redis 4.0 对于持久化机制的优化Redis 4.0 开始支持 RDB 和 AOF 的混合持久化(默认关闭,可以通过配置项 aof-use-rdb-preamble 开启)。 如果把混合持久化打开,AOF 重写的时候就直接把 RDB 的内容写到 AOF 文件开头。这样做的好处是可以结合 RDB 和 AOF 的优点, 快速加载同时避免丢失过多的数据。当然缺点也是有的, AOF 里面的 RDB 部分是压缩格式不再是 AOF 格式,可读性较差。 事务12345678# 开启事务,设定事务的开启位置,此指令执行后,后续的所有指令均加入到事务中multi# 执行提交事务,加入事务的命令暂时到任务队列中,并没有立即执行,只有执行exec命令才开始执行exec# 取消事务,终止当前事务定义,发生在multi之后,exec之前discard 事务的工作流程 事务的注意事项 语法错误指命令书写格式有误 处理结果如果定义的事务中所包含的命令存在语法错误,整体事务中所有命令均不会被执行。包括那些语法正确的命令 运行错误指命令格式正确,但是无法正常的执行。例如对list进行incr操作 处理结果能够正确运行的命令会执行,运行错误的命令不会执行注意:已经执行完毕的命令对应的数据不会自动回滚,需要程序员自己在代码中实现回滚。 手动进行事务回滚(基本没法用) 记录操作过程中被影响的数据之前的状态单数据:string多数据:hash,list,set,zset 设置指令恢复所有的被修改的项单数据:直接set(注意周边属性,例如时效)多数据:修改对应值或整体克隆复制 锁12345# 对key添加监视锁,在执行exec前如果key发生了变化,终止事务执行watch key1 [key2…]# 取消对所有key的监视unwatch 分布式锁12345678# 加锁setnx lock-key value# 设置超时时间expire lock-key second# 删除锁dek lock-key 利用setnx命令的返回值特征,有值则返回设置失败,无值则返回设置成功 对于返回设置成功的,拥有控制权,进行下一步的具体业务操作 对于返回设置失败的,不具有控制权,排队或等待操作完毕通过del操作释放锁 删除策略过期数据删除策略 Redis是一种内存级数据库,所有数据均存放在内存中,内存中的数据可以通过TTL指令获取其状态 XX : 具有时效性的数据 1 : 永久有效的数据 2 : 已经国企的数据 或 被删除的数据 或 未定义的数据 时效性数据的存储结构 定时删除 创建一个定时器,当key设置过期时间,且过期时间到达时,由定时器任务立即执行对键的删除操作 优点:节约内存,到时就删除,快速释放掉不必要的内存占用 缺点:CPU压力很大,无论CPU此时负载多高,均占用CPU,会影响redis服务器响应时间和指令吞吐量 总结:用处理器性能换取存储空间 惰性删除 数据到达过期时间,不做处理。等下次访问该数据 如果未过期,返回数据 发现已经过期,删除,返回不存在 优点:节约CPU性能,发现必须删除的时候才删除 缺点:内存压力很大,出现长期占用内存的数据 总结:用存储空间换取处理器性能 定期删除 Redis启动服务器初始化时,读取配置server.hz的值,默认为10 每秒钟执行server.hz次serverCron() 周期性轮询redis库中时效性数据,采用随机抽取的策略,利用过期数据占比的方式删除频度 特点1:CPU性能占用设置有峰值,检测频度可自定义设置 特点2:内存压力不是很大,长期占用内存的冷数据会被持续清理 总结:周期性抽查存储空间 逐出算法 Redis使用内存存储数据,在执行每一个命令前,会调用freeMemorylfNeeded()检测内存是否充足。如果内存不满足新加入数据的最低存储要求,redis要临时删除一些数据为当前指令清理存储空间。清理数据的策略称为逐出算法。 注意:逐出数据的过程不是100%能够清理出足够的可使用的内存空间,如果不成功则反复执行。当对所有数据尝试完毕后,如果不能达到内存清理的要求,将出现错误信息 相关配置 12345678# 最大可使用内存maxmemory# 每次选取代删除数据的个数maxmemory-samples# 删除策略maxmemory-policy 属性 说明 volatile-lru 从已设置过期时间的数据集(server.db[i].expires)中挑选最近最少使用的数据淘汰 volatile-ttl 从已设置过期时间的数据集(server.db[i].expires)中挑选将要过期的数据淘汰 volatile-random 从已设置过期时间的数据集(server.db[i].expires)中任意选择数据淘汰 volatile-lfu 从已设置过期时间的数据集(server.db[i].expires)中挑选最不经常使用的数据淘汰 allkeys-lru 当内存不足以容纳新写入数据时,在键空间中,移除最近最少使用的key (这个是最常用的) allkeys-random 从数据集(server.db[i].dict)中任意选择数据淘汰 allkeys-lfu 当内存不足以容纳新写入数据时,在键空间中,移除最不经常使用的key no-eviction 禁止驱逐数据,也就是说当内存不足以容纳新写入数据时,新写入操作会报错。这个应该没人使用吧! 数据逐出策略配置依据 使用INFO命令输出监控信息,查询缓存int和miss的次数,根据业务需求调优Redis配置 配置文件1234567891011121314151617181920212223242526272829# 设置服务器以守护进程的方式运行deamonize yes|no# 绑定主机地址bind 127.0.0.1# 设置服务器端口号port 6379# 设置数据库数量databases 16----------------------------------日志配置--------------------------------------# 设置服务器以指定日志记录级别loglevel debug|verbose|notice|warning# 日志记录文件名logfile 端口号.log# 持久化文件存放目录dir ./----------------------------------日志配置--------------------------------------# 设置同一时间最大客户链接数,默认无限制。当客户端连接到达上线,Redis会关闭新的链接maxclients 0# 客户端限制等待最大时常,达到最大之后关闭连接。如需关闭该功能,设置为0timeout 300 Redis-Cluster结构设计数据存储设计Key -> CRC16(Key) 相当于HashCode值—>%16384 将所有的存储空间计划切割成16384份,每台主机保存一部分,每份代表的使一个存储空间,不是一个key的保存空间 将key按照计算出的结果放到对应的存储空间 当有新的机器加入集群的时候,就会每台机器转移一些数据空间 集群内部通讯设计 集群常用命令参考:https://www.cnblogs.com/kevingrace/p/7910692.html 以下命令是Redis Cluster集群所独有的,执行下面命令需要先登录redis 123# (客户端命令:redis-cli -c -p port -h ip)[root@manage redis]# redis-cli -c -p 6382 -h 192.168.10.12192.168.10.12:6382> 集群cluster info :打印集群的信息cluster nodes :列出集群当前已知的所有节点( node),以及这些节点的相关信息。 节点cluster meet <ip> <port> :将 ip 和 port 所指定的节点添加到集群当中,让它成为集群的一份子。cluster forget <node_id>:从集群中移除 node_id 指定的节点。cluster replicate <master_node_id> :将当前从节点设置为 node_id 指定的master节点的slave节点。只能针对slave节点操作。cluster saveconfig :将节点的配置文件保存到硬盘里面。 slotcluster addslots <slot> [slot ...] :将一个或多个槽( slot)指派( assign)给当前节点。cluster delslots <slot> [slot ...] :移除一个或多个槽对当前节点的指派。cluster flushslots :移除指派给当前节点的所有槽,让当前节点变成一个没有指派任何槽的节点。cluster setslot <slot> node <node_id>:将槽 slot 指派给 node_id 指定的节点,如果槽已经指派给 另一个节点,那么先让另一个节点删除该槽>,然后再进行指派cluster setslot <slot> migrating <node_id>:将本节点的槽 slot 迁移到 node_id 指定的节点中。cluster setslot <slot> importing <node_id> :从 node_id 指定的节点中导入槽 slot 到本节点。cluster setslot <slot> stable:取消对槽 slot 的导入( import)或者迁移( migrate)。 键cluster keyslot <key>:计算键 key 应该被放置在哪个槽上。cluster countkeysinslot <slot>:返回槽 slot 目前包含的键值对数量。cluster getkeysinslot <slot> <count>:返回 count 个 slot 槽中的键 。 解决方案缓存预热在高请求之前,做好一系列措施,保证大量用户数量点击造成灾难。 请求数量较高 主从之间数据吞吐量较大,数据同步操作频度较高 缓存预热解决方案前置准备工作: 日常例行统计数据访问记录,统计访问频度较高的热点数据 利用LRU数据删除策略,构建数据留存队列例如:storm与kafka配合 准备工作: 将统计结果中的数据分类,根据级别,redis优先加载级别较高的热点数据 利用分布式多服务器同时进行数据读取,提速数据加载过程 实施: 使用脚本程序固定出大数据预热过程 如果条件允许,使用CDN(内容分发网络),效果会更好 缓存预热总结:缓存预热就是系统启动前,提前将相关的缓存数据直接加载到缓存系统。避免在用户请求的时候,先查询数据库,然后再将数据缓存的问题!用户直接查询事先被预热的缓存数据! 缓存雪崩 系统平稳运行过程中,忽然数据库连接量激增 应用服务器无法及时请求 大量408,500错误页面出现 客户反复刷新页面获取数据 数据库崩溃 应用服务器崩溃 重启应用服务器无效 Redis服务器崩溃 Redis集群崩溃 重启数据之后再次被瞬间流量放倒 问题排查简介:缓存同一时间大面积的失效,所以,后面的请求都会落到数据库上,造成数据库短时间内承受大量请求而崩掉 在一个较短的时间内,缓存中较多的key集中过期 此周期内请求访问过期的数据,redis未命中,redis向数据库获取数据 数据库同时接受到大量的请求无法即时处理 Redis大量请求被积压,开始出现超时现象 数据库流量激增,数据库崩溃 重启后任然面对缓存中无数据可用 Redis服务器资源被严重占用,Redis服务器崩溃 Redis集群呈现崩塌,集群瓦解 应用服务器无法即时得到数据响应请求,来自客户端的请求数量越来越多,应用服务器崩溃 应用服务器,redis,数据库全部重启,效果不理想 问题分析 短时间范围内 大量key集中过期 解决方案(道) 更多的页面静态化处理 构建多级缓存架构Nginx缓存+redis缓存+ehcache缓存 检测Mysql严重耗时业务进行优化对数据库的瓶颈排查:例如超时查询、耗时较高事务等 灾难预警机制监控redis服务器性能指标1、CPU占用、CPU使用率2、内存容量3、查询平均响应时间4、线程数 限流、降级短时间范围内习生一些客户体验,限制一部分请求访问,降低应用服务器压力,待业务低速运转后再逐渐放开访问 解决方案(术) LRU与LFU切换 数据有效期策略调整根据业务数据有效期进行分类错峰,A类90分钟,B类80分钟,C类70分钟过期时间使用固定形式+随机值的形式,稀释集中到期的key的数量 超热数据使用永久key 定期维护(自动+人工)对即将过期数据做访问量分析,确认是否演示,配合访问量统计,做热点数据的延时 加锁 慎用!!!!!!! 总结缓存雪崩式瞬间过期数量太大,导致对数据库服务器造成压力。如果能有效避免过期时间集中,可以有效解决雪崩现象的出现(约40%)。配合其他策略一起使用,并监控服务器的运行数据,根据运行巨鹿做快速调整 缓存击穿数据库服务器崩溃(2) 系统平稳运行过程中 数据库连接量瞬间激增 Redis服务器无大量key过期 Redis内存平稳,无波动 Redis服务器CPU正常 数据库崩溃 问题排查 Redis中某个key过期,该key访问量巨大 多个数据请求从服务器直接压到Redis后,均为命中 Redis在短时间内发起了大量对数据库中同一个数据的访问 问题分析 单个key高热数据 key过期 解决方案(术) 预先设定以电商为例,每个商家根据店铺等级,指定若干款主打商品,在购物节期间,加大此类信息key的过期时常注意:购物节不仅仅指当天,以及后续若干天,访问峰值呈现逐渐降低趋势 现场调整监控访问量,对自然流量激增的数据延长过期时间或设置为永久性key 后台刷新数据启动定时任务,高峰期来临之前,刷新数据有效期,保存不丢失 二级缓存设置不同的失效时间,保障不会被同时淘汰就行 加锁分布式锁,防止被击穿,但是要注意也是性能瓶颈,慎重!!!!!!!! 总结:缓存击穿就是单个高热数据过期的瞬间,数据访问较大,未命中redis后,发起了大量对同一数据的数据库访问,导致对数据库服务器造成压力。应对策略应该在业务数据分析与预防方面进行,配合运行监控测试与即时调整策略,毕竟单个key的过期监控难度较高,配合雪崩处理策略即可 缓存穿透数据库服务器崩溃(3)简介:一般是黑客故意去请求缓存中不存在的数据,导致所有的请求都落到数据库上,造成数据库短时间内承受大量请求而崩掉。 系统平稳运行过程中 应用服务器流量随时间增量较大 Redis服务器命中率随时间逐步降低 Redis内存平稳,内存无压力 Redis服务器CPU占用激增 数据库服务器压力激增 数据库崩溃 问题排查 redis中大面积出现未命中 出现非正常URL访问 问题分析 获取的数据在数据库中也不存在,数据库查询未得到对应数据 Redis获取到null数据未进行持久化,直接返回 下次此类数据到达重复上述过程 出现黑客攻击服务器 解决方法(术) 缓存null对查询结果为null的数据进行缓存(长期使用,定期清理),设定短时限,例如30-60秒,最高五分钟 白名单策略提前预热各种分类数据id对应的bitmaps,id作为bitmaps的offset,相当于设置了数据白名单。当加载正常数据后放型,加载异常数据时直接拦截(效率偏低)使用布隆过滤器(有关布隆过滤器的命中问题对当前状态可以忽略) 实时监控试试监控redis命中率(业务正常范围时,通常回有一个波动值)与null数据的占比非活动时间波动:通常检测3-5倍,超过5倍纳入重点排查对象活动时间波动:通常检测10-50倍,超过50倍纳入重点排查对象根据背书不同,启动不同的排查流程。然后使用黑名单进行防控(运营) key加密问题出现后,临时启动防灾业务key,对key进行业务层传输加密服务,设定校验程序,过来的key校验例如每天随机分配60个加密串,挑选2-3个,混淆到页面数据id中,发现访问key不满足规则,驳回数据访问 总结缓存穿透是访问了不存在的数据,跳过了合法数据的redis数据缓存阶段,每次访问数据库,导致对数据库服务器造成压力。通常此类数据的出现量是一个较低的值,当出现此类情况以毒攻毒,并即时报警。应对策略应该在临时预案防范方面多做文章无论是黑名单还是白名单,都是对整体系统的压力,警报解除后尽快移除 Redis-Java单机Jedis123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149/** * 192.168.0.5 单机 */public class SingletonDemo { private Jedis jedis; @Before public void before() { //指定Redis服务Host和port jedis = new Jedis("192.168.0.5", 6379); } @After public void after() { //使用完关闭连接 jedis.close(); } /** * 设值 */ @Test public void set() { //访问Redis服务 jedis.set("k1", "v1"); System.out.println(jedis.get("k1")); } /** * 设值,并且设置超时时间 */ @Test public void setTime() { String key = "setTimeKey"; jedis.setex(key, 20000, "setTimeValue"); System.out.println(jedis.get(key)); System.out.println(jedis.ttl(key)); } /** * 分布式锁原理, * key不存在的时候才插入 */ @Test public void setNX() { String key = "setNXKey"; // 1.先插入一个值 jedis.set(key, "setNXValue"); // 2.NX是不存在时才set, XX是存在时才set, EX是秒,PX是毫秒 String set = jedis.set(key, "setNXValue---NX", "NX", "EX", 20000L); System.out.println(set); System.out.println(jedis.get(key)); System.out.println(jedis.ttl(key)); System.out.println("======================================================"); // 3.设置个没有的key String key2 = "setNXKey2"; String set2 = jedis.set(key2, "setNXValue2---NX", "NX", "EX", 20000L); System.out.println(set2); System.out.println(jedis.get(key2)); System.out.println(jedis.ttl(key2)); } /** * key存在时才插入 */ @Test public void setXX() { String key = "setXXKey"; // 1.先插入一个值 jedis.set(key, "setXXValue"); // 2.NN String set = jedis.set(key, "setXXValue---XX", "XX", "EX", 20000L); System.out.println(set); System.out.println(jedis.get(key)); System.out.println(jedis.ttl(key)); System.out.println("======================================================"); // 3.设置个没有的key String key2 = "setXXKey2"; String set2 = jedis.set(key2, "setXXValue2---NX", "XX", "EX", 20000L); System.out.println(set2); System.out.println(jedis.get(key2)); System.out.println(jedis.ttl(key2)); } @Test public void expire() { } /** * redis监控信息 */ @Test public void info() { String info = jedis.info(); Stream.of(info.split("\r\n")).forEach(row -> { String[] split = row.split(":"); if (split.length == 2) { System.out.printf("key:%s ==== value:%s \r\n",split[0],split[1]); } } ); } /** * jedispool */ @Test public void jedisPool() throws InterruptedException { StopWatch watch = new StopWatch(); watch.start(); for (int i = 0; i < 100000; i++) { jedis.set(i + "", i + ""); jedis.close(); } watch.stop(); System.out.println("5-"+watch.getTime()); } @Test public void jedisPool2() { // 替换成你的reids地址和端口 String redisIp = "192.168.0.5"; int reidsPort = 6379; JedisPool jedisPool = new JedisPool(new JedisPoolConfig(), redisIp, reidsPort); Jedis resource = jedisPool.getResource(); StopWatch watch = new StopWatch(); watch.start(); for (int i = 0; i < 100000; i++) { resource.set(i + "", i + ""); } watch.stop(); System.out.println("5-"+watch.getTime()); }} 单机Jedis连接集群节点会报错 12345678redis.clients.jedis.exceptions.JedisMovedDataException: MOVED 12706 192.168.0.8:6379 at redis.clients.jedis.Protocol.processError(Protocol.java:115) at redis.clients.jedis.Protocol.process(Protocol.java:161) at redis.clients.jedis.Protocol.read(Protocol.java:215) at redis.clients.jedis.Connection.readProtocolWithCheckingBroken(Connection.java:340) at redis.clients.jedis.Connection.getStatusCodeReply(Connection.java:239) at redis.clients.jedis.Jedis.set(Jedis.java:121) at reids.ClusterDemo.set(ClusterDemo.java:68) 集群Jedis1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677/** * anthony@base:/var/log/redis$ redis-cli --cluster check 192.168.0.6:6379 * 192.168.0.6:6379 (99af86e5...) -> 0 keys | 5461 slots | 1 slaves. * 192.168.0.8:6379 (982f74c7...) -> 3 keys | 5461 slots | 1 slaves. * 192.168.0.7:6379 (1069a632...) -> 0 keys | 5462 slots | 1 slaves. * [OK] 3 keys in 3 masters. * 0.00 keys per slot on average. * >>> Performing Cluster Check (using node 192.168.0.6:6379) * M: 99af86e59c7f5a4ddf212b29e9e1b12fa5e7a866 192.168.0.6:6379 * slots:[0-5460] (5461 slots) master * 1 additional replica(s) * S: 102517bddae6af2434519e8f348cf062b0152fa7 192.168.0.9:6379 * slots: (0 slots) slave * replicates 982f74c79ed5c5811b7011507c56f81374c70a0b * S: 392c5e4dcd5608c74a76902668ed58fb6ab50aaf 192.168.0.11:6379 * slots: (0 slots) slave * replicates 1069a6320a0489f63e807c970c27f86f13f417cf * S: 4d12dd0de1af9d90bfac7dd141c36c348071d59f 192.168.0.10:6379 * slots: (0 slots) slave * replicates 99af86e59c7f5a4ddf212b29e9e1b12fa5e7a866 * M: 982f74c79ed5c5811b7011507c56f81374c70a0b 192.168.0.8:6379 * slots:[10923-16383] (5461 slots) master * 1 additional replica(s) * M: 1069a6320a0489f63e807c970c27f86f13f417cf 192.168.0.7:6379 * slots:[5461-10922] (5462 slots) master * 1 additional replica(s) * [OK] All nodes agree about slots configuration. * >>> Check for open slots... * >>> Check slots coverage... * [OK] All 16384 slots covered. */public class ClusterDemo { private JedisCluster jedis; @Before public void before() { Set<HostAndPort> nodes = new HashSet<>(); nodes.add(new HostAndPort("192.168.0.6", 6379)); nodes.add(new HostAndPort("192.168.0.7", 6379)); nodes.add(new HostAndPort("192.168.0.8", 6379)); nodes.add(new HostAndPort("192.168.0.9", 6379)); nodes.add(new HostAndPort("192.168.0.10", 6379)); nodes.add(new HostAndPort("192.168.0.11", 6379)); GenericObjectPoolConfig poolConfig = new GenericObjectPoolConfig(); jedis = new JedisCluster(nodes,poolConfig); } @After public void after() throws IOException { //使用完关闭连接 jedis.close(); } /** * 设值 */ @Test public void set() { //访问Redis服务 jedis.set("k1", "v1"); System.out.println(jedis.get("k1")); } /** * redis集群的节点信息 * map.key = IP:PORT */ @Test public void info() { Map<String, JedisPool> clusterNodes = jedis.getClusterNodes(); clusterNodes.keySet().forEach(key->{ System.out.printf("%s==%s\r\n",key,clusterNodes.get(key).getResource().info()); }); }} RedisTemplate 123456789101112# 插入myVO.setCode(typeCode);redisTemplate.opsForList().rightPush(key, JSONUtil.toJsonStr(myVO));# 取出List<String> range = redisTemplate.opsForList().range(key, 0, 100);# 删除取出的# trim 是保留的意思if(range.size() >0){ redisTemplate.opsForList().trim(key, range.size() + 1, -1);}
Linux软件
JumpSserver设置SFTP根目录3.0版本设置文件管理 克隆一份Linux平台 修改或者创建资产的时候,选择该克隆的平台,在资产这里不能设置SFTP路径 安装vmware tool123运行 ./vmware-install.pl一直按yes,按完一段之后就会有暂时的停顿运行命令 reboot 安装Wget最开始的Centos7 感觉什么都没有wget也没有 1234567#远程下载的工具yum -y install wget#bash: make: command not found 是因为缺少这些工具yum -y install gcc automake autoconf libtool makeyum -y install wget 安装LNMP环境12345678910111213141516171819202122232425262728293031323334353637383940414243# 更新源apt-get update && apt-get dist-upgrade -y# 安装nginxapt-get install nginx# 安装php-fpm和常用php扩展apt-get install php-fpm php-gd php-mbstring php-curl php-xml php-mcrypt php-mysql php-zip php-json php-redis php-memcached# 安装mysqlapt-get install mysql-server# 建立测试站点# 1.我们在/var/www下面新建一个test目录,作为站点目录。 运行以下命令:mkdir /var/www/test# 2.新建php入口文件echo '<?php echo 1;' > /var/www/test/index.php# 3.授权给fpm用户www-data,使fpm进程可以访问站点文件chown -R www-data:www-data /var/www/test && chmod -R 755 /var/www/test# 4.设置nginx站点配置,在/etc/nginx/conf.d新增一个test.conf文件,并写入以下内容# 这个配置表示站点监听80端口,网站根目录为/var/www/test,入口文件为index.php,通过php-fpm进程来执行php脚本。server { listen 80 default_server; listen [::]:80 default_server; root /var/www/test; index index.php index.html index.htm; server_name _; location / { try_files $uri $uri/ =404; } location ~ \.php$ { include snippets/fastcgi-php.conf; fastcgi_pass unix:/run/php/php7.0-fpm.sock; }}# 5.重新加载nginx -t && nginx -s reload# 6.访问试试看 Ubuntu卸载Apache123456789101112# 删除apache$ sudo apt-get --purge remove apache2$ sudo apt-get --purge remove apache2.2-common$ sudo apt-get autoremove# 找到没有删除掉的配置文件,一并删除$ sudo find /etc -name "*apache*" -exec rm -rf {} \;$ sudo rm -rf /var/www# 重装apache2$ sudo apt-get install apache2$ sudo /etc/init.d/apache2 restart Ubuntu安装软件指定版本号 123456# 安装软件的时候,机器上安装的依赖版本可能高于软件本身需要的The following packages have unmet dependencies: libpcre3-dev : Depends: libpcre3 (= 2:8.39-12build1) but 2:8.44-1+ubuntu18.04.1+deb.sury.org+1 is to be installed Depends: libpcrecpp0v5 (= 2:8.39-12build1) but 2:8.44-1+ubuntu18.04.1+deb.sury.org+1 is to be installedsudo apt install libpcrecpp0v5=2:8.39-12build1 libpcre3=2:8.39-12build1 Gitlab安装https://ken.io/note/centos7-gitlab-install-tutorial#H3-11 Zsh安装1234567891011121314$ sudo apt-get install zsh# 验证是否安装成功$ zsh --version# 将 Zsh 设为默认 Shell$ sudo chsh -s $(which zsh)# 安装oh my zshhttps://ohmyz.sh/# 注销当前用户重新登录# 若输出为 /bin/zsh 或者 /usr/bin/zsh 则表示当前默认 Shell 是 Zsh$ echo $SHELL 安装confluenceatlassian的相关软件 confluence下载地址 安装: 1234567docker run -v \ /root/confluence/:/var/atlassian/application-data/confluence/ \ --name="confluence" \ -d \ -p 8090:8090 \ -p 8091:8091 \ atlassian/confluence-server 破解: 1./opt/atlassian/confluence/confluence/WEB-INF/lib/atlassian-extras-decoder-v2-3.4.1.jar备份2.atlassian-extras-decoder-v2-3.4.1.jar重命名为atlassian-extras-2.4.jar并下载到本地 (和破解工具放在同一目录)3.下载破解工具confluence_keygen.jar,在上面的超链接下载 4.将已被破解的jar包atlassian-extras-2.4.jar重名为atlassian-extras-decoder-v2-3.4.1.jar 并放置在原路径/opt/atlassian/confluence/confluence/WEB-INF/lib 5.下载mysql-connector-java-5.1.32-bin.jar 上传/opt/atlassian/confluence/confluence/WEB-INF/lib 6.开始安装到需要授权码 相关报错: 12启动发生报错 "Spring Application context has not been set"rm -f /var/atlassian/application-data/confluence/confluence.cfg.xml Nmap找出活跃的主机123nmap -sP 192.168.41.136nping --echo-client "public" echo.nmap.org 查看打开的端口nmap 192.168.41.136 网络软件清理网络DNS缓存 检查nameserver有没有生效 DNS生效的国家 IP查询 发送文件到Kindle VIMvim的配置文件设置123456789# 先复制一份vim配置模板到个人目录下cp /usr/share/vim/vimrc ~/.vimrc# 编辑vi ~/.vimrc# 添加syntax on # 开启高亮set nu! # 默认开启行号 行号1set nu 过滤注释1cat redis.conf |grep -v '#' |grep -v '^$' >redis-6237.conf 多行注释123451 Ctrl+v进入v模式2 上下方向键选中要注释的行3 shift+i(即大写的I)行首插入4 输入注释符//5 按esc返回 反注释1231 Ctrl+v进入v模式2 上下方向键选中要注释的行,左右键选择要删除的字符//3 按d删除 格式化1234# gg 跳转到第一行# shift+v 转到可视模式# shift+g 全选# 按下=号 删除#开头的行1:g/^#/d SSH安装ssh12345678# 判断Ubuntu是否安装了ssh服务,检查有没有可以看到“sshd”ps -e | grep ssh# 安装ssh服务,输入命令sudo apt-get install openssh-server# 启动服务sudo /etc/init.d/ssh start SSH配置文件设置1234567891011121314vim /etc/ssh/sshd_config# 这是设置允许root登陆PermitRootLogin prohibit-password ===> PermitRootLogin yes# 这是设置允许使用账号密码登录# 不能只注释,注释会不起作用,还是可以用密码登录PasswordAuthentication no ===> PasswordAuthentication yes# 允许用证书登录方式PubkeyAuthentication yes# 重启ssh服务sudo service ssh reload SSH连接,key失效1234567891011121314λ ssh [email protected]@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ WARNING: REMOTE HOST IDENTIFICATION HAS CHANGED! @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@IT IS POSSIBLE THAT SOMEONE IS DOING SOMETHING NASTY!Someone could be eavesdropping on you right now (man-in-the-middle attack)!It is also possible that a host key has just been changed.The fingerprint for the ECDSA key sent by the remote host isSHA256:RIqSp3Pd94k4PPQcDtKAzK0sQbf4VWffKGwy/nvO8Kw.Please contact your system administrator.Add correct host key in C:\\Users\\Owner/.ssh/known_hosts to get rid of this message.Offending ECDSA key in C:\\Users\\Owner/.ssh/known_hosts:12ECDSA host key for 10.19.44.12 has changed and you have requested strict checking.Host key verification failed. 解决方法:找到.ssh文件下的known_hosts,删除相关ip的行 生成证书1234567891011121314151617181920# 客户端生成秘钥ssh-keygen# ssh目录的文件# id_rsa,私钥文件。不应该分享给任何人。私钥文件通常以 PEM 格式存储。# id_rsa.pub,公钥文件。公钥可以公开分享,用于配置在你需要访问的服务器上。服务器使用公钥来加密信息,私钥可以解密。# known_hosts, 文件存储了你之前连接过的所有服务器的主机密钥。这些密钥用于验证服务器的身份,以防止中间人攻击。每次你连接一个新的服务器,服务器的公钥会被添加到这个文件中。# known_hosts.old,是 `known_hosts` 文件的备份。每当 `known_hosts` 文件更新时,旧版本会被保存为 `known_hosts.old`。这可以作为一个安全机制,如果新的 `known_hosts` 文件出现问题,可以回滚到旧版本。➜ .ssh ll /Users/anthony/.ssh-rw-------@ 1 anthony staff 1.6K 6 8 21:50 id_rsa -rw-r--r--@ 1 anthony staff 425B 6 8 21:50 id_rsa.pub-rw-------@ 1 anthony staff 3.0K 6 8 21:24 known_hosts -rw-------@ 1 anthony staff 2.3K 6 8 21:23 known_hosts.old# 把公钥拷贝到服务器,这次是要输入密码ssh-copy-id -i ~/.ssh/id_rsa.pub user@remote_host# 使用证书登录服务器# 实验的时候有个问题,如果不指定证书登录的命令,还是会提示输入密码ssh -i ~/.ssh/id_rsa_server2 ubuntu@your_server_ip2 root账号登录并且配置证书12345678910111213# 已经使用别的用户(账户名是:anthony)登录了服务器之后,再执行sudo passwd root# 第一次输入anthony用户的密码# 第二次输入新密码# 第三次再次输入新密码# 修改配置文件,让root用户可以登录,看上面的配置# 客户端上传rsa公钥,留意这里是root的账户的路径cat ~/.ssh/id_rsa.pub | ssh [email protected] 'cat >> /root/.ssh/authorized_keys'# 然后就可以使用root账户和证书登录了# 证书本身是不区分用户名,只认是不是一对公钥还是私钥 root登录之后没有高亮12cp /home/kali/.bashrc /rootsource .bashrc oh-my-zsh安装oh-my-zsh123456789# 配置文件位置vim ~/.zshrc# 配置主题# jonathan 是主题的名字ZSH_THEME="jonathan"# 配置插件,空格分开 插件的名字plugins=(git zsh-syntax-highlighting zsh-autosuggestions) oh my zsh插件zsh-syntax-highlighting高亮语法,如图,输入正确语法会显示绿色,错误的会显示红色,使得我们无需运行该命令即可知道此命令语法是否正确 1git clone https://github.com/zsh-users/zsh-syntax-highlighting.git ${ZSH_CUSTOM:-~/.oh-my-zsh/custom}/plugins/zsh-syntax-highlighting plugins=(其他的插件 zsh-syntax-highlighting) zsh-autosuggestions自动补全 只需输入部分命令即可根据之前输入过的命令提示,按右键→即可补全 1git clone https://github.com/zsh-users/zsh-autosuggestions ${ZSH_CUSTOM:-~/.oh-my-zsh/custom}/plugins/zsh-autosuggestions plugins=(其他的插件 zsh-syntax-highlighting) Shell设置别名经常要用命令行,进入到某个目录 编辑你的 Shell 配置文件,例如 Bash 的配置文件是 ~/.bashrc 或 ~/.bash_profile,Zsh 的配置文件是 ~/.zshrc。 添加 alias work='cd /path/to/your/work/directory' 运行 source ~/.bashrc 或 source ~/.zshrc 来使配置生效。 终端中输入 work 命令,就会进入你设置的工作目录 设置环境变量12345# 设置环境变量export http_proxy=http://127.0.0.1:3128# 删除环境变量unset http_proxy curl换行1curl http://www.baidu.com -w '\n' curl请求不校验证书12345678# 报错信息# curl failed to verify the legitimacy of the server and therefore could not# establish a secure connection to it. To learn more about this situation and# how to fix it, please visit the web page mentioned above.curl -k https://random.com# 或者curl -insecure https://random.com 脚本首行1#!/bin/bash 脚本变量1234# shell脚本给变量赋值的时候 = 两边不能加空格your_name="runoob"# 使用双引号拼接greeting="hello, "$your_name" !" 判断Linux shell脚本之 if条件判断_doiido的专栏-CSDN博客 判断数值 命令 解释 [ INT1 -eq INT2 ] INT1和INT2两数相等返回为真 ,= [ INT1 -ne INT2 ] INT1和INT2两数不等返回为真 ,<> [ INT1 -gt INT2 ] INT1大于INT2返回为真 ,> [ INT1 -ge INT2 ] INT1大于等于INT2返回为真,>= [ INT1 -lt INT2 ] INT1小于INT2返回为真 ,< [ INT1 -le INT2 ] INT1小于等于INT2返回为真,<= 字符串判断 命令 解释 [ -z STRING ] 如果STRING的长度为零则返回为真,即空是真 [ -n STRING ] 如果STRING的长度非零则返回为真,即非空是真 [ STRING1 ] 如果字符串不为空则返回为真,与-n类似 [ STRING1 == STRING2 ] 如果两个字符串相同则返回为真 [ STRING1 != STRING2 ] 如果字符串不相同则返回为真 [ STRING1 < STRING2 ] 如果 “STRING1”字典排序在“STRING2”前面则返回为真。 [ STRING1 > STRING2 ] 如果 “STRING1”字典排序在“STRING2”后面则返回为真。 12345678910111213#!/bin/shSTRING=if [ -z "$STRING" ]; then echo "STRING is empty"fiif [ -n "$STRING" ]; then echo "STRING is not empty"fi# 结果是:STRING is empty
JumpServer
笔记安装JumpServer官网一键安装 修改配置远程访问的端口12345678910# 进入到jp的配置文件目录cd /opt/jumpserver/config# 修改配置文件vim config.txt# 修改配置,对外提供服务端口, 如果与现有服务冲突请自行修改HTTP_PORT=8083 # 修改这个端口# 重新启动cd /opt/jumpserver-installer-v3.6.3./jmsctl.sh restart 修改系统管理员密码123456789# 进入Docker容器$ docker exec -it jms_core /bin/bash# 进入根目录,如果不是Docker,进入cd /opt/jumpserver/apps$ cd apps/$ python manage.py changepassword admin# 输入新的密码$ password Nginx反向代理Nginx反向代理 配置SSL12345678910111213# 先停止服务cd /opt/jumpserver-installer-v3.6.3./jmsctl.sh stop# 把证书移动到这个目录/opt/jumpserver/config/nginx/cert# 修改配置文件,域名和ssl证书的文职/opt/jumpserver/config/config.txt# 启动服务cd /opt/jumpserver-installer-v3.6.3./jmsctl.sh start 配置SSH超时时间默认的时间好像半个小时不操作了,就会自动断开,改成最大值
Linux
系统命令解压缩demo(文件夹) |——|1.txt(文件) 12345678# -r:递归地压缩目录,意味着 zip 命令会包含 demo 文件夹内的所有文件和子目录。# demo.zip:这是输出的压缩文件名。# demo:这是您要压缩的目录名。zip -r demo.zip demo# -o:强制覆盖现有的文件和文件夹,而不会询问用户。# demo.zip:这是您想要解压的 .zip 文件名。unzip -o demo.zip 查看前几行1ifconfig | head -n 100 安装中文123456789apt install language-pack-zh-hansvim /etc/default/localeLANG=zh_CN.UTF-8LANGUAGE=zh_CN:zh:en_US:en# 验证root@anthony:~# echo $LANGzh_CN.UTF-8 Update索引失效12345678910111213正在读取软件包列表... 完成E: 无法下载 http://mirror.rise.ph/ubuntu/dists/focal-updates/main/dep11/Components-amd64.yml.xz 文件尺寸不符(264288 != 264484)。您使用的镜像正在同步中? [IP: 43.226.6.79 80] Hashes of expected file: - Filesize:264484 [weak] - SHA256:ea09acc6bddd6a503a3812ba1d3a025e5515c5469dcad172c86e2bda6f203752 - SHA1:60863a9cc3181eb87007edcfc2de1ba6a0b81a5f [weak] - MD5Sum:80baa34a1b24d245341757fa51ebd29e [weak] Release file created at: Sat, 27 Feb 2021 08:09:39 +0000E: 部分索引文件下载失败。如果忽略它们,那将转而使用旧的索引文件。# 解决办法sudo rm -rf /var/lib/apt/lists/partial/sudo apt update 修改时区12345678910111213141516# 查询当前时间date -R# tzselect选择 4,9,1,1# ubuntu特有的,如果不是ubuntu 跳过这一步sudo cp /usr/share/zoneinfo/Asia/Shanghai /etc/localtime# java设置时区在/etc/profile或~/.bashrc文件中设置环境变量TZexport TZ='Asia/Shanghai'source /etc/profile# ubuntu20.04sudo timedatectl set-timezone Asia/Shanghai 历史命令12# 历史命令history -3 sudo免密vim /etc/sudoers 123456789101112131415# Host alias specification# User alias specification# Cmnd alias specification# User privilege specificationroot ALL=(ALL:ALL) ALL# 加在这里会被覆盖掉anthony ALL=(ALL) NOPASSWD:ALL# Members of the admin group may gain root privileges%admin ALL=(ALL) ALL# 最好是加在最后anthony ALL=(ALL) NOPASSWD:ALL# Allow members of group sudo to execute any command%sudo ALL=(ALL:ALL) ALLanthony ALL=(ALL) NOPASSWD:ALL grep管道1cat hive-default.xml | grep hive.cli.print 系统升级123yum -y update(centos)apt-get update(ubuntu 软件包的列表 )sudo apt-get dist-upgrade (进入列新你系统和系统里安装的软件) ll文件大小按MB显示12ls -lhll -lh 查看系统版本1cat /etc/os-release 立即关机和重启12345# 现在立即关机shutdown -h now # 现在立即重启shutdown -r now 当前时间1data / date -R 目录占用空间1234567891011121314151617181920# 查看当前目录下一级子文件和子目录占用的磁盘容量du -lh --max-depth=1 /# 查看当前目录总共占的容量。而不单独列出各子项占用的容量du -sh# 使用lsof命令排查,可以查看到状态为deleted的文件# 可考虑kill掉程序,就会自动释放lsof |grep deleted--------------------------------------------------------------------------------COMMAND PID TID USER FD TYPE DEVICE SIZE/OFF NODE NAMEdbus-daem 456 dbus txt REG 253,1 441144 141672 /usr/bin/dbus-daemon (deleted)...node 11595 11733 root txt REG 253,1 29851602 1461079 /home/elk/kibana-5.5.1-linux-x86_64/node/bin/node (deleted)node 11595 11733 root 1w REG 253,1 11374904415 1059873 /home/elk/kibana-5.5.1-linux-x86_64/nohup.out (deleted)node 11595 11733 root 2w REG 253,1 11374904415 1059873 /home/elk/kibana-5.5.1-linux-x86_64/nohup.out (deleted)async_17 24113 24210 rabbitmq 1w REG 253,1 8556707104 659069 /var/log/rabbitmq/startup_log (deleted)async_18 24113 24211 rabbitmq 1w REG 253,1 8556707104 659069 /var/log/rabbitmq/startup_log (deleted)async_19 24113 24212 rabbitmq 1w REG 253,1 8556707104 659069 /var/log/rabbitmq/startup_log (deleted)async_20 24113 24213 rabbitmq 1w REG 253,1 8556707104 659069 /var/log/rabbitmq/startup_log (deleted) 文件传输1234567891011# 获取远程服务器上的文件scp -P 2222 [email protected]:/root/lnmp0.4.tar.gz /home/lnmp0.4.tar.gz# 获取远程服务器上的目录scp -P 2222 -r [email protected]:/root/lnmp0.4/ /home/lnmp0.4/# 将本地文件上传到服务器上scp -P 2222 /home/lnmp0.4.tar.gz* *[email protected]:/root/lnmp0.4.tar.gz# 将本地目录上传到服务器上scp -P 2222 -r /home/lnmp0.4/* *[email protected]:/root/lnmp0.4/ 修改主机名字123456789# 第一种方法sudo vim /etc/sysconfig/network# 编辑HOSTNAME=weekend100sudo hostname weenkend110# 第二种方法,需要重启vim /etc/hostname Ubuntu右上角网络图标消失并且上不了网12345678910sudo nmcli networking offsudo nmcli networking onsudo service network-manager stopsudo rm /var/lib/NetworkManager/NetworkManager.statesudo service network-manager startsudo gedit /etc/NetworkManager/NetworkManager.conf# 把false改成truesudo service network-manager restart Cockpit1Activate the web console with: systemctl enable --now cockpit.socke Cockpit介绍自己是一个Web端的系统管理工具,只用鼠标点点就能管理系统,事实上也确实如此,我实际使用来说,启动Cockpit服务之后,只需要鼠标点点点就能完成系统很多基础操作,比如查看系统信息,启动/停止服务,新增或者更改账户,系统更新,Web终端及查看网络流量等功能。 12345systemctl start cockpit.socket # 运行Cockpit服务systemctl enable –now cockpit.socket # 启动该服务,随系统启动一同启动systemctl status cockpit.socket 然后在另一台电脑上用浏览器登录 https://IP:9090 top12345top - 21:08:58 up 6:15, 4 users, load average: 0.01, 0.03, 0.05Tasks: 262 total, 1 running, 261 sleeping, 0 stopped, 0 zombie%Cpu(s): 0.0 us, 1.4 sy, 0.0 ni, 98.6 id, 0.0 wa, 0.0 hi, 0.0 si, 0.0 stKiB Mem : 3861300 total, 1922280 free, 1029068 used, 909952 buff/cacheKiB Swap: 2097148 total, 2097148 free, 0 used. 2545024 avail Mem 第一行 程序名(top) 21:08:58(系统时间) up 6:15(运行时间) 4 users(登录用户数) load average(cpu 负载数,5分钟的平均负载,10分钟的平均负载,15分钟的平均负载) 第二行 task(总进程数) 1 running (运行数:1) 261 sleeping(睡眠树:1) stopped(停止数) zoimbie(僵尸数) 第三行 %cpu(cpu使用占比) us (用户) sy (系统) ni(优先级) id(空闲) wa(等待) hi(硬件) st(虚拟机) 第四行 kib Mem (物理内存 单位:K) free (空间,单位:k) userd(使用) cache(缓存硬盘内容大小) 第五行 交换区 第六行 进程ID 用户名 优先级(PR) 内存(VIRT RES SHR) 状态(S) cpu 内存 运行时间 命令 tree12345# 排除目录venv目录tree -I '*venv*'# 排除目录venv和venve2目录tree -I '*venv|venv2*' 配置环境变量配置Java环境变量123export JAVA_HOME=/home/anthony/soft/jdk1.8.0_19export PATH=$JAVA_HOME/bin:$PATHexport CLASSPATH=.:$JAVA_HOME/lib/dt.jar:$JAVA_HOME/lib/tools.jar 配置Maven环境变量12export M2_HOME=/usr/local/software/apache-maven-3.6.1export PATH=${M2_HOME}/bin:$PATH 安装和卸载OpenJDK12345678910111213# 安装apt-get install -y openjdk-17-jdk# 卸载openJDKsudo apt-get remove openjdk*# 查看jdk可安装的版本yum -y list java*# 选择一个安装# 要选择 要带有-devel的安装,因为这个安装的是jdk,而那个不带-devel的安装完了其实是jreyum install -y java-1.8.0-openjdk-devel.x86_64# 查找安装路径which java 配置Scala12export SCALA_HOME=/usr/local/scala-2.13.4export PATH=$SCALA_HOME/bin:$PATH 固定ip配置完了,最好是重启下vmware和虚拟机,和禁用开启 win10上的vmare8网卡 vmare配置 电脑配置虚拟机配置 Centos配置12345678910vim /etc/sysconfig/network-scripts/ifcfg-eth0BOOTPROTO="static" #dhcp改为staticONBOOT="yes" #开机启用本配置IPADDR=10.14.2.50 #静态IPGATEWAY=10.14.2.11 #默认网关NETMASK=255.255.255.0 #子网掩码# 重启网卡,或者重启电脑service network restart Ubuntu配置vim /etc/netplan 编辑完之后执行netplan apply 123456789101112131415161718192021222324# Ubuntu桌面版本network: version: 2 renderer: NetworkManager ethernets: ens33: dhcp4: no optional: true addresses: [192.168.0.5/24] gateway4: 192.168.0.1 nameservers: addresses: [192.168.0.1,8.8.8.8]# Ubuntu服务器版本network: ethernets: ens33: dhcp4: true optional: true addresses: [192.168.0.6/24] gateway4: 192.168.0.1 nameservers: addresses: [192.168.0.1,8.8.8.8] version: 2 关闭IPV6Ubuntu关闭IPV6 123sudo sysctl -w net.ipv6.conf.all.disable_ipv6=1sudo sysctl -w net.ipv6.conf.default.disable_ipv6=1sudo sysctl -w net.ipv6.conf.lo.disable_ipv6= 防火墙Centos防火墙12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849# 启动systemctl start firewalld# 查看状态systemctl status firewalld# 停止systemctl disable firewalld# 禁用systemctl stop firewalld# 开机启动防火墙systemctl enable firewalld.service# 查看防火墙是否开机启动systemctl is-enabled firewalld.service# 开机时禁用防火墙systemctl disable firewalld.service# 查看版本firewall-cmd --version# 显示状态firewall-cmd --state# 查看所有打开的端口firewall-cmd --zone=public --list-ports# 重新载入,更新防火墙规则firewall-cmd --reload# 查看区域信息firewall-cmd --get-active-zones# 拒绝所有包firewall-cmd --panic-on# 取消拒绝状态firewall-cmd --panic-off# 查看是否拒绝firewall-cmd --query-panic# 开启80端口,–permanent永久生效,没有此参数重启后失效firewall-cmd --zone=public --add-port=80/tcp --permanent# 查看80端口是否开放firewall-cmd --zone=public --query-port=80/tcp# 删除80端口配置firewall-cmd --zone=public --remove-port=80/tcp --permanent Ubuntu防火墙12345678910# 开放端口sudo ufw allow 80# 查看所有的服务端口netstat -ap# 查看已经连接的服务端口netstat -a# 如查看8888端口,则在终端中输入:lsof -i:8888# lsof -i:端口号查看某个端口是否被占用netstat -anp|grep 80 转发IP12345678910111213141516171819# 在文件中添加或修改以下行sudo vim /etc/sysctl.confnet.ipv4.ip_forward=1# 激活 IP 转发功能。sudo sysctl -p# 182.160.10.16 目标到目标服务器的ip# 将到达端口 80 的流量转发到另一台服务器sudo iptables -t nat -A PREROUTING -p tcp --dport 80 -j DNAT --to-destination 182.160.10.16:80# 设置 MASQUERADE 以便正确处理返回的流量sudo iptables -t nat -A POSTROUTING -j MASQUERADE# 检查和保存规则sudo iptables -t nat -L -n -v# 如果您希望这些规则在系统重启后依然有效,您需要安装 iptables-persistent 包:sudo apt-get install iptables-persistent 用户和权限用户和用户组普通用户和非普通用户12$ 是普通用户# 不是普通用户 修改用户名12345678# //-l 新的登陆名称,-d 用户新的主目录, -m将家目录内容移至新位置 (仅于 -d 一起使用)usermod -l newname -d /home/newname -m oldname# 修改组groupmod -n oldname newname# 重启reboot 用户组123456789101112# 新建用户组groupadd hr# 查看用户组[root@192 ~]# tail -1 /etc/grouphr:x:1001:# 修改用户组名,sudan新名字,dan就名字groupmod -n susan dan# 删除用户组groupdel hr 用户12345678910111213141516171819202122232425262728293031323334# 添加用户:useradd hadoop,创建用户未指定任何选项,系统会创建一个同名的用户组# 添加用户adduser hadoop# 删除用户,# -r 同时也删除这个用户的主目录userdel -r hadoop# 修改hadoop用户密码passwd hadoop# 修改自己的用户名密码passwd# 把用户加入到用户组:vi /etc/sudoers# 查看用户id user01# 或者whoami# 或者cat /etc/shadow# 切换用户su anthony# 查看用户组信息[root@192 ~]# tail -2 /etc/passwdanthony:x:1000:1000:anthony:/home/anthony:/bin/bash[root@192 ~]# tail /etc/groupanthony:x:1000:anthony# 修改用户基本组usermod username -g 组名 修改用户基本组和附加组的demo1234567891011121314151617181920212223242526272829303132333435363738394041424344454647# 创建hadoop用户[root@192 ~]# useradd hadoop# 查看hadoop用户,基本组是1002[root@192 ~]# tail -1 /etc/passwdhadoop:x:1001:1002::/home/hadoop:/bin/bash# 查看hadoop基本组信息[root@192 ~]# tail -1 /etc/grouphadoop:x:1002:# 创建两个用户组[root@192 ~]# groupadd hadoop_base[root@192 ~]# groupadd hadoop_more# 查看两个用户组[root@192 ~]# tail -3 /etc/grouphadoop:x:1002:hadoop_base:x:1003:hadoop_more:x:1004:# 修改用户基本组[root@192 ~]# usermod hadoop -g hadoop_base[root@192 ~]# tail -3 /etc/grouphadoop:x:1002:hadoop_base:x:1003:hadoop_more:x:1004:# 用户的基本组已经变成1003[root@192 ~]# tail -3 /etc/passwdhadoop:x:1001:1003::/home/hadoop:/bin/bash# 修改用户附加组[root@192 ~]# usermod hadoop -G hadoop_more# 用户的附加组已经变成1004[root@192 ~]# tail -1 /etc/grouphadoop_more:x:1004:hadoop# 最后查看# uid 用户名# gid 基本组id# 组 包括基本组和附加组[root@192 ~]# id hadoopuid=1001(hadoop) gid=1003(hadoop_base) 组=1003(hadoop_base),1004(hadoop_more)# 从组中删除成员[root@192 ~]# gpasswd -d hadoop hadoop_base正在将用户“hadoop”从“hadoop_base”组中删除gpasswd:用户“hadoop”不是“hadoop_base”的成员[root@192 ~]# gpasswd -d hadoop hadoop_more正在将用户“hadoop”从“hadoop_more”组中删除 修改用户权限1root修改用户权限: vim /etc/sudousers 组信息文件123456[root@192 ~]# cat /etc/grouproot:x:0:bin:x:1:daemon:x:2:sys:x:3:adm:x:4: 一共有4列 组名 组密码 组ID 备用的 用户文件信息1234[root@192 ~]# cat /etc/passwdroot:x:0:0:root:/root:/bin/bashbin:x:1:1:bin:/bin:/sbin/nologindaemon:x:2:2:daemon:/sbin:/sbin/nologin 用户名,登录名中不能有冒号 口令,把真正的加密后的用户口令字存放到/etc/shadow文件中 用户标识符,取值范围是0-65535。0是超级用户root的标识号,1-99由系统保留,作为管理账号,普通用户的标识号从100开始。在Linux系统中,这个界限是500, 组标识符,也是基本组 注释性描述,例如用户的真实姓名、电话、地址等,这个字段并没有什么实际的用途 主目录 登录shell,解释器 权限123[anthony@192 ~]$ touch /tmp/demo.txt[anthony@192 ~]$ ll /tmp/demo.txt-rw-rw-r--. 1 anthony anthony 0 1月 11 21:57 /tmp/demo.txt 第一位 代表文件,d代表文件夹 接下来的三位(rw-),是用户权限 接下来的三位(rw-),是组权限 接下来的三位(r–),是其他人权限 . 不知道是啥东西 第二位,连接 第三位,属主 第四位,属组 第五位,大小 第六位,7位,时间 第八位,名字 提升权限12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061[root@192 tmp]# touch fil1.txt[root@192 tmp]# ll-rw-r--r--. 1 root root 0 1月 12 21:56 fil1.txt# 给所有人和用户都设置读写执行的权限[root@192 tmp]# chmod a=rwx fil1.txt[root@192 tmp]# ll-rwxrwxrwx. 1 root root 0 1月 12 21:56 fil1.txt# 给所有人和用户设置读写权限[root@192 tmp]# chmod a=rx fil1.txt[root@192 tmp]# ll-r-xr-xr-x. 1 root root 0 1月 12 21:56 fil1.txt# 给属主设置读权限[root@192 tmp]# chmod u=r fil1.txt[root@192 tmp]# ll-r--r-xr-x. 1 root root 0 1月 12 21:56 fil1.txt# 给组设置读写执行的权限[root@192 tmp]# chmod g=rwx fil1.txt[root@192 tmp]# ll-r--rwxr-x. 1 root root 0 1月 12 21:56 fil1.txt[root@192 tmp]## 给其他人 没有读写执行的权限[root@192 tmp]# chmod o= fil1.txt[root@192 tmp]# ll-r--rwx---. 1 root root 0 1月 12 21:56 fil1.txt# 给属主单独设置一个写的权限[root@192 tmp]# chmod u+w fil1.txt[root@192 tmp]# ll总用量 0-rw-rwx---. 1 root root 0 1月 12 21:56 fil1.txt# 修改文件的属主和组权限[root@192 tmp]# chown hadoop.hr fil1.txt[root@192 tmp]# ll总用量 0-rw-rwx---. 1 hadoop hr 0 1月 12 21:56 fil1.txt# 查看acl权限[root@192 tmp]# getfacl acldemo.txt# file: acldemo.txt# owner: root# group: rootuser::rw-group::r--other::r--# 当对一个文件或者文件夹需要设置3个以上的权限用户,和权限用户组,上面的那种方法就不够用了# 需要引入 acl权限# setfacl -m 对象:对象名:权限 文件名# ll之后就会先是+号setfacl -m g:hr:rw /tmp/acldemo.txt[root@192 tmp]# ll-rw-rw-r--+ 1 root root 0 1月 12 22:19 acldemo.txt 磁盘管理磁盘的三个步骤先分区,再格式化,再挂载 MBR一共只能有4个分区,如果要需要很多个分区 新建主分区和逻辑分区123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101[root@192 ~]# lsblkNAME MAJ:MIN RM SIZE RO TYPE MOUNTPOINTsda 8:0 0 20G 0 disk├─sda1 8:1 0 1G 0 part /boot└─sda2 8:2 0 19G 0 part ├─centos-root 253:0 0 17G 0 lvm / └─centos-swap 253:1 0 2G 0 lvm [SWAP]sdb 8:16 0 5G 0 disksdc 8:32 0 5G 0 disksdd 8:48 0 5G 0 disksde 8:64 0 5G 0 disksdf 8:80 0 5G 0 disksdg 8:96 0 5G 0 disksdh 8:112 0 5G 0 disksdi 8:128 0 5G 0 disksr0 11:0 1 4.4G 0 rom# 启动分区工具[root@192 ~]# fdisk /dev/sdb欢迎使用 fdisk (util-linux 2.23.2)。更改将停留在内存中,直到您决定将更改写入磁盘。使用写入命令前请三思。Device does not contain a recognized partition table使用磁盘标识符 0xda85d71c 创建新的 DOS 磁盘标签。命令(输入 m 获取帮助):nPartition type: p primary (0 primary, 0 extended, 4 free) e extendedSelect (default p): p分区号 (1-4,默认 1):起始 扇区 (2048-10485759,默认为 2048):+2GLast 扇区, +扇区 or +size{K,M,G} (4194304-10485759,默认为 10485759):将使用默认值 10485759分区 1 已设置为 Linux 类型,大小设为 3 GiB命令(输入 m 获取帮助):wThe partition table has been altered!Calling ioctl() to re-read partition table.正在同步磁盘。# 刷新分区[root@192 ~]# partprobe /dev/sdb# 查看分区结果[root@192 ~]# fdisk -l /dev/sdb磁盘 /dev/sdb:5368 MB, 5368709120 字节,10485760 个扇区Units = 扇区 of 1 * 512 = 512 bytes扇区大小(逻辑/物理):512 字节 / 512 字节I/O 大小(最小/最佳):512 字节 / 512 字节磁盘标签类型:dos磁盘标识符:0xda85d71c 设备 Boot Start End Blocks Id System/dev/sdb1 4194304 10485759 3145728 83 Linux# 格式化(创建文件系统)# ext4 扩展文件系统第四代,是文件系统的类型[root@192 ~]# mkfs.ext4 /dev/sdb1mke2fs 1.42.9 (28-Dec-2013)文件系统标签=OS type: Linux块大小=4096 (log=2)分块大小=4096 (log=2)Stride=0 blocks, Stripe width=0 blocks196608 inodes, 786432 blocks39321 blocks (5.00%) reserved for the super user第一个数据块=0Maximum filesystem blocks=80530636824 block groups32768 blocks per group, 32768 fragments per group8192 inodes per groupSuperblock backups stored on blocks: 32768, 98304, 163840, 229376, 294912Allocating group tables: 完成正在写入inode表: 完成Creating journal (16384 blocks): 完成Writing superblocks and filesystem accounting information: 完成# 手动挂载[root@192 ~]# mkdir /mnt/disk1[root@192 mnt]# mount -t ext4 /dev/sdb1 /mnt/disk1/# 查看[root@192 mnt]# df -hT文件系统 类型 容量 已用 可用 已用% 挂载点devtmpfs devtmpfs 1.9G 0 1.9G 0% /devtmpfs tmpfs 1.9G 0 1.9G 0% /dev/shmtmpfs tmpfs 1.9G 13M 1.9G 1% /runtmpfs tmpfs 1.9G 0 1.9G 0% /sys/fs/cgroup/dev/mapper/centos-root xfs 17G 4.4G 13G 26% //dev/sda1 xfs 1014M 239M 776M 24% /boottmpfs tmpfs 378M 12K 378M 1% /run/user/42tmpfs tmpfs 378M 0 378M 0% /run/user/0# 刚挂载的/dev/sdb1 ext4 2.9G 9.0M 2.8G 1% /mnt/disk1 新建SWAP123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148[root@localhost ~]# lsblkNAME MAJ:MIN RM SIZE RO TYPE MOUNTPOINTsda 8:0 0 20G 0 disk├─sda1 8:1 0 1G 0 part /boot└─sda2 8:2 0 19G 0 part ├─centos-root 253:0 0 17G 0 lvm / └─centos-swap 253:1 0 2G 0 lvm [SWAP]sdb 8:16 0 5G 0 disk└─sdb1 8:17 0 3G 0 part /mnt/disk1sdc 8:32 0 5G 0 disksdd 8:48 0 5G 0 disksde 8:64 0 5G 0 disksdf 8:80 0 5G 0 disksdg 8:96 0 5G 0 disksdh 8:112 0 5G 0 disksdi 8:128 0 5G 0 disksr0 11:0 1 4.4G 0 rom /run/media/anthony/CentOS 7 x86_64[root@localhost ~]# fdisk /dev/sdb欢迎使用 fdisk (util-linux 2.23.2)。更改将停留在内存中,直到您决定将更改写入磁盘。使用写入命令前请三思。命令(输入 m 获取帮助):nPartition type: p primary (2 primary, 0 extended, 2 free) e extendedSelect (default p): p分区号 (3,4,默认 3):No free sectors available命令(输入 m 获取帮助):q[root@localhost ~]# fdisk /dev/sdc欢迎使用 fdisk (util-linux 2.23.2)。更改将停留在内存中,直到您决定将更改写入磁盘。使用写入命令前请三思。Device does not contain a recognized partition table使用磁盘标识符 0x661ba46a 创建新的 DOS 磁盘标签。命令(输入 m 获取帮助):nPartition type: p primary (0 primary, 0 extended, 4 free) e extendedSelect (default p): p分区号 (1-4,默认 1):起始 扇区 (2048-10485759,默认为 2048):将使用默认值 2048Last 扇区, +扇区 or +size{K,M,G} (2048-10485759,默认为 10485759):将使用默认值 10485759分区 1 已设置为 Linux 类型,大小设为 5 GiB命令(输入 m 获取帮助):p磁盘 /dev/sdc:5368 MB, 5368709120 字节,10485760 个扇区Units = 扇区 of 1 * 512 = 512 bytes扇区大小(逻辑/物理):512 字节 / 512 字节I/O 大小(最小/最佳):512 字节 / 512 字节磁盘标签类型:dos磁盘标识符:0x661ba46a 设备 Boot Start End Blocks Id System/dev/sdc1 2048 10485759 5241856 83 Linux命令(输入 m 获取帮助):wThe partition table has been altered!Calling ioctl() to re-read partition table.正在同步磁盘。[root@localhost ~]# partprobe /dev/sdc[root@localhost ~]# fdisk -l /dev/sdb磁盘 /dev/sdb:5368 MB, 5368709120 字节,10485760 个扇区Units = 扇区 of 1 * 512 = 512 bytes扇区大小(逻辑/物理):512 字节 / 512 字节I/O 大小(最小/最佳):512 字节 / 512 字节磁盘标签类型:dos磁盘标识符:0xda85d71c 设备 Boot Start End Blocks Id System/dev/sdb1 4194304 10485759 3145728 83 Linux/dev/sdb2 2048 4194303 2096128 83 LinuxPartition table entries are not in disk order[root@localhost ~]# mkswap /dev/sdb2/dev/sdb2: 没有那个文件或目录[root@localhost ~]# mkswap /dev/sdc2/dev/sdc2: 没有那个文件或目录[root@localhost ~]# fdisk -l /dev/sdc磁盘 /dev/sdc:5368 MB, 5368709120 字节,10485760 个扇区Units = 扇区 of 1 * 512 = 512 bytes扇区大小(逻辑/物理):512 字节 / 512 字节I/O 大小(最小/最佳):512 字节 / 512 字节磁盘标签类型:dos磁盘标识符:0x661ba46a 设备 Boot Start End Blocks Id System/dev/sdc1 2048 10485759 5241856 83 Linux[root@localhost ~]# mkswap /dev/sdc1anaconda-ks.cfg .bashrc .dbus/ .local/ 模板/ 下载/.bash_history .cache/ .esd_auth .tcshrc 视频/ 音乐/.bash_logout .config/ .ICEauthority .viminfo 图片/ 桌面/.bash_profile .cshrc initial-setup-ks.cfg 公共/ 文档/[root@localhost ~]# mkswap /dev/sdc1正在设置交换空间版本 1,大小 = 5241852 KiB无标签,UUID=074198c5-dff9-4834-9fbb-06cee839372d[root@localhost ~]# df -hT文件系统 类型 容量 已用 可用 已用% 挂载点devtmpfs devtmpfs 1.9G 0 1.9G 0% /devtmpfs tmpfs 1.9G 0 1.9G 0% /dev/shmtmpfs tmpfs 1.9G 13M 1.9G 1% /runtmpfs tmpfs 1.9G 0 1.9G 0% /sys/fs/cgroup/dev/mapper/centos-root xfs 17G 4.4G 13G 26% //dev/sda1 xfs 1014M 239M 776M 24% /boot/dev/sdb1 ext4 2.9G 9.0M 2.8G 1% /mnt/disk1tmpfs tmpfs 378M 32K 378M 1% /run/user/1000/dev/sr0 iso9660 4.4G 4.4G 0 100% /run/media/anthony/CentOS 7 x86_64tmpfs tmpfs 378M 0 378M 0% /run/user/0[root@localhost ~]# lsblkNAME MAJ:MIN RM SIZE RO TYPE MOUNTPOINTsda 8:0 0 20G 0 disk├─sda1 8:1 0 1G 0 part /boot└─sda2 8:2 0 19G 0 part ├─centos-root 253:0 0 17G 0 lvm / └─centos-swap 253:1 0 2G 0 lvm [SWAP]sdb 8:16 0 5G 0 disk└─sdb1 8:17 0 3G 0 part /mnt/disk1sdc 8:32 0 5G 0 disk└─sdc1 8:33 0 5G 0 partsdd 8:48 0 5G 0 disksde 8:64 0 5G 0 disksdf 8:80 0 5G 0 disksdg 8:96 0 5G 0 disksdh 8:112 0 5G 0 disksdi 8:128 0 5G 0 disksr0 11:0 1 4.4G 0 rom /run/media/anthony/CentOS 7 x86_64[root@localhost ~]# free -m total used free shared buff/cache availableMem: 3770 919 2208 26 642 2586Swap: 2047 0 2047[root@localhost ~]# swapon /dev/sdc1[root@localhost ~]# free -m total used free shared buff/cache availableMem: 3770 925 2201 26 642 2579Swap: 7166 0 7166 禁用swap12345678910一、不重启电脑,禁用启用swap,立刻生效# 禁用命令sudo swapoff -a# 启用命令sudo swapon -a# 查看交换分区的状态sudo free -m 创建LVM无限制的扩张容量 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120[root@192 ~]# lsblkNAME MAJ:MIN RM SIZE RO TYPE MOUNTPOINTsda 8:0 0 20G 0 disk├─sda1 8:1 0 1G 0 part /boot└─sda2 8:2 0 19G 0 part ├─centos-root 253:0 0 17G 0 lvm / └─centos-swap 253:1 0 2G 0 lvm [SWAP]sdb 8:16 0 5G 0 disk├─sdb1 8:17 0 3G 0 part└─sdb2 8:18 0 2G 0 partsdc 8:32 0 5G 0 disk└─sdc1 8:33 0 5G 0 part# 一会要操作的是这个盘sdd 8:48 0 5G 0 disksde 8:64 0 5G 0 disk# 一会要操作的是这个盘sdf 8:80 0 5G 0 disksdg 8:96 0 5G 0 disksdh 8:112 0 5G 0 disksdi 8:128 0 5G 0 disk# 物理磁盘转成物理卷[root@192 ~]# pvcreate /dev/sdd Physical volume "/dev/sdd" successfully created.# 创建数据卷组[root@192 ~]# vgcreate vg1 /dev/sdd Volume group "vg1" successfully created# 创建逻辑卷# lvcreate# -L 4g,要用磁盘的4G# -n lv1 卷的名字,自定义# vg1 卷组名,要把4G,加入到哪个卷组[root@192 ~]# lvcreate -L 4G -n lv1 vg1 Logical volume "lv1" created.# 格式化# 这里要用/dev/卷组名/逻辑卷的名字[root@192 ~]# mkfs.ext4 /dev/vg1/lv1mke2fs 1.42.9 (28-Dec-2013)文件系统标签=OS type: Linux块大小=4096 (log=2)分块大小=4096 (log=2)Stride=0 blocks, Stripe width=0 blocks262144 inodes, 1048576 blocks52428 blocks (5.00%) reserved for the super user第一个数据块=0Maximum filesystem blocks=107374182432 block groups32768 blocks per group, 32768 fragments per group8192 inodes per groupSuperblock backups stored on blocks: 32768, 98304, 163840, 229376, 294912, 819200, 884736Allocating group tables: 完成正在写入inode表: 完成Creating journal (32768 blocks): 完成Writing superblocks and filesystem accounting information: 完成# 挂载,这里要用/dev/卷组名/逻辑卷的名字[root@192 ~]# mkdir /mnt/lv1[root@192 ~]# mount /dev/vg1/lv1 /mnt/lv1[root@192 ~]# df -hT文件系统 类型 容量 已用 可用 已用% 挂载点devtmpfs devtmpfs 1.9G 0 1.9G 0% /devtmpfs tmpfs 1.9G 0 1.9G 0% /dev/shmtmpfs tmpfs 1.9G 13M 1.9G 1% /runtmpfs tmpfs 1.9G 0 1.9G 0% /sys/fs/cgroup/dev/mapper/centos-root xfs 17G 4.4G 13G 26% //dev/sda1 xfs 1014M 239M 776M 24% /boottmpfs tmpfs 378M 28K 378M 1% /run/user/1000/dev/sr0 iso9660 4.4G 4.4G 0 100% /run/media/anthony/CentOS 7 x86_64# 这里就有4个G的可用空间/dev/mapper/vg1-lv1 ext4 3.9G 16M 3.6G 1% /mnt/lv1# 把sdf物理磁盘转成物理卷[root@192 ~]# pvcreate /dev/sdf Physical volume "/dev/sdf" successfully created.[root@192 ~]# vgextend vg1 /dev/sdf Volume group "vg1" successfully extended# 扩容lv[root@192 ~]# lvextend -L +4G /dev/vg1/lv1 Size of logical volume vg1/lv1 changed from 4.00 GiB (1024 extents) to 8.00 GiB (2048 extents). Logical volume vg1/lv1 successfully resized.# 刷新[root@192 ~]# resize2fs /dev/vg1/lv1resize2fs 1.42.9 (28-Dec-2013)Filesystem at /dev/vg1/lv1 is mounted on /mnt/lv1; on-line resizing requiredold_desc_blocks = 1, new_desc_blocks = 1The filesystem on /dev/vg1/lv1 is now 2097152 blocks long.# 再次查看[root@192 ~]# df -hT文件系统 类型 容量 已用 可用 已用% 挂载点devtmpfs devtmpfs 1.9G 0 1.9G 0% /devtmpfs tmpfs 1.9G 0 1.9G 0% /dev/shmtmpfs tmpfs 1.9G 13M 1.9G 1% /runtmpfs tmpfs 1.9G 0 1.9G 0% /sys/fs/cgroup/dev/mapper/centos-root xfs 17G 4.4G 13G 26% //dev/sda1 xfs 1014M 239M 776M 24% /boottmpfs tmpfs 378M 28K 378M 1% /run/user/1000# 这里就有8个G的可用空间/dev/mapper/vg1-lv1 ext4 7.8G 18M 7.4G 1% /mnt/lv1[root@192 ~]# pvs PV VG Fmt Attr PSize PFree /dev/sda2 centos lvm2 a-- <19.00g 0 /dev/sdd vg1 lvm2 a-- <5.00g 0 /dev/sdf vg1 lvm2 a-- <5.00g 1.99g[root@192 ~]# vgs VG #PV #LV #SN Attr VSize VFree centos 1 2 0 wz--n- <19.00g 0 vg1 2 1 0 wz--n- 9.99g 1.99g yumyum的所有文件都在/etc/yum.repos.d下 yum挂载光盘源新建一个xxx.repo的文件 12345[root@192 yum.repos.d]# cat ./dvd.repo[dvd]name=this is descreptionbaseurl=file:///mnt/cdromgpgcheck=0 挂载光盘 1234567891011121314151617181920212223[root@192 yum.repos.d]# df -hT文件系统 类型 容量 已用 可用 已用% 挂载点devtmpfs devtmpfs 1.9G 0 1.9G 0% /devtmpfs tmpfs 1.9G 0 1.9G 0% /dev/shmtmpfs tmpfs 1.9G 13M 1.9G 1% /runtmpfs tmpfs 1.9G 0 1.9G 0% /sys/fs/cgroup/dev/mapper/centos-root xfs 17G 4.4G 13G 26% //dev/sda1 xfs 1014M 239M 776M 24% /boottmpfs tmpfs 378M 32K 378M 1% /run/user/1000/dev/mapper/vg1-lv1 ext4 7.8G 18M 7.4G 1% /mnt/lv1/dev/sr0 iso9660 4.4G 4.4G 0 100%# 挂载mount /dev/sr0 /mnt/cdrom# 查看光盘你的包ls /mnt/cdrom/Packages/# 安装软件yum -y instal httpd# 安装rpm包rpm -ivh xxx.rpm
SpringBoot
定时器定时器的数值放到配置文件,如果使用@Scheduled(fixedDelay = 类中的变量)这种方式试过不行 123@Scheduled(fixedDelayString = "${fixedDelayString}")public void open() {} 1fixedDelayString=30000 RocketMQ下载源码或者二进制包 12345678# 源码安装# 解压文件unzip rocketmq-all-5.2.0-source-release.zipcd rocketmq-all-5.2.0-source-release/# 编译mvn -Prelease-all -DskipTests -Dspotbugs.skip=true clean install -U# 编译后的二进制的目录,配置文件在这里cd ./distribution 修改配置文件 1234567# 修改配置文件# 修改内存配置,虚拟机的内存太小了# vim ./bin/runserver.shJAVA_OPT="${JAVA_OPT} -server -Xms512m -Xmx521m -Xmn256g -XX:MetaspaceSize=128m -XX:MaxMetaspaceSize=320m"# vim ./bin/runbroker.shJAVA_OPT="${JAVA_OPT} -server -Xms512m -Xmx512m" 启动 123456# 第一步.启动Name Serversh bin/mqnamesrvThe Name Server boot success...# 第二步.启动Brokersh bin/mqbroker -n localhost:9876 The broker[%s, 172.30.30.233:10911] boot success... 测试发送消息 12345export NAMESRV_ADDR=localhost:9876# 发送消息sh bin/tools.sh org.apache.rocketmq.example.quickstart.Producer# 接收消息sh bin/tools.sh org.apache.rocketmq.example.quickstart.Consumer 关闭服务12sh bin/mqshutdown brokersh bin/mqshutdown namesrv 监控1234567# 记得checkout release-rocketmq-console-1.0.0分支git clone https://github.com/apache/rocketmq-externals/tree/release-rocketmq-console-1.0.0# 修改配置文件里的rocketMQ远程地址NameSvrAddrList = 192.168.254.124:9876# 或者rocketmq.config.namesrvAddr=192.168.254.124:9876 代码单个生产者消费者12345678910111213141516171819202122232425262728293031public class Provider { public static void main(String[] args) throws Exception { DefaultMQProducer producer = new DefaultMQProducer("group1"); producer.setNamesrvAddr("192.168.254.190:9876"); producer.start(); String msg = "什么鬼,咋莫名其妙就收到了"; Message message = new Message("topic1", "tag1", msg.getBytes()); SendResult sendResult = producer.send(message); System.out.printf("%s%n", sendResult); producer.shutdown(); }}public class Consumer { public static void main(String[] args) throws MQClientException { DefaultMQPushConsumer consumer = new DefaultMQPushConsumer("group1"); consumer.setNamesrvAddr("192.168.254.190:9876"); consumer.subscribe("topic1", "*"); consumer.registerMessageListener((MessageListenerConcurrently) (list, consumeConcurrentlyContext) -> { // 业务逻辑 list.forEach(one->{ byte[] body = one.getBody(); System.out.println(new String(body)); }); return ConsumeConcurrentlyStatus.CONSUME_SUCCESS; }); consumer.start(); System.out.println("消费者启起来了"); }} 单个生产者多个消费者相同的Topic,相同的Group-负载均衡默认就是负载均衡,多个消费者,每个人收一条信息,不重复 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253public class Provider { public static void main(String[] args) throws Exception { DefaultMQProducer producer = new DefaultMQProducer("group2"); producer.setNamesrvAddr("192.168.254.190:9876"); producer.start(); for (int i = 0; i < 10; i++) { String msg = "默认接受者是负载均衡"+i; Message message = new Message("topic2", "tag1", msg.getBytes()); SendResult sendResult = producer.send(message); System.out.printf("%s%n", sendResult); } producer.shutdown(); }}public class Consumer { public static void main(String[] args) throws MQClientException { // 谁来收 DefaultMQPushConsumer consumer = new DefaultMQPushConsumer("group2"); // 从哪里收 consumer.setNamesrvAddr("192.168.254.190:9876"); // 监听队列 consumer.subscribe("topic2", "*"); // 监听器 consumer.registerMessageListener((MessageListenerConcurrently) (list, consumeConcurrentlyContext) -> { // 业务逻辑 list.forEach(one->{ byte[] body = one.getBody(); System.out.println(new String(body)); }); return ConsumeConcurrentlyStatus.CONSUME_SUCCESS; }); consumer.start(); System.out.println("消费者启起来了"); }}// 第一个消费者默认接受者是负载均衡0默认接受者是负载均衡1默认接受者是负载均衡4默认接受者是负载均衡5默认接受者是负载均衡8默认接受者是负载均衡9// 第二个消费者默认接受者是负载均衡2默认接受者是负载均衡3默认接受者是负载均衡6默认接受者是负载均衡7 相同的Topic,相同的Group-轮训只是添加了消费者的一段代码:consumer.setMessageModel(MessageModel.BROADCASTING); 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051public class Provider { public static void main(String[] args) throws Exception { // 谁来发 DefaultMQProducer producer = new DefaultMQProducer("group3"); // 发给谁 producer.setNamesrvAddr("192.168.254.190:9876"); producer.start(); for (int i = 0; i < 10; i++) { String msg = "默认接受者是负载均衡"+i; Message message = new Message("topic3", "tag1", msg.getBytes()); SendResult sendResult = producer.send(message); System.out.printf("%s%n", sendResult); } producer.shutdown(); }}public class Consumer { public static void main(String[] args) throws MQClientException { DefaultMQPushConsumer consumer = new DefaultMQPushConsumer("group3"); consumer.setNamesrvAddr("192.168.254.190:9876"); consumer.subscribe("topic3", "*"); consumer.setMessageModel(MessageModel.BROADCASTING); // 监听器 consumer.registerMessageListener((MessageListenerConcurrently) (list, consumeConcurrentlyContext) -> { // 业务逻辑 list.forEach(one->{ byte[] body = one.getBody(); System.out.println(new String(body)); }); return ConsumeConcurrentlyStatus.CONSUME_SUCCESS; }); consumer.start(); System.out.println("消费者启起来了"); }}// 两个消费者都是收到相同的消息默认接受者是负载均衡0默认接受者是负载均衡1默认接受者是负载均衡2默认接受者是负载均衡3默认接受者是负载均衡4默认接受者是负载均衡5默认接受者是负载均衡6默认接受者是负载均衡7默认接受者是负载均衡8默认接受者是负载均衡9 不同的Topic,不同的Group 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788// 消费者1public class Consumer { public static void main(String[] args) throws MQClientException { DefaultMQPushConsumer consumer = new DefaultMQPushConsumer("group4"); consumer.setNamesrvAddr("192.168.254.190:9876"); consumer.subscribe("topic4", "*"); // 监听器 consumer.registerMessageListener((MessageListenerConcurrently) (list, consumeConcurrentlyContext) -> { // 业务逻辑 list.forEach(one->{ byte[] body = one.getBody(); System.out.println(new String(body)); }); return ConsumeConcurrentlyStatus.CONSUME_SUCCESS; }); consumer.start(); System.out.println("消费者启起来了"); }}// 消费者2public class Consumer { public static void main(String[] args) throws MQClientException { DefaultMQPushConsumer consumer = new DefaultMQPushConsumer("group4"); consumer.setNamesrvAddr("192.168.254.190:9876"); consumer.subscribe("topic4", "*"); // 监听器 consumer.registerMessageListener((MessageListenerConcurrently) (list, consumeConcurrentlyContext) -> { // 业务逻辑 list.forEach(one->{ byte[] body = one.getBody(); System.out.println(new String(body)); }); return ConsumeConcurrentlyStatus.CONSUME_SUCCESS; }); consumer.start(); System.out.println("消费者启起来了"); }}// 消费者3public class Consumer { public static void main(String[] args) throws MQClientException { DefaultMQPushConsumer consumer = new DefaultMQPushConsumer("group4-1"); consumer.setNamesrvAddr("192.168.254.190:9876"); consumer.subscribe("topic4", "*"); // 监听器 consumer.registerMessageListener((MessageListenerConcurrently) (list, consumeConcurrentlyContext) -> { // 业务逻辑 list.forEach(one->{ byte[] body = one.getBody(); System.out.println(new String(body)); }); return ConsumeConcurrentlyStatus.CONSUME_SUCCESS; }); consumer.start(); System.out.println("消费者启起来了"); }}// 消费者1默认接受者是负载均衡2默认接受者是负载均衡3默认接受者是负载均衡6默认接受者是负载均衡7// 消费者2默认接受者是负载均衡0默认接受者是负载均衡1默认接受者是负载均衡4默认接受者是负载均衡5默认接受者是负载均衡8默认接受者是负载均衡9// 消费者3默认接受者是负载均衡0默认接受者是负载均衡1默认接受者是负载均衡2默认接受者是负载均衡3默认接受者是负载均衡4默认接受者是负载均衡5默认接受者是负载均衡6默认接受者是负载均衡7默认接受者是负载均衡8默认接受者是负载均衡9 Swagger123456<!-- 引入Swagger3依赖 --><dependency> <groupId>io.springfox</groupId> <artifactId>springfox-boot-starter</artifactId> <version>3.0.0</version></dependency> 1234567891011121314151617181920212223242526272829303132333435363738import io.swagger.annotations.ApiOperation;import org.springframework.context.annotation.Bean;import org.springframework.context.annotation.Configuration;import springfox.documentation.builders.ApiInfoBuilder;import springfox.documentation.builders.PathSelectors;import springfox.documentation.builders.RequestHandlerSelectors;import springfox.documentation.oas.annotations.EnableOpenApi;import springfox.documentation.service.ApiInfo;import springfox.documentation.service.Contact;import springfox.documentation.spi.DocumentationType;import springfox.documentation.spring.web.plugins.Docket;/** * Swagger配置类 */@EnableOpenApi@Configurationpublic class SwaggerConfig { @Bean public Docket docket(){ return new Docket(DocumentationType.OAS_30) .apiInfo(apiInfo()).enable(true) .select() //apis: 添加swagger接口提取范围 .apis(RequestHandlerSelectors.basePackage("com.example.study.controller")) .apis(RequestHandlerSelectors.withMethodAnnotation(ApiOperation.class)) .paths(PathSelectors.any()) .build(); } private ApiInfo apiInfo(){ return new ApiInfoBuilder() .title("XX项目接口文档") .description("XX项目描述") .contact(new Contact("作者", "作者URL", "作者Email")) .version("1.0") .build(); }} 123# 原因是在springboot 2.6.0中将SpringMVC 默认路径匹配策略从AntPathMatcher# 更改为PathPatternParser,导致出错,解决办法是切换回原先的AntPathMatcherspring.mvc.pathmatch.matching-strategy=ant_path_matcher knife4j12345<dependency> <groupId>com.github.xiaoymin</groupId> <artifactId>knife4j-spring-boot-starter</artifactId> <version>3.0.3</version></dependency> application.properties 12spring.mvc.pathmatch.matching-strategy=ant_path_matcherknife4j.enable: true 12345678910111213141516171819@RestController@RequestMapping("/test")@Api(tags = "测试2")// 排序@ApiSort(value = 1)public class TestController2 { @GetMapping("/3") @ApiOperation("3") public String method(){ return "hello wolrd"; } @GetMapping("/4") @ApiOperation("4") public String method2(){ return "hello wolrd"; }} Log4j2Log4j2日志所有级别级别从上到下 ,级别从低到高,报错(更重要)这种,级别最高,info,debug相对不怎么重要 all trace debug info warn error fail Log4j2.xml配置例子 抄网上的示例实际开发用的1234info级别输出到info.logwarn级别输出warn.logerror级别输出到error.logdebug级别输出到debug.log 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164<?xml version="1.0" encoding="UTF-8"?><Configuration> <!--<Configuration status="WARN" monitorInterval="30"> --> <properties> <property name="LOG_HOME">./service-logs</property> </properties> <Appenders> <!--*********************控制台日志***********************--> <Console name="consoleAppender" target="SYSTEM_OUT"> <!--设置日志格式及颜色--> <PatternLayout pattern="%style{%d{ISO8601}}{bright,green} %highlight{%-5level} [%style{%t}{bright,blue}] %style{%C{}}{bright,yellow}: %msg%n%style{%throwable}{red}" disableAnsi="false" noConsoleNoAnsi="false"/> </Console> <!--*********************文件日志***********************--> <!--all级别日志--> <RollingFile name="allFileAppender" fileName="${LOG_HOME}/all.log" filePattern="${LOG_HOME}/$${date:yyyy-MM}/all-%d{yyyy-MM-dd}-%i.log.gz"> <!--设置日志格式--> <PatternLayout> <pattern>%d %p %C{} [%t] %m%n</pattern> </PatternLayout> <Policies> <!-- 设置日志文件切分参数 --> <!--<OnStartupTriggeringPolicy/>--> <!--设置日志基础文件大小,超过该大小就触发日志文件滚动更新--> <SizeBasedTriggeringPolicy size="100 MB"/> <!--设置日志文件滚动更新的时间,依赖于文件命名filePattern的设置--> <TimeBasedTriggeringPolicy/> </Policies> <!--设置日志的文件个数上限,不设置默认为7个,超过大小后会被覆盖;依赖于filePattern中的%i--> <DefaultRolloverStrategy max="100"/> </RollingFile> <!--debug级别日志--> <RollingFile name="debugFileAppender" fileName="${LOG_HOME}/debug.log" filePattern="${LOG_HOME}/$${date:yyyy-MM}/debug-%d{yyyy-MM-dd}-%i.log.gz"> <Filters> <!--过滤掉info及更高级别日志--> <ThresholdFilter level="info" onMatch="DENY" onMismatch="NEUTRAL"/> </Filters> <!--设置日志格式--> <PatternLayout> <pattern>%d %p %C{} [%t] %m%n</pattern> </PatternLayout> <Policies> <!-- 设置日志文件切分参数 --> <!--<OnStartupTriggeringPolicy/>--> <!--设置日志基础文件大小,超过该大小就触发日志文件滚动更新--> <SizeBasedTriggeringPolicy size="100 MB"/> <!--设置日志文件滚动更新的时间,依赖于文件命名filePattern的设置--> <TimeBasedTriggeringPolicy/> </Policies> <!--设置日志的文件个数上限,不设置默认为7个,超过大小后会被覆盖;依赖于filePattern中的%i--> <DefaultRolloverStrategy max="100"/> </RollingFile> <!--info级别日志--> <RollingFile name="infoFileAppender" fileName="${LOG_HOME}/info.log" filePattern="${LOG_HOME}/$${date:yyyy-MM}/info-%d{yyyy-MM-dd}-%i.log.gz"> <Filters> <!--过滤掉warn及更高级别日志--> <ThresholdFilter level="warn" onMatch="DENY" onMismatch="NEUTRAL"/> </Filters> <!--设置日志格式--> <PatternLayout> <pattern>%d %p %C{} [%t] %m%n</pattern> </PatternLayout> <Policies> <!-- 设置日志文件切分参数 --> <!--<OnStartupTriggeringPolicy/>--> <!--设置日志基础文件大小,超过该大小就触发日志文件滚动更新--> <SizeBasedTriggeringPolicy size="100 MB"/> <!--设置日志文件滚动更新的时间,依赖于文件命名filePattern的设置--> <TimeBasedTriggeringPolicy interval="1" modulate="true" /> </Policies> <!--设置日志的文件个数上限,不设置默认为7个,超过大小后会被覆盖;依赖于filePattern中的%i--> <!--<DefaultRolloverStrategy max="100"/>--> </RollingFile> <!--warn级别日志--> <RollingFile name="warnFileAppender" fileName="${LOG_HOME}/warn.log" filePattern="${LOG_HOME}/$${date:yyyy-MM}/warn-%d{yyyy-MM-dd}-%i.log.gz"> <Filters> <!--过滤掉error及更高级别日志--> <ThresholdFilter level="error" onMatch="DENY" onMismatch="NEUTRAL"/> </Filters> <!--设置日志格式--> <PatternLayout> <pattern>%d %p %C{} [%t] %m%n</pattern> </PatternLayout> <Policies> <!-- 设置日志文件切分参数 --> <!--<OnStartupTriggeringPolicy/>--> <!--设置日志基础文件大小,超过该大小就触发日志文件滚动更新--> <SizeBasedTriggeringPolicy size="100 MB"/> <!--设置日志文件滚动更新的时间,依赖于文件命名filePattern的设置--> <TimeBasedTriggeringPolicy/> </Policies> <!--设置日志的文件个数上限,不设置默认为7个,超过大小后会被覆盖;依赖于filePattern中的%i--> <DefaultRolloverStrategy max="100"/> </RollingFile> <!--error及更高级别日志--> <RollingFile name="errorFileAppender" fileName="${LOG_HOME}/error.log" filePattern="${LOG_HOME}/$${date:yyyy-MM}/error-%d{yyyy-MM-dd}-%i.log.gz"> <!--设置日志格式--> <PatternLayout> <pattern>%d %p %C{} [%t] %m%n</pattern> </PatternLayout> <Policies> <!-- 设置日志文件切分参数 --> <!--<OnStartupTriggeringPolicy/>--> <!--设置日志基础文件大小,超过该大小就触发日志文件滚动更新--> <SizeBasedTriggeringPolicy size="100 MB"/> <!--设置日志文件滚动更新的时间,依赖于文件命名filePattern的设置--> <TimeBasedTriggeringPolicy/> </Policies> <!--设置日志的文件个数上限,不设置默认为7个,超过大小后会被覆盖;依赖于filePattern中的%i--> <DefaultRolloverStrategy max="100"/> </RollingFile> <!--json格式error级别日志--> <RollingFile name="errorJsonAppender" fileName="${LOG_HOME}/error-json.log" filePattern="${LOG_HOME}/error-json-%d{yyyy-MM-dd}-%i.log.gz"> <JSONLayout compact="true" eventEol="true" locationInfo="true"/> <Policies> <SizeBasedTriggeringPolicy size="100 MB"/> <TimeBasedTriggeringPolicy interval="1" modulate="true"/> </Policies> </RollingFile> </Appenders> <Loggers> <!-- 根日志设置 --> <Root level="debug"> <AppenderRef ref="allFileAppender" level="all"/> <AppenderRef ref="consoleAppender" level="debug"/> <AppenderRef ref="debugFileAppender" level="debug"/> <AppenderRef ref="infoFileAppender" level="info"/> <AppenderRef ref="warnFileAppender" level="warn"/> <AppenderRef ref="errorFileAppender" level="error"/> <AppenderRef ref="errorJsonAppender" level="error"/> </Root> <!--spring日志--> <Logger name="org.springframework" level="debug"/> <!--druid数据源日志--> <Logger name="druid.sql.Statement" level="warn"/> <!-- mybatis日志 --> <Logger name="com.mybatis" level="warn"/> <Logger name="org.hibernate" level="warn"/> <Logger name="com.zaxxer.hikari" level="info"/> <Logger name="org.quartz" level="info"/> <Logger name="com.andya.demo" level="debug"/> </Loggers></Configuration>123error级别 打印error.loginfo级别 打印info,error 日志到info.logdebug级别 打印info,error,debug日志到debug.log 基本结构是: Configuration properties 属性配置,可以在这里配置全局变量,可以在xml别的地方引入 Appenders 具体配置日志框架该如何收集日志的动作 Console 控制台输出 RollingFile 滚动日志文件 Loggers Root 理论上只有一个Root,level属性,是全局日志级别,如果AppenderRef没有配置level,就使用全局级别 AppenderRef 引用具体配置的动作,level 没有的话,就使用全局级别,就像CSS属性那样,标签里的属性优先级最高 Logger 单独配置一些类,level 没有的话,就使用全局级别,就像CSS属性那样,标签里的属性优先级最高 过滤器的具体使用方法 1234<Filters> <!--过滤掉info及更高级别日志--> <ThresholdFilter level="info" onMatch="DENY" onMismatch="NEUTRAL"/></Filters> level指定日志等级的阈值。 onMatch 定义当日志事件等级等于或高于 level 属性指定的等级时的行为。 ACCEPT:接受日志事件,让它通过过滤器。 DENY:拒绝日志事件,不让它通过过滤器。 NEUTRAL:对日志事件不做决定,继续应用其他的过滤规则。 onMismatch 定义当日志事件等级低于 level 属性指定的等级时的行为。 ACCEPT:接受日志事件,让它通过过滤器。 DENY:拒绝日志事件,不让它通过过滤器。 NEUTRAL:对日志事件不做决定,继续应用其他的过滤规则。 123456789if 日志的级别 >= level配置的级别 ACCEPT 接受日志 DENY 拒绝日志 NEUTRAL 对日志事件不做决定,继续应用其他的过滤规则if 日志的级别 < level配置的级别 ACCEPT 接受日志 DENY 拒绝日志 NEUTRAL 对日志事件不做决定,继续应用其他的过滤规则 还是没有搞清楚 onMismatch=”NEUTRAL” 的作用是怎样的 使用ChatGPT提供的demo,没有验证 info级别 记录到info.logerror级别 记录到error.loginfo级别和error级别 记录到dev.log 1234567891011121314151617181920212223242526272829<Configuration> <Appenders> <!-- Info 级别日志的 Appender --> <File name="InfoAppender" fileName="logs/info.log"> <ThresholdFilter level="INFO" onMatch="ACCEPT" onMismatch="DENY"/> <PatternLayout pattern="%d{yyyy-MM-dd HH:mm:ss} [%t] %-5level %logger{36} - %msg%n"/> </File> <!-- Error 级别日志的 Appender --> <File name="ErrorAppender" fileName="logs/error.log"> <ThresholdFilter level="ERROR" onMatch="ACCEPT" onMismatch="DENY"/> <PatternLayout pattern="%d{yyyy-MM-dd HH:mm:ss} [%t] %-5level %logger{36} - %msg%n"/> </File> <!-- 同时记录 Info 和 Error 级别日志的 Appender --> <File name="DevAppender" fileName="logs/dev.log"> <Filters> <ThresholdFilter level="INFO" onMatch="ACCEPT" onMismatch="DENY"/> <ThresholdFilter level="ERROR" onMatch="ACCEPT" onMismatch="NEUTRAL"/> </Filters> <PatternLayout pattern="%d{yyyy-MM-dd HH:mm:ss} [%t] %-5level %logger{36} - %msg%n"/> </File> </Appenders> <Loggers> <Root level="DEBUG"> <AppenderRef ref="InfoAppender"/> <AppenderRef ref="ErrorAppender"/> <AppenderRef ref="DevAppender"/> </Root> </Loggers></Configuration> 只是引入web框架只是引入web框架 1234<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId></dependency> 123456789101112@GetMapping("/test")public String test(){ // 格式化当前日期时间 String formattedDateTime = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")); log.debug("当前日期时间:{}", formattedDateTime); log.info("当前日期时间:{}", formattedDateTime); log.error("当前日期时间:{}", formattedDateTime); log.warn("当前日期时间:{}", formattedDateTime); log.trace("当前日期时间:{}", formattedDateTime); return formattedDateTime;} 12345# 控制台打印INFO 64710 --- [nio-8080-exec-1] c.e.s.controller.IndexController : 当前日期时间:2024-03-03 21:37:40ERROR 64710 --- [nio-8080-exec-1] c.e.s.controller.IndexController : 当前日期时间:2024-03-03 21:37:40WARN 64710 --- [nio-8080-exec-1] c.e.s.controller.IndexController : 当前日期时间:2024-03-03 21:37:40# 为什么控制台没有打印 trace和debug日志
NodeJs
语法基本使用初始化项目 1npm init -y 创建文件 12# 新建src文件夹# 在src里新建index.html和index.css 安装jQuery 123# -S 的意思是 jquery安装好了,记录在package.json的 dependencies里# -S 等于 --save的简写npm i jquery -S index.html 12345678910111213141516171819202122<!DOCTYPE html><html lang="en"> <head> <meta charset="UTF-8"> <title>隔行变色</title> <script src="./index.js"></script> </head> <body> <ul> <li>这是第 1 个li</li> <li>这是第 2 个li</li> <li>这是第 3 个li</li> <li>这是第 4 个li</li> <li>这是第 5 个li</li> <li>这是第 6 个li</li> <li>这是第 7 个li</li> <li>这是第 8 个li</li> <li>这是第 9 个li</li> </ul> </body></html> index.js 1234567// 使用ES6的导入语法,导入jQueryimport $ from 'jquery'$(function(){ $("li:odd").css("background-color","red") $("li:even").css("background-color","pink")}) 运行index.html,就会报错,这时需要安装webpack 安装webpack ⛔ 安装webpack遇到的版本问题 要安装最新版本或特定版本,请运行以下命令之一: 123npm install --save-dev webpacknpm install --save-dev webpack@<version> 如果你使用 webpack 4+ 版本,你还需要安装 CLI。 1npm install --save-dev webpack-cli 1234567# 这个时候运行就会出问题# 需要安装webpack# @是指定版本# -D是指把版本信息记录到 devDepencies# devDepencies开发阶段会用到# -D 等于 --save-dev的简写npm insatll [email protected] [email protected] -D 创建webpack的配置文件webpack.config.js 123456789# 项目跟目录创建webpack配置文件,webpack.config.js# 使用Node.js的导出语法,向外导出一个webpack的配置对象modle.exports={ // 代表webpack的运行的模式, // 可选的值有两个 development 和 production mode : "development"} 修改package.json 1234# 在package.json的scripts节点下,新增dev脚本"scripts":{ "dev":"webpack"} 运行 12# 就会多了个dist的文件夹npm run dev 修改index.html的引入js路径 12# 修改index.html文件夹的scr导入路劲scr="./index.js" 改成 src="./dist/main.js" entry和output123456789101112const path = require("path")module.exports = { // 指定要最先处理的文件 entry:path.join(__dirname,"./src/index1.js"), // 指定生成的文件放在哪里 output:{ // 生成文件的目录 path:path.join(__dirname,"./dist2"), // 生成的文件名 filename:"bundle.js" }} 修改代码实时生效插件1npm install webpack-dev-server --save-dev 修改package.json 123"scripts": { "dev": "webpack serve"} 1npm i --save-dev html-webpack-plugin 修改webpack.config.json 12345678910111213const htmlplugin = require("html-webpack-plugin")plugin = new htmlplugin({ // 指定要服务的页面 template:"./src/index.html", // 复制到哪里去 filename:"./index.html"})module.exports = { mode:"development", plugins:[plugin]} 配置devServer12345678910module.exports = { mode:"development", plugins:[plugin], devServer:{ // 自动打开浏览器 open:true, // 端口 port:10086 }} loaderwebpack默认只处理js格式的文件,比如要处理CSS,需要安装 12npm install --save-dev css-loadernpm install --save-dev style-loader 在src下新建css文件夹,在css文件夹下新建一个index.css 在index.js导入css文件 12// 导入样式,在webpack中,所有的文件都可以用ES6语法导入import "./css/index.css" 配置webpack.config.js 12345678910111213// 先找到css-loader处理,再给style-loader处理module.exports = { mode:"development", module:{ // 所有第三方文件模块的匹配规则 rules:[ // 文件后缀的匹配规则 { test:/\.css$/,use:["style-loader","css-loader"] } ] }} less-loader在上面的css文件夹下,创建个index.less文件 12345678910// index.lesshtml,body,ul{ padding: 0; margin: 0; li{ line-height: 30px; padding-left: 20px; font-size: 12px; }} 12npm install less -Dnpm install less less-loader --save-dev 配置webpack.config.js 1234567891011121314module.exports = { mode:"development", module:{ // 所有第三方文件模块的匹配规则 rules:[ // 文件后缀的匹配规则 // 处理css {test:/\.css$/,use:["style-loader","css-loader"]}, // 处理less {test: /\.less$/, use: ["style-loader", "css-loader", "less-loader",], }, ] }} 图片loader没学 babel-loader没学 build打包优化自动清理dist目录下的旧文件1npm install --save-dev clean-webpack-plugin 12345678const { CleanWebpackPlugin } = require('clean-webpack-plugin');module.exports = { plugins:[plugin,new CleanWebpackPlugin()], output:{ path:path.join(__dirname,"./dist") }} Source Map一个信息文件,存储这位置信息,存储压缩混淆后的代码,所对应的转换钱的位置 123456module.exports = { mode:"development", // 生产的时候要关闭,也可以 设置 nosources-source-map // nosources-source-map 显示行号,不显示源码 devtool:"eval-source-map"} @的原理标识src源代码目录,从外往里找, 就可以不用使用../从里往外找 告诉webpack,@的目录 1234567module.exports = { resolve:{ alias:{ "@":path.join(__dirname,"./src/") } }} Eslint12345678910{ // 是否使用分号结尾 "semi": true, // 是否使用单引号 "singleQuote": false, // 最后一句话逗号结尾 "trailingComma": "none"} 在Vue项目的根目录中创建vue.config.js文件 12345678910# 在脚手架生成的代码添加module.exports = defineConfig({ # 要添加的代码 lintOnSave:false})# 或者module.exports = { lintOnSave:false} 入门应用1234567891011121314151617var http = require('http');http.createServer(function (request, response) { // 发送 HTTP 头部 // HTTP 状态值: 200 : OK // 内容类型: text/plain response.writeHead(200, {'Content-Type': 'text/plain'}); // 发送响应数据 "Hello World" response.end('Hello World\n');}).listen(8888);// 终端打印如下信息console.log('Server running at http://127.0.0.1:8888/');// 运行node server.js Npm升级 1npm i -g npm to update Node.jsmac安装 1brew install nodejs npm更换版本 12# npm更换npm install [email protected] -g node.js更换版本 12345678910111213141516171819202122# node更换sudo npm install n -g# 安装指定版本号的nodejs,记得使用sudosudo n 14.16.0# 切换nodejs版本号n# 输入n之后,打印的数据ο node/14.16.0------------------------------------------# 使用nvm更换版本brew install nvm# 查看远程版本nvm ls-remote# 安装制定版本号nvm install v15.3.2# 已经安装的版本号nvm ls# 切换已经安装的版本号nvm use 4.2 依赖管理 123456789101112131415161718192021# 安装但不写入package.json;npm install xxx# 安装并写入package.json的"dependencies"中npm install xxx –S# 安装并写入package.json的"devDependencies"中npm install xxx –D# 全局安装npm install xxx -g# 安装指定版本npm install [email protected]# 更新包npm install -g npm-check-updates# 检查可更新的模块ncu# 来更新package.json的依赖包到最新版本ncu -u node.js升级 12345678910111213# 更新npmnpm install -g npm# 清空npm缓存npm cache clean -f# 安装n模块npm install -g n# 升级node.js到最新稳定版n stable# 有时候需要sudo权限,或者安装好了之后要开启一个新的shell才能用到新的版本 https://www.bilibili.com/video/BV1vE411871g?p=120 安装源 依赖升级 123npm install -g npm-upgrade # 先全局安装 npm-upgradenpm-upgrade # 当前项目下的包全都更新npm-upgrade <package> # 当前项目下的指定包更新 ES6 & JS字符串转数字 遍历2 排序 排序 join方法 遍历 1JavaScript for...in .. foreach 1row.typeID.split(",").forEach(id=>{this.gameTypes.push(parseInt(id))}) NodJs项目部署到Vercel1.创建node项目 1npm init 2.安装依赖,package.json 123456{ "dependencies": { "@vercel/node": "^2.15.3", "express": "^4.18.2" }} 3.创建vercel.json 123456789101112131415{ "version": 2, "builds": [ { "src": "app.js", "use": "@vercel/node" } ], "routes": [ { "src": "/(.*)", "dest": "app.js" } ]} 4.创建app.js 1234567891011const express = require('express')const app = express()const port = 3000app.get('/', (req, res) => { res.send('Hello World!')})app.listen(port, () => { console.log(`Example app listening on port ${port}`)}) 5.像平常部署别的项目一样,部署这个项目就可以,部署好之后,就能看到首页的Hello World!
SpringBoot配置Gmail
申请应用程序验证码 配置二步验证 要配置二步验证,才能看到应用程序的选项 最后就能获取到应用程序验证码 引入代码 1234<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-mail</artifactId></dependency> application.properties文件配置 12345678910111213spring.mail.host=smtp.gmail.com# 邮件服务器端口号spring.mail.port=587# 邮件发送方的电子邮件地址spring.mail.username=你的Gmail邮箱账号# 邮件发送方的密码或应用程序专用密码一定要开启了两步验证spring.mail.password=应用程序专用密码# 启用TLS加密spring.mail.properties.mail.smtp.starttls.enable=true# 验证邮件服务器的身份spring.mail.properties.mail.smtp.auth=true# 邮件传输协议spring.mail.properties.mail.transport.protocol=smtp 配置的代码 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108@Servicepublic class EmailUtil implements EmailService{ private final Logger logger = LoggerFactory.getLogger(this.getClass()); //Spring Boot 提供了一个发送邮件的简单抽象,使用的是下面这个接口,这里直接注入即可使用 @Autowired private JavaMailSender mailSender; // 配置文件中我的谷歌邮箱 @Value("${spring.mail.username}") private String from; /** * 简单文本邮件 * @param to 收件人 * @param subject 主题 * @param content 内容 */ @Override public void sendSimpleMail(String to, String subject, String content) { //创建SimpleMailMessage对象 SimpleMailMessage message = new SimpleMailMessage(); //邮件发送人 message.setFrom(from); //邮件接收人 message.setTo(to); //邮件主题 message.setSubject(subject); //邮件内容 message.setText(content); //发送邮件 mailSender.send(message); } /** * html邮件 * @param to 收件人,多个时参数形式 :"[email protected],[email protected],[email protected]" * @param subject 主题 * @param content 内容 */ @Override public void sendHtmlMail(String to, String subject, String content) { //获取MimeMessage对象 MimeMessage message = mailSender.createMimeMessage(); MimeMessageHelper messageHelper; try { messageHelper = new MimeMessageHelper(message, true); //邮件发送人 messageHelper.setFrom(from); //邮件接收人,设置多个收件人地址 InternetAddress[] internetAddressTo = InternetAddress.parse(to); messageHelper.setTo(internetAddressTo); //messageHelper.setTo(to); //邮件主题 message.setSubject(subject); //邮件内容,html格式 messageHelper.setText(content, true); //发送 mailSender.send(message); //日志信息 logger.info("邮件已经发送。"); } catch (Exception e) { logger.error("发送邮件时发生异常!", e); } } /** * 带附件的邮件 * @param to 收件人 * @param subject 主题 * @param content 内容 * @param filePath 附件 */ @Override public void sendAttachmentsMail(String to, String subject, String content, String filePath) { MimeMessage message = mailSender.createMimeMessage(); try { MimeMessageHelper helper = new MimeMessageHelper(message, true); helper.setFrom(from); helper.setTo(to); helper.setSubject(subject); helper.setText(content, true); FileSystemResource file = new FileSystemResource(new File(filePath)); String fileName = filePath.substring(filePath.lastIndexOf(File.separator)); helper.addAttachment(fileName, file); mailSender.send(message); //日志信息 logger.info("邮件已经发送。"); } catch (Exception e) { logger.error("发送邮件时发生异常!", e); } } /** * 验证邮箱格式 * @param email * @return */ public boolean isEmail(String email) { if (email == null || email.length() < 1 || email.length() > 256) { return false; } Pattern pattern = Pattern.compile("^\\w+([-+.]\\w+)*@\\w+([-.]\\w+)*\\.\\w+([-.]\\w+)*$"); return pattern.matcher(email).matches(); }} 1234567891011121314151617181920212223242526272829public interface EmailService { /** * 发送文本邮件 * * @param to 收件人 * @param subject 主题 * @param content 内容 */ void sendSimpleMail(String to, String subject, String content); /** * 发送HTML邮件 * * @param to 收件人 * @param subject 主题 * @param content 内容 */ public void sendHtmlMail(String to, String subject, String content); /** * 发送带附件的邮件 * * @param to 收件人 * @param subject 主题 * @param content 内容 * @param filePath 附件 */ public void sendAttachmentsMail(String to, String subject, String content, String filePath);} 123456789101112131415161718192021222324@Servicepublic class CodeUtil { @Autowired private StringRedisTemplate redisTemplate; public String generateVerificationCode() { // 生成6位随机数字验证码 return String.valueOf((int) ((Math.random() * 9 + 1) * 100000)); } // 验证验证码是否正确 public boolean verifyCode(String userId, String code,String key) { key = key + userId; String storedCode = redisTemplate.opsForValue().get(key); return code.equals(storedCode); } //删除redis中验证码 public void deleteCode(String userId,String key) { key = key + userId; redisTemplate.delete(key); }} 1234567//写在需要发送验证码的地方//存储时间 private static final int EXPIRATION_TIME_IN_MINUTES = 3; //键名 private static final String KEY_PREFIX =="xxx"; key=KEY_PREFIX+"123456"redisTemplate.opsForValue().set(key, 随机数字验证码, EXPIRATION_TIME_IN_MINUTES, TimeUnit.MINUTES);
Vue后台系统
项目由 Vue3 + Pina + JavaScript + Ant Design Vue 创建项目12345# 直接创建npm create vite@latest my-vue-app -- --template vue# 如果要创建ts或者自定义配置的项目就用这个npm create vite 引入Ant Design Vue1npm i --save [email protected] mian.js 删掉自带的style.css文件 12345678import {createApp} from 'vue'import Antd from 'ant-design-vue';import 'ant-design-vue/dist/reset.css';import App from './App.vue'const app = createApp(App);app.use(Antd)app.mount('#app'); 安装router1npm install vue-router@4 创建src/router/index.js文件 12345678910111213141516171819202122232425262728293031323334353637383940414243444546import { createWebHistory, createRouter } from 'vue-router'/** * Note: 路由配置项 * * hidden: true // 当设置 true 的时候该路由不会再侧边栏出现 如401,login等页面,或者如一些编辑页面/edit/1 * alwaysShow: true // 当你一个路由下面的 children 声明的路由大于1个时,自动会变成嵌套的模式--如组件页面 * // 只有一个时,会将那个子路由当做根路由显示在侧边栏--如引导页面 * // 若你想不管路由下面的 children 声明的个数都显示你的根路由 * // 你可以设置 alwaysShow: true,这样它就会忽略之前定义的规则,一直显示根路由 * redirect: noRedirect // 当设置 noRedirect 的时候该路由在面包屑导航中不可被点击 * name:'router-name' // 设定路由的名字,一定要填写不然使用<keep-alive>时会出现各种问题 * query: '{"id": 1, "name": "ry"}' // 访问路由的默认传递参数 * roles: ['admin', 'common'] // 访问路由的角色权限 * permissions: ['a:a:a', 'b:b:b'] // 访问路由的菜单权限 * meta : { noCache: true // 如果设置为true,则不会被 <keep-alive> 缓存(默认 false) title: 'title' // 设置该路由在侧边栏和面包屑中展示的名字 icon: 'svg-name' // 设置该路由的图标,对应路径src/assets/icons/svg breadcrumb: false // 如果设置为false,则不会在breadcrumb面包屑中显示 activeMenu: '/system/user' // 当路由设置了该属性,则会高亮相对应的侧边栏。 } */// 公共路由export const constantRoutes = [ { path: '/login', component: () => import('@/views/login'), hidden: true },]const router = createRouter({ history: createWebHistory(), routes: constantRoutes, scrollBehavior(to, from, savedPosition) { if (savedPosition) { return savedPosition } else { return { top: 0 } } },});export default router; 配置开发环境编辑vite.config.js 123456789101112131415161718192021import { defineConfig, loadEnv } from 'vite'import path from 'path'import vue from '@vitejs/plugin-vue'// https://vitejs.dev/config/export default defineConfig(({ mode, command }) => { return { plugins: [vue()], resolve: { // https://cn.vitejs.dev/config/#resolve-alias alias: { // 设置路径 '~': path.resolve(__dirname, './'), // 设置别名 '@': path.resolve(__dirname, './src') }, // https://cn.vitejs.dev/config/#resolve-extensions extensions: ['.mjs', '.js', '.ts', '.jsx', '.tsx', '.json', '.vue'] } }}) 项目根目录创建环境配置文件 .env.development .env.prod 12345678# 页面标题VITE_APP_TITLE = 若依管理系统# 开发环境配置VITE_APP_ENV = 'development'# 若依管理系统/开发环境VITE_APP_BASE_API = '/dev-api' 安装sass12345npm install -D sass# 直接使用就可以<style lang="scss" scoped></style> 全局样式创建src/style/index.scss文件 123456789101112131415161718@import './demo.scss';html { height: 100vh; box-sizing: border-box;}body { height: 100vh; margin: 0; //-moz-osx-font-smoothing: grayscale; //-webkit-font-smoothing: antialiased; //text-rendering: optimizeLegibility; //font-family: Helvetica Neue, Helvetica, PingFang SC, Hiragino Sans GB, Microsoft YaHei, Arial, sans-serif;}#app { height: 100vh;} 创建src/style/demo.scss文件 1// 这个文件是用来写css的,前面导入的这个文件,所以后面只需要写就行了 导入scss,编辑main.js 1import './style/index.scss' Ant Design Vue Icon的使用123456789101112131415<template> <a-form class="login-from"> <a-form-item size> <a-input placeholder="请输入用户名" size="large"> <template #prefix> <UserOutlined/> </template> </a-input> </a-form-item> </a-form></template><script setup> import {UserOutlined, LockOutlined} from '@ant-design/icons-vue'</script> 安装Axios1npm install axios 创建 src/utils/request.js 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556import axios from 'axios'axios.defaults.headers['Content-Type'] = 'application/json;charset=utf-8'// 创建axios实例const service = axios.create({ // axios中请求配置有baseURL选项,表示请求URL公共部分 baseURL: import.meta.env.VITE_APP_BASE_API, // 超时 timeout: 10000})// request拦截器service.interceptors.request.use(config => { return config}, error => { console.log(error) Promise.reject(error)})// 响应拦截器service.interceptors.response.use(res => { // 未设置状态码则默认成功状态 const code = res.data.code || 200; if (code === 401) { return Promise.reject('无效的会话,或者会话已过期,请重新登录。') } else if (code === 500) { ElMessage({ message: msg, type: 'error' }) return Promise.reject(new Error(msg)) } else if (code === 601) { ElMessage({ message: msg, type: 'warning' }) return Promise.reject(new Error(msg)) } else if (code !== 200) { ElNotification.error({ title: msg }) return Promise.reject('error') } else { return Promise.resolve(res.data) } }, error => { console.log('err' + error) let { message } = error; if (message == "Network Error") { message = "后端接口连接异常"; } else if (message.includes("timeout")) { message = "系统接口请求超时"; } else if (message.includes("Request failed with status code")) { message = "系统接口" + message.substr(message.length - 3) + "异常"; } ElMessage({ message: message, type: 'error', duration: 5 * 1000 }) return Promise.reject(error) })export default service main.js 1import router from './router'