Class: RGossip2::Client

Inherits:
Object
  • Object
show all
Includes:
Enumerable, ContextHelper
Defined in:
lib/rgossip2/client.rb

Overview

class Client ゴシッププロトコルのクライアント兼サーバ

---------- -------- | Client |<>—---| Node | ---------- | --------

|    +-----------------------+
+---+|  @node_list:NodeList  |
|    +-----------------------+
|    +-----------------------+
+---+|  @dead_list:NodeList  |
     +-----------------------+

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(context, initial_nodes = [], address = nil, data = nil) ⇒ Client

Returns a new instance of Client.



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
# File 'lib/rgossip2/client.rb', line 31

def initialize(context, initial_nodes = [], address = nil, data = nil)
  @context = context

  # データがバッファサイズを超える場合はエラー
  if data and data.length > @context.buffer_size
    raise 'Data is too large'
  end

  # IPアドレスを取得。デフォルトはローカルホストアドレス
  @address = name2addr(address || IPSocket.getaddress(Socket.gethostname))
  info("Client is initialized: initial_nodes=#{initial_nodes.inspect}, address=#{@address}, data=#{data.inspect}")

  # NodeListを生成
  @node_list = create(NodeList)
  @dead_list = create(NodeList)

  # Nodeを生成
  @self_node = create(Node, @node_list, @dead_list, @address, data, nil)
  @self_node.update_timestamp
  @node_list[@address] = @self_node

  # 初期ノードを追加
  initial_nodes_threads = []

  initial_nodes.uniq.each do |i|
    initial_nodes_threads << Thread.start(i) do |addr|
      addr = name2addr(i)
      # 自ノードはスキップ
      next if addr == @address
      # つながらない場合はスキップ
      next unless connectable?(addr, @context.port)
      @node_list[addr] = create(Node, @node_list, @dead_list, addr, nil, nil)
    end
  end

  initial_nodes_threads.each {|i| i.join }

  # Gossiper、Receiverを生成
  @gossiper = create(Gossiper, @self_node, @node_list)
  @receiver = create(Receiver, @self_node, @node_list, @dead_list)
end

Instance Attribute Details

#contextObject (readonly)

Returns the value of attribute context.



29
30
31
# File 'lib/rgossip2/client.rb', line 29

def context
  @context
end

#dead_listObject (readonly)

Returns the value of attribute dead_list.



26
27
28
# File 'lib/rgossip2/client.rb', line 26

def dead_list
  @dead_list
end

#node_listObject (readonly)

Returns the value of attribute node_list.



25
26
27
# File 'lib/rgossip2/client.rb', line 25

def node_list
  @node_list
end

#self_nodeObject (readonly)

Returns the value of attribute self_node.



27
28
29
# File 'lib/rgossip2/client.rb', line 27

def self_node
  @self_node
end

Instance Method Details

#add_node(address) ⇒ Object

ノードの追加



132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
# File 'lib/rgossip2/client.rb', line 132

def add_node(address)
  address = name2addr(address)

  @node_list.synchronize {
    @dead_list.synchronize {
      # すでに存在する場合はエラー
      raise 'The node already exists' if @node_list[address]

      node = create(Node, @node_list, @dead_list, address, nil, nil)
      @node_list[address] = node

      # デッドリストからは追加したノードを削除
      @dead_list.delete(address)

      node.start_timer if @running

      callback(:add, address, nil, nil, nil)
    }
  }
end

#addressObject



113
114
115
# File 'lib/rgossip2/client.rb', line 113

def address
  @self_node.address
end

#clear_dead_listObject

デッドリストのクリーニング



176
177
178
179
180
181
182
183
184
185
# File 'lib/rgossip2/client.rb', line 176

def clear_dead_list
  dead_list_len = 0

  @dead_list.synchronize {
    dead_list_len = @dead_list.length
    @dead_list.clear
  }

  return dead_list_len
end

#dataObject



117
118
119
# File 'lib/rgossip2/client.rb', line 117

def data
  @self_node.data
end

#data=(v) ⇒ Object



121
122
123
# File 'lib/rgossip2/client.rb', line 121

def data=(v)
  @self_node.data = v
end

#delete_node(address) ⇒ Object

ノードの削除



154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
# File 'lib/rgossip2/client.rb', line 154

def delete_node(address)
  address = name2addr(address)

  # 自分自身は削除できない
  raise 'Own node cannot be deleted' if @self_node.address == address

  @node_list.synchronize {
    @dead_list.synchronize {
      # ノードリストから削除してTimerを止める
      node = @node_list.delete(address)
      node.stop_timer if node

      # デッドリストからも削除
      node = @dead_list.delete(address)
      node.stop_timer if node

      callback(:delete, address, nil, nil, nil)
    }
  }
end

#eachObject

ノードを舐める



188
189
190
191
192
193
194
195
196
197
198
199
# File 'lib/rgossip2/client.rb', line 188

def each
  @node_list.each do |node|
    address = node.address.dup
    timestamp = node.timestamp.dup

    if data = node.data
      data = data.dup
    end

    yield([address, timestamp, data])
  end
end

#joinObject



104
105
106
107
# File 'lib/rgossip2/client.rb', line 104

def join
  @gossiper.join
  @receiver.join
end

#loggerObject



201
202
203
# File 'lib/rgossip2/client.rb', line 201

def logger
  @context.logger
end

#running?Boolean

Returns:

  • (Boolean)


109
110
111
# File 'lib/rgossip2/client.rb', line 109

def running?
  !!@running
end

#startObject



73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
# File 'lib/rgossip2/client.rb', line 73

def start
  # 開始している場合はスキップ
  return if @running

  info("Client is started: address=#{@address}")

  # NodoのTimerをスタート
  @node_list.each do |node|
    if node.address != @self_node.address
      node.start_timer
    end
  end

  @gossiper.start
  @receiver.start
ensure
  @running = true
end

#stopObject



92
93
94
95
96
97
98
99
100
101
102
# File 'lib/rgossip2/client.rb', line 92

def stop
  # 停止している場合はスキップ
  return unless @running

  info("Client is stopped")

  @gossiper.stop
  @receiver.stop
ensure
  @running = false
end

#transactionObject



125
126
127
128
129
# File 'lib/rgossip2/client.rb', line 125

def transaction
  @node_list.synchronize {
    yield
  }
end