2011年9月27日 星期二

[XML] The difference between value-of and copy-of

差別:

  • xsl:value-of returns all the TEXT within the selected tag(s).
  • xsl:copy-of returns all the ELEMENTS (both tags and text) of the selected tag(s).

一言以蔽之, value-of 只會回傳你選擇node裡面的text, 但是copy-of 卻是會把整個node structure都回傳回來.

例子:

<Name>
 <Family>Smith</Family>
 <Given>John</Given>
</Name>

XSLT:
<xsl:value-of select="Name"/> 

<!-- 輸出element Name下面的所有text -->

Output:
Smith
John

 

XSLT:

<xsl:value-of select="Name"/> 

輸出Name底下的所有element (包含tag)

Output:

<Name>

 <Family>Smith</Family>

 <Given>John</Given>

</Name>

 

XSLT:
<xsl:value-of select="Name/node()"/> 

輸出Name底下子節點的所有element (包含tag), 不包含自己

Output:

 <Family>Smith</Family>

 <Given>John</Given>

 

XSLT:

<xsl:value-of select="Name/text()"/> 
 輸出Name底下的text, 但是這邊沒有, 所以沒有輸出

Output:

Reference:
http://dev.ektron.com/blogs.aspx?id=10472

2011年9月26日 星期一

[C++] Constructor call other constructor


#include

class Foo
{
public:
Foo() { Foo(1); };
Foo(int a) { m = a; std::cout <<"Foo " << a << std::endl; }

private:
int m;
};

int main()
{
Foo a, b(2);
return 0;
}

It is ok to call other constructor on method body, but calling other constructor on Initialize list is not allowed!
The following example is not allowed.


#include

class Foo
{
public:
// this will cause compile error!!
Foo() : this(1) { };
Foo(int a) { m = a; std::cout <<"Foo " << a << std::endl; }

private:
int m;
};

int main()
{
Foo a, b(2);
return 0;
}

Reference:
http://stackoverflow.com/questions/308276/c-call-constructor-from-constructor

2011年9月16日 星期五

[RabbitMQ] cluster setting

在Ubuntu機器上面安裝很方便, 先安裝完erlang,另外要在裝erlang-nox, 接著直接把抓下來的rabbitmq-server deb安裝就行


$ sudo apt-get install erlang
$ sudo apt-get install erlang-nox
$ dpkg -i rabbitmq-server_2.6.1-1_all.deb
$

另外rabbitmq有提供使用plugin, 如果你是用deb安裝, 那你只需要把所有plugin (.ez)放到 /usr/lib/rabbitmq/lib/rabbitmqserver-2.6.1/plugins下就行了.
我是安裝rabbitmq-management plugin, 在cluster裡面, 只需要有一台安裝完整management plugin, 其他台都指需要裝management
agent就行~


$ ls /usr/lib/rabbitmq/lib/rabbitmq_server-2.6.1/plugins/
amqp_client-2.6.1.ez
rabbitmq_mochiweb-2.6.1.ez
mochiweb-1.3-rmq2.6.1-git9a53dbd.ez
README
rabbitmq_management-2.6.1.ez
webmachine-1.7.0-rmq2.6.1-hg0c4b60a.ez
rabbitmq_management_agent-2.6.1.ez

在erlang的世界裡面, 需要設定同樣的cookie,不同的node彼此之間才可以溝通, 所以假設我們有lab1, lab2, lab3三台機器, 只要把lab1 上面的/var/lib/rabbitmq/.erlang.cookie copy到其他兩台機器上面, 就可以溝通了, 之後就可以透過rabbitmqctl來建立cluster, 有幾點要特別注意

  • 更換.erlang.cookie完之後, 要記得rm -rf /var/lib/rabbitmq/mnesia
  • 要記得更改/etc/hosts裡面關於cluster ip host mapping, 這樣erlang才可以查到domain name的ip
  • 之後要自動設定, 可以寫檔案在/etc/rabbitmq 下面

$ rm -rf /var/lib/rabbitmq/mnesia
$ rabbitmqctl cluster_status
...
$ rabbitmqctl stop_app
...
$ rabbitmqctl reset
...
$ rabbitmqctl cluster rabbit@lab001 # add lab002 as mem mode
...
$ rabbitmqctl cluster rabbit@lab001 rabbit@lab003 # add lab003 as disk mode
...
$ rabbitmqctl cluster_status
...
$ cat /etc/rabbitmq/rabbitmq-env.conf
RABBITMQ_CONFIG_FILE=/etc/rabbitmq/rabbitmq.conf
$ cat /etc/rabbitmq/rabbitmq.conf
[ {rabbit, [ {cluster_nodes, ['rabbit@lab001', 'rabbit@lab003']} ]} ].
$

