การปรับปรุงซอฟต์แวร์รุ่นเก่าให้ทันสมัย: การเขียนโปรแกรม MUD โดยใช้ Erlang และ CloudI

เผยแพร่แล้ว: 2022-03-11

Legacy Modernization คืออะไร?

รหัสเดิมมีอยู่ทุกที่ และในขณะที่อัตราการเพิ่มจำนวนโค้ดยังคงเพิ่มขึ้นแบบทวีคูณ โค้ดนั้นจึงถูกลดระดับเป็นสถานะเดิมมากขึ้นเรื่อยๆ ในองค์กรขนาดใหญ่หลายแห่ง การบำรุงรักษาระบบเดิมใช้ทรัพยากรระบบสารสนเทศมากกว่า 90%

ความจำเป็นในการปรับปรุงโค้ดและระบบเดิมให้ทันสมัยเพื่อตอบสนองความต้องการด้านประสิทธิภาพและการประมวลผลในปัจจุบันเป็นที่แพร่หลาย โพสต์นี้มีกรณีศึกษาเกี่ยวกับการใช้ภาษาการเขียนโปรแกรม Erlang และสถาปัตยกรรมเชิงบริการ CloudI ของ Erlang (SOA) ที่ใช้ Erlang เพื่อปรับโค้ดดั้งเดิม โดยเฉพาะอย่างยิ่ง คอลเล็กชันซอร์สโค้ด C ที่มีอายุหลายสิบปีจนถึงศตวรรษที่ 21 .

ในองค์กรขนาดใหญ่หลายแห่ง การบำรุงรักษาระบบเดิมใช้ทรัพยากรระบบสารสนเทศมากกว่า 90%

การสังหาร Source Code Dragon

หลายปีก่อน ฉันเป็นแฟนตัวยงของเกมออนไลน์แบบผู้เล่นหลายคนแบบข้อความที่รู้จักกันในชื่อ Multi-User Dungeons (MUDs) แต่ปัญหาเหล่านี้มักเต็มไปด้วยปัญหาด้านประสิทธิภาพ ฉันตัดสินใจย้อนกลับไปที่ซอร์สโค้ด C ที่มีอายุหลายสิบปี และดูว่าเราจะปรับปรุงโค้ดเดิมนี้ให้ทันสมัยและผลักดันเกมออนไลน์ในยุคแรกๆ เหล่านี้ให้ถึงขีดจำกัดได้อย่างไร ในระดับสูง โครงการนี้เป็นตัวอย่างที่ดีของการใช้ Erlang เพื่อปรับซอฟต์แวร์รุ่นเก่าให้เป็นไปตามข้อกำหนดของศตวรรษที่ 21

สรุปสั้น ๆ :

  • เป้าหมาย : นำวิดีโอเกม MUD ที่จำกัดผู้เล่น 50 คนและผลักดันซอร์สโค้ดเพื่อรองรับการเชื่อมต่อหลายพันครั้งพร้อมกัน
  • ปัญหา : ซอร์สโค้ด C แบบเธรดเดียวแบบเก่า
  • โซลูชัน : CloudI ซึ่งเป็นบริการตาม Erlang ที่ให้ความทนทานต่อข้อผิดพลาดและความสามารถในการปรับขนาด

การปรับปรุงซอฟต์แวร์รุ่นเก่าให้ทันสมัย: การเขียนโปรแกรม MUD โดยใช้ Erlang และ CloudI

MUD แบบข้อความคืออะไร?

เกมเล่นตามบทบาทออนไลน์ที่มีผู้เล่นหลายคนจำนวนมาก (MMORPG) เช่น World of Warcraft และ EverQuest ได้พัฒนาคุณลักษณะที่จุดเริ่มต้นแรกๆ สามารถตรวจสอบย้อนกลับไปยังเกมออนไลน์แบบผู้เล่นหลายคนแบบข้อความที่เก่ากว่าที่เรียกว่า Multi-User Dungeons (MUDs)

