Golang import 导入包语法介绍(绝对路径/相对路径/./_)

 go  Golang import 导入包语法介绍(绝对路径/相对路径/./_)已关闭评论
1月 102019
 

package 的导入语法,转自:https://blog.csdn.net/u010649766/article/details/79458004

写 Go 代码的时经常用到 import 这个命令用来导入包,参考如下:

import(
    “fmt”
)

然后在代码里面可以通过如下的方式调用:

fmt.Println( “我爱北京天安门” )
fmt 是 Go 的标准库,它其实是去 GOROOT 下去加载该模块,当然 Go 的 import 还支持如下两种方式来加载自己写的模块:

相对路径
import   “./model”  // 当前文件同一目录的 model 目录,但是不建议这种方式 import

绝对路径
import   “shorturl/model”  // 加载 GOPATH/src/shorturl/model 模块

package 的导入的特殊用法
上面展示了一些 import 常用的几种方式,但是还有一些特殊的 import ,让很多新手很费解,下面是三种导入包的使用方法。

点操作
有时候会看到如下的方式导入包:

import( 
    . “fmt” 

这个点操作的含义就是这个包导入之后在你调用这个包的函数时,你可以省略前缀的包名,也就是前面你调用的:

fmt.Println( “我爱北京天安门” )
可以省略的写成:

Println( “我爱北京天安门” )
别名操作
别名操作顾名思义可以把包命名成另一个用起来容易记忆的名字:

import( 
    f “fmt” 

别名操作调用包函数时前缀变成了重命名的前缀,即:

     f.Println( “我爱北京天安门” )

下划线操作
这个操作经常是让很多人费解的一个操作符,请看下面这个 import

import ( 
    “database/sql” 
    _ “github.com/ziutek/mymysql/godrv” 

下滑线 “_” 操作其实只是引入该包。当导入一个包时,它所有的 init() 函数就会被执行,但有些时候并非真的需要使用这些包,仅仅是希望它的 init() 函数被执行而已。这个时候就可以使用 “_” 操作引用该包了。即使用 “_” 操作引用包是无法通过包名来调用包中的导出函数,而是只是为了简单的调用其 init() 函数。

ffmpeg常用使用场景及命令

 ffmpeg  ffmpeg常用使用场景及命令已关闭评论
1月 102019
 

网上收集的ffmpeg常用使用场景及命令使用:

应用场景1:格式转换

我想把用iPhone拍的.MOV文件转成.avi文件。最简单了,可以执行下面的命令:

ffmpeg -i D:\Media\IMG_0873.MOV D:\Media\output.avi

意思是,把D:\Media目录下的源文件IMG_0873.MOV(视频:h.264,音频:aac)转换成output.avi(编码格式自动选择为:视频mpeg4,音频mp3),目标文件仍然保存到D:\Media目录下。问题来了:我想自己指定编码格式,怎么办呢?一种方法是,通过目标文件的扩展名(.flv、.mpg、.mp4、.wmv等)来控制,比如:

ffmpeg -i D:\Media\IMG_0873.MOV D:\Media\output2.flv

另一种方法是通过-c:v参数来控制,比如我想输出的视频格式是H.265(警告:编码时间会比较长哦)。命令行如下:

ffmpeg -i D:\Media\IMG_0873.MOV -c:v libx265 D:\Media\output265.avi

注:可以先用ffmpeg -encoders命令查看一下所有可选的编码格式。

不再深究了,我们继续。我发现源文件的图像帧尺寸是1920x 1080,我不需要这么大——能有720 x 480就够了。于是,就要用上-s参数了。为了保证图像缩放后的质量,最好加上码率参数-b:v。如下:

ffmpeg -i D:\Media\IMG_0873.MOV -s 720×480 -b:v 1500k D:\Media\output2.avi

还可以更简单一点,使用-target参数匹配行业标准,参数值可以是vcd、svcd、dvd、dv、dv50等,可能还需要加上电视制式作为前缀(pal-、ntsc-或film-)。如下:

ffmpeg -i D:\Media\IMG_0873.MOV -target pal-dvd D:\Media\output2dvd.avi

又来一个问题:我发现用手机拍的视频中,有些是颠倒的,我想让它顺时针旋转90度。这时候,可以使用-vf参数加入一个过滤器,如下:

ffmpeg -i D:\Media\IMG_0873.MOV -vf “rotate=90*PI/180” D:\Media\output3.avi

注:如果想逆时针旋转90度,90前面加个负号就可以了。

如果我只需要从源视频里截取一小段,怎么办呢?比如从第2秒的地方开始,往后截取10秒钟。命令行可以这样:

ffmpeg -ss 2 -t 10 -i D:\Media\IMG_0873.MOV D:\Media\output4.avi

注:这种情况下,-ss和-t参数必须放在-i前面,表示是限定后面跟着的输入文件的。

应用场景2:视频合成

我发现,用手机拍的视频有时候背景噪音比较大。怎么把噪音去掉,换成一段美妙的音乐呢?使用FFmpeg也能轻易做到。

第一步:把源文件里的音频去掉,生成一个临时文件tmp.mov

ffmpeg -i D:\Media\IMG_0873.MOV -vcodec copy -an D:\Media\tmp.mov

注:-vcodeccopy的意思是对源视频不解码,直接拷贝到目标文件;-an的意思是将源文件里的音频丢弃。

第二步:把这个无声的视频文件(tmp.mov)与一个音乐文件(music.mp3)合成,最终生成output.mov

ffmpeg -i D:\Media\tmp.mov -ss 30 -t 52 -i D:\Media\music.mp3 -vcodec copy D:\Media\output5.avi

为了保证良好的合成效果,音乐时长必须匹配视频时长。这里我们事先知道视频时长为52秒,于是截取music.mp3文件的第30秒往后的52秒与视频合成。另外,为了保证音频时长截取的准确性,我们这里没有使用-acodec copy,而是让音频重新转码。

还有一种情况:我们希望在一段视频上叠加一张图片。可以简单实现如下:

ffmpeg -i D:\Media\IMG_0873.MOV -i D:\Media\logo.png -filter_complex ‘overlay’ D:\Media\output6.avi

应用场景3:视频播放

格式转换或合成之后,我们需要试着播放一下。播放器的选择很多。这里顺手用ffplay工具也行:

ffplay -i D:\Media\output6.avi

应用场景4:获取视频信息

有时候,我只是想看看这个视频文件的格式信息。可以用ffprobe工具:

ffprobe -i D:\Media\IMG_0873.MOV

应用场景5: 截取视频片段转为GIF动画

可以简单地执行下面的命令行:

ffmpeg -ss 25 -t 10 -i D:\Media\bear.wmv -f gif D:\a.gif

意思是:将D:\Media目录下的源文件bear.wmv,从第25秒的位置开始,截取10秒长度的视频转成GIF文件,保存为D:\a.gif。

想要知道FFmpeg到底支持哪些格式吗?执行ffmpeg –formats即可。

问题来了,你的源文件可能是1080P的高清视频,帧率可能还比较高。为了便于网络分享,GIF文件最好小一点。于是,我们需要使用-s参数来进行图像的缩放,使用-r参数来限制目标文件的帧率。命令行如下:

ffmpeg -ss 25 -t 10 -i D:\Media\bear.wmv -s 320×240 -f gif -r 1 D:\b.gif

把b.gif拖进浏览器预览,结果发现:虽然帧率降到了1 fps(从源视频里每隔一秒抽取一帧图像输出到目标文件),整个动画播放还是持续了10秒钟,看着很揪心!能不能在源视频跳帧的情况下同时提高GIF的播放速率呢(比如说在2秒内播完)?查了一遍FFmpeg的说明文档,似乎没有哪个参数可以快速达到这样的目的。也罢,那就分两步走吧:

首先,执行ffmpeg -ss 25 -t 10 -i D:\Media\bear.wmv -r 1 -s 320×240 -f image2 D:\foo-%03d.jpeg,从源视频中每秒钟抽取一帧图像,保存为一系列JPEG文件。然后,再执行ffmpeg -f image2 -framerate 5 -i D:\foo-%03d.jpeg D:\c.gif,将这一系列JPEG图像合成为帧率5 fps的GIF文件。Bingo!

上面提到,把GIF文件拖进浏览器可以进行预览。当然,使用ffplay.exe工具也是可以的——命令行:ffplay D:\a.gif。

p.s. 附送一条指令:截取视频内任意时间点(比如第16.1秒处)的一帧图像保存为JPEG文件:ffmpeg -ss 16.1 -i D:\Media\bear.wmv -s 320×240 -vframes 1 -f image2 D:\d.jpeg

其他应用

FFmpeg的功能非常强大。关键是要理解各种参数的意义,并且巧妙搭配。必要的话,就把在线文档完整读一遍吧:http://www.ffmpeg.org/ffmpeg.html

================ffmpeg 常用基本命令=========================

1.分离视频音频流

ffmpeg -i input_file -vcodec copy -an output_file_video //分离视频流 ffmpeg -i input_file -acodec copy -vn output_file_audio //分离音频流

2.视频解复用

ffmpeg –i test.mp4 –vcodec copy –an –f m4v test.264ffmpeg –i test.avi –vcodec copy –an –f m4v test.264

3.视频转码

ffmpeg –i test.mp4 –vcodec h264 –s 352*278 –an –f m4v test.264              //转码为码流原始文件 ffmpeg –i test.mp4 –vcodec h264 –bf 0 –g 25 –s 352*278 –an –f m4v test.264  //转码为码流原始文件 ffmpeg –i test.avi -vcodec mpeg4 –vtag xvid –qsame test_xvid.avi            //转码为封装文件//-bf B帧数目控制,-g 关键帧间隔控制,-s 分辨率控制

4.视频封装

ffmpeg –i video_file –i audio_file –vcodec copy –acodec copy output_file

5.视频剪切

ffmpeg –i test.avi –r 1 –f image2 image-%3d.jpeg        //提取图片 ffmpeg -ss 0:1:30 -t 0:0:20 -i input.avi -vcodec copy -acodec copy output.avi    //剪切视频//-r 提取图像的频率,-ss 开始时间,-t 持续时间

6.视频录制

ffmpeg –i rtsp://192.168.3.205:5555/test –vcodec copy out.avi

7.YUV序列播放

ffplay -f rawvideo -video_size 1920x1080 input.yuv

8.YUV序列转AVI

ffmpeg –s w*h –pix_fmt yuv420p –i input.yuv –vcodec mpeg4 output.avi

常用参数说明:

主要参数: -i 设定输入流 -f 设定输出格式 -ss 开始时间 视频参数: -b 设定视频流量,默认为200Kbit/s -r 设定帧速率,默认为25 -s 设定画面的宽与高 -aspect 设定画面的比例 -vn 不处理视频 -vcodec 设定视频编解码器,未设定时则使用与输入流相同的编解码器 音频参数: -ar 设定采样率 -ac 设定声音的Channel数 -acodec 设定声音编解码器,未设定时则使用与输入流相同的编解码器 -an 不处理音频

——————————————————————————————–

——————————————————————————————–

1、将文件当做直播送至live

ffmpeg -re -i localFile.mp4 -c copy -f flv rtmp://server/live/streamName

2、将直播媒体保存至本地文件

 

ffmpeg -i rtmp://server/live/streamName -c copy dump.flv

3、将其中一个直播流,视频改用h264压缩,音频不变,送至另外一个直播服务流

 

ffmpeg -i rtmp://server/live/originalStream -c:a copy -c:v libx264 -vpre slow -f flv rtmp://server/live/h264Stream

 

4、将其中一个直播流,视频改用h264压缩,音频改用faac压缩,送至另外一个直播服务流

ffmpeg -i rtmp://server/live/originalStream -c:a libfaac -ar 44100 -ab 48k -c:v libx264 -vpre slow -vpre baseline -f flv rtmp://server/live/h264Stream

5、将其中一个直播流,视频不变,音频改用faac压缩,送至另外一个直播服务流

ffmpeg -i rtmp://server/live/originalStream -acodec libfaac -ar 44100 -ab 48k -vcodec copy -f flv rtmp://server/live/h264_AAC_Stream

6、将一个高清流,复制为几个不同视频清晰度的流重新发布,其中音频不变

ffmpeg -re -i rtmp://server/live/high_FMLE_stream -acodec copy -vcodec x264lib -s 640×360 -b 500k -vpre medium -vpre baseline rtmp://server/live/baseline_500k -acodec copy -vcodec x264lib -s 480×272 -b 300k -vpre medium -vpre baseline rtmp://server/live/baseline_300k -acodec copy -vcodec x264lib -s 320×200 -b 150k -vpre medium -vpre baseline rtmp://server/live/baseline_150k -acodec libfaac -vn -ab 48k rtmp://server/live/audio_only_AAC_48k

7、功能一样,只是采用-x264opts选项

ffmpeg -re -i rtmp://server/live/high_FMLE_stream -c:a copy -c:v x264lib -s 640×360 -x264opts bitrate=500:profile=baseline:preset=slow rtmp://server/live/baseline_500k -c:a copy -c:v x264lib -s 480×272 -x264opts bitrate=300:profile=baseline:preset=slow rtmp://server/live/baseline_300k -c:a copy -c:v x264lib -s 320×200 -x264opts bitrate=150:profile=baseline:preset=slow rtmp://server/live/baseline_150k -c:a libfaac -vn -b:a 48k rtmp://server/live/audio_only_AAC_48k

8、将当前摄像头及音频通过DSSHOW采集,视频h264、音频faac压缩后发布

ffmpeg -r 25 -f dshow -s 640×480 -i video=”video source name”:audio=”audio source name” -vcodec libx264 -b 600k -vpre slow -acodec libfaac -ab 128k -f flv rtmp://server/application/stream_name

9、将一个JPG图片经过h264压缩循环输出为mp4视频

ffmpeg.exe -i INPUT.jpg -an -vcodec libx264 -coder 1 -flags +loop -cmp +chroma -subq 10 -qcomp 0.6 -qmin 10 -qmax 51 -qdiff 4 -flags2 +dct8x8 -trellis 2 -partitions +parti8x8+parti4x4 -crf 24 -threads 0 -r 25 -g 25 -y OUTPUT.mp4

10、将普通流视频改用h264压缩,音频不变,送至高清流服务(新版本FMS live=1)

ffmpeg -i rtmp://server/live/originalStream -c:a copy -c:v libx264 -vpre slow -f flv “rtmp://server/live/h264Stream live=1〃

————————————————————————

————————————————————————

1.采集usb摄像头视频命令:

ffmpeg -t 20 -f vfwcap -i 0 -r 8 -f mp4 cap1111.mp4

 

./ffmpeg -t 10 -f vfwcap -i 0 -r 8 -f mp4 cap.mp4

具体说明如下:我们采集10秒,采集设备为vfwcap类型设备,第0个vfwcap采集设备(如果系统有多个vfw的视频采集设备,可以通过-i num来选择),每秒8帧,输出方式为文件,格式为mp4。

 

2.最简单的抓屏:

ffmpeg -f gdigrab -i desktop out.mpg 

 

3.从屏幕的(10,20)点处开始,抓取640×480的屏幕,设定帧率为5 :

ffmpeg -f gdigrab -framerate 5 -offset_x 10 -offset_y 20 -video_size 640×480 -i desktop out.mpg 

 

4.ffmpeg从视频中生成gif图片:

ffmpeg -i capx.mp4 -t 10 -s 320×240 -pix_fmt rgb24 jidu1.gif

 

5.ffmpeg将图片转换为视频:

http://blog.sina.com.cn/s/blog_40d73279010113c2.html

MAC PRO 换SSD盘时,用time machine恢复,提示“将恢复系统添加到目的磁盘时发生错误”解决方法

 mac, macpro  MAC PRO 换SSD盘时,用time machine恢复,提示“将恢复系统添加到目的磁盘时发生错误”解决方法已关闭评论
12月 292018
 

目前很多老mac pro用户都会有想着换个SSD硬盘已优化机器性能。

在使用时间机器备份完了老数据后,正常换好SSD硬盘,并在启动时使用 COMMAND + R 进入macOS实用程序界面,在正常格式话硬盘后,再使用“选择“从Time Machine备份恢复””,并选择对应的硬盘恢复时,可能会出现: 

“将恢复系统添加到目的磁盘时发生错误”,重启系统再试还是一样的效果。

后来发现,对于想要完全恢复硬盘数据或着是空硬盘的,比如换SSD硬盘的情况,开机启动后的按键:

 应该是COMMAND + OPTION + R  而不是 COMMAND + R, 

加OPTION后启动将出现进度条,启动后工具的界面也稍有不同,至此一步步往下走,问题解决,GoodLuck!!

Mybatis中实体类属性和数据列之间映射的四种办法

 开发  Mybatis中实体类属性和数据列之间映射的四种办法已关闭评论
12月 292018
 

网上总结的4个方法,挺好的,分享下: https://blog.csdn.net/lmy86263/article/details/53150091

首先先定义一个测试实体类,如下:

public class User implements Serializable {
    private Integer userId;
    private String userName;
    ……
}

1. 通过XML映射文件中的resultMap

这种方式是最常见的,类似如下:

<mapper namespace=”data.UserMapper”>
    <resultMap type=”data.User” id=”userResultMap”>
        <!– 用id属性来映射主键字段 –>
        <id property=”id” column=”user_id”/>
        <!– 用result属性来映射非主键字段 –>
        <result property=”userName” column=”user_name”/>
    </resultMap>
</mapper>
通过里面的id标签和result标签来建立映射关系,由property和column分别指定实体类属性和数据表的列名。

2. 通过注解@Results和@Result

这两个注解是与XML文件中的标签相对应的:

@Results对应resultMap
@Result对应result
这两个注解是应用在方法的级别上的,也就是在mapper方法上,如下:

@Select(“select * from t_user where user_name = #{userName}”)
@Results(
        @Result(property = “userId”, column = “user_id”),
        @Result(property = “userName”, column = “user_name”)
)
User getUserByName(@Param(“userName”) String userName);
缺点:

由于注解是针对方法的,对于Mapper中的每个操作数据库的方法都必须有相同的注解完成映射关系的建立,导致很多的配置是重复的;
如果要避免配置重复的问题,可以采用在XML配置文件中配置这个resultMap,然后再@Result中通过id属性引用这个resultMap, 
但是这样感觉很麻烦(由于使用了两种配置方式),不如直接使用基于XML的resultMap配置方式;
3. 通过属性配置完成映射

使用者最陌生的是通过配置属性来完成映射,Mybatis给我们提供了一种映射方式,如果属性的命名是遵从驼峰命名法的,数据列名遵从下划线命名, 
那么可以使用这种方式,类似如下:

userName对应user_name;
userId对应user_id;
配置代码如下:

SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
Configuration configuration = new Configuration();
configuration.setMapUnderscoreToCamelCase(true);
sqlSessionFactoryBean.setConfiguration(configuration);

4. 通过使用在SQL语句中定义别名完成映射

这种方式最直接,直接在SQL语句中建立别名来完成映射,如下:

@Select(“select user_name as userName, user_id as userId from t_user where user_name = #{userName}”)
User getUserByName(@Param(“userName”) String userName);

navcat 连接本地mysql,出现“client does not support authentication”

 mysql  navcat 连接本地mysql,出现“client does not support authentication”已关闭评论
12月 242018
 

navcat 使用root连接本地mysql,出现“client does not support authentication”

先使用命令行mysql -uroot -p连接mysql, 输入密码后进入

mysql>

然后运行以下命令如下:

1、mysql> use mysql;

2、mysql> alter user ‘root’@’localhost’ identified with mysql_native_password by ‘********’;

3、mysql> flush privileges;

mac下安装MySQL_python时出现_mysql.c:44:10: fatal error: ‘my_config.h’ file not found

 python  mac下安装MySQL_python时出现_mysql.c:44:10: fatal error: ‘my_config.h’ file not found已关闭评论
12月 242018
 

mac下使用brew install mysql,安装pip install MySQL-python出现下面错误提示: 

    _mysql.c:44:10: fatal error: ‘my_config.h’ file not found
    #include “my_config.h”
             ^~~~~~~~~~~~~
    1 error generated.
    error: command ‘cc’ failed with exit status 1

网上很多方法都无效,使用下面都步骤可解决

brew remove mysql
brew install mysql@5.7
brew link --force mysql@5.7
pip install msyql-python (如权限不足,请使用sudo)

Tornado异步笔记(三)— 持久连接 KeepAlive 简介

 tornado  Tornado异步笔记(三)— 持久连接 KeepAlive 简介已关闭评论
12月 062018
 

网上看到的3篇连载关于tornado异步的文章,写的很好,虽然有些内容有些老了,但不影响文章的借鉴意义,强烈推荐:

转自:https://www.jianshu.com/p/3cc234198567

HTTP 持久连接

HTTP通信中,client和server一问一答的方式。HTTP是基于TCP的应用层协议,通常在发送请求之前需要创建TCP连接,然后在收到响应之后会断开这个TCP连接。这就是常见的http短连接。既然有短连接,那么也有长连接。

HTTP协议最初的设计是无连接无状态的方式。为了维护状态,引入了cookie和session方式认证识别用户。早期的web开发中,为了给用户推送数据,通常使用所谓的长连接。那时的长连接还是基于短连接的方式实现,即通过client的轮询查询,在用户层面看起来连接并没有断开。随着技术的发展,又出现了Websockt和MQTT等通信协议。Websockt和MQTT则是全双工的通信协议。

相比全双工实现的长连接,我们还会在web开发中遇到伪长连接。即HTTP协议中的keepalive模式。因为HTTP设计是无连接设计,请求应答结束之后就关闭了TCP连接。在http通信中,就会有大量的新建和销毁tcp连接的过程,那怕是同一个用户同一个客户端。为了优化这种方式,HTTP提出了KeepAlive模式,即创建的tcp连接后,传输数据,server返回响应之后并不会关掉tcp连接,下一次http请求就能复用这个tcp连接。

这是一种协商式的连接,毕竟每次的http发送数据的时候,还是要单独为每个请求发送header之类的信息。相比全双工的websocket,一旦创建了连接,下一次就不需要再发送header,直接发送数据即可。因此描述http的keepalive应该是持久连接(HTTP persistent connection )更准确。

keepalive 简介

HTTP的keepalive模式提供了HTTP通信的时候复用TCP连接的协商功能。http1.0默认是关闭的,只有在http的header加入Connection: Keep-Alive才能开启。而http1.1则正相反,默认就打开了,只有显示的在header里加入Connection: close才能关闭。现在的浏览器基本都是http1.1的协议,能否使用长连接,权看服务器的支持状况了。下图说明了开启keepalive模式的持久连接与短连接的通信示意图

短连接与持久连接,图片来源网络

当开启了持久连接,就不能使用返回EOF的方式来判断数据结尾了。对于静态和动态的数据,可以使用Conent-Lenght和Transfer-Encoding`来做应用层的区分。

requests与持久连接

了解了keeplive模式,接下来我们就来使用keepalive方式。服务器使用Tornado,tornado实现了keepalive的处理,客户端我们可以分别使用同步的requests和异步的AsyncHTTPClient。

先写一个简单的服务器:

micro-server.py

import tornado.httpserver 
import tornado.ioloop 
import tornado.web 
class IndexHandler(tornado.web.RequestHandler): 
def get(self, *args, **kwargs): 
        self.finish('It works')

app = tornado.web.Application(
handlers=[
('/', IndexHandler),
],
debug=True ) if __name__ == '__main__':
server = tornado.httpserver.HTTPServer(app)
server.listen(8000)
tornado.ioloop.IOLoop().instance().start()

requests 短连接

requests不愧是一个”for human” 的软件,实现一个http客户端非常简单。

import argparse 
import requests

url = 'http://127.0.0.1:8000' 
def short_connection(): 
    resp = requests.get(url)
    print(resp.text)

    resp = requests.get(url)
    print(resp.text) 
def long_connection(): 
    pass 
if __name__ == '__main__':
    ap = argparse.ArgumentParser()
    ap.add_argument("-t", "--type", default="short")
    args = ap.parse_args()
    type_ = args.type if type_ == 'short':
        short_connection() elif type_ == 'long':
        long_connection()

运行keepalive python requests-cli.py –type=short,可以看见返回了数据,同时通过另外一个神器wireshark抓包如下:

requests 短连接

从抓包的情况来看,两次http请求,一共创建了两次tcp的握手连接和挥手断开。每次发送http数据都需要先创建tcp连接,然后就断开了连接。通常是客户端发起的断开连接。

requests 持久连接

requests的官网也说明了,基于urllib3的方式,requests百分比实现了keepalive方式,只需要创建一个客户端session即可,代码如下:

def long_connection(): s = requests.Session()

    resp = s.get(url)
    print(resp.text)

    resp = s.get(url)
    print(resp.text)

    s.close()

再次通过抓包如下图:

requests 持久连接模式

可以看到,同样也是两次http请求,只创建了一次tcp的握手和挥手。两次http请求都基于一个tcp连接。再次查看包43,可以看到下图中的报文header指定了keepalive。

http请求的数据包

AsyncHTTPClient与持久连接

tornado是一个优秀高性能异步非阻塞(non-block)web框架。如果torando的handler中也需要请求别的三方资源,使用requests的同步网络IO,将会block住整个tornado的进程。因此tornado也实现了异步的http客户端AsyncHTTPClient。

短连接

使用AsyncHTTPClient也不难,但是想要使用其异步效果,就必须把其加入事件循环中,否则只有连接的创立,而没有数据的传输就退出了。

import tornado.httpclient
import tornado.ioloop 
import time

url = 'http://127.0.0.1:8000' 
def handle_response(response):
 if response.error:
        print("Error: %s" % response.error) else:
        print(response.body)

http_client = tornado.httpclient.AsyncHTTPClient()
http_client.fetch(url, handle_response)
http_client.fetch(url, handle_response)

运行上述代码,将会看到wirshark中,创建了两次TCP连接和断开了连接,并没有发送http数据。为了发送http数据,还需要加入tornado的事件循环。即在最后一行加入tornado.ioloop.IOLoop.instance().start()

再次运行,客户端正常收到了数据,抓包如下:

async http client 短连接

抓包的结果咋一看像是持久连接,仔细一看却有两次握手和挥手的操作。的确,客户端发送异步http请求的时候,创建了两个端口49989和49990两个tcp连接。因为是异步的请求,因此先创建了两个连接,然后才发送数据,发送数据的时候都是基于所创建的端口进行的。也就是没有使用持久连接。

持久连接

AsyncHTTPClient使用持久连接也很简单。现在流行微服务架构。通常提供给客户端的服务称之为网关,网关从各种微服务中调用获取数据,通信的方式中,同步的有http和rpc,异步的有mq之类的。而http通常都是使用持久连接的方式。

下面我们介绍一下在tornado server的handler中使用async client请求微服务的资源。

再写一个简单server

#!/usr/bin/env python 
# -*- coding:utf-8 -*- 
import tornado.gen 
import tornado.httpclient 
import tornado.httpserver 
import tornado.ioloop 
import tornado.web 
class AsyncKeepAliveHandler(tornado.web.RequestHandler):
  @tornado.web.asynchronous
  @tornado.gen.coroutine
    def get(self, *args, **kwargs):
        url = 'http://127.0.0.1:8000/'
        http_client = tornado.httpclient.AsyncHTTPClient()
    response = yield tornado.gen.Task(http_client.fetch, url) 
    print response.code print response.body
        self.finish("It works")

app = tornado.web.Application(
        handlers=[
            ('/async/keepalive', AsyncKeepAliveHandler)
        ],
        debug=True ) 
if __name__ == '__main__':
    server = tornado.httpserver.HTTPServer(app)
    server.listen(5050)
    tornado.httpclient.AsyncHTTPClient.configure("tornado.curl_httpclient.CurlAsyncHTTPClient")
    tornado.ioloop.IOLoop().instance().start()

然后我们请求5050端口的服务,也连接发送两次http请求:

(venv)☁  keepalive  curl http://127.0.0.1:5050/async/keepalive It works%                                                                                                                                                     (venv)☁  keepalive  curl http://127.0.0.1:5050/async/keepalive It works%

再看我们的抓包情况:

tornado handler使用持久连接

从图中可以看到,即使是两个请求,最终都是复用了断开为50784的tcp连接。

因为asynchttpclient默认使用的是SimpleAsyncHTTPClient,实现持久连接只需要配置一下tornado.httpclient.AsyncHTTPClient.configure(“tornado.curl_httpclient.CurlAsyncHTTPClient”)即可。当然,这个需要tornado的版本4.2以上,当前的版本是4.5。

CurlAsyncHTTPClient依赖于pycurl。pycurl又依赖libcurl。在安装pycurl的时候,可能会出现link的问题。例如ImportError: pycurl: libcurl link-time version (7.37.1) is older than compile-time version (7.43.0) 。 解决了link问题,如果是mac系统,安装的时候可能出现error: Setup script exited with error: command ‘cc’ failed,多半是由于xcode做鬼,这里有一个解决说明

AsyncHTTPClient设置成为keepalive模式是全局性的,比较tornado是单进程单线程的,访问三方或者微服务,都是一个客户端,所有的模式都是持久连接。

短连接与持久连接的应用场景

持久连接可以减少tcp连接的创建和销毁,提升服务器的处理性能。但是并不是所有连接都得使用持久连接。长短连接都有其使用场景。

既然持久连接在于连接的持久,因此对于频繁通信,点对点的就可以使用。例如网关和微服务之间。如果创建了持久连接,就必须在意连接的存活状态。客户端一般不会主动关闭,因此服务端需要维护这个连接状态,对于一些长时间没有读写事件发生的连接,可以主动断开,节省资源。

对于一些用完就走的场景,也不需要使用持久连接。而另外一些需要全双工通信,例如推送和实时应用,则需要真正的长连接,比如MQTT实现推送和websocket实现实时应用等。

总结

微服务大行其道,从微观来看,增加了更多的网络IO。而IO又是最耗时的操作。相比之下,程式的计算速度就显得没那么紧要了。优化网络IO才是提升性能的关键。一些频繁通信的场景,使用持久连接或长连接更能优化大量TCP连接的创建和销毁。

就Python的而言,Tornado的诞生就是为了解决网络IO的瓶颈。并且很多tornado及其三方库的问题,都能在github和stackoverflow找到作者的参与和回答。可见作者对项目的负责。由于tornado单线程的特性,因此做任何IO操作,都需要考虑是否block。幸好有AsyncHTTPClinet,既可以提供异步IO,也可以实现持久连接,当然,tornado也支持websocket。

Tornado异步笔记(二)— 异步客户端AsyncHTTPClient

 tornado  Tornado异步笔记(二)— 异步客户端AsyncHTTPClient已关闭评论
12月 062018
 

网上看到的3篇连载关于tornado异步的文章,写的很好,虽然有些内容有些老了,但不影响文章的借鉴意义,强烈推荐:

转自: https://www.jianshu.com/p/ff9cb6dea27a/


Tornado异步笔记(二)— 异步客户端
前面了解Tornado的异步任务的常用做法,姑且归结为异步服务。通常在我们的服务内,还需要异步的请求第三方服务。针对HTTP请求,Python的库Requests是最好用的库,没有之一。官网宣称:HTTP for Human。然而,在tornado中直接使用requests将会是一场恶梦。requests的请求会block整个服务进程。
上帝关上门的时候,往往回打开一扇窗。Tornado提供了一个基于框架本身的异步HTTP客户端(当然也有同步的客户端)— AsyncHTTPClient。
AsyncHTTPClient 基本用法
AsyncHTTPClient是 tornado.httpclinet 提供的一个异步http客户端。使用也比较简单。与服务进程一样,AsyncHTTPClient也可以callback和yield两种使用方式。前者不会返回结果,后者则会返回response。
如果请求第三方服务是同步方式,同样会杀死性能。
class SyncHandler(tornado.web.RequestHandler):
    def get(self, *args, **kwargs):

        url = ‘https://api.github.com/’
        resp = requests.get(url)
        print resp.status_code

        self.finish(‘It works’)

使用ab测试大概如下:
Document Path:          /sync
Document Length:        5 bytes

Concurrency Level:      5
Time taken for tests:   10.255 seconds
Complete requests:      5
Failed requests:        0
Total transferred:      985 bytes
HTML transferred:       25 bytes
Requests per second:    0.49 [#/sec] (mean)
Time per request:       10255.051 [ms] (mean)
Time per request:       2051.010 [ms] (mean, across all concurrent requests)
Transfer rate:          0.09 [Kbytes/sec] received

性能相当慢了,换成AsyncHTTPClient再测:
class AsyncHandler(tornado.web.RequestHandler):
    @tornado.web.asynchronous
    def get(self, *args, **kwargs):

        url = ‘https://api.github.com/’
        http_client = tornado.httpclient.AsyncHTTPClient()
        http_client.fetch(url, self.on_response)
        self.finish(‘It works’)

    @tornado.gen.coroutine
    def on_response(self, response):
        print response.code
        

qps 提高了很多
Document Path:          /async
Document Length:        5 bytes

Concurrency Level:      5
Time taken for tests:   0.162 seconds
Complete requests:      5
Failed requests:        0
Total transferred:      985 bytes
HTML transferred:       25 bytes
Requests per second:    30.92 [#/sec] (mean)
Time per request:       161.714 [ms] (mean)
Time per request:       32.343 [ms] (mean, across all concurrent requests)
Transfer rate:          5.95 [Kbytes/sec] received

同样,为了获取response的结果,只需要yield函数。
class AsyncResponseHandler(tornado.web.RequestHandler):
    @tornado.web.asynchronous
    @tornado.gen.coroutine
    def get(self, *args, **kwargs):

        url = ‘https://api.github.com/’
        http_client = tornado.httpclient.AsyncHTTPClient()
        response = yield tornado.gen.Task(http_client.fetch, url)
        print response.code
        print response.body

AsyncHTTPClient 转发
使用Tornado经常需要做一些转发服务,需要借助AsyncHTTPClient。既然是转发,就不可能只有get方法,post,put,delete等方法也会有。此时涉及到一些 headers和body,甚至还有https的waring。
下面请看一个post的例子, yield结果,通常,使用yield的时候,handler是需要 tornado.gen.coroutine。
headers = self.request.headers
body = json.dumps({‘name’: ‘rsj217’})
http_client = tornado.httpclient.AsyncHTTPClient()

resp = yield tornado.gen.Task(
    self.http_client.fetch, 
    url,
    method=”POST”, 
    headers=headers,
    body=body, 
    validate_cert=False)

AsyncHTTPClient 构造请求
如果业务处理并不是在handlers写的,而是在别的地方,当无法直接使用tornado.gen.coroutine的时候,可以构造请求,使用callback的方式。

body = urllib.urlencode(params)
req = tornado.httpclient.HTTPRequest(
  url=url, 
  method=’POST’, 
  body=body, 
  validate_cert=False)  

http_client.fetch(req, self.handler_response)

def handler_response(self, response):
    
    print response.code

用法也比较简单,AsyncHTTPClient中的fetch方法,第一个参数其实是一个HTTPRequest实例对象,因此对于一些和http请求有关的参数,例如method和body,可以使用HTTPRequest先构造一个请求,再扔给fetch方法。通常在转发服务的时候,如果开起了validate_cert,有可能会返回599timeout之类,这是一个warning,官方却认为是合理的。
AsyncHTTPClient 上传图片
AsyncHTTPClient 更高级的用法就是上传图片。例如服务有一个功能就是请求第三方服务的图片OCR服务。需要把用户上传的图片,再转发给第三方服务。
class ApiAccountUploadHandler(helper.BaseHandler):
    @tornado.gen.coroutine
    @helper.token_require
    def post(self, *args, **kwargs):
        upload_type = self.get_argument(‘type’, None)

        files_body = self.request.files[‘file’]
        
        new_file = ‘upload/new_pic.jpg’
        new_file_name = ‘new_pic.jpg’

        # 写入文件
        with open(new_file, ‘w’) as w:
            w.write(file_[‘body’])

        logging.info(‘user {} upload {}’.format(user_id, new_file_name))

        # 异步请求 上传图片
        with open(new_file, ‘rb’) as f:
            files = [(‘image’, new_file_name, f.read())]

        fields = ((‘api_key’, KEY), (‘api_secret’, SECRET))

        content_type, body = encode_multipart_formdata(fields, files)

        headers = {“Content-Type”: content_type, ‘content-length’: str(len(body))}
        request = tornado.httpclient.HTTPRequest(config.OCR_HOST,
                                                 method=”POST”, headers=headers, body=body, validate_cert=False)

        response = yield tornado.httpclient.AsyncHTTPClient().fetch(request)

def encode_multipart_formdata(fields, files):
    “””
    fields is a sequence of (name, value) elements for regular form fields.
    files is a sequence of (name, filename, value) elements for data to be
    uploaded as files.
    Return (content_type, body) ready for httplib.HTTP instance
    “””
    boundary = ‘———-ThIs_Is_tHe_bouNdaRY_$’
    crlf = ‘\r\n’
    l = []
    for (key, value) in fields:
        l.append(‘–‘ + boundary)
        l.append(‘Content-Disposition: form-data; name=”%s”‘ % key)
        l.append(”)
        l.append(value)
    for (key, filename, value) in files:
        filename = filename.encode(“utf8”)
        l.append(‘–‘ + boundary)
        l.append(
                ‘Content-Disposition: form-data; name=”%s”; filename=”%s”‘ % (
                    key, filename
                )
        )
        l.append(‘Content-Type: %s’ % get_content_type(filename))
        l.append(”)
        l.append(value)
    l.append(‘–‘ + boundary + ‘–‘)
    l.append(”)
    body = crlf.join(l)
    content_type = ‘multipart/form-data; boundary=%s’ % boundary
    return content_type, body

def get_content_type(filename):
    import mimetypes

    return mimetypes.guess_type(filename)[0] or ‘application/octet-stream’

对比上述的用法,上传图片仅仅是多了一个图片的编码。将图片的二进制数据按照multipart 方式编码。编码的同时,还需要把传递的相关的字段处理好。相比之下,使用requests 的方式则非常简单:

files = {}
f = open(‘/Users/ghost/Desktop/id.jpg’)
files[‘image’] = f
data = dict(api_key=’KEY’, api_secret=’SECRET’)
resp = requests.post(url, data=data, files=files)
f.close()
print resp.status_Code

总结
通过AsyncHTTPClient的使用方式,可以轻松的实现handler对第三方服务的请求。结合前面关于tornado异步的使用方式。无非还是两个key。是否需要返回结果,来确定使用callback的方式还是yield的方式。当然,如果不同的函数都yield,yield也可以一直传递。这个特性,tornado的中的tornado.auth 里面对oauth的认证。
大致就是这样的用法。

Tornado异步笔记(一)— 异步任务

 tornado  Tornado异步笔记(一)— 异步任务已关闭评论
12月 062018
 

网上看到的3篇连载关于tornado异步的文章,写的很好,虽然有些内容有些老了,但不影响文章的借鉴意义,强烈推荐:

转自:https://www.jianshu.com/p/31fae7dd05ba

高性能服务器Tornado
Python的web框架名目繁多,各有千秋。正如光荣属于希腊,伟大属于罗马。Python的优雅结合WSGI的设计,让web框架接口实现千秋一统。WSGI 把应用(Application)和服务器(Server)结合起来。Django 和 Flask 都可以结合 gunicon 搭建部署应用。
与 django 和 flask 不一样,tornado 既可以是 wsgi 应用,也可以是 wsgi 服务。当然,选择tornado更多的考量源于其单进程单线程异步IO的网络模式。高性能往往吸引人,可是有不少朋友使用之后会提出疑问,tornado号称高性能,实际使用的时候却怎么感受不到呢?
实际上,高性能源于Tornado基于Epoll(unix为kqueue)的异步网络IO。因为tornado的单线程机制,一不小心就容易写出阻塞服务(block)的代码。不但没有性能提高,反而会让性能急剧下降。因此,探索tornado的异步使用方式很有必要。
Tornado 异步使用方式
简而言之,Tornado的异步包括两个方面,异步服务端和异步客户端。无论服务端和客户端,具体的异步模型又可以分为回调(callback)和协程(coroutine)。具体应用场景,也没有很明确的界限。往往一个请求服务里还包含对别的服务的客户端异步请求。
服务端异步方式
服务端异步,可以理解为一个tornado请求之内,需要做一个耗时的任务。直接写在业务逻辑里可能会block整个服务。因此可以把这个任务放到异步处理,实现异步的方式就有两种,一种是yield挂起函数,另外一种就是使用类线程池的方式。请看一个同步例子:

class SyncHandler(tornado.web.RequestHandler):

    def get(self, *args, **kwargs):
        # 耗时的代码
        os.system(“ping -c 2 www.google.com”)
        self.finish(‘It works’)

使用ab测试一下:
ab -c 5 -n 5 http://127.0.0.1:5000/sync

Server Software:        TornadoServer/4.3
Server Hostname:        127.0.0.1
Server Port:            5000

Document Path:          /sync
Document Length:        5 bytes

Concurrency Level:      5
Time taken for tests:   5.076 seconds
Complete requests:      5
Failed requests:        0
Total transferred:      985 bytes
HTML transferred:       25 bytes
Requests per second:    0.99 [#/sec] (mean)
Time per request:       5076.015 [ms] (mean)
Time per request:       1015.203 [ms] (mean, across all concurrent requests)
Transfer rate:          0.19 [Kbytes/sec] received
qps 仅有可怜的 0.99,姑且当成每秒处理一个请求吧。
下面祭出异步大法:
class AsyncHandler(tornado.web.RequestHandler):
    @tornado.web.asynchronous
    @tornado.gen.coroutine
    def get(self, *args, **kwargs):

        tornado.ioloop.IOLoop.instance().add_timeout(1, callback=functools.partial(self.ping, ‘www.google.com’))

        # do something others

        self.finish(‘It works’)

    @tornado.gen.coroutine
    def ping(self, url):
        os.system(“ping -c 2 {}”.format(url))
        return ‘after’
尽管在执行异步任务的时候选择了timeout 1秒,主线程的返回还是很快的。ab压测如下:
Document Path:          /async
Document Length:        5 bytes

Concurrency Level:      5
Time taken for tests:   0.009 seconds
Complete requests:      5
Failed requests:        0
Total transferred:      985 bytes
HTML transferred:       25 bytes
Requests per second:    556.92 [#/sec] (mean)
Time per request:       8.978 [ms] (mean)
Time per request:       1.796 [ms] (mean, across all concurrent requests)
Transfer rate:          107.14 [Kbytes/sec] received

上述的使用方式,通过tornado的IO循环,把可以把耗时的任务放到后台异步计算,请求可以接着做别的计算。可是,经常有一些耗时的任务完成之后,我们需要其计算的结果。此时这种方式就不行了。车道山前必有路,只需要切换一异步方式即可。下面使用协程来改写:
class AsyncTaskHandler(tornado.web.RequestHandler):
    @tornado.web.asynchronous
    @tornado.gen.coroutine
    def get(self, *args, **kwargs):
        # yield 结果
        response = yield tornado.gen.Task(self.ping, ‘ www.google.com’)
        print ‘response’, response
        self.finish(‘hello’)

    @tornado.gen.coroutine
    def ping(self, url):
        os.system(“ping -c 2 {}”.format(url))
        return ‘after’
可以看到异步在处理,而结果值也被返回了。

Server Software:        TornadoServer/4.3
Server Hostname:        127.0.0.1
Server Port:            5000

Document Path:          /async/task
Document Length:        5 bytes

Concurrency Level:      5
Time taken for tests:   0.049 seconds
Complete requests:      5
Failed requests:        0
Total transferred:      985 bytes
HTML transferred:       25 bytes
Requests per second:    101.39 [#/sec] (mean)
Time per request:       49.314 [ms] (mean)
Time per request:       9.863 [ms] (mean, across all concurrent requests)
Transfer rate:          19.51 [Kbytes/sec] received

qps提升还是很明显的。有时候这种协程处理,未必就比同步快。在并发量很小的情况下,IO本身拉开的差距并不大。甚至协程和同步性能差不多。例如你跟博尔特跑100米肯定输给他,可是如果跟他跑2米,鹿死谁手还未定呢。
yield挂起函数协程,尽管没有block主线程,因为需要处理返回值,挂起到响应执行还是有时间等待,相对于单个请求而言。另外一种使用异步和协程的方式就是在主线程之外,使用线程池,线程池依赖于futures。Python2需要额外安装。
下面使用线程池的方式修改为异步处理:
from concurrent.futures import ThreadPoolExecutor

class FutureHandler(tornado.web.RequestHandler):
    executor = ThreadPoolExecutor(10)

    @tornado.web.asynchronous
    @tornado.gen.coroutine
    def get(self, *args, **kwargs):

        url = ‘www.google.com’
        tornado.ioloop.IOLoop.instance().add_callback(functools.partial(self.ping, url))
        self.finish(‘It works’)

    @tornado.concurrent.run_on_executor
    def ping(self, url):
        os.system(“ping -c 2 {}”.format(url))

再运行ab测试:
Document Path:          /future
Document Length:        5 bytes

Concurrency Level:      5
Time taken for tests:   0.003 seconds
Complete requests:      5
Failed requests:        0
Total transferred:      995 bytes
HTML transferred:       25 bytes
Requests per second:    1912.78 [#/sec] (mean)
Time per request:       2.614 [ms] (mean)
Time per request:       0.523 [ms] (mean, across all concurrent requests)
Transfer rate:          371.72 [Kbytes/sec] received

qps瞬间达到了1912.78。同时,可以看到服务器的log还在不停的输出ping的结果。
想要返回值也很容易。再切换一下使用方式接口。使用tornado的gen模块下的with_timeout功能(这个功能必须在tornado>3.2的版本)。
class Executor(ThreadPoolExecutor):
    _instance = None

    def __new__(cls, *args, **kwargs):
        if not getattr(cls, ‘_instance’, None):
            cls._instance = ThreadPoolExecutor(max_workers=10)
        return cls._instance

class FutureResponseHandler(tornado.web.RequestHandler):
    executor = Executor()

    @tornado.web.asynchronous
    @tornado.gen.coroutine
    def get(self, *args, **kwargs):

        future = Executor().submit(self.ping, ‘www.google.com’)

        response = yield tornado.gen.with_timeout(datetime.timedelta(10), future,
                                                  quiet_exceptions=tornado.gen.TimeoutError)

        if response:
            print ‘response’, response.result()

    @tornado.concurrent.run_on_executor
    def ping(self, url):
        os.system(“ping -c 1 {}”.format(url))
        return ‘after’

线程池的方式也可以通过使用tornado的yield把函数挂起,实现了协程处理。可以得出耗时任务的result,同时不会block住主线程。
Concurrency Level:      5
Time taken for tests:   0.043 seconds
Complete requests:      5
Failed requests:        0
Total transferred:      960 bytes
HTML transferred:       0 bytes
Requests per second:    116.38 [#/sec] (mean)
Time per request:       42.961 [ms] (mean)
Time per request:       8.592 [ms] (mean, across all concurrent requests)
Transfer rate:          21.82 [Kbytes/sec] received

qps为116,使用yield协程的方式,仅为非reponse的十分之一左右。看起来性能损失了很多,主要原因这个协程返回结果需要等执行完毕任务。
好比打鱼,前一种方式是撒网,然后就完事,不闻不问,时间当然快,后一种方式则撒网之后,还得收网,等待收网也是一段时间。当然,相比同步的方式还是快了千百倍,毕竟撒网还是比一只只钓比较快。
具体使用何种方式,更多的依赖业务,不需要返回值的往往需要处理callback,回调太多容易晕菜,当然如果需要很多回调嵌套,首先优化的应该是业务或产品逻辑。yield的方式很优雅,写法可以异步逻辑同步写,爽是爽了,当然也会损失一定的性能。
异步多样化
Tornado异步服务的处理大抵如此。现在异步处理的框架和库也很多,借助redis或者celery等,也可以把tonrado中一些业务异步化,放到后台执行。
此外,Tornado还有客户端异步功能。该特性主要是在于 AsyncHTTPClient的使用。此时的应用场景往往是tornado服务内,需要针对另外的IO进行请求和处理。顺便提及,上述的例子中,调用ping其实也算是一种服务内的IO处理。接下来,将会探索一下AsyncHTTPClient的使用,尤其是使用AsyncHTTPClient上传文件与转发请求。

AWS SSH使用证书登录时出现:Permission denied (publickey). (之前能够连接,而现在却不能连接)问题解决

 aws  AWS SSH使用证书登录时出现:Permission denied (publickey). (之前能够连接,而现在却不能连接)问题解决已关闭评论
12月 062018
 

使用ssh -i /path/my-key-pair.pem ec2-user@public-dns-hostname
出现:Permission denied (publickey). 

出现这样的错误,先确认下手头的私钥pem文件确实对应自己的EC2实例。然后可以通过下面几个步骤排查:

1. 登录所使用的用户名是否正确?
正确的用户名如下所示:
对于 Amazon Linux 2 或 Amazon Linux AMI,用户名称是 ec2-user。
对于 Centos AMI,用户名称是 centos。
对于 Debian AMI,用户名称是 admin 或 root。
对于 Fedora AMI,用户名为 ec2-user 或 fedora。
对于 RHEL AMI,用户名称是 ec2-user 或 root。
对于 SUSE AMI,用户名称是 ec2-user 或 root。
对于 Ubuntu AMI,用户名称是 ubuntu。
另外,如果 ec2-user 和 root 无法使用,请与 AMI 供应商核实。
例如,要使用 SSH 客户端连接到从 Amazon Linux 实例,请使用以下命令:
ssh -i /path/my-key-pair.pem ec2-user@public-dns-hostname

2. 如果登录时还看到类似以下的错误:
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
@         WARNING: UNPROTECTED PRIVATE KEY FILE!          @
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
Permissions 0777 for ‘.ssh/my_private_key.pem’ are too open.
It is required that your private key files are NOT accessible by others.
This private key will be ignored.
bad permissions: ignore key: .ssh/my_private_key.pem
Permission denied (publickey).

需要使用下面命令调整证书文件权限
[ec2-user ~]$ chmod 0400 .ssh/my_private_key.pem

3. 如果上面的步骤还是没解决问题,而且可能是之前能够连接,而现在却不能连接了,则可能是实例主目录的权限发生了更改。/home/ec2-user/.ssh/authorized_keys 的目录及文件权限必须限制为仅限所有者
具体操作如下(以 Amazon Linux 实例为例,用户名ec2-user):
chmod 600 /home/ec2-user/.ssh/authorized_keys
chmod 700 /home/ec2-user/.ssh
chmod 700 /home/ec2-user
(测试发现700权限的扩大到755也可以)

Done!!!

*******************************************************
附录1:打开AWS用户名密码登录方式,此处以root登录为例


1.首先 用密钥登陆 
2.给 root 设置密码 sudo passwd root
3.密码设置好后 切换到root用户 su root
4.修改ssh配置文件,允许密码登录
 vim /etc/ssh/sshd_config 
将 passwordAuthentication no 改为  passwordAuthentication yes
将PermitRootLogin 改为yes 此处表示允许root登录(当然如果使用其它用户登录可以保持这项为no,但记得赋予新用户有sudo权限方便修改恢复配置)

#其它参数说明:
# 是否让 sshd 去检查用户家目录或相关档案的权限数据,
# 这是为了担心使用者将某些重要档案的权限设错,可能会导致一些问题所致。
# 例如使用者的 ~.ssh/ 权限设错时,某些特殊情况下会不许用户登入
#StrictModes no

# 是否允许用户自行使用成对的密钥系统进行登入行为,仅针对 version 2。
# 至于自制的公钥数据就放置于用户家目录下的 .ssh/authorized_keys 内
#RSAAuthentication yes
#PubkeyAuthentication yes
#AuthorizedKeysFile      %h/.ssh/authorized_keys

5 重启 ssh service sshd restart

********************************************************
附录2:更新替换AWS密钥/证书(以 AWS ubuntu举例)


1.在aws后台生成并下载密钥,然后将密钥保存到自己电脑。

2.执行ssh-keygen -y,会提示要求输入密钥路径, 复制密钥路径,回车得到public key,例如
ssh-rsa AAAAB3NzaC1yc2EAAAADAQA231BAQCMASwerewg23XvhyGzydp0V234lP/fyfuhsHKMECZydc5ewytvTq0mqYTfjKBS++PeBpEL1Zx/ilEYCmgY6omTrIMtG8s1jf/lAk0l9++f2ldp/w2U86seARyRxVEexxxyuwJJDASDHNiEbshXQ6M49nUsE6tfETG3sFl+XDeva0lkNkssA4JDU+eivPRGma3XcBAXvsUsD8VkKQJvudrpJDSjjncdjYOVd2Wcrcj5Li8MmLvIkEX1pmqTT6O6oUfEtCdpVi4tCwTXV5ydU8UtjJDSGDFSJgbY9Unve4LgjgoWF677FdUpvVFD1NPoLH

3.利用以前的密钥/用户名密码登录服务器,将上面第二步的public key粘贴到~/.ssh/authorized_keys。 
比如: aws ubuntu系统默认用户名ubuntu位置在/home/ubuntu/.ssh/authorized_keys,如没有.ssh目录则新建目录, 
然后将旧的public key注释或者删除。

4.注意将上面3所在目录和文件修改为正确的权限
chmod 600 /home/ubuntu/.ssh/authorized_keys
chmod 700 /home/ubuntu/.ssh
chmod 700 /home/ubuntu

4.然后你就可以利用新的密钥文件(.pem后缀文件)登录服务器了 
例如:ssh -i /path/my-key-pair.pem ubuntu@public-dns-hostname