日期: 2021 年 5 月 24 日

苹果IOS手机设置BurpSuite抓包(详细步骤)

0×01 添加BP的Proxy Listeners
点击Add添加

%title插图%num

端口填写8080,然后选择Specific address,选手机和电脑同时在的那个IP区段

IP具体查询方式使用Win+R

%title插图%num

输入ipconfig

然后看这个

%title插图%num

在Specific address中选择这个IP即可,配置完之后记得勾选

 

0×02 在Intercept Client Requests中勾选
Intercept requests based on the following rules和Automatically……edited,截图如下

%title插图%num

至此电脑端配置完成

0×03 在Safari浏览器中打开网址http://burp下载证书
打开网页后点击右上角的CA证书进行下载,省去苹果手机的邮箱操作。

 

0×04 手机管理证书
首先打开设置->通用->关于本机->描述文件

在里面配置好下载的证书

%title插图%num

然后在关于本机->证书信任设置里面开启信任

%title插图%num

0×05 手机设置代理
点开无线局域网(注意手机和电脑处于同一网络)

点击连接到的WIFI的旁边蓝色圆框(里面是i)

%title插图%num

配置代理改为手动

%title插图%num

服务器填写IP地址

IP地址和端口就是刚刚在电脑上用ipconfig查找到的

%title插图%num

至此就可以成功抓包了

 

iOS下的实际网络连接状态检测:RealReachability

序言
网络连接状态检测对于我们的iOS app开发来说是一个非常通用的需求。为了更好的用户体验,我们会在无网络时展现本地或者缓存的内容,并对用户进行合适的提示。对*大部分iOS开发者来说,从苹果示例代码改变而来的各种Reachablity框架是实现这个需求的普遍选择,比如这个库。但事实上,基于此方案的所有实现,都无法帮助我们检测真正的网络连接状态,它们能检测的只是本地连接状态;这种情况包括但不限于如下场景:
1.现在很流行的公用wifi,需要网页鉴权,鉴权之前无法上网,但本地连接已经建立;
2.存在了本地网络连接,但信号很差,实际无法连接到服务器;
3.iOS连接的路由设备本身没有连接外网。
cocoachina上已有很多网友对此进行提问和吐槽,比如:
如何判断设备是否真正连上互联网?而不是只有网络连接
[Reachability reachabilityWithHostName:]完全没用!

苹果的Reachability示例中有如下说明,告诉我们其能力受限于此:
“Reachability cannot tell your application if you can connect to a particular host, only that an interface is available that might allow a connection, and whether that interface is the WWAN.”
苹果的SCNetworkReachability API则告诉了我们更多:
“Reachability does not guarantee that the data packet will actually be received by the host. ”
而Reachability相关的所有框架在底层实现都是通过SCNetworkReachability进行检测,所以无法检测实际网络连接情况。
有鉴于此,笔者希望打造一个通用、简单、可靠的实际网络连接状态检测框架,于是RealReachability诞生了。

RealReachability简单介绍
RealReachability是笔者几个月前发布到github的开源库,目前有1000多个star,200多个fork,几经修改完善后,当前pod版本为1.1.5。
项目地址如下:
https://github.com/dustturtle/RealReachability。
此框架开发的初衷来源于项目实际需求,离线模式对网络连接状态的要求比较苛刻,且实际场景经常会遇到“伪连接”的情况,Reachability面对此场景力不从心。多方研究后引入了ping能力(此方案流量开销*小,也*简单),实现了简单的实际网络连接监测;后面经过提炼和优化,就有了这个框架。可以告诉大家的是,这个框架在appstore上架应用中已经经受了考验,目前也不断完善中,追求稳定的朋友可以使用*新的pod版本(修复了已知的*大部分问题,参考demo的使用方式即可)。

集成和使用介绍
集成
*简便的集成方法当属pod: pod ‘RealReachability’。
手动集成:将RealReachability文件夹加入到工程即可。
依赖:Xcode5.0+,支持ARC, iOS6+.项目需要引入SystemConfiguration.framework.
使用介绍
其接口的设计和调用方法和Reachability非常相似,大家可以无缝上手,非常方便。
开启网络监听(建议在didFinishLaunchingWithOptions中进行监听):

– (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
[GLobalRealReachability startNotifier];
return YES;
}

监听网络变化通知:
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(networkChanged:)
name:kRealReachabilityChangedNotification
object:nil];

通知回调代码示例:
– (void)networkChanged:(NSNotification *)notification
{
RealReachability *reachability = (RealReachability *)notification.object;
ReachabilityStatus status = [reachability currentReachabilityStatus];
NSLog(@”currentStatus:%@”,@(status));
}

触发实时网络状态查询代码示例:
[GLobalRealReachability reachabilityWithBlock:^(ReachabilityStatus status) {
switch (status)
{
case NotReachable:
{
// case NotReachable handler
break;
}

case ReachableViaWiFi:
{
// case ReachableViaWiFi handler
break;
}

case ReachableViaWWAN:
{
// case ReachableViaWWAN handler
break;
}

default:
break;
}
}];

查询当前实际网络连接状态:
ReachabilityStatus status = [reachability currentReachabilityStatus];

设置ping检测用的host服务器地址(可选):
注意:这里你需要确保该服务器支持ping操作。不设置的情况下我们默认使用www.apple.com作为ping服务器。
可以使用自己的服务器,或者使用稳定、可靠的网络地址(比如百度、qq等)。设置示例如下:

GLobalRealReachability.hostForPing = @”www.apple.com”;

获取当前的数据网络连接类型(高级功能):
WWANAccessType accessType = [GLobalRealReachability currentWWANtype];

该类型可以被用来优化应用程序的体验,比如不同的网络类型下设置不同的网络超时时间等,关于此方面的优化方案,可以参考携程给出的分享:http://www.infoq.com/cn/articles/how-ctrip-improves-app-networking-performance

Demo:
我们在github的repository中已经包含了简单的Demo工程,直接下载运行即可。相关的Api调用也可以参考demo中的实现。
demo截图:

RealReachability的实现原理
RealReachability架构图:

RealReachability主要包含3大模块:connection、ping、FSM;
其中Ping模块通过对同样是苹果提供的ping样例代码进行了封装,connection模块实现了基于SCNetworkReachability的本地状态检测,FSM模块是有限状态机。通过FSM的状态管理控制connection模块和Ping模块协同工作,并通过可配置的定时策略等业务逻辑优化,*终得到了我们的实现。
PS:其中connection模块和ping模块也可独立使用,分别提供本地网络检测和ping的能力,感兴趣的读者也可以尝试(调用方式请参考RealReachability开源代码)。

结束语
希望这个框架能够帮助到大家的iOS开发! 遇到任何疑问或者使用上的问题,都可以联系我,期待与您交流iOS开发技术(可以直接在我的博客提问或者email给我).

更新:github上的master版本已经支持IPV6,请开发者尽快升级。

什么是端口转发?什么是端口映射?如何设置端口映射?

很多朋友多次提到端口映射与端口转发的区别?这二者如果不细区分的,是容易弄混,今天我们来看下。

一、什么是端口映射

端口映射:端口映射就是将内网中的主机的一个端口映射到外网主机的一个端口,提供相应的服务。当用户访问外网IP的这个端口时,服务器自动将请求映射到对应局域网内部的机器上。

比如:

我们在内网中有一台Web服务器,但是外网中的用户是没有办法直接访问该服务器的。于是我们可以在路由器上设置一个端口映射,只要外网用户访问路由器ip的80端口,那么路由器会把自动把流量转到内网Web服务器的80端口上。并且,在路由器上还存在一个Session,当内网服务器返回数据给路由器时,路由器能准确的将消息发送给外网请求用户的主机。在这过程中,路由器充当了一个反向代理的作用,他保护了内网中主机的安全。

 

什么是端口转发?什么是端口映射?如何设置端口映射?

 

 

 

二、什么是端口转发

端口转发:端口转发(Port forwarding),有时被叫做隧道,是安全壳(SSH) 为网络安全通信使用的一种方法。

比如,我们现在在内网中,是没有办法直接访问外网的。但是我们可以通过路由器的NAT方式访问外网。假如我们内网现在有100台主机,那么我们现在都是通过路由器的这一个公网IP和外网通信的。那么,当互联网上的消息发送回来时,路由器是怎么知道这个消息是给他的,而另外消息是给你的呢?这就要我们的ip地址和路由器的端口进行绑定了,这时,在路由器中就会有一个内网ip和路由器端口对应的一张表。当路由器的10000端口收到消息时,就知道把消息发送给他,而当20000端口收到消息时,就知道把消息发送给你。这就是端口转发,其转发一个端口收到的流量,给另一个主机。

 

什么是端口转发?什么是端口映射?如何设置端口映射?

 

 

三、端口映射与端口转发的区别

这里举个例子方便大家就理解了:

端口转发:转发的重点在“转”上面。送快递的过来,把所有快递都交给门口保安,保安再根据每件快递上的收件人来分发。

端口映射:就是在大门口给每个人装个柜子,送快递的直接按收件人名字,把快递放到对应的柜子里面。

四、如何利用花生壳进行端口映射?实现外网访问本地服务器

映射实现外网访问本地服务器,当你的内外的内容需要外网的用户进行访问时,可以进行端口映射。

 

下载花生壳,注册后然后会分配到一个二级域名,设置内外网端口,或内网主机。

 

什么是端口转发?什么是端口映射?如何设置端口映射?

 

 

