ASP.NET Thread Pool 與 Redis Timeout Exception

概述

ASP.NET Thread Pool 的機制如何影響 Redis

案例

線上維護的系統偶爾會發生 Redis Timeout Exception ,
並在 elmah 發現以下的錯誤記錄

1
2
3
Timeout performing SETEX Cache:Prod:WebAPI:Key:20161121152607, inst: 18, mgr: ExecuteSelect, err: never,
queue: 0, qu: 0, qs: 0, qc: 0, wr: 0, wq: 0, in: 0, ar: 1,
IOCP: (Busy=0,Free=1000,Min=8,Max=1000), WORKER: (Busy=13,Free=32754,Min=8,Max=32767), clientName: TYO-MWEB

解析

這時當 StackExchange.Redis 在進行同步作業的時候,
如果超過 syncTimeout 的設定值(預設是 1000ms),
Redis 會佔用.NET 的 worker Thread
而在 .NET 底層隱含著一個機制,
會導致錯誤。

Thread Pool 500ms 的機制

一種簡化的說法「 ASP.NET Thread Pool 一秒能建立 2 個 Thread。」

設定值 minworkerthread 就像是遊樂場已經開啟的閘門,
每當有一個遊客(Task)進來時,立即提供給它使用。
但是當遊客(Task)變多的時候,就會開始排隊(Queue),
ASP.NET Thread Pool 隱含著一個機制,
當它的隊伍(Queue)長達 500 豪秒沒有移動的話,
就會開啟新的閘門(建立新的 Thread)。
而我的情境屬於Burst of traffic,
突然大量 Task 湧入 Queue ,
ThreadPool 需要大量的 Thread
每個新的 Thread 都需要 500ms 的反應時間 ,
而累積的時間超過 Task 的 Timeout 設定值時 ,
就會拋出 Exception.

ASP.NET Thread Pool

ASP.NET Thread Pool 的排隊機制與minworkerthread 設定值相關。
可以透過調整 machine.config 來修正。
參考(爭用、 效能不佳、 和死結 (deadlock) 當您從 ASP.NET 應用程式呼叫 Web 服務)。

minworkerthread 的預設值是 1 。
但是會與執行環境的 CPU 個數有關,
假設你是四核的主機,那就要乘上 4。

1
2
3
<system.web>
<processModel autoConfig="false" minWorkerThreads="1" />
</system.web>

範例

當 Redis 發生 Timeout 時,
可以透過錯誤訊息判斷其背後的原因是否與 workerthread 有關。
在以下的例子可以看到 IOCP 與 WORKER 兩個值。
這兩值表示 .Net ThreadPool 內的兩種執行緒,
如果 Busy 值很高, 就有可能是 ThreadPool 來不及建立造成的錯誤。

1
2
3
System.TimeoutException: Timeout performing MGET 2728cc84-58ae-406b-8ec8-3f962419f641,
inst: 1,mgr: Inactive, queue: 73, qu=6, qs=67, qc=0, wr=1/1, in=0/0
IOCP: (Busy=0, Free=999, Min=2,Max=1000), WORKER (Busy=7,Free=8184,Min=2,Max=8191)

Error Code 說明

Error code Details 範例 說明
inst in the last time slice: 0 commands have been issued 在最後時脈:已發出 0 個命令 最後的時脈發出的命令個數
mgr the socket manager is performing “socket.select”,
which means it is asking the OS to indicate a socket
that has something to do;
basically: the reader is not actively reading from
the network because it doesn’t think there
is anything to do
最後的操作命令
queue there are 73 total in-progress operations 73 個正在排隊中的操作 正在排隊中的操作
qu 6 of those are in unsent queue: they have not yet been written to the outbound network 6 個未發送的 queue 未發送的 queue
qs 67 of those have been sent to the server but a response is not yet available. The response could be: Not yet sent by the server sent by the server but not yet processed by the client. 67 個已發送的 queue 已發送的 queue
qc 0 of those have seen replies but have not yet been marked as complete due to waiting on the completion loop 0 個已發送未標記完成的 queue 已發送未標記完成的 queue
wr there is an active writer (meaning - those 6 unsent are not being ignored) bytes/activewriters 有 1 個啟用的 writer,(意味著 qu 的工作並沒有被忽略) bytes/activewriters bytes/activewriters
in there are no active readers and zero bytes are available to be read on the NIC bytes/activereaders 0 個 reader bytes/activereaders

