Computer >> คอมพิวเตอร์ >  >> การเขียนโปรแกรม >> Ruby

Push และ Pub/Sub ใน Ruby

การสร้างฟีเจอร์เรียลไทม์ใน Rails ทำได้ง่ายกว่ามากกับไลบรารี เช่น Action Cable ใน AppSignal Academy ในตอนนี้ เราจะเจาะลึกเกี่ยวกับการอัปเดตตามเวลาจริงและเล่นกับการสร้างเซิร์ฟเวอร์ WebSocket แบบมินิมอล เพื่อดูว่ามันทำงานอย่างไรภายใต้ประทุน

เราจะสร้างแอปพลิเคชันที่ผลักดัน และใช้ Pub/Sub ผ่าน WebSocket . ก่อนที่เราจะเริ่มต้นเกี่ยวกับโค้ด เรามาใช้เวลาสักเล็กน้อยเพื่ออธิบายความหมายของแนวคิดทั้งสามนี้:

  • ดัน หมายถึงการส่งข้อมูลไปยังผู้รับแทนที่จะมีการสำรวจข้อมูลผู้รับสำหรับข้อมูลนั้น สิ่งที่ต้องมีในการอัปเดตแบบเรียลไทม์ เช่น ราคาหุ้น แอปพลิเคชันแชท หรือคอนโซลการดำเนินการ

  • ผับ/ย่อย หรือเผยแพร่และสมัครสมาชิกเป็นรูปแบบการโต้ตอบสำหรับการผลักดันข้อมูลที่ TIBCO ได้รับความนิยมใน Wall Street ในปี 1990 ผู้รับสมัครสมาชิกเรื่องและรอให้ผู้เผยแพร่ส่งข้อมูลไปยังหัวเรื่องนั้น เป็นเรื่องปกติที่จะรวมการจับคู่รูปแบบสัญลักษณ์แทนเพื่อให้ตรงกับข้อความที่เผยแพร่ไปยังผู้ฟัง แม้ว่าการใช้งานที่ง่ายกว่าบางรายการจะใช้เฉพาะช่องที่มีชื่อแทนการใช้สัญลักษณ์แทนในหัวเรื่อง ฉันเริ่มต้นที่ TIBCO ในช่วงแรกๆ ดังนั้นฉันจึงชอบความยืดหยุ่นของการจับคู่รูปแบบสัญลักษณ์แทน

  • WebSocket เป็นโปรโตคอลสำหรับการแลกเปลี่ยนข้อมูล โดยปกติแล้วจะเป็นระหว่างเว็บเบราว์เซอร์และแอปพลิเคชัน การเชื่อมต่อ HTTP ได้รับการอัพเกรดเป็นการเชื่อมต่อ WebSocket จากนั้นข้อมูลสามารถส่งได้ทั้งสองทางระหว่างจุดปลายทั้งสอง WebSockets สามารถส่งข้อมูลจากแอปพลิเคชันไปยังเบราว์เซอร์ได้ นอกจากนี้ยังมีกลไกอื่นนอกเหนือจาก POST หรือ PUT สำหรับการส่งข้อมูลจากโค้ด JavaScript ในเบราว์เซอร์กลับไปยังแอปพลิเคชัน น่ารักดีนะ ว่าไหม

ภายใต้ประทุน

ลองดูตัวอย่างการทำงานของเซิร์ฟเวอร์ WebSocket กัน จากเบราว์เซอร์ ไคลเอ็นต์พยายามทำการเชื่อมต่อ WebSocket กับเซิร์ฟเวอร์ด้วยโค้ด JavaScript

var sock = new WebSocket("ws://" + document.URL.split("/")[2] + "/upgrade");