另外提到一點, erlang的shell可以拿來幫助debug, 當你選定某組cookie來跑rabbitmq server, 假設跑在lab001, 可以利用erlang shell確認連線有沒有錯誤,
假如回傳的是pang, 那代表連線有問題~ 另外也要確定epmd 這個erlang內建的daemon有沒有跑起來~


lab002 $ erl -sname ooo -setcookie
Erlang R13B03 (erts-5.7.4) [source] [64-bit] [smp:2:2] [rq:2] [async-threads:0] [hipe] [kernel-poll:false]

Eshell V5.7.4 (abort with ^G)
(oo@lab002)1> net_adm:ping('rabbit@lab001').
pong
(oo@lab002)2>

$ epmd -names
epmd: up and running on port 4369 with data:
name rabbit at port 58909

Reference
http://www.rabbitmq.com/clustering.html

[Network] TCP hole punching under NAT

NAT (network address translation) box, 目前可以分為四種address mapping方式

  • Full Cone mapping
  • Restricted Cone mapping
  • Port Restricted Cone mapping
  • Symmetric mapping

當我們要從Addr1: Port1 經過NAT1: Port 0出去到Addr2: Port 2的時候

Full Cone mapping:
NAT1: Port 0會處理任何跟Addr1:Port 1的連線, 就算是Addr3:Port3, 只要能猜到NAT1:Port 0, 都可以跟Addr1: Port 1溝通

Restricted Cone mapping:
只有當Addr1: Port 1之前有連線到Addr 3, Addr 3 才可以透過NAT1: Port 0連進來 Addr1: Port 1

Port Restricted Cone mapping:
只有當Addr1: Port 1之前有連線到Addr 3: Port 3, Addr 3:Port 3 才可以透過NAT1: Port 0連進來 Addr1: Port 1

Symmetric mapping:
NAT會自動為每一組Addr1: Port 1所連出去的IP Port都準備獨立的Port mapping, 例如
Addr1: Port 1 -> NAT: Port 0 -> Addr2: Port 2 
Addr1: Port 1 -> NAT: Port 4 -> Addr2: Port 3

這四種mapping的方式會影響到, 你如何利用TCP 來對NAT 打洞 (TCP hole punching), 前三種都算好解, 畢竟妳只要有辦法知道Addr 1:Port 1連出去的 NAT: Port 0, 你就可以跟他溝通, 第四種可能只能靠port predict的方式來做

2011年9月6日 星期二

[C++] Derived class no need to write virtual when base class already has

From
http://stackoverflow.com/questions/4895294/c-virtual-keyword-for-functions-in-derived-classes-is-it-necessary

But I think it's more readable and clear to write on virtual...

2011年8月30日 星期二

[C++] Effective C++ Item 42: Understand the two meanings of typename

今天剛好遇到一個小問題, 之前剛好在翻Effective C++, 翻一下書剛好就找到了, 順便在加深一下印象~

typename除了直接使用在template裡面當然宣告template parameter的地方, 如以下情形之外

template
class Foo
{
public:
    void foo(T a);
};

當在程式內部的name跟template parameter有相依關係的時候, 這個就叫做dependent name, 在C++當中, 他預設不會幫你假設這樣的name是type, 除非你有明確的告訴parser.

所以像以下的情形, 你就必須在前面加上typename, 這樣compiler才會認為宣告的T::iterator 也是一個type, 才不會有compiler error.


template
class Foo
{
public:
    void foo(T a);
    void goo(typename T::iterator i);
private:
    typename std::vector::iterator a;
};

2011年8月28日 星期日

[Erlang] RabbitMQ rabbit_alarm module

在Erlang裡面, 我們可以很簡單的實做一個alarm handler, 他可以接收各種alarm event, 並根據event  message來做出回應, 同時Erlang不限制你只能有一個alarm handler, 你可以根據不同功能來實做相對應的alarm handler, 只要註冊不同的名字, 在你觸發事件的時候, 可以選擇要給哪些alarm handler處理.

當要觸發事件的時候, 只需要呼叫gen_event:notify, 並且在handle_event處理相對應的event即可.

