0%

TortoiseGit使用扩展名为ppk的密钥,而不是ssh-keygen生成的rsa密钥。使用命令ssh-keygen -C "邮箱地址" -t rsa产生的密钥在TortoiseGit中不能用。

常见操作是用TortoiseGit自带的工具putty key generator来生成对应的ppk文件,但是因为开发中遇到需要部署key到服务器,引用多个key自然会造成管理困难.所以这里介绍另一种方法.

在TortoiseGit安装过程中,其实可以选择使用PuttyKey还是OpenSSH的key,如果是新安装,不妨注意选择一下.实际操作往往是安装之后需要修改,怎么办呢?

  • 打开TortoiseGit的设置,选择网络选项卡
  • 右边的SSH Client就是切换ssh加密客户端的设置
  • 选择{Git安装目录}/usr/bin/ssh.exe
  • 确定或者应用

这个时候,TortoiseGit就是使用OpenSSH的key啦,也就是~/.ssh/id_rsa

然后再说说怎么切换回使用PuttyKey,很简单,将上述第3步骤选择成{TortoiseGit安装目录}/bin/TortoiseGitPlink.exe,确定或者应用就完成OpenSSH到PuttyKey的切换啦!

##一、问题及原因分析

最近一个老的应用被部署到jetty后定时出现404错误,

老应用的代码比较糟糕,也出过不少问题,本以为是程序问题、

查询负载、线程、dump文件、io都没有明显异常,挺奇怪一直没找到原因

查看了jetty启动是war解压目录/tmp/jetty-0.0.0.0-8100-hrs-web.war-_hrs-any-/webapp

因为在/tmp目录,应该是有问题,linux会清除/tmp目录。

至于/tmp目录多久清除一次,看

1
cat /etc/cron.daily/tmpwatch
1
2
3
4
5
6
7
8
9
10
flags=-umc                                                                                                                                                              
/usr/sbin/tmpwatch "$flags" -x /tmp/.X11-unix -x /tmp/.XIM-unix \
-x /tmp/.font-unix -x /tmp/.ICE-unix -x /tmp/.Test-unix \
-X '/tmp/hsperfdata_*' 720 /tmp
/usr/sbin/tmpwatch "$flags" 720 /var/tmp
for d in /var/{cache/man,catman}/{cat?,X11R6/cat?,local/cat?}; do
if [ -d "$d" ]; then
/usr/sbin/tmpwatch "$flags" -f 720 "$d"
fi
done

二、解决方法

问题找到了,那如何指定war解压目录? 在网上找到一份详细说明,如下:

http://docs.codehaus.org/display/JETTY/Temporary+Directories

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
Jetty itself has no temporary directories, but each web application can be assigned a directory into which the war is unpacked, JSPs compiled on-the-fly etc.

The algorithm for determining a webapp's temporary directory location is as follows:

Try to use an explicit directory specifically for this webapp:
Iff webapp.getTempDirectory() is set, use it. Do NOT delete it on jvm exit.
Iff javax.servlet.context.tempdir context attribute is set for this webapp && exists && writeable, then use it. Do NOT delete on jvm exit.
Create a directory based on global settings. The new directory will be called "Jetty_"host""port""context""+virtualhost :
Iff $(jetty.home)/work exists create the directory there. Do NOT delete on jvm exit. Do NOT delete contents if dir already exists.
Iff WEB-INF/work exists create the directory there. Do NOT delete on jvm exit. Do NOT delete contents if dir already exists.
Else create dir in $(java.io.tmpdir). Set delete on jvm exit. Delete contents if dir already exists.

It is important to note that a temporary directory will have its contents deleted when the webapp is stopped unless either:

it is called "work"
it pre-existed the deployment of the webapp

Once a tempory directory has been allocated, a File instance for it is set and retrievable as the javax.servlet.context.tempdir attribute of the web application.

意思是如果$(jetty.home)/work文件夹存在,则Jetty会使用该目录作为工作目录。所以,最后我选择了在$(jetty.home)/work下建立一个统一的work目录,这样最方便部署,jetty 每台机子统一安装就可以。

另外也可以直接通过改变JAVA_OPTIONS指定临时目录:

jetty.sh

1
2
3
4
#####################################################
# Add jetty properties to Java VM options.
#####################################################
JAVA_OPTIONS+=("-Djetty.home=$JETTY_HOME" "-Djava.io.tmpdir=$TMPDIR")

Google在5月中旬发布了AndroidStudio的第三个大版本的预览版 Android Studio 3.0 Canary 1 ,AndroidStudio3的版本里面最让人期待的有两点,一个是对于Kotlin语言的支持,另一个是结合Gradle 4.x的编译支持.后者可以大大缩短项目编译时间.

本人在收到AndroidStudio推送后第一时间下载并且体验了一把,在Canary 1和Canary 2由于莫名其妙的故障太多也就没有深入研究,直到6月初发布了Canary 3,才决定正式切入项目.

在升级Gradle和 com.android.tools.build 编译插件之后,发现原来的输出文件名自动配置,输出版本号自动配置一个报错一个没有用了,大写的尴尬.

于是想到用2.x的编译插件和3.0的IDE结合使用,可是理想很丰满,现实却很骨感,2.x的编译插件不支持Gradle4.+版本,无奈放弃.

