• Cap'n Web คือ โปรโตคอล RPC ใหม่ที่พัฒนาด้วย TypeScript โดยออกแบบมาให้เหมาะกับสภาพแวดล้อมเว็บและทำงานได้บน JavaScript runtime ที่หลากหลาย
  • โดยไม่ต้องมีสคีมาหรือ boilerplate ที่ยุ่งยาก ก็สามารถใช้ การซีเรียลไลซ์แบบอิง JSON และ รูปแบบข้อมูลที่มนุษย์อ่านได้
  • ด้วย โมเดลแบบ object-capability จึงรองรับ การเรียกแบบสองทาง, การส่งต่อ reference ของฟังก์ชันและอ็อบเจ็กต์, promise pipelining และการสร้างแพตเทิร์นด้านความปลอดภัย
  • รองรับสภาพแวดล้อมเครือข่ายหลากหลาย เช่น WebSocket, HTTP, postMessage และเป็นโอเพนซอร์สน้ำหนักเบาขนาดต่ำกว่า 10kB
  • ไม่เพียงช่วยแก้ปัญหา waterfall แบบเดียวกับ GraphQL เท่านั้น แต่ยังทำให้สามารถออกแบบ RPC ได้อย่างเป็นธรรมชาติคล้าย JavaScript API ทั่วไป

Cap'n Web คืออะไร

  • Cap'n Web คือ ระบบ RPC โอเพนซอร์สบนพื้นฐาน TypeScript ที่พัฒนาโดย Cloudflare
  • ได้แรงบันดาลใจจาก Cap'n Proto แต่ทำงานได้โดย ไม่ต้องกำหนดสคีมาแยกต่างหาก และใช้ JSON เป็นวิธี ซีเรียลไลซ์ที่เป็นมิตรต่อมนุษย์
  • ทำงาน ผสานกับ TypeScript ช่วยยกระดับประสบการณ์นักพัฒนา เช่น autocomplete และ type check ส่วนการตรวจสอบ type ตอนรันไทม์สามารถจัดการแยกต่างหากได้ เช่น type guard
  • รองรับ โปรโตคอลเครือข่ายอย่าง HTTP, WebSocket, postMessage และทำงานได้บนเบราว์เซอร์หลัก, Cloudflare Workers, Node.js เป็นต้น
  • มีโครงสร้างแบบเบาโดยไม่มี dependency และมีขนาด ต่ำกว่า 10kB เมื่อ minify + gzip

โมเดล object-capability (OCap) ของ Cap'n Web

  • ใช้ โมเดลแบบ object-capability ทำให้แสดงรูปแบบการทำงานได้หลากหลายกว่าระบบ RPC แบบเดิม
    • การเรียกแบบสองทาง: ไคลเอนต์และเซิร์ฟเวอร์สามารถเรียกฟังก์ชันของกันและกันได้
    • การส่งต่อ reference ของฟังก์ชันและอ็อบเจ็กต์: เมื่อส่งฟังก์ชันหรืออ็อบเจ็กต์ผ่าน RPC ฝั่งตรงข้ามจะได้รับสตับและเมื่อเรียกใช้งานจะไปทำงานที่ต้นทาง
    • Promise Pipelining: เมื่อเชื่อมหลาย RPC เป็น chain สามารถประมวลผลได้ด้วยการรับส่งข้อมูลไป-กลับบนเครือข่ายเพียงครั้งเดียว
    • แพตเทิร์นด้านความปลอดภัย: สามารถสร้างกลไกควบคุมด้านความปลอดภัย เช่น การมอบสิทธิ์และการจัดการเซสชัน ได้อย่างเป็นธรรมชาติ

