Cảm ơn bạn đã đọc và ủng hộ blog KTMT ʘ‿ʘ Từ bây giờ chúng tôi sẽ là kipalog.com !

Lưu ý trước khi đọc tiếp: Ở bài viết này tác giả dùng chữ “hacker”, không phải theo nghĩa chỉ những người làm trong lĩnh vực bảo mật hay an toàn thông tin. “Hacker” ở đây là những kỹ sư, những nhà phát triền có năng lực tự tìm hiểu, mày mò, có kỹ năng “bắt máy tính phục vụ sở thích của mình”.

Ý tưởng

Nếu bạn là một hacker làm việc nhiều với Mac hoặc Linux, chắc các bạn chẳng xa lạ gì với terminal - giao diện dòng lệnh cơ bản nhất của hệ điều hành Unix. Tôi là một hacker bị “cuồng terminal”, zsh, prezto, tmux, irssi, vim, tig là những tools ưa thích nhất. Tôi từng có ước mơ muốn từ bỏ các giao diện đồ hoạ, có thể lập trình, chat chit, nghe nhạc v.v.. ngay trên môi trường không-đồ-hoạ.

Bên cạnh đó, mặc dù không mấy mặn mà với Facebook nhưng gần đây lại bị nghiện Twitter, trong đầu tôi luôn hiện lên câu hỏi: làm thế nào để cũng có thể tương tác với Twitter chỉ qua terminal của MacOSX ?

Trên thực tế đã có khá nhiều thư viện mã nguồn mở có thể đáp ứng được nhu cầu trên. t hay earthquake là những gem(Ruby) được viết rất bài bản và đa tính năng. Tuy nhiên tôi đã quyết định tự viết một phần mềm của riêng mình, bởi tự phát triền và làm sản phẩm của mình được cộng đồng đón nhận là một mục tiêu mới mẻ và đầy thử thách.

Trong bài viết này, tôi sẽ giới thiệu với các bạn tôi đã xây dựng một phần mềm mã nguồn mở như thế nào, về cả kỹ năng phát triển và cách mang phần mềm của mình đến với cộng đồng hacker trên thế giới.

Xác định mục tiêu

Khi bạn bắt đầu viết một phần mềm mã nguồn mở, điều quan trọng đầu tiên sẽ là : đã có ai thực hiện ý tưởng của bạn chưa và họ đã thực hiện được tốt đến đâu. Khi chuẩn bị viết phần mềm của mình, tôi nhận thấy t giống như 1 twitter command trên Unix, focus vào khả năng pipe với các command khác. Ngược lại, earthquake là 1 app hoàn chỉnh nhưng xử lý hiển thị tweets lại chưa thật tốt.

Và từ đó Rainbow Stream ra đời. Bạn có thể nhận ra 2 điểm nêu trên khi nhìn vào cách thức hoạt động của app dưới đây:

Tạo nên sự khác biệt

Để gây được ấn tượng với người dùng, sản phẩm của bạn vẫn cần có 1 đến 2 tính năng nổi trội. Bạn sẽ không muốn phần mềm mình viết ra mãi chỉ là “alternative to xxx or yyy, can consider if zzz stops development”. Ở đây, tôi xây đựng Rainbow Stream tập trung vào 2 tính năng chính:

  • Khả năng hiển thị màu sắc trên các terminal hỗ trợ 256 màu, cung cấp sẵn 1 số themes nổi tiếng.
  • Hiện thị ảnh trực tiếp trên terminal.

Chúng ta sẽ đi vào cụ thể trong các phần tiếp theo.

Hiển thị màu của terminal

Hầu hết các terminal hiện đại đều hỗ trợ hiển thị 256 ANSI colors. Trên shell bạn có thể dễ dàng in ra chữ theo các màu định sẵn bẳng các dùng Escape character như dưới đây

color - color.sh
1
2
3
4
5
6
7
$ echo -e "\e[31mRed"
$ echo -e "\e[32mGreen"
$ echo -e "\e[33mYellow"
$ echo -e "\e[34mBlue"
$ echo -e "\e[35mMagenta"
$ echo -e "\e[36mCyan"
$ echo -e "\e[37mLight gray"

Hiện thị màu trên Python có thể được viết gọn theo function như sau

color - color.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
def basic_color(code):
    """
    16 colors supported
    """
    def inner(text, bold=True):
        c = code
        if bold:
            c = "1;%s" % c
        return "\033[%sm%s\033[0m" % (c, text)
    return inner


def term_color(code):
    """
    256 colors supported
    """
    def inner(text):
        c = code
        return "\033[38;5;%sm%s\033[0m" % (c, text)
    return inner

Sử dụng những function ở trên thực tế rất đơn giản: gọi thẳng function với parameter là mã ANSI color, trả về là một function khác và lần này nhận parameter là string để đổi thành string có màu tương ứng.