什么是端口转发?什么是端口映射?如何设置端口映射?

 

 

其中内网主机IP地址是你的内网同网段的ip地址,怎么查看自己的内网IP?请win键搜索框中输入cmd,打开命令行输入ipconfig,就可以查看到内网的ip段了。

然后设置路由器的端口映射功能。

 

什么是端口转发?什么是端口映射?如何设置端口映射?

 

 

有的路由器是直接在虚拟服务器中设置。

 

什么是端口转发?什么是端口映射?如何设置端口映射?

 

 

设置后,重启路由器,当然,如果端口被其它的占用了,可以使用其它的端口。

可以使用域名 25c8898k47.zicp.vip:55694 进行访问内网服务器。

五、路由器在网关中设置了端口映射,但外网访问不了的原因

1、首先检查您设置的端口映射是否正确映射到您内网的服务器。即您设置的转发规则的IP地址是否为您服务器的IP地址。

2、无法访问内网的服务器,请检查服务器是否开启了。您可以在局域网内使用其他的计算机访问一下您的服务器,确认服务器是否开启,并能成功访问。

3、请您检查您是否映射了服务器所需的所有端口,有可能没有完全设置访问服务器所需的端口,导致访问失败。您可以通过开启“DMZ主机”来检测一下,是否端口没有映射完全。若开启DMZ主机后,能正常访问服务器,则可能是端口添加不完全。

4、网关上是否设置了“防火墙”,是否将服务器通信的端口过滤,导致访问失败。

5、本机防火墙:因本地网络为信任区域,防火墙并不会阻止本地网络的访问,若访问源地址为外网地址(非信任区域地址),则可能会被防火墙拦截而无法访问。

什么是端口转发?什么是端口映射?如何设置端口映射?

 

 

6、服务商将相应端口屏蔽:在网关WAN口接一台电脑模拟内外网环境,若WAN口所接电脑可以正常访问内网服务器,但在Internet上无法访问,则可能是服务器将相应端口屏蔽导致虚拟服务器无法访问;

*后补充下:

1、若是80端口的WEB服务器,一定要注意网关的管理端口是否更改,否则两者会冲突;

2、检查虚拟服务器是否映射成功一定要从外网进行访问,不要从内网用WAN口IP去访问,这样在测试的时候是成功的,但实际外网并不能成功访问。

3、特殊的服务器,如网络摄像机及特殊的应用软件等,如果客户自己都不清楚通讯的端口,可先尝试DMZ主机以确认是否可以使用,同时建议客户咨询软件提供商。

iOS通过dSYM文件分析crash

我们在ios开发中会碰到的很多crash问题,如果Debug调试模式的话,我们可以往往很容易的根据log的输出定位到导致crash的原因,但对于已经上线的应用,或者是release环境包导致的crash,我们就需要一些特殊的手段来通过crash log进行分析定位了。

通过参考网上的一些资料,总结了一下,下面介绍一下通过dSYM文件以及crash log分析定位的方法。

1.导出crash log

通过Xcode的Organizer查看某台iphone设备的DeviceLog,选择需要的crash log,导出XXX.crash文件。

2.找到对应的app文件

找到当前iphone设备上安装的ipa文件,更改文件后缀名为zip,解压后得到Payload文件夹,你需要的app文件就在其中了。

3.找到对应build版本的dSYM文件

dSYM文件是iOS编译后保存16进制函数地址映射信息的文件,每次应用程序build后,都会生成对应的xxx.app, xxx.app.dSYM文件。

4.确定dSYM、app以及crash文件的关系

每一个xx.app, xxx.app.dSYM文件都拥有相应的uuid,crash文件也有uuid,只有三者uuid一至才表明之三者可以解析出正确的日志文件。
查看xx.app文件的uuid的方法,在terminal中输入命令:

dwarfdump –uuid xxx.app/xxx (xxx工程名)

查看xx.app.dSYM文件的uuid的方法,在terminal中输入命令:

dwarfdump –uuid xxx.app.dSYM (xxx工程名)

而.crash的uuid位于,crash日志中的Binary Images:中的*行尖括号内。如:

armv7 <8bdeaf1a0b233ac199728c2a0ebb4165>

将对应的xxx.app.dSYM文件以及xxx.app文件以及xxx.crash文件拷贝到同一文件夹中,如:~/Desktop/DebugLog。

5.通过symbolicatecrash分析crash文件

Xcode有自带的symbolicatecrash工具,可以通过dSYM文件将crash文件中的16进制地址转换成可读的函数地址。symbolicatecrash工具位于:
/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/Library/PrivateFrameworks/DTDeviceKit.framework/Versions/A/Resources/symbolicatecrash(Xcode 4.5)
/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/Library/PrivateFrameworks/DTDeviceKitBase.framework/Versions/A/Resources/symbolicatecrash(Xcode 5.0)

该文件是隐藏文件,可以通过如下命令查找并拷贝到系统目录下,并建立快捷方式。

1)打开终端,进入到symbolicatecrash工具所在的文件夹目录

cd /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/Library/PrivateFrameworks/DTDeviceKitBase.framework/Versions/A/Resources/

2)查找确认是否存在symbolicatecrash

ls -al | grep symbolicatecrash

3)将symbolicatecrash工具拷贝到/usr/bin目录下

sudo cp symbolicatecrash /usr/bin/symbolicatecrash

4)设置DEVELOPER_DIR系统变量

cd ~/

vi .bash_profile

并输入如下内容
export DEVELOPER_DIR=”/Applications/Xcode.app/Contents/Developer”

保存并退出

source .bash_profile

5)重启终端,确认是否已正确设置DEVELOPER_DIR系统变量

echo $DEVELOPER_DIR

查看输出结果是否为/Applications/Xcode.app/Contents/Developer

6)查看PATH系统变量是否存在如下路径/usr/bin

echo $PATH

7)如果PATH不存在如下路径/usr/bin,可在~/.bash_profile中添加如下代码

export PATH=”/usr/bin:$PATH”

保存并退出

source .bash_profile

8)上述准备工作完成后,进入dSYM和crash文件对应的文件夹目录,如

cd ~/Desktop/DebugLog

9)执行如下命令,即可正确解析crash文件

symbolicatecrash xxx.crash xxx.app.dSYM > test.txt

注意:symbolicatecrash的参数顺序,否则会报类似如下错误