在折腾一点时间后,输出文件名是搞定了,动态版本号确实无能为力.故而把其中的差异总结以便日后查阅.

替换文件名

在 com.android.tools.build:gradle:2.x.x版本里,配置输出文件名通常是这样的

1
2
3
4
5
6
7
8
9
android {
...
applicationVariants.all { variant ->
variant.outputs.each { output ->
// 这里的file就是最终输出的文件
def file = output.outputFile
}
}
}

其中的outputFile就是最终文件,我们可以对其重新赋值以实现动态控制文件名

而在 com.android.tools.build:gradle:3.x.x版本里,继续使用上面的代码Gradle插件便会报错,提示getMainOutputFile()已经弃用,需要使用getOutputFileName()方法,问题是正常情况下,可以点击该方法查看到改方法涉及的字段内容,以致于最后硬着头皮写了这个方法死马当活马医发现竟然编译成功了...于是上面的代码就是这样的了

1
2
3
4
5
6
7
8
9
android {
...
applicationVariants.all { variant ->
variant.outputs.each { output ->
// 这里的name就是最终输出的文件名
def name = output.outputFileName
}
}
}

里面的字段属性也可以用get/set获取和使用,例如

1
2
3
4
5
6
7
8
9
10
11
12
13
14
android {
...
applicationVariants.all { variant ->
variant.outputs.each { output ->
// gradle:3.0.0-alpha1 以前是替换文件
def file = output.getOutputFile()
output.setOutputFile(file)

// gradle:3.0.0-alpha1 以后是替换文件名
def name = output.getOutputFileName()
output.setOutputFileName(name)
}
}
}

版本号

在 com.android.tools.build:gradle:2.x.x版本里,配置版本号一般是这样写:

1
2
3
4
5
6
7
8
9
10
android {
...
applicationVariants.all { variant ->
// 方法一
defaultConfig.versionName += "(测试版)"

// 方法二
variant.mergedFlavor.versionName += "(测试版)"
}
}

上面只是举例子,编译后Apk的versionName就会加上 "(测试版)" 的字样妥妥的

但是在com.android.tools.build:gradle:3.x.x版本里,目前无论我怎样写,都没有成功,最后查看编译插件源代码发现

1
2
3
4
5
6
7
8
9
public interface ProductFlavor extends BaseConfig, DimensionAware {
...

Integer getVersionCode();

String getVersionName();

...
}

吖只提供了get方法,没有set方法...没办法,只能先放弃了

好了,这是截至 Android Studio 3.0 Canary 3 我的一些心得,如果大家找到了解决方案,还请能够交流学习一下~~~

so库是Linux的动态链接库文件,在开发App某些功能的时候,会使用加载so库的方式运行一些特殊的代码.一般情况下,我们都会编译出基本所有平台的so库,但也有可能不兼容某些CPU而不想使用该版本so库的情况.

背景知识:

apk包在安装的时候,系统会把包中与自己的abi对应的lib目录中的so库文件拷贝到system分区中,32位机器中只有一个目录/system/lib,64位机器中有两个目录/system/lib和/system/lib64,app启动进行链接时,64位机器中会先到/system/lib64目录中去找,如果没有找到再到/system/lib目录中去找。如果你把32位的so库拷贝到了lib64目录中,会导致链接失败,同样,64位的so库被拷贝到lib目录中也会导致失败,所以so库要和目录一一对应。

如果我们的工程的so库目录中没有arm64目录,默认情况下,Android Studio会在apk中自动创建一个空的arm64-v8a文件夹,并根据一些规则把某些so库(具体是怎样的规则,我也不知道)拷贝到这个目录中,这样就导致,在64位机器上在lib64目录下找到的so库并不是正确的so库文件,从而出现了上面的错误。

引用腾讯X5内核的处理方式(它就没有提供64位的so):

64位手机无法加载x5(libmttwebview.so is 32-bit instead of 64-bit)

x5内核暂时不提供64位的so文件,在64位手机上需要让AP以32位模式运行。具体操作如下: 1.如果使用是Eclipse则需要将所有的.so文件都放置在so加载目录:lib/armeabi文件夹下(没有该目录则新建一个,

AP中没有使用到.so文件则需要拷贝任意一个32位的so文件到该目录下,如果没有合适的so可以到官网

http://x5.tencent.com/tbs/sdk.html

下载官网“SDK接入示例“,拷贝对应目录下的liblbs.so文件),

lib文件夹下不要有其他以”armeabi“开头的文件夹。

2.如果使用的是 Android studio则需要进行两项配置 (1)打开对应module中的build.gradle文件,在文件的android{}中的defaultConfig{}里(如果没有defaultConfig{}则手动添加)添加如下配置:

1
2
3
4
5
6
7
8
9
10
11
android {
......

defaultConfig {
......

ndk {
abiFilters "armeabi", "armeabi-v7a", "x86", "mips"
}
}
}

如果配置后编译报错,那么需要在gradle.properties文件中加上

1
Android.useDeprecatedNdk=true

(2)找出build.gradle中配置的so加载目录:jniLibs.srcDir:customerDir,如果没有该项配置则so加载目录默认为:

1
src/main/jniLibs

需要将.so文件都放置在so加载目录的armeabi文件夹下(没有该目录则新建一个,AP中没有使用到.so文件则需要拷贝任意一个32位的so文件到该目录下,

如果没有合适的so可以到官网

http://x5.tencent.com/tbs/sdk.html

下载官网“SDK接入示例“,拷贝对应目录下的liblbs.so文件),

