2018年4月24日 星期二

基本的 Annotation (JUnit5 一)

指南:官網、中文

※Maven 設定

<properties>
    <junit.platform.version>5.1.1</junit.platform.version>
</properties>
    
<dependencies>
    <dependency>
        <groupId>org.junit.jupiter</groupId>
        <artifactId>junit-jupiter-api</artifactId>
        <version>${junit.platform.version}</version>
        <scope>test</scope>
    </dependency>
    
    <dependency>
        <groupId>org.junit.jupiter</groupId>
        <artifactId>junit-jupiter-engine</artifactId>
        <version>${junit.platform.version}</version>
        <scope>test</scope>
    </dependency>
    
    <dependency>
        <groupId>org.junit.platform</groupId>
        <artifactId>junit-platform-launcher</artifactId>
        <version>1.1.1</version>
        <scope>test</scope>
    </dependency>
</dependencies>

※尤於 JUnit5 只支援 java8 以上,所以 eclipse 要按專案右鍵內容,然後如下設定



※IntelliJ 預設會抓最高版本,想改的話,如下圖:


※gradle 設定

test {
    systemProperty 'junit.jupiter.conditions.deactivate', '*'
    systemProperties = [
        'junit.jupiter.extensions.autodetection.enabled': 'true',
        'junit.jupiter.testinstance.lifecycle.default': 'per_class'
    ]
}
    
dependencies {
    testCompile("org.junit.jupiter:junit-jupiter-api:5.1.1")
    testRuntime("org.junit.jupiter:junit-jupiter-engine:5.1.1")
}

※


※測試

@BeforeAll
public static void start1() {
    System.err.println("class之前執行1");
}
    
@BeforeAll
public static void start2() {
    System.err.println("class之前執行2");
}
    
@BeforeEach
public void begin1() {
    System.out.println("方法之前執行1");
}
    
@BeforeEach
public void begin2() {
    System.out.println("方法之前執行2");
}
    
@AfterEach
public void end1() {
    System.out.println("方法之後執行1");
}
    
@AfterEach
public void end2() {
    System.out.println("方法之後執行2\r\n");
}
    
@AfterAll
public static void theEnd1() {
    System.err.println("class之後執行1");
}
    
@AfterAll
public static void theEnd2() {
    System.err.println("class之後執行2");
}
    
@Test
public void bruceTest1() {
    System.out.println("測試1");
}
    
@Test
public void bruceTest2() {
    System.out.println("測試2");
}
    
@Disabled
@Test
public void bruceTest3() {
    System.out.println("測試3");
}

※結果:
class之前執行1
class之前執行2
方法之前執行1
方法之前執行2
測試1
方法之後執行1
方法之後執行2

方法之前執行1
方法之前執行2
測試2
方法之後執行1
方法之後執行2

class之後執行1
class之後執行2

※JUnit4 和 JUnit5 對應:
@Test--------------@Test
@Before-----------@BeforeEach
@After-------------@AfterEach
@BeforeClass-----@BeforeAll
@AfterClass-------@AfterAll
@Ignore------------@Disabled

※除了 @Disabled 外,其他的都沒有屬性了

※@BeforeAll 和 @AfterAll 印出來是紅色的

※一樣都是 public void xxx(),或者 public static void xxx() 才可以測試

※注意以上的 annotation 除了 @Disabled 沒有繼承關係外,其他 5 個都有

2018年4月22日 星期日

@Rule、常用方法、測試其他類別 (JUnit4 二)

※@Rule


※內鍵的 Rule

※所謂內鍵,就是在 org.junit.rules 這包裡

import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
import org.junit.rules.Timeout;
    
public class AppTest3 {
    @Rule
    public Timeout timeout1 = Timeout.millis(10);
    // public Timeout timeout2 = new Timeout(10, TimeUnit.MILLISECONDS);
    
    @Rule
    public final ExpectedException thrown = ExpectedException.none();
    
    @Test
    public void xxx() throws InterruptedException {
        Thread.sleep(5);
    }
    
    @Test
    public void ooo() throws InterruptedException {
        Thread.sleep(5);
    }
    
    @Test
    public void zzz() throws InterruptedException {
        thrown.expect(ArithmeticException.class);
        int i = 1 / 0;
    }
}