วิธีใช้งานพื้นฐาน

  • ตัวอย่างไคลเอนต์

    import { newWebSocketRpcSession } from "capnweb"  
    let api = newWebSocketRpcSession("wss://example.com/api")  
    let result = await api.hello("World")  
    console.log(result)  
    
  • ตัวอย่างเซิร์ฟเวอร์ (อิง Cloudflare Worker)

    import { RpcTarget, newWorkersRpcResponse } from "capnweb"  
    class MyApiServer extends RpcTarget {  
      hello(name) {  
        return `Hello, ${name}!`  
      }  
    }  
    export default {  
      fetch(request, env, ctx) {  
        let url = new URL(request.url)  
        if (url.pathname === "/api") {  
          return newWorkersRpcResponse(request, new MyApiServer())  
        }  
        return new Response("Not found", {status: 404})  
      }  
    }  
    
  • สามารถเพิ่มเมธอดให้ API, ส่ง callback function จากไคลเอนต์, รวมถึงกำหนดและนำ TypeScript interface ไปใช้ได้อย่างง่ายดาย

RPC คืออะไร และจุดเด่นของ Cap'n Web

  • RPC (Remote Procedure Call) คือแนวคิดที่ทำให้โปรแกรมสองตัวบนเครือข่ายสื่อสารกันได้ราวกับเป็นการเรียกฟังก์ชัน
  • ต่างจากโปรโตคอล HTTP/REST แบบดั้งเดิม RPC ใช้ abstraction แบบการเรียกฟังก์ชัน ทำให้เขียนโค้ดได้ สอดคล้องกับวิธีคิดของนักพัฒนา
  • Cap'n Web เข้ากันได้ดีกับแนวทางของ JavaScript ยุคใหม่ เช่น async/await, Promise, Exception
  • จากเดิมที่ RPC เคยมีข้อถกเถียงทางประวัติศาสตร์ เช่น การเรียกแบบ synchronous และข้อผิดพลาดเครือข่าย ในสภาพแวดล้อม JS สมัยใหม่สามารถใช้งานได้อย่างปลอดภัยและมีประสิทธิภาพมากขึ้น

สถานการณ์การใช้งานของ Cap'n Web

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

ความสามารถหลากหลาย

โหมด HTTP batch

  • หากไม่จำเป็นต้องเชื่อมต่อแบบต่อเนื่อง สามารถใช้ โหมด HTTP batch เพื่อรวมหลาย RPC call แล้วประมวลผลพร้อมกันได้

    import { newHttpBatchRpcSession } from "capnweb"  
    let batch = newHttpBatchRpcSession("https://example.com/api")  
    let result = await batch.hello("World")  
    console.log(result)  
    
  • สามารถรันหลายคำสั่งเรียกภายใน batch เดียวพร้อมกัน และรับผลลัพธ์แบบขนานได้

    let promise1 = batch.hello("Alice")  
    let promise2 = batch.hello("Bob")  
    let [result1, result2] = await Promise.all([promise1, promise2])  
    

Promise Pipelining (การเรียกแบบ chain)

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

  • ตัวอย่าง) ส่ง Promise ของผลลัพธ์จาก getMyName() เข้า hello() ได้ทันที และประมวลผลด้วยการรับส่งข้อมูลไป-กลับเพียงครั้งเดียว

    let namePromise = batch.getMyName()  
    let result = await batch.hello(namePromise)  
    
  • Promise ของ Cap'n Web ทำงานเป็น อ็อบเจ็กต์ proxy จึงสามารถ chain การเรียกเมธอดเพิ่มเติมได้โดยไม่เกิดความล่าช้า

    let sessionPromise = batch.authenticate(apiKey)  
    let name = await sessionPromise.whoami()  
    

ความปลอดภัย: การยืนยันตัวตนและ object-capability

  • ผ่านเมธอด authenticate สามารถ มอบอ็อบเจ็กต์สิทธิ์ใช้งาน (session) เมื่อสำเร็จ และหลังจากนั้นเรียกใช้ความสามารถต่าง ๆ ได้โดยไม่ต้องผ่านขั้นตอนยืนยันตัวตนเพิ่ม
  • ต่างจาก RPC แบบเดิม ไม่สามารถปลอมแปลง session object ได้ และไม่อาจเข้าถึงเมธอดที่ต้องใช้สิทธิ์โดยไม่มีการยืนยันตัวตน
  • ช่วยก้าวข้ามข้อจำกัดเชิงโครงสร้างของ WebSocket ได้อย่างเป็นธรรมชาติ และทำให้ตรรกะการยืนยันตัวตนมีความสอดคล้องกัน
  • เมื่อประกาศ API interface ด้วย TypeScript ก็สามารถนำไปใช้กับทั้งไคลเอนต์และเซิร์ฟเวอร์ได้โดยอัตโนมัติ พร้อมรองรับ autocomplete และ type safety

