Fullstack Station https://fullstackstation.com Hướng dẫn lập trình, thiết kế, lập trình web, thiết kế web, lập trình javascript, lập trình fullstack từ cơ bản đến nâng cao Sun, 05 May 2024 01:08:00 +0000 vi hourly 1 https://wordpress.org/?v=6.4.5 https://fullstackstation.com/wp-content/uploads/2019/08/favicon.ico Fullstack Station https://fullstackstation.com 32 32 Python: Sự cần thiết của MyPy https://fullstackstation.com/gioi-thieu-mypy-python/ https://fullstackstation.com/gioi-thieu-mypy-python/#respond Wed, 24 Apr 2024 08:24:43 +0000 https://fullstackstation.com/?p=1948 Nhân việc người tạo ra Python, ông Guido van Rossum giới thiệu về MyPy, mình sẽ nói kỹ thêm về sự hữu dụng và sự cần thiết của MyPy trong lập trình Python. Bạn có thể tải về slide của ông Guido van Rossum ở cuối bài. Mình đã giới thiệu MyPy trong bài viết […]

The post Python: Sự cần thiết của MyPy appeared first on Fullstack Station.

]]>
Nhân việc người tạo ra Python, ông Guido van Rossum giới thiệu về MyPy, mình sẽ nói kỹ thêm về sự hữu dụng và sự cần thiết của MyPy trong lập trình Python. Bạn có thể tải về slide của ông Guido van Rossum ở cuối bài.

Mình đã giới thiệu MyPy trong bài viết “Giới thiệu một số công cụ hỗ trợ lập trình Python“, bài này sẽ đề cập chi tiết hơn về MyPy. Bất kỳ ngôn ngữ nào cũng phải trải qua quá trình dài để phát triển. Việc thiếu một vài tính năng ngay từ đầu cũng không có gì lạ, vì nguyên tắc thiết kế của Python là đặt sự đơn giản lên hàng đầu.

The Zen of Python (import this):

  • Beautiful is better than ugly.
  • Explicit is better than implicit.
  • Simple is better than complex.
  • Complex is better than complicated.
  • Flat is better than nested.
  • Sparse is better than dense.
  • Readability counts.
  • Special cases aren’t special enough to break the rules.
  • Although practicality beats purity.
  • Errors should never pass silently.
  • Unless explicitly silenced.
  • In the face of ambiguity, refuse the temptation to guess.
  • There should be one– and preferably only one –obvious way to do it.
  • Although that way may not be obvious at first unless you’re Dutch.
  • Now is better than never.
  • Although never is often better than right now.
  • If the implementation is hard to explain, it’s a bad idea.
  • If the implementation is easy to explain, it may be a good idea.
  • Namespaces are one honking great idea — let’s do more of those!

Tuy nhiên, sự cần thiết của kiểm tra kiểu dữ liệu là rất quan trọng, dù nó đã không được hỗ trợ ngay từ lúc ban đầu. Hoặc cũng không là bắt buộc ở hiện tại. Không biết ông Guido hối hận gì không vì đã không đưa type-checking vào Python trước đây :D.

MyPy là gì

Ngày nay, nhiều công cụ để kiểm tra kiểu dữ liệu, nhưng MyPy trước đây được tạo ra bởi anh JukkaL, thì bây giờ đã được đưa chính thức vào chung với Python: https://github.com/python/mypy. Đó là một sự thừa nhận sự phát triển và trưởng thành của MyPy.

Type Annotation

Type checking (kiểm tra kiểu dữ liệu) là xác định rõ kiểu dữ liệu của biến, giúp phát hiện lỗi sớm hơn trong quá trình phát triển. MyPy là công cụ được sử dụng để kiểm tra các lỗi liên quan đến loại dữ liệu dựa trên type annotations.

Ví dụ:

def add(x, y):    
    return x + y

add("hai", "ba")
=> "haiba" # Kiểu string
add(2, 3)
=> 5 # Kiểu integer

add("hai", 3)
hoặc 
add(2, "ba")
=> chúng ta sẽ bị lỗi `TypeError` lúc Runtime

Vậy thì với cách viết thêm kiểu dữ liệu như dưới đây:

def add(x: int, y: int) -> int:    
    return x + y

Khi dùng MyPy để kiểm tra, thì chúng ta sẽ phát hiện ngay được lỗi trong lúc lập trình mà không sợ sẽ bị lỗi lúc runtime nữa. Python khá dễ viết nếu mã nguồn đơn giản, ngắn gọn và còn mới. Nhưng khi mã nguồn dự án trải qua thời gian dài, và lượng code phình to ra thì nếu không có xác định, kiểm tra loại dữ liệu thì sẽ gặp rất nhiều khó khăn khi debug hoặc khó hiểu khi đọc code.

Type T

Sử dụng với các kiểu dữ liệu

Kiểu dữ liệu cơ bản

Bạn có thể dùng tất cả các kiểu dữ liệu cơ bản để diễn giải kiểu dữ liệu như int, float, str, dict, tuple, set, list…, hoặc kết hợp như Tuple[int, str], Iterator[int]…

def gcd(a: int, b: int) -> int:
    while a:
        a, b = b % a, a
    return b

Ở đây mình dùng VSCode, đưa trỏ chuột vào b sẽ biết b có kiểu dữ liệu gì

Hay với ví dụ của trang chủ mypy:

from typing import Iterator

def fib(n: int) -> Iterator[int]:
    a, b = 0, 1
    while a < n:
        yield a
        a, b = b, a + b

Loại biến gọi hàm (Callable)

from typing import Callable

def twice(i: int, next: Callable[[int], int]) -> int:    
    return next(next(i))

def add(i: int) -> int:
    return i + 1

print(twice(3, add))  # 5

Với ví dụ trên, bạn có thể xác định biến đưa vào có thể gọi hàm, rất tường minh.

Kiểu dữ liệu nâng cao

Sử dụng MyPy tuy dễ nhưng cũng cần thời gian để thành thạo, bạn có thể cải thiện Type-Annotation từng bước nhỏ một. Không nhất thiết phải làm 1 lúc cho toàn bộ mã nguồn của dự án. Như vậy sẽ không bị ngán và lười.

Tuy nhiên với dự án mới thì hãy sử dụng Type-Annotation càng nhiều càng tốt. Vừa lập trình vừa có trợ lý MyPy thật là sướng biết bao.

https://mypy.readthedocs.io/en/latest/

MyPy cho Django: https://github.com/machinalis/mypy-django

The post Python: Sự cần thiết của MyPy appeared first on Fullstack Station.

]]>
https://fullstackstation.com/gioi-thieu-mypy-python/feed/ 0
Bắt kịp xu hướng AI với serverless AI https://fullstackstation.com/serverless-ai-la-gi/ https://fullstackstation.com/serverless-ai-la-gi/#respond Mon, 22 Apr 2024 11:07:40 +0000 https://fullstackstation.com/?p=8108 Cách đây vài năm khi xu hướng serverless chỉ mới chớm thì giờ đây đã trưởng thành và có nhiều nền tảng sử dụng serverless như là cơ sở hạ tầng chính. Và càng quan trọng hơn khi AI nổi lên khiến cho giá GPU ngày càng trở nên đắt đỏ, thì serverless AI lại […]

The post Bắt kịp xu hướng AI với serverless AI appeared first on Fullstack Station.

]]>
Cách đây vài năm khi xu hướng serverless chỉ mới chớm thì giờ đây đã trưởng thành và có nhiều nền tảng sử dụng serverless như là cơ sở hạ tầng chính. Và càng quan trọng hơn khi AI nổi lên khiến cho giá GPU ngày càng trở nên đắt đỏ, thì serverless AI lại trở thành mối quan tâm mới. Bài viết này sẽ giúp cho bạn bước vào thế giới AI một cách dễ dàng hơn.

Khái niệm Serverless AI

Serverless là gì

Nếu bạn là người mới về Serverless thì có thể đọc thêm bài Serverless là gì để hiểu rõ thêm. Nói đơn giản, Serverless giống như môi trường thực thi một tác vụ nào đó, ví dụ như API, mà bạn chỉ cần triển khai xong là có thể sử dụng được. Bạn không cần quan tâm đến các hạ tầng, cấu hình mạng, bộ nhớ…

Điểm mạnh của Serverless là giúp cho bạn tập trung vào giải quyết vấn đề, hơn là dành thời gian cho những tác vụ không mấy vui vẻ như cập nhật hệ thống, kiểm tra khả năng lưu trữ server, kiểm tra nhật ký hệ thống (log) xem có ai tấn công không…Quan trọng hơn hết là chỉ trả tiền cho những khi mình sử dụng!

Serverless AI là gì

Serverless AI là hạ tầng máy chủ có bộ xử lý đồ họa GPU mạnh mẽ, có thể tích hợp sẵn các model AI (Llama2, Llama3, GPT3, ResNes, StableDiffusion, Whisper…)

Tại sao Serverless AI rất quan trọng cho những người nghiên cứu AI

Chi phí, chính xác là điều chúng ta quan tâm nhất đối với serverless AI. Để đầu tư một hệ thống GPU mạnh mẽ thường sẽ tốn khá nhiều chi phí ban đầu, cũng như chi phí vận hành thường xuyên. Hoặc nếu dùng cloud, bạn cũng sẽ tốn nhiều chi phí khác để vận hành, và mất thêm thời gian để quản lý. Với serverless AI, bạn có thể chỉ trả tiền cho những lúc chúng ta gọi API.

Về cơ bản thì serverless AI cũng là API được sử dụng như API để tương tác với OpenAI chẳng hạn, và OpenAI thì đã tích hợp sẵn một số model như GPT-3, GPT-4

Một số nhà cung cấp

Cloudflare

Website: https://ai.cloudflare.com/

Danh sách các model

Text Generation @cf/meta/llama-3-8b-instruct

Text Generation @cf/mistral/mistral-7b-instruct-v0.1

Text to Image @cf/bytedance/stable-diffusion-xl-lightning

Speech Recognition @cf/openai/whisper

Image Classification @cf/microsoft/resnet-50

Text Classification @cf/huggingface/distilbert-sst-2-int8

Text Embedding @cf/baai/bge-base-en-v1.5

Translation @cf/meta/m2m100-1.2b

Runpods

Website: https://www.runpod.io/serverless-gpu

Fermyon

Website: https://www.fermyon.com/

Fullstack Station Tips

Với CloudFlare là nhà cung cấp hạ tầng khá nổi tiếng, với việc hỗ trợ nhiều model và cách sử dụng khá là dễ dàng. Chỉ cần tạo token key là bạn có thể thao tác được với các model trong CloudFlare. CloudFlare có gói miễn phí dùng thử khá hấp dẫn, được khởi tạo lại theo ngày, chứ không phải theo tháng. Nên với các ứng dụng đơn giản, thì hoàn toàn có thể sử dụng trên CloudFlare miễn phí

Với Serverless AI, bạn có thể nhanh chóng tìm hiểu và kiểm tra tính thực tiễn của các model. Thực nghiệm các ý tưởng để bắt xu hướng AI nhanh hơn!

The post Bắt kịp xu hướng AI với serverless AI appeared first on Fullstack Station.

]]>
https://fullstackstation.com/serverless-ai-la-gi/feed/ 0
Ngôn ngữ lập trình Mojo🔥: Python++ nhanh hơn Python tùy theo tác vụ đến 68.000 lần https://fullstackstation.com/ngon-ngu-lap-trinh-mojo%f0%9f%94%a5-python-nhanh-hon-python-tuy-theo-tac-vu-den-68-000-lan/ https://fullstackstation.com/ngon-ngu-lap-trinh-mojo%f0%9f%94%a5-python-nhanh-hon-python-tuy-theo-tac-vu-den-68-000-lan/#respond Sat, 13 Apr 2024 15:00:04 +0000 https://fullstackstation.com/?p=8047 Ngôn ngữ lập trình Mojo dù chỉ mới ra mắt chưa được 1 năm, nhưng đến nay đã nhận được sự chú ý của cộng đồng lập trình viên và nhận nhiều phản hồi tích cực. Dù tiếp xúc chưa được lâu, nhưng với những gì cảm nhận được thì đây là một ngôn ngữ […]

The post Ngôn ngữ lập trình Mojo🔥: Python++ nhanh hơn Python tùy theo tác vụ đến 68.000 lần appeared first on Fullstack Station.

]]>
Ngôn ngữ lập trình Mojo dù chỉ mới ra mắt chưa được 1 năm, nhưng đến nay đã nhận được sự chú ý của cộng đồng lập trình viên và nhận nhiều phản hồi tích cực. Dù tiếp xúc chưa được lâu, nhưng với những gì cảm nhận được thì đây là một ngôn ngữ lập trình đáng được quan tâm trong vòng 10 năm tới.