※這個annotation是 4.7 版增加的,宣告時一定要 public

※@Test 裡的 timeout 和 expected 都有,但宣告 @Rule 是全域的,每個方法都會遵從

※ExpectedException.none() 表示不能有例外


※Verifier

原本的測試結束後,會執行這裡的邏輯,這裡的邏輯過了,測試才會過

@Rule
public Verifier verifier = new Verifier() {
    @Override
    protected void verify() {
        Assert.assertEquals(1, 1);
    }
};

※可看高手寫的文章

※自訂 Rule

import org.junit.rules.TestRule;
import org.junit.runner.Description;
import org.junit.runners.model.Statement;
    
public class RuleTest implements TestRule {
    public Statement apply(Statement base, Description des) {
        return new Statement() {
            @Override
            public void evaluate() throws Throwable {
                System.out.println("className=" + des.getClassName());
                System.out.println("displayName=" + des.getDisplayName());
                System.out.println("methodName=" + des.getMethodName());
                System.out.println("children=" + des.getChildren());
                System.out.println("annotations=" + des.getAnnotations());
                base.evaluate();
            }
        };
    }
}

※實作 TestRule 後,覆寫 apply 方法

※測試

@Rule
public RuleTest ruleTest = new RuleTest();
    
@Test
public void xxx() throws InterruptedException {
    Thread.sleep(5);
    System.out.println("aaa\r\n");
}
    
@Test
public void ooo() throws InterruptedException {
    Thread.sleep(5);
    System.out.println("bbb\r\n");
}

※如果不想寫 class 去實作,還可以用匿名和 lambda,如下:
@Rule
public TestRule xxx = new TestRule() {
    @Override
    public Statement apply(Statement base, Description des) {
        System.out.println("className=" + des.getClassName());
        System.out.println("displayName=" + des.getDisplayName());
        System.out.println("methodName=" + des.getMethodName());
        System.out.println("children=" + des.getChildren());
        System.out.println("annotations=" + des.getAnnotations());
//        try {
//            base.evaluate();
//        } catch (Throwable e) {
//            e.printStackTrace();
//        }
        return base;
    }
};
    
    
    
@Rule
public TestRule ooo = (base, des) -> {
    System.out.println("className=" + des.getClassName());
    System.out.println("displayName=" + des.getDisplayName());
    System.out.println("methodName=" + des.getMethodName());
    System.out.println("children=" + des.getChildren());
    System.out.println("annotations=" + des.getAnnotations());
    return base;
};

※此時不用寫 base.evaluate(),否則會執行兩次 @Test 裡的方法



※常用方法

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertSame;
import static org.junit.Assert.assertThat;
import org.hamcrest.CoreMatchers;
import org.junit.Test;
    
public class AppTest {
    @Test
    public void testApp() {
        assertEquals(5, 5);
        assertEquals("我錯了", 5, 5);
    
        AppTest t = new AppTest();
        assertSame(t, t);
    
        // Assert.fail();//一定不會過
    
        final Integer FIVE = 5;
        assertThat(FIVE, CoreMatchers.anything());// 一定會過
        assertThat(FIVE, CoreMatchers.is(5));// 5等於5嗎?
        assertThat(FIVE, CoreMatchers.not(10));// 5是10嗎
        assertThat(FIVE, CoreMatchers.notNullValue());// 5不是null嗎
    
        // 全部是5才會過
        assertThat(FIVE, CoreMatchers.allOf(CoreMatchers.is(5), CoreMatchers.is(5), CoreMatchers.is(5)));
    
        // 有一個是5就會過
        assertThat(FIVE, CoreMatchers.anyOf(CoreMatchers.is(1), CoreMatchers.is(5), CoreMatchers.is(2)));
    }
}

※很多 assertXxx 有很多的 overloading 方法,有些第一個參數都是String,可以打一些字,出錯時會顯示裡面的內容



※測試其他類別

@RunWith(Suite.class)
@Suite.SuiteClasses({ 
    TestXxx.class ,TestOoo.class
})
public class TestHaHaHa {
    
}

※可以測試 TestXxx 和 TestOoo 這兩個類別