เซิร์ฟเวอร์ได้รับคำขอ HTTP พร้อมตัวบ่งชี้ว่ามีการร้องขอการอัพเกรด โดยทั่วไป เซิร์ฟเวอร์อนุญาตให้แอปพลิเคชันตัดสินใจว่าจะอัพเกรดหรือไม่ มันขึ้นอยู่กับ API ที่จัดเตรียมให้กับแอพอย่างไร เซิร์ฟเวอร์ที่รองรับ Rack มีตัวเลือกในการจี้ซ็อกเก็ตและให้นักพัฒนาจัดการรายละเอียดโปรโตคอลทั้งหมด หรือตาม PR ที่เสนอ การตอบสนองต่อการอัพเกรดก็เพียงพอแล้ว

การอัพเกรดเป็นชุดของการแลกเปลี่ยนระหว่างเซิร์ฟเวอร์และไคลเอนต์ เบราว์เซอร์ทั้งหมดและอัญมณีเซิร์ฟเวอร์บางตัวซ่อนรายละเอียดเหล่านี้ เมื่อสร้างการเชื่อมต่อแล้ว จะสามารถแลกเปลี่ยนข้อความตามโปรโตคอล WebSocket

เวทมนตร์ภายใต้ประทุนจะจัดการกับการเข้ารหัส ถอดรหัส และโปรโตคอลการแลกเปลี่ยนข้อความ ข้อความเป็นโครงสร้างไบนารีที่มีความกว้างคงที่พร้อมเพย์โหลดต่อท้าย เข้ารหัสโดยใช้ SHA1 โปรโตคอล WebSocket ประกอบด้วยประเภทข้อความและการแลกเปลี่ยนหลายประเภท เช่น จังหวะปิงปอง/ปิงปอง และการแลกเปลี่ยนข้อความเปิดและปิด นั่นคือความมหัศจรรย์ที่เซิร์ฟเวอร์ดำเนินการโดยไม่ใช้วิธีการเชื่อมต่อ-จี้

ดำน้ำ

เราจะใช้ตัวอย่างของเธรดนาฬิกาที่เริ่มเผยแพร่เวลาปัจจุบันไปยังไคลเอนต์ที่รับฟังทั้งหมด เราจะใช้ Agoo เพื่อสร้างเซิร์ฟเวอร์ของเราเพราะมันรวดเร็วและรักษาความซับซ้อนให้น้อยที่สุด

เราจะเริ่มต้นด้วย JavaScript บางส่วนในฐานะไคลเอนต์โดยแสดงเวลาปัจจุบันบนหน้า HTML หลังจากสร้าง WebSocket . ใหม่แล้ว onopen มีการตั้งค่าการโทรกลับที่เปลี่ยนสถานะองค์ประกอบ HTML onmessage โทรกลับอัปเดต onmessage องค์ประกอบ HTML การเรียกกลับเป็นรูปแบบการออกแบบทั่วไปเมื่อทำงานกับการเรียกแบบอะซิงโครนัส เช่น เผยแพร่และสมัครการแลกเปลี่ยน

<!-- websocket.html -->
<html>
  <body>
    <p id="status">...</p>
    <p id="message">... waiting ...</p>
 
    <script type="text/javascript">
      var sock = new WebSocket(
        "ws://" + document.URL.split("/")[2] + "/upgrade"
      );
      sock.onopen = function () {
        document.getElementById("status").textContent = "connected";
      };
      sock.onmessage = function (msg) {
        document.getElementById("message").textContent = msg.data;
      };
    </script>
  </body>
</html>

เมื่อลูกค้าทำเสร็จแล้ว เรามาติดตั้งเซิร์ฟเวอร์ ซึ่งเป็นแอปพลิเคชัน Ruby โดยใช้ Rack API Clock ตัวคลาสเองจะเป็นตัวจัดการสำหรับคำขอ HTTP ทั้งหมดใน /upgrade เส้นทาง. หากคำขอเป็นการอัพเกรด เราเพียงแค่ส่งคืน Success ด้วยรหัสสถานะ HTTP 200 มิฉะนั้น เราจะส่งคืน 404 สำหรับ Page Not Found อีกขั้นตอนเดียวใน #call method คือการกำหนดตัวจัดการ WebSocket

class Clock
  def self.call(env)
    unless env['rack.upgrade?'].nil?
      env['rack.upgrade'] = Clock
      [ 200, { }, [ ] ]
    else
      [ 404, { }, [ ] ]
    end
  end