so加载目录下不要有其他以”armeabi“开头的文件夹。

可参考论坛回复:

http://bbs.mb.qq.com/thread-1473368-1-1.html

如果仍未能解决您的问题,请直接在论坛回复并描述您的问题

总结: 可以看出,上面的方法均是阻止编译Apk时打包进64位的jni库文件夹和库.所以,使用上面的方法之后,App可能也不能使用其他正常的64位so库的.好在64位CPU是兼容32位so库的,在编写代码的时候就得注意一下.

1.参数的含义

1
2
3
4
5
6
-vmargs -Xms128M -Xmx512M -XX:PermSize=64M -XX:MaxPermSize=128M
-vmargs 说明后面是VM的参数,所以后面的其实都是JVM的参数了
-Xms128m JVM初始分配的堆内存
-Xmx512m JVM最大允许分配的堆内存,按需分配
-XX:PermSize=64M JVM初始分配的非堆内存
-XX:MaxPermSize=128M JVM最大允许分配的非堆内存,按需分配

我们首先了解一下JVM内存管理的机制,然后再解释每个参数代表的含义。

1)堆(Heap)和非堆(Non-heap)内存

按照官方的说法:“Java 虚拟机具有一个堆,堆是运行时数据区域,所有类实例和数组的内存均从此处分配。堆是在 Java 虚拟机启动时创建的。”“在JVM中堆之外的内存称为非堆内存(Non-heap memory)”。 可以看出JVM主要管理两种类型的内存:堆和非堆。简单来说堆就是Java代码可及的内存,是留给开发人员使用的;非堆就是JVM留给自己用的, 所以方法区、JVM内部处理或优化所需的内存(如JIT编译后的代码缓存)、每个类结构(如运行时常数池、字段和方法数据)以及方法和构造方法的代码都在非堆内存中。

堆内存分配

JVM初始分配的堆内存由-Xms指定,默认是物理内存的1/64;JVM最大分配的堆内存由-Xmx指定,默认是物理内存的1/4。默认空余堆内存小于40%时,JVM就会增大堆直到-Xmx的最大限制; 空余堆内存大于70%时,JVM会减少堆直到-Xms的最小限制。因此服务器一般设置-Xms、-Xmx 相等以避免在每次GC 后调整堆的大小。 说明:如果-Xmx 不指定或者指定偏小,应用可能会导致java.lang.OutOfMemory错误,此错误来自JVM,不是Throwable的,无法用try...catch捕捉。

非堆内存分配

JVM使用-XX:PermSize设置非堆内存初始值,默认是物理内存的1/64;由XX:MaxPermSize设置最大非堆内存的大小,默认是物理内存的1/4。(还有一说:MaxPermSize缺省值和-server -client选项相关, -server选项下默认MaxPermSize为64m,-client选项下默认MaxPermSize为32m。这个我没有实验。) 上面错误信息中的PermGen space的全称是Permanent Generation space,是指内存的永久保存区域。还没有弄明白PermGen space是属于非堆内存,还是就是非堆内存,但至少是属于了。 XX:MaxPermSize设置过小会导致java.lang.OutOfMemoryError: PermGen space 就是内存益出。 说说为什么会内存益出:

(1)这一部分内存用于存放Class和Meta的信息,Class在被 Load的时候被放入PermGen space区域,它和存放Instance的Heap区域不同。

(2)GC(Garbage Collection)不会在主程序运行期对PermGen space进行清理,所以如果你的APP会LOAD很多CLASS 的话,就很可能出现PermGen space错误。 这种错误常见在web服务器对JSP进行pre compile的时候。

2. JVM内存限制(最大值)

1. 首先JVM内存限制于实际的最大物理内存,假设物理内存无限大的话,JVM内存的最大值跟操作系统有很大的关系。简单的说就32位处理器虽然可控内存空间有4GB,但是具体的操作系统会给一个限制,

这个限制一般是2GB-3GB(一般来说Windows系统下为1.5G-2G,Linux系统下为2G-3G),而64bit以上的处理器就不会有限制了。

2. 为什么有的机器我将-Xmx和-XX:MaxPermSize都设置为512M之后Eclipse可以启动,而有些机器无法启动?

通过上面对JVM内存管理的介绍我们已经了解到JVM内存包含两种:堆内存和非堆内存,另外JVM最大内存首先取决于实际的物理内存和操作系统。所以说设置VM参数导致程序无法启动主要有以下几种原因:

  1. 参数中-Xms的值大于-Xmx,或者-XX:PermSize的值大于-XX:MaxPermSize;
  2. -Xmx的值和-XX:MaxPermSize的总和超过了JVM内存的最大限制,比如当前操作系统最大内存限制,或者实际的物理内存等等。说到实际物理内存这里需要说明一点的是,如果你的内存是1024MB,但实际系统中用到的并不可能是1024MB,因为有一部分被硬件占用了。

3. 为何将上面的参数写入到eclipse.ini文件Eclipse没有执行对应的设置?