Bài viết này sẽ tập trung vào việc đo hiệu suất của ngôn ngữ lập trình Mojo, để xem thực tế chính xác đến đâu nhé. Bạn cũng đừng bỏ qua bài viết Tại sao nên học Python nhé.

Sơ lược về ngôn ngữ lập trình Mojo

Ngôn ngữ lập trình Mojo là gì

Ngôn ngữ lập trình Mojo được phát triển bởi công ty Modular, có cú pháp tương tự như Python và đạt hiệu cao hơn Python nhiều lần như C. Ban đầu họ hướng đến ngôn ngữ lập trình dành cho lập trình AI, nhưng ở thời điểm hiện tại thì đã trở thành ngôn ngữ lập trình cho những mục tiêu chung.

Ngôn ngữ lập trình Mojo khá dễ học do tương đồng với ngôn ngữ lập trình Python và được xem là Python++ nên sẽ giúp rút ngắn khoảng cách của những người nghiên cứu AI (thường sử dụng Python) đến phát triển sản phẩm (Python khá chậm và đòi hỏi nhiều tối ưu để sản phẩm đạt hiệu suất tốt)

Những đặc tính của ngôn ngữ lập trình Mojo

  • Sử dụng được các thư viện Python (numpy, pandas, …): sử dụng thông Cython nên có thể dùng được hầu hết các thư viện này. Tuy nhiên, theo kết quả đo hiệu suất bên dưới, thì việc sử dụng các thư viện này không mang lại kết quả tốt về hiệu suất mong muốn.
  • Sử dụng được cú pháp Python: Bạn hoàn toàn có thể sử dụng code Python trong Mojo, tuy nhiên việc sử dụng Python trong Mojo không nên ưu tiên nếu quan tâm đến hiệu suất.
  • Cú pháp tương tự Python: phong cách viết tương tự Python, tuy nhiên thuần Mojo thì sẽ khắt khe trong type-checker
  • Chạy đa luồng: Mojo sử dụng hạ tầng biên dịch (MLIR) để hỗ trợ nhiều loại phần cứng, bao gồm GPU chạy CUDA và phần cứng tương tự, mà không làm tăng thêm sự phức tạp trong lập trình.

Những điểm còn thiếu sót của ngôn ngữ lập trình Mojo

  • Chưa hỗ trợ Mac Intel, Window và các hệ Linux khác ngoài Ubuntu
  • Còn xa để đạt được mức tập cha (superset) của Python
  • Rất ít các thư viện hỗ trợ
  • Chưa có dependencies management: làm gì có thư viện khác mà đòi quản lý :))

Bạn có thể xem những vấn đề đang tồn tại: https://docs.modular.com/mojo/roadmap#mojo-sdk-known-issues

Cha đẻ của Mojo: Chris Latter

Thật thiếu sót nếu không nói về cha đẻ của ngôn ngữ lập trình Mojo: Chris Latter – người cũng là tác giả của trình biên dịch LLVM. Đồng thời ông cũng là người tạo ra MLIR – compiler stack thế hệ tiếp theo, và Mojo được thiết kế để biên dịch trên MLIR, đó cũng có thể là lý do vì sao Mojo nhanh hơn Rust (Rust và Swift được xây dựng trên nền tảng LLVM). Người sáng lập ngôn ngữ lập trình cũng là người tham gia vào quá trình thiết kế compiler, vì vậy ông rất am hiểu về cách vận hành của các chip cũng như trình biên dịch tương ứng. Đó cũng là ưu thế rất lớn để phát triển ngôn ngữ lập trình Mojo trở thành ngôn ngữ của thập niên tiếp theo – đặc biệt là trong mảng lập trình AI.

So sánh hiệu suất với Python

Môi trường

Công cụ được sử dụng đo hiệu suất: Hyperfine (https://github.com/sharkdp/hyperfine)
Hàm sử dụng: Fibonacci (thuật toán không tối ưu)
Cấu hình máy chính: 2.4 GHz 8-Core Intel Core i9
Cấu hình docker: RAM 8GB, max 200% CPU

Cài đặt Mojo

Cài đặt ngôn ngữ lập trình Mojo chỉ mới dành cho MacOS Apple, và Ubuntu. Với các máy khác có thể sử dụng docker. Chi tiết cho từng phiên bản thì bạn tham khảo ở đây:

https://docs.modular.com/mojo/manual/get-started/

Việc cài đặt cũng không mất nhiều thời gian và khó khăn gì cả nên mình không bổ sung gì thêm.

Code

# fibonacci.mojo
# Fibonacci thuần mojo
import sys
fn fibonacci(n: Int) -> Int:
    if n == 0 or n == 1:
        return n
    else:
        return fibonacci(n - 2) + fibonacci(n - 1)


fn main():
    try:
        print(fibonacci(atol(sys.argv()[1])))
    except:
        pass
# fibonacci_python_style_no_type_annotation.mojo
# Fibonacci không  type-annotation với code kiểu Python
import sys
def fibonacci(n):
    if n == 0 or n == 1:
        return n
    else:
        return fibonacci(n - 2) + fibonacci(n - 1)

fn main():
    try:
        print(fibonacci(atol(sys.argv()[1])))
    except:
        pass
# fibonacci_python_style_with_type_annotation.mojo
# Fibonacci  type-annotation với code kiểu Python
import sys
def fibonacci(n: Int) -> Int:
    if n == 0 or n == 1:
        return n
    else:
        return fibonacci(n - 2) + fibonacci(n - 1)

fn main():
    try:
        print(fibonacci(atol(sys.argv()[1])))
    except:
        pass
# fibonacci.py
# Fibonacci không  type-annotation (Python)
import sys

def fibonacci(n):
    if n == 0 or n == 1:
        return n
    else:
        return fibonacci(n - 2) + fibonacci(n - 1)

def main():
    print(fibonacci(int(sys.argv[1])))

main()
vscode/workspaces/ubuntu $ hyperfine "mojo fibonacci.mojo 40"
Benchmark 1: mojo fibonacci.mojo 40
  Time (mean ± σ):     700.7 ms ±  16.8 ms    [User: 431.9 ms, System: 19.5 ms]
  Range (minmax):   677.3 ms735.1 ms    10 runs
 
vscode/workspaces/ubuntu $ hyperfine "mojo fibonacci_python_style_with_type_annotation.mojo 40"
Benchmark 1: mojo fibonacci_python_style_with_type_annotation.mojo 40
  Time (mean ± σ):      1.202 s ±  0.012 s    [User: 0.927 s, System: 0.021 s]
  Range (minmax):    1.184 s1.217 s    10 runs
 
vscode/workspaces/ubuntu $ hyperfine "mojo fibonacci_python_style_no_type_annotation.mojo 40"
Benchmark 1: mojo fibonacci_python_style_no_type_annotation.mojo 40
  Time (mean ± σ):     15.129 s ±  3.435 s    [User: 14.793 s, System: 0.059 s]
  Range (minmax):   10.228 s19.135 s    10 runs
 
vscode/workspaces/ubuntu $ hyperfine "python3 fibonacci.py 40"
Benchmark 1: python3 fibonacci.py 40
  Time (mean ± σ):     44.586 s ±  3.859 s    [User: 44.502 s, System: 0.033 s]
  Range (minmax):   37.771 s51.637 s    10 runs
 
vscode/workspaces/ubuntu $ hyperfine "./fibonacci 40"
Benchmark 1: ./fibonacci 40
  Time (mean ± σ):     350.3 ms ±   5.5 ms    [User: 346.5 ms, System: 2.4 ms]
  Range (minmax):   340.9 ms358.6 ms    10 runs
 
vscode/workspaces/ubuntu $ hyperfine "./fibonacci_python_style_no_type_annotation 40"
Benchmark 1: ./fibonacci_python_style_no_type_annotation 40
  Time (mean ± σ):     10.667 s ±  0.872 s    [User: 10.650 s, System: 0.007 s]
  Range (minmax):    9.323 s11.830 s    10 runs
 
vscode/workspaces/ubuntu $ hyperfine "./fibonacci_python_style_with_type_annotation 40"
Benchmark 1: ./fibonacci_python_style_with_type_annotation 40
  Time (mean ± σ):     831.7 ms ±  11.5 ms    [User: 827.4 ms, System: 2.5 ms]
  Range (minmax):   814.2 ms852.2 ms    10 runs

Kết quả so sánh

MinMaxMeanHiệu suất
Python thuần37.771 (s)51.637(s)44.586(s)N/A
Mojo phong cách Python10.228(s)19.135(s)15.129(s)x3
Mojo phong cách Python có type-annotation1.184(s)1.217(s)1.202(s)x37
Mojo thuần677.3(ms)735.1 (ms)700.7(ms)x75
Mojo phong cách Python [compiled]9.323(s)11.830(s)10.667(s)x3
Mojo phong cách Python có type-annotation [compiled]814.2(ms)852.2(ms)831.7(ms)x55
Mojo thuần [compiled]340.9(ms)358.6(ms)350.3(ms)x127
Hiệu suất thực tế của ngôn ngữ lập trình Mojo theo từng cách viết so sánh với Python.

Bonus

Với phiên bản Python thuần, nếu bạn tối ưu bằng decorator cache thì tốc độ cũng cải thiện rất nhiều, tuy nhiên Mojo chưa hỗ trợ decorator này nên chưa so sánh được.

import sys
from functools import cache

@cache
def fibonacci(n):
    if n == 0 or n == 1:
        return n
    else:
        return fibonacci(n - 2) + fibonacci(n - 1)

def main():
    print(fibonacci(int(sys.argv[1])))

main()
vscode/workspaces/ubuntu $ hyperfine "python3 fibonacci_with_cache.py 40"
Benchmark 1: python3 fibonacci_with_cache.py 40
  Time (mean ± σ):      14.0 ms ±   1.1 ms    [User: 11.6 ms, System: 2.2 ms]
  Range (minmax):    12.7 ms19.3 ms    159 runs
 

Kết luận

Nhanh hơn Python 68.000 lần?

Without a parallel Python implementation, It would be more fair to claim that Mojo is 874 times faster than a naive CPython implementation, 175 times faster than a (rather naive) Numpy code, and 40 times faster than a PyPy implementation (on this specific Mandelbrot set computation).

StackOverflow

Mặc dù con số 68.000 lần là thực tế từ kết quả, tuy nhiên đây chỉ là con số mang tính chất biểu tượng/marketing là chính chứ không có tác dụng trong thực tế nhiều lắm.

  • Mojo là ngôn ngữ multi-threaded chứ không có chạy tuần tự, nên một số tác vụ tính toán có thể hưởng lợi
  • Sử dụng tính toán vector, lại là về tính toán con số; numpy có thực hiện tính toán vector nên vẫn ổn
  • Sử dụng chiến thuật thực thi song hành (parallel) trên con CPU khủng 88-Core Intel Xeon

Bạn có thể đọc thêm cách đo hiệu suất trong 3 bài viết từ blog của Modular có gắn kèm bên dưới.

Bài toán thực tế

Với kết quả từ thực nghiệm ở trên với hàm Fibonacci (một hàm thường được dùng cho việc tính hiệu suất vì có dùng đệ quy), có thể kết luận việc sử dụng ngôn ngữ lập trình Mojo có thể nhanh hơn Python khoảng 100 lần. Đây là con số khá hấp dẫn trong việc chọn lựa với những hệ thống yêu cầu tốc độ. Với những người yêu thích tốc độ như mình thì khó có thể cưỡng lại được 😀

Tốc độ đến từ ngôn ngữ biên dịch và type-checked cũng là điều dễ hiểu, ngoài ra kết hợp tính toán vector, multi-thread thì ngôn ngữ lập trình Mojo có thể còn phát huy được nhiều ứng dụng khác. Về điểm này thì chúng ta nên so sánh với Golang mới đúng, hi vọng sẽ có thời gian để viết bài so sánh với Golang.

Fullstack Station Tips:

  • Không khuyến khích dành cho người mới học lập trình, khuyến khích dành cho người đã biết Python
  • Mặc dù có thể sử dụng cú pháp Python, nhưng với kết quả kiểm tra hiệu suất thì rõ ràng không nên sử dụng cú pháp Python nếu muốn có hiệu suất cao, chí ít cũng phải có type-annotation (xem thêm MyPy là gì). Tốt nhất cũng là sử dụng ngôn ngữ lập trình Mojo thuần để cho tốt độ cao nhất.
  • Tuy tuổi đời còn ít, nhưng với những thành tựu đã đạt được thì ngôn ngữ lập trình Mojo sẽ còn phát triển nhanh và xa. Đặc biệt là rút ngắn khoảng cách từ nghiên cứu đến phát triển sản phẩm AI.
  • Vì chưa trưởng thành, nên nhiều cú pháp sẽ bị thay đổi, không nên sử dụng ngôn ngữ lập trình cho sản phẩm thực tế.
  • Với những lợi ích mà ngôn ngữ lập trình Mojo mang lại, mình nghĩ sẽ rất có lợi cho bạn ở tương lai gần khi học Mojo. Giống như lập trình viên Golang, là một ngôn ngữ lập trình cho bạn mức thu nhập rất tốt.

Tham khảo:

  • https://www.modular.com/blog/how-mojo-gets-a-35-000x-speedup-over-python-part-1
  • https://www.modular.com/blog/how-mojo-gets-a-35-000x-speedup-over-python-part-2
  • https://www.modular.com/blog/mojo-a-journey-to-68-000x-speedup-over-python-part-3
  • https://dev.classmethod.jp/articles/try-mojo-programming-langurage/
  • https://stackoverflow.com/questions/77070883/performance-comparison-mojo-vs-python
  • https://augierpi.gricad-pages.univ-grenoble-alpes.fr/mojo-the-point-of-view-of-a-researcher-using-python.html

The post Ngôn ngữ lập trình Mojo🔥: Python++ nhanh hơn Python tùy theo tác vụ đến 68.000 lần appeared first on Fullstack Station.

]]>
https://fullstackstation.com/ngon-ngu-lap-trinh-mojo%f0%9f%94%a5-python-nhanh-hon-python-tuy-theo-tac-vu-den-68-000-lan/feed/ 0
Dùng CI:GithubAction để phát hiện toàn bộ câu truy vấn N+1 trong Laravel https://fullstackstation.com/dung-cigithubaction-de-phat-hien-toan-bo-cau-truy-van-n1-trong-laravel/ https://fullstackstation.com/dung-cigithubaction-de-phat-hien-toan-bo-cau-truy-van-n1-trong-laravel/#respond Wed, 06 Mar 2024 11:53:19 +0000 https://fullstackstation.com/?p=7923 Nếu bạn không chắc hệ thống của mình đang tồn tại bao nhiêu câu truy vấn có vấn đề về N+1 Query thì bài viết này dành cho bạn. Đặc biệt dành cho các bạn quản lý dự án, team leader. Đã sử dụng Laravel thì có lẽ mọi người cũng biết đến laravel-query-detector, một […]