Use of uninitialized value $data in substitution (s///) at /usr/bin/symbolicatecrash line 678.

Use of uninitialized value $data in substitution (s///) at /usr/bin/symbolicatecrash line 681.

Use of uninitialized value $data in substitution (s///) at /usr/bin/symbolicatecrash line 685.

Use of uninitialized value in pattern match (m//) at /usr/bin/symbolicatecrash line 404.

Use of uninitialized value in scalar assignment at /usr/bin/symbolicatecrash line 418.

No crash report version in XXX.app.dSYM/ at /usr/bin/symbolicatecrash line 954.

今天就先到这里,希望对大家有所帮助。

将ios代码在后台(子线程)执行的两种简单方式

a.使用gcd

//切换到线程中执行
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
// Do the work in background
[[MyManager shareInstance] myMethod];
});

b.使用performSelectorInBackground方法
[self performSelectorInBackground:@selector(myMethod:)
withObject:myObj];

对应的原始方法:- (void)myMethod:(id)myObj;

其中方式a适应性强,使用方便,唯一可能是缺点的地方就是使用了gcd+block,代码看上去略长,不过习惯了这种语法也不是什么问题,推荐方式a.

ios 代码写Button小结

本文实现的是一个不用拖控件,而是用代码写出一个按钮,然后点击弹出一个警告信息,有人问那么好的IB工具不用却去苦逼的写代码呢?因为IB高度集成开发工具,拖出的控件帮我省了很大麻烦,这个过程农民工也可以干,但是作为初学者,IB是个比较高层的东西,我们是不是应该了解一下IB底层的东西呢,如果一味追求方便快捷,哪天突然有人问怎么用代码写出来,咱岂不是要被鄙视了;所以吧,初学者不要学懒,多写代码提高我们的编程能力,当我们在开发项目或者在公司工作去用IB,来帮我们节省时间提高效率;

初始化视图代码,绘制了一个距原点(100,100)的140×50像素的按钮,有一点需要注意的是iphone的远点坐标是在左上角,屏幕640×480像素,不过现在用个是Retina分辨率,画质更加细腻;

– (void)viewDidLoad
{
[super viewDidLoad];

// 设置按钮类型,此处为圆角按钮
UIButton *writeButton = [UIButton buttonWithType:UIButtonTypeRoundedRect];
// 设置和大小
CGRect frame = CGRectMake(100.0f, 100.0f, 140.0f, 50.0f);
// 将frame的位置大小复制给Button
writeButton.frame = frame;

//———————————————–
// 给Button添加标题
[writeButton setTitle:@”代码按钮” forState:UIControlStateNormal];
// 设置按钮背景颜色
writeButton.backgroundColor = [UIColor clearColor];
// 设置按钮标题文字对齐方式,此处为左对齐
writeButton.contentHorizontalAlignment =UIControlContentHorizontalAlignmentLeft;
//使文字距离做边框保持10个像素的距离。
writeButton.contentEdgeInsets = UIEdgeInsetsMake(0,30, 0, 0);
//—————————————————-

/******************************************************
//此处类容目的掩饰代码代码操作按钮一些属性,如果设置按钮背景为图片可以将此处注释取消,注释掉上没横线范围类代码,进行测试

// 设置按钮背景图片
UIImage *image= [UIImage imageNamed:@”background.png”];

[writeButton setBackgroundImage:image forState:UIControlStateNormal];
// 按钮的相应事件

*****************************************************/
[writeButton addTarget:self action:@selector(buttonClicked:)forControlEvents:UIControlEventTouchUpInside];

[self.view addSubview:writeButton];
}

UIButton *writeButton = [UIButton buttonWithType:UIButtonTypeRoundedRect];
设置按钮类型,按钮类型定义在一个枚举类型中
typedef enum {

UIButtonTypeCustom = 0, // 没有风格

UIButtonTypeRoundedRect, // 圆角风格按钮

UIButtonTypeDetailDisclosure, //

UIButtonTypeInfoLight, // 明亮背景的信息按钮

UIButtonTypeInfoDark, // 黑暗背景的信息按钮

UIButtonTypeContactAdd, //

} UIButtonType;

截图是每个按钮对应枚举类型中的风格,

%title插图%num

%title插图%num

%title插图%num

%title插图%num

%title插图%num

%title插图%num
但是考虑的ios开发中,为了界面美观一般设置背景图片,代替按钮的标题设置,此处推荐一个所搜icon的网址,里面有基本用的icon素材,个人觉得不错,给分享下

http://www.easyicon.cn/点击打开链接;

在点击按钮是按钮是凹下去,然后弹起才触发起事件,按钮的状态有:

UIControlEventTouchDown // 按下

UIControlEventTouchDownRepeat // 多次按下

UIControlEventTouchDragInside // 保持按下然后在按钮及其一定的外围拖动

UIControlEventTouchDragOutside // 保持按下,在按钮外面拖动

UIControlEventTouchDragEnter // DragOutside进入DragInside触发

UIControlEventTouchDragExit // in到out触发

UIControlEventTouchUpInside // 在按钮及其一定外围内松开

UIControlEventTouchUpOutside // 按钮外面松开

UIControlEventTouchCancel // 点击取消

//弹出一个警告,一般都这样写
-(void) buttonClicked:(id)sender
{
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@”提示” message:@”你点击了一个按钮” delegate:self cancelButtonTitle:@”OK” otherButtonTitles: nil];
[alert show];
}

此处弹出的一个警告,主要用到UIAlertView这个类,initWithTitle初始化标题,message是弹出警告类容,提示你做了什么事,delegate是委托代理,此处不需要其他类做什么事,自个完全能搞定,所以设置为self,类似于C++中的this指针,cancelButtonTitle这个一看就能明白,取消按钮的标题是什么了,otherButtonTitles设置其他按钮,也就是说你需要更多按钮支持的时候,此处不需要,只要一个nil就好了,就如还需要其他的,你可以添加代码假如: otherButtonTitles:@”test1″ ,@”test2″ ,@”test3″ ,@”test4″ , nil,运行的效果就是这样:
%title插图%num

本程序运行效果:
%title插图%num

%title插图%num

5种分布式事务解决方案优缺点对比

背景

分布式事务是企业集成中的一个技术难点,也是每一个分布式系统架构中都会涉及到的一个东西,特别是在微服务架构中,几乎可以说是无法避免。

ACID

指数据库事务正确执行的四个基本要素:

  1. 原子性(Atomicity)
  2. 一致性(Consistency)
  3. 隔离性(Isolation)
  4. 持久性(Durability)

CAP

CAP原则又称CAP定理,指的是在一个分布式系统中,一致性(Consistency)、可用性(Availability)、分区容忍性(Partition tolerance)。CAP 原则指的是,这三个要素*多只能同时实现两点,不可能三者兼顾。

  • 一致性:在分布式系统中的所有数据备份,在同一时刻是否同样的值。
  • 可用性:在集群中一部分节点故障后,集群整体是否还能响应客户端的读写请求。
  • 分区容忍性:以实际效果而言,分区相当于对通信的时限要求。系统如果不能在时限内达成数据一致性,就意味着发生了分区的情况,必须就当前操作在C和A之间做出选择。

BASE理论

BASE理论是对CAP中的一致性和可用性进行一个权衡的结果,理论的核心思想就是:我们无法做到强一致,但每个应用都可以根据自身的业务特点,采用适当的方式来使系统达到*终一致性。

  • Basically Available(基本可用)
  • Soft state(软状态)
  • Eventually consistent(*终一致性)

解决方案

01 两阶段提交(2PC)

两阶段提交2PC是分布式事务中*强大的事务类型之一,两段提交就是分两个阶段提交,*阶段询问各个事务数据源是否准备好,第二阶段才真正将数据提交给事务数据源。

为了保证该事务可以满足ACID,就要引入一个协调者(Cooradinator)。其他的节点被称为参与者(Participant)。协调者负责调度参与者的行为,并*终决定这些参与者是否要把事务进行提交。处理流程如下:

5种分布式事务解决方案优缺点对比

 

阶段一

a) 协调者向所有参与者发送事务内容,询问是否可以提交事务,并等待答复。

b) 各参与者执行事务操作,将 undo 和 redo 信息记入事务日志中(但不提交事务)。

c) 如参与者执行成功,给协调者反馈 yes,否则反馈 no。

阶段二

如果协调者收到了参与者的失败消息或者超时,直接给每个参与者发送回滚(rollback)消息;否则,发送提交(commit)消息。两种情况处理如下:

情况1:当所有参与者均反馈 yes,提交事务

a) 协调者向所有参与者发出正式提交事务的请求(即 commit 请求)。

b) 参与者执行 commit 请求,并释放整个事务期间占用的资源。

c) 各参与者向协调者反馈 ack(应答)完成的消息。

d) 协调者收到所有参与者反馈的 ack 消息后,即完成事务提交。

情况2:当有一个参与者反馈 no,回滚事务

a) 协调者向所有参与者发出回滚请求(即 rollback 请求)。

b) 参与者使用阶段 1 中的 undo 信息执行回滚操作,并释放整个事务期间占用的资源。

c) 各参与者向协调者反馈 ack 完成的消息。

d) 协调者收到所有参与者反馈的 ack 消息后,即完成事务。

问题

1) 性能问题:所有参与者在事务提交阶段处于同步阻塞状态,占用系统资源,容易导致性能瓶颈。

2) 可靠性问题:如果协调者存在单点故障问题,或出现故障,提供者将一直处于锁定状态。

3) 数据一致性问题:在阶段 2 中,如果出现协调者和参与者都挂了的情况,有可能导致数据不一致。

优点:尽量保证了数据的强一致,适合对数据强一致要求很高的关键领域。(其实也不能100%保证强一致)。

缺点:实现复杂,牺牲了可用性,对性能影响较大,不适合高并发高性能场景。

02 三阶段提交(3PC)

三阶段提交是在二阶段提交上的改进版本,3PC*关键要解决的就是协调者和参与者同时挂掉的问题,所以3PC把2PC的准备阶段再次一分为二,这样三阶段提交。处理流程如下:

5种分布式事务解决方案优缺点对比

 

阶段一

a) 协调者向所有参与者发出包含事务内容的 canCommit 请求,询问是否可以提交事务,并等待所有参与者答复。

b) 参与者收到 canCommit 请求后,如果认为可以执行事务操作,则反馈 yes 并进入预备状态,否则反馈 no。

阶段二

协调者根据参与者响应情况,有以下两种可能。

情况1:所有参与者均反馈 yes,协调者预执行事务

a) 协调者向所有参与者发出 preCommit 请求,进入准备阶段。

b) 参与者收到 preCommit 请求后,执行事务操作,将 undo 和 redo 信息记入事务日志中(但不提交事务)。

c) 各参与者向协调者反馈 ack 响应或 no 响应,并等待*终指令。

情况2:只要有一个参与者反馈 no,或者等待超时后协调者尚无法收到所有提供者的反馈,即中断事务

a) 协调者向所有参与者发出 abort 请求。

b) 无论收到协调者发出的 abort 请求,或者在等待协调者请求过程中出现超时,参与者均会中断事务。

阶段三

该阶段进行真正的事务提交,也可以分为以下两种情况。

情况 1:所有参与者均反馈 ack 响应,执行真正的事务提交

a) 如果协调者处于工作状态,则向所有参与者发出 do Commit 请求。

b) 参与者收到 do Commit 请求后,会正式执行事务提交,并释放整个事务期间占用的资源。

c) 各参与者向协调者反馈 ack 完成的消息。

d) 协调者收到所有参与者反馈的 ack 消息后,即完成事务提交。

情况2:只要有一个参与者反馈 no,或者等待超时后协调组尚无法收到所有提供者的反馈,即回滚事务。

a) 如果协调者处于工作状态,向所有参与者发出 rollback 请求。

b) 参与者使用阶段 1 中的 undo 信息执行回滚操作,并释放整个事务期间占用的资源。

c) 各参与者向协调组反馈 ack 完成的消息。

d) 协调组收到所有参与者反馈的 ack 消息后,即完成事务回滚。

优点:相比二阶段提交,三阶段提交降低了阻塞范围,在等待超时后协调者或参与者会中断事务。避免了协调者单点问题。阶段 3 中协调者出现问题时,参与者会继续提交事务。

缺点:数据不一致问题依然存在,当在参与者收到 preCommit 请求后等待 do commite 指令时,此时如果协调者请求中断事务,而协调者无法与参与者正常通信,会导致参与者继续提交事务,造成数据不一致。