那为什么同样的参数在快捷方式或者命令行中有效而在eclipse.ini文件中是无效的呢?这是因为我们没有遵守eclipse.ini文件的设置规则: 参数形如“项 值”这种形式,中间有空格的需要换行书写,如果值中有空格的需要用双引号包括起来。比如我们使用-vm C:/Java/jre1.6.0/bin/javaw.exe参数设置虚拟机, 在eclipse.ini文件中要写成这样:

1
2
3
4
5
6
-vm C:/Java/jre1.6.0/bin/javaw.exe 
-vmargs
-Xms128M
-Xmx512M
-XX:PermSize=64M
-XX:MaxPermSize=128M

实际运行的结果可以通过Eclipse中“Help”-“About Eclipse SDK”窗口里面的“Configuration Details”按钮进行查看。 另外需要说明的是,Eclipse压缩包中自带的eclipse.ini文件内容是这样的:

1
2
3
4
5
-showsplash org.eclipse.platform 
--launcher.XXMaxPermSize 256m
-vmargs
-Xms40m
-Xmx256m

其中–launcher.XXMaxPermSize(注意最前面是两个连接线)跟-XX:MaxPermSize参数的含义基本是一样的,我觉得唯一的区别就是前者是eclipse.exe启动的时候设置的参数, 而后者是eclipse所使用的JVM中的参数。其实二者设置一个就可以了,所以这里可以把–launcher.XXMaxPermSize和下一行使用#注释掉。

4. 其他的启动参数。 如果你有一个双核的CPU,也许可以尝试这个参数:

1
-XX:+UseParallelGC

让GC可以更快的执行。(只是JDK 5里对GC新增加的参数)

补充:   如果你的WEB APP下都用了大量的第三方jar,其大小超过了服务器jvm默认的大小,那么就会产生内存益出问题了。 解决方法: 设置MaxPermSize大小 可以在myelipse里选中相应的服务器比如tomcat5,展开里面的JDK子项页面,来增加服务器启动的JVM参数设置:

1
2
3
4
5
-Xms128m 
-Xmx256m
-XX:PermSize=128M
-XX:MaxNewSize=256m
-XX:MaxPermSize=256m

或者手动设置MaxPermSize大小,比如tomcat, 修改TOMCAT_HOME/bin/catalina.bat,在echo "Using CATALINA_BASE: $CATALINA_BASE"上面加入以下行:

1
JAVA_OPTS="-server -XX:PermSize=64M -XX:MaxPermSize=128m

建议:将相同的第三方jar文件移置到tomcat/shared/lib目录下,这样可以减少jar 文档重复占用内存

#一、gzip介绍

Gzip是一种流行的文件压缩算法,现在的应用十分广泛,尤其是在Linux平台。当应用Gzip压缩到一个纯文本文件时,效果是非常明显的,大约可以减少70%以上的文件大小。这取决于文件中的内容。 利用Apache中的Gzip模块,我们可以使用Gzip压缩算法来对Apache服务器发布的网页内容进行压缩后再传输到客户端浏览器。这样经过压缩后实际上降低了网络传输的字节数,最明显的好处就是可以加快网页加载的速度。

网页加载速度加快的好处不言而喻,除了节省流量,改善用户的浏览体验外,另一个潜在的好处是Gzip与搜索引擎的抓取工具有着更好的关系。

HTTP协议上的GZIP编码是一种用来改进web应 用程序性能的技术。大流量的WEB站点常常使用GZIP压缩技术来让用户感受更快的速度。这一般是指WWW服务器中安装的一个功能,当有人来访问这个服务 器中的网站时,服务器中的这个功能就将网页内容压缩后传输到来访的电脑浏览器中显示出来.一般对纯文本内容可压缩到原大小的40%.这样传输就快了,效果 就是你点击网址后会很快的显示出来.当然这也会增加服务器的负载. 一般服务器中都安装有这个功能模块的。

减少文件大小有两个明显的好处,一是可以减少存储空间,二是通过网络传输文件时,可以减少传输的时间。gzip 是在 Linux 系统中经常使用的一个对文件进行压缩和解压缩的命令,既方便又好用。 #二、Web服务器处理

HTTP压缩的过程如下:

Web服务器接收到浏览器的HTTP请求后,检查浏览器是否支持HTTP压缩(Accept-Encoding 信息);

如果浏览器支持HTTP压缩,Web服务器检查请求文件的后缀名;

如果请求文件是HTML、CSS等静态文件,Web服务器到压缩缓冲目录中检查是否已经存在请求文件的最新压缩文件;

如果请求文件的压缩文件不存在,Web服务器向浏览器返回未压缩的请求文件,并在压缩缓冲目录中存放请求文件的压缩文件;

如果请求文件的最新压缩文件已经存在,则直接返回请求文件的压缩文件;

如果请求文件是动态文件,Web服务器动态压缩内容并返回浏览器,压缩内容不存放到压缩缓存目录中。

#三、开启Gzip

##Apache

Apache利用Gzip压缩算法进行压缩的模块有两种:mod_gzip 和mod_deflate

现在浏览器本身也自动Gzip压缩功能,支持Accept-Encoding: gzip,deflate,这里我在firefox浏览器下测试。

alt

通过查看HTTP头,我们可以快速判断使用的客户端浏览器是否支持接受gzip压缩。

若发送的HTTP头中出现以下信息,则表明你的浏览器支持接受相应的gzip压缩:

