Gradle Plugin Samples 之 Gradle Unit Test

Gradle Unit Test

Android Studio 1.1 正式版本已经发布, 1.1 版本最大的新功能就是正式支持 Unit 。所谓支持 Unit 是指 Gradle 的 Unit 支持。本文中的内容,也需要在 Android Studio 1.1 中才能正常执行。

文本是对 Unit testing support 的个人理解和总结。不足的地方,还望指正。

环境设置

讲解1

在默认情况下,Android Studio 只开启了 Android Instrumentation Test ,需要手动开启 Unit Test

Settings–>Gradle–>Experimental ,勾选 Enable Unit Testing support

讲解2

Build Variants 面板中,选择 Unit Tests

讲解3

当你在在 Build Variants 面板中,选择 Unit Tests 时,可能会有如下的提示:

其实,你可能在上一步勾选 Enable Unit Testing support 时就已经发现了,Android Studio 已经提示了, Unit Testing 需要 Android Gradle plugin 1.1 以及以上版本,所以我们需要设置 {@projectName}/build.gradle 中的Android Gradle plugin 版本为 1.1 ,如果你的 Android Gradle plugin 已经为 1.1 或者以上版本,可以跳过此步。

buildscript {
repositories {
jcenter()
}
dependencies {
classpath 'com.android.tools.build:gradle:1.1.0'

// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files
}
}

allprojects {
repositories {
jcenter()
}
}

讲解4

Java 在做 Unit Test 的时候,最常用的便是 JUnit 了,所以我们需要加入 JUnit 的依赖。在{@projectName}/{@moduleName}/build.gradle 中添加 JUnitMaven 依赖。

dependencies {
testCompile 'junit:junit:4.12'
}

testCompile 意思为,test模式下依赖某一个库,该库不会在我们正式发布时打包到我们的程序中,作用和debugCompile 类似。

讲解5

{@moduleName}/src 目录下创建 test/java 文件夹。该文件夹是 Unit Test 代码存放的位置。

测试代码的包名、类名、方法名均可以自定义,不用和被测试代码相同。但是为了方便自己和他人,建议使用一一对应的方式建立原代码和测试代码的关系

编写测试

讲解6

Demo GradleUnitTest 是一个测试 Utils 类中 add 方法的项目,借助测试该类我们学习 Unit Test 的基本使用。该类的具体文件路径为 {@projectName}/{@moduleName}/src/main/java/cc/bb/aa/gradleunittest/util/Utils.java

public class Utils
{
public static int add(int a, int b)
{
return a + b;
}
}

我们为 Utils 类编写一个测试类,类名为 UtilsTest ,该类具体的具体文件路径为{@projectName}/{@moduleName}/src/test/java/cc/bb/aa/gradleunittest/util/UtilsTest.java

public class UtilsTest
{
@Test
public void add()
{
Assert.assertTrue(3 == Utils.add(1, 2));
}
}

其中 @TestJUnit 的注解,用于标注测试方法。关于更多 JUnit 的知识,请自行搜索学习。

我们可以在 Build Variants 面板中切换 Unit TestsAndroid Instrumentation Test 来观察 Project 面板中项目目录的显示情况。

勾选 Android Instrumentation Test 时:

勾选 Unit Tests 时:

执行测试

JUnit

讲解7

选择 Edit configurations

添加一个 JUnit

删除 Before launch 中的 Make ,添加一个 Gradle-aware makeTask 可以不用填写,直接点击 OK

你可以在 Configuration 中设置测试过滤。例如,我在 Test kind 中选择 All in directory ,并且在 Directory 中选择了 {@projectName}/{@moduleName}/src/test/java 文件夹,这样便可以一次性测试所有的测试代码。

需要在 Use classpath of module 中选择需要测试的 module

最终的设置结果如下:

点击 OK ,运行刚刚设置的 JUnit ,便可以观察到测试结果。

 

Gradle Unit

讲解8

我们除了在 Run/Debug Configurations 中添加 JUnit 之外,还可以在 Gradle 面板中执行 test 命令。

双击 Gradle 面板中执行 test 命令,发现该命令除了在 MessageRun 面板中输入大段大段的文字,并没有什么具体的反应。其实,测试的结果放在了一个目录下面。