The post Dùng CI:GithubAction để phát hiện toàn bộ câu truy vấn N+1 trong Laravel appeared first on Fullstack Station.

]]>
Nếu bạn không chắc hệ thống của mình đang tồn tại bao nhiêu câu truy vấn có vấn đề về N+1 Query thì bài viết này dành cho bạn. Đặc biệt dành cho các bạn quản lý dự án, team leader.

Đã sử dụng Laravel thì có lẽ mọi người cũng biết đến laravel-query-detector, một thư viện nho nhỏ để phát hiện N+1 Query. Tuy nhiên, vì nhiều lý do thì trong quá trình phát triển, chúng ta chưa chú ý giải quyết vấn đề “N+1 Query” này. Đến một lúc nào đó, khi lượng truy vấn nhiều lên (mức sử dụng database) thì vấn đề N+1 Query lại trở thành chủ đề cần giải quyết càng sớm càng tốt.

Câu truy vấn N+1 (N+1 Query) là gì

Nói ngắn gọn là Laravel sinh ra nhiều câu query hơn mức cần thiết. Chính xác hơn là 1+N, tức là khi ta truy vấn 1 query, mà query đó có quan hệ 1-nhiều (1-n), nhiều-nhiều (n-n) sẽ làm nảy sinh thêm n (nhiều) câu query khác. Lý do là Laravel cho phép chúng ta viết code cho model dễ đọc, mà không cần phải hiểu cách hoạt động đằng sau của chúng.

Tất nhiên, vấn đề này nảy sinh không phải chỉ do Eloquent hay chỉ đối với mỗi mình Laravel, mà trong cả ngành công nghiệp lập trình khi mà chúng ta sử dụng các framework vì sự tiện dụng của chúng.

Trong nội dung bài viết này, sẽ không đề cập đến cách giải quyết vấn đề này trong Laravel, bạn có thể tìm kiếm trên các bài viết khác nhé.

Phát hiện câu truy vấn có vấn đề N+1 Query

Khi sử dụng laravel-query-detector, bạn sẽ phát hiện N+1 Query và dựa theo cấu hình tương ứng để nắm thông tin N+1 Query.

Trong từng API (dùng clockwork):

Trong từng màn hình (dùng cấu hình Alert):

Trong từng màn hình (sử dụng debugbar, kết hợp cấu hình Log):

Danh sách các thiết lập thì bạn có thể xem thêm tại đây: https://beyondco.de/docs/laravel-query-detector/usage


    /*
     * Define the output format that you want to use. Multiple classes are supported.
     * Available options are:
     *
     * Alert:
     * Displays an alert on the website
     * \BeyondCode\QueryDetector\Outputs\Alert::class
     *
     * Console:
     * Writes the N+1 queries into your browsers console log
     * \BeyondCode\QueryDetector\Outputs\Console::class
     *
     * Clockwork: (make sure you have the itsgoingd/clockwork package installed)
     * Writes the N+1 queries warnings to Clockwork log
     * \BeyondCode\QueryDetector\Outputs\Clockwork::class
     *
     * Debugbar: (make sure you have the barryvdh/laravel-debugbar package installed)
     * Writes the N+1 queries into a custom messages collector of Debugbar
     * \BeyondCode\QueryDetector\Outputs\Debugbar::class
     *
     * JSON:
     * Writes the N+1 queries into the response body of your JSON responses
     * \BeyondCode\QueryDetector\Outputs\Json::class
     *
     * Log:
     * Writes the N+1 queries into the Laravel.log file
     * \BeyondCode\QueryDetector\Outputs\Log::class
     */
    'output' => [
        \BeyondCode\QueryDetector\Outputs\Log::class,
        \BeyondCode\QueryDetector\Outputs\Alert::class,
    ]

Hệ thống của bạn có bao nhiêu câu truy vấn có vấn đề N+1 Query?

Vì nhiều lý do, trong 1 dự án có nhiều người với peer-review yếu, hoặc chưa quan tâm lắm đến vấn đề N+1 Query, thì khả năng bỏ qua vấn đề N+1 Query khá là cao. Vì vậy đến một lúc nào đó khi bạn trở thành người quản lý dự án đó, chịu trách nhiệm nâng cao hiệu suất sử dụng database bạn sẽ rất vất vả để tìm ra các điểm nghẽn đó.

Ý tưởng cũng khá đơn giản: viết toàn bộ testcase cho tất cả router, khi chạy test thì sử dụng laravel-query-detector ghi log các câu truy vấn có vấn đề N+1 Query ra 1 file riêng, dùng các lệnh về xử lý file để tìm kiếm và thống kê.

Viết toàn bộ testcase cho tất cả router

Nếu bạn có viết testcase rồi thì tốt, không có thì cũng chỉ cần viết cái testcase đơn giản là gọi đến toàn bộ route rồi cho skip cũng được 😑. Làm sao đảm bảo là gọi hết đến tất cả các route thì mới phát hiện được hết được. Không có thời gian thì viết cho mấy cái route quan trọng cũng được 🥲

Cấu hình laravel-query-detector

php artisan vendor:publish --provider="BeyondCode\QueryDetector\QueryDetectorServiceProvider"

Ta sẽ có được file config/querydetector.php

...
/* Chú ý dòng này, nếu bạn có file phpunit.xml, cần thiết lập QUERY_DETECTOR_ENABLED=true */
'enabled' => env('QUERY_DETECTOR_ENABLED', null),
...
/* Tùy theo tính chất khắt khe mà đặt chốt chặn phù hợp */
'threshold' => (int) env('QUERY_DETECTOR_THRESHOLD', 1),
...
'log_channel' => env('QUERY_DETECTOR_LOG_CHANNEL', 'querydetector'),
...
'output' => [
      ...
        \BeyondCode\QueryDetector\Outputs\Log::class,
      ...
    ]

Chúng ta sẽ ghi log vào channel ‘querydetector’, vậy nên phải cấu hình channel trong file config/logging.php

'querydetector' => [
            'driver' => 'single',
            'path' => storage_path('logs/querydetector.log'),
            'level' => env('LOG_LEVEL', 'debug'),
            'days' => 14,
        ]

Như vậy toàn bộ kết quả của laravel-query-detector sẽ được ghi vào file storage/logs/querydetector.log

Thống kê

grep "Detected N+1 Query" storage/logs/querydetector.log | wc -l

Ok, như vậy là ta sẽ ra được 1 con số, được hiểu là số vấn đề N+1 Query đang tồn tại trong hệ thống. Con số này có thể không chính xác vì nhiều lý do:

  • Một route được viết testcase nhiều lần – nói 1 cách khác là được gọi nhiều lần.
  • Trong một route, số vấn đề N+1 Query là số nhiều

Nhưng nếu kết quả là số 0 thì chúc mừng bạn, hệ thống của bạn không có vấn đề (mặc dù còn phụ thuộc số lượng testcase)

Sử dụng CI: GithubAction để thống kê

Nếu bạn có sử dụng CI với GithubAction, thì với kết quả của phpUnit, bạn làm thêm 1 vài thao tác nữa sẽ được kết quả như thế này:

316 – con số thật khủng khiếp phải không nào :D, điều tuyệt vời là bạn đã biết được con số đó, và mỗi khi giải quyết N+1 Query được push lên, GithubAction sẽ comment con số cuối cùng.

File cấu hình ci.yml sẽ được chỉnh sửa thêm vào như sau:
//ci.yml
on: [ pull_request ]
jobs:
  phpUnit:
      - name: chạy phpunit
     .... "./vendor/bin/phpunit --stop-on-failure"
      - name: N+1 Query Report #Đọc kết quả querydetector, thống kê và lưu vào file n-1-query-summary.log
        run: |
          grep "Detected N+1 Query" storage/logs/querydetector.log | wc -l > storage/logs/n-1-query-summary.log
        working-directory: ${{env.working-directory}}
      - name: Read coverage summary #Đọc nội dung file n-1-query-summary.log
        id: n-1-query-summary
        uses: juliangruber/read-file-action@v1
        with:
          path: ./app/laravel/storage/logs/n-1-query-summary.log
      - name: Comment N+1 Query Summary # Comment kết quả vào PR
        uses: marocchino/sticky-pull-request-comment@v2
        with:
          recreate: true
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
          header: n-1-query
          message: |
            ## N+1 Query Summary
            Number of N+1 queries: ${{ steps.n-1-query-summary.outputs.content }}
      - name: Archive N+1 query detector results # Lưu log của querydetector vào Artifact của github
        uses: actions/upload-artifact@v2
        with:
          name: n+1-query-report
          path: ./app/laravel/storage/logs/querydetector.log

File querydetector.log sẽ được upload lên artifacts của Github

Như vậy ở góc độ quản lý dự án, bạn không cần phải chạy test trên máy của mình mà vẫn đảm bảo chất lượng dự án ngày càng cải thiện, ít nhất là phải biết hiện trạng của dự án.

Fullstack Station Tips

  • Khi bạn đã biết số vấn đề còn tồn tại , có thể đưa vào CI (ci.yml chẳng hạn) để đảm bảo con số đó không tăng lên khi phát triển thêm chức năng.
  • Có thể dựa vào danh sách file có vấn đề N+1 Query trong querydetector.log để so sánh với danh sách file được thêm/sửa. Nếu nằm trong querydetector.log thì không cho pass CI 🤣

The post Dùng CI:GithubAction để phát hiện toàn bộ câu truy vấn N+1 trong Laravel appeared first on Fullstack Station.

]]>
https://fullstackstation.com/dung-cigithubaction-de-phat-hien-toan-bo-cau-truy-van-n1-trong-laravel/feed/ 0
Tạo mật khẩu SMTP trong Amazon SES https://fullstackstation.com/tao-mat-khau-smtp-trong-amazon-ses/ https://fullstackstation.com/tao-mat-khau-smtp-trong-amazon-ses/#respond Wed, 17 Aug 2022 01:16:00 +0000 https://fullstackstation.com/?p=6853 Vào một ngày đẹp trời thiết lập gởi mail trong wordpress thì hỡi ôi làm mãi không gởi email đi được. Mãi mới xác định được là cách tạo xác thực trước đây cho SMTP bằng mật khẩu của AWS đã thay bằng Access key ID/AWS secret access key. Với cặp Access key ID/AWS secret […]