03 补偿事务(TCC)

TCC 是服务化的二阶段编程模型,采用的补偿机制:

5种分布式事务解决方案优缺点对比

 

条件:

需要实现确认和补偿逻辑

需要支持幂等

处理流程:

a) Try 阶段主要是对业务系统做检测及资源预留。

这个阶段主要完成:

完成所有业务检查( 一致性 ) 。

预留必须业务资源( 准隔离性 ) 。

Try 尝试执行业务。

b) Confirm 阶段主要是对业务系统做确认提交。

Try阶段执行成功并开始执行 Confirm阶段时,默认 Confirm阶段是不会出错的。即:只要Try成功,Confirm一定成功。

c) Cancel 阶段主要是在业务执行错误,需要回滚的状态下执行的业务取消,预留资源释放。

优点:

性能提升:具体业务来实现控制资源锁的粒度变小,不会锁定整个资源。

数据*终一致性:基于 Confirm 和 Cancel 的幂等性,保证事务*终完成确认或者取消,保证数据的一致性。

可靠性:解决了 XA 协议的协调者单点故障问题,由主业务方发起并控制整个业务活动,业务活动管理器也变成多点,引入集群。

缺点:TCC 的 Try、Confirm 和 Cancel 操作功能要按具体业务来实现,业务耦合度较高,提高了开发成本。

04 本地消息表(消息队列)

其核心思想是将分布式事务拆分成本地事务进行处理。

方案通过在消费者额外新建事务消息表,消费者处理业务和记录事务消息在本地事务中完成,轮询事务消息表的数据发送事务消息,提供者基于消息中间件消费事务消息表中的事务。

5种分布式事务解决方案优缺点对比

 

条件:

服务消费者需要创建一张消息表,用来记录消息状态。

服务消费者和提供者需要支持幂等。

需要补偿逻辑。

每个节点上起定时线程,检查未处理完成或发出失败的消息,重新发出消息,即重试机制和幂等性机制。

处理流程:

1. 服务消费者把业务数据和消息一同提交,发起事务。

2. 消息经过MQ发送到服务提供方,服务消费者等待处理结果。

3. 服务提供方接收消息,完成业务逻辑并通知消费者已处理的消息。

容错处理情况如下:

当步骤1处理出错,事务回滚,相当于什么都没有发生。

当步骤2、3处理出错,由于消息保存在消费者表中,可以重新发送到MQ进行重试。

如果步骤3处理出错,且是业务上的失败,服务提供者发送消息通知消费者事务失败,且此时变为消费者发起回滚事务进行回滚逻辑。

优点:从应用设计开发的角度实现了消息数据的可靠性,消息数据的可靠性不依赖于消息中间件,弱化了对 MQ 中间件特性的依赖。

缺点:与具体的业务场景绑定,耦合性强,不可公用。消息数据与业务数据同库,占用业务系统资源。业务系统在使用关系型数据库的情况下,消息服务性能会受到关系型数据库并发性能的局限。

MQ事务消息(*终一致性)

支持事务消息的MQ,其支持事务消息的方式采用类似于二阶段提交。

基于 MQ 的分布式事务方案其实是对本地消息表的封装,将本地消息表基于 MQ 内部,其他方面的协议基本与本地消息表一致。

5种分布式事务解决方案优缺点对比

 

条件:

a) 需要补偿逻辑

b) 业务处理逻辑需要幂等

处理流程:

c) 消费者向MQ发送half消息。

d) MQ Server将消息持久化后,向发送方ack确认消息发送成功。

e) 消费者开始执行事务逻辑。

f) 消费者根据本地事务执行结果向MQ Server提交二次确认或者回滚。

g) MQ Server收到commit状态则将half消息标记可投递状态。

h) 服务提供者收到该消息,执行本地业务逻辑。返回处理结果。

优点:

消息数据独立存储,降低业务系统与消息系统之间的耦合。

吞吐量优于本地消息表方案。

缺点:

一次消息发送需要两次网络请求(half消息 + commit/rollback)。

需要实现消息回查接口。

05 Sagas事务模型(*终一致性)

Saga模式是一种分布式异步事务,一种*终一致性事务,是一种柔性事务,有两种不同的方式来实现saga事务,*流行的两种方式是:

一、 事件/编排Choreography:没有中央协调器(没有单点风险)时,每个服务产生并聆听其他服务的事件,并决定是否应采取行动。

该实现*个服务执行一个事务,然后发布一个事件。该事件被一个或多个服务进行监听,这些服务再执行本地事务并发布(或不发布)新的事件,当*后一个服务执行本地事务并且不发布任何事件时,意味着分布式事务结束,或者它发布的事件没有被任何Saga参与者听到都意味着事务结束。

5种分布式事务解决方案优缺点对比

 

处理流程:

订单服务保存新订单,将状态设置为pengding挂起状态,并发布名为ORDER_CREATED_EVENT的事件。

支付服务监听ORDER_CREATED_EVENT,并公布事件BILLED_ORDER_EVENT。

库存服务监听BILLED_ORDER_EVENT,更新库存,并发布ORDER_PREPARED_EVENT。

货运服务监听ORDER_PREPARED_EVENT,然后交付产品。*后,它发布ORDER_DELIVERED_EVENT。

*后,订单服务侦听ORDER_DELIVERED_EVENT并设置订单的状态为concluded完成。

假设库存服务在事务过程中失败了。进行回滚:

库存服务产生PRODUCT_OUT_OF_STOCK_EVENT

订购服务和支付服务会监听到上面库存服务的这一事件:

①支付服务会退款给客户。

②订单服务将订单状态设置为失败。

优点:事件/编排是实现Saga模式的自然方式; 它很简单,容易理解,不需要太多的努力来构建,所有参与者都是松散耦合的,因为他们彼此之间没有直接的耦合。如果您的事务涉及2至4个步骤,则可能是非常合适的。

二、 命令/协调orchestrator:中央协调器负责集中处理事件的决策和业务逻辑排序。

saga协调器orchestrator以命令/回复的方式与每项服务进行通信,告诉他们应该执行哪些操作。

5种分布式事务解决方案优缺点对比

 

订单服务保存pending状态,并要求订单Saga协调器(简称OSO)开始启动订单事务。

OSO向收款服务发送执行收款命令,收款服务回复Payment Executed消息。

OSO向库存服务发送准备订单命令,库存服务将回复OrderPrepared消息。

OSO向货运服务发送订单发货命令,货运服务将回复Order Delivered消息。

OSO订单Saga协调器必须事先知道执行“创建订单”事务所需的流程(通过读取BPM业务流程XML配置获得)。如果有任何失败,它还负责通过向每个参与者发送命令来撤销之前的操作来协调分布式的回滚。当你有一个中央协调器协调一切时,回滚要容易得多,因为协调器默认是执行正向流程,回滚时只要执行反向流程即可。

优点:

避免服务之间的循环依赖关系,因为saga协调器会调用saga参与者,但参与者不会调用协调器。

集中分布式事务的编排。

只需要执行命令/回复(其实回复消息也是一种事件消息),降低参与者的复杂性。

在添加新步骤时,事务复杂性保持线性,回滚更容易管理。

如果在*笔交易还没有执行完,想改变有第二笔事务的目标对象,则可以轻松地将其暂停在协调器上,直到*笔交易结束。

可能是把Docker的概念讲的*清楚的一篇文章

转载自:https://juejin.im/post/5b260ec26fb9a00e8e4b031a

本文只是对Docker的概念做了较为详细的介绍,并不涉及一些像Docker环境的安装以及Docker的一些常见操作和命令。

阅读本文大概需要15分钟,通过阅读本文你将知道一下概念:

  • 容器
  • 什么是Docker?
  • Docker思想、特点
  • Docker容器主要解决什么问题
  • 容器 VS 虚拟机
  • Docker基本概念: 镜像(Image),容器(Container),仓库(Repository)

Docker 是世界*的软件容器平台,所以想要搞懂Docker的概念我们必须先从容器开始说起。

一 先从认识容器开始

1.1 什么是容器?

先来看看容器较为官方的解释

一句话概括容器:容器就是将软件打包成标准化单元,以用于开发、交付和部署。

  • 容器镜像是轻量的、可执行的独立软件包 ,包含软件运行所需的所有内容:代码、运行时环境、系统工具、系统库和设置。
  • 容器化软件适用于基于Linux和Windows的应用,在任何环境中都能够始终如一地运行。
  • 容器赋予了软件独立性 ,使其免受外在环境差异(例如,开发和预演环境的差异)的影响,从而有助于减少团队间在相同基础设施上运行不同软件时的冲突。

再来看看容器较为通俗的解释

如果需要通俗的描述容器的话,我觉得容器就是一个存放东西的地方,就像书包可以装各种文具、衣柜可以放各种衣服、鞋架可以放各种鞋子一样。我们现在所说的容器存放的东西可能更偏向于应用比如网站、程序甚至是系统环境。

 

认识容器

 

 

1.2 图解物理机、虚拟机与容器

关于虚拟机与容器的对比在后面会详细介绍到,这里只是通过网上的图片加深大家对于物理机、虚拟机与容器这三者的理解。

物理机

物理机

 

 

虚拟机:

 

虚拟机

 

 

容器:

 

容器

 

 

