理解线程池运行过程(ThreadPoolExecutor举例,打印线程池当前线程数、队列任务数、已完成数等信息)

 java  理解线程池运行过程(ThreadPoolExecutor举例,打印线程池当前线程数、队列任务数、已完成数等信息)已关闭评论
11月 292021
 

jdk提供了非常方便的方式来生成线程池, 如Executors.newxxxxxx的方式, 实现实际使用的都是以下ThreadPoolExecutor的方法:

/**
* Creates a new {@code ThreadPoolExecutor} with the given initial
* parameters.
*
* @param corePoolSize the number of threads to keep in the pool, even
* if they are idle, unless {@code allowCoreThreadTimeOut} is set
* @param maximumPoolSize the maximum number of threads to allow in the
* pool
* @param keepAliveTime when the number of threads is greater than
* the core, this is the maximum time that excess idle threads
* will wait for new tasks before terminating.
* @param unit the time unit for the {@code keepAliveTime} argument
* @param workQueue the queue to use for holding tasks before they are
* executed. This queue will hold only the {@code Runnable}
* tasks submitted by the {@code execute} method.
* @param threadFactory the factory to use when the executor
* creates a new thread
* @param handler the handler to use when execution is blocked
* because the thread bounds and queue capacities are reached
* @throws IllegalArgumentException if one of the following holds:<br>
* {@code corePoolSize < 0}<br>
* {@code keepAliveTime < 0}<br>
* {@code maximumPoolSize <= 0}<br>
* {@code maximumPoolSize < corePoolSize}
* @throws NullPointerException if {@code workQueue}
* or {@code threadFactory} or {@code handler} is null
*/

public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory, RejectedExecutionHandler handler)

线程池的管理是这样一个过程:

首先创建线程池, 然后根据任务的数量逐步将线程增大到corePoolSize数量, 如果此时任有任务增加, 则放置到workQueue中, 直到workQueue爆满为止, 然后继续增加线程池中的线程数量,增加处理能力,最终达到maximumPoolSize。 如果此时还是有任务增加进来会怎样呢 ? 这就需要handler来处理了,或者丢弃新任务, 或者拒绝新任务,或者挤占已有任务等。在任务队列何线程池都饱和情况下,一旦有线程处于等待(任务处理完毕, 没有新任务增加)状态的时间超过keepAliveTime,则该线程终止, 也就是说池中的线程数量会足部降低, 直至为corePoolSize数量为止。 至于threadFactory,可以自己新增一个,设置线程的自定义名称, daemon状态,便于后续排查错误。

我们用一个测试程序来帮助理解线程池的运行过程:

public class ThreadPoolTest {

private static ExecutorService es = new ThreadPoolExecutor(50, 100, 0L, TimeUnit.MILLISECONDS,new LinkedBlockingQueue<Runnable>(100000));

public static void main(String[] args) throws Exception {
for (int i = 0; i < 100000; i++) {
es.execute(() -> {
System.out.print(1);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
});

}

ThreadPoolExecutor tpe = ((ThreadPoolExecutor) es);
while (true) {
System.out.println();
int queueSize = tpe.getQueue().size();
System.out.println(“当前排队任务数:” + queueSize);

int activeCount = tpe.getActiveCount();
System.out.println(“当前活动线程数:” + activeCount);

long completedTaskCount = tpe.getCompletedTaskCount();
System.out.println(“执行完成线程数:” + completedTaskCount);

long taskCount = tpe.getTaskCount();
System.out.println(“总线程数:” + taskCount);

Thread.sleep(3000);
}
}

}

 

程序每3秒打印一次线程池情况:

11111111111111111111111111111111111111111111111111
当前排队线程数:99950
当前活动线程数:50
执行完成线程数:0
总线程数:100000
1111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111
当前排队线程数:99849
当前活动线程数:50
执行完成线程数:127
总线程数:100000
111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111
当前排队线程数:99700
当前活动线程数:50
执行完成线程数:250
总线程数:100000
111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111
当前排队线程数:99550
当前活动线程数:50
执行完成线程数:400
总线程数:100000
111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111
当前排队线程数:99400
当前活动线程数:50
执行完成线程数:550
总线程数:100000
111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111
当前排队线程数:99250
当前活动线程数:50
执行完成线程数:700
总线程数:100000

。。。。。。

可以看到当前活动的线程数永远都是50, 为什么就没有突破?怎么没到100呢? 大家可以思考下,相信看了上面的描述,很容易就能理解了,DONE!

 

linux/window环境下设置ipv6和ipv4优先级

 linux, windows  linux/window环境下设置ipv6和ipv4优先级已关闭评论
10月 082021
 

一台机器系统配置ipv6地址和ipv4地址,由于各种原因,有些网站V6的速度比V4差很多, 如果想设置下优先顺序, 该如何设置呢?

 

Linux设置ipv4优先:

 可以将IPv4设置为优先。