※經過測試,TestHaHaHa 裡面只能有一個 @Test,但也不會被執行

2018年4月1日 星期日

設定檔的 server、log、location(nginx 二)

※server

server {
    listen       11111;
    server_name  localhost;
    
    location / {
        root   xxx;
        index  one.html;
    }
}
    
    
    
server {
    listen       22222;
    server_name  localhost;
    
    location / {
        root /usr/local/nginx/xxx
        #root D:\xxx
        index  two.html;
    }
}
    
    
    
server {
    listen       80;
    server_name ooo.com
    
    location / {
        root   xxx;
        index  name.html;
    }
}

※改好可用 ./nginx -t 測試設定檔有沒有錯

※http 裡的 server 可以有很多

※可以有不同的 port

※root 可以寫絕對路徑和相對路徑
但絕對路徑可能會有權限問題
Linux 的相對路徑預設是 /usr/local/nginx;Windows 為 nginx.exe 同層

※server_name 的網址必需寫在 hosts 檔裡,Linux 在 /etc;Windows 在 %SystemRoot%\system32\drivers\etc


※log

http {
    include mime.types;
    default_type application/octet-stream;
    
    log_format main '$remote_addr - $remote_user [$time_local] "$request" '
                    '$status $body_bytes_sent "$http_referer" '
                    '"$http_user_agent" "$http_x_forwarded_for"';
    
    log_format xxx $remote_addr
    
    sendfile on;
    
    server {
        listen 11111;
        server_name localhost;
    
        location / {
            root   one;
            index  one.html;
        }
        access_log logs/access_11111.log main;
    }
    
    server {
        listen 22222;
        server_name localhost;
    
        location / {
            root D:;
            index two.html;
        }
        access_log logs/access_22222.log xxx;
    }
}

※每一個 server 裡都可以有一個自己的 log,log_format 後面定義一個名字,然後格式是 $開頭的東西,想知道更多可參考官網,使用瀏覽器或 curl 指令請求後,就可以看到 log 寫出來了



※locatioon

.官網
.如果操作過程出現錯誤,請看 logs/error.log
.預設的 location 全部刪除,用 curl 指令或網址輸入 localhost 還是能抓到 html/index.html
.root 不寫,預設為 html,也就是 /usr/local/nginx/html,官網
.index 預設為 index.html,可以寫多個,用空隔隔開,找到第一個就用,找不到才往後找;但如果寫在 location 後面,只能寫一個,官網
.如果 location / 後面接檔案,index 的設定沒有效果,但不會報錯
.如果 location / 後面接目錄,index 的設定沒有效果,執行時會報 301 Moved Permanently

= ~ ~* ^~
=精準匹配
^~只匹配開頭
~正則匹配,大小寫不一樣
~*正則匹配,大小寫一樣
不加為一般匹配


※優先級

以完全相同為例
精準匹配 > 開頭匹配 > 正則匹配 > 一般匹配
開頭匹配和一般匹配不能同時寫,啟動伺服器時會報錯
正則匹配有兩個,誰寫前面就抓誰

※
#location /one.txt {
#    root one;
#}
    
location ~* /one.txt {
    root regex_not_i;
}
    
location ~ /one.txt {
    root regex_have_i;
}
    
location ^~ /one.txt {
    root headXXX;
}

※

※匹配目錄

location = / {
    root   /usr;
    index  index.html;
}
    
location / {
    root   one;
    index  one.txt;
}
    
#location /index.html {
#    root   two;
#    index  two.txt;
#}

※如果只有一個精準的 location = /,那 curl localhost 會將精準匹配的 /加上 index.html,然後再次發出 /index.html;如果只有一個一般的 location /,那就會直接將檔案顯示出來或 404

※此例使用 curl localhost 會匹配到精準匹配的 /,一定要有 /usr/index.html (檔案或資料夾都可以),不然會 403 Forbidden

然後再次匹配 /index.html ,最適合的只有一般匹配的 /,所以會找 /usr/local/nginx/one/index.html (因為 location 和 index 都有檔案,會忽略 index 的 one.txt)
但如果連第二個也註解,那會去找預設的 /usr/local/nginx/html/index.html

※如果將最後一個 location 註解打開,那 /index.html 會匹配到註解的 location,此時會找 /usr/local/nginx/two/index.html