參考


其它的 Redis Error 情境

只作摘要式的翻譯。

Memory pressure (記憶體壓力)

Problem:
記憶體不足而開始讀取虛擬記憶體(磁碟)而導致特能低落。
Memory pressure on the client machine leads to all kinds of performance problems that can
delay processing of data that was sent by the Redis instance without any delay.
When memory pressure hits, the system typically has to page data from physical memory to
virtual memory which is on disk. This page faulting causes the system to slow down significantly.

Measurement:
Monitory memory usage on machine to make sure that it does not exceed available memory.
Monitor the Page Faults/Sec perf counter. Most systems will have some page faults even during
normal operation, so watch for spikes in this page faults perf counter which correspond with timeouts.

Resolution:

增加記憶體或是減少記憶體使用量
Upgrade to a larger client VM size with more memory or dig into your memory usage patterns
to reduce memory consuption.

Burst of traffic

Problem:

ThreadPool 突然大量的工作湧入 queue 導致執行緒來不及建立。
Bursts of traffic combined with poor ThreadPool settings can result in delays in processing
data already sent by the Redis Server but not yet consumed on the client side.

測量:

可以用程式監控 ThreadPool .
或是單純透過 TimeoutException 的訊息,觀察 IOCP 與 WORKER 的 Busy 值來判斷.

Measurement:

Monitor how your ThreadPool statistics change over time using code like this.
You can also look at the TimeoutException message from StackExchange.Redis.
Here is an example :

1
2
System.TimeoutException: Timeout performing EVAL, inst: 8, mgr: Inactive, queue: 0, qu: 0, qs: 0, qc: 0, wr: 0, wq: 0, in: 64221, ar: 0,
IOCP: (Busy=6,Free=999,Min=2,Max=1000), WORKER: (Busy=7,Free=8184,Min=2,Max=8191)

In the above message, there are several issues that are interesting:
Notice that in the “IOCP” section and the “WORKER” section you have a “Busy” value that is
greater than the “Min” value. This means that your threadpool settings need adjusting.
You can also see “in: 64221”. This indicates that 64211 bytes have been received at the kernel
socket layer but haven’t yet been read by the application (e.g. StackExchange.Redis).
This typically means that your application isn’t reading data from the network as quickly as
the server is sending it to you.

Resolution:

調整 ThreadPool 設定
Configure your ThreadPool Settings to make sure that your threadpool will scale up quickly under burst scenarios.


High CPU usage (CPU 過載)

Problem:

High CPU usage on the client is an indication that the system cannot keep up with the work that it has been asked to perform. High CPU is a problem because the CPU is busy and it can’t keep up with the work the application is asking it to do. The response from Redis can come very quickly, but because the CPU isn’t keeping up with the workload, the response sits in the socket’s kernel buffer waiting to be processed. If the delay is long enough, a timeout occurs in spite of the requested data having already arrived from the server.

Measurement:

Monitor the System Wide CPU usage through the azure portal or through the associated perf counter.
Be careful not to monitor process CPU because a single process can have low CPU usage at the same time that overall system CPU can be high.
Watch for spikes in CPU usage that correspond with timeouts. As a result of high CPU,
you may also see high “in: XXX” values in TimeoutException error messages as described above in the “Burst of traffic” section.
Note that in newer builds of StackExchange.Redis, the client-side CPU will be printed out in the timeout error message as long as the environment doesn’t block access to the CPU perf counter.

Note:

StackExchange.Redis version 1.1.603 or later now prints out “local-cpu” usage when a timeout occurs to help understand when client-side CPU usage may be affecting performance.

Resolution:

增加 CPU 或是找出 CPU 產生過載的原因
Upgrade to a larger VM size with more CPU capacity or investigate what is causing CPU spikes.