[[email protected] ~]#vi /etc/gai.conf
找到 下面一行
#precedence ::ffff:0:0/96  100
将其改为(把最前面的#号删除)
precedence ::ffff:0:0/96  100
Windows设置ipv4优先:

Windows 平台也有这样的问题,解决的方式有二个,一个是把 Windows 的 IPv6 关闭,另一个可以使用下面的命令设置优先级。

 

1. 以系统管理者身份执行 命令提示字元,查询连线顺序
netsh interface ipv6 show prefixpolicies

优先顺序 标签 前缀
———- —– ——————————–
50 0 ::1/128
40 1 ::/0
35 4 ::ffff:0:0/96
30 2 2002::/16
5 5 2001::/32

IPv4 ::ffff:0:0/96 的顺序是 35

排第一的是50, 这时候系统是以 IPv6 为优先

2. 修改顺序,让 IPv4 优先,数字越大,优先性愈高
netsh interface ipv6 set prefixpolicy ::ffff:0:0/96 60 4
就是以 IPv4 为优先

改回來
netsh interface ipv6 set prefixpolicy ::ffff:0:0/96 35 4

 

DONE!!!!

手机蓝牙与mac pro传送文件失败问题解决

 mac  手机蓝牙与mac pro传送文件失败问题解决已关闭评论
9月 162021
 

使用手机与已经蓝牙配对的mac pro机器传送文件,却总提示失败。 解决方法如下:

mac机器还需要打开共享的权限:

打开“系统偏好设置”  -> 选择“共享”  -> 勾选左边的“蓝牙共享”,也可以在右侧界面做一些接收文件存放默认路径等的设置。

DONE!

ubuntu安装、查看已安装包的方法(转)

 ubuntu  ubuntu安装、查看已安装包的方法(转)已关闭评论
9月 162021
 

网上发现的一篇很好的关于ubuntu下查看或安装包的方法的文章,转载分享下,原文链接见文末。

 

说明:由于图形化界面方法(如Add/Remove… 和Synaptic Package Manageer)比较简单,所以这里主要总结在终端通过命令行方式进行的软件包安装、卸载和删除的方法。

一、Ubuntu中软件安装方法

1、APT方式

(1)普通安装:apt-get install softname1 softname2 …;

(2)修复安装:apt-get -f install softname1 softname2… ;(-f Atemp to correct broken dependencies)

(3)重新安装:apt-get –reinstall install softname1 softname2…;

2、Dpkg方式

(1)普通安装:dpkg -i package_name.deb

3、源码安装(.tar、tar.gz、tar.bz2、tar.Z)

首先解压缩源码压缩包然后通过tar命令来完成

a.解xx.tar.gz:tar zxf xx.tar.gz
b.解xx.tar.Z:tar zxf xx.tar.Z
c.解xx.tgz:tar zxf xx.tgz
d.解xx.bz2:bunzip2 xx.bz2
e.解xx.tar:tar xf xx.tar

然后进入到解压出的目录中,建议先读一下README之类的说明文件,因为此时不同源代码包或者预编译包可能存在差异,然后建议使用ls -F –color或者ls -F命令(实际上我的只需要 l 命令即可)查看一下可执行文件,可执行文件会以*号的尾部标志。

一般依次执行./configure

make

sudo make install

即可完成安装。

二、Ubuntu中软件包的卸载方法

1、APT方式

(1)移除式卸载:apt-get remove softname1 softname2 …;(移除软件包,当包尾部有+时,意为安装)

(2)清除式卸载 :apt-get –purge remove softname1 softname2…;(同时清除配置)

清除式卸载:apt-get purge sofname1 softname2…;(同上,也清除配置文件)

2、Dpkg方式

(1)移除式卸载:dpkg -r pkg1 pkg2 …;

(2)清除式卸载:dpkg -P pkg1 pkg2…;

 

三、Ubuntu中软件包的查询方法

Dpkg 使用文本文件来作为数据库.通称在 /var/lib/dpkg 目录下. 通称在 status 文件中存储软件状态,和控制信息. 在 info/ 目录下备份控制文件, 并在其下的 .list 文件中记录安装文件清单, 其下的 .mdasums 保存文件的 MD5 编码.

体验使用数据库的时刻到了:

$ dpkg -l Desired=Unknown/Install/Remove/Purge/Hold | Status=Not/Installed/Config-files/Unpacked/Failed-config/Half-installed |/ Err?=(none)/Hold/Reinst-required/X=both-problems (Status,Err: uppercase=bad) ||/ Name Version Description +++-===========-================-======================================== ii aalib1 1.4p5-28 ascii art library - transitional package ii adduser 3.85 Add and remove users and groups ii alien .63 install non-native packages with dpkg ... ... 

每条记录对应一个软件包, 注意每条记录的第一, 二, 三个字符. 这就是软件包的状态标识, 后边依此是软件包名称, 版本号, 和简单描述.

  • 第一字符为期望值,它包括:
    • u 状态未知,这意味着软件包未安装,并且用户也未发出安装请求.
    • i 用户请求安装软件包.
    • r 用户请求卸载软件包.
    • p 用户请求清除软件包.
    • h 用户请求保持软件包版本锁定.
  • 第二列,是软件包的当前状态.此列包括软件包的六种状态.
    • n 软件包未安装.
    • i 软件包安装并完成配置.
    • c 软件包以前安装过,现在删除了,但是它的配置文件还留在系统中.
    • u 软件包被解包,但还未配置.
    • f 试图配置软件包,但是失败了.
    • h 软件包安装,但是但是没有成功.
  • 第三列标识错误状态,可以总结为四种状态. 第一种状态标识没有问题,为空. 其它三种符号则标识相应问题.
    • h 软件包被强制保持,因为有其它软件包依赖需求,无法升级.
    • r 软件包被破坏,可能需要重新安装才能正常使用(包括删除).
    • x 软包件被破坏,并且被强制保持.

也可以以统配符模式进行模糊查询, 比如我要查找以nano字符开始的所有软件包:

$ dpkg -l nano* Desired=Unknown/Install/Remove/Purge/Hold | Status=Not/Installed/Config-files/Unpacked/Failed-config/Half-installed |/ Err?=(none)/Hold/Reinst-required/X=both-problems (Status,Err: uppercase=bad) ||/ Name Version Description +++-==============-==============-============================================ ii nano 1.3.10-2 free Pico clone with some new features pn nano-tiny <none> (no description available) un nanoblogger <none> (no description available) 

以上状态说明: 系统中安装了 nano 版本为 1.3.10-2 ;安装过 nano-tiny , 后来又清除了; 从未安装过nanoblogger .

如果觉得 dpkg 的参数过多, 不利于记忆的话, 完全可以使用 dpkg-query 进行 dpkg 数据库查询.

应用范例:

  • 查询系统中属于nano的文件:

    $ dpkg –listfiles nano

    or

    $ dpkg-query -L nano

  • 查看软件nano的详细信息:

    $ dpkg -s nano

    or

    $ dpkg-query -s nano

  • 查看系统中软件包状态, 支持模糊查询:

    $ dpkg -l

    or

    $dpkg-query -l

  • 查看某个文件的归属包:

    $ dpkg-query -S nano

    or

    $ dpkg -S nano

三、其他应用总结

apt-cache search # ——(package 搜索包)
apt-cache show #——(package 获取包的相关信息,如说明、大小、版本等)
apt-get install # ——(package 安装包)
apt-get install # —–(package –reinstall 重新安装包)
apt-get -f install # —–(强制安装, “-f = –fix-missing”当是修复安装吧…)
apt-get remove #—–(package 删除包)
apt-get remove –purge # ——(package 删除包,包括删除配置文件等)
apt-get autoremove –purge # —-(package 删除包及其依赖的软件包+配置文件等(只对6.10有效,强烈推荐))
apt-get update #——更新源
apt-get upgrade #——更新已安装的包
apt-get dist-upgrade # ———升级系统
apt-get dselect-upgrade #——使用 dselect 升级
apt-cache depends #——-(package 了解使用依赖)
apt-cache rdepends # ——(package 了解某个具体的依赖,当是查看该包被哪些包依赖吧…)
apt-get build-dep # ——(package 安装相关的编译环境)
apt-get source #——(package 下载该包的源代码)
apt-get clean && apt-get autoclean # ——–清理下载文件的存档 && 只清理过时的包
apt-get check #——-检查是否有损坏的依赖
dpkg -S filename —–查找filename属于哪个软件包
apt-file search filename —–查找filename属于哪个软件包
apt-file list packagename —–列出软件包的内容
apt-file update –更新apt-file的数据库

dpkg –info “软件包名” –列出软件包解包后的包名称.
dpkg -l –列出当前系统中所有的包.可以和参数less一起使用在分屏查看. (类似于rpm -qa)
dpkg -l |grep -i “软件包名” –查看系统中与”软件包名”相关联的包.
dpkg -s 查询已安装的包的详细信息.
dpkg -L 查询系统中已安装的软件包所安装的位置. (类似于rpm -ql)
dpkg -S 查询系统中某个文件属于哪个软件包. (类似于rpm -qf)
dpkg -I 查询deb包的详细信息,在一个软件包下载到本地之后看看用不用安装(看一下呗).
dpkg -i 手动安装软件包(这个命令并不能解决软件包之前的依赖性问题),如果在安装某一个软件包的时候遇到了软件依赖的问题,可以用apt-get -f install在解决信赖性这个问题.
dpkg -r 卸载软件包.不是完全的卸载,它的配置文件还存在.
dpkg -P 全部卸载(但是还是不能解决软件包的依赖性的问题)
dpkg -reconfigure 重新配置

apt-get install
下载软件包,以及所有依赖的包,同时进行包的安装或升级。如果某个包被设置了 hold (停止标志,就会被搁在一边(即不会被升级)。更多 hold 细节请看下面。
apt-get remove [–purge]
移除 以及任何依赖这个包的其它包。
–purge 指明这个包应该被完全清除 (purged) ,更多信息请看 dpkg -P。

apt-get update
升级来自 Debian 镜像的包列表,如果你想安装当天的任何软件,至少每天运行一次,而且每次修改了
/etc/apt/sources.list 後,必须执行。

apt-get upgrade [-u]
升 级所有已经安装的包为最新可用版本。不会安装新的或移除老的包。如果一个包改变了依赖关系而需要安装一个新的包,那么它将不会被升级,而是标志为 hold。apt-get update 不会升级被标志为 hold 的包 (这个也就是 hold 的意思)。请看下文如何手动设置包为 hold。我建议同时使用 ‘-u’ 选项,因为这样你就能看到哪些包将会被升级。

apt-get dist-upgrade [-u]
和 apt-get upgrade 类似,除了 dist-upgrade 会安装和移除包来满足依赖关系。因此具有一定的危险性。

apt-cache search
在软件包名称和描述中,搜索包含xxx的软件包。

apt-cache show
显示某个软件包的完整的描述。

apt-cache showpkg
显示软件包更多细节,以及和其它包的关系。

dselect
console-apt
aptitude
gnome-apt
APT 的几个图形前端(其中一些在使用前得先安装)。这里 dselect 无疑是最强大的,也是最古老,最难驾驭。

普通 Dpkg 用法
dpkg -i
安装一个 Debian 包文件,如你手动下载的文件。

dpkg -c
列出 的内容。

dpkg -I
从 中提取包信息。

dpkg -r
移除一个已安装的包。

dpkg -P
完全清除一个已安装的包。和 remove 不同的是,remove 只是删掉数据和可执行文件,purge 另外还删除所有的配制文件。

dpkg -L
列出 安装的所有文件清单。同时请看 dpkg -c 来检查一个 .deb 文件的内容。

dpkg -s
显示已安装包的信息。同时请看 apt-cache 显示 Debian 存档中的包信息,以及 dpkg -I 来显示从一个 .deb 文件中提取的包信息。

dpkg-reconfigure
重 新配制一个已经安装的包,如果它使用的是 debconf (debconf 为包安装提供了一个统一的配制界面)。你能够重新配制 debconf 它本身,如你想改变它的前端或提问的优先权。例如,重新配制 debconf,使用一个 dialog 前端,简单运行:

dpkg-reconfigure –frontend=dialog debconf (如果你安装时选错了,这里可以改回来哟:)

echo ” hold” | dpkg –set-selections
设置 的状态为 hlod (命令行方式)

dpkg –get-selections “”
取的 的当前状态 (命令行方式)

支持通配符,如:
Debian:~# dpkg –get-selections *wine*
libwine hold
libwine-alsa hold
libwine-arts hold
libwine-dev hold
libwine-nas hold
libwine-print hold
libwine-twain hold
wine hold
wine+ hold
wine-doc hold
wine-utils hold

例如:
大家现在用的都是 gaim-0.58 + QQ-plugin,为了防止 gaim 被升级,我们可以采用如下方法:

方法一:
Debian:~# echo “gaim hold” | dpkg –set-selections
然後用下面命令检查一下:
Debian:~# dpkg –get-selections “gaim”
gaim hold
现在的状态标志是 hold,就不能被升级了。

如果想恢复怎么办呢?
Debian:~# echo “gaim install” | dpkg –set-selections
Debian:~# dpkg –get-selections “gaim”
gaim install
这时状态标志又被重置为 install,可以继续升级了。

同志们会问,哪个这些状态标志都写在哪个文件中呢?
在 /var/lib/dpkg/status 里,你也可以通过修改这个文件实现 hold。

有时你会发现有的软件状态标志是 purge,不要奇怪。
如:事先已经安装了 amsn,然後把它卸了。
apt-get remove –purge amsn
那么状态标志就从 install 变成 purge。

方法二:
在/etc/apt 下手动建一个 preferences 文件
内容:
Package: gaim
Pin: version 0.58*
保存

dpkg -S
在包数据库中查找 ,并告诉你哪个包包含了这个文件。(注:查找的是事先已经安装的包)

——————————————–
Debian的软件包管理工具命令不完全列表
——————————————–
Debian系统中所有的包信息都在/var/lib/dpkg下.其中/var/lib/dpkg/info目录中保存了各个软件包的信息及管理文件.每个文件的作用如下:
以 “.conffiles”     结尾的文件记录软件包的配置列表.
以 “.list”          结尾的文件记录了软件包的文件列表,用户可在文件当中找到软件包文件的具体安装位置.
以 “.md5sums”       结尾的文件记录了md5信息,用来进行包的验证的.
以 “.config”        结尾的文件是软件包的安装配置角本.
以 “.postinst”      角本是完成Debian包解开之后的配置工作,通常用来执行所安装软件包相关的命令和服务的重新启动.
以 “.preinst”       角本在Debain解包之前运行,主要作用是是停止作用于即将升级的软件包服务直到软件包安装或和升级完成.
以 “.prerm”         脚本负责停止与软件包关联的daemon服务,在删除软件包关联文件之前执行.
以 “.postrm”        脚本负责修改软件包链接或文件关联,或删除由它创建的文件.

/var/lib/dpkg/available是软件包的描述信息.
包括当前系统中所有使用的Debian安装源中所有的软件包,还包括当前系统中已经安装和未安装的软件包.

1.dpkg包管理工具

dpkg -r 卸载软件包.不是完全的卸载,它的配置文件还存在.
dpkg –info “软件包名” –列出软件包解包后的包名称.
dpkg -l     –列出当前系统中所有的包.可以和参数less一起使用在分屏查看.
dpkg -l |grep -i “软件包名” –查看系统中与”软件包名”相关联的包.
dpkg -s   查询已安装的包的详细信息. dpkg -L   查询系统中已安装的软件包所安装的位置.
dpkg -S   查询系统中某个文件属于哪个软件包.
dpkg -I   查询deb包的详细信息,在一个软件包下载到本地之后看看用不用安装(看一下呗).
dpkg -i 手动安装软件包(这个命令并不能解决软件包之前的依赖性问题),如果在安装某一个软件包的时候遇到了软件依赖的问题,可以用apt-get -f install在解决信赖性这个问题.
dpkg -reconfigure 重新配置
dpkg -P 全部卸载(但是还是不能解决软件包的依赖性的问题)

 

2. apt高级包管理工具
(1)GTK图形的”synaptic”,这是APT的前端工具.
(2)”aptitude”,这也是APT的前端工具.
用APT管理工具进行包的管理,可以有以下几种方法做源:
(1)拿安装盘做源,方法如下:
apt-cdrom ident        扫描光盘的信息
apt-cdrom add          添加光盘源
(2)这也是最常用的方法就是把源添加到/etc/apt/source.list中,之后更新列apt-get update

APT管理工具常用命令
apt-cache 加上不同的子命令和参数的使用可以实现查找,显示软件,包信息及包信赖关系等功能.
apt-cache stats 显示当前系统所有使用的Debain数据源的统计信息.
apt-cache search +”包名”,可以查找相关的软件包.
apt-cache show   +”包名”,可以显示指定软件包的详细信息.
apt-cache depends +”包名”,可以查找软件包的依赖关系.
apt-get upgrade   更新系统中所有的包到最新版
apt-get install   安装软件包
apt-get –reindtall install 重新安装软件包
apt-get remove 卸载软件包
apt-get –purge remove 完全卸载软件包
apt-get clean 清除无用的软件包
在用命令apt-get install之前,是先将软件包下载到/var/cache/apt/archives中,之后再进行安装的.所以我们可以用apt-get clean清除/var/cache/apt/archives目录中的软件包.

源码包安装
apt-cache showsrc 查找看源码包的文件信息(在下载之前)
apt-get source 下载源码包.
apt-get build-dep +”包名” 构建源码包的编译环境.

 

清除处于rc状态的软件包

dpkg -l |grep ^rc|awk ‘{print $2}’ |tr [“\n”] [” “] | sudo xargs dpkg -P –

 

转自: http://www.cnblogs.com/forward/archive/2012/01/10/2318483.html

 

下载保存后的CSDN页面,再次打开跳转CSDN首页问题解决

 http, javascript  下载保存后的CSDN页面,再次打开跳转CSDN首页问题解决已关闭评论
8月 252021
 

下载保存后的CSDN页面,再次打开会跳转CSDN首页,可以使用下面方式解决:

使用编辑器打开下载的html页面,找到类似下面的行:

<div style=”display:none;”>
<img src=”https://blog.csdn.net/robinvista/article/details/96437123″ onerror=”setTimeout(function(){if(!/(csdn.net|iteye.com|baiducontent.com|googleusercontent.com|360webcache.com|sogoucdn.com|bingj.com|baidu.com)$/.test(window.location.hostname)){window.location.href=&quot;\x68\x74\x74\x70\x73\x3a\x2f\x2f\x77\x77\x77\x2e\x63\x73\x64\x6e\x2e\x6e\x65\x74&quot;}},3000);”>
</div>

删除后保存即可,DONE!

Resolving Segmentation Fault (“Core dumped”) in Ubuntu 解决方法

 linux, ubuntu  Resolving Segmentation Fault (“Core dumped”) in Ubuntu 解决方法已关闭评论
8月 202021
 

ubuntu 16.04 QT编译c++项目产生的二进制文件执行时出现下面提示: Segmentation Fault (“Core dumped”),

先确认下是否是权限不足造成的,使用root用户运行下编译后的二进制文件!!!

 

如果不行,再尝试下下面的方法:

分段错误是指您的系统尝试访问不存在的内存页面。核心转储意味着当一部分代码尝试在只读或空闲位置执行读写操作时。段错误通常与名为 core 的文件相关联,它通常发生在升级过程中。

在核心转储期间运行某些命令时,您可能会遇到“无法打开锁定文件”,这是因为系统正在尝试捕获不存在的位块,这是由于某些特定程序的二进制文件崩溃.

您可以通过回溯或调试来解决它,但解决方案是修复损坏的包,我们可以通过执行以下步骤来完成:

 

Step 1: Remove the lock files present at different locations.

sudo rm -rf /var/lib/apt/lists/lock /var/cache/apt/archives/lock /var/lib/dpkg/lock 

Step 2: Remove repository cache.

sudo apt-get clean all

Step 3: Update and upgrade your repository cache.

sudo apt-get update && sudo apt-get upgrade

Step 4: Now upgrade your distribution, it will update your packages.

sudo apt-get dist-upgrade

Step 5: Find the broken packages and delete them forcefully.

sudo dpkg -l | grep ^..r | apt-get purge

我运行完上面的步骤就OK了,希望可以帮到需要的人!

敏捷开发简介

 开发  敏捷开发简介已关闭评论
7月 312021
 

重温下敏捷开发概念!

敏捷开发(agile development)是非常流行的软件开发方法。据统计,2018年90%的软件开发采用敏捷开发。

但是,到底什么是敏捷开发,能说清的人却不多。本文尝试用简洁易懂的语言,解释敏捷开发。

 

一、迭代开发

敏捷开发的核心是迭代开发(iterative development)。敏捷一定是采用迭代开发的方式。

那么什么是”迭代开发”呢?迭代的英文是 iterative,直译为”重复”,迭代开发其实就是”重复开发”。

对于大型软件项目,传统的开发方式是采用一个大周期(比如一年)进行开发,整个过程就是一次”大开发”;迭代开发的方式则不一样,它将开发过程拆分成多个小周期,即一次”大开发”变成多次”小开发”,每次小开发都是同样的流程,所以看上去就好像重复在做同样的步骤。

举例来说,SpaceX 公司想造一个大推力火箭,将人类送到火星。但是,它不是一开始就造大火箭,而是先造一个最简陋的小火箭 Falcon 1。结果,第一次发射就爆炸了,直到第四次发射,才成功进入轨道。然后,开发了中型火箭 Falcon 9,九年中发射了70次。最后,才开发 Falcon 重型火箭。如果 SpaceX 不采用迭代开发,它可能直到现在还无法上天。

迭代开发将一个大任务,分解成多次连续的开发,本质就是逐步改进。开发者先快速发布一个有效但不完美的最简版本,然后不断迭代。每一次迭代都包含规划、设计、编码、测试、评估五个步骤,不断改进产品,添加新功能。通过频繁的发布,以及跟踪对前一次迭代的反馈,最终接近较完善的产品形态。

二、增量开发

迭代开发只是要求将开发分成多个迭代,并没有回答一个重要的问题:怎么划分迭代,哪个任务在这个迭代,哪个任务在下个迭代?这时,一般采用”增量开发”(incremental development)划分迭代。

所谓”增量开发”,指的是软件的每个版本,都会新增一个用户可以感知的完整功能。也就是说,按照新增功能来划分迭代。

举例来说,房产公司开发一个10栋楼的小区。如果采用增量开发的模式,该公司第一个迭代就是交付一号楼,第二个迭代交付二号楼……每个迭代都是完成一栋完整的楼。而不是第一个迭代挖好10栋楼的地基,第二个迭代建好每栋楼的骨架,第三个迭代架设屋顶……

增量开发加上迭代开发,才算真正的敏捷开发。

三、敏捷开发的好处

3.1 早期交付

敏捷开发的第一个好处,就是早期交付,从而大大降低成本。

还是以上一节的房产公司为例,如果按照传统的”瀑布开发模式”,先挖10栋楼的地基、再盖骨架、然后架设屋顶,每个阶段都等到前一个阶段完成后开始,可能需要两年才能一次性交付10栋楼。也就是说,如果不考虑预售,该项目必须等到两年后才能回款。

敏捷开发是六个月后交付一号楼,后面每两个月交付一栋楼。因此,半年就能回款10%,后面每个月都会有现金流,资金压力就大大减轻了。

3.2 降低风险

敏捷开发的第二个好处是,及时了解市场需求,降低产品不适用的风险。

请想一想,哪一种情况损失比较小:10栋楼都造好以后,才发现卖不出去,还是造好第一栋楼,就发现卖不出去,从而改进或停建后面9栋楼?

对于软件项目来说,先有一个原型产品,了解市场的接受程度,往往是项目成功的关键。有一本书叫做《梦断代码》,副标题就是”20+个程序员,三年时间,4732个bug,100+万美元,最后失败的故事”,这就是没有采用敏捷开发的结果。相反的,Instagram 最初是一个地理位置打卡 App,后来发现用户不怎么在乎地理位置,更喜欢上传照片,就改做照片上传软件,结果成了独角兽。

由于敏捷开发可以不断试错,找出对业务最重要的功能,然后通过迭代,调整软件方向。相比传统方式,大大增加了产品成功的可能性。如果市场需求不确定,或者你对该领域不熟悉,那么敏捷开发几乎是唯一可行的应对方式。

四、如何进行每一次迭代

虽然敏捷开发将软件开发分成多个迭代,但是也要求,每次迭代都是一个完整的软件开发周期,必须按照软件工程的方法论,进行正规的流程管理。

具体来说,每次迭代都必须依次完成以下五个步骤。

  1. 需求分析(requirements analysis)
  2. 设计(design)
  3. 编码(coding)
  4. 测试(testing)
  5. 部署和评估(deployment / evaluation)

每个迭代大约持续2~6周。

五、敏捷开发的价值观

《敏捷软件开发宣言》里面提到四个价值观。

  • 程序员的主观能动性,以及程序员之间的互动,优于既定流程和工具。
  • 软件能够运行,优于详尽的文档。
  • 跟客户的密切协作,优于合同和谈判。
  • 能够响应变化,优于遵循计划。

六、十二条原则

该宣言还提出十二条敏捷开发的原则。

  1. 通过早期和持续交付有价值的软件,实现客户满意度。
  2. 欢迎不断变化的需求,即使是在项目开发的后期。要善于利用需求变更,帮助客户获得竞争优势。
  3. 不断交付可用的软件,周期通常是几周,越短越好。
  4. 项目过程中,业务人员与开发人员必须在一起工作。
  5. 项目必须围绕那些有内在动力的个人而建立,他们应该受到信任。
  6. 面对面交谈是最好的沟通方式。
  7. 可用性是衡量进度的主要指标。
  8. 提倡可持续的开发,保持稳定的进展速度。
  9. 不断关注技术是否优秀,设计是否良好。
  10. 简单性至关重要,尽最大可能减少不必要的工作。
  11. 最好的架构、要求和设计,来自团队内部自发的认识。
  12. 团队要定期反思如何更有效,并相应地进行调整。

 

转自: https://www.ruanyifeng.com/blog/2019/03/agile-development.html

Redis的三种集群方案简介(Master, Slave, Sentinel, Cluster)

 redis  Redis的三种集群方案简介(Master, Slave, Sentinel, Cluster)已关闭评论
7月 292021
 

重温下redis集群的三种集群方案,分享下网络上的一篇文章,写的很好, 原文链接见文末。

 

在开发测试环境中,我们一般搭建Redis的单实例来应对开发测试需求,但是在生产环境,如果对可用性、可靠性要求较高,则需要引入Redis的集群方案。虽然现在各大云平台有提供缓存服务可以直接使用,但了解一下其背后的实现与原理总还是有些必要(比如面试), 本文就一起来学习一下Redis的几种集群方案。

Redis支持三种集群方案

  • 主从复制模式
  • Sentinel(哨兵)模式
  • Cluster模式

主从复制模式

1. 基本原理

主从复制模式中包含一个主数据库实例(master)与一个或多个从数据库实例(slave),如下图

redis-master-slave

客户端可对主数据库进行读写操作,对从数据库进行读操作,主数据库写入的数据会实时自动同步给从数据库。

具体工作机制为:

  1. slave启动后,向master发送SYNC命令,master接收到SYNC命令后通过bgsave保存快照(即上文所介绍的RDB持久化),并使用缓冲区记录保存快照这段时间内执行的写命令
  2. master将保存的快照文件发送给slave,并继续记录执行的写命令
  3. slave接收到快照文件后,加载快照文件,载入数据
  4. master快照发送完后开始向slave发送缓冲区的写命令,slave接收命令并执行,完成复制初始化
  5. 此后master每次执行一个写命令都会同步发送给slave,保持master与slave之间数据的一致性

2. 部署示例

本示例基于Redis 5.0.3版。

redis.conf的主要配置

###网络相关###
# bind 127.0.0.1 # 绑定监听的网卡IP,注释掉或配置成0.0.0.0可使任意IP均可访问
protected-mode no # 关闭保护模式,使用密码访问
port 6379  # 设置监听端口,建议生产环境均使用自定义端口
timeout 30 # 客户端连接空闲多久后断开连接,单位秒,0表示禁用

###通用配置###
daemonize yes # 在后台运行
pidfile /var/run/redis_6379.pid  # pid进程文件名
logfile /usr/local/redis/logs/redis.log # 日志文件的位置

###RDB持久化配置###
save 900 1 # 900s内至少一次写操作则执行bgsave进行RDB持久化
save 300 10
save 60 10000 
# 如果禁用RDB持久化,可在这里添加 save ""
rdbcompression yes #是否对RDB文件进行压缩,建议设置为no,以(磁盘)空间换(CPU)时间
dbfilename dump.rdb # RDB文件名称
dir /usr/local/redis/datas # RDB文件保存路径,AOF文件也保存在这里

###AOF配置###
appendonly yes # 默认值是no,表示不使用AOF增量持久化的方式,使用RDB全量持久化的方式
appendfsync everysec # 可选值 always, everysec,no,建议设置为everysec

###设置密码###
requirepass 123456 # 设置复杂一点的密码

部署主从复制模式只需稍微调整slave的配置,在redis.conf中添加

replicaof 127.0.0.1 6379 # master的ip,port
masterauth 123456 # master的密码
replica-serve-stale-data no # 如果slave无法与master同步,设置成slave不可读,方便监控脚本发现问题

本示例在单台服务器上配置master端口6379,两个slave端口分别为7001,7002,启动master,再启动两个slave

[[email protected] master-slave]# redis-server master.conf
[[email protected] master-slave]# redis-server slave1.conf
[[email protected] master-slave]# redis-server slave2.conf

进入master数据库,写入一个数据,再进入一个slave数据库,立即便可访问刚才写入master数据库的数据。如下所示

[[email protected] master-slave]# redis-cli 
127.0.0.1:6379> auth 123456
OK
127.0.0.1:6379> set site blog.jboost.cn
OK
127.0.0.1:6379> get site
"blog.jboost.cn"
127.0.0.1:6379> info replication
# Replication
role:master
connected_slaves:2
slave0:ip=127.0.0.1,port=7001,state=online,offset=13364738,lag=1
slave1:ip=127.0.0.1,port=7002,state=online,offset=13364738,lag=0
...
127.0.0.1:6379> exit

[[email protected] master-slave]# redis-cli -p 7001
127.0.0.1:7001> auth 123456
OK
127.0.0.1:7001> get site
"blog.jboost.cn"

执行info replication命令可以查看连接该数据库的其它库的信息,如上可看到有两个slave连接到master

3. 主从复制的优缺点

优点:

  1. master能自动将数据同步到slave,可以进行读写分离,分担master的读压力
  2. master、slave之间的同步是以非阻塞的方式进行的,同步期间,客户端仍然可以提交查询或更新请求

缺点:

  1. 不具备自动容错与恢复功能,master或slave的宕机都可能导致客户端请求失败,需要等待机器重启或手动切换客户端IP才能恢复
  2. master宕机,如果宕机前数据没有同步完,则切换IP后会存在数据不一致的问题
  3. 难以支持在线扩容,Redis的容量受限于单机配置

Sentinel(哨兵)模式

1. 基本原理

哨兵模式基于主从复制模式,只是引入了哨兵来监控与自动处理故障。如图

redis-sentinel

哨兵顾名思义,就是来为Redis集群站哨的,一旦发现问题能做出相应的应对处理。其功能包括

  1. 监控master、slave是否正常运行
  2. 当master出现故障时,能自动将一个slave转换为master(大哥挂了,选一个小弟上位)
  3. 多个哨兵可以监控同一个Redis,哨兵之间也会自动监控

哨兵模式的具体工作机制:

在配置文件中通过 sentinel monitor <master-name> <ip> <redis-port> <quorum> 来定位master的IP、端口,一个哨兵可以监控多个master数据库,只需要提供多个该配置项即可。哨兵启动后,会与要监控的master建立两条连接:

  1. 一条连接用来订阅master的_sentinel_:hello频道与获取其他监控该master的哨兵节点信息
  2. 另一条连接定期向master发送INFO等命令获取master本身的信息

与master建立连接后,哨兵会执行三个操作:

  1. 定期(一般10s一次,当master被标记为主观下线时,改为1s一次)向master和slave发送INFO命令
  2. 定期向master和slave的_sentinel_:hello频道发送自己的信息
  3. 定期(1s一次)向master、slave和其他哨兵发送PING命令

发送INFO命令可以获取当前数据库的相关信息从而实现新节点的自动发现。所以说哨兵只需要配置master数据库信息就可以自动发现其slave信息。获取到slave信息后,哨兵也会与slave建立两条连接执行监控。通过INFO命令,哨兵可以获取主从数据库的最新信息,并进行相应的操作,比如角色变更等。

接下来哨兵向主从数据库的_sentinel_:hello频道发送信息与同样监控这些数据库的哨兵共享自己的信息,发送内容为哨兵的ip端口、运行id、配置版本、master名字、master的ip端口还有master的配置版本。这些信息有以下用处:

  1. 其他哨兵可以通过该信息判断发送者是否是新发现的哨兵,如果是的话会创建一个到该哨兵的连接用于发送PING命令。
  2. 其他哨兵通过该信息可以判断master的版本,如果该版本高于直接记录的版本,将会更新
  3. 当实现了自动发现slave和其他哨兵节点后,哨兵就可以通过定期发送PING命令定时监控这些数据库和节点有没有停止服务。

如果被PING的数据库或者节点超时(通过 sentinel down-after-milliseconds master-name milliseconds 配置)未回复,哨兵认为其主观下线(sdown,s就是Subjectively —— 主观地)。如果下线的是master,哨兵会向其它哨兵发送命令询问它们是否也认为该master主观下线,如果达到一定数目(即配置文件中的quorum)投票,哨兵会认为该master已经客观下线(odown,o就是Objectively —— 客观地),并选举领头的哨兵节点对主从系统发起故障恢复。若没有足够的sentinel进程同意master下线,master的客观下线状态会被移除,若master重新向sentinel进程发送的PING命令返回有效回复,master的主观下线状态就会被移除

哨兵认为master客观下线后,故障恢复的操作需要由选举的领头哨兵来执行,选举采用Raft算法:

  1. 发现master下线的哨兵节点(我们称他为A)向每个哨兵发送命令,要求对方选自己为领头哨兵
  2. 如果目标哨兵节点没有选过其他人,则会同意选举A为领头哨兵
  3. 如果有超过一半的哨兵同意选举A为领头,则A当选
  4. 如果有多个哨兵节点同时参选领头,此时有可能存在一轮投票无竞选者胜出,此时每个参选的节点等待一个随机时间后再次发起参选请求,进行下一轮投票竞选,直至选举出领头哨兵

选出领头哨兵后,领头者开始对系统进行故障恢复,从出现故障的master的从数据库中挑选一个来当选新的master,选择规则如下:

  1. 所有在线的slave中选择优先级最高的,优先级可以通过slave-priority配置
  2. 如果有多个最高优先级的slave,则选取复制偏移量最大(即复制越完整)的当选
  3. 如果以上条件都一样,选取id最小的slave

挑选出需要继任的slave后,领头哨兵向该数据库发送命令使其升格为master,然后再向其他slave发送命令接受新的master,最后更新数据。将已经停止的旧的master更新为新的master的从数据库,使其恢复服务后以slave的身份继续运行。

2. 部署演示

本示例基于Redis 5.0.3版。

哨兵模式基于前文的主从复制模式。哨兵的配置文件为sentinel.conf,在文件中添加

sentinel monitor mymaster 127.0.0.1 6379 1 # mymaster定义一个master数据库的名称,后面是master的ip, port,1表示至少需要一个Sentinel进程同意才能将master判断为失效,如果不满足这个条件,则自动故障转移(failover)不会执行
sentinel auth-pass mymaster 123456 # master的密码

sentinel down-after-milliseconds mymaster 5000 # 5s未回复PING,则认为master主观下线,默认为30s
sentinel parallel-syncs mymaster 2  # 指定在执行故障转移时,最多可以有多少个slave实例在同步新的master实例,在slave实例较多的情况下这个数字越小,同步的时间越长,完成故障转移所需的时间就越长
sentinel failover-timeout mymaster 300000 # 如果在该时间(ms)内未能完成故障转移操作,则认为故障转移失败,生产环境需要根据数据量设置该值

一个哨兵可以监控多个master数据库,只需按上述配置添加多套

分别以26379,36379,46379端口启动三个sentinel

[[email protected] sentinel]# redis-server sentinel1.conf --sentinel
[[email protected] sentinel]# redis-server sentinel2.conf --sentinel
[[email protected] sentinel]# redis-server sentinel3.conf --sentinel

也可以使用redis-sentinel sentinel1.conf 命令启动。此时集群包含一个master、两个slave、三个sentinel,如图,

redis-cluster-instance

我们来模拟master挂掉的场景,执行 kill -9 3017 将master进程干掉,进入slave中执行 info replication查看,

[[email protected] sentinel]# redis-cli -p 7001
127.0.0.1:7001> auth 123456
OK
127.0.0.1:7001> info replication
# Replication
role:slave
master_host:127.0.0.1
master_port:7002
master_link_status:up
master_last_io_seconds_ago:1
master_sync_in_progress:0
# 省略
127.0.0.1:7001> exit
[[email protected] sentinel]# redis-cli -p 7002
127.0.0.1:7002> auth 123456
OK
127.0.0.1:7002> info replication
# Replication
role:master
connected_slaves:1
slave0:ip=127.0.0.1,port=7001,state=online,offset=13642721,lag=1
# 省略

可以看到slave 7002已经成功上位晋升为master(role:master),接收一个slave 7001的连接。此时查看slave2.conf配置文件,发现replicaof的配置已经被移除了,slave1.conf的配置文件里replicaof 127.0.0.1 6379 被改为 replicaof 127.0.0.1 7002。重新启动master,也可以看到master.conf配置文件中添加了replicaof 127.0.0.1 7002的配置项,可见大哥(master)下位后,再出来混就只能当当小弟(slave)了,三十年河东三十年河西。

3. 哨兵模式的优缺点

优点:

  1. 哨兵模式基于主从复制模式,所以主从复制模式有的优点,哨兵模式也有
  2. 哨兵模式下,master挂掉可以自动进行切换,系统可用性更高

缺点:

  1. 同样也继承了主从模式难以在线扩容的缺点,Redis的容量受限于单机配置
  2. 需要额外的资源来启动sentinel进程,实现相对复杂一点,同时slave节点作为备份节点不提供服务

Cluster模式

1. 基本原理

哨兵模式解决了主从复制不能自动故障转移,达不到高可用的问题,但还是存在难以在线扩容,Redis容量受限于单机配置的问题。Cluster模式实现了Redis的分布式存储,即每台节点存储不同的内容,来解决在线扩容的问题。如图

redis-cluster

Cluster采用无中心结构,它的特点如下:

  1. 所有的redis节点彼此互联(PING-PONG机制),内部使用二进制协议优化传输速度和带宽
  2. 节点的fail是通过集群中超过半数的节点检测失效时才生效
  3. 客户端与redis节点直连,不需要中间代理层.客户端不需要连接集群所有节点,连接集群中任何一个可用节点即可

Cluster模式的具体工作机制:

  1. 在Redis的每个节点上,都有一个插槽(slot),取值范围为0-16383
  2. 当我们存取key的时候,Redis会根据CRC16的算法得出一个结果,然后把结果对16384求余数,这样每个key都会对应一个编号在0-16383之间的哈希槽,通过这个值,去找到对应的插槽所对应的节点,然后直接自动跳转到这个对应的节点上进行存取操作
  3. 为了保证高可用,Cluster模式也引入主从复制模式,一个主节点对应一个或者多个从节点,当主节点宕机的时候,就会启用从节点
  4. 当其它主节点ping一个主节点A时,如果半数以上的主节点与A通信超时,那么认为主节点A宕机了。如果主节点A和它的从节点都宕机了,那么该集群就无法再提供服务了

Cluster模式集群节点最小配置6个节点(3主3从,因为需要半数以上),其中主节点提供读写操作,从节点作为备用节点,不提供请求,只作为故障转移使用。

2. 部署演示

本示例基于Redis 5.0.3版。

Cluster模式的部署比较简单,首先在redis.conf中

port 7100 # 本示例6个节点端口分别为7100,7200,7300,7400,7500,7600 
daemonize yes # r后台运行 
pidfile /var/run/redis_7100.pid # pidfile文件对应7100,7200,7300,7400,7500,7600 
cluster-enabled yes # 开启集群模式 
masterauth passw0rd # 如果设置了密码,需要指定master密码
cluster-config-file nodes_7100.conf # 集群的配置文件,同样对应7100,7200等六个节点
cluster-node-timeout 15000 # 请求超时 默认15秒,可自行设置 

分别以端口7100,7200,7300,7400,7500,7600 启动六个实例(如果是每个服务器一个实例则配置可一样)

[[email protected] cluster]# redis-server redis_7100.conf
[[email protected] cluster]# redis-server redis_7200.conf
...

然后通过命令将这个6个实例组成一个3主节点3从节点的集群,

redis-cli --cluster create --cluster-replicas 1 127.0.0.1:7100 127.0.0.1:7200 127.0.0.1:7300 127.0.0.1:7400 127.0.0.1:7500 127.0.0.1:7600 -a passw0rd

执行结果如图

redis-cluster-deploy

可以看到 7100, 7200, 7300 作为3个主节点,分配的slot分别为 0-5460, 5461-10922, 10923-16383, 7600作为7100的slave, 7500作为7300的slave,7400作为7200的slave。

我们连接7100设置一个值

[[email protected] cluster]# redis-cli -p 7100 -c -a passw0rd
Warning: Using a password with '-a' or '-u' option on the command line interface may not be safe.
127.0.0.1:7100> set site blog.jboost.cn
-> Redirected to slot [9421] located at 127.0.0.1:7200
OK
127.0.0.1:7200> get site
"blog.jboost.cn"
127.0.0.1:7200>

注意添加 -c 参数表示以集群模式,否则报 (error) MOVED 9421 127.0.0.1:7200 错误, 以 -a 参数指定密码,否则报(error) NOAUTH Authentication required错误。

从上面命令看到key为site算出的slot为9421,落在7200节点上,所以有Redirected to slot [9421] located at 127.0.0.1:7200,集群会自动进行跳转。因此客户端可以连接任何一个节点来进行数据的存取。

通过cluster nodes可查看集群的节点信息

127.0.0.1:7200> cluster nodes
eb28aaf090ed1b6b05033335e3d90a202b422d6c 127.0.0.1:[email protected] slave c1047de2a1b5d5fa4666d554376ca8960895a955 0 1584165266071 5 connected
4cc0463878ae00e5dcf0b36c4345182e021932bc 127.0.0.1:[email protected] slave 5544aa5ff20f14c4c3665476de6e537d76316b4a 0 1584165267074 4 connected
dbbb6420d64db22f35a9b6fa460b0878c172a2fb 127.0.0.1:[email protected] master - 0 1584165266000 1 connected 0-5460
d4b434f5829e73e7e779147e905eea6247ffa5a2 127.0.0.1:[email protected] slave dbbb6420d64db22f35a9b6fa460b0878c172a2fb 0 1584165265000 6 connected
5544aa5ff20f14c4c3665476de6e537d76316b4a 127.0.0.1:[email protected] myself,master - 0 1584165267000 2 connected 5461-10922
c1047de2a1b5d5fa4666d554376ca8960895a955 127.0.0.1:[email protected] master - 0 1584165268076 3 connected 10923-16383

我们将7200通过 kill -9 pid杀死进程来验证集群的高可用,重新进入集群执行cluster nodes可以看到7200 fail了,但是7400成了master,重新启动7200,可以看到此时7200已经变成了slave。

3. Cluster模式的优缺点

优点:

  1. 无中心架构,数据按照slot分布在多个节点。
  2. 集群中的每个节点都是平等的关系,每个节点都保存各自的数据和整个集群的状态。每个节点都和其他所有节点连接,而且这些连接保持活跃,这样就保证了我们只需要连接集群中的任意一个节点,就可以获取到其他节点的数据。
  3. 可线性扩展到1000多个节点,节点可动态添加或删除
  4. 能够实现自动故障转移,节点之间通过gossip协议交换状态信息,用投票机制完成slave到master的角色转换

缺点:

  1. 客户端实现复杂,驱动要求实现Smart Client,缓存slots mapping信息并及时更新,提高了开发难度。目前仅JedisCluster相对成熟,异常处理还不完善,比如常见的“max redirect exception”
  2. 节点会因为某些原因发生阻塞(阻塞时间大于 cluster-node-timeout)被判断下线,这种failover是没有必要的
  3. 数据通过异步复制,不保证数据的强一致性
  4. slave充当“冷备”,不能缓解读压力
  5. 批量操作限制,目前只支持具有相同slot值的key执行批量操作,对mset、mget、sunion等操作支持不友好
  6. key事务操作支持有线,只支持多key在同一节点的事务操作,多key分布不同节点时无法使用事务功能
  7. 不支持多数据库空间,单机redis可以支持16个db,集群模式下只能使用一个,即db 0

Redis Cluster模式不建议使用pipeline和multi-keys操作,减少max redirect产生的场景。

总结

本文介绍了Redis集群方案的三种模式,其中主从复制模式能实现读写分离,但是不能自动故障转移;哨兵模式基于主从复制模式,能实现自动故障转移,达到高可用,但与主从复制模式一样,不能在线扩容,容量受限于单机的配置;Cluster模式通过无中心化架构,实现分布式存储,可进行线性扩展,也能高可用,但对于像批量操作、事务操作等的支持性不够好。三种模式各有优缺点,可根据实际场景进行选择。

 

转自:https://segmentfault.com/a/1190000022028642

python 2.7.x 安装 pip

 pip, python  python 2.7.x 安装 pip已关闭评论
6月 152021
 

一般Python 2 >=2.7.9的版本都自带了pip,但有些时候因为这样那样的原因卸载了pip模块,可以使用下面的方式安装(先下载get-pip.py文件,再安装):

1. 如果python是 2.7.x版本

[[email protected] o-u-u]# curl https://bootstrap.pypa.io/pip/2.7/get-pip.py -o get-pip.py & python get-pip.py

或:

python -m ensurepip –default-pip 

 

2. python >=3.6

[[email protected] o-u-u]# curl https://bootstrap.pypa.io/get-pip.py -o get-pip.py & python get-pip.py

 

Unable to convert string [${xxxx}] to class [java.util.Date] for attribute [value]: [Property Editor not registered with the PropertyEditorManager]

 java, tomcat  Unable to convert string [${xxxx}] to class [java.util.Date] for attribute [value]: [Property Editor not registered with the PropertyEditorManager]已关闭评论
4月 162021
 

使用intellj idea 启动tomcat测试一个简单war包,测试里面一个spring mvc 页面, 使用jstl方式时出现一个怪问题,记录下:

 

index.jsp页面内容如下:

<%@ page import="java.util.Date" %>
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
                    "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
    <title>Index</title>
</head>
<body>
<p>
    Hello Spring MVC!!!
</p>
<p>
    <%
        Date now1 = new Date();
        %>
    服务器时间1: <fmt:formatDate value="<%=now1%>" pattern="yyyy-MM-dd HH:mm:ss" />
</p>
<p>
    服务器时间2: <fmt:formatDate value="${now2}" pattern="yyyy-MM-dd HH:mm:ss" />
</p>

</body>
</html>

IndexController内容:
@Controller
public class IndexController {

    @RequestMapping(value = {"", "/index"})
    public ModelAndView dicts() {
        ModelAndView mv = new ModelAndView("index");
        mv.addObject("now2", new Date());
        return mv;
    }
}
测试结果,出现错误:
Unable to convert string [${now2}] to class [java.util.Date] for attribute [value]: [Property Editor not registered with the PropertyEditorManager]

如果把jsp页面中  “服务器时间2xxxx”  这行注释掉又一切正常,这就奇怪了。

后来在网上找到解决方案:发现有由于web.xm声明的问题:

原来声明:
<!DOCTYPE web-app PUBLIC
 "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
 "http://java.sun.com/dtd/web-app_2_3.dtd" >

这样写有问题,修改为:

<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xmlns="http://java.sun.com/xml/ns/javaee"
         xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
         metadata-complete="true" version="3.0">
。。。。
</web-app>

便不会出现这个问题了。网上有说是因为Tomcat的支持web.xml的Servlet是2.5版本。

记录下!

DONE!


 

Mac OS 系统, 打开pycharm时右下角提示: xcrun: error: invalid active developer path, missing xcrun

 mac  Mac OS 系统, 打开pycharm时右下角提示: xcrun: error: invalid active developer path, missing xcrun已关闭评论
4月 092021
 

要解决此问题,请安装Xcode。如果您从事iOS App开发,则可以安装完整版本,它也将解决此问题。从Apple developers page下载Xcode .dmg文件。

如果你不使用xcode,则只需搜索Xcode的Command Line Tools(命令行工具包),然后下载.dmg文件并安装。

可以尝试下面的几个解决方案:

如果您不想下载任何内容,而只想在终端中运行一些命令来解决此问题,那么以下是适合您的命令:

xcode-select --install

这是可以在终端中运行的命令,它将为Xcode安装命令行工具。当您运行此命令时,系统将提示您接受许可证,同意条款和条件等。

 

也可以尝试下下面的命令后再使用上面的命令解决该问题:

xcode-select --reset

尽管这可以解决您的问题,但作为后续步骤,您可能需要设置命令行工具在不使用Xcode的情况下运行的路径。

xcode-select --switch /Library/Developer/CommandLineTools

如果在运行上述任何命令时遇到权限问题,请sudo与这些命令一起使用。例如,

sudo xcode-select --switch /Library/Developer/CommandLineTools

如果您已安装Xcode应用程序,请尝试运行以下命令:

sudo xcode-select --switch /Applications/Xcode.app/Contents/Developer

maven下产生一个包含依赖jar的可执行jar文件(create an executable JAR with dependencies using Maven)

 MAVEN  maven下产生一个包含依赖jar的可执行jar文件(create an executable JAR with dependencies using Maven)已关闭评论
4月 082021
 

想使用 java -jar xxx.jar 命令行直接执行一个可执行的jar包,而这个xxx.jar包是包含第三方的依赖包的,网上有许多文章描述如何修改pom.xml,但实际是没什么效果的,实验下来如下方法可以达到想要的效果:

 

  •  1.  pom.xml 的build下添加下列 plugin。
<build>
    <plugins>
      <plugin>
        <artifactId>maven-assembly-plugin</artifactId>
        <configuration>
          <archive>
            <manifest>
              <mainClass>com.ouu.App</mainClass>
            </manifest>
          </archive>
          <descriptorRefs>
            <descriptorRef>jar-with-dependencies</descriptorRef>
          </descriptorRefs>
        </configuration>
      </plugin>

    </plugins>
</build>
  • 2. 进入项目根目录, 在命令行下运行:

mvn clean compile assembly:single

这是在项目target目录下会产生一个xxxx-jar-with-dependencies.jar的可执行文件

linux shell 字符串操作详解 (长度,读取,替换,截取,连接,对比,删除,位置 )(转)

 linux, shell  linux shell 字符串操作详解 (长度,读取,替换,截取,连接,对比,删除,位置 )(转)已关闭评论
4月 012021
 

网上看到的一篇关于shell下操作字符串的好文章,linux shell 字符串操作详解 (长度,读取,替换,截取,连接,对比,删除,位置 ),原文地址见文末。

 

1.Linux shell 截取字符变量的前8位

实现方法有如下几种:

  1. expr substr “$a” 1 8
  2. echo $a|awk ‘{print substr(,1,8)}’
  3. echo $a|cut -c1-8
  4. echo $
  5. expr $a : ‘\(.\\).*’
  6. echo $a|dd bs=1 count=8 2>/dev/null

 

2.按指定的字符串截取

(1)第一种方法:

从左向右截取最后一个string后的字符串
${varible##*string}
从左向右截取第一个string后的字符串
${varible#*string}
从右向左截取最后一个string后的字符串
${varible%%string*}
从右向左截取第一个string后的字符串
${varible%string*}
“*”只是一个通配符可以不要

请看下面的例子:

$ MYVAR=foodforthought.jpg
$ echo ${MYVAR##*fo}
rthought.jpg
$ echo ${MYVAR#*fo}
odforthought.jpg

(2)第二种方法:

${varible:n1:n2}:截取变量varible从n1开始的n2个字符,组成一个子字符串。可以根据特定字符偏移和长度,使用另一种形式的变量扩展,来选择特定子字符串。试着在 bash 中输入以下行:

$ EXCLAIM=cowabunga
$ echo ${EXCLAIM:0:3}
cow
$ echo ${EXCLAIM:3:7}
abunga

这种形式的字符串截断非常简便,只需用冒号分开来指定起始字符和子字符串长度。

3.按照指定要求分割:

比如获取后缀名

ls -al | cut -d “.” -f2

小结:shell对应字符串的处理方法很多,根据需求灵活选择。

 

在做shell批处理程序时候,经常会涉及到字符串相关操作。有很多命令语句,如:awk,sed都可以做字符串各种操作。 其实shell内置一系列操作符号,可以达到类似效果,大家知道,使用内部操作符会省略启动外部程序等时间,因此速度会非常的快。

 

一、判断读取字符串值

表达式 含义

${var} 变量var的值, 与$var相同
${var-DEFAULT} 如果var没有被声明, 那么就以$DEFAULT作为其值 *
${var:-DEFAULT} 如果var没有被声明, 或者其值为空, 那么就以$DEFAULT作为其值 *
${var=DEFAULT} 如果var没有被声明, 那么就以$DEFAULT作为其值 *
${var:=DEFAULT} 如果var没有被声明, 或者其值为空, 那么就以$DEFAULT作为其值 *
${var+OTHER} 如果var声明了, 那么其值就是$OTHER, 否则就为null字符串
${var:+OTHER} 如果var被设置了, 那么其值就是$OTHER, 否则就为null字符串
${var?ERR_MSG} 如果var没被声明, 那么就打印$ERR_MSG *
${var:?ERR_MSG} 如果var没被设置, 那么就打印$ERR_MSG *
${!varprefix*} 匹配之前所有以varprefix开头进行声明的变量
${[email protected]} 匹配之前所有以varprefix开头进行声明的变量

加入了“*”  不是意思是: 当然, 如果变量var已经被设置的话, 那么其值就是$var.

 

 

二、字符串操作(长度,读取,替换)

表达式 含义

${#string} $string的长度
${string:position} 在$string中, 从位置$position开始提取子串
${string:position:length} 在$string中, 从位置$position开始提取长度为$length的子串
${string#substring} 从变量$string的开头, 删除最短匹配$substring的子串
${string##substring} 从变量$string的开头, 删除最长匹配$substring的子串
${string%substring} 从变量$string的结尾, 删除最短匹配$substring的子串
${string%%substring} 从变量$string的结尾, 删除最长匹配$substring的子串
${string/substring/replacement} 使用$replacement, 来代替第一个匹配的$substring
${string//substring/replacement} 使用$replacement, 代替所有匹配的$substring
${string/#substring/replacement} 如果$string的前缀匹配$substring, 那么就用$replacement来代替匹配到的$substring
${string/%substring/replacement} 如果$string的后缀匹配$substring, 那么就用$replacement来代替匹配到的$substring

说明:”* $substring”可以是一个正则表达式.

 

实例:

 

读取:

Java代码  收藏代码
  1. $ echo ${abc-‘ok’}  
  2. ok
  3. $ echo $abc
  4. $ echo ${abc=‘ok’}  
  5. ok
  6. $ echo $abc
  7. ok
  8. #如果abc 没有声明“=” 还会给abc赋值。
  9. $ var1=11;var2=12;var3=  
  10. $ echo ${[email protected]}
  11. var1 var2 var3
  12. $ echo ${!v*}
  13. var1 var2 var3
  14. #${!varprefix*}与${[email protected]}相似,可以通过变量名前缀字符,搜索已经定义的变量,无论是否为空值。

 

1,取得字符串长度

C代码  收藏代码
  1. string=abc12342341          //等号二边不要有空格  
  2. echo ${#string}             //结果11  
  3. expr length $string         //结果11  
  4. expr “$string” : “.*”       //结果11 分号二边要有空格,这里的:根match的用法差不多  

2,字符串所在位置

C代码  收藏代码
  1. expr index $string ‘123’    //结果4 字符串对应的下标是从1开始的   
C代码  收藏代码
  1. str=“abc”  
  2. expr index $str “a”  # 1  
  3. expr index $str “b”  # 2  
  4. expr index $str “x”  # 0  
  5. expr index $str “”   # 0   

 

这个方法让我想起来了js的indexOf,各种语言对字符串的操作方法大方向都差不多,如果有语言基础的话,学习shell会很快的。

 

3,从字符串开头到子串的最大长度

C代码  收藏代码
  1. expr match $string ‘abc.*3’ //结果9    

个人觉得这个函数的用处不大,为什么要从开头开始呢。

 

4,字符串截取

C代码  收藏代码
  1. echo ${string:4}      //2342341  从第4位开始截取后面所有字符串    
  2. echo ${string:3:3}    //123      从第3位开始截取后面3位    
  3. echo ${string:3:6}    //123423   从第3位开始截取后面6位    
  4. echo ${string: -4}    //2341  :右边有空格   截取后4位    
  5. echo ${string:(-4)}   //2341  同上    
  6. expr substr $string 3 3   //123  从第3位开始截取后面3位    

 

C代码  收藏代码
  1. str=“abcdef”  
  2. expr substr “$str” 1 3  # 从第一个位置开始取3个字符, abc  
  3. expr substr “$str” 2 5  # 从第二个位置开始取5个字符, bcdef   
  4. expr substr “$str” 4 5  # 从第四个位置开始取5个字符, def  
  5. echo ${str:2}           # 从第二个位置开始提取字符串, bcdef
  6. echo ${str:2:3}         # 从第二个位置开始提取3个字符, bcd
  7. echo ${str:(-6):5}        # 从倒数第二个位置向左提取字符串, abcde
  8. echo ${str:(-4):3}      # 从倒数第二个位置向左提取6个字符, cde

 

上面的方法让我想起了,php的substr函数,后面截取的规则是一样的。

 

5,匹配显示内容

C代码  收藏代码
  1. //例3中也有match和这里的match不同,上面显示的是匹配字符的长度,而下面的是匹配的内容    
  2. expr match $string ‘\([a-c]*[0-9]*\)’  //abc12342341    
  3. expr $string : ‘\([a-c]*[0-9]\)’       //abc1    
  4. expr $string : ‘.*\([0-9][0-9][0-9]\)’ //341 显示括号中匹配的内容    

 

这里括号的用法,是不是根其他的括号用法有相似之处呢,

 

6,截取不匹配的内容

C代码  收藏代码
  1. echo ${string#a*3}     //42341  从$string左边开始,去掉最短匹配子串    
  2. echo ${string#c*3}     //abc12342341  这样什么也没有匹配到    
  3. echo ${string#*c1*3}   //42341  从$string左边开始,去掉最短匹配子串    
  4. echo ${string##a*3}    //41     从$string左边开始,去掉最长匹配子串    
  5. echo ${string%3*1}     //abc12342  从$string右边开始,去掉最短匹配子串    
  6. echo ${string%%3*1}    //abc12     从$string右边开始,去掉最长匹配子串    
C代码  收藏代码
  1. str=“abbc,def,ghi,abcjkl”  
  2. echo ${str#a*c}     # 输出,def,ghi,abcjkl  一个井号(#) 表示从左边截取掉最短的匹配 (这里把abbc字串去掉)
  3. echo ${str##a*c}    # 输出jkl,             两个井号(##) 表示从左边截取掉最长的匹配 (这里把abbc,def,ghi,abc字串去掉)
  4. echo ${str#“a*c”}   # 输出abbc,def,ghi,abcjkl 因为str中没有”a*c”子串  
  5. echo ${str##“a*c”}  # 输出abbc,def,ghi,abcjkl 同理  
  6. echo ${str#*a*c*}   # 空
  7. echo ${str##*a*c*}  # 空
  8. echo ${str#d*f)     # 输出abbc,def,ghi,abcjkl,
  9. echo ${str#*d*f}    # 输出,ghi,abcjkl
  10. echo ${str%a*l}     # abbc,def,ghi  一个百分号(%)表示从右边截取最短的匹配
  11. echo ${str%%b*l}    # a             两个百分号表示(%%)表示从右边截取最长的匹配
  12. echo ${str%a*c}     # abbc,def,ghi,abcjkl

这里要注意,必须从字符串的第一个字符开始,或者从最后一个开始,可以这样记忆, 井号(#)通常用于表示一个数字,它是放在前面的;百分号(%)卸载数字的后面; 或者这样记忆,在键盘布局中,井号(#)总是位于百分号(%)的左边(即前面)  。

 

7,匹配并且替换

C代码  收藏代码
  1. echo ${string/23/bb}   //abc1bb42341  替换一次    
  2. echo ${string//23/bb}  //abc1bb4bb41  双斜杠替换所有匹配    
  3. echo ${string/#abc/bb} //bb12342341   #以什么开头来匹配,根php中的^有点像    
  4. echo ${string/%41/bb}  //abc123423bb  %以什么结尾来匹配,根php中的$有点像   

 

C代码  收藏代码
  1. str=“apple, tree, apple tree”  
  2. echo ${str/apple/APPLE}   # 替换第一次出现的apple
  3. echo ${str//apple/APPLE}  # 替换所有apple  
  4. echo ${str/#apple/APPLE}  # 如果字符串str以apple开头,则用APPLE替换它
  5. echo ${str/%apple/APPLE}  # 如果字符串str以apple结尾,则用APPLE替换它
C代码  收藏代码
  1. $ test=‘c:/windows/boot.ini’  
  2. $ echo ${test/\//\\}  
  3. c:\windows/boot.ini
  4. $ echo ${test//\//\\}  
  5. c:\windows\boot.ini
  6. #${变量/查找/替换值} 一个“/”表示替换第一个,”//”表示替换所有,当查找中出现了:”/”请加转义符”\/”表示。  

8. 比较

C代码  收藏代码
  1. [[ “a.txt” == a* ]]        # 逻辑真 (pattern matching)  
  2. [[ “a.txt” =~ .*\.txt ]]   # 逻辑真 (regex matching)  
  3. [[ “abc” == “abc” ]]       # 逻辑真 (string comparision)   
  4. [[ “11” < “2” ]]           # 逻辑真 (string comparision), 按ascii值比较  

9. 连接

C代码  收藏代码
  1. s1=“hello”  
  2. s2=“world”  
  3. echo ${s1}${s2}   # 当然这样写 $s1$s2 也行,但最好加上大括号
10. 字符串删除

Java代码  收藏代码
  1. $ test=‘c:/windows/boot.ini’  
  2. $ echo ${test#/}
  3. c:/windows/boot.ini
  4. $ echo ${test#*/}
  5. windows/boot.ini
  6. $ echo ${test##*/}
  7. boot.ini
  8. $ echo ${test%/*} 
  9. c:/windows 
  10. $ echo ${test%%/*} 
  11. #${变量名#substring正则表达式}从字符串开头开始配备substring,删除匹配上的表达式。 
  12. #${变量名%substring正则表达式}从字符串结尾开始配备substring,删除匹配上的表达式。 
  13. #注意:${test##*/},${test%/*} 分别是得到文件名,或者目录地址最简单方法。   

转自:https://www.cnblogs.com/gaochsh/p/6901809.html

一次java war包在tomcat启动后服务乱码问题解决

 java, tomcat  一次java war包在tomcat启动后服务乱码问题解决已关闭评论
3月 312021
 

一个运行于tomcat下的war包,忽然服务出现了乱码,问题排查如下:

  • 检查点一:
  • 1. 检查tomcat 的config目录下server.xml 文件:

<Connector ……
redirectPort=”443″ URIEncoding=”UTF-8″ />, 发现问题不在这里

 

  • 检查点二:
  • 2.1. 命令行下执行: ps -ef | grep java  获取java运行所在的pid为6724

[[email protected]]# ps -ef | grep java
root 6724 1 3 14:30 pts/0 00:01:04 /usr/local/jdk/bin/java –

  • 2.2. 查看问题机器的jvm的编码格式

[[email protected]]# jinfo 6724 |grep enc
sun.jnu.encoding = ANSI_X3.4-1968
file.encoding.pkg = sun.io
sun.io.unicode.encoding = UnicodeLittle
file.encoding = ANSI_X3.4-1968

果然问题出在这里。 可以在tomcat的bin目录下的catalina.sh,添加给 JAVA_OPTS 的多添加两个参数

-Dsun.jnu.encoding=UTF-8 -Dfile.encoding=UTF-8

重启tomcat,DONE!

 

 

 

linux下取出两个文本文件中相同的行或不同的行,计算行数,排除重复行等

 linux  linux下取出两个文本文件中相同的行或不同的行,计算行数,排除重复行等已关闭评论
3月 302021
 

1.  两个文本文件,现在想取出其中的相同的行或不同的行,可以以下操作:(比如文件a.txt和文件b.txt)

grep -wf a.txt b.txt   (取出相同的行)

grep -wvf a.txt b.txt   (取出不相同的行)

 

2.  如果要计算取出的行一共多少行? 如下:

grep -wf a.txt b.txt  | wc -l   (相同的行一共有多少行)

 

3. 如果a.txt有重复行数据,可以使用下面命令过滤并产生新文件a1.txt,然后再用上面的方法比较

sort a.txt | uniq -c | sort -rn | cut -c 9-  > a1.txt  (sort先排序,uniq再计数,sort -rn再按重复数量倒序, cut再截取,后导出)