The post Tạo mật khẩu SMTP trong Amazon SES appeared first on Fullstack Station.

]]>
Vào một ngày đẹp trời thiết lập gởi mail trong wordpress thì hỡi ôi làm mãi không gởi email đi được. Mãi mới xác định được là cách tạo xác thực trước đây cho SMTP bằng mật khẩu của AWS đã thay bằng Access key ID/AWS secret access key.

Với cặp Access key ID/AWS secret access key thì sẽ được sử dụng cho Amazon SES API, nhưng các plugin dành cho wordpress thì lại không hỗ trợ, nếu có hỗ trợ thì phải sử dụng bản PRO mới có.

Vì vậy muốn sử dụng gởi email qua smtp bằng user/password thì phải “tạo mật khẩu smtp từ AWS secret access key

Bước 1: Tạo tập tin smtp_credentials_generate.py

Cứ copy rồi lưu lại thành tập tin là được, không cần chỉnh sửa gì thêm nhé.

#!/usr/bin/env python3

import hmac
import hashlib
import base64
import argparse

SMTP_REGIONS = [
    'us-east-2',       # US East (Ohio)
    'us-east-1',       # US East (N. Virginia)
    'us-west-2',       # US West (Oregon)
    'ap-south-1',      # Asia Pacific (Mumbai)
    'ap-northeast-2',  # Asia Pacific (Seoul)
    'ap-southeast-1',  # Asia Pacific (Singapore)
    'ap-southeast-2',  # Asia Pacific (Sydney)
    'ap-northeast-1',  # Asia Pacific (Tokyo)
    'ca-central-1',    # Canada (Central)
    'eu-central-1',    # Europe (Frankfurt)
    'eu-west-1',       # Europe (Ireland)
    'eu-west-2',       # Europe (London)
    'sa-east-1',       # South America (Sao Paulo)
    'us-gov-west-1',   # AWS GovCloud (US)
]

# These values are required to calculate the signature. Do not change them.
DATE = "11111111"
SERVICE = "ses"
MESSAGE = "SendRawEmail"
TERMINAL = "aws4_request"
VERSION = 0x04


def sign(key, msg):
    return hmac.new(key, msg.encode('utf-8'), hashlib.sha256).digest()


def calculate_key(secret_access_key, region):
    if region not in SMTP_REGIONS:
        raise ValueError(f"The {region} Region doesn't have an SMTP endpoint.")

    signature = sign(("AWS4" + secret_access_key).encode('utf-8'), DATE)
    signature = sign(signature, region)
    signature = sign(signature, SERVICE)
    signature = sign(signature, TERMINAL)
    signature = sign(signature, MESSAGE)
    signature_and_version = bytes([VERSION]) + signature
    smtp_password = base64.b64encode(signature_and_version)
    return smtp_password.decode('utf-8')


def main():
    parser = argparse.ArgumentParser(
        description='Convert a Secret Access Key for an IAM user to an SMTP password.')
    parser.add_argument(
        'secret', help='The Secret Access Key to convert.')
    parser.add_argument(
        'region',
        help='The AWS Region where the SMTP password will be used.',
        choices=SMTP_REGIONS)
    args = parser.parse_args()
    print(calculate_key(args.secret, args.region))


if __name__ == '__main__':
    main()

Chạy lệnh tạo mật khẩu

python path/to/smtp_credentials_generate.py wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY us-east-1

Sau khi chạy lệnh trên, chúng ta sẽ được 1 dãy ký tự mới, đó chính là mật khẩu STMP. Sử dụng Access key ID và mật khẩu được tạo ra ở trên để cài đặt trong các plugin gởi email bằng SMTP. Bên dưới là phần thiết lập cho plugin WP MAIL SMTP:

Tham khảo thêm các bài viết về Amazon SES:

https://docs.aws.amazon.com/ses/latest/dg/smtp-credentials.html

https://cuongthach.com/email-marketing/huong-dan-su-dung-amazon-ses-smtp-de-gui-email-tu-wordpress

https://vticloud.io/amazon-ses-la-gi-huong-dan-tong-hop-ve-dich-vu-amazon-ses/

The post Tạo mật khẩu SMTP trong Amazon SES appeared first on Fullstack Station.

]]>
https://fullstackstation.com/tao-mat-khau-smtp-trong-amazon-ses/feed/ 0
Tối ưu tốc độ tải trang: “mili giây tạo triệu đô $$$” https://fullstackstation.com/huong-dan-toi-uu-toc-do-tai-trang-pagespeed-testmysite/ https://fullstackstation.com/huong-dan-toi-uu-toc-do-tai-trang-pagespeed-testmysite/#respond Thu, 24 Sep 2020 02:23:51 +0000 https://fullstackstation.com/?p=2218 Đây là bài viết tổng hợp lại kiến thức thực tế tối ưu tốc độ mình đã áp dụng và có những thành quả nhất định trên website https://imitsu.jp (Một website B2B giới thiệu/đấu giá dự án hàng đầu Nhật Bản). Bên cạnh các phương pháp được áp dụng, bài viết cũng chứa đựng những […]

The post Tối ưu tốc độ tải trang: “mili giây tạo triệu đô $$$” appeared first on Fullstack Station.

]]>
Đây là bài viết tổng hợp lại kiến thức thực tế tối ưu tốc độ mình đã áp dụng và có những thành quả nhất định trên website https://imitsu.jp (Một website B2B giới thiệu/đấu giá dự án hàng đầu Nhật Bản). Bên cạnh các phương pháp được áp dụng, bài viết cũng chứa đựng những thách thức và khó khăn phải đối mặt. Hãy cùng Fullstack Station cải thiện tốc độ tải trang (pagespeed) nhé.

Theo góc nhìn của mình thì pagespeed là “tốc độ tải trang” đã bao gồm tốc độ phản hồi từ máy chủ luôn nhé. Tốc độ phản hồi từ máy chủ liên quan đến nhiều yếu tố khác không nằm trong phạm vi bài viết này. Còn Pagespeed nói riêng là những gì xảy ra ở trình duyệt web (browser).

Tại sao tối ưu tốc độ tải trang lại quan trọng?

Tốc độ kiểm tra tại TestMySite của trang https://imitsu.jp/matome/hp-design/

Người sử dụng website cảm thấy hạnh phúc” là tiêu chí cực kỳ quan trọng! Không những có giá trị trực tiếp lên người dùng mà còn ảnh hưởng đến xếp hạng tìm kiếm của Google. Bởi vì Google cũng muốn người dùng hạnh phúc với kết quả tìm kiếm. Nên những trang web có tốc độ nhanh sẽ được Google ưu ái xếp hạng. Việc cải thiện thứ hạng tìm kiếm trên Google là cái kết cực kỳ đáng giá. Điều đó đồng nghĩa với việc website sẽ được nhiều lượt truy cập hơn, việc kinh doanh sẽ trở nên tốt hơn.

Milliseconds Make Millions“, mili giây tạo triệu đô: cứ 0.1 giây cải thiện được thì tỉ lệ chuyển đổi (conversion rate) tăng 8%. Đây là con số không hề nhỏ, và nếu bạn thực hiện tối ưu tốc độ theo bài viết này, bạn sẽ cải thiện ít nhất 0.5 đến 1.5s, tương đương “tỉ lệ chuyển đổi” tăng từ 40%~80%. Woa!!!

Miliseconds Make Milliions

Chú ý: chạy theo thành tích về điểm số của Google sẽ đánh đổi khá nhiều thời gian và công sức. Và đó là một công việc khá dài hơi và có tính chu kỳ. Tuy nhiên, kết quả thì sẽ rất ngọt ngào!

Fullstack Station Tips

Cho dù bạn có làm nội dung và SEO tốt nhưng pagespeed lại chậm thì sẽ ảnh hưởng rất nhiều đến thứ hạng tìm kiếm, cũng như trải nghiệm người dùng. Là nguyên nhân dẫn đến lượng truy cập thấp. SEO thì tất nhiên quan trọng, nhưng đầu tư vào tối ưu tốc độ tải trang pagespeed thì hiệu quả lâu dài và chắc chắn có kết quả tốt.

Khó khăn và thách thức

Khó khăn

Một điều khá lạ nhưng cũng thường xảy ra là chẳng ai quan tâm đến pagespeed khi mới phát triển trang web cả. Chỉ là sau 1 thời gian thấy thứ hạng tìm kiếm không tăng nổi, hoặc là truy cập không đạt chỉ tiêu thì chủ doanh nghiệp mới bắt đầu lôi cổ phòng marketing ra, lúc đó để ý đến pagespeed.

Rất ít chủ quản website quan tâm đến pagespeed, đó chính là khó khăn cho các bạn làm marketing nói chung và lập trình viên nói riêng. Bởi vì các con số của pagespeed gần như không liên quan đến lượt truy cập và thứ hạng tìm kiếm? Hoặc là để đánh giá KPI thì rất khó khăn. Khi bạn cải thiện tốc độ lên, đồng thời thứ hạng tìm kiếm cũng lên, vậy kết quả là do chiến lược SEO, do Google thay đổi thuật toán tìm kiếm, hay do kết quả của pagespeed?

Xác định được quả thật là rất khó, tuy nhiên bạn hãy tin tưởng vào câu “Miliseconds Make Millions” do chính Google đưa ra. Và cũng có tiêu chí xác định ROI (Return On Investment): với các thông số Lượng truy cập, Tỉ lệ chuyển đổi, Giá trị trung bình đơn hàng thì TestMySite sẽ đưa ra con số doanh thu hàng năm (annual revenue) sẽ tăng lên bao nhiêu nếu bạn cải thiện được pagespeed bao nhiêu giây. Đây là phương pháp xác định con số cụ thể biết có xứng đáng đầu tư vào việc cải thiện pagespeed hay không.

Nhưng bạn phải quán triệt một điều là tốc độ tải trang không phải là chiếc đũa thần. Nếu chất lượng nội dung không tốt thì tốc độ tải trang sẽ không có ý nghĩa gì cả. Khi các trang có chất lượng bằng nhau, thì tốc độ tải trang, trải nghiệm người dùng sẽ được ưu tiên trên hệ thống tìm kiếm. Tóm lại, làm người sử dụng hạnh phúc là tôn chỉ cao nhất!

Thách thức

Như đã nói ở trên thì bạn thường bắt tay vào việc cải thiện pagespeed khi website đã đi vào hoạt động đã lâu. Và vì cũng phải có lượng truy cập tương đối khá (nhưng bắt đầu hụt hơi?) thì mới xác định được ROI mà nên thực hiện cải thiện pagespeed hay không. Với lý do đó mà tại thời điểm bạn bắt đầu cải thiện thì hệ thống website đã như 1 bãi chiến trường :)). Đó chính là thách thức.

Vì lý do tăng trưởng, nên thường thì website sẽ đặt mục tiêu về chức năng trước. Còn các mặt khác đều là thứ yếu. Tất nhiên đó là điều dễ hiểu vì nếu không có giai đoạn đó thì bạn sẽ không có cơ hội cải thiện pagespeed đâu. Tại thời điểm này, những quy tắc pagespeed đều chỉ là 1 thứ lý thuyết suông mà thôi. Bạn phải vắt óc suy nghĩ làm sao kết hợp các quy tắc đó vào hệ thống hiện tại.

Cải thiện pagespeed với công số thấp nhất mà hiệu quả cao nhất. Đó chính là “State of the art” mà bạn phải đối mặt giải quyết. Và đó cũng chính là điều mình hi vọng giúp được các bạn trong bài viết này.

Cách đơn giản để tối ưu tốc độ tải trang

Sử dụng mod_pagespeed chính chủ Google

Mình đã từng giới thiệu về mod_pagspeed để tăng tốc pagespeed cho website. Cách này sử dụng cho kết quả tốt mà lại dễ dàng. Tuy nhiên chỉ áp dụng với site tĩnh hoàn toàn. Các trang web có sử dụng form với crsf-token được tạo ra theo trang thì khó đạt kết quả như mong đợi.

Hơn nữa, khi áp dụng thì bạn gần như lệ thuộc hoàn toàn vào các thiết lập và cài đặt. Khó có thể tự quản lý các nhu cầu tuỳ biến cao. Mình nghĩ chỉ nên sử dụng đối với site nhỏ, hoặc site mới đang trong thời kỳ triển khai. Vì các tham số khá nhiều nên việc cấu hình khá khó khăn. Qua thời gian nâng cấp, các tập tin css và js được thêm vào nhiều thì rủi ro sẽ tăng lên. Bởi vì tự động nên cũng có khi có lỗi ngoài tầm kiểm soát.