color - color.py
1
2
3
4
5
6
7
8
9
10
black = basic_color('30')
red = basic_color('31')
green = basic_color('32')
yellow = basic_color('33')
blue = basic_color('34')
magenta = basic_color('35')
cyan = basic_color('36')

print green("Green text")
print term_color('112')("A text with ANSI color 112")

Giả sử chúng ta có một tập vô hạn các word không biết trước. muốn mỗi word có một màu và các word lặp lại sẽ có màu giống nhau, chúng ta có thể dùng Memoization trong Python như sau:

color - color.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
import itertools
from functools import wraps
cyc = itertools.cycle([black,red,green,yellow,blue,magenta,cyan])

def Memoize(func):
    cache = {}
    @wraps(func)
    def wrapper(*args):
        if args not in cache:
            cache[args] = func(*args)
        return cache[args]
    return wrapper

@Memoize
def cycle_color(s):
    return next(cyc)(s)

for s in ["w1","w2","w3","w1","w4"]:
    print cycle_color(s) # Now every word will has its own color, while the 1st "w1" and 2nd "w1" ends up with same color

Các màu sắc hiển thị trong Rainbow Stream đều dựa theo nguyên lý nói trên.

Hiển thị ảnh trên terminal

Để nói cụ thể về phần này sẽ hơi dài dòng, nhưng có thể tóm gọn trong các ý sau đây:

  • Python có một thư viện xử lý ảnh rất tốt là Pillow. Pillow cung cấp những tính năng cơ bản để thao tác với lượng thông tin trong một tấm ảnh. Nhược điểm của Pillow là khá buggy khi install và không hỗ trợ Window.
  • Tôi dùng Pillow để đọc thông tin về từng Pixel trong một ảnh, mỗi pixel sẽ có 4 chỉ số gồm 3 chỉ số màu (R,G,B) và 1 chỉ số về độ trong (A).
  • Màu sắc của 1 pixel nói trên được quy đổi về tập 256 màu ANSI hiển thị được trên terminal (phương pháp xem ở dưới).
  • Với mỗi pixel, tôi in ra như 1 ký tự Space với màu ANSI tương ứng, sử dụng hàm term_color ở đoạn trên.

Trong các bước trên thì bước quy đổi màu là quan trọng nhất. Thuật toán quy đổi dùng ở đây là phương pháp tính khoảng cách vector trong không gian Euclide 3 chiều:

  • Mỗi màu RGB coi như 1 vector với 3 chiều là R (Red), G (Green), B (Blue).
  • Mỗi màu ANSI (trong tập 256 màu của terminal) cũng tương ứng với 1 vector 3 chiều. Chúng ta có tập tiêu chuẩn 256 vector ở đây.
  • Mỗi vector RGB của 1 pixel sẽ được quy về vector tiêu chuẩn ANSI gần nhất. Công thức tính khoảng cách giữa 2 đầu vector như trong hình học 3 chiều : ((x1-x2)**2 + (y1-y2)**2 + (z1-z2)**2)**0.5

Như vậy “ảnh” ở trên terminal thực chất là các ký tự Space với màu ANSI đã được quy đổi và in ra liên tiếp :)

Các vấn đề kỹ thuật khác

Để hoàn thiện Rainbow Stream thực ra cần một số kỹ năng khác như lập trình với thread, tạo interactive input bẳng readline, gọi chương trình C compile sẵn hay xử lý chung cho cả Python 2 và Python 3… Trong khuôn khổ một bài viết tôi khó có thể trình bày hết những vấn đề trên, vì vậy nếu bạn quan tâm hãy mở thẳng Github repo và đọc source code. Rainbow Stream là một phần mềm mã nguồn mở với MIT license.

(… còn tiếp - Làm thế nào để mang phần mềm của mình đến với thế giới hacker …)

Giới thiệu

Load Average – tạm dịch là “giá trị tải trung bình” – là một chỉ số liên quan đến CPU rất cơ bản và quan trọng. Việc nắm rõ ý nghĩa của chỉ số này giúp chúng ta đánh giá được hiệu năng hiện thời của máy tính cũng như sử dụng CPU nói riêng, máy tính nói chung một cách hiệu quả nhất

Bài viết này bắt đầu bằng việc giải thích ý nghĩa của “giá trị tải trung bình”. Sau đó bài viết sẽ trình bày cách đánh giá chỉ số này trong thực tế. Cuối cùng bài viết đưa ra một trường hợp thực tế về cách đánh giá hiệu năng máy tính theo chỉ số này.

“Tải trung bình” là gì?

Ví dụ trạm thu phí

Để hiểu *tải trung bình" là gì ta sẽ xem xét một ví dụ thực tế như sau.

Bạn đang tham gia giao thông trên đường cao tốc và trước mặt của bạn là trạm thu phí đường bộ. Bạn giảm tốc để chuẩn bị qua cửa soát vé. Trạm xoát vé có 4 cửa soát vé. Tất cả các cửa đều trống và bạn chọn cửa số 1 như dưới đây.

    H                               <-- Xe ôtô của bạn