※如果想執行的是第一個 location 裡的檔案,要寫成 = /index.html 才可以,而 index 可以不寫,因為 location 和 index 都寫,index 會被忽略

2018年3月29日 星期四

用戶端 (Redis 五)

官網連結
可以看到有非常多的用戶端,以 node.js 的 node_redis 和 java 的 jedis 2.9.0為例


※Node.js


dnf install nodejs -- 安裝 nodejs
npm install redis -- 安裝 redis

vi nodeRedis --產生並編輯一個空檔案,內容如下:

※nodeRedis

var redis = require("redis"),
client = redis.createClient();
    
client.set("xxx1", "x1");
console.log("yeah");

※必須先開啟伺服器才行



※Java


Jedis jedis = new Jedis("127.0.0.1", 6379);
System.out.println(jedis.ping());
jedis.set("k4", "'");
System.out.println(jedis.get("k4"));
jedis.close();

※也有類似連線池的功能,API

JedisPoolConfig jedisPoolConfig = new JedisPoolConfig();
jedisPoolConfig.setMaxTotal(10);
try (
        JedisPool pool = new JedisPool(jedisPoolConfig, "localhost"); 
        Jedis jedis = pool.getResource();
    ) {
    jedis.set("k4", "v4");
} catch (Exception e) {
    e.printStackTrace();
}
// pool.close();
// jedis.close();

※可以看出三層關係,redis.clients.jedis.JedisPoolConfig
extends org.apache.commons.pool2.impl.GenericObjectPoolConfig
extends org.apache.commons.pool2.impl.extends BaseObjectPoolConfig

※由於 JedisPool 和 Jedis 都有繼承 AutoCloseable,所以使用 try 的圓括號會自動關閉

參考
http://webcache.googleusercontent.com/search?q=cache:sFi0y--GsrMJ:bijian1013.iteye.com/blog/2100714+&cd=1&hl=zh-TW&ct=clnk&gl=ph

安裝 (nginx 一)

官網
此篇以 1.12.2 穩定版為主

※Linux (Fedora)

※安裝

pcre  pcre-devel 正則
zlib  zlib-devel

dnf install pcre
要有 root 權限才能安裝

tar -zxvf *.tar.gz
解壓完進去目錄做三步
1../configure 會產生一個檔案和一個資料夾,Makefile 和 objs
預設會安裝到 /usr/local/nginx,想改可用 --prefix 路徑

2.make,如果有失敗要再來一次,有可能會使用到 make distclean
3.make install

※操作

安裝完後在/usr/local/nginx 有很多東西,以後都要在這操作
/usr/local/nginx/sbin/ 有 nginx
.啟動 ./nginx,啟動後,預設會在 /usr/local/nginx/sbin/logs/ 生成 nginx.pid 檔案,裡面記錄 master pid,想改可在 /usr/local/nginx/conf/nginx.conf 修改
.關閉 ./nginx -s quit,nginx.pid 會刪除

-s 後可加的指令:
stop 關閉,不會等 session 離開
quit 關閉,會等 session 離開
reload 不用重啟就可以讀到新的設定檔 nginx.conf
reopen 重新打開 log 檔

信號:

TERM、INT 相當於 -s stop
QUIT 相當於 -s quit
HUP 相當於 -s reload
USR1 相當於 -s reopen
以下兩個 -s 沒有
USR2 升級 nginx 使用
WINCH 升級後關才舊的 worker process

例:
kill -INT(TERM) master process 的pid號
kill -INT 26666
ps -aux |grep nginx 可查到 pid 號

USR1的用法:假設一直有資料寫進 access.log
如果將 access.log 改名,還是會寫入
必需使用 kill -USR1 PID號

不想看 pid 可用如下的方法
cat logs/nignx.pid 裡面已經記錄了 pid 號
所以可用 kill -USR1 `cat logs/nignx.pid`



※Windows

和 linux 很不一樣,所有的東西都在解壓後的資料夾裡

※查看 pid

滑鼠:
執行打 taskmgr (在最下面按右鍵 --> 工作管理員) --> detail

指令:
1-1.tasklist |findstr /i nginx  :/i為忽略大小寫
1-2.tasklist |find /i "nginx"
2.tskill pid號

