文章系转载,方便整理和归纳
版权声明:本文为CSDN博主「斌哥谈编程」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。 原文链接:https://blog.csdn.net/houpeibin2012/article/details/105839651
1.pom文件引入jedis和spring-boot-starter-data-redis的依赖 2.其他依赖的引入(Swagger) 3.Jedis配置类 4.Jedis及spring-boot-starter-data-redis的配置信息 5.Swagger配置类 6.编写测试相关类 7.测试 7.1插入数据测试 7.2读取数据测试 7.3多线程读取数据测试 8. 结论
温馨提示: 本文配套代码:https://gitee.com/guduwuhen/springboot2-lesson/tree/master/redispro
1.pom文件引入jedis和spring-boot-starter-data-redis的依赖 <properties> <java.version>1.8</java.version> <jedis.version>2.9.0</jedis.version> <fastjson.version>1.2.68</fastjson.version> <guava.version>28.2-jre</guava.version> </properties> <dependency> <groupId>redis.clients</groupId> <artifactId>jedis</artifactId> <version>${jedis.version}</version> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId> </dependency> Springboot 使用的是2.2.4.RELEASE版本。
2.其他依赖的引入(Swagger) 如下是完整的pom文件代码:
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.2.4.RELEASE</version> <relativePath/> <!--lookup parent from repository --> </parent> <groupId>com.ieslab.powergrid</groupId> <artifactId>redispro</artifactId> <version>0.0.1-SNAPSHOT</version> <name>demosvr</name> <description>Demo project for Spring Boot</description> <properties> <java.
时间:
|
阅读:
262 字 ~2分钟
Spring自带的线程池ThreadPoolTaskExecutor 前言 上一篇分享了JDK自带的线程池ThreadPoolExecutor的配置和参数详解,然而我们实际开发中更多的是使用SpringBoot来开发,Spring默认也是自带了一个线程池方便我们开发,它就是ThreadPoolTaskExecutor,接下来我们就来聊聊Spring的线程池吧。
Spring默认线程池simpleAsyncTaskExecutor Spring异步线程池的接口类是TaskExecutor,本质还是java.util.concurrent.Executor,没有配置的情况下,默认使用的是simpleAsyncTaskExecutor。
@Async演示Spring默认的simpleAsyncTaskExecutor
@Component @EnableAsync public class ScheduleTask { SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); @Async @Scheduled(fixedRate = 2000) public void testScheduleTask() { try { Thread.sleep(6000); System.out.println("Spring1自带的线程池" + Thread.currentThread().getName() + "-" + sdf.format(new Date())); } catch (InterruptedException e) { e.printStackTrace(); } } @Async @Scheduled(cron = "*/2 * * * * ?") public void testAsyn() { try { Thread.sleep(1000); System.out.println("Spring2自带的线程池" + Thread.currentThread().getName() + "-" + sdf.format(new Date())); } catch (Exception ex) { ex.
时间:
|
阅读:
92 字 ~1分钟
https://www.cnblogs.com/zeng1994/p/06917ed3b98677fa7a1b0f74de16c3be.html
使用maven的profile功能,我们可以实现多环境配置文件的动态切换,可参考我的上一篇博客。但随着SpringBoot项目越来越火,越来越多人喜欢用SpringBoot的profile功能。但是用SpringBoot的profile功能时,一般我们默认激活的profile肯定是开发环境的profile。当我们打成jar包后,如果在生产环境下运行,就需要在运行这个jar包的命令后面加个命令行参数来指定切换的profile。虽然这样很方便,但是容易忘记加这个参数。
我们可以通过maven的profile功能和SpringBoot的profile功能结合使用。效果为:当maven打包时通过profile指定配置为test环境的配置,那么我们SpringBoot里面默认激活的就是test环境的配置。 这样我们只需要打包时指定profile后,直接运行jar就可以,不需要在命令行加参数了。这个效果就和我们普通web项目使用maven的profile的效果类似了。
一、思路 (1)通过maven的profile功能,在打包的时候,通过-P指定maven激活某个pofile,这个profile里面配置了一个参数activatedProperties,不同的profile里面的这个参数的值不同
(2)SpringBoot的application.properties文件里面spring.profiles.active填的值取上面maven的activatedProperties参数值。
这样能实现的效果为:
示例一: maven打包命令为 mvn clean package -P test 那么application.properties里面的spring.profiles.active值就是maven中 id为test的profile的activatedProperties参数值 示例二: maven打包命令为 mvn clean package -P product 那么application.properties里面的spring.profiles.active值就是maven中 id为product的profile的activatedProperties参数值 二、案例 (1)项目结构介绍 项目结构如下图所示,是个常见的SpringBoot项目结构,不同环境的propertis文件的后缀不同(见图中红框处)
(2)pom文件中配置maven的profile maven的profile的配置见下面代码
注意:maven的profile中activatedProperties参数值需要和SpringBoot的不同环境Properties文件的后缀一样。
比如开发环境的Properties的文件名为application-develop.properties,那么maven中develop的profile里面的activatedProperties参数值就应该是develop
<profiles> <profile> <!-- 开发 --> <id>develop</id> <activation> <activeByDefault>true</activeByDefault> </activation> <properties> <activatedProperties>develop</activatedProperties> </properties> </profile> <profile> <!-- 测试 --> <id>fuy</id> <properties> <activatedProperties>fuy</activatedProperties> </properties> </profile> <profile> <!-- 生产 --> <id>production</id> <properties> <activatedProperties>production</activatedProperties> </properties> </profile> </profiles> (3)application.properties中的配置 在application.properties文件中配置SpringBoot默认激活的propertis文件。这时候spring.profiles.active取上面maven的profile里面配置的activatedProperties的值,这个取值要用@符号来取。具体见下面代码
spring.profiles.active=${activatedProperties} (4)如何打包 打包时用 mvn clean package -P profile的id
时间:
|
阅读:
123 字 ~1分钟
ssh远程执行命令出现「Are you sure you want to continue connecting (yes/no)?」解决方法 The authenticity of host '10.4.111.1 (10.4.111.1)' can't be established. ECDSA key fingerprint is SHA256:S1VBZZWcIFk1B1dfasDFfwgU2LN1f9WX4gynfkivTsGsU. ECDSA key fingerprint is MD5:eb:9e:4d:ad:e8:f2:fe:f5:a2:ea:15:1b:9a:7d:6d:93. Are you sure you want to continue connecting (yes/no)? yes Warning: Permanently added '10.4.111.1' (ECDSA) to the list of known hosts. 因为要通过sshpass批量去远程执行命令,所以每一次都弹出这个很麻烦。
解决办法 :
#首先备份 ssh 配置文件 ~]# cp /etc/ssh/ssh_config /etc/ssh/ssh_config_bak 将配置文件中的 StrictHostKeyChecking ask改为 StrictHostKeyChecking no,GSSAPIAuthentication yes 改为 GSSAPIAuthentication no。
Host * GSSAPIAuthentication no StrictHostKeyChecking no 然后再执行批量的sshpass批量命令就虽然会有提示,但是可以正常执行。
时间:
|
阅读:
45 字 ~1分钟
sublime text增加插入当前时间快捷键 1、创建时间插件 Tools -> developer -> New Plugin…
2、插入如下代码,保存在 Packages\User\addCurrentTime.py import sublime import sublime_plugin import datetime class AddCurrentTimeCommand(sublime_plugin.TextCommand): def run(self, edit): self.view.run_command("insert_snippet", { "contents": "%s" % datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S") } ) 3、设置快捷键映射 Preference → Key Bindings - User
{ "keys": ["ctrl+t"], "command": "add_current_time"} 4、重启sublime text即可
时间:
|
阅读:
62 字 ~1分钟
sudo 与 su 两个命令的最大区别是:
sudo 命令需要输入当前用户的密码,su 命令需要输入 root 用户的密码。另外一个区别是其默认行为。sudo 命令只允许使用提升的权限运行单个命令,而 su 命令会启动一个新的 shell,同时允许使用 root 权限运行尽可能多的命令,直到明确退出登录。
su 用以切换成不同的用户的身份
默认只是切换身份,并没有切换环境变量,环境变量依然是普通用户的。切换用户身份时,用户的环境变量也切换成新用户的环境变量,所以”-“不能省略,不然有些操作无法执行。
su root 输入root密码后切换之root用户但是pwd目录不变
su - root 输入root密码后切换之root用户但是pwd目录/root
sudo 一般加的是命令
sudo -i root与sudo - root、sudo -i ,sudo - ,sudo root效果相同 提示输入密码时该密码为当前账户的密码 要求执行该命令的用户必须在sudoers中才可以 su需要的是切换后账户的密 用法为“su 账户名称”
sudo : 暂时切换到超级用户模式以执行超级用户权限,一般指的是root用户,提示输入密码时该密码为当前用户的密码,而不是超级账户的密码。不过有时间限制,Ubuntu默认为一次时长15分钟。
su : 切换到某某用户模式,提示输入密码时该密码为切换后账户的密码,用法为“su 账户名称”。如果后面不加账户时系统默认为root账户,密码也为超级账户的密码。没有时间限制。
sudo -i: 为了频繁的执行某些只有超级用户才能执行的权限,而不用每次输入密码,可以使用该命令。提示输入密码时该密码为当前账户的密码。没有时间限制。执行该命令后提示符变为“#”而不是“$”。想退回普通账户时可以执行“exit”或“logout” 。 要求执行该命令的用户必须在sudoers中才可以
sudo -i 直接运行sudo命令加-i参数 要求执行该命令的用户必须在sudoers中才可以
sudo su 运行sudo命令给su命令提权,运行su命令。 要求执行该命令的用户必须在sudoers中才可以。
时间:
|
阅读:
185 字 ~1分钟
swap是干嘛的? 在Linux下,SWAP的作用类似Windows系统下的“虚拟内存”。当物理内存不足时,拿出部分硬盘空间当SWAP分区(虚拟成内存)使用,从而解决内存容量不足的情况。
SWAP意思是交换,顾名思义,当某进程向OS请求内存发现不足时,OS会把内存中暂时不用的数据交换出去,放在SWAP分区中,这个过程称为SWAP OUT。当某进程又需要这些数据且OS发现还有空闲物理内存时,又会把SWAP分区中的数据交换回物理内存中,这个过程称为SWAP IN。
当然,swap大小是有上限的,一旦swap使用完,操作系统会触发OOM-Killer机制,把消耗内存最多的进程kill掉以释放内存。
数据库系统为什么嫌弃swap? 显然,swap机制的初衷是为了缓解物理内存用尽而选择直接粗暴OOM进程的尴尬。但坦白讲,几乎所有数据库对swap都不怎么待见,无论MySQL、Oracal、MongoDB抑或HBase,为什么?这主要和下面两个方面有关:
\1. 数据库系统一般都对响应延迟比较敏感,如果使用swap代替内存,数据库服务性能必然不可接受。对于响应延迟极其敏感的系统来讲,延迟太大和服务不可用没有任何区别,比服务不可用更严重的是,swap场景下进程就是不死,这就意味着系统一直不可用……再想想如果不使用swap直接oom,是不是一种更好的选择,这样很多高可用系统直接会主从切换掉,用户基本无感知。
\2. 另外对于诸如HBase这类分布式系统来说,其实并不担心某个节点宕掉,而恰恰担心某个节点夯住。一个节点宕掉,最多就是小部分请求短暂不可用,重试即可恢复。但是一个节点夯住会将所有分布式请求都夯住,服务器端线程资源被占用不放,导致整个集群请求阻塞,甚至集群被拖垮。
从这两个角度考虑,所有数据库都不喜欢swap还是很有道理的!
swap的工作机制 既然数据库们对swap不待见,那是不是就要使用swapoff命令关闭磁盘缓存特性呢?非也,大家可以想想,关闭磁盘缓存意味着什么?实际生产环境没有一个系统会如此激进,要知道这个世界永远不是非0即1的,大家都会或多或少选择走在中间,不过有些偏向0,有些偏向1而已。很显然,在swap这个问题上,数据库必然选择偏向尽量少用。HBase官方文档的几点要求实际上就是落实这个方针:尽可能降低swap影响。知己知彼才能百战不殆,要降低swap影响就必须弄清楚Linux内存回收是怎么工作的,这样才能不遗漏任何可能的疑点。
先来看看swap是如何触发的? 简单来说,Linux会在两种场景下触发内存回收,一种是在内存分配时发现没有足够空闲内存时会立刻触发内存回收;一种是开启了一个守护进程(swapd进程)周期性对系统内存进行检查,在可用内存降低到特定阈值之后主动触发内存回收。第一种场景没什么可说,来重点聊聊第二种场景,如下图所示:
这里就要引出我们关注的第一个参数:vm.min_free_kbytes,代表系统所保留空闲内存的最低限watermark[min],并且影响watermark[low]和watermark[high]。简单可以认为:
watermark[min] = min_free_kbytes watermark[low] = watermark[min] * 5 / 4 = min_free_kbytes * 5 / 4 watermark[high] = watermark[min] * 3 / 2 = min_free_kbytes * 3 / 2 watermark[high] - watermark[low] = watermark[low] - watermark[min] = min_free_kbytes / 4 可见,LInux的这几个水位线与参数min_free_kbytes密不可分。min_free_kbytes对于系统的重要性不言而喻,既不能太大,也不能太小。
min_free_kbytes如果太小,[min,low]之间水位的buffer就会很小,在kswapd回收的过程中一旦上层申请内存的速度太快(典型应用:数据库),就会导致空闲内存极易降至watermark[min]以下,此时内核就会进行direct reclaim(直接回收),直接在应用程序的进程上下文中进行回收,再用回收上来的空闲页满足内存申请,因此实际会阻塞应用程序,带来一定的响应延迟。当然,min_free_kbytes也不宜太大,太大一方面会导致应用程序进程内存减少,浪费系统内存资源,另一方面还会导致kswapd进程花费大量时间进行内存回收。再看看这个过程,是不是和Java垃圾回收机制中CMS算法中老生代回收触发机制神似,想想参数-XX:CMSInitiatingOccupancyFraction,是不是?官方文档中要求min_free_kbytes不能小于1G(在大内存系统中设置8G),就是不要轻易触发直接回收。
至此,基本解释了Linux的内存回收触发机制以及我们关注的第一个参数vm.min_free_kbytes。接下来简单看看Linux内存回收都回收些什么。Linux内存回收对象主要分为两种:
\1. 文件缓存,这个容易理解,为了避免文件数据每次都要从硬盘读取,系统会将热点数据存储在内存中,提高性能。如果仅仅将文件读出来,内存回收只需要释放这部分内存即可,下次再次读取该文件数据直接从硬盘中读取即可(类似HBase文件缓存)。那如果不仅将文件读出来,而且对这些缓存的文件数据进行了修改(脏数据),回收内存就需要将这部分数据文件写会硬盘再释放(类似MySQL文件缓存)。
\2. 匿名内存,这部分内存没有实际载体,不像文件缓存有硬盘文件这样一个载体,比如典型的堆、栈数据等。这部分内存在回收的时候不能直接释放或者写回类似文件的媒介中,这才搞出来swap这个机制,将这类内存换出到硬盘中,需要的时候再加载出来。
具体Linux使用什么算法来确认哪些文件缓存或者匿名内存需要被回收掉,这里并不关心,有兴趣可以参考这里。但是有个问题需要我们思考:既然有两类内存可以被回收,那么在这两类内存都可以被回收的情况下,Linux到底是如何决定到底是回收哪类内存呢?还是两者都会被回收?这里就牵出来了我们第二个关心的参数:swappiness,这个值用来定义内核使用swap的积极程度,值越高,内核就会积极地使用swap,值越低,就会降低对swap的使用积极性。该值取值范围在0~100,默认是60。这个swappiness到底是怎么实现的呢?具体原理很复杂,简单来讲,swappiness通过控制内存回收时,回收的匿名页更多一些还是回收的文件缓存更多一些来达到这个效果。swappiness等于100,表示匿名内存和文件缓存将用同样的优先级进行回收,默认60表示文件缓存会优先被回收掉,至于为什么文件缓存要被优先回收掉,大家不妨想想(回收文件缓存通常情况下不会引起IO操作,对系统性能影响较小)。对于数据库来讲,swap是尽量需要避免的,所以需要将其设置为0。此处需要注意,设置为0并不代表不执行swap哦!
至此,我们从Linux内存回收触发机制、Linux内存回收对象一直聊到swap,将参数min_free_kbytes以及swappiness进行了解释。接下来看看另一个与swap有关系的参数:zone_reclaim_mode,文档说了设置这个参数为0可以关闭NUMA的zone reclaim,这又是怎么回事?提起NUMA,数据库们又都不高兴了,很多DBA都曾经被坑惨过。那这里简单说明三个小问题:NUMA是什么?NUMA和swap有什么关系?zone_reclaim_mode的具体意义?
时间:
|
阅读:
102 字 ~1分钟
ulimit: command not found 问题解决 执行命令unlimit报错
# unlimit -a bash: unlimit: command not found 因为ulimit不是一个单独的程序。sudo会去找二进制文件运行。由于找不到ulimit的二进制可执行文件,故报错。
可以这样执行命令:
# sh -c "ulimit -a" core file size (blocks, -c) 0 data seg size (kbytes, -d) unlimited scheduling priority (-e) 0 file size (blocks, -f) unlimited pending signals (-i) 514527 max locked memory (kbytes, -l) 64 max memory size (kbytes, -m) unlimited open files (-n) 655360 pipe size (512 bytes, -p) 8 POSIX message queues (bytes, -q) 819200 real-time priority (-r) 0 stack size (kbytes, -s) 8192 cpu time (seconds, -t) unlimited max user processes (-u) unlimited virtual memory (kbytes, -v) unlimited file locks (-x) unlimited