1
2
3
Accept-Encoding: gzip 支持mod_gzip
Accept-Encoding: deflate 支持mod_deflate
Accept-Encoding: gzip,deflate

同时支持mod_gzip 和mod_deflate Apache内置有mod_deflate模块来启用gzip功能,但假如安装apache的时候没有编译相关模块,就需要你手动安装一次,以启用它:

首先到你的apache源码目录,查找到mod_deflate.c文件 ,通常位置:apachehttpd源码目录/modules/filters/mod_deflate.c,进入上面找到的目录运行下面的命令:

1
/usr/local/apache2/bin/apxs -i -c -a mod_deflate.c

注:apxs目录请参照您自己的机器,通常在apache安装目录的bin目录下。

安装完成,到apache的modules目录看看是不是有了mod_deflates.so,httpd.conf中打开deflate_Module和headers_Module模块:

1
LoadModule deflate_module modules/mod_deflate.so

加载mod_deflate.so模块,默认安装会自动写入httpd.conf。

如果服务器开启了对Gzip组件的支持,那么我们就可以在http.conf定制文件压缩,下面是一个配置的简单实例:

1、mod_gzip方式

1
2
3
4
5
6
7
8
9
10
# mod_gzip
<ifModule mod_gzip.c>
mod_gzip_on Yes
mod_gzip_dechunk Yes
mod_gzip_item_include file \.(html?|txt|css|js|php|pl)$
mod_gzip_item_include handler ^cgi-script$
mod_gzip_item_include mime ^text/.*
mod_gzip_item_include mime ^application/x-javascript.*
mod_gzip_item_exclude rspheader ^Content-Encoding:.*gzip.*
<ifModule>

2、deflate_Module方式

(1)严格匹配文件类型

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
# mod_deflate:
<ifmodule mod_deflate.c>
DeflateCompressionLevel 6 #压缩率, 6是建议值.
AddOutputFilterByType DEFLATE text/plain
AddOutputFilterByType DEFLATE text/html
AddOutputFilterByType DEFLATE text/php
AddOutputFilterByType DEFLATE text/xml
AddOutputFilterByType DEFLATE text/css
AddOutputFilterByType DEFLATE text/javascript
AddOutputFilterByType DEFLATE application/xhtml+xml
AddOutputFilterByType DEFLATE application/xml
AddOutputFilterByType DEFLATE application/rss+xml
AddOutputFilterByType DEFLATE application/atom_xml
AddOutputFilterByType DEFLATE application/x-javascript
AddOutputFilterByType DEFLATE application/x-httpd-php
AddOutputFilterByType DEFLATE image/svg+xml
AddOutputFilterByType DEFLATE image/gif image/png image/jpe image/swf image/jpeg image/bmp
# Don't compress images and other #排除不需要压缩的文件
BrowserMatch ^Mozilla/4 gzip-only-text/html
BrowserMatch ^Mozilla/4\.0[678] no-gzip
BrowserMatch \bMSIE !no-gzip !gzip-only-text/html
SetEnvIfNoCase Request_URI .(?:html|htm)$ no-gzip dont-varySetEnvIfNoCase
#SetEnvIfNoCase Request_URI .(?:gif|jpe?g|png)$ no-gzip dont-vary
SetEnvIfNoCase Request_URI .(?:exe|t?gz|zip|bz2|sit|rar)$ no-gzip dont-vary
SetEnvIfNoCase Request_URI .(?:pdf|doc)$ no-gzip dont-vary
</ifmodule>

(2)过滤文件类型

1
2
3
4
5
6
7
8
9
10
# mod_deflate:
<ifmodule mod_deflate.c>
DeflateCompressionLevel 6
SetOutputFilter DEFLATE #压缩所有文件
#Don't compress images and other #过滤不需要压缩文件
#SetEnvIfNoCase Request_URI .(?:gif|jpe?g|png)$ no-gzip dont-vary
SetEnvIfNoCase Request_URI .(?:html|htm)$ no-gzip dont-vary
SetEnvIfNoCase Request_URI .(?:exe|t?gz|zip|bz2|sit|rar)$ no-gzip dont-vary
SetEnvIfNoCase Request_URI .(?:pdf|doc)$ no-gzip dont-vary
</IfModule>

文件MIME类型可以根据自己情况添加,也可以通过浏览器查看connect-type:

alt

mod_gzip 和mod_deflate的主要区别是什么?(来自互联网)

首先一个区别是安装它们的Apache Web服务器版本的差异。Apache 1.x系列没有内建网页压缩技术,所以才去用额外的第三方mod_gzip 模块来执行压缩。而Apache 2.x官方在开发的时候,就把网页压缩考虑进去,内建了mod_deflate 这个模块,用以取代mod_gzip。虽然两者都是使用的Gzip压缩算法,它们的运作原理是类似的。 第二个区别是压缩质量。mod_deflate 压缩速度略快而mod_gzip 的压缩比略高。一般默认情况下,mod_gzip 会比mod_deflate 多出4%~6%的压缩量。 那么,为什么使用mod_deflate?第三个区别是对服务器资源的占用。 一般来说mod_gzip 对服务器CPU的占用要高一些。mod_deflate 是专门为确保服务器的性能而使用的一个压缩模块,mod_deflate 需要较少的资源来压缩文件。这意味着在高流量的服务器,使用mod_deflate 可能会比mod_gzip 加载速度更快