通过上面这三张抽象图,我们可以大概可以通过类比概括出: 容器虚拟化的是操作系统而不是硬件,容器之间是共享同一套操作系统资源的。虚拟机技术是虚拟出一套硬件后,在其上运行一个完整操作系统。因此容器的隔离级别会稍低一些。


相信通过上面的解释大家对于容器这个既陌生又熟悉的概念有了一个初步的认识,下面我们就来谈谈Docker的一些概念。

二 再来谈谈Docker的一些概念

 

Docker的一些概念

 

 

2.1 什么是Docker?

说实话关于Docker是什么并太好说,下面我通过四点向你说明Docker到底是个什么东西。

  • Docker 是世界*的软件容器平台。
  • Docker 使用 Google 公司推出的 Go 语言 进行开发实现,基于 Linux 内核 的cgroup,namespace,以及AUFS类的UnionFS等技术,对进程进行封装隔离,属于操作系统层面的虚拟化技术。 由于隔离的进程独立于宿主和其它的隔离的进 程,因此也称其为容器。Docke*初实现是基于 LXC.
  • Docker 能够自动执行重复性任务,例如搭建和配置开发环境,从而解放了开发人员以便他们专注在真正重要的事情上:构建杰出的软件。
  • 用户可以方便地创建和使用容器,把自己的应用放入容器。容器还可以进行版本管理、复制、分享、修改,就像管理普通的代码一样。

 

什么是Docker

 

 

2.2 Docker思想

  • 集装箱
  • 标准化: ①运输方式 ② 存储方式 ③ API接口
  • 隔离

2.3 Docker容器的特点

  • 轻量

    在一台机器上运行的多个 Docker 容器可以共享这台机器的操作系统内核;它们能够迅速启动,只需占用很少的计算和内存资源。镜像是通过文件系统层进行构造的,并共享一些公共文件。这样就能尽量降低磁盘用量,并能更快地下载镜像。

  • 标准

    Docker 容器基于开放式标准,能够在所有主流 Linux 版本、Microsoft Windows 以及包括 VM、裸机服务器和云在内的任何基础设施上运行。

  • 安全

    Docker 赋予应用的隔离性不仅限于彼此隔离,还独立于底层的基础设施。Docker 默认提供*强的隔离,因此应用出现问题,也只是单个容器的问题,而不会波及到整台机器。

2.4 为什么要用Docker

  • Docker 的镜像提供了除内核外完整的运行时环境,确保了应用运行环境一致性,从而不会再出现 “这段代码在我机器上没问题啊” 这类问题;——一致的运行环境
  • 可以做到秒级、甚至毫秒级的启动时间。大大的节约了开发、测试、部署的时间。——更快速的启动时间
  • 避免公用的服务器,资源会容易受到其他用户的影响。——隔离性
  • 善于处理集中爆发的服务器使用压力;——弹性伸缩,快速扩展
  • 可以很轻易的将在一个平台上运行的应用,迁移到另一个平台上,而不用担心运行环境的变化导致应用无法正常运行的情况。——迁移方便
  • 使用 Docker 可以通过定制应用镜像来实现持续集成、持续交付、部署。——持续交付和部署

每当说起容器,我们不得不将其与虚拟机做一个比较。就我而言,对于两者无所谓谁会取代谁,而是两者可以和谐共存。

三 容器 VS 虚拟机

简单来说: 容器和虚拟机具有相似的资源隔离和分配优势,但功能有所不同,因为容器虚拟化的是操作系统,而不是硬件,因此容器更容易移植,效率也更高。

3.1 两者对比图

传统虚拟机技术是虚拟出一套硬件后,在其上运行一个完整操作系统,在该系统上再运行所需应用进程;而容器内的应用进程直接运行于宿主的内核,容器内没有自己的内核,而且也没有进行硬件虚拟。因此容器要比传统虚拟机更为轻便.

 

容器 VS 虚拟机

 

 

3.2 容器与虚拟机 (VM) 总结

 

容器与虚拟机 (VM) 总结

 

 

  • 容器是一个应用层抽象,用于将代码和依赖资源打包在一起。 多个容器可以在同一台机器上运行,共享操作系统内核,但各自作为独立的进程在用户空间中运行 。与虚拟机相比, 容器占用的空间较少(容器镜像大小通常只有几十兆),瞬间就能完成启动 。
  • 虚拟机 (VM) 是一个物理硬件层抽象,用于将一台服务器变成多台服务器。 管理程序允许多个 VM 在一台机器上运行。每个VM都包含一整套操作系统、一个或多个应用、必要的二进制文件和库资源,因此 占用大量空间 。而且 VM 启动也十分缓慢 。

通过Docker官网,我们知道了这么多Docker的优势,但是大家也没有必要完全否定虚拟机技术,因为两者有不同的使用场景。虚拟机更擅长于彻底隔离整个运行环境。例如,云服务提供商通常采用虚拟机技术隔离不同的用户。而 Docker通常用于隔离不同的应用 ,例如前端,后端以及数据库。

3.3 容器与虚拟机 (VM)两者是可以共存的

就我而言,对于两者无所谓谁会取代谁,而是两者可以和谐共存。

 

两者是可以共存的

 

 


Docker中非常重要的三个基本概念,理解了这三个概念,就理解了 Docker 的整个生命周期。

四 Docker基本概念

Docker 包括三个基本概念

  • 镜像(Image)
  • 容器(Container)
  • 仓库(Repository)

理解了这三个概念,就理解了 Docker 的整个生命周期

 

Docker 包括三个基本概念

 

 

4.1 镜像(Image)——一个特殊的文件系统

操作系统分为内核和用户空间。对于 Linux 而言,内核启动后,会挂载 root 文件系统为其提供用户空间支持。而Docker 镜像(Image),就相当于是一个 root 文件系统。

Docker 镜像是一个特殊的文件系统,除了提供容器运行时所需的程序、库、资源、配置等文件外,还包含了一些为运行时准备的一些配置参数(如匿名卷、环境变量、用户等)。 镜像不包含任何动态数据,其内容在构建之后也不会被改变。

Docker 设计时,就充分利用 Union FS的技术,将其设计为 分层存储的架构 。 镜像实际是由多层文件系统联合组成。

镜像构建时,会一层层构建,前一层是后一层的基础。每一层构建完就不会再发生改变,后一层上的任何改变只发生在自己这一层。 比如,删除前一层文件的操作,实际不是真的删除前一层的文件,而是仅在当前层标记为该文件已删除。在*终容器运行的时候,虽然不会看到这个文件,但是实际上该文件会一直跟随镜像。因此,在构建镜像的时候,需要额外小心,每一层尽量只包含该层需要添加的东西,任何额外的东西应该在该层构建结束前清理掉。

分层存储的特征还使得镜像的复用、定制变的更为容易。甚至可以用之前构建好的镜像作为基础层,然后进一步添加新的层,以定制自己所需的内容,构建新的镜像。

4.2 容器(Container)——镜像运行时的实体

镜像(Image)和容器(Container)的关系,就像是面向对象程序设计中的 类 和 实例 一样,镜像是静态的定义,容器是镜像运行时的实体。容器可以被创建、启动、停止、删除、暂停等 。

容器的实质是进程,但与直接在宿主执行的进程不同,容器进程运行于属于自己的独立的 命名空间。前面讲过镜像使用的是分层存储,容器也是如此。

容器存储层的生存周期和容器一样,容器消亡时,容器存储层也随之消亡。因此,任何保存于容器存储层的信息都会随容器删除而丢失。

按照 Docker *佳实践的要求,容器不应该向其存储层内写入任何数据 ,容器存储层要保持无状态化。所有的文件写入操作,都应该使用数据卷(Volume)、或者绑定宿主目录,在这些位置的读写会跳过容器存储层,直接对宿主(或网络存储)发生读写,其性能和稳定性更高。数据卷的生存周期独立于容器,容器消亡,数据卷不会消亡。因此, 使用数据卷后,容器可以随意删除、重新 run ,数据却不会丢失。

4.3 仓库(Repository)——集中存放镜像文件的地方

镜像构建完成后,可以很容易的在当前宿主上运行,但是, 如果需要在其它服务器上使用这个镜像,我们就需要一个集中的存储、分发镜像的服务,Docker Registry就是这样的服务。

一个 Docker Registry中可以包含多个仓库(Repository);每个仓库可以包含多个标签(Tag);每个标签对应一个镜像。所以说:镜像仓库是Docker用来集中存放镜像文件的地方类似于我们之前常用的代码仓库。

通常,一个仓库会包含同一个软件不同版本的镜像,而标签就常用于对应该软件的各个版本 。我们可以通过<仓库名>:<标签>的格式来指定具体是这个软件哪个版本的镜像。如果不给出标签,将以 latest 作为默认标签.。

这里补充一下Docker Registry 公开服务和私有 Docker Registry的概念:

Docker Registry 公开服务 是开放给用户使用、允许用户管理镜像的 Registry 服务。一般这类公开服务允许用户免费上传、下载公开的镜像,并可能提供收费服务供用户管理私有镜像。

*常使用的 Registry 公开服务是官方的 Docker Hub ,这也是默认的 Registry,并拥有大量的高质量的官方镜像,网址为:hub.docker.com/ 。在国内访问Docker Hub 可能会比较慢国内也有一些云服务商提供类似于 Docker Hub 的公开服务。比如 时速云镜像库网易云镜像服务DaoCloud 镜像市场阿里云镜像库等。