Client Side Bandwidth Exceeded (頻寬不足)

Problem:

Different sized client machines have limitations on how much network bandwidth they have available.
If the client exceeds the available bandwidth, then data will not be processed on the client side as quickly as the server is sending it. This can lead to timeouts.

Measurement:

Monitor how your Bandwidth usage change over time using code like this. Note that this code may not run successfully in some environments with restricted permissions (like Azure WebSites).

Resolution:

加大頻寬或減少使用量
Increase Client VM size or reduce network bandwidth consumption.

Large Request/Response Size (過大的請求/回應量)

Problem:

如圖所示,A 與 B 兩個 Request 都太過龐大,當同時發動請求時,
A 回應的時間過長, 導致 B 的 Timeout
A large request/response can cause timeouts. As an example, suppose your timeout value configured is 1 second.
Your application requests two keys (e.g. ‘A’ and ‘B’) at the same time using the same physical network connection.
Most clients support “Pipelining” of requests, such that both requests ‘A’ and ‘B’ are sent on the wire to the server one after the other without waiting for the responses.
The server will send the responses back in the same order. If response ‘A’ is large enough it can eat up most of the timeout for subsequent requests.

Below, I will try to demonstrate this. In this scenario, Request ‘A’ and ‘B’ are sent quickly,
the server starts sending responses ‘A’ and ‘B’ quickly, but because of data transfer times,
‘B’ get stuck behind the other request and times out even though the server responded quickly.

1
2
3
4
5
6
|-------- 1 Second Timeout (A)----------|
|-Request A-|
|-------- 1 Second Timeout (B) ----------|
|-Request B-|
|- Read Response A --------|
|- Read Response B-| (**TIMEOUT**)

Measurement:

This is a difficult one to measure. You basically have to instrument your client code to track large requests and responses.

Resolution:

將所需要的資料分割成數個小片段 再分別取回
Redis is optimized for a large number of small values, rather than a few large values.
The preferred solution is to break up your data into related smaller values. See here for details around why smaller values are recommended.
Increase the size of your VM (for client and Redis Cache Server), to get higher bandwidth capabilities,
reducing data transfer times for larger responses.
Note that getting more bandwidth on just the server or just on the client may not be enough.
Measure your bandwidth usage and compare it to the capabilities of the size of VM you currently have.
Increase the number of ConnectionMultiplexer objects you use and round-robin requests over different connections (e.g. use a connection pool).
If you go this route,make sure that you don’t create a brand new ConnectionMultiplexer for each request as the overhead of creating the new connection will kill your performance.

(fin)

有關 HTTP Header Content-Type

引言

當我們對 Server 發出 request 的時候
需要註明你 request 的 Content-Type
以下簡單介紹一下這些格式

測試工具

PostMan這套工具
可以模擬不同的格式資料發動 request 到你的 Server


Content-Type

application/x-www-form-urlencoded

常用的 Content-Type,簡單說就是 KEY-VALUE 的方式
如下, KEY firstname 的值是 marsen
lastname 是由使用者輸入

1
2
3
4
5
6
<form>
First name:<br />
<input type="text" name="firstname" value="marsen" /><br />
Last name:<br />
<input type="text" name="lastname" />
</form>

同時資料會作一次 url encoded,
產生類似下列的資料
firstname=marsen&lastname=lin&key%5b1%5d=value%5b1%5d


multipart/form-data

PostMan 中的選項 binary 其實就是包成這種格式
上傳檔案會使用這種 Content-Type,
這通常表示你的 html element 包含有 <input type="file">


其它

PostMan 中的選項 raw,可以用字串組合成任意 Content-Type,
參考Content-Type Table

  • application/json
    目前主流的 API 會用輕巧的 JSON 作為傳遞資訊的媒介
  • text/xmlapplication/xml
    早期標準的 Web 服務通常會透過 xml 作為交換資訊的媒介
  • text/plain
    有些 email 或 debug 的情況會使用 text/plain 作為 Content-Type,但是一般的 Request 情況不建議使用
  • 更多請參考Spec

參考

(fin)

