plux 0.1.2 → 0.1.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: a48ed6a3720e61c2bce1d3b181f93b2b8c290019c7feb076ca2398a4c0b6f203
4
- data.tar.gz: 67d7f993aeba391f3986e284c38846e5bc4afb3305b475e390969c3f3dca5c09
3
+ metadata.gz: fe3a01341377759b20ec857779769a121787a2caa79d1f06c33871e779a4a2db
4
+ data.tar.gz: bdd9a7988dd40776d31b62989b59191b3474436f6a0a847869eb1b568de59303
5
5
  SHA512:
6
- metadata.gz: 10539dd311f59a391218943e159d389b99033239136f6aeb04137f39e3d563c5fb5af287d907a708bb1f4cbbfec942e3a1e4d053c64e92147f91ca121b3c1559
7
- data.tar.gz: eab024ca828276eebc07118249bd7d9493dde24988f14bd7df71f367d865a5a7580fdc7dc5fdd97e6ee119b56d9cb43f98f9bd81ce0dd58cd74932719c46bed9
6
+ metadata.gz: 1dda3d9c1d45d6acd3af4f8cdba9e3ba954b5d3c8ac0c35cada18ffcf89c08bfe7aabdf31ecb46de30c304a57a78c8762d1b66694244f0b7252974c12337fac5
7
+ data.tar.gz: 142cff4debc8825f8911d2c542d2f03e13a04d49d9a81a8cf414f313073d5be9eb67b608d58c8d03be0ec0a069d4db2c95b1c957769860bb7a841edb6abe05a6
@@ -1,7 +1,8 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- plux (0.1.0)
4
+ plux (0.1.6)
5
+ nio4r (~> 2.0)
5
6
 
6
7
  GEM
7
8
  remote: https://rubygems.org/
@@ -9,6 +10,7 @@ GEM
9
10
  coderay (1.1.2)
10
11
  method_source (0.9.2)
11
12
  minitest (5.14.1)
13
+ nio4r (2.5.4)
12
14
  pry (0.12.2)
13
15
  coderay (~> 1.1.0)
14
16
  method_source (~> 0.9.0)
data/README.md CHANGED
@@ -21,32 +21,57 @@ Or install it yourself as:
21
21
  ## Usage
22
22
 
23
23
  ```ruby
24
- # start one and only process named 'abc',
24
+ # start one and only process named 'abc', with 2 threads in it (1 thread if not specified),
25
25
  # no matter the code below is called how many times in whatever processes/threads
26
- server = Plux.worker(:abc) do
26
+ server = Plux.worker(:abc, thread: 2) do
27
27
 
28
- # prepare resources like mq/db, to handle requests
29
- def initialize
28
+ # prepare thread-safe resources like mq/db, to handle requests
29
+ def prepare
30
30
  # @db = ...
31
31
  end
32
32
 
33
33
  # threads call this method to deal with clients' message
34
- def work(msg)
34
+ def process(msg)
35
35
  # @db << parse(msg)
36
36
  end
37
37
  end
38
38
 
39
- # five threads will be started to handle these clients,
40
- # and finished once their counterparts call close
39
+ # clients connect, send msg, and close concurrently
41
40
  5.times do |n|
42
41
  Thread.new do
43
42
  client = server.connect
44
- client.puts "hello #{n}"
43
+ client.puts "hello"
44
+ client.puts "my name is #{n}"
45
45
  client.close
46
46
  end
47
47
  end
48
48
  ```
49
49
 
50
+ Also, you can boot multiple servers(processes) with same logic:
51
+
52
+ ```ruby
53
+ class AwesomeServer < Plux::Engine
54
+ # prepare thread-safe resources like mq/db, to handle requests
55
+ def prepare
56
+ # @db = ...
57
+ end
58
+
59
+ # threads call this method to deal with clients' message
60
+ def process(msg)
61
+ # @db << parse(msg)
62
+ end
63
+ end
64
+
65
+ a = AwesomeServer.new(:a)
66
+ b = AwesomeServer.new(:b)
67
+ c = AwesomeServer.new(:c, thread: 4)
68
+
69
+ a_client = a.connect
70
+ b_client = b.connect
71
+ c_client = c.connect
72
+ c_client_2 = c.connect
73
+ ```
74
+
50
75
  ## Development
51
76
 