|   1   |   2   |   3   |   4   |   <-- Trạm thu phí

Bạn đánh xe đến cửa số 1, trả phí cho nhân viên soát vé. Nhân viên soát vé mở barrier chắn, và bạn đi qua. Có duy nhất xe bạn qua trạm nên từ phía trạm thu phí, trạm đang phục vụ 1 xe.

|   H   |   2   |   3   |   4   |

Giờ tưởng tượng có nhiều xe khác cũng lưu thông trên đường cao tốc. Giả sử có trước khi bạn đến trạm thu phí, đang có 2 xe khác làm thủ tục ở cửa số 1 và số 2, bạn chú ý cửa số 3,4 còn trống nên lái xe qua cửa số 3 và làm thủ tục mà không phải chờ đợi. Trạm phục vụ 3 xe.

|   H'   |   H'   |   H   |   4   |

Có thể thấy ở 2 trường hợp trên, trạm thu phí đang làm việc khá hiệu quả. Các xe ôtô đi qua với thời gian chờ đợi bằng 0. Các xe ôtô đi qua trạm xoát vé một cách nhanh chóng. Người lái xe là bạn cảm thấy thoải mái vì không phải chờ đợi.

Giả sử hôm nay là ngày nghỉ lễ, mọi người về quê đông nên xe khách chạy rất đông. Các gia đình tranh thủ ngày lễ nên cũng đánh xe đi chơi xa. Đường cao tốc trở nên đông đúc. Bạn đến trạm thu phí và nhận ra rằng 4 cửa đang có xe làm thủ tục. Chưa kể bạn còn đến sau 2 xe khác và phải đợi xếp hàng sau 2 xe này.

    H                                   <-- Xe ôtô của bạn
    H'
    H'
|   H'   |   H'   |   H'   |   H'   |   <-- Trạm thu phí

Trong trường hợp này, bạn chắc chắn sẽ phải chờ, không những chờ các xe đang làm thủ tục tại cửa trạm mà còn chờ cả các xe đến trước bạn. Thời gian có thể bị kéo dài vì nhiều lý do như 1 xe làm thủ tục mất thời gian hơn các xe khác hoặc có sự cố ở cửa soát vé. Đứng từ góc độ của trạm thu phí, trạm đang phải xử lý số lượng xe (7 xe) nhiều hơn khả năng của trạm (4 cửa). Tại thời điểm hiện tại, trạm đang bị quá tải.

Ta định nghĩa số lượng tải trung bình của trạm là số lượng xe mà trạm phải phục vụ trong một đơn vị thời gian. Như vậy ở ví dụ trên trung bình tải của trạm thu phí tại thời điểm bạn đến là 7.

Load Average của CPU

Tương tự như ví dụ trạm soát vé, “Load Average” của CPU được định nghĩa là số lượng process cần tài nguyên tính toán của CPU tại thời điểm nhất định. Giả sử tải trung bình của máy tính bạn hiện tại là 3.2, điều đó có nghĩa là tại thời điểm đó đang có trung bình 3.2 processes cần CPU xử lý. Tại thời điểm process cần CPU, nếu CPU đang rảnh process sẽ được OS cho chạy trên CPU rảnh.

Mổi “cửa soát vé” trong CPU máy tính sẽ là 1 lõi CPU. Với các CPU thế hệ mới trang bị công nghệ Hyperthreading, mỗi lõi vật lý có thể hoạt động được như 2 lõi logic. Vì vậy OS sẽ nhận diện 8 lõi. Ví dụ máy tính của bạn được trang bị chip mới nhất hiện tại Corei7 MQ– 4 cores 8 threads với công nghệ Hyperthreading thì đối với hệ điều hành máy tính của bạn có 8 cores.

Để kiểm tra máy tính của bạn có bao nhiêu lõi (cores), trên windows bạn có thể kiểm tra qua TaskManager > Performance. Bên cạnh biểu đồ tỉ lệ sử dụng CPU nói chung là tỉ lệ sử dụng CPU của từng lõi. Số lượng cửa sổ bên tay phải là số lượng lõi logic.

Trên Linux bạn có thể kiểm tra bằng nhiều cách:

