Class: Swee::Server

Inherits:
Object show all
Includes:
Daemonize
Defined in:
lib/swee/server.rb

Instance Attribute Summary collapse

Instance Method Summary collapse

Methods included from Daemonize

#safefork

Constructor Details

#initializeServer

Returns a new instance of Server.



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
# File 'lib/swee/server.rb', line 8

def initialize
  @config = Swee.config
  @options = @config.server

  @signature = nil

  @handle_request_mode = @options[:handle_request_mode]
  
  @restart_mode = @options[:restart_mode]
  @pid_file   = File.expand_path(@options[:pid_file],ENV["app_path"])
  @touch_file = File.expand_path(@options[:touch_file],ENV["app_path"])

  @logger                         = nil
  @log_file_options               = @options[:log_file]
  @log_file                       = File.expand_path(@log_file_options.first,ENV["app_path"])
  @log_file_options[0]            = @log_file

  @logger_level                   = @options[:logger_level]

  @code_reload                    = @options[:code_reload]

  # 最大连接数 和 稳定连接处
  @maximum_connections            = @options[:max_connections]
  @maximum_persistent_connections = @options[:max_connections]

  # 监听端口
  @listen = @options[:listen]

  # 实际连接 __id__ => connection 
  @connections                    = {}

  # 超时时间
  @timeout                        = 30

  # 活动连接数
  @persistent_connection_count    = 0

  # touch 模式重启时间
  @restart_time = nil

  # 信号队列 暂时只处理 :USR2
  @signal_queue = []

  # 守护进程
  @daemonize = @options[:run_background]

  # todo: 性能监控
  # @performance_monitoring = @options[:performance_monitoring]
end

Instance Attribute Details

#code_reloadObject (readonly)

Returns the value of attribute code_reload.



6
7
8
# File 'lib/swee/server.rb', line 6

def code_reload
  @code_reload
end

Instance Method Details

#connectionObject

连接



274
275
276
277
278
279
280
281
282
283
284
# File 'lib/swee/server.rb', line 274

def connection
  EventMachine::start_server "0.0.0.0", @listen, Connection do |_connection|
    _connection.server                  = self

    # 记录活动连接数
    if @persistent_connection_count < @maximum_persistent_connections
      @persistent_connection_count += 1
    end
    @connections[_connection.__id__] = _connection
  end
end

#connection_finished(connection) ⇒ Object

完成连接



292
293
294
295
296
297
# File 'lib/swee/server.rb', line 292

def connection_finished(connection)
  @persistent_connection_count -= 1
  @connections.delete(connection.__id__)
  
  # TODO: 停止或重启
end

#create_touch_and_pid_fileObject

创建日志 和 pid 文件



234
235
236
237
238
239
240
241
242
# File 'lib/swee/server.rb', line 234

def create_touch_and_pid_file
  [@pid_file,@touch_file].each do |_file|
    if !File.exist?(_file)
      FileUtils.mkdir_p File.dirname(_file)
      open(_file,"w")
      File.chmod(0644, _file)
    end
  end
end

#current_pid?(pid) ⇒ Boolean

判断 pid 文件是否为 当前进程ID

Returns:

  • (Boolean)


154
155
156
# File 'lib/swee/server.rb', line 154

def current_pid? pid
  read_pid_file == Process.pid.to_s
end

#disconnectObject

断开



287
288
289
# File 'lib/swee/server.rb', line 287

def disconnect
  EventMachine.stop_server(@signature)
end

#handle_daemonizeObject

处理守护进程(后台运行) 创建子进程 进程命名 注册守护进程重启



205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
# File 'lib/swee/server.rb', line 205

def handle_daemonize
  # 先删除pid文件
  remove_pid_file

  # todo 关闭io 管道
  # rd, wr = IO.pipe
  # grandparent = $$

  # 创建子进程
  safefork && exit

  # 设定进程名称
  $0 = "swee"
  
  # 重新创建pid文件
  write_pid_file

  # 注册守护进程重启
  at_exit{
    # 简单的用异常判断 系统退出,并非其他异常 获得重启
    if $!.class == SystemExit
      @logger << "Swee服务器重启!"
      remove_pid_file
      ::Swee::Engine.restart_server!
    end
  }
end

#handle_loggerObject

处理日志 stdout 和 file 两种



102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
# File 'lib/swee/server.rb', line 102

def handle_logger
  # 创建日志文件
  if !File.exist?(@log_file)
    FileUtils.mkdir_p File.dirname(@log_file)
  end
  logs = []
  logs << Logger.new(*@log_file_options)
  unless @daemonize
    logs << Logger.new(STDOUT)
  end

  # todo: 日志等级, datetime, 格式配置
  # logs.each do |_logger|
  #   _logger.level = @logger_level

  #   _logger.datetime_format = '%Y-%m-%d %H:%M:%S'
  #   _logger.formatter = proc do |severity, datetime, progname, msg|
  #     "Started GET #{msg} at #{datetime}"
  #   end
  # end

  @logger = SweeLogger.new

  logs.each { |_logger| @logger.addlog _logger }