在rabbitmq內部他有實做一套memory monitor的機制, 這個機制是可以運作在cluster node內部或是單一node的情況. 而這個memory monitr有兩個部份, 關於內部所有用到的queue, channel所使用的memory monitor下次在介紹, 這次的memory monitor 只監控整體的使用量, 當到達高水位的時候, 便會block新進來的connection, 並且通知cluster其他node也要開始節省使用.

所在檔案rabbit_alarm.erl & vm_memory_monitor.erl

rabbitmq 在啟動的時候, boot的階段會去spawn一個alarm process.

rabbit.erl

 88 -rabbit_boot_step({rabbit_alarm,           
 89          [{description, "alarm handler"},
 90           {mfa,         {rabbit_alarm, start, []}},
 91           {requires,    kernel_ready},
 92           {enables,     core_initialized}]}).

在rabbit_alarm 這個process啟動的時候做了以下的事情, 他會從ebin/rabbit.app 裡面你所設定的memory條件來設定並啟動另外一個monitor process 來monitor memory, 當memory到達那個條件後的時候給你alarm.


rabbit_alarm.erl

 45 start() ->
        % 新增一個新的alarm handler, 名字是rabbit_alarm
 46     ok = alarm_handler:add_alarm_handler(?MODULE, []),
 47     {ok, MemoryWatermark} = application:get_env(vm_memory_high_watermark),
 48     ok = case MemoryWatermark == 0 of
            % 如果沒設定就不監控
 49          true  -> ok;
            % 啟動另外一個monitor process
 50          false -> rabbit_sup:start_restartable_child(vm_memory_monitor,
 51                [MemoryWatermark])
 52     end,
 53     ok.

    % rabbit_alarm本身有maintain兩個table
    % alertees 代表發生alarm他要通知的其他node
    % alarmed_nodes 代表已經發生過alarm的node
 78 init([]) ->
 79     {ok, #alarms{alertees      = dict:new(),
 80                  alarmed_nodes = sets:new()}}.

接下來先來看他監控的方式.

vm_memory_monitor.erl

105 start_link(Args) ->
106     gen_server:start_link({local, ?SERVER}, ?MODULE, [Args], []).
107  
108 init([MemFraction]) ->
109     TotalMemory =
        % get_total_memory 可以查看該檔案內部
        % 針對不同OS的處理
110         case get_total_memory() of
111             unknown ->
112                 error_logger:warning_msg(
113                   "Unknown total memory size for your OS ~p. "
114                   "Assuming memory size is ~pMB.~n",
115                   [os:type(), trunc(?MEMORY_SIZE_FOR_UNKNOWN_OS/1048576)]),
116                 ?MEMORY_SIZE_FOR_UNKNOWN_OS;
117             M -> M
118         end,
119     MemLimit = get_mem_limit(MemFraction, TotalMemory),
120     error_logger:info_msg("Memory limit set to ~pMB.~n",
121                           [trunc(MemLimit/1048576)]),
        % 啟動timer, 固定時間檢查memory usage
122     TRef = start_timer(?DEFAULT_MEMORY_CHECK_INTERVAL),
123     State = #state { total_memory = TotalMemory,
124                      memory_limit = MemLimit,
125                      timeout = ?DEFAULT_MEMORY_CHECK_INTERVAL,
126                      timer = TRef,
127                      alarmed = false},
128     {ok, internal_update(State)}.

171 internal_update(State = #state { memory_limit = MemLimit,
172                                  alarmed = Alarmed}) ->
        % erlang VM內部目前使用的
173     MemUsed = erlang:memory(total),
174     NewAlarmed = MemUsed > MemLimit,
175     case {Alarmed, NewAlarmed} of
176         {false, true} ->
            % 如果之前還沒發出過alarm
            % 就會發出alarm, 給alarm_handler
177             emit_update_info(set, MemUsed, MemLimit),
178             alarm_handler:set_alarm({{vm_memory_high_watermark, node()}, []});
179         {true, false} ->
            % 反之則清理掉alarm
180             emit_update_info(clear, MemUsed, MemLimit),
181             alarm_handler:clear_alarm({vm_memory_high_watermark, node()});
182         _ ->
183             ok
184     end,
185     State #state {alarmed = NewAlarmed}.
186  
    % 印出log
187 emit_update_info(State, MemUsed, MemLimit) ->
188     error_logger:info_msg(
189       "vm_memory_high_watermark ~p. Memory used:~p allowed:~p~n",
190       [State, MemUsed, MemLimit]).
191  
    % 定時update memory usage state