$ top
# ấn 1
top - 20:38:48 up 2 days,  4:50,  1 user,  load average: 11.30, 11.54, 10.17
Tasks: 430 total,   2 running, 428 sleeping,   0 stopped,   0 zombie
Cpu0  : 20.5%us,  2.4%sy,  0.0%ni, 76.2%id,  0.4%wa,  0.0%hi,  0.5%si,  0.0%st
Cpu1  : 20.2%us,  1.9%sy,  0.0%ni, 77.4%id,  0.5%wa,  0.0%hi,  0.1%si,  0.0%st
Cpu2  : 19.9%us,  1.8%sy,  0.0%ni, 77.7%id,  0.5%wa,  0.0%hi,  0.1%si,  0.0%st
Cpu3  : 19.9%us,  2.3%sy,  0.0%ni, 77.2%id,  0.2%wa,  0.0%hi,  0.4%si,  0.0%st
Cpu4  : 19.8%us,  2.3%sy,  0.0%ni, 77.1%id,  0.4%wa,  0.0%hi,  0.4%si,  0.0%st
Cpu5  : 19.7%us,  2.3%sy,  0.0%ni, 77.4%id,  0.2%wa,  0.0%hi,  0.4%si,  0.0%st
Cpu6  : 20.1%us,  1.6%sy,  0.0%ni, 78.1%id,  0.1%wa,  0.0%hi,  0.0%si,  0.0%st
Cpu7  : 19.6%us,  2.2%sy,  0.0%ni, 77.7%id,  0.1%wa,  0.0%hi,  0.3%si,  0.0%st
Cpu8  : 19.4%us,  2.2%sy,  0.0%ni, 78.0%id,  0.1%wa,  0.0%hi,  0.3%si,  0.0%st
Cpu9  : 19.8%us,  2.2%sy,  0.0%ni, 77.6%id,  0.1%wa,  0.0%hi,  0.3%si,  0.0%st
Cpu10 : 19.5%us,  1.6%sy,  0.0%ni, 78.8%id,  0.1%wa,  0.0%hi,  0.0%si,  0.0%st
Cpu11 : 19.9%us,  2.2%sy,  0.0%ni, 77.5%id,  0.1%wa,  0.0%hi,  0.3%si,  0.0%st
Mem:  32846220k total, 32593588k used,   252632k free,   434464k buffers
Swap:  4194296k total,        0k used,  4194296k free, 22380012k cached

PID USER      PR  NI  VIRT  RES  SHR S %CPU %MEM    TIME+  COMMAND
14489 hadoop    20   0 1643m 684m  16m S 104.7  2.1  32:15.93 java
14496 hadoop    20   0 1635m 705m  16m S 104.7  2.2  32:14.63 java
16194 hadoop    20   0 1637m 655m  16m S 104.7  2.0  29:45.06 java
16243 hadoop    20   0 1630m 687m  16m S 104.7  2.1  29:28.34 java

hoặc

$ mpstat -P ALL
Linux 2.6.32-358.11.1.el6.x86_64 (bb2-dn07)     07/20/2014      _x86_64_        (12 CPU)

08:39:53 PM  CPU    %usr   %nice    %sys %iowait    %irq   %soft  %steal  %guest   %idle
08:39:53 PM  all   19.88    0.00    2.09    0.23    0.00    0.27    0.00    0.00   77.53
08:39:53 PM    0   20.52    0.00    2.38    0.42    0.00    0.46    0.00    0.00   76.21
08:39:53 PM    1   20.19    0.00    1.85    0.51    0.00    0.11    0.00    0.00   77.35
08:39:53 PM    2   19.95    0.00    1.81    0.48    0.00    0.11    0.00    0.00   77.65
08:39:53 PM    3   19.92    0.00    2.33    0.20    0.00    0.38    0.00    0.00   77.16
08:39:53 PM    4   19.82    0.00    2.28    0.39    0.00    0.45    0.00    0.00   77.07
08:39:53 PM    5   19.74    0.00    2.33    0.19    0.00    0.38    0.00    0.00   77.36
08:39:53 PM    6   20.14    0.00    1.64    0.13    0.00    0.01    0.00    0.00   78.09
08:39:53 PM    7   19.65    0.00    2.19    0.13    0.00    0.35    0.00    0.00   77.69
08:39:53 PM    8   19.38    0.00    2.20    0.11    0.00    0.34    0.00    0.00   77.97
08:39:53 PM    9   19.78    0.00    2.23    0.07    0.00    0.35    0.00    0.00   77.57
08:39:53 PM   10   19.53    0.00    1.59    0.11    0.00    0.01    0.00    0.00   78.76
08:39:53 PM   11   19.93    0.00    2.21    0.07    0.00    0.34    0.00    0.00   77.45

hoặc

$ cat /proc/cpuinfo
.....

Hiểu và đánh giá “tải trung bình” như thế nào?

Bên cạnh chỉ số tận dụng CPU bạn có thêm 1 chỉ số nữa gọi là “tải trung bình”. Bạn nên hiểu 2 giá trị này thế nào?

Tỉ lệ tận dụng CPU nói rằng một process sử dụng CPU nhiều hay ít. Giả sử bạn có một tính toán khá lớn (ví dụ sắp xếp 10GB dữ liệu), phần lớn thời gian của CPU của bạn chắc chắn sẽ bận rộn so sánh và di chuyển dữ liệu. Phần trăm sử dụng CPU sẽ cao, thời gian rảnh (idle) của CPU chắc sẽ thấp.