Trang fullstackstation.com có kết quả 1.8s/Good trên testmysite cũng đang sử dụng mod_pagespeed. Mình khá hài lòng kết quả này. Tuy nhiên điểm trên Pagespeed Insights thì vẫn không được cao. Do Google mới thay đổi thuật toán tính điểm.

Sử dụng các framework/plugins hỗ trợ tốt về pagespeed

Plugins cho WordPress

Hiện tại Fullstack Station sử dụng wordpress nên mình dùng Autoptimize (nén js và css thành 1 file) và Hyper Cache (tạo cache trang thành html), kết hợp với mod_pagespeed. Về cơ bản thì những plugin cho wordpress chỉ giúp cải thiện ở một vài quy tắc. Để tối cải thiện tốc độ tải trang thì plugins là chưa đủ.

Có rất nhiều plugins cho wordpress cải thiện tốc độ nói chung, nhưng không nằm trong phạm vi bài viết này. Đơn giản nhất là chỉ cần sử dụng 2 plugins nói trên. Với các website doanh nghiệp, mình khuyến nghị nên dùng bộ lập trình của https://roots.io/. Sử dụng bộ của Roots để tạo theme cũng như quản lý các plugins 1 cách bảo mật. Khi bạn tự phát triển theme, thì sẽ dễ dàng quản lý các javascript và css hơn.

Các js framework

Với kỹ thuật code splitting, thì các framework hay thư viện hỗ trợ dựa trên webpack sẽ giúp tốc độ trang khá tốt. Cơ bản thì sẽ giúp trang tải css và js cần thiết cho việc render. Ngoài ra còn cải thiện tốc độ rất tốt với kỹ thuật tạo trang html tĩnh và động tương ứng.

Điển hình là các js framework như Gatsby.js, Ghost CMS, Next.js, Nuxt.js. Cơ chế trang tĩnh rất quan trọng, để đảm bảo tốc độ tốt nhất. Và cũng cần cơ chế tải từng phần như Gatsby để nâng cao trải nghiệm người dùng.

Các quy tắc đảm bảo tối ưu tốc độ tải trang tốt

Đơn giản (nhưng cũng đầy cam go) là bạn sử dụng Lighthouse ngay trên trình duyệt Chrome hoặc dùng Pagespeed Insights để kiểm tra và chẩn đoán từng trang. Các công cụ này sẽ đưa ra các khuyến nghị và chỉ cần làm theo là sẽ cải thiện được.

Mình cũng đã phải trải qua hàng trăm lần kiểm tra để đưa ra giải pháp tối ưu nhất cho website imitsu.jp. Và kinh nghiệm được rút ra như sau:

  • Tốc độ phản hồi máy chủ nhanh: mỗi request dưới 300ms cho 4G/mobile, 100ms cho desktop, đặc biệt là trang html (Doc).
  • Mỗi request (html, css, js, image): dưới 50kB cho mobile và hạn chế nhất có thể trên bản desktop.
  • Hạn chế số lượng request: dưới 35 requests, càng ít càng tốt
  • Cache Policy: nên để thời gian cache lâu (6 tháng~), khi bạn cần thay đổi thì thêm parameter ở URL hoặc, thay đổi tên file.
  • CDN: những file tĩnh nên sử dụng CDN để tăng hiệu quả tốc độ
  • Sử dụng lazyload: kỹ thuật này giúp browser không tải ảnh khi chưa sử dụng (chưa hiển thị trong viewport)
  • Tối ưu tải css và javascript: css ở head và javascript file thì ở dưới phần footer, inline phù hợp.
  • Sử dụng worker để xử lý các script, tính toán không cần thiết ở main thread.
  • Sử dụng loại định dạng ảnh mới như WebP, JPEG 2000, …

Trên đây là một số quy tắc chủ đạo xoay quanh các tiêu chí:

  • Nhanh nhất có thể
  • Nhỏ, nhẹ nhất có thể
  • Sử dụng đúng nơi, đúng chỗ (không thừa)

Tuy các quy tắc thì nhiều, nhưng tựu chung cũng tập trung cho các tiêu chí trên. Đảm bảo các tiêu chí trên thì dù Goole có thay đổi các tiêu chí đánh giá, thì điểm số vẫn sẽ cao.

Core web vitals

Tối ưu tốc độ tải trang với Core Web Vitals

Core web vitals sẽ là tiêu chí quan trọng ảnh hưởng đến xếp hạng tìm kiếm. Do dịch corona nên tiêu chí này được dời lại 1 năm. Năm 2021 sẽ áp dụng Core Web Vitals để đánh giá xếp hạng tìm kiếm. Tiêu chí này ảnh hưởng trực tiếp đến trải nghiệm người dùng. Vì vậy bạn nên chuẩn bị cải thiện sớm nhất có thể. Vào các bài viết sau mình sẽ trình bày chi tiết hơn về Core Web Vitals.

Testmysite và Pagespeed Insight: ưu tiên tối ưu tốc độ bằng cái gì?

Sơ lược về TestMySite và Pagespeed Insights

Pagespeed Insights là gì?

Pagespeed Insights là công cụ đo lường tốc độ tải trang pagespeed của một site sử dụng dữ liệu thực tế của người dùng trên Chrome kết hợp dữ liệu từ Lab (công cụ kiểm tra như Lighthouse). Từ kết quả đo lường, Pagespeed Insights sẽ đưa ra các chẩn đoán và khuyến nghị giúp tối ưu tốc độ tải trang/pagespeed.

First Contentful Paint (FCP)15%
Speed Index15%
Largest Contentful Paint (LCP)25%
Time to Interactive15%
Total Blocking Time25%
Cumulative Layout Shift (CLS)5%
Các tiêu chí đánh giá chính của Pagespeed Insights (Lighthouse 6)

Nhìn vào bảng biểu trên thì dễ dàng nhận ra Largest Contentful Paint (LCP) Total Blocking Time là 2 tiêu chí có trọng số cao nhất. Đó cũng chính là tiêu chí nên tập trung giải quyết, sẽ được đề cập chi tiết trong các bài viết sau. Pagespeed Insights sử dụng Lighthouse để kiểm tra, nên bạn chỉ cần sử dụng Lighthouse và làm theo các khuyến nghị tối ưu tốc độ là được. Kết quả điểm số ở Lighthouse sẽ có chênh lệch so với Pagespeed Insights, cũng như chính bản thân Pagespeed Insights qua các lần kiểm tra khác nhau. Lý do là tốc độ kết nối vào máy chủ trang web tuỳ thuộc vị trí địa lý mà bị ảnh hưởng, A/B Test hoặc quảng cáo trên trang.

Testmysite là gì?

TestMySite là công cụ đo lường tốc độ tải trang thực tế trên điện thoại di động, đo lường chính ở tốc độ 4G hoặc 3G cho các nước chưa phổ biến 4G, hoặc đối tượng sử dụng trang web đó chỉ sử dụng mạng 3G. TestMySite cung cấp báo cáo dành riêng cho người làm tiếp thị (marketing) hoặc lập trình viên là khác nhau. Với mục đích cho người đọc báo cáo dễ hiểu, chứ các nguyên tắc cơ bản chủ yếu thì vẫn như phần trên mình đã trình bày.

Ngoài thông số tốc độ tải trang cho toàn site (thống kê theo tháng), TestMySite có 3 chức năng chủ yếu như sau:

  • Cho phép kiểm tra tốc độ tải trang ở kết nối 4G từng đường dẫn URL riêng biệt, tối đa 10 đường dẫn
  • So sánh số liệu với trang web đối thủ
  • Cung cấp số liệu tính toán ROI

Nên ưu tiên Testmysitehay Pagespeed Insights?

Về cơ bản thì 2 công cụ này cùng phản ánh kết quả của pagespeed. Mặc dù thông số có khác nhau, nhưng mình đánh giá TestMySite tốt hơn ở góc độ thống kê hàng tháng theo thời gian (giây). Bạn sẽ biết được site của mình trong tháng trước đó nhanh hơn hay chậm hơn. Còn Pagespeed Insights cũng có thống kê nhưng là có đạt chuẩn của “Core web vital” hay không.

Theo chiến lược “Mobile First” mà hầu hết mọi người đang hướng tới, thì TestMySite đánh đúng trọng tâm hơn. Còn Pagespeed Insights không có kết quả toàn site, nên cũng ít ý nghĩa hơn. Hơn nữa thì kết quả của Pagespeed Insights cũng trùng lắp với Lighthouse, nên nếu bạn đã cải thiện theo Lighthouse thì kết quả tối ưu tốc độ cũng sẽ được phản ánh tốt trên Pagespeed Insights. Và cũng phản ánh lên TestMySite.

Fullstack Station Tips

  • Bóp băng thông: thông số 4G dùng để test của Google là 1,638.4 Kbps, với độ trễ là 150 ms. Vì vậy khi bạn test phiên bản mobile, phải dùng thông số này thì mới chính xác. Mặc dù tốc độ xử lý của CPU vẫn mạnh hơn nhiều so với Moto G4.
  • Tốc độ của máy chủ, dung lượng từng tập tin và số lượng request là rất quan trọng. Thông số tối ưu là dưới 100ms cho trang (HTML) với dung lượng ~50kB, và ~35 requests cho toàn bộ hình ảnh, css, js. Mỗi tập tin đều dưới 50kB là lý tưởng, dưới 100kB là chấp nhận được.
  • Đừng quá phụ thuộc vào bất kỳ 1 công cụ nào để đánh giá hiệu suất website. Luôn sử dụng các công cụ kiểm tra tốc độ khác nhau như Pingdom hay GTMetrix để kiếm tra chúng ta áp dụng có hiệu quả hay không.
  • Điểm số tầm 75~ cho mobile và 85~ cho desktop là một kết quả khá tốt. Nếu bạn tập trung vào điểm số quá nhiều, chỉ để lên 90~ mà bỏ qua các cơ hội phát triển các chức năng khác cho dịch vụ thì không nên cho lắm. Nếu có nhiều nhân lực thì triển khai, không phải suy nghĩ.

Bài viết này đã sơ lược hầu hết các quy tắc cần thiết để tối ưu tốc độ tải trang pagespeed. Các bài viết sau mình sẽ trình bày chi tiết và các thủ thuật để cải thiện. Các bạn nhớ đăng ký theo dõi để xem bài mới sớm nhất nhé.

The post Tối ưu tốc độ tải trang: “mili giây tạo triệu đô $$$” appeared first on Fullstack Station.

]]>
https://fullstackstation.com/huong-dan-toi-uu-toc-do-tai-trang-pagespeed-testmysite/feed/ 0
Gatsby Js là gì https://fullstackstation.com/gatsby-js-la-gi/ https://fullstackstation.com/gatsby-js-la-gi/#respond Wed, 22 Jul 2020 05:46:54 +0000 https://fullstackstation.com/?p=2049 Tạo website tĩnh là một kỹ thuật, xu hướng phát triển web không những cải thiện tốc độ mà còn tối đa hóa bảo mật. Nổi bật trong mảng tạo web tĩnh là Gatsby JS. Gatsby JS đã trở nên phổ biến và đã trưởng thành với hệ sinh thái phong phú, Fullstack Station đánh […]

The post Gatsby Js là gì appeared first on Fullstack Station.

]]>
Tạo website tĩnh là một kỹ thuật, xu hướng phát triển web không những cải thiện tốc độ mà còn tối đa hóa bảo mật. Nổi bật trong mảng tạo web tĩnh là Gatsby JS. Gatsby JS đã trở nên phổ biến và đã trưởng thành với hệ sinh thái phong phú, Fullstack Station đánh giá rất cao Gatsby Js, vì vậy đã đến lúc giới thiệu đến tất cả mọi người Gatsby JS là gì.

Gatsby Js là gì?

Gastby JS là nền tảng dùng để xây dựng website và web app để hoạt động ở hiệu suất rất cao. Gatsby JS sử dụng ReactGraphql là thành phần chính, trái tim của nền tảng. GatsbyJS được sử dụng khá rộng rãi và trở thành tên tuổi khá được chú ý hơn sau khi gọi vốn được 15 triệu đô ở vòng gọi vốn Series A.

Tương lai của web chính là mobile dựa trên Progressive Web App (PWA), vì vậy với sự phát triển của Javascript tạo ra xu hướng JAM stack (Javascript + API + Mockup), giúp cho việc phát triển website ngày càng thú vị và đơn giản với Gatsby Js.