MUD แรกคือ Essex MUD (หรือ MUD1) ของ Roy Trubshaw ซึ่งพัฒนาครั้งแรกในปี 1978 โดยใช้ภาษาแอสเซมเบลอร์ MARO-10 บน DEC PDP-10 แต่ถูกแปลงเป็น BCPL ซึ่งเป็นบรรพบุรุษของภาษาการเขียนโปรแกรม C (และทำงานจนถึง พ.ศ. 2530) (อย่างที่คุณเห็น สิ่งเหล่านี้เก่ากว่าโปรแกรมเมอร์ส่วนใหญ่)

MUD ค่อยๆ ได้รับความนิยมในช่วงปลายทศวรรษ 1980 และต้นทศวรรษ 1990 ด้วยฐานรหัส MUD ต่างๆ ที่เขียนด้วยภาษา C ตัวอย่างเช่น ฐานรหัส DikuMUD เรียกว่ารากของต้นไม้ต้นที่ใหญ่ที่สุดต้นหนึ่งของซอร์สโค้ด MUD ที่ได้รับมา โดยมีตัวแปรที่ไม่ซ้ำกันอย่างน้อย 51 แบบทั้งหมด ขึ้นอยู่กับซอร์สโค้ด DikuMUD เดียวกัน (ในช่วงเวลานี้ โดยบังเอิญ MUD กลายเป็นที่รู้จักในชื่อ "เรือพิฆาตหลายระดับปริญญาตรี" เนื่องจากจำนวนนักศึกษาระดับปริญญาตรีที่ล้มเหลวในการออกจากโรงเรียนเนื่องจากความหลงใหลในพวกเขา)

ปัญหาเกี่ยวกับ MUDs เดิม

ซอร์สโค้ด C MUD ในอดีต (รวมถึง DikuMUD และตัวแปรต่างๆ) มีปัญหาด้านประสิทธิภาพเนื่องจากข้อจำกัดที่มีอยู่ในขณะที่สร้าง

ขาดเกลียว

สมัยนั้นไม่มีไลบรารีเธรดที่เข้าถึงได้ง่าย นอกจากนี้ เธรดจะทำให้ซอร์สโค้ดดูแลรักษาและแก้ไขได้ยากขึ้น ด้วยเหตุนี้ MUD เหล่านี้จึงเป็นแบบเธรดเดียวทั้งหมด

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

ในระหว่างการ "ติ๊ก" ครั้งเดียว (การเพิ่มขึ้นของนาฬิกาภายในที่ติดตามความคืบหน้าของกิจกรรมเกมทั้งหมด) ซอร์สโค้ด MUD จะต้องประมวลผล ทุก กิจกรรมของเกมสำหรับซ็อกเก็ตที่เชื่อมต่อ ทุก อัน กล่าวอีกนัยหนึ่ง: โค้ดทุกชิ้นทำให้การประมวลผลขีดเดียวช้าลง และหากการคำนวณใดๆ บังคับให้การประมวลผลขยายเวลานานกว่าขีดเดียว MUD จะล่าช้า ซึ่งส่งผลต่อผู้เล่นที่เชื่อมต่อทุกคน

ด้วยความล่าช้านี้ เกมจะมีส่วนร่วมน้อยลงในทันที ผู้เล่นมองอย่างช่วยไม่ได้เมื่อตัวละครของพวกเขาตายโดยที่ยังไม่ได้รับคำสั่งของตัวเอง

แนะนำ SillyMUD

สำหรับจุดประสงค์ของการทดลองปรับปรุงแอปพลิเคชันรุ่นเก่านี้ ฉันเลือก SillyMUD ซึ่งเป็นอนุพันธ์ทางประวัติศาสตร์ของ DikuMUD ที่มีอิทธิพลต่อ MMORPG สมัยใหม่และปัญหาด้านประสิทธิภาพที่พวกเขาแบ่งปัน ในช่วงปี 1990 ฉันเล่น MUD ที่มาจากโค้ดเบสของ SillyMUD ดังนั้นฉันจึงรู้ว่าซอร์สโค้ดจะเป็นจุดเริ่มต้นที่น่าสนใจและค่อนข้างคุ้นเคย