除了使用公开服务外,用户还可以在 本地搭建私有 Docker Registry 。Docker 官方提供了 Docker Registry 镜像,可以直接使用做为私有 Registry 服务。开源的 Docker Registry 镜像只提供了 Docker Registry API 的服务端实现,足以支持 docker 命令,不影响使用。但不包含图形界面,以及镜像维护、用户管理、访问控制等高级功能。


Docker的概念基本上已经讲完,*后我们谈谈:Build, Ship, and Run。

五 *后谈谈:Build, Ship, and Run

如果你搜索Docker官网,会发现如下的字样:“Docker – Build, Ship, and Run Any App, Anywhere”。那么Build, Ship, and Run到底是在干什么呢?

 

build ship run

 

 

  • Build(构建镜像) : 镜像就像是集装箱包括文件以及运行环境等等资源。
  • Ship(运输镜像) :主机和仓库间运输,这里的仓库就像是超级码头一样。
  • Run (运行镜像) :运行的镜像就是一个容器,容器就是运行程序的地方。

Docker 运行过程也就是去仓库把镜像拉到本地,然后用一条命令把镜像运行起来变成容器。所以,我们也常常将Docker称为码头工人或码头装卸工,这和Docker的中文翻译搬运工人如出一辙。

六 总结

本文主要把Docker中的一些常见概念做了详细的阐述,但是并不涉及Docker的安装、镜像的使用、容器的操作等内容。这部分东西,希望读者自己可以通过阅读书籍与官方文档的形式掌握。如果觉得官方文档阅读起来很费力的话,这里推荐一本书籍《Docker技术入门与实战第二版》。

超级有用的15个mysqlbinlog命令

转载自:https://www.cnblogs.com/zhengchunyuan/p/8175975.html

在MySQL或MariaDB中,任意时间对数据库所做的修改,都会被记录到日志文件中。例如,当你添加了一个新的表,或者更新了一条数据,这些事件都会被存储到二进制日志文件中。二进制日志文件在MySQL主从复合中是非常有用的,主服务器会发送其数据到远程服务器中。

 

当你需要恢复MySQL时,也会需要使用到二进制日志文件。

mysqlbinlog 命令,以用户可视的方式展示出二进制日志中的内容。同时,也可以将其中的内容读取出来,供其他MySQL实用程序使用。

在此示例中,我们将会涉及以下内容:

  • 获取当前二进制日志列表
  • mysqlbinlog默认行为
  • 获取特定数据库条目
  • 禁止恢复过程产生日志
  • 在输出中控制base-64 BINLOG
  • mysqlbinlog输出调试信息
  • 跳过前N个条目
  • 保存输出到文件
  • 从一个特定位置提取条目
  • 将条目截止到一个特定的位置
  • 刷新日志以清除Binlog输出
  • 在输出中只显示语句
  • 查看特定开始时间的条目
  • 查看特定结束时间的条目
  • 从远程服务器获取二进制日志

1 获取当前二进制日志列表

在mysql中执行以下命令,即可查看二进制日志文件的列表。

 

1

2

3

4

5

6

7

8

mysql> SHOW BINARY LOGS;

+———————-+———-+

| Log_name              | File_size |

+————————–+————+

| mysqld-bin.000001 |     15740 |

| mysqld-bin.000002 |       3319 |

..

..

如果熊没有开启此功能,则会显示:

 

1

2

mysql> SHOW BINARY LOGS;

ERROR 1381 (HY000): You are not using binary logging

二进制日志文件默认会存放在 /var/lib/mysql 目录下

 

1

2

3

4

5

$ ls -l /var/lib/mysql/

-rw-rw—-. 1 mysql mysql 15740 Aug 28 14:57 mysqld-bin.000001

-rw-rw—-. 1 mysql mysql  3319 Aug 28 14:57 mysqld-bin.000002

..

..

 

2 mysqlbinlog 默认行为

下面将以一种用户友好的格式显示指定的二进制日志文件(例如:mysqld.000001)的内容。

 

1 $ mysqlbinlog mysqld-bin.000001

mysqlbinlog默认会显示为以下内容:

 

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

/*!40019 SET @@session.max_insert_delayed_threads=0*/;/*!50003 SET @OLD_COMPLETION_TYPE=@@COMPLETION_TYPE,COMPLETION_TYPE=0*/;DELIMITER /*!*/;# at 4#170726 14:57:37 server id 1  end_log_pos 106   Start: binlog v 4, server v 5.1.73-log created 170726 14:57:37 at startup# Warning: this binlog is either in use or was not closed properly.

ROLLBACK/*!*/;

BINLOG ‘ IeZ4WQ8BAAAAZgAAAGoAAAABAAQANS4xLjczLWxvZwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAh5nhZEzgNAAgAEgAEBAQEEgAAUwAEGggAAAAICAgC ‘/*!*/;

# at 106

#170726 14:59:31 server id 1  end_log_pos 182   Query   thread_id=2     exec_time=0     error_code=0

SET TIMESTAMP=1501095571/*!*/;

SET @@session.pseudo_thread_id=2/*!*/;

SET @@session.foreign_key_checks=1, @@session.sql_auto_is_null=1, @@session.unique_checks=1, @@session.autocommit=1/*!*/;

SET @@session.sql_mode=0/*!*/;

SET @@session.auto_increment_increment=1, @@session.auto_increment_offset=1/*!*/;

/*!\C latin1 *//*!*/;

SET @@session.character_set_client=8,@@session.collation_connection=8,@@session.collation_server=8/*!*/;

..

..

..

# at 14191

#170726 15:20:38 server id 1  end_log_pos 14311         Query   thread_id=4     exec_time=0     error_code=0SET TIMESTAMP=1501096838/*!*/;

insert into salary(name,dept) values(‘Ritu’, ‘Accounting’)/*!*/;

DELIMITER ;

# End of log file

ROLLBACK /* added by mysqlbinlog */;

/*!50003 SET COMPLETION_TYPE=@OLD_COMPLETION_TYPE*

上面的命令将会显示出,在该系统上数据库发生的所有改变事件。

3 获取特定数据库条目

默认情况下,mysqlbinlog会显示所有的内容,太过于杂乱。使用 -d 选项,可以指定一个数据库名称,将只显示在该数据库上所发生的事件。

 

 

1 $ mysqlbinlog -d crm mysqld-bin.000001 > crm-events.txt

也可以使用 –database 命令,效果相同。

 

1 $ mysqlbinlog -database crm mysqld-bin.000001 > crm-events.txt

 

4 禁止恢复过程产生日志

在使用二进制日志文件进行数据库恢复时,该过程中也会产生日志文件,就会进入一个循环状态,继续恢复该过程中的数据。因此,当使用mysqlbinlog命令时,要禁用二进制日志,请使用下面所示的-D选项:

 

1 $ mysqlbinlog -D mysqld-bin.000001

也可以使用 –disable-log-bin 命令,效果相同。

 

1 $ mysqlbinlog –disable-log-bin mysqld-bin.000001

备注:在输出中,当指定-D选项时,将看到输出中的第二行。也就是SQL_LOG_BIN=0

 

1

2

3

/*!40019 SET @@session.max_insert_delayed_threads=0*/;

/*!32316 SET @OLD_SQL_LOG_BIN=@@SQL_LOG_BIN, SQL_LOG_BIN=0*/;

/*!50003 SET @OLD_COMPLETION_TYPE=@@COMPLETION_TYPE,COMPLETION_TYPE=0*/;

 

当使用-to-last-log选项时,这个选项也会有所帮助。另外,请记住,该命令需要root权限来执行。

5 在输出中控制base-64 BINLOG

使用base64-output选项,可以控制输出语句何时是输出base64编码的BINLOG语句。以下是base64输出设置的可能值:

 

  • never
  • always
  • decode-rows
  • auto(默认)

never:当指定如下所示的“never”时,它将在输出中显示base64编码的BINLOG语句。

 

1 $ mysqlbinlog –base64-output=never mysqld-bin.000001

将不会有任何与下面类似的行,它具有base64编码的BINLOG。

 

1 BINLOG ‘ IeZ4WQ8BAAAAZgAAABAAQANS4xLjczLWxvZwAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAh5nhZEzgNAAgAEgAEBAQEEgAAUwAEGggAAAAICAgC

always:当指定“always”选项时,只要有可能,它将只显示BINLOG项。因此,只有在专门调试一些问题时才使用它。

 

1 $ mysqlbinlog –base64-output=always mysqld-bin.000001

下面是“always”的输出,它只显示了BINLOG项。

 

1

2

3

4

5

6

7

8

9

10

BINLOG ‘ IeZ4WQ8BAAAAZgAAAGoAAAABAAQANS4xLjczLWxvZwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAh5nhZEzgNAAgAEgAEBAQEEgAAUwAEGggAAAAICAgC ‘/*!*/;

# at 106

#170726 14:59:31 server id 1  end_log_pos 182

BINLOG ‘ k+Z4WQIBAAAATAAAALYAAAAIAAIAAAAAAAAADAAAGgAAAEAAAAEAAAAAAAAAAAYDc3RkBAgACAAI AHRoZWdlZWtzdHVmZgBCRUdJTg== ‘/*!*/;

# at 182

#170726 14:59:30 server id 1  end_log_pos 291

BINLOG ‘ kuZ4WQIBAAAAbQAAACMBAAAAAAIAAAAAAAAADAAAGgAAAEAAAAEAAAAAAAAAAAYDc3RkBAgACAAI AHRoZWdlZWtzdHVmZgBJTlNFUlQgSU5UTyB0IFZBTFVFUygxLCAnYXBwbGUnLCBOVUxMKQ== ‘/*!*/;