[記錄]PowerShell 初體驗

需求

  1. 將指定的 Log 記錄,匯入資料庫,產生 row data
  2. 將 row data 轉換成為需要的報表資料
  3. 產生報表

規劃

  1. powershell 讀取檔案
  2. powershell 連接資料庫
  3. powershell 執行 SQL
  4. powershell 作 BulkInsert
  5. powershell 寫入檔案

PowerShell

簡記要點

  • powershell 可以直接取用 .Net Framework 或 COM 元件
  • 宣告變用要用$字號
  • # 是註解

讀取檔案

1
2
3
4
5
6
#用New-Object 建立.Net StreamReader 物件
$reader = New-Object System.io.streamReader(get-item $filePath)
#使用`[]`建立靜態類別讀取檔案
$file = [System.IO.File]::ReadAllLines($filePath)
#直接使用Get-Content讀取文檔
$file = Get-Content "C:\filepath\file"

連線資料庫與執行語法

1
2
3
4
5
$connection = New-Object System.Data.SQLClient.SQLConnection
$connection.ConnectionString = "server='$server';database='$database';uid='$user'; pwd='$pwd';Integrated Security=False;"
$connection.Open()
# do something
$connection.Close()

BulkInsert

  • 從檔案建立 DataTable
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
$table = New-Object System.Data.DataTable
#建立欄位
$col_title = New-Object system.Data.DataColumn "Title",([string])
$table.Columns.Add($col_title);
$col_content = New-Object system.Data.DataColumn "Content",([string])
$table.Columns.Add($col_content);
$col_author = New-Object system.Data.DataColumn "Author",([string])
$table.Columns.Add($col_author);
#建立資料
foreach($file in $files){
$dr = $table.NewRow();
$dr["Title"] = $file["title"]
$dr["Content"] = $file["content"]
$dr["Author"] = $file["author"]
}
#寫入資料表
$table.Rows.Add($dr);
  • 透過 BulkCopy 將 DataTable 寫入資料庫
1
2
3
4
5
$connection.Open()
$bulkCopy = New-Object (“Data.SqlClient.SqlBulkCopy”) -ArgumentList $connection
$bulkCopy.DestinationTableName = "tablename"
$bulkCopy.WriteToServer($datatable)
$connection.Close()

進度條

1
Write-Progress -Activity "BulkInsert" -Status "載入百分比: 100 %" -PercentComplete 100;

產生報表

1
$datatable | export-csv C:\Reports\20161026.csv -Encoding UTF8

參考

  1. https://msdn.microsoft.com/en-us/powershell
  2. https://msdn.microsoft.com/en-us/powershell/scripting/getting-started/cookbooks/using-static-classes-and-methods
  3. https://cmatskas.com/execute-sql-query-with-powershell/
  4. https://blogs.technet.microsoft.com/heyscriptingguy/

(fin)

[KATA] 用 TypeScript 作一個簡易的 TodoList (二) - 用JQuery實作

設計理念

  1. 顯示/新增/刪除 TodoList
  2. TodoList 會是一堆 todoItem 的集合,所以要定義 todoItem 的形別
    • Content : todoItem 的內容
    • Status : todoItem 的狀態,完成(done)、未完成(undo) ,設計成列舉
  3. 主要的功能
    • 建立 todoItem
    • 完成 todoItem
    • 繪製 todoList 到前端的畫面上

自我分析

跟 UI 耦合太高,Render 應該與 TodoService 分離 ,
DOM 註冊事件相依在 Service 裡面要抽離也不好抽 。
沒有先寫測試 , 要想一想怎麼與 UI 層作隔離。

程式碼

建立 BaseService

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class BaseService<T> {
constructor(private type: string) {}

List: Array<T>;

Create(data: T) {
this.List.push(data);
}

Delete(data: T) {
var index = this.List.indexOf(data);
this.List.splice(index, 1);
}

Render() {}
}

建立 todoItem interface

1
2
3
4
interface todoItem {
Content: string;
Status: todoStatus;
}

建立 todoItem Status 列舉

1
2
3
4
enum todoStatus {
undo,
done,
}

