<?xml version="1.0" encoding="UTF-8"?><?xml-stylesheet href="/scripts/pretty-feed-v3.xsl" type="text/xsl"?><rss version="2.0" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>Blog - Dat Le</title><description>Một blog đơn giản.</description><link>https://lethanhdat.com/</link><language>vi</language><item><title>Pentomino và Sudoku: tự tạo đề, in A4 rồi chơi</title><link>https://lethanhdat.com/vi/blog/pentomino-sudoku-a4-puzzles/</link><guid isPermaLink="true">https://lethanhdat.com/vi/blog/pentomino-sudoku-a4-puzzles/</guid><description>Hai công cụ nhỏ để tự sinh đề xếp hình và Sudoku theo ý thích, in ra giấy A4 để chơi offline.</description><pubDate>Mon, 26 Oct 2026 17:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Tôi vừa làm thêm hai trò chơi nhỏ: &lt;strong&gt;Pentomino&lt;/strong&gt; và &lt;strong&gt;Sudoku&lt;/strong&gt;. Lần này, phần tôi muốn giới thiệu nhất là &lt;strong&gt;generator: công cụ tự sinh đề&lt;/strong&gt;, để mỗi lần muốn chơi lại có một thử thách mới, không phải đi tìm đề có sẵn.&lt;/p&gt;
&lt;h3 id=&quot;pentomino-tạo-đề-xếp-hình-của-riêng-bạn&quot;&gt;Pentomino: tạo đề xếp hình của riêng bạn&lt;/h3&gt;
&lt;p&gt;Với &lt;strong&gt;&lt;a href=&quot;https://tdat00.github.io/pentomino/generator/&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;Pentomino Generator&lt;/a&gt;&lt;/strong&gt;, bạn xếp các mảnh ghép sao cho phủ kín bàn, không chồng lên nhau. Bạn có thể chọn hình dạng bàn hoặc tự chặn bớt ô, sinh bộ mảnh ghép phù hợp, rồi tạo đề theo độ khó mong muốn. Có cả tùy chọn yêu cầu đề chỉ có một lời giải.&lt;/p&gt;
&lt;p&gt;Bản in A4 có bàn chơi và các mảnh để cắt rời: in ra, lấy kéo cắt là có một bộ xếp hình bằng giấy. Bạn cũng có thể in kèm lời giải để đối chiếu sau.&lt;/p&gt;
&lt;img src=&quot;https://lethanhdat.com/images/pentomino-generator.gif&quot; alt=&quot;Tạo bàn, sinh đề và tùy chọn in của Pentomino Generator&quot; class=&quot;zoomable&quot;&gt;
&lt;h3 id=&quot;sudoku-không-chỉ-có-bảng-99&quot;&gt;Sudoku: không chỉ có bảng 9×9&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;&lt;a href=&quot;https://tdat00.github.io/sudoku/generator/&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;Sudoku Generator&lt;/a&gt;&lt;/strong&gt; có 15 biến thể, từ Sudoku cổ điển đến Killer, Jigsaw hay Samurai, cùng nhiều mức độ khó. Một điểm vui nữa là bạn có thể xem lại từng bước máy đã tạo ra đề.&lt;/p&gt;
&lt;p&gt;Bạn có thể tạo nhiều đề, chọn in 1, 2 hoặc 4 đề mỗi trang và để lời giải ở trang riêng. In trên giấy A4 hoặc lưu thành PDF là có ngay một tập đề để dành.&lt;/p&gt;
&lt;img src=&quot;https://lethanhdat.com/images/sudoku-generator.gif&quot; alt=&quot;Sinh đề, khám phá biến thể và tùy chọn in của Sudoku Generator&quot; class=&quot;zoomable&quot;&gt;
&lt;h3 id=&quot;tạo-đề-trên-máy-chơi-trên-giấy&quot;&gt;Tạo đề trên máy, chơi trên giấy&lt;/h3&gt;
&lt;p&gt;Cả hai đều có giao diện tiếng Việt, không cần tài khoản hay cài đặt. Mỗi trò cũng có &lt;strong&gt;player&lt;/strong&gt; để chơi ngay trong trình duyệt, nhưng đó chỉ là lựa chọn thêm: &lt;strong&gt;sinh đề, in A4 rồi rời màn hình&lt;/strong&gt; mới là cách chơi tôi muốn gợi ý lần này.&lt;/p&gt;
&lt;p&gt;Thử tạo vài đề, chuẩn bị giấy bút (và một chiếc kéo nếu chọn Pentomino), rồi chơi lúc rảnh nhé.&lt;/p&gt;</content:encoded></item><item><title>Captain Tsubasa II Password Generator: Tuổi thơ được làm lại bằng AI</title><link>https://lethanhdat.com/vi/blog/captain-tsubasa-ii-password-generator/</link><guid isPermaLink="true">https://lethanhdat.com/vi/blog/captain-tsubasa-ii-password-generator/</guid><description>Công cụ tạo password chạy ngay trên trình duyệt cho game NES/Famicom Captain Tsubasa Vol. II - Super Striker, được xây dựng lại với sự hỗ trợ của AI từ một thuật toán có sẵn.</description><pubDate>Sun, 11 Oct 2026 02:00:00 GMT</pubDate><content:encoded>&lt;img src=&quot;https://lethanhdat.com/images/captain-tsubasa.jpg&quot; alt=&quot;Màn hình tiêu đề và các cảnh trong game Captain Tsubasa Vol. II - Super Striker&quot; class=&quot;zoomable&quot;&gt;
&lt;p&gt;Nếu lớn lên vào những năm 90, rất có thể &lt;strong&gt;Captain Tsubasa Vol. II: Super Striker&lt;/strong&gt; (キャプテン翼II スーパーストライカー) đã là một phần tuổi thơ của bạn. Game ra mắt trên Famicom/NES do Tecmo phát hành năm 1990, dựa trên bộ manga nổi tiếng của Yoichi Takahashi. Nó biến bóng đá thành một trò chơi căng thẳng theo từng lệnh: chọn một chiêu, tiêu hao guts, rồi hy vọng người hùng của bạn tung được Drive Shot hay Cyclone trước khi thủ môn kịp cản phá.&lt;/p&gt;
&lt;p&gt;Với thế hệ millennial, đó không chỉ là một trò chơi. Đó là băng game cắm vào chiếc máy mượn của bạn, là câu chuyện bằng tiếng Nhật mà ta không đọc được, và là người bạn ngồi bên cạnh cùng mày mò từng nút bấm một.&lt;/p&gt;
&lt;h2 id=&quot;màn-hình-password&quot;&gt;Màn hình password&lt;/h2&gt;
&lt;p&gt;Game không có pin lưu. Muốn chơi tiếp lần sau, bạn phải ghi lại một &lt;strong&gt;password gồm 18 ký tự&lt;/strong&gt; là các chữ kana của Nhật rồi nhập vào màn hình nhập mã. Password đó lưu lại mọi thứ: vòng đấu bạn đã đến, level và kinh nghiệm của các cầu thủ, các cú sút đặc biệt, và cả guts của Misugi.&lt;/p&gt;
&lt;p&gt;Ngày ấy, password hoặc là chép từ tạp chí, hoặc là mất luôn. Ngày nay ta có thể tự tạo password cho riêng mình.&lt;/p&gt;
&lt;h2 id=&quot;công-cụ&quot;&gt;Công cụ&lt;/h2&gt;
&lt;p&gt;Tôi làm một công cụ tạo password để bạn có thể bắt đầu từ &lt;strong&gt;bất kỳ vòng đấu nào&lt;/strong&gt;, với &lt;strong&gt;level cầu thủ tùy ý&lt;/strong&gt;:&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://lethanhdat.com/toys/captain-tsubasa-ii-super-striker.html&quot;&gt;&lt;strong&gt;Mở Captain Tsubasa II Password Generator&lt;/strong&gt;&lt;/a&gt;&lt;/p&gt;
&lt;img src=&quot;https://lethanhdat.com/images/captain-tsubasa-password-generator.png&quot; alt=&quot;Captain Tsubasa II password generator&quot; class=&quot;size-75 zoomable&quot;&gt;
&lt;p&gt;Bạn có thể làm gì với nó:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Chọn vòng đấu.&lt;/strong&gt; Cả 33 vòng đấu chính thức đều được liệt kê cùng đối thủ, từ Fluminense ở vòng mở màn đến Brazil ở vòng chung kết World Cup.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Tinh chỉnh từng nhóm cầu thủ.&lt;/strong&gt; Đặt Level và Exp cho từng nhóm trong mười nhóm. Các cầu thủ hiển thị thay đổi theo vòng đấu hiện tại: São Paulo, Nankatsu hoặc Nhật Bản.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Mở khóa khả năng.&lt;/strong&gt; Bật hoặc tắt Cyclone Shot, Drive Overhead Shot, và việc guts của Misugi có thấp hơn 100 hay không.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Thử vận may.&lt;/strong&gt; &lt;em&gt;Random match&lt;/em&gt; tạo ngẫu nhiên toàn bộ thiết lập, với level cầu thủ phù hợp với vòng đấu đã chọn. Hai lựa chọn khác mở rộng phạm vi: không cân bằng, mỗi nhóm có một level ngẫu nhiên riêng nằm giữa mức khó và mức dễ của vòng đấu, cộng thêm 3 level ở mỗi đầu; hoặc ngẫu nhiên hoàn toàn, mỗi level được chọn ở bất kỳ đâu từ mức hợp lý thấp nhất đến cao nhất của vòng đấu.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Theo dõi ngay trong game.&lt;/strong&gt; Password cập nhật khi bạn gõ, còn &lt;em&gt;Game input screen&lt;/em&gt; tô sáng đúng phím cần bấm tiếp theo. Dùng &lt;code&gt;&amp;#x3C;&lt;/code&gt; và &lt;code&gt;&gt;&lt;/code&gt; để lần lượt đi qua cả 18 ký tự.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Đổi ngôn ngữ.&lt;/strong&gt; Màn hình nhập mã có thể hiển thị theo kana tiếng Nhật hoặc theo bộ ký tự của bản tiếng Anh.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Mọi thứ chạy ngay trong trình duyệt của bạn, không cần cài đặt gì và không gửi dữ liệu đi đâu cả.&lt;/p&gt;
&lt;h2 id=&quot;làm-lại-với-ai&quot;&gt;Làm lại với AI&lt;/h2&gt;
&lt;p&gt;Tôi không phải người nghĩ ra thuật toán. Nó đến từ &lt;a href=&quot;https://cah4e3.shedevr.org.ru/cheatsbase_passg.php#249&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;password generator của cah4e3&lt;/a&gt;, còn chi tiết thuật toán được ghi công cho Gennadiy_Master. Công cụ đó đã phục vụ cộng đồng rất tốt, nhưng giao diện của nó đã lỗi thời.&lt;/p&gt;
&lt;p&gt;Với sự hỗ trợ của AI, tôi xây dựng lại chính thuật toán đó bằng HTML, CSS và JavaScript thuần, với giao diện gọn gàng và hiện đại hơn: nút tăng giảm và thanh trượt thay cho các ô nhập thô, bàn phím nhập được tô sáng, tooltip giải thích từng tùy chọn, và bố cục dùng tốt trên điện thoại.&lt;/p&gt;
&lt;p&gt;Bên trong, password đóng gói level, kinh nghiệm, vòng đấu, các cờ và một seed 6 bit thành 18 giá trị 6 bit. Sau đó nó cộng thêm checksum, xáo trộn các giá trị bằng một bảng thay thế, rồi dịch từng giá trị theo một độ lệch tăng dần. Seed không ảnh hưởng gì trong game, nhưng đổi seed sẽ cho bạn một password khác mà vẫn hợp lệ với cùng một thiết lập.&lt;/p&gt;
&lt;p&gt;Giờ thì hãy lấy emulator của bạn (hoặc chiếc máy Famiclone cũ ấy), chọn vòng đấu, và ghi vài bàn cho đỡ nhớ.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Lưu ý:&lt;/strong&gt; Bạn có thể chơi game này trực tuyến tại &lt;a href=&quot;https://www.retrogames.cc/nes-games/c/index.html&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;https://www.retrogames.cc/nes-games/c/index.html&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;</content:encoded><category>captain-tsubasa</category><category>retro-games</category><category>ai</category></item><item><title>Ba trò chơi nhỏ: tìm chữ, tìm số và tìm con vật</title><link>https://lethanhdat.com/vi/blog/three-small-games/</link><guid isPermaLink="true">https://lethanhdat.com/vi/blog/three-small-games/</guid><description>Ba trò chơi đơn giản để vừa chơi, vừa rèn khả năng quan sát, có thể in ra giấy A4.</description><pubDate>Fri, 11 Sep 2026 17:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Tôi làm ba trò chơi nhỏ cho những lúc muốn vừa chơi, vừa rèn khả năng quan sát. Cả ba đều có thể in ra giấy A4 để chơi cùng nhau.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;&lt;a href=&quot;https://lethanhdat.com/toys/word-search-puzzle.html&quot;&gt;Mê cung chữ&lt;/a&gt;:&lt;/strong&gt; Tìm những từ ẩn trong bảng chữ do bạn tự tạo.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;a href=&quot;https://lethanhdat.com/toys/animal-search-puzzle.html&quot;&gt;Mê cung con vật&lt;/a&gt;:&lt;/strong&gt; Tìm các chuỗi con vật trong bảng hình, phù hợp cả với các bé chưa biết chữ.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;a href=&quot;https://lethanhdat.com/toys/find-number-puzzle.html&quot;&gt;Trò chơi tìm số&lt;/a&gt;:&lt;/strong&gt; Thử tài tinh mắt với bảng số xáo trộn trong khoảng số bạn chọn.&lt;/li&gt;
&lt;/ul&gt;
&lt;img src=&quot;https://lethanhdat.com/images/animal-search-puzzle.gif&quot; alt=&quot;Minh họa trò chơi Mê cung con vật&quot; class=&quot;zoomable&quot;&gt;</content:encoded></item><item><title>Adaptive Sampling: Giảm 99% dữ liệu Distributed Tracing mà không mất khả năng quan sát</title><link>https://lethanhdat.com/vi/blog/adaptive-sampling-distributed-tracing/</link><guid isPermaLink="true">https://lethanhdat.com/vi/blog/adaptive-sampling-distributed-tracing/</guid><description>Cách chúng tôi mở rộng Distributed Tracing trên hàng nghìn máy chủ Microsoft 365 mà không bị ngập trong dữ liệu, đồng thời không đánh đổi độ tin cậy của Active Monitoring.</description><pubDate>Sat, 02 May 2026 02:00:00 GMT</pubDate><content:encoded>&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;Tuyên bố miễn trừ: Tên các hệ thống trong bài đã được thay đổi để tránh tiết lộ thông tin mật. Mọi con số đều chính xác.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Trong hai năm qua, nhóm của tôi tại Microsoft đã tích hợp Distributed Tracing (DT) vào hầu hết các microservice quan trọng của &lt;strong&gt;Substrate&lt;/strong&gt;, nền tảng vận hành Microsoft 365. DT là công cụ chúng tôi tìm đến mỗi khi một API gặp sự cố: nó cho phép xác định chính xác node đã gây ra lỗi và chỉ ra những thành phần đang kéo độ trễ lên.&lt;/p&gt;
&lt;p&gt;DT kết hợp rất tự nhiên với một hệ thống do nhóm đối tác vận hành — &lt;strong&gt;Active Monitoring&lt;/strong&gt; — một đội probe tổng hợp liên tục gọi vào các dịch vụ nội bộ và máy chủ vật lý để đảm bảo mọi thứ luôn khỏe mạnh. Khi một probe thất bại, một trace là khác biệt giữa &lt;em&gt;“có chỗ nào đó đang hỏng”&lt;/em&gt; và &lt;em&gt;“lời gọi downstream cụ thể này đã hết thời gian chờ trên chiếc máy cụ thể này.”&lt;/em&gt;&lt;/p&gt;
&lt;h2 id=&quot;bài-toán-mở-rộng-quy-mô&quot;&gt;Bài toán mở rộng quy mô&lt;/h2&gt;
&lt;p&gt;Microsoft 365 chạy trên &lt;strong&gt;hàng trăm nghìn máy chủ vật lý&lt;/strong&gt; trải khắp thế giới. Chỉ riêng Active Monitoring đã tạo ra khoảng &lt;strong&gt;78 triệu request mỗi giờ&lt;/strong&gt; ở quy mô đó. Mỗi request được lấy mẫu đều tạo ra một trace, và mỗi trace mang theo hàng chục span qua nửa tá dịch vụ. Tổng khối lượng đơn giản là quá lớn để backend DT của chúng tôi tiếp nhận, lưu trữ và truy vấn với chi phí chấp nhận được.&lt;/p&gt;
&lt;p&gt;Vì vậy chúng tôi cần lấy mẫu (sampling). Điểm khó là ràng buộc mà Active Monitoring đặt ra:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Nếu một request tổng hợp thất bại, chúng tôi &lt;em&gt;bắt buộc&lt;/em&gt; phải có trace của nó. Không có ngoại lệ.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Mất trace thì cảnh báo trở nên vô dụng — on-call bị gọi dậy mà không có con đường nào để tìm ra nguyên nhân gốc. Điều đó còn tệ hơn là không có cảnh báo.&lt;/p&gt;
&lt;p&gt;Vậy thử thách thật sự không phải là “giảm dữ liệu”. Mà là: &lt;strong&gt;giảm dữ liệu thật mạnh tay, nhưng không bao giờ bỏ sót một request không khỏe mạnh.&lt;/strong&gt;&lt;/p&gt;
&lt;h2 id=&quot;vì-sao-các-sampler-có-sẵn-không-phù-hợp&quot;&gt;Vì sao các sampler có sẵn không phù hợp&lt;/h2&gt;
&lt;p&gt;OpenTelemetry cung cấp hai nhóm chiến lược lấy mẫu. Chúng tôi đã xem xét kỹ cả hai.&lt;/p&gt;
&lt;h3 id=&quot;head-based-ratio-sampler&quot;&gt;Head-based: Ratio Sampler&lt;/h3&gt;
&lt;p&gt;Head-based sampling quyết định giữ hay bỏ một trace ngay khi nó bắt đầu, trước khi có bất kỳ span nào được ghi nhận. Biến thể phổ biến nhất là &lt;strong&gt;ratio sampler&lt;/strong&gt;: giữ N% số trace, bỏ phần còn lại.&lt;/p&gt;
&lt;p&gt;Ưu điểm:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Rẻ. Quyết định được đưa ra cục bộ và tức thì.&lt;/li&gt;
&lt;li&gt;Khối lượng dữ liệu dự đoán được. Chọn 1% thì bạn nhận đúng 1%.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Nhược điểm (với trường hợp của chúng tôi):&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Nó mù về kết quả. Một request không khỏe mạnh có cơ hội được giữ lại ngang với một request khỏe mạnh.&lt;/li&gt;
&lt;li&gt;Ở quy mô toàn cầu, tỷ lệ lỗi &lt;em&gt;rất&lt;/em&gt; nhỏ — chỉ một phần nhỏ của một phần trăm. Bất kỳ tỷ lệ nào đủ thấp để vừa ngân sách dữ liệu của chúng tôi cũng sẽ âm thầm vứt bỏ phần lớn những lỗi mà chúng tôi sinh ra để điều tra.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;tail-based-sampler&quot;&gt;Tail-based Sampler&lt;/h3&gt;
&lt;p&gt;Tail-based sampling đưa ra quyết định &lt;em&gt;sau khi&lt;/em&gt; trace kết thúc, khi đã biết kết quả. Về lý thuyết nó hoàn hảo: giữ 100% trace thất bại, bỏ 100% trace thành công.&lt;/p&gt;
&lt;p&gt;Trên thực tế, nó có hai vấn đề hóa ra là điểm chết với chúng tôi:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Bạn phải đệm mọi span của mọi trace cho đến khi có quyết định.&lt;/strong&gt; Bản thân bộ đệm đó đã là một chi phí lưu trữ và bộ nhớ khổng lồ — bạn chỉ chuyển vấn đề đi chỗ khác chứ chưa giải quyết nó. Ở khối lượng của chúng tôi, bộ đệm “tạm thời” sẽ lớn hơn cả backend mà chúng tôi đang cố bảo vệ.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Bạn phải biết trace kết thúc ở đâu.&lt;/strong&gt; Tail-based sampling cần một “cái đuôi” — span cuối cùng — để kích hoạt quyết định. Microsoft 365 là một đồ thị dịch vụ rộng lớn và liên tục thay đổi. Không có một cái đuôi chuẩn duy nhất. Dịch vụ được thêm vào, đồ thị lời gọi dịch chuyển, các nhánh fan-out bất đồng bộ khiến “điểm kết thúc” trở nên mơ hồ. Bất kỳ heuristic nào chúng tôi chọn cũng sẽ lỗi thời trong vòng một quý.&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id=&quot;hình-hài-của-một-lời-giải-tốt-hơn-adaptive-sampling&quot;&gt;Hình hài của một lời giải tốt hơn: adaptive sampling&lt;/h2&gt;
&lt;p&gt;Trong lúc đào sâu các thảo luận của OpenTelemetry và các buổi nói chuyện tại hội nghị, tôi liên tục gặp thuật ngữ &lt;strong&gt;adaptive sampling&lt;/strong&gt; — ý tưởng rằng chính &lt;em&gt;cấu hình&lt;/em&gt; của sampler nên thay đổi theo thời gian để phản ứng với những gì hệ thống đang làm. Có những gợi ý, bản phác thảo và các mô tả dài một đoạn, nhưng không có thiết kế nào đã được kiểm chứng thực chiến để chúng tôi lấy về dùng.&lt;/p&gt;
&lt;p&gt;Vì vậy chúng tôi tự thiết kế. Nhận định cốt lõi nhỏ nhưng mạnh:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Một head-based ratio sampler thì nhanh và rẻ. Một tail-based sampler thì biết kết quả. Nếu chúng ta giữ head-based sampler, nhưng để &lt;em&gt;kết quả của vài phút trước&lt;/em&gt; quyết định tỷ lệ của nó thì sao?&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Chúng tôi chia công việc cho hai thành phần mới:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;AdaptiveSampler&lt;/code&gt;&lt;/strong&gt; là sampler tùy biến của chúng tôi, áp dụng cho Active Monitoring. Nó vẫn là head-based — quyết định diễn ra ngay khi trace bắt đầu, không cần đệm. Nhưng thay vì một tỷ lệ cố định duy nhất, nó giữ một bản đồ tỷ lệ theo từng server (&lt;code&gt;[{serverA: 1.0}, {serverB: 0.1}, ...]&lt;/code&gt;) do Config System đẩy xuống. Khi một request đến server X, nó tra tỷ lệ của X rồi tung một số ngẫu nhiên: &lt;strong&gt;SAMPLED&lt;/strong&gt; với xác suất đó, ngược lại &lt;strong&gt;DROP&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;AdaptiveSamplerWorker&lt;/code&gt;&lt;/strong&gt; là bộ điều khiển. Cứ 5 phút, nó đọc kết quả Active Monitoring trong 5 phút gần nhất từ MDM, chọn ra những server có &lt;strong&gt;ít nhất một request không khỏe mạnh&lt;/strong&gt; (các server &lt;em&gt;hot&lt;/em&gt;), rồi ghi một bản đồ tỷ lệ mới vào Config System: &lt;strong&gt;100%&lt;/strong&gt; cho các server hot, &lt;strong&gt;10%&lt;/strong&gt; cho tất cả server còn lại.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Sau đó Config System lan truyền bản đồ này tới mọi server Active Monitoring, nơi mỗi &lt;code&gt;AdaptiveSampler&lt;/code&gt; nhận thay đổi và điều chỉnh hành vi ở request kế tiếp.&lt;/p&gt;
&lt;p&gt;Cái giá phải trả là &lt;strong&gt;độ trễ thời gian&lt;/strong&gt;. Nếu một server lỗi lần đầu tiên tại thời điểm T, chúng tôi sẽ không lấy mẫu nó ở mức 100% cho đến khi lần chạy kế tiếp của worker tính ra bản đồ và Config System đẩy nó xuống. Câu hỏi trở thành: &lt;em&gt;độ trễ đó là bao lâu, và có chấp nhận được không?&lt;/em&gt;&lt;/p&gt;
&lt;h2 id=&quot;vì-sao-cách-này-hiệu-quả-ở-microsoft-365-hạ-tầng-sẵn-có&quot;&gt;Vì sao cách này hiệu quả ở Microsoft 365: hạ tầng sẵn có&lt;/h2&gt;
&lt;p&gt;Câu hỏi về độ trễ là chỗ thiết kế gặp may. Microsoft 365 vốn đã có hai mảnh hạ tầng mà chúng tôi có thể dựa vào:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Active Monitoring → MDM&lt;/strong&gt; (kho metrics của chúng tôi). Kết quả probe đi vào MDM với độ trễ tối đa &lt;strong&gt;~2 phút&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Config System → mọi server.&lt;/strong&gt; Các thay đổi cấu hình lan đến mọi server trên toàn thế giới trong vòng &lt;strong&gt;~5 phút&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;Ghép chúng lại:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Một request không khỏe mạnh xảy ra tại thời điểm &lt;strong&gt;T&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Kết quả của nó có thể truy vấn được trong MDM chậm nhất vào &lt;strong&gt;T + 2 phút&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;AdaptiveSamplerWorker&lt;/code&gt; đọc MDM, tính tỷ lệ mới và ghi bản đồ cập nhật vào Config System.&lt;/li&gt;
&lt;li&gt;Cấu hình mới đến server bị ảnh hưởng chậm nhất vào &lt;strong&gt;T + 7 phút&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Server đó duy trì mức lấy mẫu 100% qua cửa sổ quyết định kế tiếp — chậm nhất đến &lt;strong&gt;T + 12 phút&lt;/strong&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Vậy trong trường hợp xấu nhất, chỉ một request lỗi cũng cho chúng tôi một &lt;strong&gt;cửa sổ trace đầy đủ độ phân giải kéo dài 5–10 phút&lt;/strong&gt; trên server gặp vấn đề, bắt đầu khoảng 7 phút sau lỗi. Đủ dài để bắt được lỗi &lt;em&gt;kế tiếp&lt;/em&gt; nếu sự cố vẫn còn tiếp diễn — chính là trường hợp quan trọng nhất với on-call.&lt;/p&gt;
&lt;h2 id=&quot;thiết-kế-tổng-quan&quot;&gt;Thiết kế tổng quan&lt;/h2&gt;
&lt;p&gt;Bốn thành phần, một vòng lặp:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://lethanhdat.com/images/2026-05-02-adaptive-sampling-diagram.svg&quot; alt=&quot;Vòng điều khiển của adaptive sampler: Active Monitoring ghi kết quả vào MDM, AdaptiveSamplerWorker đọc MDM mỗi 5 phút, và Config System đẩy các tỷ lệ lấy mẫu mới trở lại Active Monitoring — khép kín vòng lặp.&quot; class=&quot;zoomable&quot;&gt;&lt;/p&gt;
&lt;p&gt;Thành phần mới duy nhất là &lt;strong&gt;&lt;code&gt;AdaptiveSampler&lt;/code&gt;&lt;/strong&gt; (sampler tùy biến, kế thừa lớp cơ sở của OpenTelemetry) và &lt;strong&gt;&lt;code&gt;AdaptiveSamplerWorker&lt;/code&gt;&lt;/strong&gt; (Azure Function, kích hoạt mỗi 5 phút). MDM, Config System và bản thân Active Monitoring đã có sẵn và do các nhóm khác duy trì. Chúng tôi không xây một nền tảng lấy mẫu — chúng tôi viết một sampler tùy biến nhỏ và một bộ điều khiển phản ứng để điều khiển nó.&lt;/p&gt;
&lt;p&gt;Một vài lưu ý về bộ điều khiển mà sơ đồ không cho thấy rõ:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Quyết định idempotent.&lt;/strong&gt; Mỗi lần chạy tính lại toàn bộ tập server hot từ đầu. Không có trạng thái tăng dần nào có thể bị hỏng; nếu worker sập rồi khởi động lại, lần chạy kế tiếp vẫn đúng.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Không hysteresis, có chủ ý.&lt;/strong&gt; Một server hot tại T và yên tĩnh tại T+5 sẽ quay về 10%. Chúng tôi từng cân nhắc một cơ chế cooldown (“giữ hot thêm hai chu kỳ sau khi phục hồi”) và quyết định không làm — lượng dữ liệu thêm không đáng với độ phức tạp tăng thêm, và 5 phút đệm ở lần lỗi kế tiếp đã là đủ.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Hai tỷ lệ, không phải một đường cong.&lt;/strong&gt; Chúng tôi từng thử thang trượt (lỗi càng nhiều → tỷ lệ càng cao) rồi bỏ. Nhị phân 100/10 dễ suy luận hơn, dễ gỡ lỗi hơn, và phần dữ liệu tiết kiệm được chủ yếu đến từ kích thước của phần fleet &lt;em&gt;yên tĩnh&lt;/em&gt;, chứ không phải từ việc tinh chỉnh phần ồn ào.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;kết-quả&quot;&gt;Kết quả&lt;/h2&gt;
&lt;p&gt;Sau khi triển khai:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Giảm ~99%&lt;/strong&gt; số trace khỏe mạnh mà backend DT tiếp nhận.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Giảm 0%&lt;/strong&gt; số trace không khỏe mạnh Active Monitoring thu được trong cửa sổ của nó.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Trong &lt;strong&gt;Test ring&lt;/strong&gt;, nơi lưu lượng không khỏe mạnh cao hơn do chủ ý thiết kế, mức tiết kiệm khá khiêm tốn — không có nhiều lưu lượng khỏe mạnh để sampler loại bỏ:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://lethanhdat.com/images/2026-01-21-active-monitoring-tdf.png&quot; alt=&quot;Lưu lượng Active Monitoring trong Test ring qua khoảng 7 ngày. Các cột khỏe mạnh (xanh dương) và không khỏe mạnh (cam) nằm trong cùng một dải; phần khỏe mạnh giảm nhẹ nhưng phần lớn khối lượng vẫn được giữ lại.&quot; class=&quot;zoomable&quot;&gt;
&lt;em&gt;Test ring: trace khỏe mạnh chỉ giảm khiêm tốn — phần lớn lưu lượng vốn không khỏe mạnh theo thiết kế, nên sampler không có nhiều thứ để bỏ.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Trong &lt;strong&gt;Global ring&lt;/strong&gt;, nơi fleet gần như hoàn toàn khỏe mạnh, mức giảm là không thể nhầm lẫn. Hãy nhìn nơi đợt triển khai đáp xuống:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://lethanhdat.com/images/2026-01-21-active-monitoring-ww.png&quot; alt=&quot;Lưu lượng Active Monitoring trong Global ring qua khoảng 7 ngày. Các trace khỏe mạnh rơi thẳng đứng khi AdaptiveSampler được bật — từ khoảng 400M xuống còn một phần nhỏ trên mỗi cột — trong khi các trace không khỏe mạnh gần như không đổi.&quot; class=&quot;zoomable&quot;&gt;
&lt;em&gt;Global ring: trace khỏe mạnh rơi thẳng đứng ngay khi AdaptiveSampler bắt đầu hoạt động, trong khi lưu lượng không khỏe mạnh gần như không thay đổi.&lt;/em&gt;&lt;/p&gt;
&lt;h2 id=&quot;điều-rút-ra&quot;&gt;Điều rút ra&lt;/h2&gt;
&lt;p&gt;Vài điều tôi muốn chia sẻ với bất kỳ ai gặp bài toán tương tự:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Đừng chọn một sampler. Hãy chọn một vòng điều khiển.&lt;/strong&gt; Head-based hay tail-based là một thế lưỡng phân sai lầm nếu bạn có thể đổi cấu hình lấy mẫu trong vài phút. Không gian thiết kế thú vị nằm ở &lt;em&gt;tín hiệu nào điều khiển việc thay đổi cấu hình&lt;/em&gt;.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Tận dụng ngân sách độ trễ bạn đã có.&lt;/strong&gt; Thiết kế của chúng tôi chỉ hoạt động vì MDM và Config System vốn đã nhanh. Nếu chúng là các hệ thống batch chạy theo giờ, cách này sẽ sụp đổ và bạn quay về với tail-based cùng mọi vấn đề của nó. Hãy biết nhịp đồng hồ của hạ tầng hiện có trước khi thiết kế bên trên nó.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;“Có mất mát nhưng đúng hướng” thắng “không mất mát nhưng không đủ tiền.”&lt;/strong&gt; Chúng tôi chấp nhận mất một lỗi thoáng qua xuất hiện lần đầu. Chúng tôi không chấp nhận mất một lỗi kéo dài. Xác định chính xác mức mất mát &lt;em&gt;chấp nhận được&lt;/em&gt; hữu ích hơn việc đuổi theo con số không.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Adaptive sampling còn thiếu tài liệu.&lt;/strong&gt; Ý tưởng này lưu hành trong các buổi nói chuyện tại hội nghị và các issue của OTel, nhưng khi bắt đầu tôi không tìm được bài viết thực tế nào. Nếu bạn đang thiết kế một hệ thống như vậy, lời giải có lẽ đơn giản hơn những gì tài liệu khiến nó trông như thế — của chúng tôi chỉ gồm vài trăm dòng code cho worker cộng với một câu truy vấn SQL vào MDM.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Nếu bạn đang nhìn hóa đơn DT không vừa ngân sách và một yêu cầu độ tin cậy không chịu nhượng bộ, mẫu thiết kế này đáng để thử.&lt;/p&gt;</content:encoded><category>distributed-tracing</category><category>opentelemetry</category><category>microsoft-365</category></item><item><title>Vui lên nào.</title><link>https://lethanhdat.com/vi/blog/cheer-up/</link><guid isPermaLink="true">https://lethanhdat.com/vi/blog/cheer-up/</guid><description>Hắn vừa feed vừa chửi...</description><pubDate>Tue, 21 Nov 2017 11:45:00 GMT</pubDate><content:encoded>&lt;img src=&quot;https://lethanhdat.com/assets/images/20171121/dota2_feed.jpg&quot; alt=&quot;&quot; class=&quot;size-75 zoomable&quot;&gt;
&lt;blockquote&gt;
&lt;p&gt;Hắn vừa feed vừa chửi. Bao giờ cũng thế, cứ feed xong là hắn chửi. Bắt đầu hắn chửi mid. Có hề gì? Đến những hai thằng đi mid. Rồi hắn chửi team. Thế cũng chả sao: team là tất cả nhưng chẳng là ai. Tức mình, hắn chửi cả cái SEA này. Nhưng cả server ai cũng nhủ “chắc nó trừ mình ra”, không ai lên tiếng cả. Tức thật! ờ! Thế này thì tức thật! Tức chết đi được mất! Đã thế, hắn phải chửi cha đứa nào không feed chung với hắn. Nhưng cũng không ai ra điều. Mẹ kiếp! Thế có phí công hắn try hard không? Thế thì có khổ hắn không? Không biết đứa chết mẹ nào lại để ra mode low priority cho hắn khổ đến nông nỗi này? A ha. Phải đấy hắn cứ thế mà chửi, hắn nghiến răng vào mà chửi “bobo putang ina mo IceFrog”. Nhưng mà biết đứa nào là IceFrog? Có mà Bruno biết. Hắn không biết, cả Steam cũng không ai biết…&lt;/p&gt;
&lt;footer&gt;Khuyết danh&lt;/footer&gt;
&lt;/blockquote&gt;</content:encoded><category>funny</category><category>dota2</category></item><item><title>Jekyll thật ấn tượng.</title><link>https://lethanhdat.com/vi/blog/jekyll-is-impressive/</link><guid isPermaLink="true">https://lethanhdat.com/vi/blog/jekyll-is-impressive/</guid><description>Dùng Jekyll để đăng blog lên GitHub hoàn toàn miễn phí.</description><pubDate>Mon, 20 Nov 2017 11:20:00 GMT</pubDate><content:encoded>&lt;p&gt;Với Jekyll, bạn có thể đăng blog lên GitHub hoàn toàn miễn phí.&lt;/p&gt;
&lt;p&gt;Bạn có thể chèn đoạn code…&lt;/p&gt;
&lt;div class=&quot;astro-code astro-code-themes github-light github-dark&quot; style=&quot;background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;js&quot;&gt;&lt;pre&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#D73A49;--shiki-dark:#F97583&quot;&gt;const&lt;/span&gt;&lt;span style=&quot;color:#005CC5;--shiki-dark:#79B8FF&quot;&gt; express&lt;/span&gt;&lt;span style=&quot;color:#D73A49;--shiki-dark:#F97583&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#6F42C1;--shiki-dark:#B392F0&quot;&gt; require&lt;/span&gt;&lt;span style=&quot;color:#24292E;--shiki-dark:#E1E4E8&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#032F62;--shiki-dark:#9ECBFF&quot;&gt;&apos;express&apos;&lt;/span&gt;&lt;span style=&quot;color:#24292E;--shiki-dark:#E1E4E8&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#D73A49;--shiki-dark:#F97583&quot;&gt;const&lt;/span&gt;&lt;span style=&quot;color:#005CC5;--shiki-dark:#79B8FF&quot;&gt; app&lt;/span&gt;&lt;span style=&quot;color:#D73A49;--shiki-dark:#F97583&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#6F42C1;--shiki-dark:#B392F0&quot;&gt; express&lt;/span&gt;&lt;span style=&quot;color:#24292E;--shiki-dark:#E1E4E8&quot;&gt;()&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#24292E;--shiki-dark:#E1E4E8&quot;&gt;app.&lt;/span&gt;&lt;span style=&quot;color:#6F42C1;--shiki-dark:#B392F0&quot;&gt;get&lt;/span&gt;&lt;span style=&quot;color:#24292E;--shiki-dark:#E1E4E8&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#032F62;--shiki-dark:#9ECBFF&quot;&gt;&apos;/&apos;&lt;/span&gt;&lt;span style=&quot;color:#24292E;--shiki-dark:#E1E4E8&quot;&gt;, (&lt;/span&gt;&lt;span style=&quot;color:#E36209;--shiki-dark:#FFAB70&quot;&gt;req&lt;/span&gt;&lt;span style=&quot;color:#24292E;--shiki-dark:#E1E4E8&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#E36209;--shiki-dark:#FFAB70&quot;&gt;res&lt;/span&gt;&lt;span style=&quot;color:#24292E;--shiki-dark:#E1E4E8&quot;&gt;) &lt;/span&gt;&lt;span style=&quot;color:#D73A49;--shiki-dark:#F97583&quot;&gt;=&gt;&lt;/span&gt;&lt;span style=&quot;color:#24292E;--shiki-dark:#E1E4E8&quot;&gt; res.&lt;/span&gt;&lt;span style=&quot;color:#6F42C1;--shiki-dark:#B392F0&quot;&gt;send&lt;/span&gt;&lt;span style=&quot;color:#24292E;--shiki-dark:#E1E4E8&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#032F62;--shiki-dark:#9ECBFF&quot;&gt;&apos;Hello World!&apos;&lt;/span&gt;&lt;span style=&quot;color:#24292E;--shiki-dark:#E1E4E8&quot;&gt;))&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#24292E;--shiki-dark:#E1E4E8&quot;&gt;app.&lt;/span&gt;&lt;span style=&quot;color:#6F42C1;--shiki-dark:#B392F0&quot;&gt;listen&lt;/span&gt;&lt;span style=&quot;color:#24292E;--shiki-dark:#E1E4E8&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#005CC5;--shiki-dark:#79B8FF&quot;&gt;3000&lt;/span&gt;&lt;span style=&quot;color:#24292E;--shiki-dark:#E1E4E8&quot;&gt;, () &lt;/span&gt;&lt;span style=&quot;color:#D73A49;--shiki-dark:#F97583&quot;&gt;=&gt;&lt;/span&gt;&lt;span style=&quot;color:#24292E;--shiki-dark:#E1E4E8&quot;&gt; console.&lt;/span&gt;&lt;span style=&quot;color:#6F42C1;--shiki-dark:#B392F0&quot;&gt;log&lt;/span&gt;&lt;span style=&quot;color:#24292E;--shiki-dark:#E1E4E8&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#032F62;--shiki-dark:#9ECBFF&quot;&gt;&apos;Example app listening on port 3000!&apos;&lt;/span&gt;&lt;span style=&quot;color:#24292E;--shiki-dark:#E1E4E8&quot;&gt;))&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;span class=&quot;language ps-1 pe-3 text-sm bg-muted text-muted-foreground&quot;&gt;js&lt;/span&gt;&lt;button class=&quot;copy text-muted-foreground p-1 box-content border rounded-lg bg-card&quot; aria-label=&quot;Copy code&quot; data-code=&quot;const express = require(&amp;#x27;express&amp;#x27;)
const app = express()