Tải trung bình nói rằng số lượng process đang đợi CPU là lớn hay nhỏ. Nếu số lượng process đợi CPU lớn, thời gian một process đợi sẽ dài, thời gian hoàn thành tác vụ của process đó sẽ dài. Bạn sẽ phải chờ kết quả lâu hơn. Ngược lại nếu số lượng process đợi thấp, bạn sẽ không phải đợi các process khác. Thời gian bạn đợi kết quả sẽ chỉ là thời gian tính toán.

Làm thế nào để biết được số lượng process đang đợi CPU là lớn hay nhỏ? Giống như trường hợp trạm thu phí, nếu số lượng process lớn hơn số lượng lõi CPU, chắc chắn sẽ phải có process đợi. Ngược lại nếu số lượng process nhỏ hơn số lượng lõi CPU, các process hầu như sẽ không phải xếp hàng chờ đợi mà sẽ được OS gán cho lõi đang rảnh rỗi tính toán.

Từ đây đặt ra câu hỏi: “đánh giá hiệu năng máy tính dựa vào tỉ lệ sử dụng CPU và tải trung bình như thế nào?”.

Việc đánh giá hiệu năng CPU tùy thuộc vào từng bài toán cụ thể. Ta sẽ đánh giá về hiệu năng sử dụng CPU qua các trường hợp sau (giả sử máy tính có 6 cores 12 threads - ví dụ Intel Xeon):

  • Tỉ lệ sử dụng CPU thấp (1%), tải CPU thấp (3 - 3 processes / 12 cores)
  • Tỉ lệ sử dụng CPU cao (80%), tải CPU thấp (3 - 3 processes / 12 cores)
  • Tỉ lệ sử dụng CPU thấp (1%), tải CPU cao (18 - 18 processes / 12 cores)
  • Tỉ lệ sử dụng CPU cao (80%), tải CPU cao (18 - 18 processes / 12 cores)

Trong trường hợp đầu, máy tính của bạn hầu như không dùng CPU mấy. CPU dành hầu hết thời gian cho tính toán thấp, số lượng process cũng không cao. Đứng từ góc độ chi phí, bạn đã chi tiền mua 1 CPu quá tốt so với nhu cầu thực tế :-)

Trường hợp 2, bạn đang sử dụng CPU ở mức khá. Bạn bắt CPU tính toán cật lực. Tuy vậy tải trung bình của CPU chỉ có 3, có nghĩa là năng lực CPU của bạn vẫn còn rất lớn mà bạn hoàn toàn có thể tận dụng. Bạn hoàn toán có thể bật thêm 9 processes với mức tính toán như hiện tại mới có thể tận dụng được hết hiệu năng của CPU.

Trường hợp 3 khá lý thú. CPU của bạn được dùng cho những tính toán rất nhẹ nhàng có thể xong ngay lập tức nhưng số lượng process cần CPU lại khá cao. Điều này nói lên rằng CPU của bạn đang bị quá tải process. Có nhiều lý do dẫn đến trường hợp này và mỗi trường hợp có nhiều cách giải quyết khác nhau. Một ví dụ cho trường hợp này là máy chủ web. Việc render các trang web là tính toán không hề nặng, tuy vậy với các máy chủ web chịu trafic lớn (số lượng connection lớn), các process phục vụ request sẽ phải xếp hàng dẫn đến tình trạng trang web bị phục vụ thời gian kéo dài hơn. Một ví dụ khác là máy chủ dành thời gian chủ yếu đợi thao tác vào ra (I/O) chẳng hạn nhưng truy vấn cơ sở dữ liệu. Số lượng query lớn, số lượng truy vấn cần sắp xếp lớn nhưng dữ liệu cần sắp xếp lại bé, thời gian đợi dữ liệu từ đĩa cứng lại cao. Vì vậy phần lớn CPU sẽ idle, nhưng tải CPU vẫn cao. Đối với trường hợp này, ta chỉ có cách là mua CPU với tần số thấp hơn và chia tải ra nhiều máy hơn để tối ưu hóa chi phí.

Trường hợp 4 là trường hợp bạn đang sử dụng CPU một cách hiệu quả nhất. Mỗi cores đều bận rộn tính toán và hầu hết các cores đều được cho sử dụng. Tùy bài toán tính toán mà trường hợp này có thể là tốt hay xấu. Nếu đây là máy chủ web có lẽ đã đến lúc bạn mua thêm máy tính.

Trường hợp thực tế

Hiểu được ý nghĩa của tải trung bình, chúng ta hiểu rằng sử dụng CPU hiệu quả có nghĩa là phải overload CPU. Một máy tính với CPU được sử dụng hết công suất suất là một máy tính được sử dụng tốt. Nắm được cách sử dụng vũ khí tải trung bình, chúng ta sẽ thử áp dụng cho 2 trường hợp thực tế.

Cấu hình máy chủ web 1