建立 TodoService

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
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
class TodoService extends BaseService<todoItem> {
constructor() {
super("todoItem");
}

public Render(): void {
let doneHtml = "";
let undoHtml = "";

this.List.forEach((item) => {
if (item.Status == todoStatus.done) {
doneHtml += `<li>${item.Content}<button class="recover-item btn btn-default btn-xs pull-right"><span class="glyphicon glyphicon-share-alt"></span></button><button class="remove-item btn btn-default btn-xs pull-right"><span class="glyphicon glyphicon-remove"></span></button></li>`;
} else if (item.Status == todoStatus.undo) {
undoHtml += `<li class="ui-state-default"><div class="checkbox"><label><input type="checkbox" value="" />${item.Content}</label></div></li>`;
}
});
$("#done-items").html(doneHtml);
$("#sortable").html(undoHtml);
$(".add-todo").val("");
}

Delete(data: todoItem) {
data.Status = todoStatus.done;
}

Init() {
//// todoList in localStorage
var list = window.localStorage.getItem("todoList");
if (!list) {
this.List = new Array<todoItem>();
} else {
this.List = JSON.parse(list);
}
window.onbeforeunload = (evt) => {
window.localStorage.setItem("todoList", JSON.stringify(this.List));
};

// mark task as done
$(".todolist").on(
"change",
'#sortable li input[type="checkbox"]',
(evt) => {
var self = evt.target;
var text = $(self).parent().text();
if ($(self).prop("checked")) {
var doneItem = this.List.filter((i) => {
return text == i.Content;
})[0];
this.Delete(doneItem);
this.Render();
}
}
);

$(".add-todo").on("keypress", (evt) => {
evt.preventDefault;
if (evt.which == 13) {
if ($(evt.target).val() != "") {
var todo = $(evt.target).val();
this.Create({
Content: $(evt.target).val(),
Status: todoStatus.undo,
});
this.Render();
} else {
// some validation
}
}
});

$(".todolist").on("click", "#done-items li button.recover-item", (evt) => {
var text = $(evt.target).parent().parent().text();
var recoverItem = this.List.filter((i) => {
return text == i.Content;
})[0];
recoverItem.Status = todoStatus.undo;
this.Render();
});

$(".todolist").on("click", "#done-items li button.remove-item", (evt) => {
var text = $(evt.target).parent().parent().text();
var removeItem = this.List.filter((i) => {
return text == i.Content;
})[0];
var index = this.List.indexOf(removeItem);
this.List.splice(index, 1);
this.Render();
});

$("#checkAll").on("click", (evt) => {
this.List.forEach((item) => {
item.Status = todoStatus.done;
});
this.Render();
});

//// Render
this.Render();
}
}

使用建立好的 TodoService

1
2
var todoService = new TodoService();
todoService.Init();

(fin)

[KATA] 用 TypeScript 作一個簡易的 TodoList (一) - 前置作業

目標

  1. 使用 typescript 開發
  2. 顯示/新增/刪除 TodoList

功能分析

  1. 只是練習,故不開發 server side 的程式
  2. 暫時存在 cookie 上
  3. 用 bootstrap 作簡單的樣式

實作記錄

UI : 使用 Bootstrap 沒有必要重新打造輪子,能用的就拿來用

  • 找到一個 TodoList 的樣版,內含 HTML 、 CSS 與 JS ,功能完整.
  • 取用樣版的 HTML.
  • 引用 bootstrap 3.3.5 CDN 上的 css .
  • 建立一個 todo.css 直接引用樣版的 css 並加入頁面參考.

開發環境

  1. 安裝 typescript
    npm install typescript --save
  2. 安裝 gulp
    npm install gulp
    npm install --global gulp
  3. 安裝 gulp-typescript
    npm install gulp-typescript
  4. 建立 gulpfile.js
1
2
3
4
5
6
7
8
var gulp = require("gulp");
var tsc = require("gulp-typescript");
gulp.task("default", function () {
return gulp
.src("public/javascripts/**/*.ts")
.pipe(tsc())
.pipe(gulp.dest("public/javascripts/"));
});