{@projectName}/{@moduleName}/build/reports/tests 中我们可以看到 debugrelease 两个文件夹,分别对应的是 debugrelease 这两个 BuildTypes 的测试结果( Gradletest 命令实际包含了testDebugtestRelease 两个命令)。在这两个文件夹中,就是各自的测试结果。

例如,在 debug 文件夹中,我们打开 index.html 就可以查看测试结果了。

执行 test 命令时,如果 testDebug 出现了断言错误,命令将停止,不再继续 testRelease 。如果你想一次性执行所有的测试,即使出现了断言错误也要继续 testRelease ,请看 讲解9

讲解9

你也可以在命令行中使用 Gradle 命令执行 Unit Test

例如,讲解8 中的 test 命令,我们可以在命令行中执行 ./gradlew test 完成。

执行 test 命令时,如果 testDebug 出现了断言错误,命令将停止,不再继续 testRelease 。如果你想一次性执行所有的测试,即使出现了断言错误也要继续的话,你可以使用 ./gradlew test –continue 命令。这样,所以的测试结果都会输出到 {@projectName}/{@moduleName}/build/reports/tests 目录,包括断言错误的内容。

如果你想单独测试某个类,你可以添加 –tests 参数。例如: ./gradlew testDebug –tests=’*.MyTestClass’

在命令行中使用 Gradle 命令是一件很痛苦的事情,因为它会下载项目构建环境依赖(~~因为 GFW ,你还可能下载失败~~)。而这些依赖已经存在 Android Studio 中了。没有使用已经存在的依赖,是因为在命令行中执行Gradle 命令已经脱离了 Android Studio,就是纯粹地使用 GradleAndroid Studio Gradle plugin 的作用便在于此)。

Flavors 和 BuildTypes

本节内容,请参考 GradleUnitTest2 中的代码。

讲解10

Gradle 支持 FlavorsBuildTypes ,因此要有对应的测试。

FlavorsBuildTypes 中的代码需要和对应分支下的测试类有对应的目录关系。 GradleUnitTest2 中对应的关系如下:

src/main/java/cc/bb/aa/gradleunittest/util/Utils.java
–>
src/test/java/cc/bb/aa/gradleunittest/util/UtilsTest.java

src/amazon/java/cc/bb/aa/gradleunittest/util/ProductFlavors.java
–>
src/testAmazon/java/cc/bb/aa/gradleunittest/util/ProductFlavorsTest.java

src/google/java/cc/bb/aa/gradleunittest/util/ProductFlavors.java
–>
src/testGoogle/java/cc/bb/aa/gradleunittest/util/ProductFlavorsTest.java

src/debug/java/cc/bb/aa/gradleunittest/util/BuildTypes.java
–>
src/testDebug/java/cc/bb/aa/gradleunittest/util/BuildTypesTest.java

src/release/java/cc/bb/aa/gradleunittest/util/BuildTypes.java
–>
src/testRelease/java/cc/bb/aa/gradleunittest/util/BuildTypesTest.java

因此,Gradle 命令不再简简单单只有 testtestDebugtestRelease 3个,现在为 testtestAmazonDebugtestAmazonReleasetestGoogleDebugtestGoogleRelease 。同样的道理,test 命令包含了其他4个命令。

其他内容

讲解11

Unit Test 应该是尽可能独立的。对一个 class 的 Unit Test 不应该再和其他 class 有任何交互。

用于运行单元测试的 android.jar 文件不包含任何实际的代码(实际的代码由程序所运行在的 Android 真实设备提供),相反,所有的方法抛出异常(默认情况下)。需要确保您的单元测试只是测试你的代码,不依赖于 Android 平台的任何特定行为。如果你没有使用虚拟依赖生产测试框架(例如: Mockito ),你需要在{@projectName}/{@moduleName}/build.gradle 文件中添加以下内容:

android {
// ...
testOptions {
unitTests.returnDefaultValues = true
}
}

例如,我们在测试代码中使用了 TextUtils.isEmpty 方法。在测试时,该方法会出现 java.lang.RuntimeException: Method isEmpty in android.text.TextUtils not mocked. 异常。当我们设置了 unitTests.returnDefaultValues = true ,无论传入的参数是什么,TextUtils.isEmpty 方法永远返回 false