ฉันได้รับมรดกอะไร

ซอร์สโค้ดของ SillyMUD นั้นคล้ายกับ C MUD ในอดีตอื่นๆ โดยจำกัดให้เล่นได้เพียง 50 ผู้เล่นพร้อมกัน (64 ตามจริงแล้ว ขึ้นอยู่กับซอร์สโค้ด)

อย่างไรก็ตาม ฉันสังเกตเห็นว่าซอร์สโค้ดได้รับการแก้ไขด้วยเหตุผลด้านประสิทธิภาพ (กล่าวคือ เพื่อผลักดันข้อจำกัดของโปรแกรมเล่นพร้อมกัน) โดยเฉพาะ:

  • ซอร์สโค้ดไม่มีการค้นหาชื่อโดเมนบนที่อยู่ IP ของการเชื่อมต่อ เนื่องจากเวลาแฝงที่บังคับใช้โดยการค้นหาชื่อโดเมน (โดยปกติ MUD รุ่นเก่าต้องการการค้นหาชื่อโดเมนเพื่อให้แบนผู้ใช้ที่ประสงค์ร้ายได้ง่ายขึ้น)
  • ซอร์สโค้ดปิดใช้งานคำสั่ง "donate" (ค่อนข้างผิดปกติ) เนื่องจากอาจมีการสร้างรายการเชื่อมโยงแบบยาวของรายการที่บริจาค ซึ่งจำเป็นต้องมีการข้ามผ่านรายการที่มีการประมวลผลสูง ในทางกลับกัน สิ่งเหล่านี้ส่งผลเสียต่อประสิทธิภาพของเกมสำหรับผู้เล่นอื่น ๆ ทั้งหมด (แบบเธรดเดียว จำได้ไหม)

แนะนำ CloudI

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

CloudI จัดเตรียมบริการที่เป็นนามธรรม (เพื่อจัดเตรียม Service Oriented-Architecture (SOA)) ใน Erlang, C/C++, Java, Python และ Ruby ในขณะที่ยังคงแยกข้อผิดพลาดของซอฟต์แวร์ภายในกรอบงาน CloudI ความทนทานต่อข้อผิดพลาดมีให้ผ่านการใช้งาน Erlang ของ CloudI โดยอาศัยคุณสมบัติที่ทนต่อข้อผิดพลาดของ Erlang และการนำ Actor Model ไปใช้ ความทนทานต่อข้อผิดพลาดนี้เป็นคุณสมบัติหลักของการนำ Erlang ของ CloudI ไปใช้ เนื่องจากซอฟต์แวร์ทั้งหมดมีข้อบกพร่อง

CloudI ยังจัดเตรียมแอปพลิเคชันเซิร์ฟเวอร์เพื่อควบคุมอายุการใช้งานของการดำเนินการบริการและการสร้างกระบวนการบริการ (ไม่ว่าจะเป็นกระบวนการของระบบปฏิบัติการสำหรับภาษาโปรแกรมที่ไม่ใช่ Erlang หรือเป็นกระบวนการ Erlang สำหรับบริการที่นำไปใช้ใน Erlang) เพื่อให้การดำเนินการบริการเกิดขึ้นโดยไม่กระทบต่อสถานะภายนอก ความน่าเชื่อถือ สำหรับข้อมูลเพิ่มเติม ดูโพสต์ก่อนหน้าของฉัน

CloudI จะทำให้ MUD แบบข้อความดั้งเดิมมีความทันสมัยได้อย่างไร

ซอร์สโค้ด C MUD ในอดีตให้โอกาสที่น่าสนใจสำหรับการรวม CloudI เนื่องจากปัญหาด้านความน่าเชื่อถือ:

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