Web tĩnh và sự khác biệt của GatsbyJs

Tại sao lại cần web tĩnh?

Trong bài giới thiệu Kỹ thuật lập trình website tĩnh thì mình có nói cũng khá đầy đủ rồi. Web tĩnh thật ra không hề xa lạ., về cơ bản bất kỳ 1 website “động” nào cũng đều có cơ chế cache. Website có lượng truy cập càng lớn thì càng phải có cache. Vậy cache ở đây được xem như là dữ liệu tĩnh tồn tại trên 1 khoảng đơn vị thời gian nhất định.

Cache thì có nhiều cấp độ, từ cơ sở dữ liệu, đến object cache, fragment cache (cache từng phần trên 1 trang), và cấp cuối thường là HTML cache (bỏ qua các lớp cache ở cấp độ hardware). Thì ở cấp cuối “HTML cache” cũng chính là thứ mà sản phẩm cuối cùng của Gatsby JS tạo ra. Và thông thường, thì thư mục cache này được đồng bộ ra các máy chủ khác nhau, giúp tốc độ truy cập nhanh vì bỏ qua hàng ngàn dòng code xử lý và thời gian thao tác I/O.

Một điều khá khôi hài nằm ở chỗ này, chúng ta thường thuê những máy chủ khá khủng để chạy các website mà phần lớn tài nguyên trong đó không phục vụ nhu cầu cuối cùng. Không những lãng phí mà hiệu quả về tốc độ cũng không được cao.

Sự khác biệt của Gatsby JS

Khác biệt so với web động có cache

Web động tạo cache ở thời điểm run-time, điều này đòi hỏi luôn luôn phải duy trì 1 hệ thống máy chủ có đầy đủ cơ sở dữ liệu, các công cụ hỗ trợ đi kèm. Trong khi Gatsby Js build xong ra tập tin tĩnh, thì máy chủ dùng để build có thể tắt đi, có thể giúp tiết kiệm chi phí rất tốt.

Khác biệt so với web tĩnh thuần túy

Web tĩnh thuần túy sẽ tạo ra tập tin HTML hoàn toàn cho mọi liên kết. Vì vậy nếu người dùng truy cập liên kết A, sau đó nhấn vào liên kết B, sẽ tải toàn bộ tập tin HTML của trang B. Gatsby cũng tạo ra HTML cho toàn bộ liên kết, tuy nhiên đồng thời tạo thêm tâp tin JSON nội dung của tất cả các trang. Sự khác biệt là với Gatsby, khi truy cập vào trang A và nhấn vào liên kết B, sẽ tải tập tin JSON nội dung mà trang B hiển thị. Và thông thường tập tin JSON và các tập tin JS liên quan này chỉ chiếm 30% so với 1 trang html hoàn chỉnh.

Ví dụ ở website reactjs.org, từ trang chủ đến trang Docs (Getting Started), thì các tài nguyên cần tải chiếm 12KB, trong khi đó nếu truy cập trực tiếp thì trang HTML này tải cần ~24KB (không tính các request phụ trợ khác), xét tương đối thì tối ưu được 50%.

Ưu và khuyết điểm của Gatsby Js

Hãy cùng Fullstack Station tìm hiểu về ưu và khuyết điểm của Gatsby Js là gì nhé.

Ưu điểm

Triển khai với chi phí thấp và dễ dàng

Việc tách biệt cơ sở dữ liệu và chỉ phục vụ hoàn toàn trên dữ liệu tĩnh giúp việc triển khai rất dễ dàng, có thể dùng bất kỳ máy chủ nào với chi phí rất thấp. Ví dụ với Amazon S3: 0.005$/1000 requests (PUT, COPY, POST, LIST) thì với website có 100k pages, thì mất 100*2 *0.005 = 1$ cho việc quản lý cập nhật mỗi ngày. Với 1 site có 100k pages thì chi phí cập nhật như vậy là quá rẻ. (2 ở đây được xem là mỗi trang có 2 tập tin HTML+JSON). Xem thêm Escalade Sports: From $5000 to $5/month in Hosting With Gatsby.

Tốc độ cao:

Là một framework để tạo web tĩnh (tương tự như Jekyll), tối ưu tốc độ và bảo mật cho website. Việc chuyển qua lại giữa các trang trong website cũng rất nhanh do tất cả các style, html và javascript sẽ được tải trong lần tải đầu tiên, khi người dùng ấn vào một bài viết trong trang web của bạn, nội dung mới sẽ được tải về dưới dạng JSON và hiển thị lên, không cần tải lại toàn bộ trang. Việc cấu hình các plugin cho phép tối ưu việc tải ảnh (progressive) và preload (tải trước nội dung của các liên kết người dùng có thể ghé qua) sẽ cho trang web có tốc độ cực cao.

Tối ưu SEO

Gatsby JS thân thiện với các công cụ tìm kiếm vì các truy cập lần đầu đối với mọi trang đều là HTML. Gatsby kết hợp cả 2 cơ chế Client rendering và Server rendering nên luôn đảm bảo SEO tốt.

Hỗ trợ Progressive Web Apps:

Thêm website của bạn vào màn hình home của di động và người dùng có thể dùng nó như một app di động luôn.

An toàn

Với toàn bộ tập tin là html và json, toàn bộ hệ thống sẽ an toàn trước các kiểu tấn công. Hơn nữa, sẽ không còn phải lo lắng gây ảnh hưởng người dùng khi cập nhật hệ thống (framework, app, database…).

Đơn giản

Thực sự bắt đầu Gatsby rất đơn giản dành cho ai đã quen với React, Graphql, Markdown… Gatsby thừa hưởng các tính năng hay của React và Graphql.

Nhược điểm

  • Gatsby.js chuyên dùng để tạo website tĩnh, nên việc triển khai hệ thống bình luận và tìm kiếm thường phải dựa vào các dịch vụ bên thứ 3 như: Disqus, Algolia, Facebook…Mặc dù bạn hoàn toàn có thể tự phát triển dạng hybrid để tạo web động trong Gatsby Js.
  • Hơi khó khăn để sử dụng đối với những người chưa quen React, Graphql
  • Thời gian build lâu: với cái starter default mà đã build gần 2s, với những site lớn khoảng 100k ~ 1M trang, thì thời gian build tầm 10~30 phút, chưa kể thời gian triển khai và đồng bộ hoàn toàn đến tất cả các edge (các node trong CDN). Tuy nhiên vẫn có những cách khắc phục ở mục Fullstack Station Tips
  • Tốn kém để chuyển đổi hệ thống hiện tại: để chuyển hệ thống sang sử dụng Gatsby JS sẽ tốn chi phí không hề nhỏ. Chúng ta cần phải cân nhắc kỹ lưỡng giữa lợi ích mà web tĩnh mang lại trong tương lai với chi phí chuyển đổi.
  • Với wordpress, hay 1 nguồn nội dung khác, thì phần “hình ảnh trong bài viết” có thể không tận dụng được khả năng xử lý hình ảnh của Gatsby Js.
  • Mất đi một số tính năng hay của các nền tảng khác cung cấp. Ví dụ với WordPress mình kết hợp với OneSignal, khi mỗi bài viết được xuất bản thì sẽ tự động gởi thông báo đến tất cả các người dùng đang theo dõi (subscribers).
  • Triển khai cho người dùng bình thường còn phức tạp. Thật khó để sử dụng chức năng hẹn giờ xuất bản, hoặc chỉ là những cập nhật nho nhỏ.

Gatsby JS phù hợp với dự án web nào?

Dựa trên những ưu điểm của Gatsby, FS nhận thấy phù hợp với các dự án web ít thay đổi về nội dung, số lượng trang không quá lớn, tầm 10000 trang đổ lại. Các trang về tin tức có rủi ro khi cần xoá bài khẩn cấp, vì thời gian build, cache, deploy cũng không thể nhanh được. Tuy nhiên nếu có các phương án chặn link khẩn cấp từ lớp Network thì về cơ bản không vấn đề gì.

Phương thức hoạt động

Credit: Hackernoon

Khi chạy ở mode “develop” thì bản chất Gatsby Js là một create-react-app (CRA), thừa hưởng toàn bộ tính năng của CRA như HOT RELOAD.

Sau khi build ra, thì bao gồm các file json và html, css, js, image…Chúng ta chỉ cần đưa thư mục chứa các tập tin này lên máy chủ là xong.

Một số CMS tương thích tốt với Gatsby

Đa phần các CMS đều có thể hoạt động với Gatsby thông qua API (Restful hoặc Graphql), do vậy việc sử dụng CMS nào phụ thuộc vào việc bạn sử dụng quen CMS nào nhất. Hoặc CMS nào tiện lợi nhất, hoặc là CMS bất kỳ tự phát triển. Miễn sao có API lấy nội dung là được.

Tuy nhiên, các CMS sau thì khá nổi tiếng và có plugin để lấy nội dung (từ API chuyển về dữ liệu graphql trong Gatsby)

Trước khi bạn muốn sử dụng gatsby, hãy kiểm tra xem đã có plugin hỗ trợ chưa, ví dụ tìm kiếm với từ khóa “wordpresshttps://www.gatsbyjs.org/plugins/?=wordpress

Fullstack Station Tips

  • Gatsby tạo cho website hoạt động tốt và bền vững

The post Gatsby Js là gì appeared first on Fullstack Station.

]]>
https://fullstackstation.com/gatsby-js-la-gi/feed/ 0
Giới thiệu Ghost CMS https://fullstackstation.com/gioi-thieu-ghost-cms/ https://fullstackstation.com/gioi-thieu-ghost-cms/#respond Thu, 12 Dec 2019 01:15:00 +0000 https://fullstackstation.com/?p=2060 Ghost CMS là một nền tảng quản trị nội dung CMS (content management system) mã nguồn mở chạy trên nền tảng Nodejs. Ghost CMS xuất hiện đã lâu nhưng khó cạnh tranh được với WordPress vì độ phủ cũng như sự dễ dàng của PHP. Nền tảng plugin, theme của WordPress phong phú hơn rất […]

The post Giới thiệu Ghost CMS appeared first on Fullstack Station.

]]>
Ghost CMS là một nền tảng quản trị nội dung CMS (content management system) mã nguồn mở chạy trên nền tảng Nodejs. Ghost CMS xuất hiện đã lâu nhưng khó cạnh tranh được với WordPress vì độ phủ cũng như sự dễ dàng của PHP. Nền tảng plugin, theme của WordPress phong phú hơn rất nhiều so với Ghost CMS. Tuy nhiên, đối với những ai yêu thích tốc độ, giao diện đẹp thì Ghost CMS là sự lựa chọn không tồi.

Demo

Mình có clone thử 1 phiên bản của Fullstack Station tại đây:

https://huongdanlaptrinh.net/

Về tốc độ thì phải nói là tuyệt vời, mặc dù so sánh sẽ khập khiễng, tuy nhiên với Fullstack Station thì mình đã tối ưu rất nhiều (plugin HyperCache, Autoptimaize của WordPress, PageSpeed cho Nginx) mới có thể đạt tốc độ nhanh được. Còn với Ghost CMS thì mình không phải làm gì, mà cũng có tốc độ nhanh hơn.

Những ưu, khuyết điểm khi sử dụng Ghost làm Website / Blog

Ưu điểm

  • Cài đặt dễ dàng, nhanh chóng
  • Giao diện quản lý nội dung, bộ soạn thảo đơn giản, dễ sử dụng
  • Hỗ trợ markdown
  • Hỗ trợ lịch đăng bài viết
  • Hỗ trợ SEO
  • Mã nguồn mở, miễn phí
  • Có công cụ quản lý Ghost CLI
  • Tích hợp HTTPs miễn phí thông qua Let’s Encrypt
  • Hỗ trợ API để tạo và quản lý bài viết
  • Cài sẵn AMP (Accelerated Mobile Pages)
  • Các bộ tích hợp phong phú, cơ bản đáp ứng đủ nhu cầu của 1 blog đơn giản, có thể tuỳ biến cao dựa vào trigger của cơ chế ITTT của Zappier (Xem thêm Huginn)
  • Có thể xuất nội dung từ WordPress và nhập vào Ghost dễ dàng