app.get(&amp;#x27;/&amp;#x27;, (req, res) =&gt; res.send(&amp;#x27;Hello World!&amp;#x27;))

app.listen(3000, () =&gt; console.log(&amp;#x27;Example app listening on port 3000!&amp;#x27;))&quot; onclick=&quot;
          navigator.clipboard.writeText(this.dataset.code);
          this.classList.add(&amp;#x27;copied&amp;#x27;);
          setTimeout(() =&gt; this.classList.remove(&amp;#x27;copied&amp;#x27;), 2000)
        &quot;&gt;&lt;div class=&quot;ready&quot;&gt;&lt;svg class=&quot;size-5&quot;&gt;&lt;use href=&quot;https://lethanhdat.com/icons/code.svg#mingcute-clipboard-line&quot;&gt;&lt;/use&gt;&lt;/svg&gt;&lt;/div&gt;&lt;div class=&quot;success hidden&quot;&gt;&lt;svg class=&quot;size-5&quot;&gt;&lt;use href=&quot;https://lethanhdat.com/icons/code.svg#mingcute-file-check-line&quot;&gt;&lt;/use&gt;&lt;/svg&gt;&lt;/div&gt;&lt;/button&gt;&lt;/div&gt;
&lt;p&gt;Bạn cũng có thể đính kèm hình ảnh…&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://jekyllrb.com/&quot;&gt;&lt;img src=&quot;https://lethanhdat.com/images/2017-11-20-jekyll.png&quot; alt=&quot;jekyll&quot; class=&quot;thumb&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;hoặc nhúng một đoạn clip trên Youtube…&lt;/p&gt;
&lt;iframe width=&quot;560&quot; height=&quot;315&quot; src=&quot;https://www.youtube.com/embed/2MsN8gpT6jY&quot; title=&quot;Video YouTube&quot; frameborder=&quot;0&quot; allowfullscreen&gt;&lt;/iframe&gt;
&lt;p&gt;Hãy thử xem sao nhé :)&lt;/p&gt;
&lt;p&gt;Demo: &lt;a href=&quot;https://github.com/tdat00/blog&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;https://github.com/tdat00/blog&lt;/a&gt;&lt;/p&gt;</content:encoded><category>jekyll</category><category>github</category><category>free</category></item><item><title>Hello World</title><link>https://lethanhdat.com/vi/blog/hello-world/</link><guid isPermaLink="true">https://lethanhdat.com/vi/blog/hello-world/</guid><description>Lại một bài Hello World nữa.</description><pubDate>Fri, 14 Jul 1989 12:05:00 GMT</pubDate><content:encoded>&lt;p&gt;Cậu bé hay kêu cứu…&lt;/p&gt;
&lt;img src=&quot;https://lethanhdat.com/assets/images/avatar.png&quot; alt=&quot;cậu bé hay kêu cứu&quot; class=&quot;zoomable&quot;&gt;
&lt;p&gt;kèm theo một đoạn code…&lt;/p&gt;
&lt;div class=&quot;astro-code astro-code-themes github-light github-dark collapsed&quot; style=&quot;background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;csharp&quot;&gt;&lt;pre&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D;--shiki-dark:#6A737D&quot;&gt;// Chương trình Hello World! bằng C#.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#D73A49;--shiki-dark:#F97583&quot;&gt;using&lt;/span&gt;&lt;span style=&quot;color:#6F42C1;--shiki-dark:#B392F0&quot;&gt; System&lt;/span&gt;&lt;span style=&quot;color:#24292E;--shiki-dark:#E1E4E8&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#D73A49;--shiki-dark:#F97583&quot;&gt;namespace&lt;/span&gt;&lt;span style=&quot;color:#6F42C1;--shiki-dark:#B392F0&quot;&gt; HelloWorld&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#24292E;--shiki-dark:#E1E4E8&quot;&gt;{&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#D73A49;--shiki-dark:#F97583&quot;&gt;    class&lt;/span&gt;&lt;span style=&quot;color:#6F42C1;--shiki-dark:#B392F0&quot;&gt; Hello&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#24292E;--shiki-dark:#E1E4E8&quot;&gt;    {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#D73A49;--shiki-dark:#F97583&quot;&gt;        static&lt;/span&gt;&lt;span style=&quot;color:#D73A49;--shiki-dark:#F97583&quot;&gt; void&lt;/span&gt;&lt;span style=&quot;color:#6F42C1;--shiki-dark:#B392F0&quot;&gt; Main&lt;/span&gt;&lt;span style=&quot;color:#24292E;--shiki-dark:#E1E4E8&quot;&gt;()&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#24292E;--shiki-dark:#E1E4E8&quot;&gt;        {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#24292E;--shiki-dark:#E1E4E8&quot;&gt;            Console.&lt;/span&gt;&lt;span style=&quot;color:#6F42C1;--shiki-dark:#B392F0&quot;&gt;WriteLine&lt;/span&gt;&lt;span style=&quot;color:#24292E;--shiki-dark:#E1E4E8&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#032F62;--shiki-dark:#9ECBFF&quot;&gt;&quot;Hello World!&quot;&lt;/span&gt;&lt;span style=&quot;color:#24292E;--shiki-dark:#E1E4E8&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D;--shiki-dark:#6A737D&quot;&gt;            // Giữ cửa sổ console luôn mở khi chạy ở chế độ debug.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#24292E;--shiki-dark:#E1E4E8&quot;&gt;            Console.&lt;/span&gt;&lt;span style=&quot;color:#6F42C1;--shiki-dark:#B392F0&quot;&gt;WriteLine&lt;/span&gt;&lt;span style=&quot;color:#24292E;--shiki-dark:#E1E4E8&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#032F62;--shiki-dark:#9ECBFF&quot;&gt;&quot;Press any key to exit.&quot;&lt;/span&gt;&lt;span style=&quot;color:#24292E;--shiki-dark:#E1E4E8&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#24292E;--shiki-dark:#E1E4E8&quot;&gt;            Console.&lt;/span&gt;&lt;span style=&quot;color:#6F42C1;--shiki-dark:#B392F0&quot;&gt;ReadKey&lt;/span&gt;&lt;span style=&quot;color:#24292E;--shiki-dark:#E1E4E8&quot;&gt;();&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#24292E;--shiki-dark:#E1E4E8&quot;&gt;        }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#24292E;--shiki-dark:#E1E4E8&quot;&gt;    }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#24292E;--shiki-dark:#E1E4E8&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;span class=&quot;language ps-1 pe-3 text-sm bg-muted text-muted-foreground&quot;&gt;csharp&lt;/span&gt;&lt;button class=&quot;copy text-muted-foreground p-1 box-content border rounded-lg bg-card&quot; aria-label=&quot;Copy code&quot; data-code=&quot;// Chương trình Hello World! bằng C#.
using System;
namespace HelloWorld
{
    class Hello
    {
        static void Main()
        {
            Console.WriteLine(&amp;#x22;Hello World!&amp;#x22;);

            // Giữ cửa sổ console luôn mở khi chạy ở chế độ debug.
            Console.WriteLine(&amp;#x22;Press any key to exit.&amp;#x22;);
            Console.ReadKey();
        }
    }
}&quot; onclick=&quot;
          navigator.clipboard.writeText(this.dataset.code);
          this.classList.add(&amp;#x27;copied&amp;#x27;);
          setTimeout(() =&gt; this.classList.remove(&amp;#x27;copied&amp;#x27;), 2000)
        &quot;&gt;&lt;div class=&quot;ready&quot;&gt;&lt;svg class=&quot;size-5&quot;&gt;&lt;use href=&quot;https://lethanhdat.com/icons/code.svg#mingcute-clipboard-line&quot;&gt;&lt;/use&gt;&lt;/svg&gt;&lt;/div&gt;&lt;div class=&quot;success hidden&quot;&gt;&lt;svg class=&quot;size-5&quot;&gt;&lt;use href=&quot;https://lethanhdat.com/icons/code.svg#mingcute-file-check-line&quot;&gt;&lt;/use&gt;&lt;/svg&gt;&lt;/div&gt;&lt;/button&gt;&lt;button class=&quot;collapse-toggle bg-card text-muted-foreground rounded-lg m-2&quot; aria-label=&quot;Toggle collapse code block&quot; onclick=&quot;this.parentElement.classList.toggle(&amp;#x27;collapsed&amp;#x27;)&quot;&gt;&lt;svg class=&quot;size-5&quot;&gt;&lt;use href=&quot;https://lethanhdat.com/icons/code.svg#mingcute-arrow-down-line&quot;&gt;&lt;/use&gt;&lt;/svg&gt;&lt;span class=&quot;desc&quot;&gt; code&lt;/span&gt;&lt;/button&gt;&lt;div class=&quot;collapse-fade&quot;&gt;&lt;/div&gt;&lt;/div&gt;</content:encoded><category>hello-world</category></item></channel></rss>