Bạn có máy tính chuyên trả về file static (css, image, js). Bạn sử dụng nginx và cấu hình để nginx trả về dữ liệu trong một thư mục nhất định. Bạn sẽ cấu hình nginx với bao nhiêu workers.

Trả lời: 12! Nếu bạn cấu hình ít hơn 12 workers, khả năng cao là CPU của máy tính bạn đang không được sử dụng hết công suất. Tại một thời điểm nào đó sẽ có một vì cores rong chơi.

Cấu hình máy chủ web 2

Giả sử bạn có máy chủ web 12 cores (logic :-)) và load average hiện tại là 5. Liệu đã đến lúc bạn mua thêm máy chủ mới chưa?

Trả lời: Không biết :-). Nếu máy chủ của bạn dành phần lớn thời gian idle đợi dữ liệu từ đĩa cứng hoặc cơ sở dữ liệu, nút thắt cổ chai hệ thống của bạn không phải là CPU mà có thể là cơ sở dữ liệu hoặc là đĩa cứng (thao tác I/O). Nếu cơ sở dữ liệu của bạn chưa hết công suất (I/O chưa hết công suất), bạn hoàn toàn không cần mua thêm máy chủ web. Bạn có thể cầu hình lại nginx / gunicorn…) để load average cao hơn (không quá 12 - số lượng cores) nhằm tận dụng hết năng lực của CPU của máy tính hiện tại).

Cấu hình hadoop

hadoop nổi tiếng trong giới BigData. Một datanode chạy các thủ tục map / reduce viết bằng java để lấy 1 block dữ liệu từ ổ cứng; chạy thao tác map để trích xuất dữ liệu; chạy thao tác reduce để tổng hợp dữ liệu. Một datanode thực hiện rất nhiều truy vấn dữ liệu từ ổ cúng cũng như sử dụng rất nhiều cpu cho thao tác sắp xếp, tổng hợp dữ liệu. Với 1 máy tính 12 cores, bạn sẽ cấu hình bao nhiêu java process cho thao tác map/reduce?

Trả lời: Không biết :-) nhưng chắc chắn là lớn hơn 12. Bạn sẽ bất ngờ vì thấy câu trả lời hơi khác máy chủ web dù rằng bài toán có vẻ giống nhau! Lý do là: mô hình map/reduce của hadoop cần rất nhiều dữ liệu do vậy truy vấn đĩa cứng sẽ rất cao, thao tác I/O lớn. Dù thao tác sắp xếp dữ liệu cũng khá tốn CPU nhưng để có dữ liệu sắp xếp, 1 map process vẫn cần thời gian để chờ dữ liệu từ ổ cúng. Trong khoảng thời gian này CPU sẽ idle. Nếu bạn chỉ cấu hình số lượng map/reduce là 12 (bằng số lượng cores), sẽ có 1 khoảng thời gian mà các cores không làm việc vì phải chờ đĩa cứng. Vì vậy CPU thực chất sẽ có những lúc rất bận và những lúc rất rảnh. Để hạn chế thời gian rảnh của CPU, “best-practice” sẽ là overload CPU bằng cách cấu hình cho số lượng process lớn hơn số cores. Tỉ lệ được khuyến cáo là 1.5 lần. Nhờ vậy trong khi có những process đợi I/O, CPU sẽ bận rộn với các process trước đó.

Cấu hình cụ thể là bài toán tùy trường hợp. Bạn nên xem bản chất bài toán và hành vi của máy chủ trước khi cấu hình

Kết luận

Bài viết đã giải thích ý nghĩa của load-average, một chỉ số quan trọng cũng như giới thiệu một số trường hợp cấu hình thực tế liên quan đến load-average. Hy vọng qua bài viết này, bạn hiểu được ý nghĩa của load-average, áp dụng vào thực tiễn công việc sử dụng máy tính hiệu quả nhất với chi phí tốt nhất.

Câu hỏi phụ :-)

Một câu hỏi phỏng vấn vị trí SRE của Google:

Lệnh uptime trả về 3 kết quả Load Average. 3 con số này là gì?

Tài liệu tham khảo

  1. hadoop operations
  2. Computer Architecture, A Quantitative Approach
  3. http://nginx.org/en/docs/
Comments

Là một lập trình viên, công việc hàng ngày của bạn là viết code. Viết code cũng như xây dựng vậy, bạn phải đắp từng viên gạch để xây nên một ngôi nhà. Một ngôi nhà có chắc chắn và dễ thay đổi hay không không chỉ dựa vào việc bạn có dùng gạch tốt hay không, mà còn phụ thuộc rất lớn vào nhà thiết kế.