Khuyết điểm

  • Ít theme miễn phí
  • Phần bình luận phải sử dụng tích hợp dịch vụ ngoài, khá bất tiện.
  • Phần tích hợp tuy nhiều, nhưng so với WordPress thì còn thua xa, hơn nữa cứ với mổi tích hợp thì bạn phải đăng ký tài khoản của các dịch vụ ngoài, khá phiền phức. Cũng như việc dữ liệu sẽ bị phân tán, khó kiểm soát.
  • Hỗ trợ tiếng Việt kém
  • Khi chỉnh sửa sao giao diện phải restart lại ghost (cách khắc phục là sửa dưới máy local rồi up nguyên theme)
  • Yêu cầu RAM nhiều hơn (1GB) so với WordPress (512MB)
  • Mặc dù hỗ trợ xuất/nhập nội dung từ WordPress, nhưng đối với website sử dụng nhiều code như FS thì nội dung xuất ra vẫn còn nhiều lỗi. (Xem bài bị lỗi)

Chuẩn bị cài đặt Ghost CMS

Bài viết sử dụng Ubuntu 16.04 hoặc 18.10, yêu cầu máy chủ tối thiếu là 1GB RAM nếu không quá trình cài đặt có thể gặp lỗi, để khắc phục bạn sẽ phải bổ sung thêm Swap RAM bằng 4 lệnh sau:

dd if=/dev/zero of=/var/swap bs=1k count=1024k
mkswap /var/swap
swapon /var/swap
echo '/var/swap swap swap default 0 0' >> /etc/fstab

Các phần chuẩn bị cho máy chủ mới

Sau khi đăng nhập vào thì user mặc định là ubuntu, lúc này ta cần tạo thêm một user khác để cài đặt Ghost.

# Thêm user mới 
sudo adduser <user>
# Cấp quyền sudo cho user bạn mới tạo bằng lệnh sau.
sudo usermod -aG sudo <user>
# Đăng nhập vào user mới tạo bằng lệnh.
su <user>

Theo khuyến cáo của Ghost, thì bạn nên tránh tạo user có tên ghost, để tránh trùng lăp và hiểu lầm sau này.

# Đảm bảo máy chủ luôn có các thư viện mới nhất
sudo apt-get update
sudo apt-get upgrade

Trường hợp đã có nginx thì bỏ qua bước này:

# Cài đặt Nginx server trên ubuntu.
sudo apt-get install nginx
# Cho phép kết nối http/https trên firewall bằng lệnh sau
sudo ufw allow 'Nginx Full'

Trường hợp đã có Mysql thì bỏ qua bước này:

# Cài đặt MySQL
sudo apt-get install mysql-server
# Sau khi cài đặt MySQL bạn cần làm các bước sau để đặt mật khẩu cho root user.chạy lệnh
sudo mysql
ALTER USER 'root'@'localhost' IDENTIFIED WITH mysql_native_password BY 'password';
quit
# đăng nhập lại vào user bạn đã tạo từ đầu
su <user> 
# Cài đặt NodeJS:
curl -sL https://deb.nodesource.com/setup_12.x | sudo -E bashsudo apt-get install -y nodejs
# Cài đặt Ghost CLI:
sudo npm install ghost-cli@latest -g

Sau khi cài ghost CLI thành công là bạn đã có một server đầy đủ các thành phần cần thiết để cài đặt Ghost.

Bắt đầu cài đặt Ghost CMS bằng Ghost CLI

Tạo một thư mới để chứa Ghost bằng lệnh sau (tên thư mục các bạn tự chọn ở đây mình chọn là huongdanlaptrinh.)

sudo mkdir -p /var/www/huongdanlaptrinh

Cài đặt quền sở hữu cho thư mục mới tạo

sudo chown <user> /var/www/huongdanlaptrinh

Cấp quyền cho thư mục mới tạo

sudo chmod 775 /var/www/huongdanlaptrinh

Vào thư mục mới tạocd /var/www/huongdanlaptrinh mục cài đặt ghost phải hoàn toàn trống nếu không quá trình cài Ghost sẽ không thể bắt đầu.

Tiến hành cài đặt Ghost

ghost install

Sau khi chạy lệnh “Ghost Install” quá trình cài đặt sẽ bắt đầu bằng những câu hỏi sau

  • Blog URL.
    Nhập chính xác domain của bạn đã chuẩn bị sẵn, ở đây mình đãdùng domain huongdanlaptrinh.net
  • MySQL hostname
    dòng này các bạn nhập localhost, hoặc địa chỉ máy chủ database.
  • MySQL username
    sử dụng username mặc đinh là root, hoặc bất kỳ username của db đã tạo
  • MySQL password
    mật khẩu cho user root  hoặc của username tương ứng các bạn đã tạo ở bước chuẩn bị server.
  • Ghost database name
    chọn tên bất kì và ghost sẽ tự tạo database cho bạn, thường thì ghost sẽ gợi ý như huongdanlaptrinh_prod
  • Set up a ghost MySQL user?
    tạo thêm user cho MySQL 
  • Set up NGINX?
    nên chọn yes và Ghost sẽ tự cấu hình Nginx cho bạn, rất đơn giản.
  • Set up SSL?
    Nếu bạn đầu bạn chọ URL có đường dẫn http các bạn nên bỏ qua bước này và cài đặt sau cũng được. Nếu URL có đường dẫn https các bạn chọn yes Ghost-CLI sẽ tự động cài đặt SSL từ Let’s Encrypt cho website của bạn.
  • Set up systemd?
    nên chọn yes ở bước này.
  • Start Ghost?
    chọn yes để chạy Ghost và website của bạn đã online.

Bậy giờ website đã online và bạn có thể bắt đầu bằng cách đăng nhập vào dashboard của Ghost bằng đường dẫn huongdanlaptrinh.net/ghost. Như vậy là phần cài đặt hoàn thành trong vòng tầm khoảng 15 phút, đây là con số ấn tượng.

Bạn sẽ cần khởi tạo 1 tài khoản quản trị, tài khoản đầu tiên mặc định là chủ sở hữu (owner)

Đây là hình ảnh phía trong phần quản lý nội dung với nội dung được xuất ra từ WordPress.

Fullstack Station Tips

Xét ở góc độ blog đơn giản, thì Ghost CMS vượt trội hơn về tốc độ, cũng như các giao diện đẹp mà không cần phải tối ưu, cấu hình nhiều. Hơn nữa, với các website có sử dụng auto ads Adsense thì layout cũng sẽ bị làm hết đẹp. Nên nếu bạn không có ý định đặt ads, thì Ghost CMS sẽ phù hợp hơn.

Ghost CMS rất phù hợp cho hướng đi làm branding cho cá nhân, tạo cảm giác chuyên nghiệp cho website. Đối với Fullstack Station thì mình không có ý định chuyển sang Ghost CMS, vì mình sử dụng được nhiều tính năng của WordPress phù hợp hơn.

The post Giới thiệu Ghost CMS appeared first on Fullstack Station.

]]>
https://fullstackstation.com/gioi-thieu-ghost-cms/feed/ 0
Streamlit: Công cụ chuyên dụng cho ứng dụng phân tích dữ liệu với Python【Cập nhật 2024】 https://fullstackstation.com/gioi-thieu-streamlit-la-gi/ https://fullstackstation.com/gioi-thieu-streamlit-la-gi/#respond Fri, 11 Oct 2019 08:26:53 +0000 https://fullstackstation.com/?p=1957 Streamlit là gì Streamlit là công cụ được xây dựng với mục đích dành cho Machine Learning Engineer, tạo ra giao diện web như Jupyter notebook. Điểm đặc biệt khác với Jupyter notebook là Streamlit không phải hiển thị code, giúp cho bạn có thể tạo ra sản phẩm có tính hoàn thiện cao. Trong […]

The post Streamlit: Công cụ chuyên dụng cho ứng dụng phân tích dữ liệu với Python【Cập nhật 2024】 appeared first on Fullstack Station.

]]>
Streamlit là gì

Streamlit là công cụ được xây dựng với mục đích dành cho Machine Learning Engineer, tạo ra giao diện web như Jupyter notebook. Điểm đặc biệt khác với Jupyter notebook là Streamlit không phải hiển thị code, giúp cho bạn có thể tạo ra sản phẩm có tính hoàn thiện cao.

Trong bài “Tại sao nên học Python“, mình đã giới thiệu về ưu điểm của Python. Bài này sẽ giới thiệu thêm 1 ứng dụng khác. Về cơ bản có thể hiển thị kết quả từ python ra web, nên có thể sử dụng để tạo app bất kỳ với python.

Các tính năng cơ bản

Các tính năng thường được sử dụng ở streamlit là các biểu đồ, đồ thị, bản đồ, hình ảnh, âm thanh, video… bạn có thể xem toàn bộ tính năng ở đây: https://streamlit.io/components

Sau hơn 5 năm phát triển nền tảng Streamlit, giờ đây đã có rất nhiều tính năng được thêm vào khá đặc biệt như WebRTC, Code Editor…hoặc là các kết nối vào các nền tảng khác như SQL, CockroachDB, AirTable, Google Sheets…

Với những tính năng hiện tại

Một số tính năng hữu ích nâng cao

Cache

Cache được lưu trữ theo mỗi trạng thái của thiết lập điều này giúp cho ứng dụng không cần phải chạy lại cho từng người dùng khác nhau.

Để đảm bảo được cache hoạt động đúng, thì trong hàm sử dụng cache, không được dùng các hàm của streamlit. Các chức năng có thể nên dùng cache như: tải file, xử lý tính toán cho kết quả.

Session State

Sesstion State được sử dụng cho mục đích lưu trữ thông tin và chia sẽ dữ liệu giữa các script trong cùng 1 ứng dụng, ví dụ như thông tin đăng nhập của người dùng. Tuy nhiên, dữ liệu đó không chia sẽ giữa các tab trong cùng 1 trình duyệt và sẽ bị xóa nếu refresh lại trang.

Sesstion State dùng kiểu dữ liệu từ điển (key-value) để lưu trữ và mặc dù khá ít được sử dụng nhưng đối với các trường hợp như thông tin có tính chất phiên làm việc thì khá quan trọng. Widgets tự quản lý state riêng của mình, nên session state không liên quan đến state của widget nhé.

Streamlit Cloud

Với Streamlit Cloud thì bạn có thể triển khai ứng dụng của mình lên máy chủ của Streamlit hoàn toàn miễn phí, với điều kiện là mã nguồn lưu trữ công khai trên Github. Sau khi triển khai xong, bạn có thể tùy chọn cho mình một đường dẫn URL kiểu “xyz.streamlit.io/repo-name”

Fullstack Station Tips

Thường các công ty lớn hay sử dụng các công cụ phân tích/biểu thị dữ liệu như Redash, hay cho vào data warehouse rồi viết lại các dashboard khá mất thời gian. Như redash thì độ linh động cũng không cao cho lắm, còn tự làm dashboard thì khá là vất vả lấy yêu cầu rồi làm frontend, backend này nọ. Cuối cùng thì kết quả cũng chỉ là đưa dữ liệu lên, cho phép người dùng tìm kiếm, lọc dữ liệu theo 1 số tiêu chí nào đó. Điều này đối với streamlit thì khá phù hợp và tiện lợi khi chỉ cần 1 ngôn ngữ duy nhất là Python.

The post Streamlit: Công cụ chuyên dụng cho ứng dụng phân tích dữ liệu với Python【Cập nhật 2024】 appeared first on Fullstack Station.

]]>
https://fullstackstation.com/gioi-thieu-streamlit-la-gi/feed/ 0
Tại sao nên học Python? https://fullstackstation.com/tai-sao-nen-hoc-python/ https://fullstackstation.com/tai-sao-nen-hoc-python/#comments Fri, 02 Aug 2019 09:00:35 +0000 http://fullstackstation.com/?p=1803 Python đã lần đầu tiên đạt được vị trí 3 trên bảng xếp hạng Tiobe Index, với mức tăng +2.90%, vượt qua C++ tại thời điểm tháng 7/2019. Với một tín hiệu tích cực, quá rõ ràng cho xu hướng của Python. Bài này mình sẽ điểm qua các giá trị của Python để trả […]

The post Tại sao nên học Python? appeared first on Fullstack Station.

]]>
Python đã lần đầu tiên đạt được vị trí 3 trên bảng xếp hạng Tiobe Index, với mức tăng +2.90%, vượt qua C++ tại thời điểm tháng 7/2019. Với một tín hiệu tích cực, quá rõ ràng cho xu hướng của Python. Bài này mình sẽ điểm qua các giá trị của Python để trả lời câu hỏi “Tại sao nên học Python” từ bây giờ?

Python ở Tiobe Index: https://www.tiobe.com/tiobe-index/python/

Đôi nét sơ lược về Python

Python là một ngôn ngữ lập trình thông dịch, tương tác, hướng đối tượng và là ngôn ngữ bậc cao, có mục đích chung chung. Được tạo ra bởi Guido van Rossum trong giai đoạn 1985 – 1990. Python là ngôn ngữ của năm 2018 theo bảng xếp hạng Tiobe Index, hạng 3 là thứ hạng cao nhất đạt được.