Typescript

  1. 關於 typescript 的定義檔, 以前有 tsd 與 typings 兩種管理工具,現在可以更簡便的合併到 npm 作管理 .

  2. 透過 TypeSearch 可以找到 bootstrap 的 typescript 定義檔.

  3. 執行 npm install --save @types/bootstrap 安裝 bootstrap (目前的版本是 Bootstrap 3.3.5) , 因為相依於 jquery 所以也會一併安裝

  4. 安裝 jquery-ui 的定義檔
    npm install --save @types/jqueryui

  5. 新增檔案 todo.ts ,將 樣版 的 javascript 複製貼上 .
    *註:因為 typescript 是 javascript 的 superset , 完全可以相容原生 javascript, 如果有任何錯誤, TypeScript 將會提示你

  6. todo.ts 引用 jquery 、jquery-ui 與 bootstrap 的 typescript 定義檔.

    1
    2
    3
    /// <reference path="../../../node_modules/@types/jquery/index.d.ts" /> ///
    <reference path="../../../node_modules/@types/bootstrap/index.d.ts" /> ///
    <reference path="../../../node_modules/@types/jqueryui/index.d.ts" />
  7. 頁面載入對應的 js 檔,記得放置順序 jquery 要在最前面,並將放在<\body>之後

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    <script
    src="http://code.jquery.com/jquery-2.2.4.min.js"
    integrity="sha256-BbhdlvQf/xTY9gja0Dq3HiwQF8LaCRTXxZKRutelT44="
    crossorigin="anonymous"
    ></script>
    <script src="//maxcdn.bootstrapcdn.com/bootstrap/3.3.5/js/bootstrap.min.js"></script>
    <script
    src="http://code.jquery.com/ui/1.12.1/jquery-ui.min.js"
    integrity="sha256-VazP97ZCwtekAsvgPBSUwPFKdrwD3unUfSGVYrahUqU="
    crossorigin="anonymous"
    ></script>
  8. 執行 gulp ,會產生 todo.js

  9. 頁面載入 js 檔,記得要放在相依的 js( jquery 、 bootstrap 、 jqueryui)之後.

1
<script type="text/javascript" src="/javascripts/kata/todo.js"></script>

截至目前為止,僅僅是在作複製樣版,同時一併處理一些基本 gulp 與 typescript 相關的配置
接下來才要開始寫 typescript

(待續)

參考

  1. 關於 TypeScript 2.0 之後的模組定義檔 ( Declaration Files ) ( *.d.ts )
  2. bootsnipp
  3. TYPESCRIPT + EXPRESS + NODE.JS

(fin)

[實作筆記] 怎麼建立一個網站?(三) - 簡單讓網站升級使用HTTP/2

應該知道的事

  • HTTP/1.1 是目前被應用得最廣泛的通訊協定,也可以說是最成功的一個
    • HTTP/1.1 基於 TCP,需要三方交握。
    • HTTP/1.1 本身存在得需多問題,安全性、效率等…。
  • HTTP/2 可以有效改善 HTTP/1.1 的缺點,而且可以向下相容
    • 主流的瀏覽器都開始支援了。
    • 只要設定好 Web Server,Client 端看使用的瀏覽器支不支援
  • 一流公司制定標準,二流作品牌,三流賣技術,四流作產品
    • HTTP/2 是源自 Google 的 SPDY。
    • Google 下一代 QUIC 推動中。
  • HTTP/2 並不強制加密(HTTPS)
    • 在主流瀏覽器為了向下相容,保留了 80 port 的 HTTP/1.1 通訊協定,用 443 port 實作 HTTP/2。
    • 所以還是要走 HTTPS,所以需要安全性憑証。
    • 安全性憑証要錢(現在也有免費的)。
    • cloudflare提供免費的安全性憑証。

前置作業

  1. 你要有cloudflare帳號。
  2. 看過我之網站的設定,簡單說就是一個 github page 與 透過 OpenShift 這個 PaaS 服務建立的網站。