ด้วยการผสานรวม CloudI ข้อบกพร่องด้านความเสถียรของเซิร์ฟเวอร์ยังคงสามารถแก้ไขได้ตามปกติ แต่ผลกระทบของข้อบกพร่องนั้นมีจำกัด ดังนั้นการทำงานของเซิร์ฟเวอร์เกมจะไม่ได้รับผลกระทบเสมอไปเมื่อจุดบกพร่องที่ยังไม่ได้ค้นพบก่อนหน้านี้ทำให้ระบบเกมภายในล้มเหลว นี่เป็นตัวอย่างที่ดีของการใช้ Erlang เพื่อบังคับใช้การทนต่อข้อผิดพลาดใน codebase ดั้งเดิม

ต้องมีการเปลี่ยนแปลงอะไรบ้าง?

Codebase ดั้งเดิมถูกเขียนเป็น single-threaded และขึ้นอยู่กับตัวแปร global เป็นอย่างมาก เป้าหมายของฉันคือการรักษาฟังก์ชันซอร์สโค้ดแบบเดิมไว้ในขณะที่ปรับปรุงให้ทันสมัยสำหรับการใช้งานในปัจจุบัน

ด้วย CloudI ฉันสามารถเก็บซอร์สโค้ดแบบเธรดเดียวในขณะที่ยังคงให้ความสามารถในการขยายการเชื่อมต่อซ็อกเก็ต

เป้าหมายของฉันคือการรักษาฟังก์ชันซอร์สโค้ดแบบเดิมไว้ในขณะที่ปรับให้เข้ากับการใช้งานสมัยใหม่

มาทบทวนการเปลี่ยนแปลงที่จำเป็นกัน:

เอาต์พุตคอนโซล

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

การจัดการซ็อกเก็ต

การจัดการซ็อกเก็ตในซอร์สโค้ดดั้งเดิมนั้นอาศัยการเรียกใช้ฟังก์ชัน select() เพื่อตรวจจับอินพุต ข้อผิดพลาด และโอกาสสำหรับเอาต์พุต รวมถึงการหยุดชั่วคราวสำหรับขีดเกม 250 มิลลิวินาทีก่อนที่จะจัดการกับเหตุการณ์เกมที่ค้างอยู่

การผสานรวม CloudI SillyMUD อาศัยคำขอบริการขาเข้าสำหรับการป้อนข้อมูลในขณะที่หยุดชั่วคราวด้วยฟังก์ชัน cloudi_poll ของ C CloudI API (สำหรับ 250 มิลลิวินาทีก่อนที่จะจัดการเหตุการณ์เกมที่รอดำเนินการเหมือนกัน) ซอร์สโค้ดของ SillyMUD ทำงานได้อย่างง่ายดายภายใน CloudI เป็นบริการ CloudI หลังจากผสานรวมกับ C CloudI API (แม้ว่า CloudI จะให้บริการ API ทั้ง C และ C++ โดยใช้ C API ช่วยให้การรวมเข้ากับซอร์สโค้ด C ของ SillyMUD นั้นสะดวกยิ่งขึ้น)

การสมัครรับข้อมูล

การรวม CloudI สมัครรับข้อมูลรูปแบบชื่อบริการหลักสามรูปแบบเพื่อจัดการการเชื่อมต่อ ยกเลิกการเชื่อมต่อ และเหตุการณ์การเล่นเกม รูปแบบชื่อเหล่านี้มาจากการเรียก C CloudI API การสมัครสมาชิกในซอร์สโค้ดการรวม ดังนั้น การเชื่อมต่อ WebSocket หรือการเชื่อมต่อ Telnet มีปลายทางของชื่อบริการสำหรับส่งคำขอบริการเมื่อมีการสร้างการเชื่อมต่อ

การสนับสนุน WebSocket และ Telnet ใน CloudI ให้บริการโดยบริการ CloudI ภายใน ( cloudi_service_http_cowboy สำหรับการสนับสนุน WebSocket และ cloudi_service_tcp สำหรับการสนับสนุน Telnet) เนื่องจากบริการ CloudI ภายในเขียนด้วยภาษา Erlang จึงสามารถใช้ประโยชน์จากความสามารถในการปรับขนาดสูงสุดของ Erlang ในขณะเดียวกันก็ใช้ CloudI service abstraction ที่มีฟังก์ชัน CloudI API