# at 291

#170726 14:59:30 server id 1  end_log_pos 422

BINLOG ‘ kuZ4WQIBAAAAgwAAAKYBAAAAAAIAAAAAAAAADAAAGgAAAEAAAAEAAAAAAAAAAAYDc3RkBAgACAAI AHRoZWdlZWtzdHVmZgBVUERBVEUgdCBTRVQgbmFtZSA9ICdwZWFyJywgZGF0ZSA9ICcyMDA5LTAx LTAxJyBXSEVSRSBpZCA9IDE=

decode-rows:这个选项将把基于行的事件解码成一个SQL语句,特别是当指定-verbose选项时,如下所示。

 

1 $ mysqlbinlog –base64-output=decode-rows –verbose mysqld-bin.000001

auto:这是默认选项。当没有指定任何base64解码选项时,它将使用auto。在这种情况下,mysqlbinlog将仅为某些事件类型打印BINLOG项,例如基于行的事件和格式描述事件。

 

1

2

$ mysqlbinlog –base64-output=auto mysqld-bin.000001

$ mysqlbinlog mysqld-bin.000001

 

6 mysqlbinlog输出调试信息

下面的调试选项,在完成处理给定的二进制日志文件之后,将检查文件打开和内存使用。

 

1 $ mysqlbinlog –debug-check mysqld-bin.000001

如下所示,在完成处理给定的二进制日志文件之后,下面的调试信息选项将显示额外的调试信息。

 

1

2

3

4

5

6

$ mysqlbinlog –debug-info mysqld-bin.000001 > /tmp/m.di

User time 0.00, System time 0.00

Maximum resident set size 2848, Integral resident set size 0

Non-physical pagefaults 863, Physical pagefaults 0, Swaps 0

Blocks in 0 out 48, Messages in 0 out 0, Signals 0

Voluntary context switches 1, Involuntary context switches 2

 

7 跳过前N个条目

除了读取整个mysql二进制日志文件外,也可以通过指定偏移量来读取它的特定部分。可以使用 -o 选项。o代表偏移。

 

下面将跳过指定的mysql bin日志中的前10个条目。

 

1 $ mysqlbinlog -o 10 mysqld-bin.000001

为了确保它正常工作,给偏移量提供一个巨大的数字,将看不到任何条目。下面的内容将从日志中跳过10,000个条目(事件)。

 

1

2

3

4

5

6

$ mysqlbinlog -o 10000 mysqld-bin.000001

/*!40019 SET @@session.max_insert_delayed_threads=0*/;

/*!50003 SET @OLD_COMPLETION_TYPE=@@COMPLETION_TYPE,COMPLETION_TYPE=0*/; .. ..

# End of log file

ROLLBACK /* added by mysqlbinlog */;

/*!50003 SET COMPLETION_TYPE=@OLD_COMPLETION_TYPE*/;

在本例中,由于这个特定的日志文件没有10,000个条目,所以在输出中没有显示任何数据库事件。

8 保存输出到文件

也可以使用简单的Linux重定向命令,将输出存储到一个文件中,如下所示。

 

1 $ mysqlbinlog mysqld-bin.000001 > output.log

或者也可以使用 -r (结果文件)选项,如下所示,将输出存储到一个文件中。

 

1 $ mysqlbinlog -r output.log mysqld-bin.000001

备注:还可以使用 -server-id 指定mysql服务器,确保是由给定服务器id的mysql服务器所生成的日志。

 

1 $ mysqlbinlog –server-id=1 -r output.log mysqld-bin.000001

 

9 从一个特定位置提取条目

通常在mysql二进制日志文件中,你将看到如下所示的位置号。下面是mysqlbinlog的部分输出,你可以看到“15028”是一个位置编号。

 

1

2

3

4

5

6

7

8

#170726 15:38:14 server id 1  end_log_pos 15028         Query   thread_id=5     exec_time=0     error_code=0

SET TIMESTAMP=1501097894/*!*/;

insert into salary values(400,’Nisha’,’Marketing’,9500)

/*!*/;

# at 15028

#170726 15:38:14 server id 1  end_log_pos 15146         Query   thread_id=5     exec_time=0     error_code=0

SET TIMESTAMP=1501097894/*!*/;

insert into salary values(500,’Randy’,’Technology’,6000)

下面的命令将从位置编号为15028的二进制日志条目处开始读取。

 

1 $ mysqlbinlog -j 15028 mysqld-bin.000001 > from-15028.out

当在命令行中指定多个二进制日志文件时,开始位置选项将仅应用于给定列表中的*个二进制日志文件。还可以使用 -H 选项来获得给定的二进制日志文件的十六进制转储,如下所示。

 

 

1 $ mysqlbinlog -H mysqld-bin.000001 > binlog-hex-dump.out

 

10 将条目截止到一个特定的位置

就像前面的例子一样,你也可以从mysql二进制日志中截止到一个特定位置的条目,如下所示。

 

1 $ mysqlbinlog –stop-position=15028 mysqld-bin.000001 > upto-15028.out

上面的示例将在15028的位置上停止binlog。当在命令行中指定多个二进制日志文件时,停止位置将仅应用于给定列表中的*后一个二进制日志文件。

11 刷新日志以清除Binlog输出

当二进制日志文件没有被正确地关闭时,将在输出中看到一个警告消息,如下所示。

 

1 $ mysqlbinlog mysqld-bin.000001 > output.out

如下所示,报告中提示binlog文件没有正确地关闭。

 

1

2

3

4

5

6

7

# head output.log

/*!40019 SET @@session.max_insert_delayed_threads=0*/;

.. ..

# Warning: this binlog is either in use or was not closed properly.

..

.. .. BINLOG ‘ IeZ4WQ8BAAAAZgAAAGoAAAABAAQANS4xLjczLWxvZwAAAAAAAAAAAAAAAAAAAAAAAA

AAAAAAAAAAAAAAAAAAAAAAAAAAAAAh5nhZEzgNAAgAEgAEBAQEEgAAUwAEGggAAAAICAgC

当看到这个提示时,需要连接到mysql并刷新日志,如下所示。

 

1 mysql> flush logs;

刷新日志之后,再次执行mysqlbinlog命令,将不会看到在mysqlbinlog输出中binlog未正确关闭的警告消息。

12 在输出中只显示语句

默认情况下,正如在前面的示例输出中看到的一样,除了SQL语句之外,在mysqlbinlog输出中还会有一些附加信息。如果只想查看常规的SQL语句,而不需要其他内容,那么可以使用 -s 选项,如下所示。

 

也可以使用 –short-form 选项,效果相同。

 

1

2

3

$ mysqlbinlog -s mysqld-bin.000001

 

$ mysqlbinlog –short-form mysqld-bin.000001

 

下面是上述命令的部分输出。在这里,它将只显示来自给定二进制日志文件的SQL语句。

 

 

1

2

3

4

5

6

SET TIMESTAMP=1501096106/*!*/;

insert into employee values(400,’Nisha’,’Marketing’,9500)/*!*/;

SET TIMESTAMP=1501096106/*!*/;

insert into employee values(500,’Randy’,’Technology’,6000)

..

..

 

不会显示像下面这样的条目:

 

 

1

2

# at 1201

#170726 15:08:26 server id 1  end_log_pos 1329  Query   thread_id=3     exec_time=0     error_code=0

 

13 查看特定开始时间的条目

下面将只提取从指定时间开始的条目。在此之前的任何条目都将被忽略。

 

 

1 $ mysqlbinlog –start-datetime=”2017-08-16 10:00:00″ mysqld-bin.000001

 

当你想要从一个二进制文件中提取数据时,这是非常有用的,因为你希望使用它来恢复或重构在某个时间段内发生的某些数据库活动。时间戳的格式可以是MySQL服务器所理解的DATETIME和timestamp中的任何类型。

 

14 查看特定结束时间的条目

与前面的开始时间示例一样,这里也可以指定结束时间,如下所示。

 

1 $ mysqlbinlog –stop-datetime=”2017-08-16 15:00:00″ mysqld-bin.000001

上面的命令将读取到给定结束时间的条目。任何来自于超过给定结束时间的mysql二进制日志文件的条目都不会被处理。

15 从远程服务器获取二进制日志

在本地机器上,还可以读取位于远程服务器上的mysql二进制日志文件。为此,需要指定远程服务器的ip地址、用户名和密码,如下所示。

 

此处使用-R选项。-R选项与-read-from-remote-server相同。

 

1 $ mysqlbinlog -R -h 192.168.101.2 -p mysqld-bin.000001

在上面命令中:

 

  • -R 选项指示mysqlbinlog命令从远程服务器读取日志文件
  • -h 指定远程服务器的ip地址
  • -p 将提示输入密码。默认情况下,它将使用“root”作为用户名。也可以使用 -u 选项指定用户名。
  • mysqld-bin.000001 这是在这里读到的远程服务器的二进制日志文件的名称。

下面命令与上面的命令完全相同:

 

1 $ mysqlbinlog –read-from-remote-server –host=192.168.101.2 -p mysqld-bin.000001

如果只指定 -h 选项,将会得到下面的错误消息。

 

1

2

$ mysqlbinlog -h 192.168.101.2 mysqld-bin.000001

mysqlbinlog: File ‘mysqld-bin.000001’ not found (Errcode: 2)