配置

  1. cloudflare 設定 DNS,在指定的 domain 下,設定為「DNS and HTTP Proxy(CDN)」。
    設定為「DNS and HTTP Proxy(CDN)」
  2. 設定 Page-Rules,這個是為了開啟 SSL 1. 設定SSLFlexible,輸入*.username.com/*
    這個時候你就可以瀏覽https://blog.username.com/,但同時也可以瀏覽http://blog.username.com/
    我希望 HTTP 能自動跳轉 HTTPS,這部份如果是 Node 建立的網站,我可以用程式作轉導,
    但是如果是 github page 建立網站,就要再仰賴 cloudflare 了。
  3. 設定 Page-Rules,這個是為了開啟 SSL 1. 設定ALWAYS USE HTTPS,輸入http://*.username.com/*
    設定為「DNS and HTTP Proxy(CDN)」

設定完大致如下,順利的話幾分鐘就生效了。
生效

這時候瀏覽 http://blog.username.com/ 就會轉導到 https://blog.username.com/ 了 。

檢驗

使用 support HTTP/2 的瀏覽器,瀏覽網頁,觀察其 protocol 。
HTTP/2

系列文章

參考

(fin)

[工作筆記] 匯入文字資料到 MsSQL

前置作業

  1. 一個有權限寫入的 MsSQL Server 與 SSMS 管理工具
  2. 準備好你的檔案資料(Row Data)
  3. 這份記錄僅供參考(DB 版本使用 Microsoft SQL Server 2014)

步驟記錄

  1. 開啟 SSMS,連線測試 SQL Server
  2. 對測試資料庫右鍵>工作>匯入資料,開啟「SQL Server 匯入匯出精靈」
  3. 「開始畫面」> 下一步
  4. 「資料來源」選擇一般檔案來源,「檔案名稱」選取 Row Data 的檔案路徑
  5. 請依實際狀況選擇下列欄位
    • 「略過的標頭資料列數」: 預設為 0,可透過設定此欄位略過 Row Data 內含註解,標頭等資料
    • 「第一個資料列的資料行名稱」:若 Row Data 第一行為標頭,請勾選此欄位
    • 左側選單選擇「資料行」>「資料列分隔符號」選擇「{CR}{LF}」、「資料行分隔符號」選擇「Tab 鍵{t}」
  6. 左側選單選擇「預覽」確認匯入的資料無誤後,點擊下一步
  7. 「目的地」請選擇「SqlServer」,設定 ConnectionString
    • Data Source=1**.*..;Initial Catalog=TestDB;Persist Security Info=True;User ID=tester;Password=******;
  8. 「選取來源資料表和檢視畫面」確認無誤後,點擊下一步。
  9. 「檢閱資料類型對應畫面」請忽略資料類型的警告,點擊下一步。
  10. 「執行封裝畫面」勾選「立即執行」,點擊下一步。
  11. 「完成精靈畫面」點選完成。
  12. 執行時間長短,依 Row Data 大小而有所差異。

(fin)

[實作筆記] 怎麼建立一個網站?(二) - 簡單用github page 建立靜態網站

前置作業

  • 你要有一個 GitHub 帳號

建立 github page

如果不排斥看原文,可以直接參考

  1. 建立一個 Repository , 並且命名為 username.github.io , 這裡的 username 請使用你的 GitHub 帳號的 username.

  2. clone username.github.io 到你的本機上.

    1
    git clone https://github.com/username/username.github.io
  3. 建立一個靜態網頁 index.html , 隨便打點什麼。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    <!DOCTYPE html PUBLIC "-//IETF//DTD HTML 2.0//EN">
    <html>
    <head>
    <title>Hello world</title>
    </head>
    <body>
    <h1>Hello world</h1>
    <p>This is my github page</p>
    </body>
    </html>
  4. commit 之後,push 到 github 上 > git add –all > git commit -m “Initial commit” > git push -u origin master

  5. 瀏覽 http://username.github.io 即完成

使用自訂的 Domain

  1. 首先準備好一個 domain ex: username.xyz
  2. 需要在根目錄底下,放入一個 CName file
    檔案的內容只需要你的 domain 即可
    ex:
    blog.username.xyz
    username.xyz
    username.xyz
  3. 在 Name Servers (例如cloudflare)上設定 CNAME 到 github page,
    blog.username.xyz 綁定到 username.github.io
    ex:
TYPE NAME VALUE TTL
CName * username.xyz auto
CName blog username.github.io auto

更多請參考「購買網域到設定 DNS」.

透過 Hexo 部署

Hexo 基本概念可以參考官方中文文件 .

  1. 重點在於 _config.yml 的設定
    deploy:
    type: git
    repository: https://github.com/username/username.github.io
    branch: master

  2. 執行 hexo d 進行部署,這個動作會將 hexo 建立出來的靜態網站(html+css+javascript+圖片等…)部署到 github page 上。

系列文章

(fin)

如何讓 Google Analytics 追踪你的 Hexo Blog

前置作業

  1. 你要有 google 帳號,並申請好你的 google_analytics ID
  2. 這個記錄僅針對 Hexo 預設的 Theme 有作用,未來有改 Theme 的話,可能會需要手動加入,再寫文章補上

開啟_config.yml

  1. 確定一下你是使用預設的 theme landscape ## Themes
    theme: landscape
  2. 開啟 root/themes/landscape/_config.yml
  3. 找到以下的設定區段 # Miscellaneous
    google_analytics:
  4. 填入步驟 1. 中所取得 google_analytics ID
  5. 部署網站,完成!

(fin)

[實作筆記] 怎麼建立一個網站?(一) - 購買網域到設定DNS

前置作業

  1. 準備好你的 google 帳號。
  2. 可以連到美國的 VPN。google domain beta 台灣尚未開放
  3. 準備一張信用卡,狠狠的刷下去(挑對 domain 其實很便宜啦)
  4. 你的網站,什麼語言都可以,靜態的網頁也可以。
    *這裡我事先準備好了兩個網站,
    分別是用github pagenodejsexpress,實作有機會再作記錄。

設定 domain

  1. Github page 所建立的網站,會提供一組 domain 給你
    ex:myDomain.github.io

  2. OpenShift 建立的網站,一樣會提供一組 domain 給你
    ex:myDomain.rhcloud.com

  3. google domain 本身有提供 Name servers , 但是由於 typeA 的 Domain,
    必須指定公用 ip(家中有裝 HiNet ADSL 可以申請一組);但實際上我的兩個網站,並不需要我準備實體 IP,
    只需要使用 type CName 將我的 subdomain 指向原本服務的 domain 即可。
    ex:

    • blog.myDomain.me → myDomain.github.io
    • www.myDomain.me → myDomain.rhcloud.com
  4. github page 要注意的事情,

你需要在你的 github page 的 repo root 加入一個名叫CName的檔案,
檔案的內容很簡單, 只需要你的 domain 即可
ex:
blog.myDomain.me
myDomain.me

Bare CName

大多數的時候, CName 的設定就夠了;不過對於我來說,
我會希望可以直接使用我的頂級 domain,畢竟這樣網址可以更短一些,
myDomain.mewww.myDomain.me 更有感覺。

這裡受限於 google 的 Name servers , 頂級 domain 必為 TypeA須綁定 ip
這裡我們可以使用一個免費的服務cloudflare,來達成目的

  1. 註冊cloudflare

  2. 登入後add site,輸入你註冊的 domain
    ex:myDomain

  3. cloudflare 會提供你至少兩組 Name servers
    ex:
    carter.ns.cloudflare.com
    tina.ns.cloudflare.com

  4. 請先登入google domain beta設定 Name servers 到cloudflare

  5. 請依以下步驟設定

    • * → myDomain.me
    • blog.myDomain.me → myDomain.github.io
    • www.myDomain.me → myDomain.rhcloud.com
  6. 等待約數分鐘就 ok 啦

結語

前前後後查資料弄了一個禮拜,但是實際上設定大概 1~2 小時就搞定了。
對 domain 跟 ip 的相對關係與實務結合後更有體會了。

歡迎指教討論。

系列文章

(fin)

Please enable JavaScript to view the LikeCoin. :P