เปรียบเทียบกับ GraphQL และจุดแตกต่างของ Cap'n Web

  • GraphQL ช่วยบรรเทาปัญหา waterfall แบบ REST แต่ต้องนำ ภาษา, สคีมา, toolchain ใหม่ เข้ามาใช้

  • Cap'n Web แก้ปัญหา waterfall ได้ด้วยโค้ด JavaScript เพียงอย่างเดียว และ

    • ด้วยการรองรับ promise pipelining/object reference จึงสามารถโมเดล nested call หรือ logic ของธุรกรรมที่ซับซ้อนได้อย่างเป็นธรรมชาติ
    let user = api.createUser({ name: "Alice" })  
    let friendRequest = await user.sendFriendRequest("Bob")  
    
  • สามารถใช้งานได้คล้าย JavaScript API โดยไม่ต้องแบกรับ ความซับซ้อนและต้นทุนในการเรียนรู้หรือดูแลรักษา แบบ GraphQL

การทำงานกับอาร์เรย์ (array.map เป็นต้น) และการเพิ่มประสิทธิภาพ

  • ใน Cap'n Web สามารถทำ map กับแต่ละสมาชิกของอาร์เรย์ได้โดยไม่ต้องเพิ่มการรับส่งข้อมูลไป-กลับบนเครือข่าย

  • callback function ของ map จะถูกรันหนึ่งครั้งที่ฝั่งไคลเอนต์เพื่อบันทึกเนื้อหาการคำนวณแบบ record-replay จากนั้นส่งไปยังเซิร์ฟเวอร์เพื่อประมวลผลแบบรวมชุด

    let friendsWithPhotos = friendsPromise.map(friend => {  
      return {friend, photo: api.getUserPhoto(friend.id)}  
    })  
    let results = await friendsWithPhotos  
    
  • ผ่าน DSL เฉพาะโดเมนที่จำกัด ทำให้เขียนได้เหมือนฟังก์ชัน JavaScript แต่เบื้องหลังใช้โปรโตคอลของ Cap'n Web เพื่อเพิ่มประสิทธิภาพการเรียกหลายครั้ง

โครงสร้างโปรโตคอลภายในและลำดับการสื่อสาร

  • ส่งข้อมูลแบบมีโครงสร้างด้วย JSON + การประมวลผลล่วงหน้าแบบพิเศษ และรองรับชนิดข้อมูลพิเศษ เช่น อาร์เรย์และวันที่
  • เป็นโปรโตคอลแบบสมมาตร จึงรองรับ การสื่อสารสองทางโดยไม่แบ่งแยกไคลเอนต์/เซิร์ฟเวอร์
  • แต่ละฝั่ง (เช่น Alice และ Bob) จะจัดการตาราง export/import และแยก reference ของอ็อบเจ็กต์กับฟังก์ชันด้วย ID
  • ด้วยข้อความแบบ push/pull และการกำหนด Promise ID ทำให้สามารถสะท้อนหลายคำสั่งเรียกได้ภายในการรับส่งข้อมูลรอบเดียว

สถานะปัจจุบันและกรณีใช้งานจริง

  • ขณะนี้ Cap'n Web ยังเป็น โอเพนซอร์สเชิงทดลอง และถูกนำไปใช้ในบริการจริงแล้ว เช่น remote bindings ของ Cloudflare Wrangler
  • มีแผนจะเผยแพร่บล็อกโพสต์เพิ่มเติมและทำการทดลองกับฝั่งฟรอนต์เอนด์อีกหลากหลายรูปแบบ
  • เผยแพร่ภายใต้สัญญาอนุญาต MIT และ ทุกคนนำไปใช้ได้อย่างอิสระ
  • ไปยัง GitHub repository

ยังไม่มีความคิดเห็น

ยังไม่มีความคิดเห็น