Posts List
  1. 原理
  2. 执行入口
  3. Client
    1. #boot_server
    2. #run
  4. Server
    1. #boot
    2. #serve
  5. ApplicationManager
    1. #run
  6. Application
    1. #initialize
    2. #run
    3. #serve
  7. 修改项目文件
    1. 属于Rails自动导入的文件
    2. 其他文件
  8. Awesome

研读spring源码

spring-1.6.4

原理

创建一个进程,提前eager_load,通过执行spring+command,把command传给进程,进程收到后,用fork创建子进程处理,这样不用每次执行命令都要load代码

执行入口

  • 项目中bin/springrequire "spring/binstub"
  • binstub文件load(执行)gem中的bin/spring
  • Spring::Client.run(ARGV)

Client

#boot_server

Process.spawn(创建一个进程来执行命令)启动server

1
2
3
4
5
pid = Process.spawn(
gem_env,
"ruby",
"-e", "gem 'spring', '#{Spring::VERSION}'; require 'spring/server'; Spring::Server.boot"
)

#run

application, client = UNIXSocket.pair 用于两个进程通信

  • 把client给server
    server.send_io client
  • application写入命令信息
    send_json application, "args" => args, "env" => ENV.to_hash
  • server通过client读取信息
    serve server.accept
    app_client = client.recv_io #这里的app_client就是上面的client

send_json server, "args" => args, "default_rails_env" => default_rails_env
这里同时把参数传给了server,是为了在server里使用

Server

#boot

#serve

  • 接收到Client传过来的client(io),然后交给ApplicationManager处理 –run返回pid,client写入流中
    client.puts @applications[rails_env_for(args, default_rails_env)].run(app_client)

ApplicationManager

#run

@child, child_socket = UNIXSocket.pair

  • Process.spawn执行require 'spring/application/boot'boot中创建Spring::Application

Application

#initialize

manger: 上面的child_socket

#run

循环的从manger获取client(包含命令参数)
serve manager.recv_io(UNIXSocket)

#serve

  • 从client读取命令参数
  • fork创建一个新的进程来执行命令

修改项目文件

属于Rails自动导入的文件

Application#serve

1
2
3
4
if Rails.application.reloaders.any?(&:updated?)
ActionDispatch::Reloader.cleanup!
ActionDispatch::Reloader.prepare!
end

其他文件

关闭server,下次执行命令重启server
Spring::Watcher::Polling-#start创建一个线程不断的检查监测文件是否改变:取所有文件最后改变时间的最大值,与之前存下的值进行比较

Awesome

  • ljust "hello".ljust(20) #=> "hello "
  • 实现两个进程之间的通信:application, client = UNIXSocket.pair