没有启动Gzip压缩: alt 启动Gzip压缩: alt

##Nginx

在Nginx安装完成之后,我们可以开启Gzip压缩功能,这里Nginx默认只能对text/html类型的文件进行压缩。

Nginx的gzip模块是内置的,在http中添加如下配置:

1
2
3
4
5
6
7
8
9
gzip on;
gzip_buffers 4 16k;
gzip_http_version 1.1;
gzip_comp_level 3;
gzip_min_length 1k;
gzip_types text/plain application/x-javascript text/css application/xml text/javascript application/x-httpd-php image/jpeg image/gif image/png;
gzip_disable "MSIE [1-6].(?!.*SV1)";
gzip_proxied any;
gzip_vary on;

配置指令详细注释:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
gzip on|off
# 默认值: gzip off
# 开启或者关闭gzip模块

gzip_static on|off

# nginx对于静态文件的处理模块
# 该模块可以读取预先压缩的gz文件,这样可以减少每次请求进行gzip压缩的CPU资源消耗。该模块启用后,nginx首先检查是否存在请求静态文件的gz结尾的文件,如果有则直接返回该gz文件内容。为了要兼容不支持gzip的浏览器,启用gzip_static模块就必须同时保留原始静态文件和gz文件。这样的话,在有大量静态文件的情况下,将会大大增加磁盘空间。我们可以利用nginx的反向代理功能实现只保留gz文件。
# 可以google"nginx gzip_static"了解更多

gzip_comp_level 4

# 默认值:1(建议选择为4)
# gzip压缩比/压缩级别,压缩级别 1-9,级别越高压缩率越大,当然压缩时间也就越长(传输快但比较消耗cpu)。

gzip_buffers 4 16k

# 默认值: gzip_buffers 4 4k/8k
# 设置系统获取几个单位的缓存用于存储gzip的压缩结果数据流。 例如 4 4k 代表以4k为单位,按照原始数据大小以4k为单位的4倍申请内存。 4 8k 代表以8k为单位,按照原始数据大小以8k为单位的4倍申请内存。
# 如果没有设置,默认值是申请跟原始数据相同大小的内存空间去存储gzip压缩结果。

gzip_types mime-type [mime-type ...]

# 默认值: gzip_types text/html (默认不对js/css文件进行压缩)
# 压缩类型,匹配MIME类型进行压缩
# 不能用通配符 text/*
# (无论是否指定)text/html默认已经压缩
# 设置哪压缩种文本文件可参考 conf/mime.types

gzip_min_length 1k

# 默认值: 0 ,不管页面多大都压缩
# 设置允许压缩的页面最小字节数,页面字节数从header头中的Content-Length中进行获取。
# 建议设置成大于1k的字节数,小于1k可能会越压越大。 即: gzip_min_length 1024

gzip_http_version 1.0|1.1

# 默认值: gzip_http_version 1.1(就是说对HTTP/1.1协议的请求才会进行gzip压缩)
# 识别http的协议版本。由于早期的一些浏览器或者http客户端,可能不支持gzip自解压,用户就会看到乱码,所以做一些判断还是有必要的。
# 注:99.99%的浏览器基本上都支持gzip解压了,所以可以不用设这个值,保持系统默认即可。
# 假设我们使用的是默认值1.1,如果我们使用了proxy_pass进行反向代理,那么nginx和后端的upstream server之间是用HTTP/1.0协议通信的,如果我们使用nginx通过反向代理做Cache Server,而且前端的nginx没有开启gzip,同时,我们后端的nginx上没有设置gzip_http_version为1.0,那么Cache的url将不会进行gzip压缩

gzip_proxied [off|expired|no-cache|no-store|private|no_last_modified|no_etag|auth|any]

# 默认值:off
# Nginx作为反向代理的时候启用,开启或者关闭后端服务器返回的结果,匹配的前提是后端服务器必须要返回包含"Via"的 header头。
off - 关闭所有的代理结果数据的压缩
expired - 启用压缩,如果header头中包含 "Expires" 头信息
no-cache - 启用压缩,如果header头中包含 "Cache-Control:no-cache" 头信息
no-store - 启用压缩,如果header头中包含 "Cache-Control:no-store" 头信息
private - 启用压缩,如果header头中包含 "Cache-Control:private" 头信息
no_last_modified - 启用压缩,如果header头中不包含 "Last-Modified" 头信息
no_etag - 启用压缩 ,如果header头中不包含 "ETag" 头信息
auth - 启用压缩 , 如果header头中包含 "Authorization" 头信息
any - 无条件启用压缩

gzip_vary on

# 和http头有关系,加个vary头,给代理服务器用的,有的浏览器支持压缩,有的不支持,所以避免浪费不支持的也压缩,所以根据客户端的HTTP头来判断,是否需要压缩

gzip_disable "MSIE [1-6]."

# 禁用IE6的gzip压缩,又是因为杯具的IE6。当然,IE6目前依然广泛的存在,所以这里你也可以设置为“MSIE [1-5].”
# IE6的某些版本对gzip的压缩支持很不好,会造成页面的假死,今天产品的同学就测试出了这个问题
后来调试后,发现是对img进行gzip后造成IE6的假死,把对img的gzip压缩去掉后就正常了
为了确保其它的IE6版本不出问题,所以建议加上gzip_disable的设置