end

API ขึ้นอยู่กับการโทรกลับ การโทรกลับอย่างเดียวที่เราสนใจสำหรับเซิร์ฟเวอร์ของเราคือ #on_open โทรกลับซึ่งช่วยให้เราสร้างการสมัครรับเรื่อง "เวลา" ข้อความจะถูกแลกเปลี่ยนผ่านช่องทางที่ระบุตามหัวเรื่องหรือหัวข้อ #on_open จะถูกเรียกเมื่อมีการเชื่อมต่อเว็บซ็อกเก็ต

class Clock
  # ...
 
  def self.on_open(client)
    client.subscribe('time')
  end
end

ตอนนี้ มาเริ่มเผยแพร่ด้วยเธรดที่เผยแพร่เวลาทุกวินาที การเรียกไปที่ Agoo.publish ส่งข้อความในหัวข้อ "เวลา" จากนั้นสมาชิกทั้งหมดจะได้รับข้อความ เซิร์ฟเวอร์ติดตามการสมัครรับข้อมูลและการเชื่อมต่อ และส่งข้อความไปยังไคลเอนต์ JavaScript ซึ่งอัปเดตองค์ประกอบ HTML

require 'agoo'
 
Thread.new {
  loop do
    now = Time.now
    Agoo.publish('time', "%02d:%02d:%02d" % [now.hour, now.min, now.sec])
    sleep(1)
  end
}

รหัสอื่นที่จำเป็นเท่านั้นคือรหัสที่เริ่มต้นและเริ่มต้นเซิร์ฟเวอร์ การเรียกไปที่ Agoo::Server.handle(:GET, '/upgrade', Clock) บอกให้เซิร์ฟเวอร์ฟังคำขอ HTTP GET บน /upgrade เส้นทาง URL และส่งคำขอเหล่านั้นไปยัง Clock ระดับ. ซึ่งจะทำให้การกำหนดเส้นทางเกิดขึ้นนอก Ruby เพื่อเพิ่มประสิทธิภาพและความยืดหยุ่น

Agoo::Server.init(6464, '.', thread_count: 0)
Agoo::Server.handle(:GET, '/upgrade', Clock)
Agoo::Server.start

เราเกือบจะอยู่ที่นั่นแล้ว รันเซิร์ฟเวอร์ด้วยคำสั่งนี้

$ ruby pubsub.rb

รายการบันทึกควรปรากฏขึ้นโดยแสดงสิ่งต่อไปนี้ ซึ่งบ่งชี้ว่าเซิร์ฟเวอร์กำลังทำงานและรับฟังบนพอร์ต 6464

I 2018/08/14 19:49:45.170618000 INFO: Agoo 2.5.0 with pid 40366 is listening on https://:6464.

ถึงเวลาดูว่าใช้งานได้หรือไม่

มาเปิด https://localhost:6464/websocket.html กันเถอะ หลังจากการสั่นไหวครั้งแรกเมื่อสร้างการเชื่อมต่อ สถานะการเชื่อมต่อและเวลาควรแสดงขึ้น เวลาจะเพิ่มขึ้นทุก ๆ วินาทีตามเข็มนาฬิกา

connected

19:50:12

ขอแสดงความยินดีกับการเผยแพร่และสมัครเว็บแอปพลิเคชัน;-)

ในตอนของวันนี้ เรามาดูการใช้ WebSocket เหตุการณ์ฝั่งเซิร์ฟเวอร์ (SSE) เสนอทางเลือกอื่นในการทำเช่นเดียวกัน และเราได้รวม SSE ไว้ในตัวอย่างซอร์สโค้ดแบบเต็มแล้ว หากคุณต้องการทราบข้อมูลเพิ่มเติม โปรดดูที่เซิร์ฟเวอร์ Agoo ที่เราใช้หรือเซิร์ฟเวอร์ Iodine WebSocket

หากคุณมีคำถามหรือความคิดเห็นใด ๆ อย่าลังเลที่จะวางสาย @AppSignal