※必需先刪除 master pid 才能刪 worker pid,否則 worker pid 會自動生成其他的 pid號,通常 master pid 的容量比較小,要正確的就看 logs\nginx.pid

※沒有 linux 的信號功能

2018年3月24日 星期六

複製、硝兵模式 Redis (四)

※複製

好幾台 redis 伺服器,有階級的關係,分成 master 和 slave,slave 下面還可以有 slave

以下創建四個伺服器,關係如下:

將 redis.conf 複製 4 個,然後修改,分別設定不同的 port
###NETWORK###
port 6379~6382

###GENERAL###
daemonize yes
pidfile /var/run/redis_6380.pid
#logfile "6380.log"


###SNAPSHOTTING###
dbfilename dump6380.rdb


port 6381 比其他 port 多做以下的設定
###REPLICATION###
slaveof 127.0.0.1 6379




1.6381 一啟動伺服器會直接是 6379 的 slave,其他必需做 slaveof 的動作才可以




2.還沒下 slaveof 之前,如果 master 已有一些資料,slaveof 之後,連 master 之前的也能取
但如果 slave 本來在 slaveof 之前已有的資料,slaveof 之後會不見,也就是會被 master 整體覆蓋


3.slave 只能讀不能寫,6380是 master,也是 slave,但 info  replication 時,還是顯示 slave,所以也不能寫

4.master 掛掉後,slave還是有值。master 回來後,一切照舊

5.slave掛掉又回來後會變成master,必需重新用 slaveof,除非有在 redis.conf 設定 (如6381)

6.slaveof no one 命令:如果 master 掛了,可以下這個指令,可以變成 master
如 6380 下這個指令會變成 master,脫離了 master,當然值還是存在
此時 6381 還是乖乖的在等 master,可以再 6381 使用 slaveof 連到新的 master (6380)
這時候 6380 有 6381 和 6382
6379 回來後(還是 master),和 6380~6382 一點關係也沒有了



※硝兵模式

master 掛了之後會自動選出新的 master,不用下 slaveof no one

需設定 sentinel.conf
格式:sentinel monitor 被監控的 master 名稱 IP port 超過多少票就是 master
sentinel monitor xxx 127.0.0.1 6379 1
xxx 掛了,誰的票超過1票,就會變成master

redis-sentinel sentinel.conf

master掛了之後,要等約 30 秒左右才會選出新的 master

舊 master 回來後,一開始是 master,過了幾秒後會變 slave

2018年3月22日 星期四

redis 設定檔、事務 Redis (三)

※redis.conf 設定檔

有神人翻譯好了
在伺服器執行期間可用
config set
config get
動態更改設定內容,但伺服器關閉並不會寫回設定檔



※事務

multi:啟動事務
exec:執行事務
discard:放棄事務
從 multi~exec 或 multi~discard 為一個事務

以下兩個命令必需在 multi 之前
watch:監視 1~多個 key,被監視的 key 若被改變,執行 exec 時不會成功
unwatch:放棄監視所有 key

※使用 SCRIPT 也能做到事務的功能,所以未來有可能會將事務移除
官網 script 連結
中文 script 連結

順利的事務:


執行 exec 不是 nil 時,就是順利的事務


放棄事務:


放棄了事務,所以 xxx 和 ooo 並沒有設定成功


編譯錯誤的事務:


編譯錯誤會整體回退,所以在這個事務裡面的操作都不會成功
fjasdf 並不是正常的命令,算編譯錯誤
又假如 set 命令的格式是 set key value,假設只有打 set key,並沒有打 value,也算是編譯錯誤


執行期錯誤的事務:


k1 並不是數字,但要到執行期才會知道,此時不會整體回退,


順利監視的事務:


如果順利,那有沒有加 watch 的結果都是一樣的


unwatch:


在 watch 之後,還沒下 multi,值就被改了(就算值改成一樣也算改),執行到 exec 時,回傳 nil,表示失敗了

此時要 unwatch 後再重新操作一次,由於已經執行過 exec,所以再操作一次時,還是要下 multi

如果再操作一次途中,值又被改了,當然也不會成功,必需一直反覆執行這個動作或放棄事務