192 start_timer(Timeout) ->
193     {ok, TRef} = timer:apply_interval(Timeout, ?MODULE, update, []),
194     TRef.

現在回到rabbitm_alarm.erl 看他怎麼處理當收到high memory watermark的alarm.

rabbit_alarm.erl

 89 handle_event({set_alarm, {{vm_memory_high_watermark, Node}, []}}, State) ->
 90     {ok, maybe_alert(fun sets:add_element/2, Node, State)};
 91  
 92 handle_event({clear_alarm, {vm_memory_high_watermark, Node}}, State) ->
 93     {ok, maybe_alert(fun sets:del_element/2, Node, State)};


126 maybe_alert(SetFun, Node, State = #alarms{alarmed_nodes = AN,
127                      alertees      = Alertees}) ->
128     AN1 = SetFun(Node, AN),
129     BeforeSz = sets:size(AN),
130     AfterSz  = sets:size(AN1),
131     %% If we have changed our alarm state, inform the remotes.
132     IsLocal = Node =:= node(),
        % 如果是local alarm
        % 就會通知其他的remote node
133     if IsLocal andalso BeforeSz < AfterSz -> ok = alert_remote(true,  Alertees);
134        IsLocal andalso BeforeSz > AfterSz -> ok = alert_remote(false, Alertees);
135        true                               -> ok
136     end,
137     %% If the overall alarm state has changed, inform the locals.
138     case {BeforeSz, AfterSz} of
139         {0, 1} -> ok = alert_local(true,  Alertees);
140         {1, 0} -> ok = alert_local(false, Alertees);
141         {_, _} -> ok
142     end,
        % 更新alarmed_node
143     State#alarms{alarmed_nodes = AN1}.

145 alert_local(Alert, Alertees)  -> alert(Alert, Alertees, fun erlang:'=:='/2).
146      
147 alert_remote(Alert, Alertees) -> alert(Alert, Alertees, fun erlang:'=/='/2).
148      
    % 找出所有需要通知的node, 並且執行Node之前register的callback
149 alert(Alert, Alertees, NodeComparator) ->
150     Node = node(),                                                                                                                                                     
151     dict:fold(fun (Pid, {M, F, A}, ok) ->
152                       case NodeComparator(Node, node(Pid)) of
153                           true  -> apply(M, F, A ++ [Pid, Alert]);
154                           false -> ok
155                       end
156               end, ok, Alertees).

所以之後其他的module, 只需要register callback, 當memory用量過高, 要怎麼處理就行, 這部份的code 是放在rabbit_reader.erl, 也就是當connection coming的時候.

rabbit_reader.erl

691     State1 = internal_conserve_memory(
           % register callback
           % 當記憶體使用過量
           % 便會呼叫
692        rabbit_alarm:register(self(), {?MODULE, conserve_memory, []}),
693        State#v1{connection_state = running,
694                connection = NewConnection}),

    % 當使用過量, state改為blocking
    % 不在接收新的connection
348 internal_conserve_memory(true,  State = #v1{connection_state = running}) ->
349     State#v1{connection_state = blocking};
350 internal_conserve_memory(false, State = #v1{connection_state = blocking}) ->                                                                                           
351     State#v1{connection_state = running};
352 internal_conserve_memory(false, State = #v1{connection_state = blocked,
353                                             heartbeater      = Heartbeater}) ->
354     ok = rabbit_heartbeat:resume_monitor(Heartbeater),
355     State#v1{connection_state = running};
356 internal_conserve_memory(_Conserve, State) ->
357     State

另外當新的cluster node 啟動的時候, 假設現在Node A收到Node B up的event, Node A會跟Node B註冊一個remote_conserve_memory callback, 當Node B 記憶體過大, 他就會呼叫Node A 開始節省memory, 直到原本的alarm被clear.

rabbit_alarm.erl

 95 handle_event({node_up, Node}, State) ->         
 96     %% Must do this via notify and not call to avoid possible deadlock.
 97     ok = gen_event:notify(                      
 98            {alarm_handler, Node},               
 99            {register, self(), {?MODULE, remote_conserve_memory, []}}),
100     {ok, State};

 69 remote_conserve_memory(Pid, true) ->
        % 叫自己開始節省記憶體
 70     gen_event:notify({alarm_handler, node(Pid)},
 71                      {set_alarm, {{vm_memory_high_watermark, node()}, []}});