52
77
  After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake test` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
@@ -3,6 +3,9 @@ require "socket"
3
3
  require "plux/version"
4
4
  require "plux/server"
5
5
  require "plux/client"
6
+ require "plux/parser"
7
+ require "plux/engine"
8
+
6
9
 
7
10
  module Plux
8
11
 
@@ -30,10 +33,12 @@ module Plux
30
33
  File.join(dir, "#{server_name}.so")
31
34
  end
32
35
 
33
- def worker(name, &block)
34
- Server.new(name).boot(block)
36
+ def worker(name, thread: 1, &block)
37
+ worker = Class.new(&block).new
38
+ Server.new(name, thread: thread).boot(worker)
35
39
  end
36
40
  end
37
41
 
38
42
  FileUtils.mkdir_p(self.dir)
43
+
39
44
  end
@@ -4,18 +4,17 @@ module Plux
4
4
 
5
5
  def initialize(server_name)
6
6
  @server_name = server_name
7
- UNIXSocket.open(Plux.server_file(server_name)) do |c|
8
- reader, @writer = IO.pipe
9
- c.send_io(reader)
10
- reader.close
11
- end
7
+ @writer = UNIXSocket.open(Plux.server_file(server_name))
12
8
  end
13
9
 
14
- def puts(arg)
15
- @writer.puts(arg)
10
+ def puts(msg)
11
+ Parser.encode(msg).each do |sub_msg|
12
+ @writer.write(sub_msg)
13
+ end
16
14
  end
17
15
 
18
16
  def close
17
+ puts(Parser::LAST_MSG)
19
18
  @writer.close
20
19
  end
21
20
  end
@@ -0,0 +1,18 @@
1
+ module Plux
2
+ class Engine
3
+
4
+ def initialize(name, thread: 1)
5
+ @server = Server.new(name, thread: thread).boot(self)
6
+ end
7
+
8
+ def connect
9
+ @server.connect
10
+ end
11
+
12
+ def prepare
13
+ end
14
+
15
+ def process(msg)
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,67 @@
1
+ module Plux
2
+ class Parser
3
+
4
+ SYSTEM = 36
5
+ SEPERATOR = '.'.freeze
6
+ STREAM_MAX_LEN = 4096
7
+ LAST_MSG = ''.freeze
8
+
9
+ def initialize
10
+ @broken = ''
11
+ end
12
+
13
+ def decode(stream)
14
+ stream = @broken.concat(stream)
15
+ result = []
16
+
17
+ start = 0
18
+ pending = stream.length
19
+
20
+ until start >= pending
21
+ sep_idx = stream.index(SEPERATOR, start)
22
+
23
+ unless sep_idx
24
+ len_str = stream[start, pending]
25
+ @broken.clear
26
+ @broken.concat(len_str)
27
+ return result
28
+ end
29
+
30
+ len_str = stream[start, sep_idx - start]
31
+ est_len = len_str.to_i(SYSTEM)
32
+ start = sep_idx + 1
33
+ msg = stream[start, est_len]
34
+ act_len = msg.length
35
+
36
+ if act_len < est_len
37
+ @broken.clear
38
+ @broken.concat(len_str)
39
+ @broken.concat(SEPERATOR)
40
+ @broken.concat(msg)
41
+ return result
42
+ end
43
+
44
+ result << msg
45
+ start += act_len
46
+ end
47
+
48
+ @broken.clear
49
+ result
50
+ end
51
+
52
+ def self.encode(msg)
53
+ len = msg.length.to_s(SYSTEM)
54
+
55
+ msg = "#{len}#{SEPERATOR}#{msg}"
56
+ len = msg.length
57
+
58
+ return [msg] if len < STREAM_MAX_LEN
59
+
60
+ div, mod = len.divmod(STREAM_MAX_LEN)
61
+ (mod > 0 ? (div + 1) : div).times.map do |start|
62
+ msg[start * STREAM_MAX_LEN, STREAM_MAX_LEN]
63
+ end
64
+ end
65
+
66
+ end
67
+ end
@@ -0,0 +1,76 @@
1
+ module Plux
2
+ class Reactor
3
+ def initialize(count, worker)
4
+ @worker = worker
5
+ @msg_q = Queue.new
6
+ @count = count
7
+
8
+ @nio = NIO::Selector.new
9
+ @newly_accepted = Queue.new
10
+ @closed = []
11
+
12
+ receive
13
+ process
14
+ end
15
+
16
+ def register(socket)
17
+ @newly_accepted << socket
18
+ @nio.wakeup
19
+ end
20
+
21
+ private
22
+
23
+ def receive
24
+ Thread.new do
25
+ loop do
26
+ @closed.size.times{ @nio.deregister(@closed.pop) }
27
+
28
+ @newly_accepted.size.times do
29
+ socket = @newly_accepted.pop
30
+ mon = @nio.register(socket, :r)
31
+ mon.value = Worker.new(socket, @msg_q)
32
+ end
33
+
34
+ @nio.select do |m|
35
+ next if m.value.process
36
+ @closed << m.io
37
+ end
38
+ end
39
+ end
40
+ end
41
+
42
+ def process
43
+ @count.times.each do
44
+ Thread.new do
45
+ loop{ @worker.process(@msg_q.deq) }
46
+ end
47
+ end
48
+ end
49
+
50
+ class Worker
51
+ def initialize(socket, q)
52
+ @parser = Parser.new
53
+ @socket = socket
54
+ @q = q
55
+ end
56
+
57
+ def process
58
+ stream = @socket.read_nonblock(Parser::STREAM_MAX_LEN, exception: false)
59
+ return true if stream == :wait_readable
60
+
61
+ msgs = @parser.decode(stream)
62
+ last_msg = msgs.pop
63
+
64
+ msgs.each{ |msg| @q << msg }
65
+ if last_msg == Parser::LAST_MSG
66
+ @socket.close
67
+ return false
68
+ end
69
+ @q << last_msg
70
+
71
+ true
72
+ end
73
+ end
74
+
75
+ end
76
+ end
@@ -1,3 +1,6 @@
1
+ require "nio"
2
+ require "plux/reactor"
3
+
1
4
  module Plux
2
5
 
3
6
  class Server
@@ -6,13 +9,14 @@ module Plux
6
9
  Active = {}
7
10
  at_exit{ Active.values.each(&:close) }
8
11
 
9
- def initialize(name)
12
+ def initialize(name, thread: )
10
13
  @name = name
14
+ @thread = thread
11
15
  end
12
16
 
13
- def boot(block)
17
+ def boot(worker)
14
18
  Plux.lock_pid_file(name) do |file|
15
- start_server_if_not_pid(file, block)
19
+ start_server_if_not_pid(file, worker)
16
20
  end
17
21
  self
18
22
  end
@@ -27,7 +31,7 @@ module Plux
27
31
 
28
32
  private
29
33
 
30
- def start_server_if_not_pid(file, block)
34
+ def start_server_if_not_pid(file, worker)
31
35
  @pid = file.read.to_i
32
36
  return unless pid == 0
33
37
 
@@ -39,11 +43,9 @@ module Plux
39
43
  child.close
40
44
  UNIXServer.open(Plux.server_file(name)) do |serv|
41
45
  parent.close
42
- worker = Class.new(&block).new
43
- loop do
44
- socket = serv.accept
45
- Worker.new(socket, worker)
46
- end
46
+ worker.prepare
47
+ reactor = Reactor.new(@thread, worker)
48
+ loop{ reactor.register(serv.accept) }
47
49
  end
48
50
  end
49
51
 
@@ -62,20 +64,6 @@ module Plux
62
64
  File.delete(Plux.send(file, name))
63
65
  end
64
66
  end
65
-
66
- class Worker
67
- def initialize(socket, worker)
68
- t = Thread.new do
69
- client = socket.recv_io
70
- socket.close
71
- while line = client.gets
72
- worker.work(line)
73
- end
74
- client.close
75
- end
76
- end
77
- end
78
-
79
67
  end
80
68
 
81
69
  end
@@ -1,3 +1,3 @@
1
1
  module Plux
2
- VERSION = "0.1.2"
2
+ VERSION = "0.1.7"
3
3
  end
@@ -23,4 +23,6 @@ Gem::Specification.new do |spec|
23
23
  spec.add_development_dependency "rake", "~> 10.0"
24
24
  spec.add_development_dependency "minitest", "~> 5.0"
25
25
  spec.add_development_dependency "pry"
26
+
27
+ spec.add_dependency "nio4r", "~> 2.0"
26
28
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: plux
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.2
4
+ version: 0.1.7
5
5
  platform: ruby
6
6
  authors:
7
7
  - ken
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2020-09-09 00:00:00.000000000 Z
11
+ date: 2020-09-23 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -66,6 +66,20 @@ dependencies:
66
66
  - - ">="
67
67
  - !ruby/object:Gem::Version
68
68
  version: '0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: nio4r
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - "~>"
74
+ - !ruby/object:Gem::Version
75
+ version: '2.0'
76
+ type: :runtime
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - "~>"
81
+ - !ruby/object:Gem::Version
82
+ version: '2.0'
69
83
  description:
70
84
  email:
71
85
  - block24block@gmail.com
@@ -85,6 +99,9 @@ files:
85
99
  - bin/setup
86
100
  - lib/plux.rb
87
101
  - lib/plux/client.rb
102
+ - lib/plux/engine.rb
103
+ - lib/plux/parser.rb
104
+ - lib/plux/reactor.rb
88
105
  - lib/plux/server.rb
89
106
  - lib/plux/version.rb
90
107
  - plux.gemspec