Từ PHP sang Golang sẽ như thế nào?
Xem tiktok nhiều quá nên cũng phải đặt cái tiêu đề cho giống mấy lời mở bài của tiktoker. Đặt tiêu đề là Golang
nhưng tên chính thức phải là Go
nhé, mình sẽ giải thích thêm trong nội dung của bài đọc ở bên dưới. Xin chào anh em thiện lành đang làm PHP
, mọi người vẫn ổn cả chứ? Phải nói là trước thời kỳ bùng nổ của AI, chưa bao giờ học một ngôn ngữ mới lại dễ dàng như thời điểm này, bạn không nhất thiết cần một trainer để hướng dẫn bạn vì chính AI
đã trở thành trợ thủ đắc lực của mình. Chính vì vậy, mình nghĩ đã đến lúc học hỏi thêm một ngôn ngữ mới để có thể dễ dàng sống với nghề ở giai đoạn này, Go
là ngôn ngữ lập trình mình chọn.
1. Tại sao lại là Go
PHP
có nhiều tiện ích và phát triển rất nhanh. Ngay cả khi bạn biết về CakePHP
, chỉ cần định nghĩa cơ sở dữ liệu là gần như đã hoàn thành xong tính năng CRUD
. Nó sẽ tạo ra model
, controller
, và view
chỉ với một lệnh. Vì vậy, PHP
phù hợp với các hệ thống quản lý, các trang tin tức hoặc hệ thống về CRM
, vì thời gian phát triển nhanh cũng như nguồn lực dồi dào giúp cho các công ty tiết kiệm được nhiều chi phí.
Tuy nhiên, việc bảo trì thì rất khó khăn và trong hệ thống có TPS
cao, nó trở thành một thảm họa. Trong trải nghiệm thực tế khi làm việc với PHP
, mình rất hiếm khi có cơ hội được làm việc với các dự án lớn, một phần vì hiệu suất làm việc của PHP
không được đánh giá cao nên rất hiếm khi Product Owner
chọn PHP
. Chính vì vậy, khả năng cao là anh em làm PHP
hiếm có cơ hội được các kỹ thuật, công cụ đang là trending hiện nay như Kafka
, RabbitMQ
, gRPC
hoặc cũng ít cơ hội được làm việc với microservice
. Cũng không thường xuyên được tiếp xúc với các tình huống thực tế xảy ra với các hệ thống lớn, tối ưu khi CCU
tăng đột biến...
Mình cần một cái gì đó dễ dàng để học, nhanh và đáng tin cậy. Go
là một trong những ngôn ngữ mình từng thấy. Nó siêu nhỏ và siêu nhanh. Nhưng bạn cần phải có kiến thức sâu về kỹ thuật phần mềm và các design patterns
.
Khi viết docker-compose
cho PHP
, nó sẽ dài dòng và khó khăn, nhưng trong Golang
chỉ cần 10 dòng. Và volume
của service microservice
bằng Golang
ít hơn 200 MB, trong khi container PHP
có thể lên đến 2GB.
Go
có cơ chế Goroutine
, concurrency
khiến Go đặc biệt mạnh trong các tình huống đòi hỏi hiệu suất cao. PHP
rất mạnh trong mô hình MVC
, nhưng nếu bạn không muốn sử dụng mô hình này thì sao? Và cũng rất khó để làm việc với gRPC
trong PHP
. Trong Golang
, mỗi microservice
có một phong cách khác nhau và dựa trên yêu cầu của khác nhau, cấu trúc của chương trình sẽ được thay đổi.
Ở phần tiếp theo, mình sẽ giới thiệu sâu về Go
, lịch sử phát triển cũng như hình thành của ngôn ngữ này. Cũng như những đặc điểm giúp Go
lại đang được ưa chuộng như hiện nay.
2. Go Overview
2.1. Nguồn gốc
Câu chuyện về Go
bắt đầu vào nửa cuối năm 2007 tại Google
. Ba người đàn ông từ Google
- Robert Griesemer
, Rob Pike
và Ken Thompson
- đã cố gắng giải quyết một số vấn đề kỹ thuật mà họ gặp phải. Google
, do quy mô hoạt động của mình, đối mặt với những vấn đề kỹ thuật khác biệt so với nhiều tổ chức khác. Các tiêu chí lựa chọn của bộ ba để xác định ngôn ngữ lập trình nào sử dụng xoay quanh ba yếu tố chính: biên dịch hiệu quả, thực thi hiệu quả và dễ lập trình.
Mặc dù họ đã đánh giá nhiều ngôn ngữ hiện có, nhưng không tìm thấy ngôn ngữ nào đáp ứng đủ ba yếu tố chính của họ. Chính vì lý do đó dẫn đến việc họ nghĩ đến việc tạo ra một ngôn ngữ lập trình mới.
Thiết kế bắt đầu vào tháng 9 năm 2007 như một dự án 20% tại Google
. Khi mọi thứ tiến triển, nhiều người khác cũng gia nhập. Vào tháng 1 năm 2008, công việc trên trình biên dịch ban đầu bắt đầu. Ngôn ngữ được mã nguồn mở sau đó vào tháng 11 năm 2009. Phải mất thêm vài năm nữa để phát hành phiên bản ổn định đầu tiên của Go
vào tháng 3 năm 2012. Vậy là câu chuyện về Go
bắt đầu và tiếp tục từ đó đến nay. Một lần nữa, respect
3 ông chú đã tạo ra một ngôn ngữ rất dễ để học tập, nhất là đối với một anti-fan của OOP
như mình.
Linh vật
Nói về Go
, chúng ta không thể bỏ qua linh vật của ngôn ngữ lập trình này có tên Gopher
. Điều thú vị là phiên bản đầu tiên của linh vật này đã được tạo ra bởi Renee French
từ trước khi Go
ra đời. Sau đó, linh vật này đã được điều chỉnh để phù hợp với dự án Go
. Gopher
nổi tiếng đến mức người ta tạo ra rất nhiều phiên bản khác nhau như Gopher fix bug
, Gopher bụng to
để ám chỉ các anh em IT ngồi nhiều. Càng tìm hiểu thêm về con này khiến mình rất thú vị để mức để cả avatar FB nhân vật này để nhắc nhở thay vì dùng FB thì hãy ngồi tìm hiểu thêm về Golang
đi.
Go
hay Golang
?
Ngôn ngữ Go
thường được gọi là Golang
, nhưng tên chính thức của ngôn ngữ là Go
như mình đã đề cập ở đầu của bài viết. Có lẽ bởi vì nó được phát triển bởi kỹ sư tại Google
nên họ lấy luôn Go
trong Google
để đặt tên cho ngôn ngữ lập trình này. Bạn có thể thắc mắc tại sao đôi khi nó lại được gọi là Golang
? Khi đến thời điểm đăng ký tên miền cho Go
, các nhà sáng lập gặp khó khăn trong việc có được tên miền chỉ với từ "go". Vì vậy, họ đã chọn golang.org
(Go language), điều này dẫn đến việc nhiều người gọi ngôn ngữ này là Golang
. Ở thời điểm hiện tại, tên miền go.org
đang được chào bán với giá "rất rẻ", khoảng gần 2 triệu đô trên các trang rao bán tên miền nổi tiếng, không biết các nhà sáng lập của Go
có định đầu tư không =)))
2.2. Go
hướng tới điều gì?
Khi Go
đang trong giai đoạn thiết kế, các nhà phát triển đã có một số mục tiêu cụ thể. Những mục tiêu này được xác định dựa trên những gì họ muốn đạt được và học hỏi từ các hạn chế của các ngôn ngữ khác mà họ đã đánh giá.
Đơn giản
Trước hết, mục tiêu là tạo ra một ngôn ngữ đơn giản với cú pháp dễ hiểu và ngắn gọn. Giảm số lượng từ khóa là một trong những cách để đạt được điều này. Hệ thống kiểu dữ liệu nhẹ nhàng, không có phân cấp kiểu dữ liệu, là mong muốn để giảm độ phức tạp. Cách tách biệt giữa interface
và implementation
được cho là giúp đơn giản hóa hơn. Composition over inheritance
là một lựa chọn ưu tiên.
Khả năng mở rộng
Hỗ trợ cho một ngôn ngữ lập trình có khả năng hỗ trợ scale
lớn là một trong những mục tiêu chính của Go
.
System scale: Cần có sự hỗ trợ tốt cho concurrency
và cơ chế giao tiếp giữa các tiến trình hiệu quả cùng với tốc độ biên dịch.
Engineering scale: Mục tiêu là phát triển một ngôn ngữ dành cho codebases lớn, được viết và duy trì bởi các team, dự án đông thành viên tham gia.
Hiệu quả lập trình
Khi viết code, sự dễ dàng trong lập trình là một trong những khía cạnh mong muốn, tương tự như các ngôn ngữ như JavaScript
hoặc Python
. Nhưng về mặt hiệu quả, sự ưu tiên là có cái gì đó giống như các ngôn ngữ statically-typed
như C
, C++
hoặc Java
.
An toàn
An toàn kiểu dữ liệu: Việc an toàn về kiểu dữ liệu là rất quan trọng. Đối với một số ngôn ngữ lập trình, đôi khi phát sinh các lỗi về kiểu dữ liệu trên các môi trường khác nhau.
An toàn bộ nhớ: Không ai thích các vấn đề về bộ nhớ. Do đó, khả năng xử lý bộ nhớ một cách an toàn cũng là điều quan trọng.
2.3 Go
có những ưu điểm gì
Các mục tiêu thiết kế đã đề ra nền tảng cho các tính năng của Go
. Vậy, Go
sẽ mang lại cho chúng ta những ưu điểm gì?
Đối với lập trình viên
Cú pháp đơn giản, ngắn gọn: Ngôn ngữ có cú pháp đơn giản và ngắn gọn, với chỉ 25 keyword
. Ít từ khóa đồng nghĩa với việc giảm độ phức tạp trong thời gian chạy. Cú pháp cơ bản khá giống với C
, với một số ảnh hưởng từ Pascal
, Modula
và Oberon
cho các khai báo và packages
.
Built-in concurrency (Nói về tính đồng thời): Các cấu trúc concurrency
tích hợp cho phép anh em coder viết ứng dụng dạng multi-threaded
dễ dàng hơn. Go
sử dụng một phương pháp khác gọi là goroutines
. Giao tiếp và đồng bộ giữa các goroutines
được xử lý thông qua một cơ chế có kiểu gọi là channels
; Hỗ trợ tích hợp cho maps
, arrays
và slices
cũng cải thiện trải nghiệm lập trình.
Đối với cơ sở hạ tầng
Ngôn ngữ lập trình Go
cũng biên dịch như Java
, nhưng không như Java
phải cần Java Virtual Machine
để thực thi vì Java
biên dịch ra Byte code
, Go
biên dịch ra mã máy (Machine code
) nên có thể chạy ngay với hệ điều hành nó biên dịch ra mà không cần cài đặt gì thêm. Có nghĩa là từ máy tính của mình, có thể biên dịch ra các chương trình chạy trên Mac
, Windows
, Linux
, sau khi biên dịch, chỉ cần 1 file duy nhất, copy đến hệ điều hành đích là chạy, rất đơn giản. Đây là tính năng ấn tượng nhất, bao gồm cả việc build rất nhanh.
3. Khác biệt khi từ PHP chuyển qua Go
Khi chọn ngôn ngữ lập trình cho sản phẩm/dự án tiếp theo của bạn, việc so sánh với các ngôn ngữ khác là rất quan trọng. Mình xuất phát điểm từ một dev PHP, khi bắt đầu làm quen với Go thực sự gặp rất nhiều khó khăn. PHP là một ngôn ngữ hỗ trợ lập trình hướng đối tượng, có một framework phổ biến là Laravel
, được support từ một cộng đồng đông đảo. Trong khi Go không phải ngôn ngữ hướng đối tượng, mình phải làm quen với khá nhiều keyword mới như struct
, slice
, con trỏ
... Mình sẽ tổng hợp lại một số điều khác biệt mà các bạn nên biết khi bắt đầu chuyển qua ngôn ngữ này.
3.1. Cấu trúc dự án
Khi làm việc với các framework của PHP, chúng ta chỉ việc clone thư mục dự án đã được build sẵn, ví dụ như Laravel, khi khởi tạo project chúng ta đã có sẵn thư mục router
, views
, controller
... Việc đơn giản là viết đúng logic code vào thư mục/file đã được định nghĩa từ trước. Ví dụ như khai báo router trong route/web.php
. Tuy nhiên, khi chuyển sang Go, mình gặp một chút bối rối về việc làm sao để chia thư mục, tổ chức code cho hợp lí. Hiện tại, không có tiêu chuẩn chính xác nào cho việc cấu trúc các thư mục và dự án.
Trong Go, có nhiều gợi ý về cấu trúc dự án. Ban đầu, việc quyết định cấu trúc nào là phù hợp đối với mình khá khó khăn. Cuối cùng, mình đã tham khảo các dự án phát triển API để follow theo cấu trúc dự án. Các bạn có thể tham khảo qua ở github này. Hiện tại repo trên đã có khoảng 48k sao, chứng minh việc cấu trúc này được rất nhiều người ủng hộ.
structor-api
- cmd/
- api/
- pkg/
- api/
- db/
- services/
- user
- product
- ...
- go.mod
- go.sum
- .env.<environment>
- README.md
Chúng ta có 3 thư mục cơ bản trong cấu trúc này: api
, pkg
và services
. Việc tách biệt các service theo resource giúp dễ dàng xác định mục đích khi thực hiện các thay đổi trong tương lai. Chúng ta không cần phải làm phức tạp hóa các service.
api/: Chúng ta xác định cách kết nối các API thông qua các services DB, HTTP routing và các config service mà chúng ta sử dụng trong các file service. Thông thường, chúng ta có một phương thức Load(c Config) duy nhất được gọi từ cmd/api/main.go
.
db/: Thư mục này chứa mọi thứ liên quan đến cơ sở dữ liệu. Nó đảm bảo kết nối thành công đến cơ sở dữ liệu. Ngoài ra, các bạn có thể bổ sung thêm các thư mục liên quan đến migrations
, seeders
trong này.
services/: Chúng ta sử dụng thư mục services để xử lý các resources
. Resources
ở đây có nghĩa là các bảng trong cơ sở dữ liệu. Ví dụ, nếu chúng ta cần lấy dữ liệu từ các bảng liên quan như users
hoặc products
. Trong tương lai, chúng ta sẽ tạo một thư mục mới gọi là “repository” và sẽ gọi các phương thức từ “services” đến “repository” cho các loại phương thức này.
Ngoài ra, nếu bạn là fan cuồng của Laravel, mình xin giới thiệu thêm 1 repo có tên là Goravel. Repo này clone toàn bộ thư mục của Laravel nhưng viết bằng ngôn ngữ Go, cách này giúp các bạn mới có thể làm quen với Go một cách nhanh chóng nhờ việc ánh xạ logic của Laravel sang Goravel và ngược lại.
3.2. Thông dịch và biên dịch
Go được chuyển đổi trực tiếp thành mã máy mà bộ xử lý có thể thực thi. Do đó, các chương trình Go thường nhanh hơn và hiệu quả hơn so với các ngôn ngữ được thông dịch. Go yêu cầu một bước "build" (biên dịch). Chúng ta cần phải "rebuild" (biên dịch lại) chương trình mỗi khi cần thực hiện thay đổi. Trình biên dịch được sử dụng cho tất cả các hệ điều hành. Các biến môi trường GOOS
và GOARCH
được thiết lập để chỉ định mục tiêu mong muốn.
PHP chạy qua một chương trình từng dòng một và thực thi từng lệnh. Trong khi đó, Go cần phải được biên dịch. Vì vậy, việc làm quen với điều này ban đầu rất khó khăn và gây khó khăn trong quá trình phát triển. Một lợi thế lớn của code được thông dịch là tất cả bộ nhớ được sử dụng bởi script được PHP quản lý, và ngôn ngữ tự động dọn dẹp sau khi mỗi script kết thúc. Điều này có nghĩa là bạn không cần phải lo lắng về việc đóng liên kết cơ sở dữ liệu, giải phóng bộ nhớ vì PHP sẽ làm điều đó cho bạn. Tuy nhiên, với Go, chúng ta cần thay đổi suy nghĩ này.
3.3. Kiểu dữ liệu
PHP là một ngôn ngữ kiểu động, bạn không cần phải khởi tạo biến. Chúng ta có thể sử dụng một biến khi cần mà không phải khởi tạo trước. PHP đã thay đổi cách tiếp cận này với PHP 7. Tuy nhiên, nó vẫn hoạt động theo cách tương tự.
<?php
$words = "this is a string";
$words = $foo + 2;
echo $words;
?>
Go là một ngôn ngữ kiểu tĩnh, vì vậy tất cả các biến đều có kiểu dữ liệu được gán cho chúng. Các biến trong function được gán bằng cách sử dụng toán tử :=
và toán tử này sẽ tự động thiết lập kiểu dữ liệu của biến cho bạn.
company := "Beyn"
// Hoặc phải dùng từ khóa var
var city string
city = "İstanbul"
3.4. Dependencies
Nếu bạn đã làm việc với PHP, chắc hẳn bạn đã nghe qua về cái tên Composer
. PHP không có tích hợp công cụ quản lí package
, vì vậy chúng ta phải sử dụng Composer
như một công cụ quản lí. Điều này gây ra một số phiền hà khi nếu muốn cài đặt project sử dụng PHP, thường thì sẽ phải cài đặt composer
. Có lẽ Go đã nhận thấy đây là một nhược điểm nên họ đã tích hợp công cụ quản lí package nên không cần cài đặt thêm bất kì công cụ nào nữa.
Nếu như trong PHP danh sách các dependencies được định nghĩa trong composer.json
, thì go.mod
là nơi định nghĩa các dependencies. Trong PHP có composer.lock
, thì Go cũng có file tương tự được đặt trên go.sum
.
3.5. Designing the Code
Sau vài năm làm việc với PHP
, chúng ta thường coi OOP
như một tiêu chuẩn lập trình, điều này hơi khó khăn một chút khi chuyển qua Go. Điều đầu tiên để học Go là phải quên hết khái niệm về class
, object
đi, vì Go sử dụng struct
và interface
. Phải thừa nhận mình mất khoảng vài tuần mới làm quen được với sự thay đổi này. Tuy nhiên, theo hướng tích cực thì tạm thời chúng ta cũng không cần phải cố gắng hiểu 4 tính chất của hướng đối tượng.
Một điểm khác nhau nữa là PHP và Go là khả năng xử lý bất đồng bộ. Với PHP, tất cả đều được xử lý đồng thời, điều này giúp code của chúng ta rõ ràng và dễ hiểu hơn, tuy nhiên nhược điểm là hiệu suất không cao, các yêu cầu phải chờ để được xử lý. Đây có lẽ cũng là phần khác biệt lớn nhất giữa PHP và Go, Go hỗ trợ xử lý đồng thời thông qua goroutines
. Các bạn có thể tìm hiểu thêm về cơ chế này, vì nó khá phức tạp và dài để truyền tại trong khuôn khổ bài viết này.
3.6. Error Handling
Go không cung cấp một cách đơn giản như try-catch
để xử lý lỗi giống như PHP. Thay vào đó, lỗi được trả về như một giá trị trả về thông thường. Ví dụ, nếu chúng ta cần xử lý lỗi khi kết nối với cơ sở dữ liệu:
<?php
$servername = "localhost";
$username = "username";
$password = "password";
try {
$conn = new PDO("mysql:host=$servername;dbname=myDB", $username, $password);
// set the PDO error mode to exception
$conn->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
echo "Connected successfully";
} catch(PDOException $e) {
echo "Connection failed: " . $e->getMessage();
}
Thay vào đó, các function
và method
trong Go trả về hai kết quả, bao gồm kết quả của phương thức và một kiểu lỗi. Nếu lỗi bằng nil
, thì việc thực thi function/method
được coi là thành công. Nếu lỗi không phải là nil, có nghĩa là đã xảy ra sự cố
và chúng ta có thể bỏ qua kết quả.
f, err := os.Open("filename.ext")
if err != nil {
log.Fatal(err)
}
// do something with the open *File f
Điều này đôi khi cũng gây ra một chút khó khăn cho mình vì với mỗi logic gọi tới function, thường mình phải kiểm tra lỗi một lần. Tuy nhiên, không nhất thiết 100% phải như vậy. Tuy nhiên, việc không sử dụng try-catch
khiến code dễ đọc hơn, dễ debug hơn và hiệu năng cũng cao hơn.
4. Tổng kết
Trong bài viết này, mình đem đến cho các bạn cái nhìn tổng thể khi chuyển từ PHP sang Go. Mình cũng không có ý định so sánh 2 ngôn ngữ lập trình này vì concept của chúng đã khác nhau. Phải nói thật là mình đã định học Go từ vài năm về trước nhưng luôn sợ sệt mấy ngôn ngữ lập trình phải sử dụng con trỏ, có lẽ vì ám ảnh từ hồi đại học. Hiện tại mình cũng mới làm qua vài dự án với Go, cũng gọi là có chút nghề thôi chứ vẫn cần phải học thêm rất nhiều. Vì vậy, trong bài viết có nhiều quan điểm cá nhân, hoặc các khái niệm có thể sai, mong anh em chỉ giáo thêm.
Tham khảo:
- Golang Official Website
- Go Language Specification
- Go Memory Model
- Go FAQ
- Go Users Wiki
- Less Is Exponentially More
- Learn Go - Part 1: The Beginning
- Our Journey Moving from PHP to Go
Đọc những bài viết khác của tác giả: Chillwithsu.com
Donate cho tác giả : Buy me a coffee
Chúc các bạn code vui, khỏe, giải trí !!!
All Rights Reserved