###关于 SEO: 有人说百度对Gzip的支持不够好,担心影响收录和SEO,经百度查阅相关资料后发现百度专门针对这个问题作过报告,声明百度是支持Gzip的。

服务器开启gzip压缩是否会影响蜘蛛抓取和收录量? 服务器开启gzip压缩,不会对spider抓取产生影响,我们会以压缩的方式来抓取。并且也能够节省站点的网络流量。

不写单元测试用例的程序员不是一个好CTO!!!

注:以下内容编码环境为AndroidStudio_2.4Preview6,测试框架 JUnit4.12

今天在研究 MVP_RxJava2_Retrofit2_OkHttp3 ,打算写一个单元测试类测试一下今天的劳动成果.犯难了,之前写的单元测试类都是同步调用的,没有处理过异步的单元测试呀!

首先想到是这样的:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
@Test
public void testQueryApi() throws Exception {
// Context of the app under test.
new QueryModelImpl().queryIp("8.8.8.8", new BasePresenterImpl.SubscriberEx<IpBean>() {
@Override
public void onSubscribeing(Subscription subscription) {
subscription.request(Long.MAX_VALUE);
}

@Override
public void onNext(IpBean bean) {
assertTrue(null != bean);
}

@Override
public void onError(Throwable e, boolean global) {
super.onError(e, global);
assertTrue(false);
}
});
}

然而...并没有什么卵用!断言根本不被执行...呵呵

仔细一想,嗯! testQueryApi() 应该是运行在主线程的,而我的 new QueryModelImpl().queryIp() 是异步的,也就是说 queryIp() 这个方法还没执行完(呵呵,可能初始化都还没走完),我们的主线程就已经死了,单元测试进程就 Game Over 了...自然是没法返回断言的数据了.

于是,我又改了一下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
@Test
public void testQueryApi() throws Exception {
// Context of the app under test.
final Object[] object = new Object[1];
new QueryModelImpl().queryIp("8.8.8.8", new BasePresenterImpl.SubscriberEx<IpBean>() {
@Override
public void onSubscribeing(Subscription subscription) {
subscription.request(Long.MAX_VALUE);
}

@Override
public void onNext(IpBean bean) {
object[0] = bean;
}

@Override
public void onError(Throwable e, boolean global) {
super.onError(e, global);
}
});
Thread.sleep(10 * 1000);
assertTrue(object[0] instanceof IpBean);
}

跑了下试试,额...确实可以了,但是勒,这简直太不优雅了吧!延时10秒是个什么鬼啊~不过幸好,我的思路是OK的,这位我最后的改动打下了坚实的基础.阻塞住主线程,当异步线程响应的时候通知主线程就OK了~于是呢,就有了一个类闪亮登场!!!(此处应有掌声)

CountDownLatch

该类属于 java.util.concurrent 包,所有对于普通Java测试用例也是支持的.

此类可以优雅的实现阻塞某个线程,直到得到一个通知或者超时.相比其他类实现相同的功能要简单很多(不能为了写单元测试去写一摩尔代码吧~),只有三句话而已~

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
@Test
public void testQueryApi() throws Exception {
// Context of the app under test.
final Object[] object = new Object[1];
final CountDownLatch latch = new CountDownLatch(1);
new QueryModelImpl().queryIp("8.8.8.8", new BasePresenterImpl.SubscriberEx<IpBean>() {
@Override
public void onSubscribeing(Subscription subscription) {
subscription.request(Long.MAX_VALUE);
}

@Override
public void onNext(IpBean bean) {
object[0] = bean;
latch.countDown();
}

@Override
public void onError(Throwable e, boolean global) {
super.onError(e, global);
latch.countDown();
}
});
latch.await();
// latch.await(10 * 1000, TimeUnit.MILLISECONDS);
assertTrue(object[0] instanceof IpBean);
}

OK,至此,异步单元测试用例已经写完了.

如果大家有更优美的方法~欢迎留言交流!

https是一个安全的访问方式,数据在传输过程中是加密的,https基于SSL。

##一、安装apache和ssl模块

1、安装apache

1
yum install httpd

2、安装ssl模块

1
yum install mod_ssl

重启apache:

1
service httpd restart

安装完mod_ssl会创建一个默认的SSL证书,路径位于/etc/pki/tls,此时可以立即通过https访问服务器了:

https://x.x.x.x/

如果不使用默认的证书,也可以使用openssl手动创建证书。

##二、使用openssl手动创建证书 1、安装openssl

1
yum install openssl

2、生成服务器私钥

1
2
cd /etc/pki/tls
openssl genrsa -out server.key 1024

注意:server.key是私钥。

3、用私钥server.key文件生成证书请求文件csr

1
openssl req -new -key server.key -out server.csr

注:server.csr是证书请求文件。

此步骤需要输入一些证书信息:

1
2
3
4
5
6
7
Country Name (2 letter code) [XX]:CN
State or Province Name (full name) []:shanghai
Locality Name (eg, city) [Default City]:shanghai
Organization Name (eg, company) [Default Company Ltd]:ccc
Organizational Unit Name (eg, section) []:bbb
Common Name (eg, your name or your server’s hostname) []:www.test.com
Email Address []:a@a.com

输入国家、省份、城市、公司、部门、姓名或服务器名、电子邮箱,随后会要求输入一个challengepassword(密码),无需输入,后面一律直接回车即可。