Lý do Youtube được Google mua lại một phần là vì Python đã được sử dụng trong nền tảng này để tạo ra, phát hành tính năng nhanh hơn Google Video tại thời điểm 2006. Nền tảng youtube lúc đó chỉ có khoảng 20 lập trình viên so với hàng trăm kỹ sư giỏi của Google sử dụng C/C++.

Những điểm mạnh của Python

Mục đích chung chung/General-purpose

Do đặc tính mục đích chung chung của Python, nên Python có thể sử dụng được ở rất nhiều lĩnh vực. Hầu như tất cả ngoại trừ xử lý giao diện ở trình duyệt. Ở độ bao phủ này thì Python chỉ thua mỗi JS, nhưng bù lại Python lại mạnh hơn JS ở mảng về AI. Như vậy nếu bạn có thể lập trình được Python thì có thể làm được rất nhiều việc. Python cũng phù hợp cho các bạn yêu thích khởi nghiệp. Chính vì độ uyển chuyển và thiết kế ra sản phẩm nhanh của Python.

Tương tác/Interactive

Tính năng tương tác nghĩa là bạn có thể truy cập vào shell của python bằng lệnh python. Sau đó có thể viết mã hàm (function), lớp (class) hoặc sử dụng các hàm tích hợp sẵn của python. Không nhiều ngôn ngữ lập trình hỗ trợ tính năng tương tác này. Chỉ có 1 vài ngôn ngữ khác không phổ biến và được sử dụng rộng rãi như lisps (bao gồm lisp, scheme gồm clojure), sml, ocaml, haskell, F#, erlang, scala, ruby, python, lua, groovy, prolog.

Sử dụng thư viện/Import

Cách quản lý thư viện của Python khá linh động, bạn có thể import một hàm; hay class từ thư viện có sẵn, hoặc được cài đặt từ bên thứ ba, hay từ thư mục của dự án. Khái niệm import này sẽ gần giống với Nodejs, PHP hoặc các ngôn ngữ khác. Tuy nhiên điểm khác biệt lớn nhất là có thể sử dụng cùng tên hàm cho từng mô đun (module) khác nhau.

Tính rõ ràng, dễ đọc hiểu/Readability & simplicity

Ví dụ xét đoạn mã sau:

// Python
x = 12
y = 5
result = x / y
if result > 5.5:
    print("12 divided by 5 is greater than 5.5)
else
    print("12 divided by 5 is NOT greater than 5.5)
// C++
#include <iostream>
int main()
{
    double x = 12;
    double y = 5;
    double result = x / y;
    if(result > 5.5)
        std::cout << "12 divided by 5 is greater than 5.5" << std::endl;
    else
        std::cout << "12 divided by 5 is NOT greater than 5.5" << std::endl;
    return 0;
}

Rõ ràng với 1 người chưa biết lập trình thì đoạn mã 1 với Python dễ đọc và dễ hiểu hơn nhiều so với C++. Ngay cả so sánh với JS hay PHP thì Python vẫn vượt trội hơn về tính dễ đọc, dễ hiểu. Python sử dụng cú pháp tương tự tiếng Anh, vì tương tự ngôn ngữ con người nên dễ tiếp thu hơn. Đó cũng là lý do vì sao Python đang dần trở thành ngôn ngữ lập trình được dùng để dạy cho trẻ em.

Những điểm yếu/vấn đề của Python

Khác biệt về cú pháp so với ngôn ngữ khác

Gọi là điểm yếu thì không hẳn, nhưng so với các ngôn ngữ khác như PHP, JS, C, Java thì có dấu chấm kết thúc dòng, có các cặp {} để phân biệt khối, khối trong Python sẽ do số lượng khoảng trắng quy định (indent). Sẽ khiến bạn khó khăn 1 chút khi mới tiếp cận Python, cũng dễ xảy ra lỗi hơn nếu sai indent.

Không thể triển khai trên di động

Python hoạt động tốt ở môi trường máy chủ và ứng dụng máy tính để bàn (desktop applications), tuy nhiên ở trên điện thoại di động thì vẫn chưa có nền tảng nào hỗ trợ. So sánh 1 chút với Nodejs với React Native hay Dart với Flutter thì độ phủ thấp hơn. Với Nodejs và Dart có thể sử dụng cho ứng dụng di động đồng thời cho cả ở máy chủ và ứng dụng máy tính để bàn.

Tốc độ thực thi chậm

Python thực thi chậm hơn PHP 7, Nodejs là điều đã được kiểm chứng, cũng nhiều benchmark được thực hiện rồi. Nếu bạn thực sự đã có ý tưởng sản phẩm và tốc độ thực thi là bắt buộc, thì đây là 1 rào cản khó khăn. Nhưng nếu sử dụng Pypy, hoặc Cython, Numba để cải thiện và nhận lấy lợi ích khác từ Python và hi sinh một phần tốc độ thì cũng không phải là không tốt.

Lỗi run-time

Vì là ngôn ngữ kiểu động (dynamic typed) vì vậy khi chạy (run-time) chương trình Python có thể vẫn xảy ra lỗi về kiểu dữ liệu. Bạn sẽ phải sử dụng try/catch tốt hoặc cải thiện bằng các công cụ checker, linter.

Tại sao Python lại tăng trưởng nhanh như vậy?

Kỷ nguyên AI

Python được sử dụng nhiều trong kỷ nguyên AI khi mà tốc độ thực thi không quá quan trọng bằng tốc độ tính toán của GPU. Còn khả năng của Python thì được ứng dụng thích hợp vì dễ hiểu, dễ bảo trì. Để làm ra sản phẩm thông minh, tính năng AI có thể được huấn luyện hàng trăm giờ nhờ GPU. Trước đó còn có công việc xử lý số liệu, làm prototype…, tất cả những tác vụ này thì Python phù hợp nhất. Vì phần lớn thời gian được thực hiện bằng GPU, nên tốc độ thực thi của C/C++ hay Java không còn là điểm mạnh.

Dành cho các bạn làm Data Science, Python có thư viện Streamlit rất hữu dụng để làm báo cáo, demo.

Blockchain & IOT (Internet of things)

Bạn có thể thiết kế 1 blockchain đơn giản với 50 dòng code! Đây là điều tuyệt vời để bắt đầu với blockchain vì có thể làm thử nghiệm 1 cái gì đó rất nhanh. Python không chạy được trực tiếp trên GPU nên vẫn còn hạn chế với các ứng dụng blockchain đòi hỏi khả năng mining. Nhưng với IOT kết hợp blockchain thì lại là chuyện khác! IOT là những thiết bị có cấu hình thấp hoặc rất thấp, vì vậy Python hoàn toàn phù hợp để lập trình tương tác với các thiết bị này.

Kết hợp ứng dụng blockchain vào IOT đã và đang được triển khai rộng rãi. Vì vậy Python có cơ hội tham gia và quá trình này một cách chặt chẽ. Vì Python đơn giản, mang tính ứng dụng cao nên được nhiều người sử dụng. Vì vậy tăng trưởng cao là điều tất yếu.

Python có thể làm được gì khác?

Ngoài AI, Blockchain và IOT, Python còn được sử dụng ở mảng web, ứng dụng và lập trình điều khiển, tự động hoá.

Lập trình Web và API

Django và Flask là 2 framework hàng đầu cho lập trình web của Python. Nếu PHP có WordPress đang thống lĩnh thị trường thì Python với Django có nền tảng tương tự. Điểm mạnh của Django chính là phát triển web dựa trên ORM và có hệ thống Dashboard mạnh mẽ bao gồm hệ thống xác thực, phân quyền người dùng. Trong phân khúc này, nếu đã có bản template HTML thì Django sẽ phát triển ra ứng dụng nhanh hơn WordPress. Tài liệu của Django cũng rất phong phú và chi tiết, rõ ràng.

Flask thì nhỏ và uyển chuyển hơn Django, có tốc độ khá tốt nhưng thường dùng để lập trình API. Ngoài ra lập trình API thì mới nổi nền tảng FastAPI. Vì tận dụng tính năng async/await cho tốc độ rất tốt và có thể tạo ra tài liệu cho API chuẩn.

Ứng dụng, game & lập trình điều khiển, tự động hoá

Nhiều nền tảng có thể dùng để viết ứng dụng đa nền tảng cho Window, Macos hay Linux như: PyQT, PyGi, Kivy, Tk… ; hoặc tập trung duy nhất cho Macos như Cocoa, PyObjC, xem thêm GUI cho Python. Trong ứng dụng đa nền tảng thì Python không mạnh bằng JS với Electron, hoặc với Dart/Flutter. Tuy nhiên ứng dụng của Python thì có giao diện tự nhiên (native) hơn. Ngoài ra cũng có thể viết game 2D với Python và cũng khá nhiều nền tảng hỗ trợ.

Về điều khiển và tự động thì Python đang dần thay thế các câu lệnh phức tạp của Bash với khả năng lập trình uyển chuyển. Ví dụ bạn có thể điều khiển hệ thống máy chủ, dịch vụ cloud ở AWS bằng Python khá dễ dàng và được hỗ trợ chính chủ, hoặc làm ứng dụng auto-pull github khi có commit mới.

Hướng dẫn cách học Python hiệu quả

Mình cũng từng lướt nhanh khi học Python, nhưng sau một hồi đi lung tung mình nhận thấy cần phải học Python căn bản thật chắc. Đây là các phần cần phải học chắc Python để sử dụng hiệu quả:

  • Cú pháp, bao gồm cách viết hàm, lớp, module
  • Kiểu dữ liệu, cần nắm vững list, tuple, set, dict
  • Cách debug
  • Các cách xử lý dữ liệu ngày tháng, dữ liệu kiểu chuỗi, xử lý tập tin

Danh sách một vài tài nguyên cho việc học Python

Bạn nào đã nắm 1 ngôn ngữ lập trình rồi thì đọc hết và làm theo được các cheatsheet là tương đối ổn rồi.

Mấy cái “cheatsheet” hay “learn X in Y” là để tham khảo thôi. Đọc xong tưởng mình hiểu rồi hông luyện tập thì mãi chỉ là số 0 tròn trĩnh. Đừng ảo tưởng sức mạnh!

Bạn nhìn danh sách này có thể dài, tất nhiên không thể học hết. Điều quan trọng là bạn thích chủ đề nào. Và quan trọng nhất là học Python cho xong một chủ đề. Không nên bỏ học giữa chừng vì thấy chủ đề khác, khoá học khác “có thể” hay hơn.

Ngoài ra, cần phải luyện hàng ngày thì mới lên tay nhanh và chắc được.

Fullstack Station Tips

Nhiều người khuyến nghị là phải học bằng cách làm 1 dự án cụ thể. Đây cũng là điều đúng tuy nhiên thực sự có 1 trở ngại cần phải nói rõ. Ví dụ mình bắt đầu học Python bằng làm dự án dùng Django, với kỹ năng lập trình web nhiều năm việc mình làm Django không hề khó. Nhưng với tài liệu chính thống 2000 trang của Django thì thật không thể nào đọc nổi.

Nhưng những kiến thức về Django không giúp gì nhiều về học Python cơ bản.

Tương tự như vậy khi học về Machine Learning, thường sẽ cần dùng Numpy, Pandas, … . Học Python bằng những thư viện này hầu như không giúp gì cho việc học Python. Tốt nhất là tài liệu của Python: https://docs.python.org/3/tutorial/index.html, hoặc 1 cuốn sách nào đó chuyên về học Python. Khi bạn giỏi Python, bạn đi hướng nào cũng được.

Tóm lại, là cần học cơ bản chắc, đừng học python kết hợp với 1 lĩnh vực mới. Học 2 thứ mới cùng 1 lúc, chỉ khiến bạn mất thời gian, vì cả 2 đều không giỏi.

Còn nếu bạn không đủ động lực để học thuần Python? OK, kết cục đã rõ!

Tham khảo

https://www.zdnet.com/article/python-is-eating-the-world-how-one-developers-side-project-became-the-hottest-programming-language-on-the-planet/

https://www.quora.com/What-about-the-future-of-Python-and-Django

https://blog.geekforbrains.com/why-im-switching-from-python-to-nodejs-1fbc17dc797a?gi=f9c2204ab02e

https://blog.geekforbrains.com/after-a-year-of-using-nodejs-in-production-78eecef1f65a?source=post_page—————————

The post Tại sao nên học Python? appeared first on Fullstack Station.

]]>
https://fullstackstation.com/tai-sao-nen-hoc-python/feed/ 2