Gradle_Unit_Test.zip

java 瞬间快速创建固定大小文件[毫秒级](下载创建占位文件)

最近突发奇想java能不能快速创建固定大小的文件,类似迅雷下载的时候会先创建一个和下载文件同样大小的文件占位,以避免空间不足下载失败。

在网上搜索一番发现提出这个问题的还真不少,但是很少就解决办法,最多的解决办法就是循环向文件里面入固定大小的空字节,但这个方法的弊端就是创建大文件很费力,我试了创建2GB的文件大概需要50秒左右,最快也要三四十秒,再大的文件就更慢了,这也许跟我电脑配置有关系,但是这跟迅雷那种瞬间创建文件实在是相差太多了。

 

这使得我很沮丧,但是昨天无意间发现java有一个类:FileChannel,查阅API发现通过这个类来实现复制文件比简单的循环读取写入可能会高效得多,很多操作系统可将字节直接从文件系统缓存传输到目标通道,而无需实际复制各字节。这使我想到使用它能不能快速的创建大文件呢!所以我写了一个测试方法,结果让我大吃一惊,创建一个2GB的大文件只需要90毫秒,你没看错,是毫秒,我试了创建更大的文件10GB,20GB,150GB,所用时间都是100毫秒左右,太令人吃惊了。这种快乐的事情当然要分享给大家了下面进入正题。

 

通过FileChannel类的 int write(ByteBuffer src, long position) throws IOException 方法可以在指定position位置写入字节,position大于文件长度的话,前面的部分会填充为空字节0。我们这种创建固定大小的空文件之需要在最后一个位置写入一个空字节0就可以使文件扩大到我们想要的长度,下面是我写的测试。