4、生成数字签名crt文件(证书文件)

1
openssl x509 -days 365 -req -in server.csr -signkey server.key -outserver.crt

用私钥签名证书请求文件,证书的申请机构和颁发机构都是自己。

5、编辑apache的ssl配置文件

1
vim/etc/httpd/conf.d/ssl.conf

/etc/httpd/conf.d/ssl.conf文件配置具体如下:

1
2
3
4
5
6
7
8
9
10
11
// 设置网页存放目录
DocumentRoot “/var/www/https”
// 服务器的端口
ServerName *:443
// 首页名称
DirectoryIndex index.html index.html.var
SSLEngine on
// 证书
SSLCertificateFile /etc/pki/tls/server.crt
// 私钥
SSLCertificateKeyFile /etc/pki/tls/server.key

6、重启apache

1
service httpd restart

访问https://ip/,就能看到证书信息了。

由于不是第三方根证书颁发机构颁发的证书,而是自己颁发的证书,所以浏览器会提示安全证书不受信任。

!!!注意:首页index.html 的文件权限为755,否则将会出现如上提示:

Forbidden

Youdon’t have permission to access /main.html on this server.

解决方法:修改首页index.html读写权限。

1
chmod755 index.html

关于openssl指令的补充说明:

1
2
3
4
5
6
7
8
9
10
11
12
13
openssl [操作] -out filename [bits]

参数说明:

[操作] 主要的操作有如下两个:
genrsa,建立RSA加密的Public key
req,建立凭证要求文件或者是凭证文件

-out ,后面加上输出的文件名,就是那把key name

bits,用在genrsa加密的公钥的长度

-x509,X.509,CertificateData Management. 一种验证的管理方式

例:建立一支长度为1024bits的Public Key,注意文件名。

1
openssl genrsa -out Server.key 1024

生成证书请求命令:

1
openssl req -new -key file.key -out file.csr -config /path/to/openssl.cnf

-config:指定openssl的配置文件路径,不指定时,默认会访问Unix格式的默认路径:/usr/local/ssl/openssl.cnf。例:

1
openssl req -new -key server.key -outserver.csr

服务器采用的是Linux系统。

安装tomcat在服务器上后,客户端进入tomcat首页点击manager想进行项目管理发现没有登录提示,直接跳转403或401权限错误。

各方面资料调查后,发现这是tomcat8.5之后的版本,已经不支持远程登录,需要修改配置文件。

具体操作如下:

可以通过putty远程操作服务器修改,或者winscp远程修改文件。

文件地址/webapps/magager/META-INF/context.xml

原文为:

1
2
3
4
<Context antiResourceLocking="false" privileged="true" >
<Valve className="org.apache.catalina.valves.RemoteAddrValve"
allow="127\.\d+\.\d+\.\d+|::1|0:0:0:0:0:0:0:1" />
</Context>

改为:

1
2
3
4
5
6
<Context antiResourceLocking="false" privileged="true" >
<!--
<Valve className="org.apache.catalina.valves.RemoteAddrValve"
allow="127\.\d+\.\d+\.\d+|::1|0:0:0:0:0:0:0:1" />
-->
</Context>

##下载Redis

在Redis的官网下载页上有各种各样的版本,我这次是在windows上部署的,要去GitHub上下载。目前的是2.8.12版的,直接解压,在\bin\release 目录下有个压缩包,这就是我们需要的。 传送门:https://github.com/MicrosoftArchive/redis/releases

##启动Redis

直接在安装的目录打开命令窗口,运行:

1
redis-server redis.windows.conf

结果就悲剧了,提示:QForkMasterInit: system error caught. error code=0x000005af, message=VirtualAllocEx failed.: unknown error 。原因是内存分配的问题(如果你的电脑够强悍,可能不会出问题)。解决方法有两个,第一:启动的时候使用—maxmemory 命令限制Redis的内存:

1
redis-server redis.windows.conf --maxmemory 200m

第二种方法就是修改配置文件 redis.windows.conf ,修改以下节点:

1
maxmemory 209715200

注意单位是字节,改完后如下:

之后再运行下面的命令就可以启动了

1
redis-server redis.windows.conf

但是问题又来了,关闭cmd窗口就会关闭Redis,难道服务器上要一直开着吗?这显然是不科学的,下面看怎么在服务器上部署。 部署Redis

其实Redis是可以安装成windows服务的,开机自启动,命令如下:

1
redis-server --service-install redis.windows.conf

安装完之后,就可看到Redis已经作为windows服务了:

但是安装好之后,Redis并没有启动,启动命令如下:

1
redis-server --service-start

停止命令:

1
redis-server --service-stop

还可以安装多个实例

1
2
3
4
5
6
redis-server --service-install –service-name redisService1 –port 10001
redis-server --service-start –service-name redisService1
redis-server --service-install –service-name redisService2 –port 10002
redis-server --service-start –service-name redisService2
redis-server --service-install –service-name redisService3 –port 10003
redis-server --service-start –service-name redisService3

卸载命令:

1
redis-server --service-uninstall

最后提示一下:2.8版本的不支持32位系统,32位系统要去下载2.6版本的。2.6版本的无法像上面一样方便的部署,它提供一个叫RedisWatcher的程序来运行redis server,Redis停止后会自动重启。