Using UNIXSockets

I'm Guo Xiang

I work REMOTELY for

We're HIRING

Hire from commits :)

https://meta.discourse.org/c/bug
https://meta.discourse.org/c/feature

DURIAN STICKERS!

What is a Unix SOCKET?

A Unix domain socket or IPC socket (inter-process communication socket) is a data communications endpoint for exchanging data between processes executing on the same host operating system.

Unix domain sockets use the file system as their address name space.
                                                               https://en.wikipedia.org/wiki/Unix_domain_socket

In RUBY CODE...

require 'socket'
require 'fileutils'

begin
  server = UNIXServer.new("/tmp/test.sock")

  puts "Accepting new connections"
  socket = server.accept

  puts "Connection received"
  content = socket.readline

  puts "Received: #{content}"
  socket.write(Process.pid)
ensure
  socket&.close
  FileUtils.remove('/tmp/test.sock')
end

In RUBY CODE...

require 'socket'

begin
  socket = UNIXSocket.new("/tmp/test.sock")

  puts "Writing to socket..."
  socket.write("Hello World from #{Process.pid}\n")

  puts "Reading response"
  puts socket.readline
ensure
  socket&.close
end

Use Cases for UNIX Sockets?

SIMPLE WEB SERVER

daemon off;
user tgxworld;
events {}
error_log /dev/stdout;

http {
  access_log /dev/stdout;

  upstream app {
    server unix:/tmp/test.sock;
  }

  server {
    listen 2048;
    server_name localhost

    root /var/www/html;
    index index.html;

    location / {
      proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
      proxy_set_header Host $http_host;
      proxy_redirect off;
      proxy_pass http://app;
    }
  }
}
require 'socket'
require 'fileutils'

begin
  server = UNIXServer.new("/tmp/test.sock")

  puts "Accepting new connections"
  socket = server.accept

  puts "Connection received"

  while (line = socket.readline) != "\r\n"
    puts line
  end

  socket.write("HTTP/1.1 200 OK\n\nYay connection received at #{Time.now.to_s}")
ensure
  socket&.close
  FileUtils.remove('/tmp/test.sock')
end

SIMPLE WEB SERVER

require 'socket'
require 'fileutils'

server = UNIXServer.new("/tmp/test.sock")

def accept_connection(server)
  begin
    puts "\n\n\n"

    socket = server.accept

    while (line = socket.readline) != "\r\n"
      puts line
    end

    puts "\n\n\n"

    socket.write("HTTP/1.1 200 OK\n\nYay connection received at #{Time.now.to_s}")
  ensure
    socket&.close
  end
end

begin
  loop do
    accept_connection(server)
  end
ensure
  FileUtils.remove('/tmp/test.sock')
end

SIMPLE WEB SERVER

# unicorn.conf
listen "/var/www/someapp/tmp/sockets/unicorn.sock"

# nginx.conf
upstream app {
  server unix:/var/www/someapp/tmp/sockets/unicorn.sock;
}

SIMPLE WEB SERVER

SIMPLE WEB SERVER

http://blog.honeybadger.io/how-unicorn-talks-to-nginx-an-introduction-to-unix-sockets-in-ruby/

Use Cases for UNIX Sockets?

Stats SERVER

Stats SErver

Ruby 2.4

Stats SErver

https://samsaffron.com/archive/2015/03/31/debugging-memory-leaks-in-ruby

 

`GC.stat` - Returns a Hash containing information about the GC for the particular Ruby process that it is called in. 

Stats SErver

desc "Returns `GC.stat` for each Sidekiq process in JSON"
task "sidekiq:gc_stat" do
  pids = `ps -eo pid,args | grep ' [s]idekiq ' | awk '{print $1}'`.split("\n").map(&:to_i)
  results = []
  hostname = `hostname`.chomp

  pids.each do |pid|
    tmp_path = Tempfile.new.path

    system(
      "bundle exec rbtrace -p #{pid} -e \"o = GC.stat; f = File.open('#{tmp_path}', 'w'); f.write(o.to_json); f.close\"",
      out: "/dev/null", err: "/dev/null"
    )

    result = JSON.parse(File.read(tmp_path))
    result["hostname"] = hostname
    results << result
  end

  puts results.to_json
end

Stats SErver

require 'socket'
require 'json'
require 'fileutils'

class StatsServer

  def initialize(socket_path)
    @socket_path = socket_path
    @server = nil
  end

  def start 
    @server = UNIXServer.new(@socket_path)
    spawn_thread
  end

  def stop
    @server.close if @server
    @server = nil
    FileUtils.rm(@socket_path)
  end

end

Stats SErver

def spawn_thread
  Thread.new do
    done = false 

    while !done
      done = !accept_connection(@server)
    end 
  end
end

Stats SErver

def accept_connection(server)
  socket = nil

  begin
    socket = server.accept
  rescue IOError
    return false
  end

  command = socket.readline

  output = 
    case command.chomp
    when "gc stat" 
      GC.stat.to_json
    else
      "Invalid Command\n"
    end

  socket.write(output)

  true
ensure
  socket&.close
end

Stats SErver DEMO

Questions?

USING UNIXSocket

By tgxworld

USING UNIXSocket

  • 815