Life of a Go Developer @ KBTG

Jirawan.C
KBTG Life
Published in
3 min readNov 8, 2022

วันนี้ผู้เขียนจะมาเล่าประสบการณ์การใช้ Go ทำโปรเจคให้กับทาง KBTG ในบทบาทของ Developer รวมถึงความท้าทายในงานที่ได้รับผิดชอบ สิ่งที่เจอเมื่อทำงานกับข้อมูลจริง โดยเฉพาะเมื่อต้องรับมือกับธุรกรรมจำนวนมหาศาล เพื่อเป็นความรู้ให้กับเพื่อนๆ กันค่ะ

Go Programming Language คืออะไร

ภาษา Go เป็นภาษาโปรแกรมคอมไพล์แบบสแตติกที่ออกแบบโดย Google กล่าวคือ ก่อน Source Code จะถูกคอมไพล์ ชนิดของข้อมูลจะต้องถูกประกาศมาเรียบร้อย ยกตัวอย่างเช่น

package mainimport ("log")func main() {n := 10log.Println(n) // out: 10}

จะเห็นว่าตัวแปร n เป็นชนิดข้อมูล int ดังนั้นไม่สามารถเปลี่ยน n ให้เป็น String ได้ สิ่งนี้ทำให้ Go เป็นภาษาที่ทำงานได้อย่างรวดเร็วและมี Syntax ที่เรียบง่าย เรียนรู้ได้ไม่ยากและมี Built-in Testing Module ออกแบบมาเพื่อให้ง่ายต่อการเขียนชุดทดสอบการทํางานของโปรแกรม รวมถึง Built-in Concurrency (Goroutines and Channels) ที่ช่วยให้ทำ Concurrency ได้ง่ายๆ เพียงแค่สร้างฟังก์ชันแล้วใส่ Go ฟังก์ชันนั้นก็ทำงานเป็นอิสระจากฟังก์ชันหลักได้แล้ว

การทำงานในทีม

ปัจจุบันทีมมีการนำ Agile Methodology มาปรับใช้ ทุกๆ 2 สัปดาห์ในทีมจะนำงานเข้ารอบการทำงานหรือที่เรียกว่า Sprint มีการประเมินความสามารถในการทำงานของคนในทีม ประเมินความสำคัญของงานแต่ละงาน จัดลำดับงานที่ต้องการทำก่อน-หลัง และวางแผนงานในอนาคต หลังจากที่วางแผนเสร็จเรียบร้อย Dev ก็เริ่มทำงานได้ ซึ่งงานในทีมส่วนใหญ่จะทำเกี่ยวกับ RESTful API ส่วนงานที่เป็น Batch Processing ก็มีนะ

RESTful Web Service

หลายคนอาจจะเคยได้ยินหรือเคยมีประสบการณ์เกี่ยวกับการเขียนเว็บมาบ้าง โดย Go มีหลาย Web Framework เช่น

ที่เราเคยใช้ในการทำโปรเจคจะมี Gin, Echo และ Gorilla จะเลือกใช้ Framework ไหน ก็ขึ้นอยู่กับความเหมาะสมและการตกลงกันในทีม สร้าง API ให้ตอบรับกับความต้องการของธุรกิจ และต้องออกแบบให้ปลอดภัยด้วย

Writing a RESTful Web Service API with Go

การสร้าง API Docs ถือเป็นส่วนสำคัญในการทำงานที่ไม่ควรละเลย การสื่อสารและการทำเอกสารเป็นสิ่งที่ช่วยให้ทีมและระหว่างทีมเรากับทีมอื่นเข้าใจได้ตรงกัน แต่ Dev ก็ไม่ได้อยากมาเขียนเอกสารให้ยุ่งยาก ทีมจึงเลือกเขียน API Docs ด้วย Source Code ซะเลย ก่อนหน้านี้จะเขียน KBMF โดยใช้ Excel ทั้งหมด เขียนอธิบายว่าแต่ละ Field คืออะไร ส่งและรับข้อมูลชนิดใด ตัวอย่าง Request and Response รูปร่างเป็นอย่างไร แต่พอ API เปลี่ยนแปลง ก็ต้องมาแก้ไขเอกสารเหล่านี้อีก ด้วยเหตุนี้ทีมตัดสินใจเอา go-swagger มาเขียน API Docs สำหรับ Endpoint ใหม่ๆ แต่การเปลี่ยนแปลงนี้ก็ทำเอาคนในทีมต้องปรับตัวและปรับวิธีอ่านกันใหม่พอสมควร

เขียนให้โปรแกรมทำงานถูกต้องนั้นสำคัญ แต่ทำงานกับธนาคาร ต้องรับมือกับธุรกรรมจำนวนมหาศาล ถ้าโปรแกรมรับรองกับ Data จำนวนมากไม่ได้ก็เปล่าประโยชน์!!!

เดือนที่ผ่านมาผู้เขียนได้ทำโปรเจคหนึ่งที่ต้องทำงานกับ Batch Processing จัดการกับธุรกรรมต่อวันจำนวนมหาศาลค่ะ สิ่งที่ท้าทายคือเมื่อระบบ Go Live ขึ้นไปแล้ว ต้องสามารถทำงานกับข้อมูลเหล่านั้นได้โดยที่ไม่พังไปซะก่อน โดยปกติเราจะรับ Requirement มาจาก BA จากนั้นก็หา Test Case และไปเขียนโค้ดให้รองรับกับแต่ละ Case ให้ได้ หลังจากเขียนโปรแกรมให้ทำงานได้ถูกต้องแล้ว มาถึงจุดที่ต้องทำ Load Testing

Load Testing: เป็น Process สำหรับการวัด Response Time ในการทำงานของระบบที่อยู่ภายใต้การใช้งานรูปแบบต่างๆ

หลังจากหาจุดที่จะทำการทดสอบได้ เตรียมข้อมูลให้เรียบร้อย แล้วมาดูผลการทดสอบกัน

ในรูปตัวอย่างนี้ จะเห็นว่า Memory เหลือน้อยมาก CPU ก็ Peek มากเช่นกัน สุดท้ายแล้วเกิด Memory Leak

Memory Leak: ระบบมีการใช้ Memory อย่างฟุ่มเฟือย เพราะ Variable ที่กำหนดในระบบจะถูกเก็บไว้ที่ RAM มีการใช้งานและไม่มีการลบทิ้งหรือคืนค่า ทำให้ CPU ทำงานช้าลง เพราะ Memory ถูกใช้งานไปเรื่อยๆ

ปัญหา CPU และ Memory Utilization เกือบ 100% ตลอด ส่งผลให้ระบบทำงานช้าลงและโปรแกรมไม่สามารถทำงานได้จนจบ (โดน Kill)

แก้ไขปัญหาอย่างไร

ปกติแล้วเวลา Service มีปัญหา เรามักจะ Debug โดยการ Log Error Message แต่เรื่องนี้น่าปวดหัวมาก เพราะ Memory Leak ไม่ Log Error Message ออกมาตรงๆ ทำให้การ Debug ทำยากขึ้น วิธีแก้ปัญหาคือต้องไปไล่โค้ดดูทีละฟังก์ชันว่าตรงจุดไหนที่สามารถเกิดขึ้นได้บ้าง ทำการลองแก้และทดสอบใหม่ ถึงตรงนี้แล้วแต่บุญกรรมแล้วค่ะ ว่าโค้ดที่เขียนมาโหดร้ายมากแค่ไหน (ฮา) ถ้าเจอโค้ดที่เขียนทุกอย่างลงไปแบบถึกๆ ก็จะลำบากหน่อย แต่ถ้าก่อนหน้านี้มีการดีไซน์หรือแบ่งฟังก์ชันหน้าที่รับผิดชอบออกมาดี ชีวิตจะดีขึ้นนิดนึง

นี่คือสิ่งที่เจอได้บ่อยๆ เมื่อต้องทำงานกับข้อมูลจำนวนมากจนเกิด Memory Leak

  • ใช้งาน Go Routine แบบ Unlimited: ใช้ Channel มาจัดการจำนวน Go Routine
  • เปิด Connection ทิ้งไว้: defer close() ทุกครั้ง
  • เก็บ Cached ไว้ในตัวแปรที่ละเยอะๆ: เก็บแค่ที่จะใช้ ที่ไม่ใช้ก็ Remove ทิ้ง
  • ประกาศ List แบบไม่ระบุ Size: ระบุ Size vals := make([]int, 0, 5)
  • Bad Design จนเกิด Variable has escaped to the heap memory แบบไม่จำเป็น

นอกจากนี้ยังมี Go Profiling Tool ที่เป็นเครื่องมือช่วยดู Memory Leaked, เช็ค Goroutines และ CPU Usage

นี่เป็นเพียงหนึ่งในความคิดเห็นและประสบการณ์ที่ท้าทายในการทำงาน ให้เราได้เรียนรู้ปัญหาที่เข้ามาและพยายามหาทางแก้ไข Lessons Learned กันไป สิ่งที่ได้จากการทำงานที่ KBTG นอกจากจะได้เจอปัญหาท้าทายความสามารถให้มันส์มือในการแก้ไขแล้ว ที่นี่ยังมีหลักสูตรพัฒนาความรู้เกี่ยวกับ Golang แบบฟรีๆ เพียบ และยังมี Go Guild สุดคูลที่รวบรวม Go Developer อัพเดตข่าวสารใหม่ๆ และคอยช่วยเหลือกันอยู่ตลอดอีกด้วย

หากคุณชอบบทความนี้ รบกวนกด “Claps” 👏 เพื่อเป็นกำลังใจให้ผู้เขียนผลิตบทความต่อไปด้วยนะคะ หรือหากเพื่อนๆ มีเรื่องราวหรือประสบการณ์สนุกๆ สามารถแบ่งปันกันได้เลย ขอบคุณค่ะ

References & More Info

สำหรับชาวเทคคนไหนที่สนใจเรื่องราวดีๆแบบนี้ หรืออยากเรียนรู้เกี่ยวกับ Product ใหม่ๆ ของ KBTG สามารถติดตามรายละเอียดกันได้ที่เว็บไซต์ www.kbtg.tech

--

--

Jirawan.C
KBTG Life

Hi, My name's JUGJIG. I working as a software engineer @Fintech company. I’m Go developer since 2020. I’m English learner. Nice to see you