end

#handle_pid_restartObject

处理pid重启方式



139
140
141
# File 'lib/swee/server.rb', line 139

def handle_pid_restart
  write_pid_file
end

#handle_restartObject

处理重启



129
130
131
# File 'lib/swee/server.rb', line 129

def handle_restart
  send "handle_" + @restart_mode.to_s + "_restart"
end

#handle_touch_restartObject

处理touch重启方式



134
135
136
# File 'lib/swee/server.rb', line 134

def handle_touch_restart
  @restart_time = File.mtime(@touch_file)
end

#loggerObject

获取 Logger STDOUT 和 File



59
60
61
# File 'lib/swee/server.rb', line 59

def logger
  @logger
end

#pid_mode?Boolean

判断是否为 USR2 pid 重启方式

Returns:

  • (Boolean)


149
150
151
# File 'lib/swee/server.rb', line 149

def pid_mode?
  @restart_mode == :pid
end

#read_pid_fileObject

读取pid文件 用于 USR2 信号获得后和Process.pid 比对



69
70
71
# File 'lib/swee/server.rb', line 69

def read_pid_file
  File.read(@pid_file)
end

#remove_pid_fileObject

删除pid文件 用于重启 和 exit



64
65
66
# File 'lib/swee/server.rb', line 64

def remove_pid_file
  File.delete(@pid_file) if @pid_file && File.exists?(@pid_file)
end

#restartObject

放入 注册的 exit_at 函数中 重启 停止服务 -> 删除pid文件 -> 退出进程



95
96
97
98
99
# File 'lib/swee/server.rb', line 95

def restart
  stop!
  remove_pid_file
  exit
end

#run!Object

启动服务器



245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
# File 'lib/swee/server.rb', line 245

def run!
  # 创建日志和pid文件
  create_touch_and_pid_file
  # 处理日志
  handle_logger
  # 处理重启
  handle_restart
  # EM默认配置
  EventMachine.threadpool_size = 20
  EventMachine.epoll
  EventMachine.set_descriptor_table_size(@maximum_connections)

  @logger << "正在启动swee服务器"

  EventMachine::run {
    @signature = connection
    @running = true

    # EM启动后处理后台守护进程运行
    handle_daemonize if @daemonize

    # 追踪重启文件
    trace_restart

    puts "swee 服务器已启动, 端口:#{@listen}"
  }
end

#running?Boolean

是否运行标志

Returns:

  • (Boolean)


80
81
82
# File 'lib/swee/server.rb', line 80

def running?
  @running
end

#stop!Object

强制停止



85
86
87
88
89
90
# File 'lib/swee/server.rb', line 85

def stop!
  @running  = false
  disconnect
  EventMachine.stop
  @connections.each_value { |connection| connection.close_connection }
end

#touch_mode?Boolean

判断是否为 touch 重启方式

Returns:

  • (Boolean)


144
145
146
# File 'lib/swee/server.rb', line 144

def touch_mode?
  @restart_mode == :touch
end

#trace_restartObject

追踪重启 EM Timer 方式 每秒执行一次 touch: 每秒读取touch文件 mtime pid: trap usr2 信号放入 信号队列, 然后每秒追踪队列变化获取信号



162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
# File 'lib/swee/server.rb', line 162

def trace_restart
  if touch_mode?
    EM.add_periodic_timer(1) {
      mtime = File.mtime(@touch_file)
      if mtime != @restart_time
        @restart_time = mtime
        puts "重启了"
        restart
        # 放弃 next_tick 原因:会在下次有请求时处理重启
        # EM.next_tick { restart }
      end
    }
  end

  if pid_mode?
    EM.add_periodic_timer(1) {
      signal = @signal_queue.shift
      case signal
      when nil
      when :QUIT # graceful shutdown
      when :TERM, :INT # immediate shutdown
        stop!
      when :USR1 # rotate logs
      when :USR2 # exec binary, stay alive in case something went wrong
        _restart = current_pid?
      when :WINCH
      when :TTIN
      when :TTOU
      when :HUP
      end
      restart if _restart
    }
    trap(:USR2) {
      # 收到 USR2信号加入 信号队列
      @signal_queue << :USR2
    }
  end
end

#write_pid_fileObject

写入Process.pid到 pid 文件



74
75
76
77
# File 'lib/swee/server.rb', line 74

def write_pid_file
  open(@pid_file,"w") { |f| f.write(Process.pid) }
  File.chmod(0644, @pid_file)
end