griffin 0.1.0 → 0.1.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +5 -5
- data/.gitignore +1 -0
- data/.rubocop.yml +86 -0
- data/.ruby-version +1 -0
- data/Gemfile +4 -2
- data/LICENSE.txt +1 -1
- data/README.md +42 -9
- data/Rakefile +5 -3
- data/bin/console +4 -3
- data/examples/helloworld/helloworld.proto +37 -0
- data/examples/helloworld/helloworld_pb.rb +18 -0
- data/examples/helloworld/helloworld_services_pb.rb +22 -0
- data/examples/helloworld_client.rb +12 -0
- data/examples/helloworld_server.rb +22 -0
- data/examples/interceptors/client_logging_interceptor.rb +48 -0
- data/examples/interceptors/server_logging_interceptor.rb +44 -0
- data/examples/routeguide/routeguide.json +601 -0
- data/examples/routeguide/routeguide.proto +110 -0
- data/examples/routeguide/routeguide_pb.rb +37 -0
- data/examples/routeguide/routeguide_services_pb.rb +61 -0
- data/examples/routeguide_client.rb +83 -0
- data/examples/routeguide_server.rb +118 -0
- data/griffin.gemspec +18 -12
- data/lib/griffin/connection_pool/multi_timed_stack.rb +78 -0
- data/lib/griffin/connection_pool/pool.rb +56 -0
- data/lib/griffin/counting_semaphore.rb +19 -0
- data/lib/griffin/engine/server.rb +29 -0
- data/lib/griffin/engine/single.rb +41 -0
- data/lib/griffin/engine/worker.rb +27 -0
- data/lib/griffin/engine.rb +20 -0
- data/lib/griffin/listener.rb +36 -0
- data/lib/griffin/server.rb +118 -0
- data/lib/griffin/server_config_builder.rb +55 -0
- data/lib/griffin/thread_pool.rb +88 -0
- data/lib/griffin/version.rb +3 -1
- data/lib/griffin.rb +10 -2
- metadata +87 -6
- data/CODE_OF_CONDUCT.md +0 -74
@@ -0,0 +1,37 @@
|
|
1
|
+
# Generated by the protocol buffer compiler. DO NOT EDIT!
|
2
|
+
# source: routeguide.proto
|
3
|
+
|
4
|
+
require 'google/protobuf'
|
5
|
+
|
6
|
+
Google::Protobuf::DescriptorPool.generated_pool.build do
|
7
|
+
add_message "routeguide.Point" do
|
8
|
+
optional :latitude, :int32, 1
|
9
|
+
optional :longitude, :int32, 2
|
10
|
+
end
|
11
|
+
add_message "routeguide.Rectangle" do
|
12
|
+
optional :lo, :message, 1, "routeguide.Point"
|
13
|
+
optional :hi, :message, 2, "routeguide.Point"
|
14
|
+
end
|
15
|
+
add_message "routeguide.Feature" do
|
16
|
+
optional :name, :string, 1
|
17
|
+
optional :location, :message, 2, "routeguide.Point"
|
18
|
+
end
|
19
|
+
add_message "routeguide.RouteNote" do
|
20
|
+
optional :location, :message, 1, "routeguide.Point"
|
21
|
+
optional :message, :string, 2
|
22
|
+
end
|
23
|
+
add_message "routeguide.RouteSummary" do
|
24
|
+
optional :point_count, :int32, 1
|
25
|
+
optional :feature_count, :int32, 2
|
26
|
+
optional :distance, :int32, 3
|
27
|
+
optional :elapsed_time, :int32, 4
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
module Routeguide
|
32
|
+
Point = Google::Protobuf::DescriptorPool.generated_pool.lookup("routeguide.Point").msgclass
|
33
|
+
Rectangle = Google::Protobuf::DescriptorPool.generated_pool.lookup("routeguide.Rectangle").msgclass
|
34
|
+
Feature = Google::Protobuf::DescriptorPool.generated_pool.lookup("routeguide.Feature").msgclass
|
35
|
+
RouteNote = Google::Protobuf::DescriptorPool.generated_pool.lookup("routeguide.RouteNote").msgclass
|
36
|
+
RouteSummary = Google::Protobuf::DescriptorPool.generated_pool.lookup("routeguide.RouteSummary").msgclass
|
37
|
+
end
|
@@ -0,0 +1,61 @@
|
|
1
|
+
# Generated by the protocol buffer compiler. DO NOT EDIT!
|
2
|
+
# Source: routeguide.proto for package 'routeguide'
|
3
|
+
# Original file comments:
|
4
|
+
# Copyright 2015 gRPC authors.
|
5
|
+
#
|
6
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
7
|
+
# you may not use this file except in compliance with the License.
|
8
|
+
# You may obtain a copy of the License at
|
9
|
+
#
|
10
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
11
|
+
#
|
12
|
+
# Unless required by applicable law or agreed to in writing, software
|
13
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
14
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
15
|
+
# See the License for the specific language governing permissions and
|
16
|
+
# limitations under the License.
|
17
|
+
#
|
18
|
+
|
19
|
+
require 'grpc'
|
20
|
+
require 'routeguide_pb'
|
21
|
+
|
22
|
+
module Routeguide
|
23
|
+
module RouteGuide
|
24
|
+
# Interface exported by the server.
|
25
|
+
class Service
|
26
|
+
|
27
|
+
include GRPC::GenericService
|
28
|
+
|
29
|
+
self.marshal_class_method = :encode
|
30
|
+
self.unmarshal_class_method = :decode
|
31
|
+
self.service_name = 'routeguide.RouteGuide'
|
32
|
+
|
33
|
+
# A simple RPC.
|
34
|
+
#
|
35
|
+
# Obtains the feature at a given position.
|
36
|
+
#
|
37
|
+
# A feature with an empty name is returned if there's no feature at the given
|
38
|
+
# position.
|
39
|
+
rpc :GetFeature, Point, Feature
|
40
|
+
# A server-to-client streaming RPC.
|
41
|
+
#
|
42
|
+
# Obtains the Features available within the given Rectangle. Results are
|
43
|
+
# streamed rather than returned at once (e.g. in a response message with a
|
44
|
+
# repeated field), as the rectangle may cover a large area and contain a
|
45
|
+
# huge number of features.
|
46
|
+
rpc :ListFeatures, Rectangle, stream(Feature)
|
47
|
+
# A client-to-server streaming RPC.
|
48
|
+
#
|
49
|
+
# Accepts a stream of Points on a route being traversed, returning a
|
50
|
+
# RouteSummary when traversal is completed.
|
51
|
+
rpc :RecordRoute, stream(Point), RouteSummary
|
52
|
+
# A Bidirectional streaming RPC.
|
53
|
+
#
|
54
|
+
# Accepts a stream of RouteNotes sent while a route is being traversed,
|
55
|
+
# while receiving other RouteNotes (e.g. from other users).
|
56
|
+
rpc :RouteChat, stream(RouteNote), stream(RouteNote)
|
57
|
+
end
|
58
|
+
|
59
|
+
Stub = Service.rpc_stub_class
|
60
|
+
end
|
61
|
+
end
|
@@ -0,0 +1,83 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# rubocop:disable Style/GlobalVars
|
4
|
+
|
5
|
+
$LOAD_PATH.unshift File.expand_path('./examples/routeguide')
|
6
|
+
|
7
|
+
require 'grpc_kit'
|
8
|
+
require 'pry'
|
9
|
+
require 'json'
|
10
|
+
require 'logger'
|
11
|
+
require 'routeguide_services_pb'
|
12
|
+
|
13
|
+
RESOURCE_PATH = './examples/routeguide/routeguide.json'
|
14
|
+
|
15
|
+
$logger = Logger.new(STDOUT)
|
16
|
+
|
17
|
+
def get_feature(stub)
|
18
|
+
$logger.info('===== get_feature =====')
|
19
|
+
points = [
|
20
|
+
Routeguide::Point.new(latitude: 409_146_138, longitude: -746_188_906),
|
21
|
+
Routeguide::Point.new(latitude: 0, longitude: 0)
|
22
|
+
]
|
23
|
+
|
24
|
+
points.each do |pt|
|
25
|
+
feature = stub.get_feature(pt, metadata: { 'metadata' => 'data1' })
|
26
|
+
if feature.name == ''
|
27
|
+
$logger.info("Found nothing at #{feature.inspect}")
|
28
|
+
else
|
29
|
+
$logger.info("Found '#{feature.name}' at #{feature.location.inspect}")
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
def list_features(stub)
|
35
|
+
$logger.info('===== list_features =====')
|
36
|
+
rect = Routeguide::Rectangle.new(
|
37
|
+
lo: Routeguide::Point.new(latitude: 400_000_000, longitude: -750_000_000),
|
38
|
+
hi: Routeguide::Point.new(latitude: 420_000_000, longitude: -730_000_000),
|
39
|
+
)
|
40
|
+
|
41
|
+
stream = stub.list_features(rect)
|
42
|
+
|
43
|
+
loop do
|
44
|
+
r = stream.recv
|
45
|
+
$logger.info("Found #{r.name} at #{r.location.inspect}")
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
def record_route(stub, size)
|
50
|
+
features = File.open(RESOURCE_PATH) do |f|
|
51
|
+
JSON.parse(f.read)
|
52
|
+
end
|
53
|
+
|
54
|
+
stream = stub.record_route({})
|
55
|
+
|
56
|
+
size.times do
|
57
|
+
location = features.sample['location']
|
58
|
+
point = Routeguide::Point.new(latitude: location['latitude'], longitude: location['longitude'])
|
59
|
+
puts "Next point is #{point.inspect}"
|
60
|
+
stream.send_msg(point)
|
61
|
+
sleep(rand(0..1))
|
62
|
+
end
|
63
|
+
|
64
|
+
resp = stream.close_and_recv
|
65
|
+
puts "summary: #{resp[0].inspect}"
|
66
|
+
end
|
67
|
+
|
68
|
+
opts = {}
|
69
|
+
|
70
|
+
if ENV['GRPC_INTERCEPTOR']
|
71
|
+
require_relative 'interceptors/client_logging_interceptor'
|
72
|
+
opts[:interceptors] = [LoggingInterceptor.new]
|
73
|
+
elsif ENV['GRPC_TIMEOUT']
|
74
|
+
opts[:timeout] = Integer(ENV['GRPC_TIMEOUT'])
|
75
|
+
end
|
76
|
+
|
77
|
+
stub = Routeguide::RouteGuide::Stub.new('localhost', 50051, **opts)
|
78
|
+
|
79
|
+
get_feature(stub)
|
80
|
+
list_features(stub)
|
81
|
+
record_route(stub, 10)
|
82
|
+
|
83
|
+
# rubocop:enable Style/GlobalVars
|
@@ -0,0 +1,118 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
$LOAD_PATH.unshift File.expand_path('./examples/routeguide')
|
4
|
+
|
5
|
+
require 'grpc_kit'
|
6
|
+
require 'pry'
|
7
|
+
require 'json'
|
8
|
+
require 'routeguide_services_pb'
|
9
|
+
require 'logger'
|
10
|
+
|
11
|
+
class Server < Routeguide::RouteGuide::Service
|
12
|
+
RESOURCE_PATH = './examples/routeguide/routeguide.json'
|
13
|
+
|
14
|
+
def initialize
|
15
|
+
@logger = Logger.new(STDOUT)
|
16
|
+
File.open(RESOURCE_PATH) do |f|
|
17
|
+
features = JSON.parse(f.read)
|
18
|
+
@features = Hash[features.map { |x| [x['location'], x['name']] }]
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
def get_feature(point, ctx)
|
23
|
+
name = @features.fetch({ 'longitude' => point.longitude, 'latitude' => point.latitude }, '')
|
24
|
+
@logger.info("Point longitude=#{point.longitude}, latitude=#{point.latitude}, metadata=#{ctx.metadata}")
|
25
|
+
Routeguide::Feature.new(location: point, name: name)
|
26
|
+
end
|
27
|
+
|
28
|
+
def list_features(rect, stream)
|
29
|
+
@logger.info('===== list_features =====')
|
30
|
+
|
31
|
+
@features.each do |location, name|
|
32
|
+
if name.nil? || name == '' || !in_range(location, rect)
|
33
|
+
next
|
34
|
+
end
|
35
|
+
|
36
|
+
pt = Routeguide::Point.new(location)
|
37
|
+
resp = Routeguide::Feature.new(location: pt, name: name)
|
38
|
+
@logger.info(resp)
|
39
|
+
stream.send_msg(resp)
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
def record_route(stream)
|
44
|
+
@logger.info('===== record_route =====')
|
45
|
+
distance = 0
|
46
|
+
count = 0
|
47
|
+
features = 0
|
48
|
+
start_at = Time.now.to_i
|
49
|
+
last = nil
|
50
|
+
|
51
|
+
loop do
|
52
|
+
point = stream.recv # XXX: raise StopIteration
|
53
|
+
@logger.info(point)
|
54
|
+
|
55
|
+
count += 1
|
56
|
+
name = @features.fetch({ 'longitude' => point.longitude, 'latitude' => point.latitude }, '')
|
57
|
+
unless name == ''
|
58
|
+
features += 1
|
59
|
+
end
|
60
|
+
|
61
|
+
last = point
|
62
|
+
distance += calculate_distance(point, last)
|
63
|
+
end
|
64
|
+
|
65
|
+
Routeguide::RouteSummary.new(
|
66
|
+
point_count: count,
|
67
|
+
feature_count: features,
|
68
|
+
distance: distance,
|
69
|
+
elapsed_time: Time.now.to_i - start_at,
|
70
|
+
)
|
71
|
+
end
|
72
|
+
|
73
|
+
private
|
74
|
+
|
75
|
+
COORD_FACTOR = 1e7
|
76
|
+
RADIUS = 637_100
|
77
|
+
|
78
|
+
def calculate_distance(point_a, point_b)
|
79
|
+
lat_a = (point_a.latitude / COORD_FACTOR) * Math::PI / 180
|
80
|
+
lat_b = (point_b.latitude / COORD_FACTOR) * Math::PI / 180
|
81
|
+
lon_a = (point_a.longitude / COORD_FACTOR) * Math::PI / 180
|
82
|
+
lon_b = (point_b.longitude / COORD_FACTOR) * Math::PI / 180
|
83
|
+
|
84
|
+
delta_lat = lat_a - lat_b
|
85
|
+
delta_lon = lon_a - lon_b
|
86
|
+
a = Math.sin(delta_lat / 2)**2 + Math.cos(lat_a) * Math.cos(lat_b) + Math.sin(delta_lon / 2)**2
|
87
|
+
(2 * RADIUS * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a))).to_i
|
88
|
+
end
|
89
|
+
|
90
|
+
def in_range(point, rect)
|
91
|
+
longitudes = [rect.lo.longitude, rect.hi.longitude]
|
92
|
+
left = longitudes.min
|
93
|
+
right = longitudes.max
|
94
|
+
|
95
|
+
latitudes = [rect.lo.latitude, rect.hi.latitude]
|
96
|
+
bottom = latitudes.min
|
97
|
+
top = latitudes.max
|
98
|
+
(point['longitude'] >= left) && (point['longitude'] <= right) && (point['latitude'] >= bottom) && (point['latitude'] <= top)
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
sock = TCPServer.new(50051)
|
103
|
+
|
104
|
+
opts = {}
|
105
|
+
|
106
|
+
if ENV['GRPC_INTERCEPTOR']
|
107
|
+
require_relative 'interceptors/server_logging_interceptor'
|
108
|
+
opts[:interceptors] = [LoggingInterceptor.new]
|
109
|
+
end
|
110
|
+
|
111
|
+
server = GrpcKit::Server.new(**opts)
|
112
|
+
server.handle(Server.new)
|
113
|
+
server.run
|
114
|
+
|
115
|
+
loop do
|
116
|
+
conn = sock.accept
|
117
|
+
server.session_start(conn)
|
118
|
+
end
|
data/griffin.gemspec
CHANGED
@@ -1,27 +1,33 @@
|
|
1
|
+
# frozen_string_literal: true
|
1
2
|
|
2
|
-
lib = File.expand_path(
|
3
|
+
lib = File.expand_path('lib', __dir__)
|
3
4
|
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
-
require
|
5
|
+
require 'griffin/version'
|
5
6
|
|
6
7
|
Gem::Specification.new do |spec|
|
7
|
-
spec.name =
|
8
|
+
spec.name = 'griffin'
|
8
9
|
spec.version = Griffin::VERSION
|
9
10
|
spec.authors = ['ganmacs']
|
10
11
|
spec.email = ['ganmacs@gmail.com']
|
11
12
|
|
12
|
-
spec.summary =
|
13
|
-
spec.description =
|
14
|
-
spec.homepage =
|
15
|
-
spec.license =
|
13
|
+
spec.summary = 'Send and Receive RPCs from Ruby'
|
14
|
+
spec.description = 'Send and Receive RPCs from Ruby'
|
15
|
+
spec.homepage = 'https://github.com/ganmacs/griffin'
|
16
|
+
spec.license = 'MIT'
|
16
17
|
|
17
18
|
spec.files = `git ls-files -z`.split("\x0").reject do |f|
|
18
19
|
f.match(%r{^(test|spec|features)/})
|
19
20
|
end
|
20
|
-
spec.bindir =
|
21
|
+
spec.bindir = 'exe'
|
21
22
|
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
22
|
-
spec.require_paths = [
|
23
|
+
spec.require_paths = ['lib']
|
23
24
|
|
24
|
-
spec.add_development_dependency
|
25
|
-
spec.add_development_dependency
|
26
|
-
spec.add_development_dependency
|
25
|
+
spec.add_development_dependency 'bundler'
|
26
|
+
spec.add_development_dependency 'rake'
|
27
|
+
spec.add_development_dependency 'rspec'
|
28
|
+
spec.add_development_dependency 'rubocop'
|
29
|
+
|
30
|
+
spec.add_dependency 'connection_pool', '~> 2.2.2'
|
31
|
+
spec.add_dependency 'grpc_kit', '~> 0.1.5'
|
32
|
+
spec.add_dependency 'serverengine', '~> 2.0.7'
|
27
33
|
end
|
@@ -0,0 +1,78 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'connection_pool'
|
4
|
+
|
5
|
+
module Griffin
|
6
|
+
module ConnectionPool
|
7
|
+
# This code is based on https://github.com/drbrain/net-http-persistent/blob/0e5a8fb8a27deff37247ddb3bc5e6c9e390face5/lib/net/http/persistent/timed_stack_multi.rb
|
8
|
+
class MultiTimedStack < ::ConnectionPool::TimedStack
|
9
|
+
def initialize(size = 0, &block)
|
10
|
+
super
|
11
|
+
|
12
|
+
@enqueued = 0
|
13
|
+
@ques = Hash.new { |h, k| h[k] = [] }
|
14
|
+
@lru = []
|
15
|
+
end
|
16
|
+
|
17
|
+
def empty?
|
18
|
+
length <= 0
|
19
|
+
end
|
20
|
+
|
21
|
+
def length
|
22
|
+
@max - @created + @enqueued
|
23
|
+
end
|
24
|
+
|
25
|
+
private
|
26
|
+
|
27
|
+
def connection_stored?(options = {})
|
28
|
+
!@ques[options[:connection_args]].empty?
|
29
|
+
end
|
30
|
+
|
31
|
+
def fetch_connection(options = {})
|
32
|
+
connection_args = options[:connection_args]
|
33
|
+
|
34
|
+
@enqueued -= 1
|
35
|
+
lru_update(connection_args)
|
36
|
+
@ques[connection_args].pop
|
37
|
+
end
|
38
|
+
|
39
|
+
def shutdown_connections
|
40
|
+
@ques.each_key do |key|
|
41
|
+
super(connection_args: key)
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
def store_connection(obj, options = {})
|
46
|
+
@ques[options[:connection_args]].push(obj)
|
47
|
+
@enqueued += 1
|
48
|
+
end
|
49
|
+
|
50
|
+
def try_create(options = {})
|
51
|
+
connection_args = options[:connection_args]
|
52
|
+
|
53
|
+
if @created >= @max && @enqueued >= 1
|
54
|
+
oldest = lru_delete
|
55
|
+
@ques[oldest].pop
|
56
|
+
|
57
|
+
@created -= 1
|
58
|
+
end
|
59
|
+
|
60
|
+
if @created < @max
|
61
|
+
obj = @create_block.call(connection_args)
|
62
|
+
@created += 1
|
63
|
+
lru_update(connection_args)
|
64
|
+
obj
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
def lru_update(key)
|
69
|
+
@lru.delete(key)
|
70
|
+
@lru.push(key)
|
71
|
+
end
|
72
|
+
|
73
|
+
def lru_delete
|
74
|
+
@lru.shift
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
@@ -0,0 +1,56 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'connection_pool'
|
4
|
+
|
5
|
+
require 'griffin/connection_pool/multi_timed_stack'
|
6
|
+
|
7
|
+
module Griffin
|
8
|
+
module ConnectionPool
|
9
|
+
class Pool
|
10
|
+
DEFAULTS = { size: 5, timeout: 5 }.freeze
|
11
|
+
|
12
|
+
def initialize(options = {}, &block)
|
13
|
+
raise ArgumentError, 'Connection pool requires a block' unless block
|
14
|
+
|
15
|
+
options = DEFAULTS.merge(options)
|
16
|
+
|
17
|
+
@size = Integer(options.fetch(:size))
|
18
|
+
@timeout = Integer(options.fetch(:timeout))
|
19
|
+
|
20
|
+
@available = Griffin::ConnectionPool::MultiTimedStack.new(@size, &block)
|
21
|
+
@key = :"current-#{@available.object_id}"
|
22
|
+
Thread.current[@key] = Hash.new { |h, k| h[k] = [] }
|
23
|
+
end
|
24
|
+
|
25
|
+
def checkin(key)
|
26
|
+
stack = Thread.current[@key][key]
|
27
|
+
raise 'no connections are checked out' if stack.empty?
|
28
|
+
|
29
|
+
conn = stack.pop
|
30
|
+
if stack.empty?
|
31
|
+
@available.push(conn, connection_args: key)
|
32
|
+
end
|
33
|
+
nil
|
34
|
+
end
|
35
|
+
|
36
|
+
def checkout(key)
|
37
|
+
stack = Thread.current[@key][key]
|
38
|
+
|
39
|
+
conn =
|
40
|
+
if stack.empty?
|
41
|
+
@available.pop(connection_args: key)
|
42
|
+
else
|
43
|
+
stack.last
|
44
|
+
end
|
45
|
+
|
46
|
+
stack.push(conn)
|
47
|
+
|
48
|
+
conn
|
49
|
+
end
|
50
|
+
|
51
|
+
def shutdown(&block)
|
52
|
+
@available.shutdown(&block)
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Griffin
|
4
|
+
class CountingSemaphore
|
5
|
+
def initialize(size)
|
6
|
+
@size = size
|
7
|
+
@queue = Queue.new
|
8
|
+
@size.times { @queue.push(0) }
|
9
|
+
end
|
10
|
+
|
11
|
+
def wait
|
12
|
+
@queue.pop
|
13
|
+
end
|
14
|
+
|
15
|
+
def signal
|
16
|
+
@queue.push(0)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'griffin/listener'
|
4
|
+
|
5
|
+
module Griffin
|
6
|
+
module Engine
|
7
|
+
module Server
|
8
|
+
attr_reader :core, :listener
|
9
|
+
|
10
|
+
def initialize
|
11
|
+
@core = Griffin::Server.new
|
12
|
+
end
|
13
|
+
|
14
|
+
def before_run
|
15
|
+
config[:services].each do |s|
|
16
|
+
@core.handle(s)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
def stop(stop_graceful)
|
21
|
+
super # needed
|
22
|
+
end
|
23
|
+
|
24
|
+
# def after_start; end
|
25
|
+
# def restart; end
|
26
|
+
# def reload_config; end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'griffin/server'
|
4
|
+
require 'griffin/listener'
|
5
|
+
|
6
|
+
module Griffin
|
7
|
+
module Engine
|
8
|
+
class Single
|
9
|
+
def self.create(config)
|
10
|
+
new(Griffin::Server.new, config)
|
11
|
+
end
|
12
|
+
|
13
|
+
def initialize(server, config)
|
14
|
+
@server = server
|
15
|
+
@config = config
|
16
|
+
@listener = Griffin::Listener.new(@config[:bind], @config[:port])
|
17
|
+
end
|
18
|
+
|
19
|
+
def run
|
20
|
+
@config[:services].each do |s|
|
21
|
+
@server.handle(s)
|
22
|
+
end
|
23
|
+
|
24
|
+
install_handler
|
25
|
+
|
26
|
+
@server.before_run
|
27
|
+
@server.run(@listener.listen)
|
28
|
+
end
|
29
|
+
|
30
|
+
def install_handler
|
31
|
+
trap('INT') do
|
32
|
+
@server.shutdown
|
33
|
+
end
|
34
|
+
|
35
|
+
trap('TERM') do
|
36
|
+
@server.shutdown
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'griffin/listener'
|
4
|
+
|
5
|
+
module Griffin
|
6
|
+
module Engine
|
7
|
+
module Worker
|
8
|
+
def before_fork
|
9
|
+
@listener = Griffin::Listener.new(config[:bind], config[:port])
|
10
|
+
server.core.before_run(worker_id)
|
11
|
+
end
|
12
|
+
|
13
|
+
def run
|
14
|
+
server.core.run(@listener.listen)
|
15
|
+
ensure
|
16
|
+
@listener.close
|
17
|
+
end
|
18
|
+
|
19
|
+
def stop
|
20
|
+
server.core.shutdown
|
21
|
+
end
|
22
|
+
|
23
|
+
# def after_fork; end
|
24
|
+
# def reload; end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'serverengine'
|
4
|
+
require 'griffin/engine/single'
|
5
|
+
require 'griffin/engine/server'
|
6
|
+
require 'griffin/engine/worker'
|
7
|
+
|
8
|
+
module Griffin
|
9
|
+
module Engine
|
10
|
+
def self.start(config, cluster: false)
|
11
|
+
if cluster
|
12
|
+
Griffin.logger.info("Griffin v#{Griffin::VERSION} starts as cluster mode")
|
13
|
+
ServerEngine.create(Griffin::Engine::Server, Griffin::Engine::Worker, config).run
|
14
|
+
else
|
15
|
+
Griffin.logger.info("Griffin v#{Griffin::VERSION} starts as single mode")
|
16
|
+
Griffin::Engine::Single.create(config).run
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'socket'
|
4
|
+
|
5
|
+
module Griffin
|
6
|
+
class Listener
|
7
|
+
DEFAULT_BACKLOG_SIZE = 1024
|
8
|
+
|
9
|
+
# @params host [String]
|
10
|
+
# @params port [Integer]
|
11
|
+
# @params backlog [Integer]
|
12
|
+
def initialize(host, port, backlog: DEFAULT_BACKLOG_SIZE)
|
13
|
+
@host = host
|
14
|
+
@port = port
|
15
|
+
@backlog = backlog
|
16
|
+
end
|
17
|
+
|
18
|
+
def listen(tcp_opt: true)
|
19
|
+
@sock = Socket.new(Socket::AF_INET, Socket::SOCK_STREAM, 0)
|
20
|
+
if tcp_opt
|
21
|
+
@sock.setsockopt(Socket::IPPROTO_TCP, Socket::TCP_NODELAY, 1)
|
22
|
+
end
|
23
|
+
|
24
|
+
@sock.setsockopt(Socket::SOL_SOCKET, Socket::SO_REUSEPORT, true)
|
25
|
+
|
26
|
+
@sock.bind(Addrinfo.tcp(@host, @port))
|
27
|
+
@sock.listen(@backlog)
|
28
|
+
Griffin.logger.info("Start listening #{@host}:#{@port}")
|
29
|
+
@sock
|
30
|
+
end
|
31
|
+
|
32
|
+
def close
|
33
|
+
@sock.close
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|