当你在远程数据库上没有足够的特权时,将得到以下“不允许连接”错误消息。在这种情况下,确保在远程数据库上为本地客户机授予适当的特权。

 

1

2

3

$ mysqlbinlog -R –host=192.168.101.2 mysqld-bin.000001

ERROR: Failed on connect: Host ‘216.172.166.27’ is not allowed to connect

to this MySQL server

如果没有使用 -p 选项指定正确的密码,那么将得到以下“访问拒*”错误消息。

 

1

2

$ mysqlbinlog -R –host=192.168.101.2 mysqld-bin.000001

ERROR: Failed on connect: Access denied for user ‘root’@’216.172.166.27’ (using password: YES)

下面的示例显示,还可以使用-u选项指定mysqlbinlog应该用于连接到远程MySQL数据库的用户名。请注意,这个用户是mysql用户(不是Linux服务器用户)。

 

 

1 $ mysqlbinlog -R –host=192.168.101.2 -u root -p mysqld-bin.000001<span style=”text-indent: 2em;”> </span>

mysql误删数据快速恢复

转载自:https://www.jianshu.com/p/c9a2fe3f4534

相信后端研发的同学在开发过程经常会遇到产品临时修改线上数据的需求,如果手法很稳那么很庆幸可以很快完成任务,很不幸某一天突然手一抖把表里的数据修改错误或者误删了,这个时候你会发现各种问题反馈接踵而来。如果身边有BDA或者有这方面经验的同事那么可以很快解决这个问题,如果没有那么希望这篇文章可以帮到你。

binglog介绍

首先*步保证mysql已经开启binlog,查看命令:

show variables like '%log_bin%'

mysql binlog分三种格式 :

Statement : 会在binlog中记录每一条执行修改数据的sql语句的相关信息,优点是不需要记录每一行的变化,减少了binlog日志量,节约了IO
Row : 会在binlog中记录每一修改语句的详细信息,包括数据在修改之前和修改之后的数据的具体信息,好处是会清晰记录每一条修改的详细信息,不好的地方是会产生大量日志
Mixed :这种格式实际上就是Statement和Row的结合体,如果遇到表结构变更就会以Statement来记录,如果涉及语句修改那么就以Row格式记录

这里的binlog格式推荐row,my.cnf 的配置可参考 :

  1. server_id = 1001
  2. log_bin = /var/log/mysql/mysql-bin.log
  3. max_binlog_size = 1G
  4. binlog_format = row
  5. binlog_row_image = full

我们来模拟一些数据:

  1. CREATE TABLE `user` (
  2. `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
  3. `name` varchar(125) NOT NULL DEFAULT COMMENT ‘名称’,
  4. `age` tinyint(3) unsigned NOT NULL DEFAULT ‘0’ COMMENT ‘年龄’,
  5. `sex` tinyint(3) unsigned NOT NULL DEFAULT ‘0’ COMMENT ‘性别’,
  6. `deleted` tinyint(4) unsigned DEFAULT ‘0’,
  7. `created` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
  8. PRIMARY KEY (`id`)
  9. ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT=‘用户表测试’;
  1. INSERT INTO `user` (`id`, `name`, `age`, `sex`, `deleted`, `created`) VALUES (‘1’, ‘小王’, ’21’, ‘1’, ‘0’, ‘2017-12-18 13:21:52’),
  2. (‘2’, ‘小张’, ’28’, ‘1’, ‘0’, ‘2017-12-18 13:21:52’),(‘3’, ‘小红’, ’22’, ‘0’, ‘0’, ‘2017-12-18 13:21:52’),
  3. (‘4’, ‘小楠’, ’23’, ‘0’, ‘0’, ‘2017-12-18 13:21:52’)
  4. ,(‘5’, ‘小柱’, ’25’, ‘1’, ‘0’, ‘2017-12-18 13:21:52’);

然后我们把数据全部删掉

delete from `user`

数据恢复方法一 :

使用开源框架binlog2sql : https://github.com/danfengcao/binlog2sql
好处是成熟,稳定,上手难度比较低且可直接生成可执行sql,示例 :

python /binlog2sql/binlog2sql.py --flashback -h127.0.0.1 -P3306 -uroot -p'123456' -dlocal -tuser --start-file='mysql-bin.000038' --sql-type=DELETE --start-datetime='2017-12-17 19:39:33' --stop-datetime='2017-12-17 19:40:01' >/**/data6.sql

解析后的结果大概是这样

  1. INSERT INTO `local`.`user`(`name`, `created`, `deleted`, `age`, `sex`, `id`) VALUES (‘小柱’, ‘20171218 13:21:52‘, 0, 25, 1, 5); #start 1890 end 2244 time 20171219 09:20:26
  2. INSERT INTO `local`.`user`(`name`, `created`, `deleted`, `age`, `sex`, `id`) VALUES (‘小楠’, ‘20171218 13:21:52‘, 0, 23, 0, 4); #start 1890 end 2244 time 20171219 09:20:26
  3. INSERT INTO `local`.`user`(`name`, `created`, `deleted`, `age`, `sex`, `id`) VALUES (‘小红’, ‘20171218 13:21:52‘, 0, 22, 0, 3); #start 1890 end 2244 time 20171219 09:20:26
  4. INSERT INTO `local`.`user`(`name`, `created`, `deleted`, `age`, `sex`, `id`) VALUES (‘小张’, ‘20171218 13:21:52‘, 0, 28, 1, 2); #start 1890 end 2244 time 20171219 09:20:26
  5. INSERT INTO `local`.`user`(`name`, `created`, `deleted`, `age`, `sex`, `id`) VALUES (‘小王’, ‘20171218 13:21:52‘, 0, 21, 1, 1); #start 1890 end 2244 time 20171219 09:20:26

参数–sql-type建议加上,因为可能会有其他类型语句生成干扰了执行结果
如果是线上阿里云或者其他产品建议先去管理后台找到事发时间的binlog日志下载下来,先在测试环境验证数据回滚结果.

数据恢复方法二:

当线上数据出现错误的时候首先可以询问具体操作人记录时间点,这个时候可以借助mysql自带的binlog解析工具mysqlbinlog,具体位置在mysql安装目录**/mysql/bin/下,示例:

mysqlbinlog --base64-output=decode-rows -v --start-datetime="2017-12-15 17:48:49" --stop-datetime="2017-12-16 23:59:49" /usr/local/mysql/mysql-bin.000038 >/**/data.sql

如果是阿里云rds或者其他产品可通过远程方式解析

mysqlbinlog --no-defaults -u账号 -p密码 -h ***.rds.aliyuncs.com --read-from-remote-server mysql-bin.000180 --base64-output=decode-rows -v > /data.sql

这里因为binlog文件默认是通过base64编码过的,所以需要加上–base64-output=decode-rows -v
解析后的格式大概是这样的 :

  1. ### DELETE FROM `local`.`user`
  2. ### WHERE
  3. ### @1=1
  4. ### @2=’小王’
  5. ### @3=21
  6. ### @4=1
  7. ### @5=0
  8. ### @6=’2017-12-18 13:21:52′
  9. ### DELETE FROM `local`.`user`
  10. ### WHERE
  11. ### @1=2
  12. ### @2=’小张’
  13. ### @3=28
  14. ### @4=1
  15. ### @5=0
  16. ### @6=’2017-12-18 13:21:52′
  17. ….

仔细查看这种格式文件,发现这种格式文件并不能直接执行,但是在where条件后面记录了被删除之前的原始数据,需要借助sed、awk把SQL文本转换成真正的SQL。或者当你在遇到开源框架解决不了的情况下,可以根据具体场景尝试手动把这种格式的文件解析成可执行的sql语句。

友情链接: SITEMAP | 旋风加速器官网 | 旋风软件中心 | textarea | 黑洞加速器 | jiaohess | 老王加速器 | 烧饼哥加速器 | 小蓝鸟 | tiktok加速器 | 旋风加速度器 | 旋风加速 | quickq加速器 | 飞驰加速器 | 飞鸟加速器 | 狗急加速器 | hammer加速器 | trafficace | 原子加速器 | 葫芦加速器 | 麦旋风 | 油管加速器 | anycastly | INS加速器 | INS加速器免费版 | 免费vqn加速外网 | 旋风加速器 | 快橙加速器 | 啊哈加速器 | 迷雾通 | 优途加速器 | 海外播 | 坚果加速器 | 海外vqn加速 | 蘑菇加速器 | 毛豆加速器 | 接码平台 | 接码S | 西柚加速器 | 快柠檬加速器 | 黑洞加速 | falemon | 快橙加速器 | anycast加速器 | ibaidu | moneytreeblog | 坚果加速器 | 派币加速器 | 飞鸟加速器 | 毛豆APP | PIKPAK | 安卓vqn免费 | 一元机场加速器 | 一元机场 | 老王加速器 | 黑洞加速器 | 白石山 | 小牛加速器 | 黑洞加速 | 迷雾通官网 | 迷雾通 | 迷雾通加速器 | 十大免费加速神器 | 猎豹加速器 | 蚂蚁加速器 | 坚果加速器 | 黑洞加速 | 银河加速器 | 猎豹加速器 | 海鸥加速器 | 芒果加速器 | 小牛加速器 | 极光加速器 | 黑洞加速 | movabletype中文网 | 猎豹加速器官网 | 烧饼哥加速器官网 | 旋风加速器度器 | 哔咔漫画 | PicACG | 雷霆加速