ดำเนินต่อไป

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

การนำการจัดการซ็อกเก็ตระดับต่ำออกช่วยแก้ไขปัญหาความสามารถในการปรับขนาดหลัก

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

MUD ดีขึ้นมากน้อยแค่ไหน?

ด้วยการผสานการทำงานกับ CloudI จำนวนการเชื่อมต่อที่ปรับขนาดตามลำดับความสำคัญสามระดับ ในขณะที่ให้ความทนทานต่อข้อผิดพลาดและเพิ่มประสิทธิภาพของการเล่นเกมเดิมแบบเดิม

การปรับปรุงมีสามด้านหลัก:

  1. ความทนทานต่อความผิดพลาด ด้วยการผสานรวมบริการ SillyMUD CloudI ที่ทันสมัย ​​การแยกข้อผิดพลาดของซ็อกเก็ตและเวลาแฝงจากซอร์สโค้ด SillyMUD ทำให้เกิดระดับความทนทานต่อข้อผิดพลาด
  2. ความสามารถในการขยายการเชื่อมต่อ ด้วยการใช้บริการ CloudI ภายใน การจำกัดผู้ใช้พร้อมกันของ SillyMUD สามารถเปลี่ยน จากผู้ใช้ 64 (ตามประวัติ) เป็น 16,384 รายได้อย่างง่ายดาย (โดยไม่มีปัญหาเวลาแฝง!)
  3. ประสิทธิภาพและประสิทธิภาพ ด้วยการ จัดการการเชื่อมต่อ ภายใน CloudI แทนที่จะเป็นซอร์สโค้ด SillyMUD แบบเธรดเดียว ประสิทธิภาพของซอร์สโค้ดการเล่นเกม SillyMUD จึงได้รับการปรับปรุงอย่างเป็นธรรมชาติและสามารถรองรับโหลดที่สูงขึ้นได้

ดังนั้น ด้วยการผสานรวม CloudI อย่างง่าย จำนวนการเชื่อมต่อจะถูกปรับขนาดตามลำดับความสำคัญสามขนาด ในขณะที่ให้ความทนทานต่อข้อผิดพลาดและเพิ่มประสิทธิภาพของการเล่นเกมเดิมแบบเดิม

ภาพใหญ่ขึ้น

Erlang ให้เวลาทำงาน 99.99999999% (เวลาหยุดทำงานน้อยกว่า 31.536 มิลลิวินาทีต่อปี) สำหรับระบบการผลิต ด้วย CloudI เรานำความเชื่อถือได้แบบเดียวกันนี้มาใช้กับภาษาและระบบการเขียนโปรแกรมอื่นๆ

นอกเหนือจากการพิสูจน์ความเป็นไปได้ของแนวทางนี้ในการปรับปรุงซอร์สโค้ดของเซิร์ฟเวอร์เกมดั้งเดิมที่หยุดนิ่ง (SillyMUD ได้รับการแก้ไขล่าสุดเมื่อ 20 ปีที่แล้วในปี 1993!) โปรเจ็กต์นี้แสดงให้เห็นในระดับที่กว้างขึ้นว่า Erlang และ CloudI สามารถใช้ประโยชน์จากการปรับปรุงแอปพลิเคชันรุ่นเก่าให้ทันสมัยได้อย่างไรและทำให้เกิดข้อผิดพลาด -ความอดทน ปรับปรุงประสิทธิภาพ และความพร้อมใช้งานสูงโดยทั่วไป ผลลัพธ์เหล่านี้มีศักยภาพที่ดีในการปรับโค้ดดั้งเดิมให้เข้ากับศตวรรษที่ 21 โดยไม่ต้องยกเครื่องซอฟต์แวร์ครั้งใหญ่