Gần đây tôi có đọc cuốn sách tựa đề ‘Practical Object Oriented Design in Ruby’ của Sandi Metz, một diễn giả ưa thích của tôi. Cuốn sách dành phần lớn để nói về các kĩ thuật thiết kế phần mềm với đối tượng là ngôn ngữ là Ruby. Tuy nhiên có rất nhiều ý tưởng của tác giả mà không chỉ giới hạn ở Ruby nói chung mà có thể áp dụng cho bất kì ngôn ngữ nào. Trong bài viết này tôi sẽ liệt kê ra một số ý trong cuốn sách mà tôi thấy rất hay và đáng nhớ.

Why

Hãy bắt đầu từ việc tại sao nên dành thời gian cho việc thiết kế phần mềm:

  • Việc nên dành một khoảng thời gian tương đối cho việc thiết kế phần mềm một cách nghiêm chỉnh hay không phụ thuộc vào việc: Logic mà bạn sẽ viết có tầm ảnh hưởng như thế nào đến hiện tại và tương lai. Ảnh hưởng đến hiện tại là nhiều khi một tính năng có ngay lập tức là quan trọng hơn cả. Ảnh hưởng đến tương lai là khi mà tính năng mà bạn code một cách cẩu thả sẽ ảnh hưởng vô cùng lớn đến tốc độ phát triển trong tương lai, gây ra các lỗi nghiêm trọng. Hãy nhìn việc thiết kế nghiêm chỉnh hay không như là một món nợ kĩ thuật (technical debt). Bạn thiết kế cẩu thả ở thời điểm hiện tại cũng như là bạn vay mượn thời gian ở tương lai vậy. Nếu khoảng thời gian đó không đến thì sẽ không sao, nhưng nếu đằng nào nó cũng xảy đến thì hãy suy nghĩ thật kĩ, bạn có thể đang làm mất thời gian trong tương lai của mình đó :).

How

Vậy việc nên dành thời gian nghiêm chỉnh để thiết kế phần mêm là nên, thì chúng ta nên thiết kế theo phương pháp thế nào:

  • Từ xưa, việc thiết kế phần mêm thường được thực hiện theo mô hình thác nước (water flow). Mô hình này được tiến hành từ trên cao xuống thấp qua các bước như nhận yêu cầu (requirement), thiết kế kiến trúc (architecture design), thiết kế cơ bản (Basic design).. Tuy nhiên mô hình này đòi hỏi bạn phải nắm rõ 90% yêu cẩu của dự án ngày từ đầu, và việc này gần như là không thể. Do đó gần đây xu hướng là Agile development, khi mà việc phát triển chia thành nhiều chặng nhỏ, mỗi chặng sẽ có yêu cầu rõ ràng, và qua mỗi chặng sẽ đánh giá lại tính khả thi của dự án và đưa ra yêu cầu cho chặng tiếp theo. Về cơ bản thì Agile development có tính khả thi hơn cả khi mà không ai có thể đoán trước được sản phẩm đầu ra khi nào sẽ hoàn thành và nên có tính năng ra sao.

Detail