/**
* 创建固定大小的文件
* @param file
* @param length
* @throws IOException
*/
public static void createFixLengthFile(File file, long length) throws IOException{
long start = System.currentTimeMillis();
FileOutputStream fos = null;
FileChannel output = null;
try {
fos = new FileOutputStream(file);
output = fos.getChannel();
output.write(ByteBuffer.allocate(1), length-1);
} finally {
try {
if (output != null) {
output.close();
}
if (fos != null) {
fos.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
long end = System.currentTimeMillis();
System.out.println("total times "+(end-start));
}

另一种方法

public static void create(File file, long length) throws IOException{
long start = System.currentTimeMillis();
RandomAccessFile r = null;
try {
r = new RandomAccessFile(file, "rw");
r.setLength(length);
} finally{
if (r != null) {
try {
r.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
long end = System.currentTimeMillis();
System.out.println(end-start);

}

git提交到多个远程仓库

我用过两种方法,最早的时候是设定多个 remote,然后写一个 alias,比如:

$ git config alias.pushall "!git push origin && git push dev"

后来在某一个版本(忘了具体的版本号)升级之后,Git 多了一项设置,使得你可以为一个 remote 设置多个 pushurl。比如说你问题里的例子,我可以不要 remote "dev",只留下 remote "origin",然后加一句:

git remote --set-url --add --push origin git@gitlab.com:root/XXX.git

在这之后,你的 remote "origin" 就变成类似如下的结构:

[remote "origin"]
        url = git@github.com:SegmentFault/XXX.git
        fetch = +refs/heads/*:refs/remotes/origin/*
        pushurl = git@github.com:SegmentFault/XXX.git
        pushurl = git@gitlab.com:root/XXX.git

如此一来,我可以直接 git push origin 就可以推向两个 repos 了。

这两种方法其实各有各的适用场景,自己取舍吧。

MYSQL企业常用架构与调优经验分享

一、选择Percona Server、MariaDB还是MYSQL

1、Mysql三种存储引擎

MySQL提供了两种存储引擎:MyISAM和 InnoDB,MySQL4和5使用默认的MyISAM存储引擎。从MYSQL5.5开始,MySQL已将默认存储引擎从MyISAM更改为InnoDB。

MyISAM没有提供事务支持,而InnoDB提供了事务支持。

XtraDB是InnoDB存储引擎的增强版本,被设计用来更好的使用更新计算机硬件系统的性能,同时还包含有一些在高性能环境下的新特性。

2、Percona  Server分支

Percona Server由领先的MySQL咨询公司Percona发布。

Percona Server是一款独立的数据库产品,其可以完全与MySQL兼容,可以在不更改代码的情况了下将存储引擎更换成XtraDB。是最接近官方MySQL Enterprise发行版的版本。

Percona提供了高性能XtraDB引擎,还提供PXC高可用解决方案,并且附带了percona-toolkit等DBA管理工具箱,

3、MariaDB

MariaDB由MySQL的创始人开发,MariaDB的目的是完全兼容MySQL,包括API和命令行,使之能轻松成为MySQL的代替品。

MariaDB提供了MySQL提供的标准存储引擎,即MyISAM和InnoDB,10.0.9版起使用XtraDB(名称代号为Aria)来代替MySQL的InnoDB。

4、如何选择

综合多年使用经验和性能对比,首选Percona分支,其次是MariaDB,如果你不想冒一点风险,那就选择MYSQL官方版本。

二、常用的MYSQL调优策略

1、硬件层相关优化

修改服务器BIOS设置

选择Performance Per Watt Optimized(DAPC)模式,发挥CPU最大性能。

Memory Frequency(内存频率)选择Maximum Performance(最佳性能)

内存设置菜单中,启用Node Interleaving,避免NUMA问题

2、磁盘I/O相关

使用SSD硬盘

如果是磁盘阵列存储,建议阵列卡同时配备CACHE及BBU模块,可明显提升IOPS。

raid级别尽量选择raid10,而不是raid5.

3、文件系统层优化

使用deadline/noop这两种I/O调度器,千万别用cfq

使用xfs文件系统,千万别用ext3;ext4勉强可用,但业务量很大的话,则一定要用xfs;

文件系统mount参数中增加:noatime, nodiratime, nobarrier几个选项(nobarrier是xfs文件系统特有的);

4、内核参数优化

修改vm.swappiness参数,降低swap使用率。RHEL7/centos7以上则慎重设置为0,可能发生OOM

调整vm.dirty_background_ratio、vm.dirty_ratio内核参数,以确保能持续将脏数据刷新到磁盘,避免瞬间I/O写。产生等待。

调整net.ipv4.tcp_tw_recycle、net.ipv4.tcp_tw_reuse都设置为1,减少TIME_WAIT,提高TCP效率。

5、Mysql参数优化建议

建议设置default-storage-engine=InnoDB,强烈建议不要再使用MyISAM引擎。

调整innodb_buffer_pool_size的大小,如果是单实例且绝大多数是InnoDB引擎表的话,可考虑设置为物理内存的50% -70%左右。

设置innodb_file_per_table = 1,使用独立表空间。

调整innodb_data_file_path = ibdata1:1G:autoextend,不要用默认的10M,在高并发场景下,性能会有很大提升。

设置innodb_log_file_size=256M,设置innodb_log_files_in_group=2,基本可以满足大多数应用场景。

调整max_connection(最大连接数)、max_connection_error(最大错误数)设置,根据业务量大小进行设置。

另外,open_files_limit、innodb_open_files、table_open_cache、table_definition_cache可以设置大约为max_connection的10倍左右大小。

key_buffer_size建议调小,32M左右即可,另外建议关闭query cache。

mp_table_size和max_heap_table_size设置不要过大,另外sort_buffer_size、join_buffer_size、read_buffer_size、read_rnd_buffer_size等设置也不要过大。

三、   MYSQL常见的应用架构分享

1、主从复制解决方案

这是MySQL自身提供的一种高可用解决方案,数据同步方法采用的是MySQL replication技术。MySQL replication就是从服务器到主服务器拉取二进制日志文件,然后再将日志文件解析成相应的SQL在从服务器上重新执行一遍主服务器的操作,通过这种方式保证数据的一致性。

为了达到更高的可用性,在实际的应用环境中,一般都是采用MySQL replication技术配合高可用集群软件keepalived来实现自动failover,这种方式可以实现95.000%的SLA。

wKioL1bnzqXTpra9AAA4W5Syh94610.jpg

2、MMM/MHA高可用解决方案

MMM提供了MySQL主主复制配置的监控、故障转移和管理的一套可伸缩的脚本套件。在MMM高可用方案中,典型的应用是双主多从架构,通过MySQL replication技术可以实现两个服务器互为主从,且在任何时候只有一个节点可以被写入,避免了多点写入的数据冲突。同时,当可写的主节点故障时,MMM套件可以立刻监控到,然后将服务自动切换到另一个主节点,继续提供服务,从而实现MySQL的高可用。

wKiom1bnzinxyNbJAAAwafwrvME471.jpg

3、Heartbeat/SAN高可用解决方案

在这个方案中,处理failover的方式是高可用集群软件Heartbeat,它监控和管理各个节点间连接的网络,并监控集群服务,当节点出现故障或者服务不可用时,自动在其他节点启动集群服务。在数据共享方面,通过SAN(Storage Area Network)存储来共享数据,这种方案可以实现99.990%的SLA。

wKioL1bnzsbSfpPAAAAtC9WNeBM583.jpg

4、Heartbeat/DRBD高可用解决方案

此方案处理failover的方式上依旧采用Heartbeat,不同的是,在数据共享方面,采用了基于块级别的数据同步软件DRBD来实现。

DRBD是一个用软件实现的、无共享的、服务器之间镜像块设备内容的存储复制解决方案。和SAN网络不同,它并不共享存储,而是通过服务器之间的网络复制数据。

 

wKiom1bnzkrB-vhLAAAxkEsaZh8093.jpg

四、MYSQL经典应用架构

wKioL1bnzuahF40iAABcNZGGaqI867.jpg

 

其中:

Dbm157是mysql主,dbm158是mysql主的备机,dbs159/160/161是mysql从。

MySQL写操作一般采用基于heartbeat+DRBD+MySQL搭建高可用集群的方案。通过heartbeat实现对mysql主进行状态监测,而DRBD实现dbm157数据同步到dbm158。

读操作普遍采用基于LVS+Keepalived搭建高可用高扩展集群的方案。前端AS应用通过提高的读VIP连接LVS,LVS有keepliaved做成高可用模式,实现互备。

最后,mysql主的从节点dbs159/160/161通过mysql主从复制功能同步mysql主的数据,通过lvs功能提供给前端AS应用进行读操作,并实现负载均衡。

本文出自 “爱维Linux” 博客,请务必保留此出处http://ixdba.blog.51cto.com/2895551/1751377

Nginx配置文件详细说明

在此记录下Nginx服务器nginx.conf的配置文件说明, 部分注释收集与网络.

#运行用户
user www-data;
#启动进程,通常设置成和cpu的数量相等
worker_processes  1;

#全局错误日志及PID文件
error_log  /var/log/nginx/error.log;
pid        /var/run/nginx.pid;

#工作模式及连接数上限
events {
use   epoll;             #epoll是多路复用IO(I/O Multiplexing)中的一种方式,但是仅用于linux2.6以上内核,可以大大提高nginx的性能
worker_connections  1024;#单个后台worker process进程的最大并发链接数
# multi_accept on;
}

#设定http服务器,利用它的反向代理功能提供负载均衡支持
http {
#设定mime类型,类型由mime.type文件定义
include       /etc/nginx/mime.types;
default_type  application/octet-stream;
#设定日志格式
access_log    /var/log/nginx/access.log;

#sendfile 指令指定 nginx 是否调用 sendfile 函数(zero copy 方式)来输出文件,对于普通应用,
#必须设为 on,如果用来进行下载等应用磁盘IO重负载应用,可设置为 off,以平衡磁盘与网络I/O处理速度,降低系统的uptime.
sendfile        on;
#tcp_nopush     on;

#连接超时时间
#keepalive_timeout  0;
keepalive_timeout  65;
tcp_nodelay        on;

#开启gzip压缩
gzip  on;
gzip_disable “MSIE [1-6]\.(?!.*SV1)”;

#设定请求缓冲
client_header_buffer_size    1k;
large_client_header_buffers  4 4k;

include /etc/nginx/conf.d/*.conf;
include /etc/nginx/sites-enabled/*;

#设定负载均衡的服务器列表
upstream mysvr {
#weigth参数表示权值,权值越高被分配到的几率越大
#本机上的Squid开启3128端口
server 192.168.8.1:3128 weight=5;
server 192.168.8.2:80  weight=1;
server 192.168.8.3:80  weight=6;
}
server {
#侦听80端口
listen       80;
#定义使用www.xx.com访问
server_name  www.xx.com;

#设定本虚拟主机的访问日志
access_log  logs/www.xx.com.access.log  main;

#默认请求
location / {
root   /root;      #定义服务器的默认网站根目录位置
index index.php index.html index.htm;   #定义首页索引文件的名称

fastcgi_pass  www.xx.com;
fastcgi_param  SCRIPT_FILENAME  $document_root/$fastcgi_script_name;
include /etc/nginx/fastcgi_params;
}

# 定义错误提示页面
error_page   500 502 503 504 /50x.html;
location = /50x.html {
root   /root;
}

#静态文件,nginx自己处理
location ~ ^/(images|javascript|js|css|flash|media|static)/ {
root /var/www/virtual/htdocs;
#过期30天,静态文件不怎么更新,过期可以设大一点,如果频繁更新,则可以设置得小一点。
expires 30d;
}
#PHP 脚本请求全部转发到 FastCGI处理. 使用FastCGI默认配置.
location ~ \.php$ {
root /root;
fastcgi_pass 127.0.0.1:9000;
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME /home/www/www$fastcgi_script_name;
include fastcgi_params;
}
#设定查看Nginx状态的地址
location /NginxStatus {
stub_status            on;
access_log              on;
auth_basic              “NginxStatus”;
auth_basic_user_file  conf/htpasswd;
}
#禁止访问 .htxxx 文件
location ~ /\.ht {
deny all;
}

}
}

 

以上是一些基本的配置,使用Nginx最大的好处就是负载均衡

如果要使用负载均衡的话,可以修改配置http节点如下:

#设定http服务器,利用它的反向代理功能提供负载均衡支持
http {
#设定mime类型,类型由mime.type文件定义
include       /etc/nginx/mime.types;
default_type  application/octet-stream;
#设定日志格式
access_log    /var/log/nginx/access.log;

#省略上文有的一些配置节点

#。。。。。。。。。。

#设定负载均衡的服务器列表
upstream mysvr {
#weigth参数表示权值,权值越高被分配到的几率越大
server 192.168.8.1x:3128 weight=5;#本机上的Squid开启3128端口
server 192.168.8.2x:80  weight=1;
server 192.168.8.3x:80  weight=6;
}

upstream mysvr2 {
#weigth参数表示权值,权值越高被分配到的几率越大

server 192.168.8.x:80  weight=1;
server 192.168.8.x:80  weight=6;
}

#第一个虚拟服务器
server {
#侦听192.168.8.x的80端口
listen       80;
server_name  192.168.8.x;

#对aspx后缀的进行负载均衡请求
location ~ .*\.aspx$ {

root   /root;      #定义服务器的默认网站根目录位置
index index.php index.html index.htm;   #定义首页索引文件的名称

proxy_pass  http://mysvr ;#请求转向mysvr 定义的服务器列表

#以下是一些反向代理的配置可删除.

proxy_redirect off;

#后端的Web服务器可以通过X-Forwarded-For获取用户真实IP
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
client_max_body_size 10m;    #允许客户端请求的最大单文件字节数
client_body_buffer_size 128k;  #缓冲区代理缓冲用户端请求的最大字节数,
proxy_connect_timeout 90;  #nginx跟后端服务器连接超时时间(代理连接超时)
proxy_send_timeout 90;        #后端服务器数据回传时间(代理发送超时)
proxy_read_timeout 90;         #连接成功后,后端服务器响应时间(代理接收超时)
proxy_buffer_size 4k;             #设置代理服务器(nginx)保存用户头信息的缓冲区大小
proxy_buffers 4 32k;               #proxy_buffers缓冲区,网页平均在32k以下的话,这样设置
proxy_busy_buffers_size 64k;    #高负荷下缓冲大小(proxy_buffers*2)
proxy_temp_file_write_size 64k;  #设定缓存文件夹大小,大于这个值,将从upstream服务器传

}

}
}