Khi đã quyết được phương pháp thiết kế, việc quan trọng nhất, khó nhất, đó là bắt tay vào làm, bắt tay vào thiết kế chương trình. Để làm được việc này tốt quả thật là rất khó, bởi vì không có một tiêu chuẩn chung nào có thể áp dụng cho mọi yêu cầu, mọi chương trình. Bản thân tôi cũng là một junior software developer, nên tôi luôn gặp khó khăn mỗi khi viết một chương trình từ đầu (from the scratch). Có rất nhiều cách để giảm khó khăn, và tăng khả năng thiết kế của bạn như: nắm vững về các design pattern, nắm vững về domain logic, đọc về kiến trúc của các phần mêm open source nổi tiếng, và sử dụng các “luật” về thiết kế. Ở dưới đây tôi sẽ nói về một số “luật” mà cuốn sách đề cập đến, mà bản thân tôi thấy khá hữu dụng.

  • Rule 1: Nền tảng cơ bản của việt thiết kế hướng đối tượng, là việc các đối tượng thao tác với nhau qua việc gửi thông điệp (sending message). Do đó mà việc thiết kế một phần mêm sẽ xoay quanh việc bạn thiết kế sao cho các đối tượng gửi thông điệp cho nhau thông qua một interface dễ hiểu nhất, rõ ràng nhất. Hãy luôn hình dung bài toán của bạn sẽ được giải quyết thông qua một loạt các đối tượng gửi rất nhiều loại thông điệp cho nhau, bạn sẽ hình dung được kiến trúc tổng thể của chương trình dễ dàng hơn.

  • Rule 2: Single Responsibility: Đây là một luật khá cơ bản trong thiết kế hướng đối tượng. Ai cũng biết về luật này nhưng rất khó để làm theo, nhất là khi khối lượng chương trình tăng lên, và công việc chính của bạn hàng ngày là thêm logic vào một code base đã có. Luật này nói rằng mỗi class chỉ nên đảm trách một vai trò duy nhất. Làm thế nào để đảm bảo tính chất này là một việc khá mơ hồ. Cuốn sách nói rằng với mỗi class, bạn nên có thử mô tả về nó chỉ trong 1 câu. Làm được việc này một cách dễ dàng đảm bảo cho việc logic của class đó thống nhất và không bị lai tạp.

  • Rule 3: Giảm sự kết dính của code (Writing loosely coupled code). Cá nhân tôi thấy rule này là rule quan trọng bậc nhất trong việc thiết kế phần mềm. Muốn đánh giá một phần mềm được thiết kế tồi hay không, hãy nhìn vào việc các logic có bị kết dính(couple) hay phụ thuộc vào nhau hay không. Vậy các bạn sẽ hỏi “kết dính” cụ thể ở đây có nghĩa là gì? Sự kết dính được hình thành khi logic này “phụ thuộc” vào logic khác. Cụ thể hơn ở khái niệm phụ thuộc, đó là việc mà khi mà một trong logic của class A lại chứa các logic class B, hay nói cách khác là khi A “biết” quá nhiều về B thì khi đó A sẽ phụ thuộc vào B. Khái niệm này hay được nhắc đến bằng những cụm từ khác như là logic hiding, tức khi thiết kế một class, bạn phải giấu logic của class đó càng nhiều càng tốt. Đó chính là lý do tại sao các ngôn ngữ như java có những keyword như public, private hay protected. Vậy quay lại từ đầu, để giảm sự kết dính của code thì chúng ta phải làm một việc là thiết kế sao cho các class không phụ thuộc vào nhau, và “biết” càng ít về nhau càng tốt. Vì vậy mỗi khi bạn viết một đoạn code nào đó, bạn hãy tự đọc lại và xem đoạn code đó có sử dụng quá nhiều logic của một class hay logic bên ngoài không. Để giải quyết cho việc “writing loosely coupled code” thì có khá nhiều kĩ thuật nổi tiếng như là: Inject Dependencies, Isolate Dependencies, Reversing Dependencies mà nếu có dịp tôi sẽ giới thiệu trong một bài viết khác. Ngoài ra còn có một luật rất hữu dụng để giải quyết vấn đề kết dính của code được gọi là Law of Demeter, các bạn có thể tham khảo ở đường link tôi vừa gửi.

Auto Testing

Bản thân việc testing không nằm trong khâu “thiết kế” phần mềm. Auto testing (hay là unit test) chỉ là một bước để đảm bảo rằng logic hiện tại đang có là đang chạy “gần như là” tốt (nói gần như là do unit test không thể đảm bảo 100% việc “chạy tốt” của tất cả mọi logic. Tuy nhiên chỉ việc đảm bảo “gần như” tốt thôi đã cho thấy tầm quan trọng của testing. Có một việc mà bất kì một nhà phát triển nào khi mới bắt đầu viết test, và ngay cả những người đã quá quen việc kĩ thuật TDD (Test Driven Development) cũng sẽ băn khoăn, đó là việc nên test cái gì. Trong cuốn sách Sandi đã chia khá rõ ràng về 2 phần mà bạn nên test đó là:

  • Test Incomming Message
  • Test Ougoing Message

Như tôi đã đề cập ở trên, bản chất của việc thiết kế hướng đối tượng xoay quanh việc các class sẽ gửi message cho nhau. Do đó khi test chúng ta cũng nên xoay quanh khái niệm mesage này. Một cách đơn giản, Incomming Message tức là các message được “gửi” đến một object X, và test các message đó tương đương với việc bạn sẽ test các interface của object X đó được công khai (public interface) ra ngoài. Outgoing Message hơi phức tạp hơn một chút, giả sử bạn có một object X với method Foo, trong Foo sẽ gọi method Bar để thực hiện một logic nào đó. Việc test Foo sẽ gọi Bar đúng N lần, với kết quả nhất định sẽ được gọi là test Outgoing Message. Việc chia ra làm 2 loại message cần test sẽ giúp cho bạn nhìn thấy một cách rõ ràng hơn cái gì nên test, và cái gì không nên test.

Conclusion

Ở trên tôi đã trình bày về một số suy nghĩ của cá nhân, và các suy nghĩ của Sandi Metz trong cuốn sách về thiết kế phần mềm. Bản thân việc thiết kế được phần mềm tốt là rất khó, mà mỗi một dạng phần mềm, với mỗi một logic domain lại có một cách giải quyết riêng. Không có một cách giải quyết nào chung cho mọi bài toán cả, nhưng có một số qui tắc chung mà bán có thể áp dụng được cho nhiều bài toán khác nhau. Để nắm được các qui tắc đó đòi hỏi bạn không những phải đọc nhiều, làm nhiều, tích luỹ nhiều kinh nghiệm, mà còn dựa trên việc bạn thất bại nhiều nữa. Tạo ra các phần mềm tồi, khó bảo trì cũng là một bước đệm tốt để bạn rút kinh nghiệm cho các lần sau :).

Copyright © 2015 kỹ thuật máy tính