From ad093750b73b872e08359943969f2485e130d450 Mon Sep 17 00:00:00 2001 From: Nguyen Van Dung Date: Fri, 18 Sep 2026 12:31:42 +0700 Subject: [PATCH] =?UTF-8?q?feat:=20b=E1=BA=A3n=20d=E1=BB=8Bch=20ti?= =?UTF-8?q?=E1=BA=BFng=20Vi=E1=BB=87t=20+=20site=20VuePress=20th=E1=BB=A9?= =?UTF-8?q?=202=20v=E1=BB=9Bi=20toggle=20ng=C3=B4n=20ng=E1=BB=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Dựng bản dịch tiếng Việt của JavaGuide dưới dạng một site VuePress riêng, dùng chung theme/component/style với bản gốc nên UI giống hệt. Kiến trúc zero-conflict với repo gốc: - Không sửa một byte nào trong docs/, package.json hay pnpm-lock.yaml (package.json bị upstream đụng ở 54/200 commit gần nhất -> chắc chắn conflict). - Toàn bộ thay đổi nằm ở file/thư mục upstream không có: vi/, i18n/, Makefile, vercel.json, CLAUDE.md. - Lệnh chuyển sang Makefile thay cho npm scripts. Nội dung (12 file, giai đoạn 1 của lộ trình học): - 7 snippet dùng chung, README, home, roadmap/README - roadmap/java-roadmap.md (1002 dòng) — file trọng tâm của lộ trình Site tiếng Việt (vi/.vuepress/): - base "/vi/", dest "./dist/vi", lang "vi-VN", docsDir "vi" - Dịch toàn bộ navbar + 11 file sidebar (289 chuỗi), SEO/JSON-LD, chuỗi UI trong component, comment trong SCSS và feature config - trimDescription() đổi dấu câu 。!?,、 sang . ! ? , ; — không đổi thì hàm không bao giờ tìm được điểm ngắt trong văn bản tiếng Việt - legacyRedirects làm rỗng (là URL tiếng Trung của site gốc, vô nghĩa dưới /vi/) - linksCheck tạm hạ xuống "warn" vì sidebar trỏ tới trang chưa dịch Toggle ngôn ngữ 中文 | VI (i18n/lang-switch/): - Teleport vào .vp-navbar-end của theme, có mặt trên CẢ HAI site - Site gốc nhận nút qua wrapper config i18n/cn.config.ts, không phải sửa docs/ - Trang chưa dịch thì nút VI mờ đi và trỏ về /vi/ thay vì tạo link gãy - Dev: site CN proxy /vi/* sang site VI để hai bên cùng một origin Công cụ đồng bộ (i18n/sync-check.mjs): - Sau khi pull upstream, báo file chưa dịch / file gốc đã đổi / file orphan Đã kiểm chứng: - Build CN 641 trang + VI 11 trang, EXIT=0, 61s, peak 2.93GB RAM - Toggle đúng cả 3 trường hợp (đã dịch / chưa dịch / chiều ngược lại), xác nhận bằng DOM thật qua headless Chromium - git diff rỗng hoàn toàn -> pull upstream không conflict Nguồn: https://github.com/Snailclimb/JavaGuide (Apache-2.0) Co-Authored-By: Claude Opus 5 (1M context) --- CLAUDE.md | 429 +++++++ Makefile | 82 ++ i18n/.sync-state.json | 4 + i18n/cn.config.ts | 21 + i18n/lang-switch/LangSwitch.vue | 140 +++ i18n/lang-switch/client.ts | 7 + i18n/lang-switch/plugin.ts | 49 + i18n/lang-switch/vi-routes.mjs | 40 + i18n/sync-check.mjs | 93 ++ vercel.json | 7 + vi/.vuepress/client.ts | 48 + vi/.vuepress/components/ClickImagePreview.vue | 173 +++ .../components/DeferredLayoutToggle.vue | 23 + vi/.vuepress/components/LayoutToggle.vue | 144 +++ vi/.vuepress/components/LazyMermaid.vue | 120 ++ .../components/unlock/GlobalUnlock.vue | 478 ++++++++ .../components/unlock/UnlockContent.vue | 251 +++++ vi/.vuepress/config.ts | 95 ++ vi/.vuepress/features/unlock/config.ts | 41 + vi/.vuepress/features/unlock/heights.ts | 10 + vi/.vuepress/navbar.ts | 64 ++ .../public/assets/icon/apple-icon-152.png | Bin 0 -> 3086 bytes .../public/assets/icon/chrome-192.png | Bin 0 -> 3760 bytes .../public/assets/icon/chrome-512.png | Bin 0 -> 9230 bytes .../public/assets/icon/chrome-mask-192.png | Bin 0 -> 4079 bytes .../public/assets/icon/chrome-mask-512.png | Bin 0 -> 9208 bytes .../public/assets/icon/guide-maskable.png | Bin 0 -> 1691 bytes .../public/assets/icon/guide-monochrome.png | Bin 0 -> 1465 bytes .../public/assets/icon/ms-icon-144.png | Bin 0 -> 3445 bytes vi/.vuepress/public/favicon.ico | Bin 0 -> 19184 bytes .../public/images/qrcode-javaguide.jpg | Bin 0 -> 18375 bytes vi/.vuepress/public/logo.png | Bin 0 -> 5328 bytes vi/.vuepress/public/logo.svg | 1 + vi/.vuepress/public/me.png | Bin 0 -> 49565 bytes vi/.vuepress/shims-vue.d.ts | 5 + vi/.vuepress/sidebar/about-the-author.ts | 29 + vi/.vuepress/sidebar/ai-coding.ts | 144 +++ vi/.vuepress/sidebar/ai.ts | 135 +++ vi/.vuepress/sidebar/books.ts | 36 + vi/.vuepress/sidebar/constants.ts | 111 ++ vi/.vuepress/sidebar/cs-basics.ts | 300 +++++ .../high-quality-technical-articles.ts | 71 ++ vi/.vuepress/sidebar/index.ts | 758 +++++++++++++ vi/.vuepress/sidebar/open-source-project.ts | 40 + vi/.vuepress/sidebar/roadmap.ts | 32 + vi/.vuepress/sidebar/zhuanlan.ts | 33 + vi/.vuepress/styles/config.scss | 1 + vi/.vuepress/styles/index.scss | 168 +++ vi/.vuepress/styles/palette.scss | 4 + vi/.vuepress/theme.ts | 484 ++++++++ vi/GLOSSARY.md | 105 ++ vi/PROGRESS.md | 200 ++++ vi/README.md | 79 ++ vi/home.md | 413 +++++++ vi/roadmap/README.md | 30 + vi/roadmap/java-roadmap.md | 1002 +++++++++++++++++ vi/snippets/article-footer.snippet.md | 7 + vi/snippets/article-header.snippet.md | 1 + vi/snippets/planet.snippet.md | 38 + vi/snippets/planet2.snippet.md | 34 + vi/snippets/rag-project.snippet.md | 26 + vi/snippets/small-advertisement.snippet.md | 11 + vi/snippets/yuanma.snippet.md | 25 + 63 files changed, 6642 insertions(+) create mode 100644 CLAUDE.md create mode 100644 Makefile create mode 100644 i18n/.sync-state.json create mode 100644 i18n/cn.config.ts create mode 100644 i18n/lang-switch/LangSwitch.vue create mode 100644 i18n/lang-switch/client.ts create mode 100644 i18n/lang-switch/plugin.ts create mode 100644 i18n/lang-switch/vi-routes.mjs create mode 100755 i18n/sync-check.mjs create mode 100644 vercel.json create mode 100644 vi/.vuepress/client.ts create mode 100644 vi/.vuepress/components/ClickImagePreview.vue create mode 100644 vi/.vuepress/components/DeferredLayoutToggle.vue create mode 100644 vi/.vuepress/components/LayoutToggle.vue create mode 100644 vi/.vuepress/components/LazyMermaid.vue create mode 100644 vi/.vuepress/components/unlock/GlobalUnlock.vue create mode 100644 vi/.vuepress/components/unlock/UnlockContent.vue create mode 100644 vi/.vuepress/config.ts create mode 100644 vi/.vuepress/features/unlock/config.ts create mode 100644 vi/.vuepress/features/unlock/heights.ts create mode 100644 vi/.vuepress/navbar.ts create mode 100644 vi/.vuepress/public/assets/icon/apple-icon-152.png create mode 100644 vi/.vuepress/public/assets/icon/chrome-192.png create mode 100644 vi/.vuepress/public/assets/icon/chrome-512.png create mode 100644 vi/.vuepress/public/assets/icon/chrome-mask-192.png create mode 100644 vi/.vuepress/public/assets/icon/chrome-mask-512.png create mode 100644 vi/.vuepress/public/assets/icon/guide-maskable.png create mode 100644 vi/.vuepress/public/assets/icon/guide-monochrome.png create mode 100644 vi/.vuepress/public/assets/icon/ms-icon-144.png create mode 100644 vi/.vuepress/public/favicon.ico create mode 100644 vi/.vuepress/public/images/qrcode-javaguide.jpg create mode 100644 vi/.vuepress/public/logo.png create mode 100644 vi/.vuepress/public/logo.svg create mode 100644 vi/.vuepress/public/me.png create mode 100644 vi/.vuepress/shims-vue.d.ts create mode 100644 vi/.vuepress/sidebar/about-the-author.ts create mode 100644 vi/.vuepress/sidebar/ai-coding.ts create mode 100644 vi/.vuepress/sidebar/ai.ts create mode 100644 vi/.vuepress/sidebar/books.ts create mode 100644 vi/.vuepress/sidebar/constants.ts create mode 100644 vi/.vuepress/sidebar/cs-basics.ts create mode 100644 vi/.vuepress/sidebar/high-quality-technical-articles.ts create mode 100644 vi/.vuepress/sidebar/index.ts create mode 100644 vi/.vuepress/sidebar/open-source-project.ts create mode 100644 vi/.vuepress/sidebar/roadmap.ts create mode 100644 vi/.vuepress/sidebar/zhuanlan.ts create mode 100644 vi/.vuepress/styles/config.scss create mode 100644 vi/.vuepress/styles/index.scss create mode 100644 vi/.vuepress/styles/palette.scss create mode 100644 vi/.vuepress/theme.ts create mode 100644 vi/GLOSSARY.md create mode 100644 vi/PROGRESS.md create mode 100644 vi/README.md create mode 100644 vi/home.md create mode 100644 vi/roadmap/README.md create mode 100644 vi/roadmap/java-roadmap.md create mode 100644 vi/snippets/article-footer.snippet.md create mode 100644 vi/snippets/article-header.snippet.md create mode 100644 vi/snippets/planet.snippet.md create mode 100644 vi/snippets/planet2.snippet.md create mode 100644 vi/snippets/rag-project.snippet.md create mode 100644 vi/snippets/small-advertisement.snippet.md create mode 100644 vi/snippets/yuanma.snippet.md diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 00000000000..f4d28dd3f57 --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,429 @@ +# CLAUDE.md — Dự án dịch JavaGuide sang tiếng Việt + +> File này là **luật hệ thống bắt buộc**. Mọi session làm việc trên repo này PHẢI đọc và tuân thủ trước khi thao tác. + +## 1. Bối cảnh dự án + +- **Repo gốc**: [JavaGuide](https://javaguide.cn) — tài liệu tiếng Trung về Java backend & phỏng vấn (455 file `.md`, ~9.5MB), build bằng VuePress 2 + theme-hope. +- **Mục tiêu**: tạo **bản tiếng Việt hoàn chỉnh** của tài liệu, đặt trong thư mục `vi/` ở root repo, **giữ nguyên 100% cấu trúc thư mục** của `docs/`. + - `docs/java/basis/reflection.md` → `vi/java/basis/reflection.md` + - `docs/roadmap/java-roadmap.md` → `vi/roadmap/java-roadmap.md` +- **Thứ tự dịch**: theo **lộ trình học** (`docs/roadmap/`), không dịch ngẫu nhiên — để người đọc có thể học tuần tự ngay khi bản dịch đang tiến hành. +- **Tiến độ**: theo dõi tại [`vi/PROGRESS.md`](vi/PROGRESS.md). **Luôn đọc file này đầu session** để biết dịch tiếp từ đâu. + +## 2. Luật tuyệt đối (KHÔNG được vi phạm) + +| # | Luật | +| --- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| 1 | **KHÔNG sửa, xoá, di chuyển bất kỳ file gốc nào** trong `docs/`, `media/`, `scripts/`, `.vuepress/`. Repo gốc là read-only. | +| 2 | Mọi bản dịch chỉ ghi vào `vi/`. Không đụng `package.json`, sidebar, navbar gốc. | +| 3 | **Giữ nguyên core** — không thêm ý, không bịa, không "bổ sung cho đầy đủ", không thêm lời bình của người dịch. | +| 4 | **Không dịch lan man**. Câu tiếng Việt ngắn hơn hoặc bằng độ dài ý gốc. Tiếng Trung vốn súc tích — bản dịch không được phình ra. | +| 5 | **Bất kỳ ký tự tiếng Trung nào cũng phải được dịch**, kể cả trong code block, comment code, tên biến mô tả, chú thích ảnh, bảng, sơ đồ ASCII. Không để sót chữ Hán. | +| 6 | **Thuật ngữ chuyên ngành giữ nguyên tiếng Anh** (xem §4). Không Việt hoá thuật ngữ đã chuẩn. | + +## 3. Quy tắc dịch chi tiết + +### 3.1 Frontmatter YAML + +Dịch `title`, `description`, `tagline`, `category`, `tag`, `content` (keywords), text trong `actions`. +Giữ nguyên: `icon`, `head`, `sitemap`, `changefreq`, `priority`, `heroImage`, `footer`, `home`, tên khoá YAML. + +```yaml +# Gốc # Dịch +title: Java 基础常见面试题总结 → title: Tổng hợp câu hỏi phỏng vấn Java Basics thường gặp +category: Java → category: Java # tên category kỹ thuật giữ nguyên +tag: + - Java基础 → - Java Basics +``` + +### 3.1.1 ⚠️ BẮT BUỘC — frontmatter phải hợp lệ YAML + +Tiếng Trung dùng dấu hai chấm fullwidth `:` (an toàn trong YAML). Tiếng Việt dùng `:` thường +→ **làm vỡ YAML và fail build**. Đây là lỗi đã thực sự xảy ra: + +``` +error YAMLException: incomplete explicit mapping pair; a key node is missed +``` + +Quy tắc: nếu giá trị trong frontmatter chứa `: ` (hai chấm + khoảng trắng), kết thúc bằng `:`, +hoặc chứa ` #` → **phải bọc trong dấu nháy kép**. + +```yaml +# SAI - vỡ build +title: Tổng hợp lộ trình học: Java backend, AI Agent + +# ĐÚNG +title: "Tổng hợp lộ trình học: Java backend, AI Agent" +``` + +Script quét & sửa hàng loạt (chạy sau mỗi batch dịch): + +```bash +python3 - <<'EOF' +import glob,re +for f in glob.glob('vi/**/*.md', recursive=True): + src=open(f,encoding='utf-8').read() + if not src.startswith('---\n'): continue + end=src.index('\n---\n',3); fm=src[4:end+1]; rest=src[end+1:] + out=[];ch=False + for line in fm.split('\n'): + m=re.match(r'^(\s*(?:-\s+)?[A-Za-z_][\w.-]*:)\s(.*)$', line) + v=m.group(2).strip() if m else '' + if m and v and v[0] not in '"\'|>[{&*!%@`' and (': ' in v or v.endswith(':') or ' #' in v): + out.append(f'{m.group(1)} "{v.replace(chr(92),chr(92)*2).replace(chr(34),chr(92)+chr(34))}"');ch=True + else: out.append(line) + if ch: open(f,'w',encoding='utf-8').write('---\n'+'\n'.join(out)+rest); print('fixed',f) +EOF +``` + +### 3.2 Giữ nguyên tuyệt đối + +- **Đường dẫn link nội bộ**: `./java-basic-questions-01.md`, `/home.md` — giữ y nguyên (cấu trúc `vi/` mirror `docs/` nên link tự đúng). +- **URL ngoài**, ảnh `https://oss.javaguide.cn/...`, `![...]()` — giữ nguyên URL, **dịch phần alt text**. +- **Directive VuePress**: ``, `::: tip`, `::: warning`, `:::` — giữ nguyên cú pháp, **dịch nội dung bên trong**. Nhãn `::: tip 提示` → `::: tip Gợi ý`. +- **Code**: tên class/method/biến/keyword/API/config key, output log, SQL, JSON — giữ nguyên. Chỉ dịch **comment** và **chuỗi string tiếng Trung**. +- HTML thô, ``, badge, table alignment. + +### 3.3 Code block — ví dụ chuẩn + +```java +// GỐC +// 创建一个线程池,核心线程数为 5 +ExecutorService pool = Executors.newFixedThreadPool(5); +System.out.println("任务执行完成"); + +// DỊCH +// Tạo một thread pool với core thread số lượng là 5 +ExecutorService pool = Executors.newFixedThreadPool(5); +System.out.println("Task thực thi hoàn tất"); +``` + +### 3.3.1 Ngoại lệ — chữ Hán ĐƯỢC PHÉP giữ lại + +Chỉ 3 trường hợp, ngoài ra không có ngoại lệ nào khác: + +1. **Mã định danh pháp lý / số đăng ký**: ví dụ `鄂ICP备2020015769号-1` trong footer. Là số giấy phép, dịch ra sẽ sai. +2. **Từ khoá người dùng phải gõ/nhắn nguyên văn**: ví dụ nhắn `微信` cho bot WeChat, nhắn `学习路线` cho tài khoản công khai. Giữ nguyên chữ Hán, **thêm chú giải tiếng Việt trong ngoặc**: `nhắn "**微信**" (WeChat)`. +3. **Tên riêng trong URL / đường dẫn ảnh**: không đụng tới URL. + +Mọi chữ Hán khác đều phải dịch. Khi verify thấy sót, kiểm tra xem có rơi vào 3 ca trên không — nếu không thì phải dịch. + +### 3.4 Giọng văn + +- Xưng hô: "bạn" (người đọc), tránh "chúng ta/tôi" trừ khi bản gốc dùng ngôi thứ nhất. +- Văn phong kỹ thuật, trực tiếp, không hoa mỹ. +- Câu hỏi phỏng vấn giữ dạng câu hỏi. +- Dịch nghĩa, không dịch từng chữ. Thành ngữ Trung → diễn đạt tự nhiên tiếng Việt. + +## 4. Glossary — thuật ngữ GIỮ NGUYÊN tiếng Anh + +Không bao giờ Việt hoá các thuật ngữ sau (danh sách mở rộng khi gặp thuật ngữ mới): + +**Java core**: thread, thread pool, heap, stack, garbage collection (GC), class loader, bytecode, JVM, JDK, JRE, reflection, generic, wildcard, type erasure, serialization, annotation, lambda, stream, interface, abstract class, override, overload, encapsulation, inheritance, polymorphism, autoboxing, immutable, singleton, enum, record, virtual thread, checked/unchecked exception. + +**Concurrency**: lock, deadlock, race condition, atomic, volatile, synchronized, CAS, AQS, JMM, happens-before, memory barrier, context switch, blocking/non-blocking, fair/unfair lock, reentrant, CountDownLatch, Semaphore, ThreadLocal. + +**Collection**: List, Map, Set, Queue, hash, hash collision, load factor, capacity, red-black tree, linked list, array, iterator, fail-fast. + +**Database**: index, clustered index, transaction, isolation level, dirty read, phantom read, ACID, MVCC, B+ tree, sharding, replication, master-slave, deadlock, lock escalation, execution plan, connection pool, binlog, redo log, undo log. + +**Distributed / System design**: load balancing, cache, cache penetration/breakdown/avalanche (cache xuyên thấu / sập cache / cache avalanche — dịch kèm giữ tiếng Anh lần đầu), CAP, BASE, consistency, availability, partition tolerance, RPC, message queue, idempotent, rate limiting, circuit breaker, service discovery, distributed lock, CDN, microservice. + +**Network / OS**: TCP, UDP, HTTP, HTTPS, handshake, DNS, socket, process, thread, context switch, kernel, user space, paging, virtual memory, deadlock, I/O multiplexing, epoll, zero-copy. + +**AI**: LLM, prompt, token, embedding, RAG, Agent, MCP, fine-tuning, inference, context window, function calling, vector database. + +**Chung**: framework, middleware, source code, build, deploy, commit, merge, refactor, performance, throughput, latency, backend, frontend, API, SDK, CLI. + +### 4.1 Thuật ngữ nên dịch (KHÔNG giữ tiếng Anh) + +| Tiếng Trung | Tiếng Việt | +| ------------ | --------------------- | +| 面试题 | câu hỏi phỏng vấn | +| 学习路线 | lộ trình học | +| 总结 | tổng hợp / tóm tắt | +| 详解 | giải thích chi tiết | +| 原理 | nguyên lý | +| 底层 | tầng dưới / bên trong | +| 常见 | thường gặp | +| 优缺点 | ưu / nhược điểm | +| 使用场景 | trường hợp sử dụng | +| 注意事项 | lưu ý | +| 建议阅读顺序 | thứ tự đọc đề xuất | +| 适合谁看 | dành cho ai | +| 学习重点 | trọng tâm học | + +## 5. Workflow bắt buộc mỗi session + +1. Đọc `vi/PROGRESS.md` → xác định file tiếp theo trong hàng đợi. +2. Đọc file gốc bằng **CodeGraph** (§6), không dùng grep/find mò mẫm. +3. Dịch **nguyên file**, ghi vào đúng đường dẫn mirror trong `vi/`. +4. Verify: `grep -P '[\x{4e00}-\x{9fff}]' ` → **phải rỗng**. Nếu còn chữ Hán, dịch tiếp cho sạch. +5. Cập nhật `vi/PROGRESS.md` (đánh dấu ✅, ghi ngày). +6. Không commit trừ khi người dùng yêu cầu. + +### 5.1 Thứ tự dịch (lộ trình học) + +``` +Giai đoạn 1 — Trục lộ trình: docs/roadmap/**, docs/README.md, docs/home.md +Giai đoạn 2 — Java core: docs/java/README.md → basis → collection → concurrent → jvm → io → new-features +Giai đoạn 3 — CS basics: docs/cs-basics/** (network, os, data-structure, algorithms) +Giai đoạn 4 — Database: docs/database/** (mysql, redis, sql, mongodb, elasticsearch) +Giai đoạn 5 — Framework: docs/system-design/** +Giai đoạn 6 — Phân tán: docs/distributed-system/**, high-performance, high-availability +Giai đoạn 7 — Còn lại: docs/ai/**, ai-coding/**, tools/**, books/**, interview-preparation/**, v.v. +``` + +Trong mỗi thư mục: dịch `README.md` trước (nó là mục lục + thứ tự đọc), rồi theo đúng thứ tự `README.md` liệt kê. + +## 6. CodeGraph — dùng đúng chỗ + +Repo đã index (`.codegraph/` tồn tại), **nhưng CodeGraph chỉ index source code** (`.vuepress/**` — TypeScript, Vue), **KHÔNG index file `.md`**. Đã xác minh: `codegraph_node docs/snippets/*.md` → _"No indexed file matches"_. + +| Việc cần làm | Công cụ | +| ------------------------------------------------------------- | ------------------------------------------------------------------------------------------ | +| Đọc file tài liệu `.md` để dịch | `cat` / Read — CodeGraph không phủ | +| Hiểu cấu hình VuePress, sidebar, navbar, theme, component Vue | `mcp__codegraph__codegraph_explore ""` — **bắt buộc dùng trước** grep/find | +| Tìm một symbol trong `.vuepress/` | `mcp__codegraph__codegraph_node ` | +| Quét sót chữ Hán trong bản dịch | `grep -P '[\x{4e00}-\x{9fff}]'` | + +Khi cần tra cứu cấu trúc site (thứ tự sidebar, alias `@snippet`, route), **luôn hỏi CodeGraph trước**, đừng grep mò trong `.vuepress/`. + +## 7. Cấu trúc thư mục `vi/` + +``` +vi/ +├── PROGRESS.md # bảng tiến độ — nguồn sự thật duy nhất về trạng thái dịch +├── GLOSSARY.md # thuật ngữ đã chốt, bổ sung dần khi gặp từ mới +├── README.md # bản dịch docs/README.md +├── roadmap/ +├── java/ +├── cs-basics/ +└── ... # mirror y hệt docs/ +``` + +`vi/` **không** chứa `.vuepress/`, `snippets/*.snippet.md` được dịch và giữ nguyên tên file. + +## 8. Deploy — site VuePress tiếng Việt + +`vi/` **không chỉ là thư mục bản dịch, nó là một site VuePress hoàn chỉnh thứ 2**, dùng chung +theme/component/style với site gốc nên **UI giống hệt**. File gốc trong `docs/` không bị đụng tới. + +### 8.1 Cấu trúc + +``` +vi/.vuepress/ +├── config.ts base:"/vi/" dest:"./dist/vi" lang:"vi-VN" ← khác bản gốc +├── theme.ts docsDir:"vi", SEO/JSON-LD tiếng Việt, canonical /vi +├── navbar.ts đã dịch +├── sidebar/ đã dịch (11 file) +├── components/ đã dịch chuỗi UI (LayoutToggle, unlock, mermaid, image preview) +├── features/ đã dịch comment +├── styles/ đã dịch comment (SCSS y hệt bản gốc → UI không đổi) +└── public/ copy nguyên (logo, favicon, icon) +``` + +### 8.2 Lệnh + +```bash +pnpm install # lần đầu +pnpm vi:dev # dev server site tiếng Việt +pnpm vi:build # build ra dist/vi +pnpm build:all # build site gốc (dist/) RỒI site vi (dist/vi/) — đúng thứ tự +``` + +**Thứ tự build quan trọng**: `vuepress build docs` ghi vào `dist/`, có thể xoá sạch thư mục đó. +Luôn build `docs` trước, `vi` sau — `pnpm build:all` đã làm đúng thứ tự. + +### 8.3 Điểm đã xử lý, đừng phá + +| Mục | Trạng thái | +| ---------------------------- | ------------------------------------------------------------------------------------------------------------------------ | +| `base: "/vi/"` | Mọi asset/route đều có prefix `/vi/`. Đổi base phải rebuild toàn bộ. | +| `linksCheck: build "warn"` | Tạm hạ từ `"error"` vì sidebar trỏ tới trang chưa dịch. **Đổi lại `"error"` khi dịch xong toàn bộ.** | +| `legacyRedirects = {}` | Đã làm rỗng — redirect cũ là URL tiếng Trung của site gốc, vô nghĩa dưới `/vi/`. | +| `trimDescription()` | Đã đổi dấu câu `。!?,、` → `. ! ? , ;` — không đổi thì hàm không bao giờ tìm được điểm ngắt trong văn bản tiếng Việt. | +| `PROGRESS.md`, `GLOSSARY.md` | Đã loại khỏi `pagePatterns`, không build thành trang. | + +### 8.4 Kiểm chứng sau mỗi lần đổi cấu hình + +```bash +pnpm vi:build # phải "success" +grep -o 'href="/vi/assets[^"]*"' dist/vi/index.html | head -1 # phải có prefix /vi/ +grep -o ']*>' dist/vi/index.html # phải lang="vi-VN" +``` + +## 9. Kiến trúc zero-conflict với repo gốc + +### 9.1 Nguyên tắc + +Fork này phải **pull được code mới từ repo gốc mà không bao giờ conflict**. Muốn vậy: + +> **KHÔNG sửa bất kỳ file nào mà upstream cũng sửa.** + +Mọi thứ của bản dịch nằm trong các file/thư mục mà upstream **không hề có**: + +| Đường dẫn | Chủ sở hữu | Ghi chú | +| -------------------------------- | ------------ | --------------------------------------------------------- | +| `docs/**` | **upstream** | Read-only tuyệt đối. Kể cả `.vuepress/`. | +| `package.json`, `pnpm-lock.yaml` | **upstream** | **KHÔNG được đụng.** 54/200 commit gần nhất sửa file này. | +| `vi/**` | fork | Nội dung dịch + site VuePress riêng | +| `i18n/**` | fork | Nút đổi ngôn ngữ + công cụ sync | +| `Makefile` | fork | Toàn bộ lệnh (thay cho npm scripts) | +| `CLAUDE.md` | fork | File luật này | + +❌ **Đừng bao giờ** thêm script vào `package.json` — dùng `Makefile`. +❌ **Đừng bao giờ** sửa `docs/.vuepress/` để thêm tính năng — bọc ngoài bằng `i18n/cn.config.ts`. + +### 9.2 Nút đổi ngôn ngữ hoạt động thế nào + +Site tiếng Trung cần nút chuyển ngôn ngữ, nhưng navbar của nó nằm trong `docs/.vuepress/` (của upstream). +Giải pháp: **wrapper config + root component**, không đụng `docs/`: + +``` +i18n/cn.config.ts import config upstream → spread → thêm plugin +i18n/lang-switch/plugin.ts VuePress plugin, inject __LANG_SITE__ + __VI_ROUTES__ +i18n/lang-switch/client.ts đăng ký root component +i18n/lang-switch/LangSwitch.vue nút [中文 | VI], Teleport vào .vp-navbar-end +i18n/lang-switch/vi-routes.mjs quét vi/**/*.md → danh sách route đã dịch +``` + +Chạy site CN bằng `npx vuepress dev docs -c i18n/cn.config.ts` (đã gói trong `make cn-dev`). + +**Vị trí nút**: component được `` vào `.vp-navbar-end` của theme (góc trên-phải, +cạnh nút GitHub/theme) **sau khi mount** — lúc SSR đích teleport chưa tồn tại nên component +render rỗng, tránh hydration mismatch. Nếu không tìm thấy `.vp-navbar-end` (theme đổi cấu trúc), +nó tự rơi về chế độ pill nổi góc dưới-phải thay vì biến mất. + +⚠️ Vì render sau mount nên nút **không có trong HTML tĩnh** — đừng dùng `grep lang-switch dist/...` +để kiểm tra. Phải dump DOM sau khi JS chạy: + +```bash +chromium --headless --disable-gpu --no-sandbox --virtual-time-budget=8000 \ + --dump-dom http://localhost:9099/roadmap/java-roadmap.html | grep -o 'lang-seg[^>]*' +``` + +**Ánh xạ trang**: `page.path` không chứa base nên giống nhau ở cả 2 site. +`/java/basis/reflection.html` ↔ `/vi/java/basis/reflection.html`. +Trang chưa dịch → nút VI mờ đi và trỏ về `/vi/` thay vì link gãy. + +### 9.3 Quy trình khi pull code mới + +```bash +git remote add upstream https://github.com/Snailclimb/JavaGuide.git # lần đầu +git fetch upstream && git merge upstream/main # không conflict vì docs/ chưa bị đụng +make sync # xem file mới / file gốc đã đổi +# ... dịch các file đó vào vi/ ... +make sync-mark # đánh dấu đã đồng bộ tới HEAD +``` + +`make sync` báo 3 thứ: + +- **Chưa dịch**: có trong `docs/` nhưng chưa có trong `vi/` +- **Gốc đã đổi**: file `docs/` thay đổi kể từ commit đánh dấu lần cuối → bản dịch bị cũ +- **Orphan**: có trong `vi/` nhưng upstream đã xoá/đổi tên + +Trạng thái lưu ở `i18n/.sync-state.json`. + +### 9.4 Dev hai site trên MỘT origin + +Site CN và site VI là hai VuePress app riêng, mặc định chạy hai port khác nhau. +Nút đổi ngôn ngữ dùng đường dẫn tuyệt đối (`/` và `/vi/`) nên **bắt buộc hai site +phải cùng một origin**, nếu không sẽ 404. + +Giải pháp: dev server CN (8080) proxy `/vi/*` sang dev server VI (8081), khai báo qua +`extendsBundlerOptions` trong `langSwitchPlugin` (option `devViPort`). + +```bash +make dev # chạy cả hai, LUÔN mở http://localhost:8080/ +``` + +⚠️ Chỉ chạy `make cn-dev` một mình → `/vi/` sẽ 502 (không có server 8081). +⚠️ Mở thẳng `http://localhost:8081/vi/` → nút 中文 sẽ 404 (port đó không có site CN). +⚠️ Nếu port 8080/8081 đã bị chiếm, VuePress tự nhảy port khác và **proxy sẽ trỏ sai**. +`make dev` có preflight chặn trường hợp này; dọn bằng `pkill -f 'vuepress[ ]dev'` +(viết dấu ngoặc như vậy để pkill không tự khớp chính nó). + +Bug đã gặp và đã sửa: site VI có base `/vi/` nên trả 404 cho `/vi` (thiếu `/` cuối). +Proxy có `rewrite` thêm dấu `/` cho đúng path đó. + +### 9.5 Lệnh (Makefile) + +```bash +make help # danh sách lệnh +make dev # CẢ HAI site trên :8080 (dùng cái này để test nút đổi ngôn ngữ) +make cn-dev # chỉ site tiếng Trung :8080 +make vi-dev # chỉ site tiếng Việt :8081/vi/ +make build # build cả hai, đúng thứ tự -> dist/ + dist/vi/ +make sync # trạng thái đồng bộ sau khi pull +make check # quét chữ Hán còn sót trong vi/ +``` + +## 10. Deploy lên Vercel + +Một project Vercel duy nhất phục vụ **cả hai** site: `/` là 中文, `/vi/` là Tiếng Việt. +Khớp đúng với `base: "/vi/"` đã cấu hình. + +### 10.1 Cấu hình + +`vercel.json` (file của fork, upstream không có → zero-conflict): + +```json +{ + "buildCommand": "vuepress build docs -c i18n/cn.config.ts && vuepress build vi", + "outputDirectory": "dist", + "framework": null +} +``` + +Vì sao viết vậy: + +| Mục | Lý do | +| ----------------------------- | ------------------------------------------------------------------------------------------------------ | +| Hai lệnh nối bằng `&&` | Build CN **trước** (ghi `dist/`, xoá sạch thư mục), rồi VI (ghi `dist/vi/`). Đảo thứ tự là mất bản VI. | +| Không dùng `make` | Build image của Vercel không đảm bảo có `make`. Gọi thẳng `vuepress`. | +| `-c i18n/cn.config.ts` | Không có cờ này thì site CN **mất nút đổi ngôn ngữ**. | +| `framework: null` | Không để Vercel tự đoán preset và ghi đè lệnh build. | +| **Không** bật `cleanUrls` | VuePress sinh link có đuôi `.html`. Bật `cleanUrls` là gãy toàn bộ link nội bộ. | +| **Không** đặt `trailingSlash` | Để mặc định Vercel; `/vi/` tự trỏ `dist/vi/index.html`. | + +### 10.2 Các bước + +```bash +git add CLAUDE.md Makefile vercel.json i18n/ vi/ +git commit -m "feat: bản dịch tiếng Việt + site VuePress thứ 2" +git push +``` + +Rồi trên Vercel: **Add New Project** → import repo → **Deploy**. +Không cần chỉnh gì trong UI, `vercel.json` đã khai báo đủ. +Vercel tự nhận `pnpm` từ `pnpm-lock.yaml` và `packageManager` trong `package.json`. + +### 10.3 Lưu ý + +- `dist/` nằm trong `.gitignore` — đúng, Vercel tự build chứ không lấy bản có sẵn. +- Build CN render 641 trang nên chậm hơn hẳn build VI. Số đo thực tế ở §10.4. +- Deploy lên domain khác cho riêng bản VI thì phải đổi `base` trong `vi/.vuepress/config.ts` + từ `"/vi/"` về `"/"`, và sửa lại đường dẫn trong `i18n/lang-switch/LangSwitch.vue`. +- Repo gốc là **Apache 2.0** → được phép redistribute, nhớ giữ file `LICENSE` và ghi nguồn. + +### 10.4 Số đo thực tế (đã chạy đúng `buildCommand` của vercel.json) + +| Chỉ số | Giá trị | Giới hạn Vercel | | +| ------------------------- | ------------------------------------------- | --------------- | --- | +| Thời gian build (CN + VI) | **61 s** | 45 phút | ✅ | +| Peak RAM (build CN) | **2.93 GB** | 8 GB | ✅ | +| Tổng file trong `dist/` | **1.639** | ~15.000 | ✅ | +| Dung lượng `dist/` | **93 MB** | — | ✅ | +| File lớn nhất | 2.3 MB (`feed.json`, `atom.xml`, `rss.xml`) | 100 MB/file | ✅ | + +Kết quả: `EXIT=0`, `dist/index.html` (667 trang CN) + `dist/vi/index.html` (11 trang VI). +Không cần `NODE_OPTIONS=--max-old-space-size`. + +Đo lại khi số trang tăng nhiều: + +```bash +rm -rf dist && time bash -c "$(python3 -c "import json;print(json.load(open('vercel.json'))['buildCommand'])")" +find dist -type f | wc -l && du -sh dist +``` diff --git a/Makefile b/Makefile new file mode 100644 index 00000000000..b317e022f7c --- /dev/null +++ b/Makefile @@ -0,0 +1,82 @@ +# JavaGuide — lệnh cho bản dịch tiếng Việt +# +# Cố ý KHÔNG thêm script vào package.json: file đó bị upstream sửa rất thường xuyên +# (54/200 commit gần nhất), thêm vào là chắc chắn conflict khi pull. +# Makefile là file của riêng fork này nên không bao giờ conflict. + +VP := npx vuepress + +.PHONY: help install dev cn-dev vi-dev cn-build vi-build build sync sync-mark missing check clean + +help: + @echo " make install Cài dependency (pnpm)" + @echo "" + @echo " make dev Chạy CẢ HAI site -> http://localhost:8080/ (nút đổi ngôn ngữ hoạt động)" + @echo " make cn-dev Chỉ site tiếng Trung -> http://localhost:8080/" + @echo " make vi-dev Chỉ site tiếng Việt -> http://localhost:8081/vi/" + @echo "" + @echo " make build Build CẢ HAI site (đúng thứ tự) -> dist/ + dist/vi/" + @echo " make cn-build Chỉ build site tiếng Trung -> dist/" + @echo " make vi-build Chỉ build site tiếng Việt -> dist/vi/" + @echo "" + @echo " make sync Sau khi git pull: xem file nào mới/đã đổi cần dịch" + @echo " make sync-mark Đánh dấu đã dịch xong tới commit hiện tại" + @echo " make missing Chỉ in danh sách file chưa dịch" + @echo " make check Quét chữ Hán còn sót trong vi/" + +install: + pnpm install + +# Chạy cả hai dev server. Site CN (8080) proxy /vi/* sang site VI (8081), +# nên cả hai nằm trên MỘT origin -> nút đổi ngôn ngữ hoạt động y hệt production. +# Luôn mở http://localhost:8080/ , đừng mở 8081 trực tiếp. +dev: + @for p in 8080 8081; do \ + if ss -ltn 2>/dev/null | grep -q ":$$p "; then \ + echo "✖ Port $$p đang bị chiếm. VuePress sẽ tự nhảy port khác và proxy /vi/ sẽ sai."; \ + echo " Tắt tiến trình cũ rồi chạy lại: kill \$$(ss -ltnp 2>/dev/null | grep ':'$$p' ' | grep -o 'pid=[0-9]*' | cut -d= -f2)"; \ + exit 1; \ + fi; \ + done + @echo "→ http://localhost:8080/ (中文)" + @echo "→ http://localhost:8080/vi/ (Tiếng Việt, qua proxy)" + @$(VP) dev vi -p 8081 & VI_PID=$$!; \ + trap "kill $$VI_PID 2>/dev/null; exit 0" INT TERM; \ + $(VP) dev docs -c i18n/cn.config.ts; \ + kill $$VI_PID 2>/dev/null; true + +# Site gốc tiếng Trung, dùng wrapper config để chèn nút đổi ngôn ngữ +# mà không phải sửa file nào trong docs/. +# LƯU Ý: chạy riêng lệnh này thì /vi/ sẽ 502 vì chưa có server 8081 -> dùng `make dev`. +cn-dev: + $(VP) dev docs -c i18n/cn.config.ts + +vi-dev: + $(VP) dev vi -p 8081 + +cn-build: + $(VP) build docs -c i18n/cn.config.ts + +# QUAN TRỌNG: build docs TRƯỚC (ghi vào dist/, có thể xoá sạch thư mục), +# rồi mới build vi (ghi vào dist/vi/). +vi-build: + $(VP) build vi + +build: cn-build vi-build + @echo "✓ dist/ (中文) + dist/vi/ (Tiếng Việt)" + +sync: + @node i18n/sync-check.mjs + +sync-mark: + @node i18n/sync-check.mjs --mark + +missing: + @node i18n/sync-check.mjs --list + +check: + @echo "Chữ Hán còn sót trong vi/ (chỉ được còn ngoại lệ ở CLAUDE.md §3.3.1):" + @grep -rnP '[\x{4e00}-\x{9fff}]' vi/ --include='*.md' --include='*.ts' --include='*.vue' --include='*.scss' || echo " (sạch)" + +clean: + rm -rf dist docs/.vuepress/.temp docs/.vuepress/.cache vi/.vuepress/.temp vi/.vuepress/.cache diff --git a/i18n/.sync-state.json b/i18n/.sync-state.json new file mode 100644 index 00000000000..7e8b4e31c61 --- /dev/null +++ b/i18n/.sync-state.json @@ -0,0 +1,4 @@ +{ + "lastSyncedCommit": "d76264cb4e000416c4adca06770ce014bd309150", + "markedAt": "2026-09-18T04:20:30.377Z" +} diff --git a/i18n/cn.config.ts b/i18n/cn.config.ts new file mode 100644 index 00000000000..4d21f8e393e --- /dev/null +++ b/i18n/cn.config.ts @@ -0,0 +1,21 @@ +// Cấu hình site GỐC tiếng Trung, bọc ngoài docs/.vuepress/config.ts. +// +// Mục đích: thêm nút chuyển ngôn ngữ vào site tiếng Trung mà KHÔNG sửa bất kỳ +// file nào trong docs/ → `git pull` từ repo gốc không bao giờ conflict. +// +// Chạy: npx vuepress dev docs -c i18n/cn.config.ts (xem Makefile) +import { getDirname, path } from "vuepress/utils"; +import baseConfig from "../docs/.vuepress/config.js"; +import { langSwitchPlugin } from "./lang-switch/plugin.js"; +import { scanViRoutes } from "./lang-switch/vi-routes.mjs"; + +const __dirname = getDirname(import.meta.url); +const viRoutes = scanViRoutes(path.resolve(__dirname, "../vi")); + +export default { + ...baseConfig, + plugins: [ + ...(baseConfig.plugins ?? []), + langSwitchPlugin({ site: "cn", viRoutes, devViPort: 8081 }), + ], +}; diff --git a/i18n/lang-switch/LangSwitch.vue b/i18n/lang-switch/LangSwitch.vue new file mode 100644 index 00000000000..707654f7126 --- /dev/null +++ b/i18n/lang-switch/LangSwitch.vue @@ -0,0 +1,140 @@ + + + + + diff --git a/i18n/lang-switch/client.ts b/i18n/lang-switch/client.ts new file mode 100644 index 00000000000..976cb8a1dd9 --- /dev/null +++ b/i18n/lang-switch/client.ts @@ -0,0 +1,7 @@ +import { defineClientConfig } from "vuepress/client"; +import { h } from "vue"; +import LangSwitch from "./LangSwitch.vue"; + +export default defineClientConfig({ + rootComponents: [() => h(LangSwitch)], +}); diff --git a/i18n/lang-switch/plugin.ts b/i18n/lang-switch/plugin.ts new file mode 100644 index 00000000000..c139f356a72 --- /dev/null +++ b/i18n/lang-switch/plugin.ts @@ -0,0 +1,49 @@ +import { getDirname, path } from "vuepress/utils"; + +const __dirname = getDirname(import.meta.url); + +export interface LangSwitchOptions { + /** Site hiện tại: "cn" = bản gốc tiếng Trung, "vi" = bản dịch tiếng Việt */ + site: "cn" | "vi"; + /** Các route đã có bản dịch VI (chỉ cần cho site "cn" để biết trang nào bật được) */ + viRoutes: string[]; + /** + * Chỉ cho site "cn" ở chế độ dev: proxy /vi/* sang dev server của site VI. + * Nhờ đó dev chạy trên MỘT origin duy nhất, nút đổi ngôn ngữ hoạt động y hệt production. + * Không đặt = không proxy. + */ + devViPort?: number; +} + +/** + * Chèn nút chuyển ngôn ngữ 中文 | VI vào cả hai site mà KHÔNG phải sửa file nào + * trong docs/ — nhờ đó pull code mới từ repo gốc không bao giờ conflict. + */ +export const langSwitchPlugin = ({ + site, + viRoutes, + devViPort, +}: LangSwitchOptions) => ({ + name: "javaguide-lang-switch", + clientConfigFile: path.resolve(__dirname, "./client.ts"), + define: { + __LANG_SITE__: site, + __VI_ROUTES__: viRoutes, + }, + // Dev server của site CN không biết gì về /vi/ (đó là app VuePress riêng). + // Không proxy thì VuePress router sẽ nuốt /vi/ rồi rewrite thành /vi.html -> 404. + extendsBundlerOptions: (bundlerOptions: any, app: any) => { + if (!devViPort || !app.env.isDev) return; + const vite = (bundlerOptions.viteOptions ??= {}); + const server = (vite.server ??= {}); + const proxy = (server.proxy ??= {}); + proxy["/vi"] = { + target: `http://localhost:${devViPort}`, + changeOrigin: true, + ws: true, + // Site VI có base "/vi/" nên nó trả 404 cho "/vi" (thiếu dấu / cuối). + // Thêm dấu / để gõ tay localhost:8080/vi cũng vào được. + rewrite: (p: string) => (p === "/vi" ? "/vi/" : p), + }; + }, +}); diff --git a/i18n/lang-switch/vi-routes.mjs b/i18n/lang-switch/vi-routes.mjs new file mode 100644 index 00000000000..cf2df75bfb8 --- /dev/null +++ b/i18n/lang-switch/vi-routes.mjs @@ -0,0 +1,40 @@ +import { readdirSync, existsSync } from "node:fs"; +import { join, relative, sep } from "node:path"; + +// File .md không được VuePress build thành trang (khớp pagePatterns của cả 2 site) +const EXCLUDE_FILES = new Set(["PROGRESS.md", "GLOSSARY.md", "TODO.md"]); +const EXCLUDE_DIRS = new Set([".vuepress", "node_modules", ".git"]); + +/** Liệt kê mọi file .md (đường dẫn tương đối, dùng "/") trong 1 thư mục nguồn. */ +export function listMarkdown(srcDir) { + if (!existsSync(srcDir)) return []; + const out = []; + const walk = (dir) => { + for (const e of readdirSync(dir, { withFileTypes: true })) { + if (e.isDirectory()) { + if (!EXCLUDE_DIRS.has(e.name)) walk(join(dir, e.name)); + } else if ( + e.name.endsWith(".md") && + !e.name.endsWith(".snippet.md") && + !EXCLUDE_FILES.has(e.name) + ) { + out.push(relative(srcDir, join(dir, e.name)).split(sep).join("/")); + } + } + }; + walk(srcDir); + return out.sort(); +} + +/** "java/basis/x.md" -> "/java/basis/x.html" · "roadmap/README.md" -> "/roadmap/" · "README.md" -> "/" */ +export function toRoute(relPath) { + if (relPath === "README.md") return "/"; + if (relPath.endsWith("/README.md")) + return `/${relPath.slice(0, -"README.md".length)}`; + return `/${relPath.slice(0, -".md".length)}.html`; +} + +/** Danh sách route đã có bản dịch tiếng Việt. */ +export function scanViRoutes(viDir) { + return listMarkdown(viDir).map(toRoute); +} diff --git a/i18n/sync-check.mjs b/i18n/sync-check.mjs new file mode 100755 index 00000000000..619e5afd942 --- /dev/null +++ b/i18n/sync-check.mjs @@ -0,0 +1,93 @@ +#!/usr/bin/env node +// Báo cáo trạng thái đồng bộ giữa docs/ (gốc tiếng Trung) và vi/ (bản dịch). +// Chạy sau mỗi lần `git pull` để biết phải dịch tiếp file nào. +// +// node i18n/sync-check.mjs # báo cáo +// node i18n/sync-check.mjs --mark # đánh dấu đã đồng bộ tới commit hiện tại +// node i18n/sync-check.mjs --list # chỉ in danh sách file thiếu (cho script khác dùng) + +import { execSync } from "node:child_process"; +import { readFileSync, writeFileSync, existsSync } from "node:fs"; +import { dirname, resolve } from "node:path"; +import { fileURLToPath } from "node:url"; +import { listMarkdown } from "./lang-switch/vi-routes.mjs"; + +const ROOT = resolve(dirname(fileURLToPath(import.meta.url)), ".."); +const STATE = resolve(ROOT, "i18n/.sync-state.json"); +const args = new Set(process.argv.slice(2)); + +const git = (cmd) => + execSync(`git ${cmd}`, { cwd: ROOT, encoding: "utf8" }).trim(); + +const head = git("rev-parse HEAD"); + +if (args.has("--mark")) { + writeFileSync( + STATE, + JSON.stringify( + { lastSyncedCommit: head, markedAt: new Date().toISOString() }, + null, + 2, + ) + "\n", + ); + console.log(`✓ Đã đánh dấu đồng bộ tới commit ${head.slice(0, 8)}`); + process.exit(0); +} + +const cn = listMarkdown(resolve(ROOT, "docs")); +const vi = new Set(listMarkdown(resolve(ROOT, "vi"))); + +const missing = cn.filter((f) => !vi.has(f)); +const orphan = [...vi].filter((f) => !cn.includes(f)); + +if (args.has("--list")) { + missing.forEach((f) => console.log(f)); + process.exit(0); +} + +// File gốc đã đổi kể từ lần đồng bộ cuối -> bản dịch tương ứng bị cũ +let stale = []; +let lastSynced = null; +if (existsSync(STATE)) { + lastSynced = JSON.parse(readFileSync(STATE, "utf8")).lastSyncedCommit; + try { + stale = git(`diff --name-only ${lastSynced} HEAD -- docs`) + .split("\n") + .filter((f) => f.endsWith(".md")) + .map((f) => f.replace(/^docs\//, "")) + .filter((f) => vi.has(f)); + } catch { + console.warn( + `⚠ Không đọc được lịch sử từ ${lastSynced} — bỏ qua kiểm tra file cũ.\n`, + ); + } +} + +const pct = cn.length ? ((vi.size / cn.length) * 100).toFixed(1) : "0.0"; +console.log(`\n📊 Tiến độ dịch: ${vi.size}/${cn.length} file (${pct}%)`); +console.log(` HEAD hiện tại: ${head.slice(0, 8)}`); +console.log( + ` Đồng bộ lần cuối: ${lastSynced ? lastSynced.slice(0, 8) : "chưa đánh dấu (chạy --mark)"}\n`, +); + +if (stale.length) { + console.log( + `♻️ ${stale.length} file GỐC ĐÃ ĐỔI kể từ lần đồng bộ — cần dịch lại:`, + ); + stale.forEach((f) => console.log(` ~ ${f}`)); + console.log(); +} + +if (orphan.length) { + console.log( + `🗑 ${orphan.length} file chỉ có ở vi/ (upstream đã xoá/đổi tên?):`, + ); + orphan.forEach((f) => console.log(` - ${f}`)); + console.log(); +} + +console.log(`⬜ ${missing.length} file CHƯA DỊCH. 15 file đầu:`); +missing.slice(0, 15).forEach((f) => console.log(` docs/${f}`)); +if (missing.length > 15) + console.log(` ... còn ${missing.length - 15} file (xem --list)`); +console.log(); diff --git a/vercel.json b/vercel.json new file mode 100644 index 00000000000..4e57cca0857 --- /dev/null +++ b/vercel.json @@ -0,0 +1,7 @@ +{ + "$schema": "https://openapi.vercel.sh/vercel.json", + "buildCommand": "vuepress build docs -c i18n/cn.config.ts && vuepress build vi", + "outputDirectory": "dist", + "framework": null, + "github": { "silent": true } +} diff --git a/vi/.vuepress/client.ts b/vi/.vuepress/client.ts new file mode 100644 index 00000000000..ce78c371142 --- /dev/null +++ b/vi/.vuepress/client.ts @@ -0,0 +1,48 @@ +import { defineClientConfig } from "vuepress/client"; +import { defineAsyncComponent, h } from "vue"; +import DeferredLayoutToggle from "./components/DeferredLayoutToggle.vue"; +import ClickImagePreview from "./components/ClickImagePreview.vue"; +import LazyMermaid from "./components/LazyMermaid.vue"; +import GlobalUnlock from "./components/unlock/GlobalUnlock.vue"; + +const UnlockContent = defineAsyncComponent( + () => import("./components/unlock/UnlockContent.vue"), +); + +const CHUNK_LOAD_ERROR_PATTERN = + /Failed to fetch dynamically imported module|Importing a module script failed|error loading dynamically imported module|Unable to preload CSS/i; + +const getCurrentLocation = (): string => + `${window.location.pathname}${window.location.search}${window.location.hash}`; + +export default defineClientConfig({ + enhance({ app, router }) { + app.component("Mermaid", LazyMermaid); + app.component("UnlockContent", UnlockContent); + + router.onError((error, to) => { + if (typeof window === "undefined") return; + + const message = error instanceof Error ? error.message : String(error); + if (!CHUNK_LOAD_ERROR_PATTERN.test(message)) return; + + const target = to?.fullPath || getCurrentLocation(); + const reloadKey = `javaguide:chunk-reload:${target}`; + + if (window.sessionStorage.getItem(reloadKey) === "1") return; + + window.sessionStorage.setItem(reloadKey, "1"); + window.location.assign(target); + }); + + router.afterEach((to) => { + if (typeof window === "undefined") return; + window.sessionStorage.removeItem(`javaguide:chunk-reload:${to.fullPath}`); + }); + }, + rootComponents: [ + () => h(DeferredLayoutToggle), + () => h(GlobalUnlock), + () => h(ClickImagePreview), + ], +}); diff --git a/vi/.vuepress/components/ClickImagePreview.vue b/vi/.vuepress/components/ClickImagePreview.vue new file mode 100644 index 00000000000..ba7a644cf6d --- /dev/null +++ b/vi/.vuepress/components/ClickImagePreview.vue @@ -0,0 +1,173 @@ + + + + + diff --git a/vi/.vuepress/components/DeferredLayoutToggle.vue b/vi/.vuepress/components/DeferredLayoutToggle.vue new file mode 100644 index 00000000000..04975151665 --- /dev/null +++ b/vi/.vuepress/components/DeferredLayoutToggle.vue @@ -0,0 +1,23 @@ + + + diff --git a/vi/.vuepress/components/LayoutToggle.vue b/vi/.vuepress/components/LayoutToggle.vue new file mode 100644 index 00000000000..f8291e792aa --- /dev/null +++ b/vi/.vuepress/components/LayoutToggle.vue @@ -0,0 +1,144 @@ + + + + + diff --git a/vi/.vuepress/components/LazyMermaid.vue b/vi/.vuepress/components/LazyMermaid.vue new file mode 100644 index 00000000000..45237b9b807 --- /dev/null +++ b/vi/.vuepress/components/LazyMermaid.vue @@ -0,0 +1,120 @@ + + + + + diff --git a/vi/.vuepress/components/unlock/GlobalUnlock.vue b/vi/.vuepress/components/unlock/GlobalUnlock.vue new file mode 100644 index 00000000000..e15568eca06 --- /dev/null +++ b/vi/.vuepress/components/unlock/GlobalUnlock.vue @@ -0,0 +1,478 @@ + + + + + diff --git a/vi/.vuepress/components/unlock/UnlockContent.vue b/vi/.vuepress/components/unlock/UnlockContent.vue new file mode 100644 index 00000000000..581343d2a84 --- /dev/null +++ b/vi/.vuepress/components/unlock/UnlockContent.vue @@ -0,0 +1,251 @@ + + + + + diff --git a/vi/.vuepress/config.ts b/vi/.vuepress/config.ts new file mode 100644 index 00000000000..4f54242fef4 --- /dev/null +++ b/vi/.vuepress/config.ts @@ -0,0 +1,95 @@ +import { createRequire } from "node:module"; +import { viteBundler } from "@vuepress/bundler-vite"; +import { defineUserConfig } from "vuepress"; +import theme from "./theme.js"; +import { langSwitchPlugin } from "../../i18n/lang-switch/plugin.js"; + +const require = createRequire(import.meta.url); +const mermaidComponentPath = require.resolve( + "@vuepress/plugin-markdown-chart/client/components/Mermaid.js", +); + +export default defineUserConfig({ + // Deploy dưới dạng thư mục con của site gốc: /vi/ + // Build site gốc TRƯỚC (ra ./dist), rồi build site này (ra ./dist/vi). + base: "/vi/", + dest: "./dist/vi", + + title: "JavaGuide (Tiếng Việt)", + description: + "JavaGuide là bộ hướng dẫn học và ôn tập dành cho backend development / phỏng vấn backend, bao phủ Java, database/MySQL, Redis, distributed, high concurrency, high availability, system design và các kiến thức cốt lõi khác.", + lang: "vi-VN", + + head: [ + // meta + ["meta", { name: "robots", content: "all" }], + ["meta", { name: "author", content: "Guide" }], + // [ + // "meta", + // { + // name: "keywords", + // content: + // "JavaGuide, phỏng vấn backend, backend development, phỏng vấn Java, Java Basics, concurrent programming, JVM, database, MySQL, Redis, Spring, distributed, high concurrency, high performance, high availability, system design, message queue, cache, computer network, Linux", + // }, + // ], + // [ + // "meta", + // { + // name: "description", + // content: + // "JavaGuide là bộ hướng dẫn học và ôn tập dành cho backend development / phỏng vấn backend, bao phủ Java, database/MySQL, Redis, distributed, high concurrency, high availability, system design và các kiến thức cốt lõi khác.", + // }, + // ], + ["meta", { name: "apple-mobile-web-app-capable", content: "yes" }], + // Thêm Baidu Analytics - load bất đồng bộ để tránh chặn render + [ + "script", + { defer: true }, + `var _hmt = _hmt || []; + (function() { + var hm = document.createElement("script"); + hm.src = "https://hm.baidu.com/hm.js?5dd2e8c97962d57b7b8fea1737c01743"; + hm.async = true; + var s = document.getElementsByTagName("script")[0]; + s.parentNode.insertBefore(hm, s); + })();`, + ], + ], + + bundler: viteBundler({ + viteOptions: { + resolve: { + alias: { + "@vuepress/plugin-markdown-chart/client/components/Mermaid.js": + mermaidComponentPath, + }, + }, + css: { + preprocessorOptions: { + scss: { + silenceDeprecations: ["if-function"], + }, + }, + }, + }, + }), + + theme, + + pagePatterns: [ + "**/*.md", + "!**/*.snippet.md", + "!**/TODO.md", + // File nội bộ của quy trình dịch, không phải nội dung site + "!PROGRESS.md", + "!GLOSSARY.md", + "!.vuepress", + "!node_modules", + ], + + // Nút chuyển ngôn ngữ 中文 | VI (dùng chung với site gốc, xem i18n/) + plugins: [langSwitchPlugin({ site: "vi", viRoutes: [] })], + + shouldPrefetch: false, + shouldPreload: false, +}); diff --git a/vi/.vuepress/features/unlock/config.ts b/vi/.vuepress/features/unlock/config.ts new file mode 100644 index 00000000000..1758b96d6e7 --- /dev/null +++ b/vi/.vuepress/features/unlock/config.ts @@ -0,0 +1,41 @@ +import { PREVIEW_HEIGHT } from "./heights"; + +const withDefaultHeight = ( + paths: readonly string[], + height: string = PREVIEW_HEIGHT.XL, +): Record => + Object.fromEntries(paths.map((path) => [path, height])); + +export const unlockConfig = { + // Đổi số phiên bản sẽ buộc người dùng xác minh lại + unlockVersion: "v1", + // Dùng để debug: đặt true sẽ bỏ qua trạng thái đã mở khoá cục bộ, luôn kích hoạt giới hạn + forceLock: false, + code: "8888", + // Dùng đường dẫn tương đối, ảnh đặt trong vi/.vuepress/public/images + qrCodeUrl: "/images/qrcode-javaguide.jpg", + // Đường dẫn -> chiều cao hiển thị (khuyến nghị dùng preset PREVIEW_HEIGHT) + protectedPaths: { + ...withDefaultHeight([ + "/java/jvm/memory-area.html", + "/cs-basics/network/tcp-connection-and-disconnection.html", + "/cs-basics/network/http-vs-https.html", + "/cs-basics/network/dns.html", + ]), + // Nếu cần chiều cao đặc biệt thì ghi đè riêng + // "/some/page.html": PREVIEW_HEIGHT.MEDIUM, + }, + // Prefix thư mục -> chiều cao hiển thị (mọi bài trong thư mục này đều kích hoạt xác minh) + // Ví dụ "/java/collection/" sẽ khớp "/java/collection/**" + protectedPrefixes: { + ...withDefaultHeight([ + "/database/", + "/high-performance/", + "/java/basis/", + "/java/collection/", + "/ai/", + ]), + }, +} as const; + +export { PREVIEW_HEIGHT }; diff --git a/vi/.vuepress/features/unlock/heights.ts b/vi/.vuepress/features/unlock/heights.ts new file mode 100644 index 00000000000..34ba390ca45 --- /dev/null +++ b/vi/.vuepress/features/unlock/heights.ts @@ -0,0 +1,10 @@ +export const PREVIEW_HEIGHT = { + SHORT: "500px", + MEDIUM: "1000px", + LONG: "1500px", + XL: "2000px", + XXL: "2500px", +} as const; + +export type PreviewHeight = + (typeof PREVIEW_HEIGHT)[keyof typeof PREVIEW_HEIGHT]; diff --git a/vi/.vuepress/navbar.ts b/vi/.vuepress/navbar.ts new file mode 100644 index 00000000000..1927d4faee0 --- /dev/null +++ b/vi/.vuepress/navbar.ts @@ -0,0 +1,64 @@ +import { navbar } from "vuepress-theme-hope"; + +export default navbar([ + { text: "Backend Development", icon: "mdi:language-java", link: "/home.md" }, + { + text: "Computer Science cơ bản", + icon: "mdi:desktop-classic", + link: "/cs-basics/", + }, + { + text: "AI Application Development", + icon: "mdi:robot-outline", + link: "/ai/", + }, + { text: "AI Coding", icon: "mdi:code-tags", link: "/ai-coding/" }, + { + text: "Đọc thêm", + icon: "mdi:book-open-page-variant-outline", + children: [ + { text: "Lộ trình học", icon: "mdi:map-outline", link: "/roadmap/" }, + { + text: "Project mã nguồn mở", + icon: "mdi:github", + link: "/open-source-project/", + }, + { + text: "Sách kỹ thuật", + icon: "mdi:book-open-page-variant-outline", + link: "/books/", + }, + { + text: "Đời lập trình viên", + icon: "mdi:code-tags", + link: "/high-quality-technical-articles/", + }, + ], + }, + { + text: "Về website", + icon: "mdi:information-outline", + children: [ + { + text: "Về tác giả", + icon: "mdi:account-edit-outline", + link: "/about-the-author/", + }, + { + text: "Tải PDF", + icon: "mdi:file-pdf-box", + link: "/interview-preparation/pdf-interview-javaguide.md", + }, + { + text: "Cấp tốc ôn phỏng vấn", + icon: "mdi:file-pdf-box", + link: "https://interview.javaguide.cn/home.html", + }, + { + text: "Lịch sử cập nhật", + icon: "mdi:history", + link: "/timeline/", + }, + ], + }, +]); diff --git a/vi/.vuepress/public/assets/icon/apple-icon-152.png b/vi/.vuepress/public/assets/icon/apple-icon-152.png new file mode 100644 index 0000000000000000000000000000000000000000..f53c6c55b049ea917c283acd84bd5a5f62a289c3 GIT binary patch literal 3086 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 aFZ=--2|INIa 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 literal 0 HcmV?d00001 diff --git a/vi/.vuepress/public/assets/icon/chrome-mask-192.png b/vi/.vuepress/public/assets/icon/chrome-mask-192.png new file mode 100644 index 0000000000000000000000000000000000000000..77c39a2a828560cdfa70eec967cf4e5892ed767d GIT binary patch literal 4079 zcmVm=KlHl)IB`_b>h$f z0PgPYOqg@-+zD78XDPDQ~-kH`1}10h2_4~@&Hhf?^aX!hSdA>+v{k!thj4);c=!_W1xsjP&{Z`uh5=$nOAQm(d>|`u+X_ zWSIa}lG89T0BgknSd#!tyP#o@07;DiJBZRcLE0t&*Wv2C&FEf@u*=!xu*KB?LW|Vk z=>Ul60Epz8w#${W$naiVzR=zPU6s3OlI`^OqPx!0D=Yu}{HnpzbDFsASzX)Y>%7g{ zC27I#I5WuC;`dA{g{Hs&T)qHXlmIe@004gOM?>D{?!(jGm#e~gox1>OoA6my@J&n4 z007Y4<^Wc{exkhrY@XjhKj!W9pRvY@s>4Knrzvxx5p12d%Gm%yxoMNM8fu#W8-nm- zU_Wug08qSRk+T3MgYGaY&)V&|#L;Y)woHVpM|h$Hq?qC7?U1gCFKogZWWedPv^#sIA9SJx zaiIWPl>h*B0F3VY?Cd0q=B~ctxXIb~+1RbT&5NPCk#M&3wzO1+tn{p_?TCl~41n!; zb?pEE{`2xbjpdlK;Fze@g`m?xd&^XV%J;y)0ARoCyuCPeqU@8B?`~|_Vqn-wOZ@Ne z?BU+~nl)($eLcnkJ>009lnb zkb~cOdEISi(Ge8cAOP9|08NwT0CLSfY`&&skpPEy0DNlFI6wGJEcQw!g*FY^E&ziK z0D!IO)4|f4hr<$T!;;3ox3{UTn`4~!_>yop4vnxiq) z$Z}fd%Q+|fz;=X#@csJx|J9>HNl8gbNl8gbNy&c-;duHWl}Zta5CYD0b`dr}AR8OQ z5PerTG_%JZzCL|?c{SoI@H0O*fkUDxV0V$3{Fa{uE{bIdb&lw*GxA#$9+7Nf z3?LNoW%5s3YCCg56axT2&=GKvS&zSX_Aobsn&F5qE5BlXMM@<3xds}a-N)w7P*}vW z12~9nFmm0(s^GB4rFC_CF<}n?{dzzMPF%cNchzR$R4lLaj9!4O{0M}8g{BK#`Sj)5 zwQFA=Etq$p267Qj2Lz^t4Oz_Qtr&SjIE8Th4N{U6x2`JYqPYs*RJ1} zrebgZev0vWy-j|{U~-8t8Z<(*tLw(xY(2ZD^kknHxYfck(2Q4`RI~MY*9behYP;$S zqf)J49Gpbl(2|(wUnYPVAb@;WDLx> z5MIl;Js_9-Be`;?mfHhDcq!uwNVun#!j((PtNZ=5P@aVV5YEC1hf~n1b;J`xO9U{0 z;u1s$7dw^slQ2#08W{f(ZvGxf~v8MtN0iMyuMGEDCxzON}U3~`r~s;>39)UR-8 za~tye{mghP@SGpRrrt^Ssy5_wQ@bLcfx-Tm2@t>en*lIQ$I(#e(3_39++La2?Wd+j z02~325C)J6`NHAw;lTZ^*&f_~@cG;7d>&ozJ_3{!_=FI2hMW;S>9YE)n`f|YKE5ii zdu{hT$DkSbgaA55B5o_2o-`6OIc-;$=V$SJPw`%0k6v$eS;=rDVAs#!+`jaWj`RJ6 z0R|lbr`2e++YcR0>2lEgk*<^H{0L51+5BXrJ5}@VK?LwKMg*a-l_2Z~i(AJCVf^OD zK%+MrN&B$OBr8H0@C}250T=1)K6zd)`FHX;fPhyBYDSzS;oQ4RQeFhfEWQggLrPDO z`tI3M3a2O>$GN}sp#T&`@&5b2AMKm_ixN>3$Ir#FqBp&$5T-^bhfrhEfq@wi9R*EM za#0ddbJa=|SGUjtjoheh+pT`7peI2Tz56XHD*B}e3ZftSANnskceJ%PS(XL!VRv@# zInK=edd|6X?>KIQGso7g+kO=OSGK|Z1MB8)e_&m{;Fp*KLx#Z3?f6jTgI9|d{tAnR zUc?5tt_k2*EdCW%EQcYZ)xf6hH^D%F^vlpM!kU27+c&M-c#HfiAGP&YLx2s9S$p7t$0wy}AG7%iU}GXa4Rj|G*a_Xb#^y^ZxDH{(18F4Ezi85CX`ymA}A}e_%@Y zd$)JUzxWs?`KA&1C+=zy@@so_`HmC*Dx~inn4jJk{l45Z4-EVvtg*vZApHl8u^c{p zxMKQdk1ad_(rc=h#3C5s4-wyYG12GI8I&DQo(oAAC-YanQg49lBskd46}J^VE%vr27r$rJQ%D%VmeM7@#&zJ8Bn}>m3H+o^;y6(Hj4eNN~yXDj2I9M zl$KnstE#o5nRv#6X3N!SUXyJZJ$Ud+41!t>U^{!|;K5M~x&|&_Gg$pKE|ZZaA~TtD zCh%V>_^2wY`;yz<@!e+K{^q5o75IlC!PR*?8G29Jv>BqUHx(e7fa)c;+2_4I3Rnu! z8)7D-JUnuQ+vCGiRH_ecnM~@2>HucY0w6qMOOIcB?o3OK5O$fLl>GoE7>jhNvxROy1HD0{5=6t-|wbwKUgAm3;vG4V!ZA+KY>d zSVaYc6wpY3>4+luEsTjzij5QsFbC2TJP=1onXy92b}?dMjM=1koE@=3M6gAnL)aCI z#auk7A_{tWsBZ(29*z+k71^7eq-fod!u{E4i-XpM=`&@v)jY9T;Twvoxfm?V+a)W+&P(TMcRTR4v>&4K7ZI>|NIqS1 ze4Gm@g)CoA30@n;D+^rxx4$GrdeZ`>;4`=P%|@V2BftQ5sxc*(OgNG%{;mLfejf^O z6>0s^5OiWFAoQET= zW*CeF?R;6JDP(}``OtCE0|ZL)gdG?{O_NIA4&V`lP!JGmNWvHA;g0PBQ1?*hbLlB8y56pEEa9ElNARdN=5Sk!_C289t^1@A<> zV)5itQaOevNh;2$9UvT#8Y!)a4Nxq2_tIE7+OnW(KD-WS0!(>MVCz@1mRNLxJ!%W@%sl|^R| z444TsHrvElZrm$5wFuCXtz%*vXjauY?~SqCc}3C$Fz7mRF(unVLdwet$2lw?h={CA z4;N~P_3nktKEjIl8qOA)5^yR3zDcj_9?O+mnVJO{_+16zWFf?IlG9xTLa3r-0VgWl zRCQus4F%9DPy+xA{jrqx*fO_m50maoO%yyTBN_j&H07NOxoGt=R@acX`Bo+3v0S$k#rGd}boX zp3P{SU9=ITBeS~#V`zKuD>8bbubVyNk9|GcXYKChJFod~KY-TJ(b3V-(b3V-(b3V- h(b3V-(b4%Ud;oR_3fSphSH1uM002ovPDHLkV1h;|2|@q> literal 0 HcmV?d00001 diff --git a/vi/.vuepress/public/assets/icon/chrome-mask-512.png b/vi/.vuepress/public/assets/icon/chrome-mask-512.png new file mode 100644 index 0000000000000000000000000000000000000000..b8349f4ea1d00b8bdc5a2dc905376803ffaa317c GIT binary patch literal 9208 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 literal 0 HcmV?d00001 diff --git a/vi/.vuepress/public/favicon.ico b/vi/.vuepress/public/favicon.ico new file mode 100644 index 0000000000000000000000000000000000000000..3a14635ac460d2194138e858ede73c919240e031 GIT binary patch literal 19184 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 literal 0 HcmV?d00001 diff --git a/vi/.vuepress/public/images/qrcode-javaguide.jpg b/vi/.vuepress/public/images/qrcode-javaguide.jpg new file mode 100644 index 0000000000000000000000000000000000000000..731d912ae05cd91d8426046a673c87d6bdcf2910 GIT binary patch literal 18375 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 literal 0 HcmV?d00001 diff --git a/vi/.vuepress/public/logo.svg b/vi/.vuepress/public/logo.svg new file mode 100644 index 00000000000..da6b683d5f1 --- /dev/null +++ b/vi/.vuepress/public/logo.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/vi/.vuepress/public/me.png b/vi/.vuepress/public/me.png new file mode 100644 index 0000000000000000000000000000000000000000..be8f2106c6cabd0937341ffcffe6c92f04927524 GIT binary patch literal 49565 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 iM4xJ61jH74hvUEY!eZvwrTIeu0000; + export default component; +} diff --git a/vi/.vuepress/sidebar/about-the-author.ts b/vi/.vuepress/sidebar/about-the-author.ts new file mode 100644 index 00000000000..e55cc561dc1 --- /dev/null +++ b/vi/.vuepress/sidebar/about-the-author.ts @@ -0,0 +1,29 @@ +import { arraySidebar } from "vuepress-theme-hope"; +import { ICONS } from "./constants.js"; + +export const aboutTheAuthor = arraySidebar([ + { + text: "Trải nghiệm cá nhân", + icon: ICONS.EXPERIENCE, + collapsible: false, + children: [ + "internet-addiction-teenager", + "my-college-life", + "javaguide-100k-star", + "feelings-after-one-month-of-induction-training", + "feelings-of-half-a-year-from-graduation-to-entry", + ], + }, + { + text: "Tản mạn", + icon: ICONS.CHAT, + collapsible: false, + children: [ + "writing-technology-blog-six-years", + "deprecated-java-technologies", + "my-article-was-stolen-and-made-into-video-and-it-became-popular", + "dog-that-copies-other-people-essay", + "zhishixingqiu-two-years", + ], + }, +]); diff --git a/vi/.vuepress/sidebar/ai-coding.ts b/vi/.vuepress/sidebar/ai-coding.ts new file mode 100644 index 00000000000..73ddcd24d50 --- /dev/null +++ b/vi/.vuepress/sidebar/ai-coding.ts @@ -0,0 +1,144 @@ +import { arraySidebar } from "vuepress-theme-hope"; +import { ICONS } from "./constants.js"; + +export const aiCoding = arraySidebar([ + { + text: "Nhập môn", + icon: ICONS.BASIC, + children: [ + { + text: "Câu hỏi phỏng vấn mở về AI Coding", + link: "practices/ai-ide", + }, + { + text: "AI Coding nên chọn CLI hay IDE?", + link: "practices/cli-vs-ide", + }, + ], + }, + { + text: "Claude Code và Codex", + icon: ICONS.CODE, + children: [ + { + text: "⭐️Hướng dẫn sử dụng Claude Code", + link: "practices/claudecode-tips", + }, + { + text: "Giải thích chi tiết các lệnh cốt lõi Claude Code", + link: "practices/claudecode-commands", + }, + { + text: "⭐️Hướng dẫn best practice OpenAI Codex", + link: "practices/codex-best-practices", + }, + { + text: "Thay thế OMP bằng Claude Code giao diện đẹp", + link: "practices/oh-my-pi", + }, + { + text: "Cài đặt, cấu hình và mẹo thường dùng cho Ghostty", + link: "practices/ghostty", + }, + { + text: "Quản lý nhiều session với Claude Code Agent View", + link: "practices/claudecode-agentview", + }, + ], + }, + { + text: "Nguyên lý Claude Code", + icon: ICONS.CODE, + prefix: "principles/", + children: [ + { + text: "Quản lý context trong Claude Code", + link: "claude-code-context-management", + }, + { + text: "Hệ thống memory của Claude Code", + link: "claude-code-memory", + }, + { + text: "Nguyên lý Claude Code Skills", + link: "claude-code-skills", + }, + { + text: "Nguyên lý Claude Code Hooks", + link: "claude-code-hooks", + }, + { + text: "Cơ chế đa Agent của Claude Code", + link: "claude-code-multi-agent", + }, + ], + }, + { + text: "Quy chuẩn & tăng hiệu suất", + icon: ICONS.PERFORMANCE, + children: [ + { + text: "⭐️Tổng hợp mẹo thực dụng Vibe Coding", + link: "practices/the-cool-tricks-for-vibe-coding", + }, + { + text: "Spec Coding - lập trình hướng đặc tả", + link: "practices/spec-coding", + }, + { + text: "⭐️Best practice cho CLAUDE.md", + link: "practices/claude-md-best-practices", + }, + { + text: "⭐️Gợi ý các Skills cần có cho AI Coding", + link: "practices/programmer-essential-skills", + }, + { + text: "Lựa chọn và tinh giản Skills cho AI Coding", + link: "practices/skill-selection-and-pruning", + }, + { + text: "Hướng dẫn sử dụng chuyên sâu mattpocock/skills", + link: "practices/mattpocock-skills", + }, + { + text: "Một AI drawing Skill dễ dùng", + link: "practices/drawio-chart-skill", + }, + ], + }, + { + text: "AI Coding thực chiến", + icon: ICONS.PROJECT, + children: [ + { + text: "Thực chiến đa tình huống plugin IDEA + Qoder", + link: "cases/idea-qoder-plugin", + }, + { + text: "Thực chiến đa tình huống Trae + MiniMax", + link: "cases/trae-m2.7", + }, + { + text: "Thực chiến tích hợp model bên thứ ba vào Claude Code", + link: "cases/cc-glm5.1", + }, + { + text: "Thực chiến DeepSeek V4 + Claude Code", + link: "cases/deepseek-v4-claude-code", + }, + { + text: "Thực chiến MiniMax M3 + Claude Code", + link: "cases/cc-m3", + }, + { + text: "Thực chiến đa tình huống Kimi K3", + link: "cases/kimi-k3", + }, + { + text: "Thực chiến plugin IDEA + CC GUI", + link: "project/cc-guide", + }, + ], + }, +]); diff --git a/vi/.vuepress/sidebar/ai.ts b/vi/.vuepress/sidebar/ai.ts new file mode 100644 index 00000000000..0d13324ec56 --- /dev/null +++ b/vi/.vuepress/sidebar/ai.ts @@ -0,0 +1,135 @@ +import { arraySidebar } from "vuepress-theme-hope"; +import { ICONS } from "./constants.js"; + +export const ai = arraySidebar([ + { + text: "Tổng quan nhập môn", + icon: ICONS.BASIC, + children: [ + { text: "⭐️Tổng quan khái niệm cốt lõi AI", link: "ai-core-concepts" }, + ], + }, + { + text: "Câu hỏi phỏng vấn", + icon: ICONS.INTERVIEW, + prefix: "interview-questions/", + children: [ + { + text: "⭐️Hướng dẫn phỏng vấn AI Application Development", + link: "ai-interview-guide", + }, + { + text: "Tổng hợp câu hỏi phỏng vấn nền tảng LLM", + link: "llm-interview-questions", + }, + { + text: "Tổng hợp câu hỏi phỏng vấn AI Agent", + link: "agent-interview-questions", + }, + { + text: "Thực chiến phỏng vấn project Agent", + link: "agent-project-interview-guide", + }, + { + text: "Tổng hợp câu hỏi phỏng vấn RAG", + link: "rag-interview-questions", + }, + { + text: "Tổng hợp câu hỏi phỏng vấn AI System Design", + link: "ai-system-design-interview-questions", + }, + ], + }, + { + text: "Nền tảng LLM", + icon: ICONS.MACHINE_LEARNING, + prefix: "llm-basis/", + children: [ + { + text: "Mổ xẻ cơ chế vận hành LLM (vạn chữ)", + link: "llm-operation-mechanism", + }, + { text: "Thực hành kỹ thuật gọi LLM API", link: "llm-api-engineering" }, + { + text: "Giải thích chi tiết structured output của LLM", + link: "structured-output-function-calling", + }, + { text: "Hệ thống đánh giá ứng dụng AI", link: "llm-evaluation" }, + ], + }, + { + text: "AI Agent", + icon: ICONS.CHAT, + prefix: "agent/", + children: [ + { + text: "⭐️Giải thích chi tiết khái niệm cốt lõi AI Agent", + link: "agent-basis", + }, + { + text: "⭐️Giải thích chi tiết hệ thống memory của AI Agent", + link: "agent-memory", + }, + { text: "Thiết kế hệ thống đa Agent phối hợp", link: "multi-agent" }, + { + text: "Hướng dẫn thực chiến Prompt Engineering", + link: "prompt-engineering", + }, + { + text: "Hướng dẫn thực chiến Context Engineering", + link: "context-engineering", + }, + { text: "Giải thích chi tiết Agent Skills (vạn chữ)", link: "skills" }, + { text: "Mổ xẻ giao thức MCP (vạn chữ)", link: "mcp" }, + { + text: "Giải thích chi tiết Harness Engineering", + link: "harness-engineering", + }, + { text: "Giải thích chi tiết AI workflow", link: "workflow-graph-loop" }, + { + text: "Giải thích chi tiết Loop Engineering", + link: "loop-engineering", + }, + ], + }, + { + text: "RAG", + icon: ICONS.SEARCH, + prefix: "rag/", + children: [ + { + text: "⭐️Giải thích chi tiết khái niệm cơ bản RAG", + link: "rag-basis", + }, + { + text: "Xử lý tài liệu và chiến lược chunking trong RAG", + link: "rag-document-processing", + }, + { + text: "⭐️Thuật toán vector index và vector database trong RAG", + link: "rag-vector-store", + }, + { + text: "Chiến lược cập nhật tài liệu knowledge base RAG", + link: "rag-knowledge-update", + }, + { text: "Giải thích chi tiết GraphRAG", link: "graphrag" }, + { text: "Tối ưu retrieval trong RAG", link: "rag-optimization" }, + ], + }, + { + text: "AI System Design", + icon: ICONS.DESIGN, + prefix: "system-design/", + children: [ + { + text: "System Design cho ứng dụng AI", + link: "ai-application-architecture", + }, + { text: "Thực chiến bảo mật LLM/Agent", link: "llm-security" }, + { text: "Giải thích chi tiết LLM Gateway", link: "llm-gateway" }, + { text: "AI Observability và Trace", link: "ai-observability" }, + { text: "Giải thích chi tiết công nghệ AI voice", link: "ai-voice" }, + ], + }, +]); diff --git a/vi/.vuepress/sidebar/books.ts b/vi/.vuepress/sidebar/books.ts new file mode 100644 index 00000000000..4ffeda97049 --- /dev/null +++ b/vi/.vuepress/sidebar/books.ts @@ -0,0 +1,36 @@ +import { arraySidebar } from "vuepress-theme-hope"; +import { ICONS } from "./constants.js"; + +export const books = arraySidebar([ + { + text: "Computer Science cơ bản", + link: "cs-basics", + icon: ICONS.COMPUTER, + }, + { + text: "Database", + link: "database", + icon: ICONS.DATABASE, + }, + { + text: "Search Engine", + link: "search-engine", + icon: ICONS.SEARCH, + }, + { + text: "Java", + link: "java", + icon: ICONS.JAVA, + }, + { + text: "Chất lượng phần mềm", + link: "software-quality", + icon: ICONS.HIGH_AVAILABLE, + }, + + { + text: "Distributed", + link: "distributed-system", + icon: ICONS.DISTRIBUTED, + }, +]); diff --git a/vi/.vuepress/sidebar/constants.ts b/vi/.vuepress/sidebar/constants.ts new file mode 100644 index 00000000000..c31e3f7db8d --- /dev/null +++ b/vi/.vuepress/sidebar/constants.ts @@ -0,0 +1,111 @@ +/** + * Hằng số icon cho sidebar + * Quản lý tập trung toàn bộ icon dùng trong cấu hình sidebar + */ +export const ICONS = { + // Icon cơ bản + STAR: "mdi:star-outline", + BASIC: "mdi:book-open-page-variant-outline", + CODE: "mdi:code-tags", + DESIGN: "mdi:palette-swatch-outline", + ROADMAP: "mdi:map-outline", + + // Lĩnh vực kỹ thuật + JAVA: "mdi:language-java", + COMPUTER: "mdi:desktop-classic", + DATABASE: "mdi:database-outline", + NETWORK: "mdi:lan", + + // Framework và công cụ + SPRING_BOOT: "mdi:leaf", + MYBATIS: "mdi:database-cog-outline", + NETTY: "mdi:server-network-outline", + + // Database + MYSQL: "mdi:database", + REDIS: "mdi:database-sync-outline", + ELASTICSEARCH: "mdi:database-search-outline", + MONGODB: "mdi:database-marker-outline", + SQL: "mdi:database-search", + + // Công cụ phát triển + TOOL: "mdi:tools", + MAVEN: "mdi:package-variant-closed", + GRADLE: "mdi:cog-outline", + GIT: "mdi:git", + DOCKER: "mdi:docker", + IDEA: "mdi:application-brackets-outline", + + // System Design + COMPONENT: "mdi:widgets-outline", + CONTAINER: "mdi:cube-outline", + SECURITY: "mdi:shield-lock-outline", + + // Distributed + DISTRIBUTED: "mdi:transit-connection-variant", + GATEWAY: "mdi:gate", + ID: "mdi:identifier", + LOCK: "mdi:lock-outline", + TRANSACTION: "mdi:bank-transfer", + RPC: "mdi:api", + FRAMEWORK: "mdi:layers-outline", + + // High Performance + PERFORMANCE: "mdi:speedometer", + CDN: "mdi:cloud-outline", + LOAD_BALANCING: "mdi:scale-balance", + MQ: "mdi:message-processing-outline", + + // High Availability + HIGH_AVAILABLE: "mdi:check-network-outline", + + // Operating System + OS: "mdi:desktop-classic", + LINUX: "mdi:linux", + VIRTUAL_MACHINE: "mdi:server", + + // Data Structure và Algorithm + DATA_STRUCTURE: "mdi:graph-outline", + ALGORITHM: "mdi:chart-tree", + + // Khác + FEATURED: "mdi:star-four-points-outline", + INTERVIEW: "mdi:briefcase-outline", + EXPERIENCE: "mdi:chart-timeline-variant", + CHAT: "mdi:comment-text-outline", + BOOK: "mdi:book-open-page-variant-outline", + PROJECT: "mdi:projector-screen-outline", + LIBRARY: "mdi:library-outline", + MACHINE_LEARNING: "mdi:robot-outline", + BIG_DATA: "mdi:database-search-outline", + SEARCH: "mdi:magnify", + WORK: "mdi:office-building-outline", +} as const; + +/** + * Hằng số text thường dùng + */ +export const COMMON_TEXT = { + IMPORTANT_POINTS: "Điểm kiến thức quan trọng", + SOURCE_CODE_ANALYSIS: "Phân tích source code", +} as const; + +/** + * Hàm hỗ trợ: tạo nhóm "Điểm kiến thức quan trọng" + */ +export const createImportantSection = (children: any[]) => ({ + text: COMMON_TEXT.IMPORTANT_POINTS, + icon: ICONS.STAR, + collapsible: true, + children, +}); + +/** + * Hàm hỗ trợ: tạo nhóm "Phân tích source code" + */ +export const createSourceCodeSection = (children: any[]) => ({ + text: COMMON_TEXT.SOURCE_CODE_ANALYSIS, + icon: ICONS.STAR, + collapsible: true, + children, +}); diff --git a/vi/.vuepress/sidebar/cs-basics.ts b/vi/.vuepress/sidebar/cs-basics.ts new file mode 100644 index 00000000000..c9dd360d202 --- /dev/null +++ b/vi/.vuepress/sidebar/cs-basics.ts @@ -0,0 +1,300 @@ +import { ICONS, createImportantSection } from "./constants.js"; + +export const csBasics = [ + { + text: "Network", + prefix: "network/", + icon: ICONS.NETWORK, + children: [ + { + text: "Câu hỏi phỏng vấn", + icon: ICONS.INTERVIEW, + children: [ + { + text: "⭐️Tổng hợp câu hỏi phỏng vấn Computer Network thường gặp (phần 1)", + link: "other-network-questions", + }, + { + text: "⭐️Tổng hợp câu hỏi phỏng vấn Computer Network thường gặp (phần 2)", + link: "other-network-questions2", + }, + // { text: "Tổng hợp kiến thức Computer Network", link: "computer-network-xiexiren-summary" }, + ], + }, + { + text: "Cơ bản", + icon: ICONS.STAR, + collapsible: true, + children: [ + { + text: "Giải thích chi tiết mô hình OSI 7 tầng và TCP/IP 4 tầng", + link: "osi-and-tcp-ip-model", + }, + { + text: "Từ lúc nhập URL đến khi trang hiển thị, thực sự đã xảy ra chuyện gì?", + link: "the-whole-process-of-accessing-web-pages", + }, + ], + }, + { + text: "Tầng Application", + icon: ICONS.CODE, + collapsible: true, + children: [ + { + text: "⭐️Tổng hợp các giao thức tầng Application thường gặp", + link: "application-layer-protocol", + }, + { text: "⭐️HTTP vs HTTPS", link: "http-vs-https" }, + { text: "⭐️Đã có HTTP, tại sao vẫn cần RPC?", link: "http-vs-rpc" }, + { + text: "RSA và ECDHE trong HTTPS handshake", + link: "https-rsa-vs-ecdhe", + }, + { text: "HTTP 1.0 vs HTTP 1.1", link: "http1.0-vs-http1.1" }, + { + text: "Tổng hợp các HTTP status code thường gặp", + link: "http-status-codes", + }, + { text: "Giải thích chi tiết DNS", link: "dns" }, + ], + }, + { + text: "Tầng Transport", + icon: ICONS.NETWORK, + collapsible: true, + children: [ + { + text: "⭐️TCP three-way handshake và four-way handshake", + link: "tcp-connection-and-disconnection", + }, + { text: "Giải thích chi tiết TCP TIME_WAIT", link: "tcp-time-wait" }, + { + text: "TCP Keepalive và HTTP Keep-Alive khác nhau thế nào?", + link: "tcp-keepalive-vs-http-keepalive", + }, + { + text: "TCP byte stream vs UDP datagram", + link: "tcp-byte-stream-udp-datagram", + }, + { + text: "⭐️TCP đảm bảo truyền tin tin cậy như thế nào?", + link: "tcp-reliability-guarantee", + }, + { + text: "Ping được thì TCP có chắc chắn kết nối được không?", + link: "can-ping-but-tcp-may-not-connect", + }, + { + text: "TCP và UDP có thể dùng chung một port không?", + link: "can-tcp-and-udp-use-the-same-port", + }, + { + text: "Một máy chủ có thể duy trì tối đa bao nhiêu TCP connection?", + link: "maximum-number-of-tcp-connections-per-host", + }, + ], + }, + { + text: "Tầng Network", + icon: ICONS.NETWORK, + collapsible: true, + children: [ + { text: "Giải thích chi tiết giao thức ARP", link: "arp" }, + { text: "Giải thích chi tiết giao thức NAT", link: "nat" }, + ], + }, + { + text: "Security", + icon: ICONS.SECURITY, + collapsible: true, + children: [ + { + text: "Tổng hợp các hình thức tấn công mạng thường gặp", + link: "network-attack-means", + }, + ], + }, + ], + }, + { + text: "Operating System", + prefix: "operating-system/", + icon: ICONS.OS, + children: [ + { + text: "Câu hỏi phỏng vấn", + icon: ICONS.INTERVIEW, + children: [ + { + text: "⭐️Tổng hợp câu hỏi phỏng vấn Operating System thường gặp (phần 1)", + link: "operating-system-basic-questions-01", + }, + { + text: "⭐️Tổng hợp câu hỏi phỏng vấn Operating System thường gặp (phần 2)", + link: "operating-system-basic-questions-02", + }, + ], + }, + { + text: "Chắc chắn hỏi khi phỏng vấn", + icon: ICONS.STAR, + children: [ + { + text: "⭐️Giải thích chi tiết virtual memory", + link: "virtual-memory", + }, + { + text: "⭐️Giải thích chi tiết I/O multiplexing", + link: "io-multiplexing", + }, + { text: "⭐️Giải thích chi tiết zero-copy", link: "zero-copy" }, + ], + }, + { + text: "Bộ nhớ và file system", + icon: ICONS.OS, + collapsible: true, + children: [ + { + text: "Giải thích chi tiết memory management", + link: "memory-management", + }, + { text: "Giải thích chi tiết file system", link: "file-system" }, + ], + }, + { + text: "Process và Thread", + icon: ICONS.STAR, + collapsible: true, + children: [ + { + text: "⭐️Giải thích chi tiết Process và Thread", + link: "process-and-thread", + }, + { text: "⭐️Lock và cơ chế đồng bộ", link: "os-lock-and-sync" }, + { text: "⭐️Giải thích chi tiết deadlock", link: "dead-lock" }, + { + text: "Interrupt, exception và system call", + link: "interrupt-exception-syscall", + }, + { + text: "CPU scheduling và system load", + link: "cpu-scheduling-and-load", + }, + { + text: "Giải thích chi tiết Inter-Process Communication (IPC)", + link: "ipc", + }, + ], + }, + { + text: "Linux", + icon: ICONS.LINUX, + children: [ + { text: "Tổng hợp kiến thức cơ bản Linux", link: "linux-intro" }, + { + text: "Tổng hợp kiến thức cơ bản Shell programming", + link: "shell-intro", + }, + ], + }, + ], + }, + { + text: "Data Structure", + prefix: "data-structure/", + icon: ICONS.DATA_STRUCTURE, + collapsible: true, + children: [ + { + text: "Hệ thống kiến thức", + link: "/cs-basics/data-structure/", + }, + { + text: "Cấu trúc cơ bản", + collapsible: true, + children: [ + { text: "Linear Data Structure", link: "linear-data-structure" }, + { text: "⭐️Hash Table", link: "hash-table" }, + ], + }, + { + text: "Tree và Heap", + collapsible: true, + children: [ + { text: "⭐️Cấu trúc Tree", link: "tree" }, + { text: "⭐️Heap", link: "heap" }, + { text: "Red-Black Tree", link: "red-black-tree" }, + ], + }, + { + text: "Graph và Set", + collapsible: true, + children: [ + { text: "Graph", link: "graph" }, + { text: "⭐️Union-Find", link: "union-find" }, + ], + }, + { + text: "String và sorted index", + collapsible: true, + children: [ + { text: "Trie (Prefix Tree)", link: "trie" }, + { text: "Skip List", link: "skip-list" }, + ], + }, + { + text: "Cấu trúc hướng kỹ thuật", + collapsible: true, + children: [ + { text: "⭐️Bloom Filter", link: "bloom-filter" }, + { text: "⭐️LRU Cache", link: "lru-cache" }, + ], + }, + ], + }, + { + text: "Algorithm", + prefix: "algorithms/", + icon: ICONS.ALGORITHM, + collapsible: true, + children: [ + { text: "Phân tích độ phức tạp", link: "complexity-analysis" }, + { text: "Binary Search", link: "binary-search" }, + { + text: "Two Pointers và Sliding Window", + link: "two-pointers-and-sliding-window", + }, + { text: "DFS và BFS", link: "dfs-bfs" }, + { text: "Backtracking", link: "backtracking" }, + { text: "Dynamic Programming", link: "dynamic-programming" }, + { text: "Greedy Algorithm", link: "greedy" }, + { text: "Bài toán Top K", link: "top-k" }, + { + text: "Tư tưởng thuật toán kinh điển", + link: "classical-algorithm-problems-recommendations", + }, + { + text: "Data Structure trên LeetCode", + link: "common-data-structures-leetcode-recommendations", + }, + { + text: "Bài toán thuật toán về chuỗi", + link: "string-algorithm-problems", + }, + { + text: "Bài toán thuật toán về linked list", + link: "linkedlist-algorithm-problems", + }, + { + text: "Coding Interviews (Jianzhi Offer)", + link: "the-sword-refers-to-offer", + }, + { + text: "Thuật toán sắp xếp kinh điển", + link: "10-classical-sorting-algorithms", + }, + ], + }, +]; diff --git a/vi/.vuepress/sidebar/high-quality-technical-articles.ts b/vi/.vuepress/sidebar/high-quality-technical-articles.ts new file mode 100644 index 00000000000..b5fbdb91b89 --- /dev/null +++ b/vi/.vuepress/sidebar/high-quality-technical-articles.ts @@ -0,0 +1,71 @@ +import { arraySidebar } from "vuepress-theme-hope"; +import { ICONS } from "./constants.js"; + +export const highQualityTechnicalArticles = arraySidebar([ + { + text: "Bí kíp lên trình", + icon: ICONS.PERFORMANCE, + prefix: "advanced-programmer/", + collapsible: false, + children: [ + "programmer-quickly-learn-new-technology", + "the-growth-strategy-of-the-technological-giant", + "ten-years-of-dachang-growth-road", + "meituan-three-year-summary-lesson-10", + "seven-tips-for-becoming-an-advanced-programmer", + "20-bad-habits-of-bad-programmers", + "thinking-about-technology-and-business-after-five-years-of-work", + ], + }, + { + text: "Trải nghiệm cá nhân", + icon: ICONS.EXPERIENCE, + prefix: "personal-experience/", + collapsible: false, + children: [ + "four-year-work-in-tencent-summary", + "two-years-of-back-end-develop--experience-in-didi-and-toutiao", + "8-years-programmer-work-summary", + "huawei-od-275-days", + ], + }, + { + text: "Lập trình viên", + icon: ICONS.CODE, + prefix: "programmer/", + collapsible: false, + children: [ + "programmer-career-directions", + "high-value-certifications-for-programmers", + "how-do-programmers-publish-a-technical-book", + "efficient-book-publishing-and-practice-guide", + ], + }, + { + text: "Phỏng vấn", + icon: ICONS.INTERVIEW, + prefix: "interview/", + collapsible: true, + children: [ + "the-experience-of-get-offer-from-over-20-big-companies", + "the-experience-and-thinking-of-an-interview-experienced-by-an-older-programmer", + "technical-preliminary-preparation", + "screen-candidates-for-packaging", + "summary-of-spring-recruitment", + "my-personal-experience-in-2021", + "how-to-examine-the-technical-ability-of-programmers-in-the-first-test-of-technology", + "some-secrets-about-alibaba-interview", + ], + }, + { + text: "Công việc", + icon: ICONS.WORK, + prefix: "work/", + collapsible: true, + children: [ + "get-into-work-mode-quickly-when-you-join-a-company", + "32-tips-improving-career", + "employee-performance", + ], + }, +]); diff --git a/vi/.vuepress/sidebar/index.ts b/vi/.vuepress/sidebar/index.ts new file mode 100644 index 00000000000..3c4fffb3f49 --- /dev/null +++ b/vi/.vuepress/sidebar/index.ts @@ -0,0 +1,758 @@ +import { sidebar } from "vuepress-theme-hope"; + +import { aboutTheAuthor } from "./about-the-author.js"; +import { ai } from "./ai.js"; +import { aiCoding } from "./ai-coding.js"; +import { books } from "./books.js"; +import { csBasics } from "./cs-basics.js"; +import { highQualityTechnicalArticles } from "./high-quality-technical-articles.js"; +import { openSourceProject } from "./open-source-project.js"; +import { roadmap } from "./roadmap.js"; +import { zhuanlan } from "./zhuanlan.js"; +import { + ICONS, + createImportantSection, + createSourceCodeSection, +} from "./constants.js"; + +export default sidebar({ + // Đường dẫn càng cụ thể thì phải đặt càng lên trên + "/ai-coding/": aiCoding, + "/ai/": ai, + "/roadmap/": roadmap, + "/cs-basics/": csBasics, + "/open-source-project/": openSourceProject, + "/books/": books, + "/about-the-author/": aboutTheAuthor, + "/high-quality-technical-articles/": highQualityTechnicalArticles, + "/zhuanlan/": zhuanlan, + // Bắt buộc đặt ở cuối cùng + "/": [ + { + text: "Giới thiệu project", + icon: ICONS.STAR, + collapsible: true, + prefix: "javaguide/", + children: ["intro", "use-suggestion", "contribution-guideline", "faq"], + }, + { + text: "Chuẩn bị phỏng vấn (bắt buộc xem)", + icon: ICONS.INTERVIEW, + collapsible: true, + prefix: "interview-preparation/", + children: [ + { + text: "Hệ thống kiến thức chuẩn bị phỏng vấn", + link: "/interview-preparation/", + }, + { + text: "⭐Kế hoạch vượt qua phỏng vấn Java backend", + link: "backend-interview-plan", + }, + { + text: "⭐Chuẩn bị phỏng vấn Java hiệu quả thế nào?", + link: "teach-you-how-to-prepare-for-the-interview-hand-in-hand", + }, + { + text: "⭐Hướng dẫn viết CV cho lập trình viên", + link: "resume-guide", + }, + { + text: "⭐Tổng hợp trọng điểm phỏng vấn Java backend", + link: "key-points-of-interview", + }, + { + text: "Tài liệu PDF phỏng vấn Java + backend", + link: "pdf-interview-javaguide", + }, + { text: "Lộ trình học Java", link: "java-roadmap" }, + { + text: "⭐Hướng dẫn về kinh nghiệm project", + link: "project-experience-guide", + }, + { + text: "⭐Trình bày project backend khi phỏng vấn thế nào?", + link: "backend-project-interview-guide", + }, + "how-to-handle-interview-nerves", + "internship-experience", + ], + }, + { + text: "Java", + icon: ICONS.JAVA, + collapsible: true, + prefix: "java/", + children: [ + { + text: "Hệ thống kiến thức Java", + link: "/java/", + }, + { + text: "Cơ bản", + prefix: "basis/", + icon: ICONS.BASIC, + children: [ + { + text: "⭐Câu hỏi phỏng vấn Java Basics thường gặp (phần 1)", + link: "java-basic-questions-01", + }, + { + text: "⭐Câu hỏi phỏng vấn Java Basics thường gặp (phần 2)", + link: "java-basic-questions-02", + }, + { + text: "⭐Câu hỏi phỏng vấn Java Basics thường gặp (phần 3)", + link: "java-basic-questions-03", + }, + createImportantSection([ + "why-there-only-value-passing-in-java", + "serialization", + "generics-and-wildcards", + "reflection", + "proxy", + "bigdecimal", + { + text: "Chọn kiểu dữ liệu tiền tệ trong Java", + link: "money-long-vs-bigdecimal", + }, + "unsafe", + "spi", + "syntactic-sugar", + ]), + ], + }, + { + text: "Collection", + prefix: "collection/", + icon: ICONS.CONTAINER, + children: [ + { + text: "⭐Câu hỏi phỏng vấn Java Collection thường gặp (phần 1)", + link: "java-collection-questions-01", + }, + { + text: "⭐Câu hỏi phỏng vấn Java Collection thường gặp (phần 2)", + link: "java-collection-questions-02", + }, + "java-collection-precautions-for-use", + createSourceCodeSection([ + "arraylist-source-code", + "linkedlist-source-code", + "hashmap-source-code", + "concurrent-hash-map-source-code", + "linkedhashmap-source-code", + "copyonwritearraylist-source-code", + "arrayblockingqueue-source-code", + "priorityqueue-source-code", + "delayqueue-source-code", + ]), + ], + }, + { + text: "Concurrent Programming", + prefix: "concurrent/", + icon: ICONS.PERFORMANCE, + children: [ + { + text: "⭐Câu hỏi phỏng vấn Java Concurrency thường gặp (phần 1)", + link: "java-concurrent-questions-01", + }, + { + text: "⭐Câu hỏi phỏng vấn Java Concurrency thường gặp (phần 2)", + link: "java-concurrent-questions-02", + }, + { + text: "⭐Câu hỏi phỏng vấn Java Concurrency thường gặp (phần 3)", + link: "java-concurrent-questions-03", + }, + createImportantSection([ + { + text: "Giải thích chi tiết lock trong Java", + link: "java-lock", + }, + "optimistic-lock-and-pessimistic-lock", + "cas", + "jmm", + "java-thread-pool-summary", + "java-thread-pool-best-practices", + "java-concurrent-collections", + "aqs", + "atomic-classes", + "threadlocal", + "completablefuture-intro", + "virtual-thread", + ]), + ], + }, + { + text: "IO", + prefix: "io/", + icon: ICONS.CODE, + collapsible: true, + children: ["io-basis", "io-design-patterns", "io-model", "nio-basis"], + }, + { + text: "JVM", + prefix: "jvm/", + icon: ICONS.VIRTUAL_MACHINE, + collapsible: true, + children: [ + { + text: "⭐Tổng hợp câu hỏi phỏng vấn JVM thường gặp", + link: "jvm-interview-questions", + }, + "memory-area", + "jvm-garbage-collection", + "class-file-structure", + "class-loading-process", + "classloader", + "jvm-parameters-intro", + "jdk-monitoring-and-troubleshooting-tools", + { + text: "Truy vết sự cố production Java backend", + link: "jvm-in-action", + }, + ], + }, + { + text: "Tính năng mới", + prefix: "new-features/", + icon: ICONS.FEATURED, + collapsible: true, + children: [ + "java8-common-new-features", + "java8-tutorial-translate", + "java9", + "java10", + "java11", + "java12-13", + "java14-15", + "java16", + "java17", + "java18", + "java19", + "java20", + "java21", + "java22-23", + "java24", + "java25", + ], + }, + ], + }, + { + text: "Database", + icon: ICONS.DATABASE, + prefix: "database/", + collapsible: true, + children: [ + { + text: "Hệ thống kiến thức Database", + link: "/database/", + }, + { + text: "Cơ bản", + icon: ICONS.BASIC, + children: [ + "basis", + "nosql", + { + text: "Giải thích chi tiết character set", + link: "character-set", + }, + { + text: "SQL", + icon: ICONS.SQL, + prefix: "sql/", + collapsible: true, + children: [ + "sql-syntax-summary", + "sql-questions-01", + "sql-questions-02", + "sql-questions-03", + "sql-questions-04", + "sql-questions-05", + ], + }, + ], + }, + { + text: "MySQL", + prefix: "mysql/", + icon: ICONS.MYSQL, + children: [ + { + text: "⭐Tổng hợp câu hỏi phỏng vấn MySQL thường gặp", + link: "mysql-questions-01", + }, + "mysql-high-performance-optimization-specification-recommendations", + createImportantSection([ + "mysql-index", + "mysql-index-invalidation", + { + text: "Giải thích chi tiết ba loại log của MySQL", + link: "mysql-logs", + }, + { + text: "Backup và restore MySQL", + link: "mysql-backup-and-restore", + }, + { + text: "Giải pháp đồng bộ MySQL sang ES", + link: "mysql-to-elasticsearch-sync", + }, + "transaction-isolation-level", + "innodb-implementation-of-mvcc", + "how-sql-executed-in-mysql", + "mysql-query-cache", + "mysql-query-execution-plan", + "mysql-auto-increment-primary-key-continuous", + "some-thoughts-on-database-storage-time", + "index-invalidation-caused-by-implicit-conversion", + ]), + ], + }, + { + text: "Redis", + prefix: "redis/", + icon: ICONS.REDIS, + children: [ + "cache-basics", + { + text: "⭐Tổng hợp câu hỏi phỏng vấn Redis thường gặp (phần 1)", + link: "redis-questions-01", + }, + { + text: "⭐Tổng hợp câu hỏi phỏng vấn Redis thường gặp (phần 2)", + link: "redis-questions-02", + }, + createImportantSection([ + "redis-delayed-task", + "redis-stream-mq", + "3-commonly-used-cache-read-and-write-strategies", + "redis-data-structures-01", + "redis-data-structures-02", + "redis-skiplist", + "redis-persistence", + "redis-memory-fragmentation", + "redis-common-blocking-problems-summary", + "redis-cluster", + ]), + ], + }, + { + text: "Elasticsearch", + prefix: "elasticsearch/", + icon: ICONS.ELASTICSEARCH, + collapsible: true, + children: ["elasticsearch-questions-01"], + }, + { + text: "MongoDB", + prefix: "mongodb/", + icon: ICONS.MONGODB, + collapsible: true, + children: ["mongodb-questions-01", "mongodb-questions-02"], + }, + ], + }, + { + text: "Công cụ phát triển", + icon: ICONS.TOOL, + prefix: "tools/", + collapsible: true, + children: [ + { + text: "Hệ thống kiến thức công cụ phát triển", + link: "/tools/", + }, + { + text: "Maven", + icon: ICONS.MAVEN, + prefix: "maven/", + children: [ + { + text: "Tổng hợp khái niệm cốt lõi Maven", + link: "maven-core-concepts", + }, + { text: "Best practice cho Maven", link: "maven-best-practices" }, + ], + }, + { + text: "Gradle", + icon: ICONS.GRADLE, + prefix: "gradle/", + children: ["gradle-core-concepts"], + }, + { + text: "Git", + icon: ICONS.GIT, + prefix: "git/", + children: ["git-intro", "github-tips"], + }, + { + text: "Docker", + icon: ICONS.DOCKER, + prefix: "docker/", + children: ["docker-intro", "docker-in-action"], + }, + { + text: "IDEA", + icon: ICONS.IDEA, + link: "https://gitee.com/SnailClimb/awesome-idea-tutorial", + }, + ], + }, + { + text: "Framework thường dùng", + prefix: "system-design/framework/", + icon: ICONS.COMPONENT, + collapsible: true, + children: [ + { + text: "Spring&Spring Boot", + icon: ICONS.SPRING_BOOT, + prefix: "spring/", + children: [ + { + text: "Tổng hợp câu hỏi phỏng vấn Spring thường gặp", + link: "spring-knowledge-and-questions-summary", + }, + { + text: "Tổng hợp câu hỏi phỏng vấn Spring Boot thường gặp", + link: "springboot-knowledge-and-questions-summary", + }, + "spring-common-annotations", + "springboot-source-code", + createImportantSection([ + "ioc-and-aop", + "spring-transaction", + "spring-design-patterns-summary", + "spring-boot-auto-assembly-principles", + "async", + ]), + ], + }, + { + text: "Tổng hợp câu hỏi phỏng vấn MyBatis thường gặp", + link: "mybatis/mybatis-interview", + }, + "netty", + ], + }, + { + text: "System Design", + icon: ICONS.DESIGN, + prefix: "system-design/", + collapsible: true, + children: [ + { + text: "Hệ thống kiến thức System Design", + link: "/system-design/", + }, + { + text: "Kiến thức cơ bản", + prefix: "basis/", + icon: ICONS.BASIC, + collapsible: true, + children: [ + "RESTfulAPI", + "software-engineering", + "naming", + "refactoring", + { + text: "Hướng dẫn unit test", + link: "unit-test", + }, + ], + }, + { + text: "Authentication & Authorization", + prefix: "security/", + icon: ICONS.SECURITY, + collapsible: true, + children: [ + "basis-of-authority-certification", + "jwt-intro", + "advantages-and-disadvantages-of-jwt", + "sso-intro", + "design-of-authority-system", + ], + }, + { + text: "An toàn dữ liệu", + prefix: "security/", + icon: ICONS.SECURITY, + collapsible: true, + children: [ + "encryption-algorithms", + "sentive-words-filter", + "data-desensitization", + "data-validation", + "why-password-reset-instead-of-retrieval", + ], + }, + { + text: "⭐Tổng hợp câu hỏi phỏng vấn System Design thường gặp", + link: "system-design-questions", + }, + { + text: "⭐Tổng hợp câu hỏi phỏng vấn Design Pattern thường gặp", + link: "https://interview.javaguide.cn/system-design/design-pattern.html", + }, + "schedule-task", + "web-real-time-message-push", + ], + }, + { + text: "Distributed", + icon: ICONS.DISTRIBUTED, + prefix: "distributed-system/", + collapsible: true, + children: [ + { + text: "Nhập môn Distributed System", + link: "distributed-system-intro", + }, + { + text: "⭐Câu hỏi phỏng vấn Distributed tần suất cao", + link: "distributed-system-interview-questions", + }, + { + text: "⭐Câu hỏi phỏng vấn Microservice tần suất cao", + link: "microservices-interview-questions", + }, + { + text: "Lý thuyết & thuật toán & giao thức", + icon: ICONS.ALGORITHM, + prefix: "protocol/", + collapsible: true, + children: [ + { + text: "Chuyên đề lý thuyết & thuật toán & giao thức", + link: "/distributed-system/protocol/", + }, + { + text: "Giải thích chi tiết định lý CAP và lý thuyết BASE", + link: "cap-and-base-theorem", + }, + { + text: "Giải thích chi tiết Distributed Coordination", + link: "centralized-and-decentralized", + }, + { + text: "Bài toán Byzantine Generals", + link: "byzantine-generals-problem", + }, + { + text: "Giải thích chi tiết thuật toán Paxos", + link: "paxos-algorithm", + }, + { + text: "Giải thích chi tiết thuật toán Raft", + link: "raft-algorithm", + }, + { text: "Giải thích chi tiết giao thức ZAB", link: "zab" }, + { + text: "Giải thích chi tiết giao thức Gossip", + link: "gossip-protocol", + }, + { + text: "Giải thích chi tiết thuật toán Consistent Hashing", + link: "consistent-hashing", + }, + ], + }, + { + text: "API Gateway", + icon: ICONS.GATEWAY, + children: [ + { + text: "Tổng hợp kiến thức cơ bản API Gateway", + link: "api-gateway", + }, + { + text: "Tổng hợp câu hỏi phỏng vấn Spring Cloud Gateway", + link: "spring-cloud-gateway-questions", + }, + ], + }, + { + text: "Distributed ID", + icon: ICONS.ID, + children: [ + { + text: "Giải thích chi tiết giải pháp sinh Distributed ID", + link: "distributed-id", + }, + { + text: "Hướng dẫn thực chiến thiết kế Distributed ID", + link: "distributed-id-design", + }, + ], + }, + { + text: "Distributed Lock", + icon: ICONS.LOCK, + children: [ + { + text: "Giới thiệu nhập môn Distributed Lock", + link: "distributed-lock", + }, + { + text: "Tổng hợp giải pháp hiện thực Distributed Lock thường gặp", + link: "distributed-lock-implementations", + }, + ], + }, + { + text: "Distributed Transaction", + icon: ICONS.TRANSACTION, + children: [ + { + text: "Tổng hợp giải pháp Distributed Transaction", + link: "distributed-transaction", + }, + ], + }, + { + text: "Distributed Configuration Center", + icon: ICONS.MAVEN, + children: [ + { + text: "Tổng hợp câu hỏi phỏng vấn Distributed Configuration Center", + link: "distributed-configuration-center", + }, + ], + }, + { + text: "RPC", + prefix: "rpc/", + icon: ICONS.RPC, + collapsible: true, + children: [ + { text: "Chuyên đề RPC", link: "/distributed-system/rpc/" }, + { text: "Tổng hợp kiến thức cơ bản RPC", link: "rpc-intro" }, + { text: "Tổng hợp câu hỏi phỏng vấn Dubbo", link: "dubbo" }, + ], + }, + { + text: "ZooKeeper", + prefix: "distributed-process-coordination/zookeeper/", + icon: ICONS.FRAMEWORK, + collapsible: true, + children: [ + { + text: "Chuyên đề ZooKeeper", + link: "/distributed-system/distributed-process-coordination/zookeeper/", + }, + { text: "Hướng dẫn nhập môn ZooKeeper", link: "zookeeper-intro" }, + { + text: "Giải thích chi tiết ZooKeeper nâng cao", + link: "zookeeper-plus", + }, + { + text: "Giáo trình thực chiến ZooKeeper", + link: "zookeeper-in-action", + }, + ], + }, + ], + }, + { + text: "High Performance", + icon: ICONS.PERFORMANCE, + prefix: "high-performance/", + collapsible: true, + children: [ + { + text: "⭐Câu hỏi phỏng vấn thiết kế hệ thống High Performance tần suất cao", + link: "high-performance-system-interview-questions", + }, + { + text: "CDN", + icon: ICONS.CDN, + children: ["cdn"], + }, + { + text: "Load Balancing", + icon: ICONS.LOAD_BALANCING, + children: [ + { + text: "Giải thích chi tiết nguyên lý và thuật toán Load Balancing", + link: "load-balancing", + }, + ], + }, + { + text: "Tối ưu Database", + icon: ICONS.MYSQL, + children: [ + "read-and-write-separation-and-library-subtable", + "data-cold-hot-separation", + "sql-optimization", + "deep-pagination-optimization", + ], + }, + { + text: "Message Queue", + prefix: "message-queue/", + icon: ICONS.MQ, + collapsible: true, + children: [ + { + text: "⭐Câu hỏi phỏng vấn Message Queue tần suất cao", + link: "message-queue-interview-questions", + }, + "message-queue", + "disruptor-questions", + "kafka-questions-01", + "rocketmq-questions", + "rabbitmq-questions", + ], + }, + ], + }, + { + text: "High Availability", + icon: ICONS.HIGH_AVAILABLE, + prefix: "high-availability/", + collapsible: true, + children: [ + { + text: "⭐Tổng hợp câu hỏi phỏng vấn hệ thống High Availability", + link: "high-availability-system-interview-questions", + }, + { + text: "Hướng dẫn thiết kế hệ thống High Availability", + link: "high-availability-system-design", + }, + { + text: "⭐Tổng hợp giải pháp idempotent cho API", + link: "idempotency", + }, + { + text: "⭐Giải thích chi tiết rate limiting", + link: "limit-request", + }, + { + text: "⭐Giải thích chi tiết cơ chế timeout và retry", + link: "timeout-and-retry", + }, + { + text: "Giải thích chi tiết degradation và circuit breaker", + link: "fallback-and-circuit-breaker", + }, + { + text: "Giải thích chi tiết thiết kế dự phòng", + link: "redundancy", + }, + { + text: "Nhập môn performance testing", + link: "performance-test", + }, + ], + }, + ], +}); diff --git a/vi/.vuepress/sidebar/open-source-project.ts b/vi/.vuepress/sidebar/open-source-project.ts new file mode 100644 index 00000000000..a1d5acd1705 --- /dev/null +++ b/vi/.vuepress/sidebar/open-source-project.ts @@ -0,0 +1,40 @@ +import { arraySidebar } from "vuepress-theme-hope"; +import { ICONS } from "./constants.js"; + +export const openSourceProject = arraySidebar([ + { + text: "Giáo trình kỹ thuật", + link: "tutorial", + icon: ICONS.BOOK, + }, + { + text: "Project thực chiến", + link: "practical-project", + icon: ICONS.PROJECT, + }, + { + text: "AI", + link: "machine-learning", + icon: ICONS.MACHINE_LEARNING, + }, + { + text: "System Design", + link: "system-design", + icon: ICONS.DESIGN, + }, + { + text: "Thư viện tiện ích", + link: "tool-library", + icon: ICONS.LIBRARY, + }, + { + text: "Công cụ phát triển", + link: "tools", + icon: ICONS.TOOL, + }, + { + text: "Big Data", + link: "big-data", + icon: ICONS.BIG_DATA, + }, +]); diff --git a/vi/.vuepress/sidebar/roadmap.ts b/vi/.vuepress/sidebar/roadmap.ts new file mode 100644 index 00000000000..7fe8d0cd3c1 --- /dev/null +++ b/vi/.vuepress/sidebar/roadmap.ts @@ -0,0 +1,32 @@ +import { arraySidebar } from "vuepress-theme-hope"; +import { ICONS } from "./constants.js"; + +export const roadmap = arraySidebar([ + { + text: "Lộ trình học", + icon: ICONS.ROADMAP, + children: [ + { text: "Tổng hợp lộ trình học (2026)", link: "/roadmap/" }, + { + text: "Lộ trình học Java backend (2026)", + link: "java-roadmap", + }, + { + text: "Lộ trình Java/Go chuyển sang AI (2026)", + link: "java-to-ai-roadmap", + }, + { + text: "Gợi ý backend chuyển sang AI Agent (2026)", + link: "backend-to-ai-agent-roadmap", + }, + { + text: "Lộ trình học full-stack cho backend (2026)", + link: "full-stack-roadmap", + }, + { + text: "Lộ trình học Test Development (2026)", + link: "test-development-roadmap", + }, + ], + }, +]); diff --git a/vi/.vuepress/sidebar/zhuanlan.ts b/vi/.vuepress/sidebar/zhuanlan.ts new file mode 100644 index 00000000000..4648c94238b --- /dev/null +++ b/vi/.vuepress/sidebar/zhuanlan.ts @@ -0,0 +1,33 @@ +import { arraySidebar } from "vuepress-theme-hope"; +import { ICONS } from "./constants.js"; + +export const zhuanlan = arraySidebar([ + { + text: "Project thực chiến", + icon: ICONS.PROJECT, + collapsible: false, + children: [ + { + text: "Nền tảng phỏng vấn thông minh Spring AI", + link: "interview-guide", + }, + { text: "Tự viết RPC framework", link: "handwritten-rpc-framework" }, + ], + }, + { + text: "Tài liệu phỏng vấn", + icon: ICONS.INTERVIEW, + collapsible: false, + children: [ + { text: "Java Interview Guide", link: "java-mian-shi-zhi-bei" }, + { + text: "Câu hỏi System Design & tình huống backend tần suất cao", + link: "back-end-interview-high-frequency-system-design-and-scenario-questions", + }, + { + text: "Java Must-Read Source Code Series", + link: "source-code-reading", + }, + ], + }, +]); diff --git a/vi/.vuepress/styles/config.scss b/vi/.vuepress/styles/config.scss new file mode 100644 index 00000000000..9c8419c3c05 --- /dev/null +++ b/vi/.vuepress/styles/config.scss @@ -0,0 +1 @@ +$theme-color: #2980b9; diff --git a/vi/.vuepress/styles/index.scss b/vi/.vuepress/styles/index.scss new file mode 100644 index 00000000000..aa1b9734a92 --- /dev/null +++ b/vi/.vuepress/styles/index.scss @@ -0,0 +1,168 @@ +body { + @media (min-width: 1440px) { + font-size: 16px; + } +} + +#markdown-content img, +.vp-content img, +.theme-hope-content img { + max-width: 100%; + height: auto; +} + +.article-promo-image { + display: block; + margin: 1rem auto; + + img { + display: block; + width: min(100%, 1774px); + aspect-ratio: 1774 / 887; + height: auto; + margin: 0 auto; + } +} + +.article-footer-qrcode { + display: block; + width: min(612px, 100%); + margin: 0 auto; +} + +// ============================================ +// Chế độ đọc tập trung - ẩn navbar, sidebar và mục lục +// ============================================ + +// Animation chuyển cảnh +.vp-navbar, +.vp-sidebar, +.vp-page, +.theme-container .vp-page { + transition: + transform 0.3s ease, + opacity 0.3s ease, + margin 0.3s ease, + padding 0.3s ease, + width 0.3s ease; +} + +// Chế độ ẩn layout +html.layout-hidden { + // Ẩn navbar trên cùng + .vp-navbar { + transform: translateY(-100%) !important; + opacity: 0 !important; + pointer-events: none !important; + } + + // Ẩn sidebar bên trái + .vp-sidebar { + transform: translateX(-100%) !important; + opacity: 0 !important; + pointer-events: none !important; + width: 0 !important; + } + + // Ẩn nút bật/tắt sidebar (nút mở rộng trên màn hình nhỏ) + .toggle-sidebar-wrapper { + display: none !important; + opacity: 0 !important; + pointer-events: none !important; + } + + // Ẩn lớp phủ của sidebar + .vp-sidebar-mask { + display: none !important; + } + + // Wrapper của sidebar + .vp-sidebar-wrapper, + .sidebar-wrapper { + width: 0 !important; + min-width: 0 !important; + padding: 0 !important; + margin: 0 !important; + } + + // Ẩn mục lục bên phải (TOC) + .vp-toc-placeholder, + .toc-wrapper, + .vp-toc, + aside.vp-toc, + .toc { + display: none !important; + width: 0 !important; + } + + // Điều chỉnh container chính - bỏ padding/margin bên trái + .theme-container { + padding-left: 0 !important; + padding-right: 0 !important; + + .vp-page { + padding-left: 2rem !important; + padding-right: 2rem !important; + padding-top: 1rem !important; + margin-left: 0 !important; + max-width: 100% !important; + width: 100% !important; + } + } + + // Điều chỉnh vùng nội dung của theme - cho nội dung rộng hơn + .theme-hope-content, + .vp-page-content, + .vp-content { + max-width: 100% !important; + width: 100% !important; + margin: 0 !important; + padding: 1rem 2rem !important; + } + + // Điều chỉnh container của trang + .vp-page-container { + padding-top: 1rem !important; + padding-left: 0 !important; + padding-right: 0 !important; + max-width: 100% !important; + } + + // Đảm bảo vùng nội dung căn giữa và có bề rộng vừa phải + .theme-container > main { + margin-left: 0 !important; + padding-left: 0 !important; + max-width: 100% !important; + } + + // Điều chỉnh responsive + @media (min-width: 960px) { + .theme-container .vp-page { + margin-left: 0 !important; + padding-left: 3rem !important; + padding-right: 3rem !important; + } + + .theme-hope-content, + .vp-page-content, + .vp-content { + max-width: 100% !important; + padding: 1rem 2rem !important; + } + } + + @media (min-width: 1440px) { + .theme-container .vp-page { + margin-left: 0 !important; + padding-left: 4rem !important; + padding-right: 4rem !important; + } + + .theme-hope-content, + .vp-page-content, + .vp-content { + max-width: 100% !important; + padding: 1rem 3rem !important; + } + } +} diff --git a/vi/.vuepress/styles/palette.scss b/vi/.vuepress/styles/palette.scss new file mode 100644 index 00000000000..de19553fc89 --- /dev/null +++ b/vi/.vuepress/styles/palette.scss @@ -0,0 +1,4 @@ +$sidebar-width: 20rem; +$sidebar-mobile-width: 16rem; +$vp-font: 'Georgia, -apple-system, "Nimbus Roman No9 L", "PingFang SC", "Hiragino Sans GB", "Noto Serif SC", "Microsoft Yahei", "WenQuanYi Micro Hei", sans-serif'; +$vp-font-heading: 'Georgia, -apple-system, "Nimbus Roman No9 L", "PingFang SC", "Hiragino Sans GB", "Noto Serif SC", "Microsoft Yahei", "WenQuanYi Micro Hei", sans-serif'; diff --git a/vi/.vuepress/theme.ts b/vi/.vuepress/theme.ts new file mode 100644 index 00000000000..be1fee84f19 --- /dev/null +++ b/vi/.vuepress/theme.ts @@ -0,0 +1,484 @@ +import { getText } from "@vuepress/helper"; +import { getDirname, path } from "vuepress/utils"; +import { hopeTheme } from "vuepress-theme-hope"; + +import navbar from "./navbar.js"; +import sidebar from "./sidebar/index.js"; + +const __dirname = getDirname(import.meta.url); +const docsearchAppId = process.env.DOCSEARCH_APP_ID; +const docsearchApiKey = process.env.DOCSEARCH_API_KEY; +const docsearchIndexName = process.env.DOCSEARCH_INDEX_NAME; +const docsearchOptions = + docsearchAppId && docsearchApiKey && docsearchIndexName + ? { + appId: docsearchAppId, + apiKey: docsearchApiKey, + indexName: docsearchIndexName, + locales: { + "/": { + placeholder: "Tìm kiếm JavaGuide", + }, + }, + } + : null; +const MIN_META_DESCRIPTION_LENGTH = 150; +const MAX_META_DESCRIPTION_LENGTH = 160; + +// Các legacy URL bên dưới thuộc site gốc tiếng Trung (javaguide.cn), không tồn tại +// dưới prefix /vi/ nên không cần redirect. Giữ lại object rỗng để cấu hình plugin +// vẫn hợp lệ; thêm cặp "đường-dẫn-cũ": "đường-dẫn-mới" ở đây khi site vi đổi cấu trúc. +const legacyRedirects = {}; + +const segmentDisplayNames = { + ai: "AI", + "ai-coding": "AI Coding", + algorithms: "Algorithm", + basis: "Kiến thức cơ bản", + books: "Sách kỹ thuật", + collection: "Java Collection", + concurrent: "Java Concurrency", + "cs-basics": "Computer Science cơ bản", + "data-structure": "Data Structure", + database: "Database", + "distributed-process-coordination": "Distributed Coordination", + "distributed-system": "Distributed System", + docker: "Docker", + elasticsearch: "Elasticsearch", + framework: "Framework", + git: "Git", + gradle: "Gradle", + "high-availability": "High Availability", + "high-performance": "High Performance", + "interview-preparation": "Chuẩn bị phỏng vấn", + io: "Java IO", + java: "Java", + javaguide: "JavaGuide", + jvm: "JVM", + "message-queue": "Message Queue", + mysql: "MySQL", + network: "Computer Network", + "new-features": "Tính năng mới của Java", + "open-source-project": "Project mã nguồn mở", + "operating-system": "Operating System", + protocol: "Giao thức & thuật toán distributed", + rag: "RAG", + redis: "Redis", + rpc: "RPC", + security: "Security", + sql: "SQL", + "system-design": "System Design", + tools: "Công cụ phát triển", + zookeeper: "ZooKeeper", +}; + +const normalizeDescriptionText = (value) => + String(value ?? "") + .replace(/<[^>]+>/g, " ") + .replace(/ /g, " ") + .replace(/&/g, "&") + .replace(/</g, "<") + .replace(/>/g, ">") + .replace(/"/g, '"') + .replace(/'/g, "'") + .replace(/\s+/g, " ") + .trim(); + +const toArray = (value) => { + if (Array.isArray(value)) return value; + return value ? [value] : []; +}; + +const formatPathSegment = (segment) => + segmentDisplayNames[segment] ?? + decodeURIComponent(segment) + .replace(/-/g, " ") + .replace(/\b\w/g, (char) => char.toUpperCase()); + +const getPathTopic = (page) => + page.path.split("/").filter(Boolean).map(formatPathSegment).join(" / "); + +const getHeaderTitles = (page) => + toArray(page.headers) + .map(({ title }) => normalizeDescriptionText(title)) + .filter(Boolean) + .slice(0, 4); + +const getPageText = (page, app) => + normalizeDescriptionText( + getText( + page.data.excerpt ?? page.contentRendered ?? page.content ?? "", + app.siteData.base, + { + length: 220, + singleLine: true, + }, + ), + ); + +const trimDescription = (description) => { + if (description.length <= MAX_META_DESCRIPTION_LENGTH) return description; + + const trimmed = description.slice(0, MAX_META_DESCRIPTION_LENGTH); + const lastStop = Math.max( + trimmed.lastIndexOf("."), + trimmed.lastIndexOf("!"), + trimmed.lastIndexOf("?"), + trimmed.lastIndexOf(";"), + ); + + if (lastStop >= MIN_META_DESCRIPTION_LENGTH - 5) + return trimmed.slice(0, lastStop + 1); + + const lastSoftStop = Math.max( + trimmed.lastIndexOf(","), + trimmed.lastIndexOf(";"), + ); + + if (lastSoftStop >= MIN_META_DESCRIPTION_LENGTH - 5) { + const base = trimmed.slice(0, lastSoftStop).replace(/[,;\s]+$/, ""); + const result = `${base} và các nội dung cốt lõi khác.`; + + return result.length <= MAX_META_DESCRIPTION_LENGTH + ? result + : `${result.slice(0, MAX_META_DESCRIPTION_LENGTH - 1)}.`; + } + + return `${description.slice(0, MAX_META_DESCRIPTION_LENGTH - 1)}.`; +}; + +const buildSeoDescription = (page, app) => { + const existingDescription = normalizeDescriptionText( + page.frontmatter.description, + ); + + if (existingDescription.length >= MIN_META_DESCRIPTION_LENGTH) + return trimDescription(existingDescription); + + if (page.path === "/") + return trimDescription( + "JavaGuide là bộ hướng dẫn học dành cho Java backend developer và người chuẩn bị phỏng vấn, bao phủ có hệ thống Java Basics, Collection, Concurrency, JVM, MySQL, Redis, distributed, high concurrency, high availability, system design, message queue, computer science cơ bản và AI application development, phù hợp để ôn tập tuyển dụng, bù lỗ hổng kiến thức và lên lộ trình học.", + ); + + if (page.path === "/home.html") + return trimDescription( + "Trang chủ JavaGuide tập hợp lộ trình học Java backend, hệ thống kiến thức cốt lõi và lối vào các câu hỏi phỏng vấn tần suất cao, bao phủ Java Basics, Concurrency, JVM, database, Redis, distributed, system design, high performance, high availability, computer science cơ bản và AI application development, giúp người đọc nhanh chóng định vị nội dung trọng tâm.", + ); + + if (page.path === "/404.html") + return trimDescription( + "Trang báo không tìm thấy nội dung của JavaGuide, giúp người đọc quay lại lối vào các nội dung cốt lõi như hướng dẫn phỏng vấn Java, kiến thức phỏng vấn backend, computer science cơ bản, database, Redis, distributed, system design và AI application development, để tiếp tục tìm tài liệu học, tổng hợp câu hỏi phỏng vấn và bài viết thực hành.", + ); + + const title = normalizeDescriptionText(page.title); + const category = toArray(page.frontmatter.category) + .map(normalizeDescriptionText) + .filter(Boolean); + const tags = toArray(page.frontmatter.tag ?? page.frontmatter.tags) + .map(normalizeDescriptionText) + .filter(Boolean) + .slice(0, 4); + const headers = getHeaderTitles(page); + const focusItems = [...headers, ...tags].filter(Boolean).slice(0, 5); + const topic = getPathTopic(page) || title || category[0] || "JavaGuide"; + const pageText = getPageText(page, app); + const parts = [ + existingDescription || (title ? `${title}: ` : ""), + focusItems.length + ? `Tập trung vào ${focusItems.join(", ")} và các nội dung liên quan. ` + : "", + `Kết hợp hệ thống kiến thức JavaGuide để hệ thống hóa khái niệm cốt lõi, phương pháp thực hành, vấn đề thường gặp và điểm thi phỏng vấn tần suất cao của ${topic}, bao phủ phân tích nguyên lý, trường hợp sử dụng, so sánh giải pháp và tổng kết kinh nghiệm, phù hợp để backend developer học có hệ thống, ôn tập phỏng vấn và bù lỗ hổng kiến thức.`, + pageText && !existingDescription.includes(pageText.slice(0, 24)) + ? pageText + : "", + ]; + + return trimDescription( + normalizeDescriptionText(parts.filter(Boolean).join("")), + ); +}; + +export default hopeTheme({ + hostname: "https://javaguide.cn/", + logo: "/logo.png", + favicon: "/favicon.ico", + + author: { + name: "Guide", + url: "https://javaguide.cn/article/", + }, + + repo: "https://github.com/Snailclimb/JavaGuide", + docsDir: "vi", + pure: true, + focus: false, + print: false, + breadcrumb: false, + navbar, + sidebar, + footer: + '鄂ICP备2020015769号-1', + displayFooter: true, + + pageInfo: ["Author", "Category", "Tag", "Original", "Word", "ReadingTime"], + + blog: { + intro: "/about-the-author/", + medias: { + Zhihu: "https://www.zhihu.com/people/javaguide", + Github: "https://github.com/Snailclimb", + Gitee: "https://gitee.com/SnailClimb", + }, + }, + + markdown: { + align: true, + codeTabs: true, + mermaid: true, + gfm: true, + // Bản dịch đang tiến hành: nhiều trang trong sidebar chưa có file .md tương ứng. + // Để "error" sẽ làm fail build. Đổi lại thành "error" khi đã dịch xong toàn bộ. + linksCheck: { + build: "warn", + }, + include: { + resolvePath: (file, cwd) => { + if (file.startsWith("@")) + return path.resolve( + __dirname, + "../snippets", + file.replace("@", "./"), + ); + + return path.resolve(cwd, file); + }, + }, + tasklist: true, + }, + + plugins: { + blog: true, + seo: { + canonical: "https://javaguide.cn/vi", + fallBackImage: "https://javaguide.cn/logo.png", + ogp: (ogp, page, app) => ({ + ...ogp, + "og:description": buildSeoDescription(page, app), + }), + jsonLd: (jsonLD, page, app) => ({ + ...jsonLD, + description: buildSeoDescription(page, app), + }), + customHead: (head, page, app) => { + page.frontmatter.description = buildSeoDescription(page, app); + + if (page.path === "/") + head.push([ + "script", + { type: "application/ld+json" }, + JSON.stringify({ + "@context": "https://schema.org", + "@type": "WebSite", + name: "JavaGuide", + alternateName: "Hướng dẫn phỏng vấn Java", + url: "https://javaguide.cn/vi/", + inLanguage: "vi-VN", + description: + "JavaGuide là bộ hướng dẫn phỏng vấn Java và phỏng vấn backend, bao phủ các kiến thức cốt lõi như Java, MySQL, Redis, Spring, distributed và system design.", + publisher: { + "@type": "Person", + name: "Guide", + url: "https://javaguide.cn/article/", + }, + }), + ]); + + if (page.path === "/home.html") + head.push([ + "script", + { type: "application/ld+json" }, + JSON.stringify({ + "@context": "https://schema.org", + "@type": "ItemList", + name: "Nội dung cốt lõi phỏng vấn Java", + itemListElement: [ + { + "@type": "ListItem", + position: 1, + name: "Câu hỏi phỏng vấn Java Basics", + url: "https://javaguide.cn/vi/java/basis/java-basic-questions-01.html", + }, + { + "@type": "ListItem", + position: 2, + name: "Câu hỏi phỏng vấn Java Collection", + url: "https://javaguide.cn/vi/java/collection/java-collection-questions-01.html", + }, + { + "@type": "ListItem", + position: 3, + name: "Câu hỏi phỏng vấn Java Concurrency", + url: "https://javaguide.cn/vi/java/concurrent/java-concurrent-questions-01.html", + }, + { + "@type": "ListItem", + position: 4, + name: "Câu hỏi phỏng vấn JVM", + url: "https://javaguide.cn/vi/java/jvm/memory-area.html", + }, + { + "@type": "ListItem", + position: 5, + name: "Câu hỏi phỏng vấn Spring", + url: "https://javaguide.cn/vi/system-design/framework/spring/spring-knowledge-and-questions-summary.html", + }, + { + "@type": "ListItem", + position: 6, + name: "Câu hỏi phỏng vấn MySQL", + url: "https://javaguide.cn/vi/database/mysql/mysql-questions-01.html", + }, + { + "@type": "ListItem", + position: 7, + name: "Câu hỏi phỏng vấn Redis", + url: "https://javaguide.cn/vi/database/redis/redis-questions-01.html", + }, + { + "@type": "ListItem", + position: 8, + name: "Câu hỏi phỏng vấn System Design", + url: "https://javaguide.cn/vi/system-design/system-design-questions.html", + }, + ], + }), + ]); + + if (page.path === "/ai/") + head.push([ + "script", + { type: "application/ld+json" }, + JSON.stringify({ + "@context": "https://schema.org", + "@type": "ItemList", + name: "Nội dung cốt lõi phỏng vấn AI Application Development", + itemListElement: [ + { + "@type": "ListItem", + position: 1, + name: "Hướng dẫn phỏng vấn AI Application Development", + url: "https://javaguide.cn/vi/ai/interview-questions/ai-interview-guide.html", + }, + { + "@type": "ListItem", + position: 2, + name: "Câu hỏi phỏng vấn nền tảng LLM", + url: "https://javaguide.cn/vi/ai/interview-questions/llm-interview-questions.html", + }, + { + "@type": "ListItem", + position: 3, + name: "Câu hỏi phỏng vấn AI Agent", + url: "https://javaguide.cn/vi/ai/interview-questions/agent-interview-questions.html", + }, + { + "@type": "ListItem", + position: 4, + name: "Câu hỏi phỏng vấn RAG", + url: "https://javaguide.cn/vi/ai/interview-questions/rag-interview-questions.html", + }, + { + "@type": "ListItem", + position: 5, + name: "Câu hỏi phỏng vấn AI System Design", + url: "https://javaguide.cn/vi/ai/interview-questions/ai-system-design-interview-questions.html", + }, + { + "@type": "ListItem", + position: 6, + name: "System Design cho ứng dụng AI", + url: "https://javaguide.cn/vi/ai/system-design/ai-application-architecture.html", + }, + ], + }), + ]); + + if (page.path === "/cs-basics/") + head.push([ + "script", + { type: "application/ld+json" }, + JSON.stringify({ + "@context": "https://schema.org", + "@type": "ItemList", + name: "Nội dung cốt lõi phỏng vấn Computer Science", + itemListElement: [ + { + "@type": "ListItem", + position: 1, + name: "Câu hỏi phỏng vấn Computer Network thường gặp", + url: "https://javaguide.cn/vi/cs-basics/network/other-network-questions.html", + }, + { + "@type": "ListItem", + position: 2, + name: "Câu hỏi phỏng vấn Operating System thường gặp", + url: "https://javaguide.cn/vi/cs-basics/operating-system/operating-system-basic-questions-01.html", + }, + { + "@type": "ListItem", + position: 3, + name: "Linear Data Structure", + url: "https://javaguide.cn/vi/cs-basics/data-structure/linear-data-structure.html", + }, + { + "@type": "ListItem", + position: 4, + name: "Mười thuật toán sắp xếp kinh điển", + url: "https://javaguide.cn/vi/cs-basics/algorithms/10-classical-sorting-algorithms.html", + }, + { + "@type": "ListItem", + position: 5, + name: "HTTP và HTTPS", + url: "https://javaguide.cn/vi/cs-basics/network/http-vs-https.html", + }, + { + "@type": "ListItem", + position: 6, + name: "TCP three-way handshake và four-way handshake", + url: "https://javaguide.cn/vi/cs-basics/network/tcp-connection-and-disconnection.html", + }, + ], + }), + ]); + }, + }, + sitemap: { + changefreq: "monthly", + }, + + redirect: { + config: legacyRedirects, + }, + + // The upstream copyright plugin can throw during hydration if `#app` is unavailable. + // Keep it disabled until the plugin adds a null-safe mount path. + copyright: false, + + feed: { + atom: true, + json: true, + rss: true, + }, + + icon: { + assets: "iconify", + }, + + photoSwipe: false, + + // Sau khi xin được DocSearch key thì cấu hình các biến môi trường ở trên; trước đó tắt index tìm kiếm cục bộ. + ...(docsearchOptions ? { docsearch: docsearchOptions } : {}), + search: false, + }, +}); diff --git a/vi/GLOSSARY.md b/vi/GLOSSARY.md new file mode 100644 index 00000000000..79b354c711a --- /dev/null +++ b/vi/GLOSSARY.md @@ -0,0 +1,105 @@ +# Glossary — Thuật ngữ JavaGuide (Trung → Việt/Anh) + +> Bổ sung khi gặp thuật ngữ mới. Quy tắc gốc: [`../CLAUDE.md`](../CLAUDE.md) §4. + +## Quy ước + +- **Giữ tiếng Anh**: thuật ngữ kỹ thuật đã chuẩn trong ngành (thread, heap, index, cache...). +- **Dịch sang Việt**: từ ngữ mô tả, tiêu đề, giải thích. +- Lần xuất hiện đầu của khái niệm khó: `dịch Việt (English term)`. + +## Bảng tra cứu + +| Tiếng Trung | Bản dịch | Ghi chú | +| ----------------- | ----------------------------------- | ---------------- | +| 面试题 | câu hỏi phỏng vấn | | +| 八股文 | câu hỏi lý thuyết phỏng vấn | cách nói lóng TQ | +| 学习路线 | lộ trình học | | +| 知识体系 | hệ thống kiến thức | | +| 总结 | tổng hợp | | +| 详解 | giải thích chi tiết | | +| 原理 | nguyên lý | | +| 底层 | tầng dưới / cơ chế bên trong | | +| 源码 | source code | giữ tiếng Anh | +| 常见 | thường gặp | | +| 优缺点 | ưu / nhược điểm | | +| 使用场景 | trường hợp sử dụng | | +| 注意事项 | lưu ý | | +| 适合谁看 | dành cho ai | heading README | +| 学习重点 | trọng tâm học | heading README | +| 建议阅读顺序 | thứ tự đọc đề xuất | heading README | +| 线程 | thread | | +| 线程池 | thread pool | | +| 进程 | process | | +| 堆 / 栈 | heap / stack | | +| 垃圾回收 | garbage collection (GC) | | +| 类加载器 | class loader | | +| 字节码 | bytecode | | +| 反射 | reflection | | +| 泛型 | generic | | +| 通配符 | wildcard | | +| 类型擦除 | type erasure | | +| 序列化 | serialization | | +| 值传递 | truyền theo giá trị (pass by value) | | +| 代理 | proxy | | +| 动态代理 | dynamic proxy | | +| 语法糖 | syntactic sugar | | +| 集合 | collection | | +| 哈希冲突 | hash collision | | +| 红黑树 | red-black tree | | +| 链表 | linked list | | +| 数组 | array | | +| 扩容 | mở rộng dung lượng (resize) | | +| 负载因子 | load factor | | +| 锁 | lock | | +| 死锁 | deadlock | | +| 悲观锁 / 乐观锁 | pessimistic lock / optimistic lock | | +| 可重入锁 | reentrant lock | | +| 公平锁 / 非公平锁 | fair lock / unfair lock | | +| 原子性 | tính nguyên tử (atomicity) | | +| 可见性 | tính khả kiến (visibility) | | +| 有序性 | tính có thứ tự (ordering) | | +| 内存屏障 | memory barrier | | +| 上下文切换 | context switch | | +| 并发 / 并行 | concurrency / parallelism | | +| 阻塞 / 非阻塞 | blocking / non-blocking | | +| 虚拟线程 | virtual thread | | +| 索引 | index | | +| 事务 | transaction | | +| 隔离级别 | isolation level | | +| 脏读 | dirty read | | +| 幻读 | phantom read | | +| 主从复制 | master-slave replication | | +| 分库分表 | sharding | | +| 执行计划 | execution plan | | +| 连接池 | connection pool | | +| 缓存 | cache | | +| 缓存穿透 | cache penetration | | +| 缓存击穿 | cache breakdown | | +| 缓存雪崩 | cache avalanche | | +| 负载均衡 | load balancing | | +| 消息队列 | message queue | | +| 幂等 | idempotent | | +| 限流 | rate limiting | | +| 熔断 | circuit breaker | | +| 服务发现 | service discovery | | +| 分布式锁 | distributed lock | | +| 微服务 | microservice | | +| 高并发 | high concurrency | | +| 高可用 | high availability | | +| 吞吐量 | throughput | | +| 延迟 | latency | | +| 三次握手 | three-way handshake | | +| 虚拟内存 | virtual memory | | +| 零拷贝 | zero-copy | | +| 大模型 | LLM (mô hình ngôn ngữ lớn) | | +| 提示词 | prompt | | +| 向量数据库 | vector database | | +| 微调 | fine-tuning | | +| 推理 | inference | | +| 上下文窗口 | context window | | +| 框架 | framework | | +| 中间件 | middleware | | +| 后端 / 前端 | backend / frontend | | +| 部署 | deploy | | +| 性能 | performance | | diff --git a/vi/PROGRESS.md b/vi/PROGRESS.md new file mode 100644 index 00000000000..de7280509d2 --- /dev/null +++ b/vi/PROGRESS.md @@ -0,0 +1,200 @@ +# Tiến độ dịch JavaGuide → Tiếng Việt + +> **Nguồn sự thật duy nhất** về trạng thái dịch. Agent/người nào tiếp tục công việc: **đọc file này đầu tiên**. +> Luật dịch: [`../CLAUDE.md`](../CLAUDE.md) · Thuật ngữ: [`GLOSSARY.md`](GLOSSARY.md) + +**Nội dung**: 12/455 file · **Hạ tầng site**: xong · **Cập nhật**: 2026-09-18 + +Ký hiệu: `[ ]` chưa dịch · `[x]` xong · `[~]` đang dịch + +--- + +## 🚦 BẮT ĐẦU TỪ ĐÂY + +**File tiếp theo cần dịch**: `roadmap/java-to-ai-roadmap.md` (63KB) + +Xem toàn bộ việc còn lại: `make sync` + +Quy trình mỗi file: + +1. Đọc file gốc: `cat docs/` (CodeGraph KHÔNG index `.md`, xem CLAUDE.md §6) +2. Dịch → ghi vào `vi/` (đúng đường dẫn mirror) +3. Verify chữ Hán: `make check` → chỉ được còn ngoại lệ ở CLAUDE.md §3.3.1 +4. Fix YAML frontmatter (CLAUDE.md §3.1.1 — **bắt buộc**, nếu không sẽ fail build) +5. `make vi-build` → phải "success" +6. Đánh dấu `[x]` vào file này + cập nhật số đếm ở trên + +### Lệnh + +| Lệnh | Việc | +| ------------- | --------------------------------------------- | +| `make help` | Danh sách lệnh | +| `make cn-dev` | Site tiếng Trung → localhost:8080 | +| `make vi-dev` | Site tiếng Việt → localhost:8081/vi/ | +| `make build` | Build cả hai site | +| `make sync` | Sau `git pull`: file nào mới / gốc nào đã đổi | +| `make check` | Quét chữ Hán còn sót | + +> ⚠️ **KHÔNG dùng `pnpm vi:dev`** — script đã bị gỡ khỏi `package.json` để tránh +> conflict khi pull upstream. Dùng `make` (xem CLAUDE.md §9). + +--- + +## ✅ Hạ tầng site VuePress (HOÀN TẤT — đã build thành công) + +Chi tiết: [`../CLAUDE.md`](../CLAUDE.md) §8. + +- [x] `.vuepress/config.ts` — `base:"/vi/"`, `dest:"./dist/vi"`, `lang:"vi-VN"`, loại PROGRESS/GLOSSARY khỏi pages +- [x] `.vuepress/theme.ts` — `docsDir:"vi"`, SEO/JSON-LD tiếng Việt, canonical `/vi`, `trimDescription()` đổi sang dấu câu Latin, `legacyRedirects` làm rỗng, `linksCheck` hạ xuống `warn` +- [x] `.vuepress/navbar.ts` — dịch xong +- [x] `.vuepress/sidebar/` — 11 file, dịch xong toàn bộ (289 chuỗi) +- [x] `.vuepress/components/` — dịch chuỗi UI (LayoutToggle, unlock ×2, LazyMermaid, ClickImagePreview) +- [x] `.vuepress/features/unlock/` — dịch comment +- [x] `.vuepress/styles/` — dịch comment (SCSS giữ nguyên → UI y hệt bản gốc) +- [x] `.vuepress/public/` — copy nguyên (logo, favicon, icon) +- [x] `package.json` — **đã revert về nguyên bản** (zero-conflict, xem CLAUDE.md §9) +- [x] `Makefile` — toàn bộ lệnh, thay cho npm scripts +- [x] `i18n/lang-switch/` — nút chuyển ngôn ngữ 中文 | VI trên **cả hai** site +- [x] `i18n/cn.config.ts` — wrapper config, chèn nút vào site CN mà không sửa `docs/` +- [x] `i18n/sync-check.mjs` — báo cáo file chưa dịch / gốc đã đổi sau khi pull + +**Kiểm chứng đã chạy**: + +- `make vi-build` → success, 11 trang, asset prefix `/vi/`, `` +- `make cn-build` → success, **641 trang**, wrapper config load được config upstream +- Toggle: trang đã dịch → link đúng trang tương ứng; trang chưa dịch → nút VI mờ, trỏ về `/vi/` (không link gãy) +- `git diff` **rỗng hoàn toàn** → pull upstream không conflict + +⚠️ **Việc phải làm khi dịch xong toàn bộ**: đổi `linksCheck.build` từ `"warn"` về `"error"` trong `vi/.vuepress/theme.ts`. + +--- + +## Giai đoạn 0 — Snippets (dùng chung) ✅ 7/7 + +- [x] `snippets/article-header.snippet.md` +- [x] `snippets/article-footer.snippet.md` +- [x] `snippets/small-advertisement.snippet.md` +- [x] `snippets/planet.snippet.md` +- [x] `snippets/planet2.snippet.md` +- [x] `snippets/rag-project.snippet.md` +- [x] `snippets/yuanma.snippet.md` + +## Giai đoạn 1 — Trục lộ trình học · 4/8 + +- [x] `README.md` — trang chủ +- [x] `home.md` — mục lục tổng +- [x] `roadmap/README.md` — tổng hợp lộ trình +- [x] `roadmap/java-roadmap.md` — ⭐ lộ trình Java backend (77KB) +- [ ] `roadmap/java-to-ai-roadmap.md` — lộ trình AI cho dev Java/Go (63KB) +- [ ] `roadmap/backend-to-ai-agent-roadmap.md` +- [ ] `roadmap/full-stack-roadmap.md` +- [ ] `roadmap/test-development-roadmap.md` + +## Giai đoạn 2 — Java core · 0/80 + +### 2.0 Mục lục + +- [ ] `java/README.md` + +### 2.1 Java Basics (15) + +- [ ] `java/basis/README.md` +- [ ] `java/basis/java-basic-questions-01.md` (70KB) +- [ ] `java/basis/java-basic-questions-02.md` (47KB) +- [ ] `java/basis/java-basic-questions-03.md` +- [ ] `java/basis/java-keyword-summary.md` +- [ ] `java/basis/why-there-only-value-passing-in-java.md` +- [ ] `java/basis/generics-and-wildcards.md` +- [ ] `java/basis/reflection.md` +- [ ] `java/basis/proxy.md` +- [ ] `java/basis/spi.md` +- [ ] `java/basis/serialization.md` +- [ ] `java/basis/bigdecimal.md` +- [ ] `java/basis/money-long-vs-bigdecimal.md` +- [ ] `java/basis/unsafe.md` +- [ ] `java/basis/syntactic-sugar.md` + +### 2.2 Collection (13) + +- [ ] `java/collection/README.md` +- [ ] `java/collection/java-collection-questions-01.md` +- [ ] `java/collection/java-collection-questions-02.md` +- [ ] `java/collection/java-collection-precautions-for-use.md` +- [ ] `java/collection/arraylist-source-code.md` +- [ ] `java/collection/linkedlist-source-code.md` +- [ ] `java/collection/hashmap-source-code.md` +- [ ] `java/collection/concurrent-hash-map-source-code.md` +- [ ] `java/collection/linkedhashmap-source-code.md` +- [ ] `java/collection/copyonwritearraylist-source-code.md` +- [ ] `java/collection/arrayblockingqueue-source-code.md` +- [ ] `java/collection/priorityqueue-source-code.md` +- [ ] `java/collection/delayqueue-source-code.md` + +### 2.3 Concurrent (17) + +- [ ] `java/concurrent/README.md` +- [ ] `java/concurrent/java-concurrent-questions-01.md` +- [ ] `java/concurrent/java-concurrent-questions-02.md` (66KB) +- [ ] `java/concurrent/java-concurrent-questions-03.md` (97KB) +- [ ] `java/concurrent/jmm.md` +- [ ] `java/concurrent/java-lock.md` +- [ ] `java/concurrent/optimistic-lock-and-pessimistic-lock.md` +- [ ] `java/concurrent/cas.md` +- [ ] `java/concurrent/aqs.md` (104KB) +- [ ] `java/concurrent/reentrantlock.md` (48KB) +- [ ] `java/concurrent/atomic-classes.md` +- [ ] `java/concurrent/threadlocal.md` +- [ ] `java/concurrent/java-thread-pool-summary.md` (52KB) +- [ ] `java/concurrent/java-thread-pool-best-practices.md` +- [ ] `java/concurrent/java-concurrent-collections.md` +- [ ] `java/concurrent/completablefuture-intro.md` +- [ ] `java/concurrent/virtual-thread.md` + +### 2.4 JVM (11) + +- [ ] `java/jvm/README.md` +- [ ] `java/jvm/memory-area.md` +- [ ] `java/jvm/jvm-garbage-collection.md` +- [ ] `java/jvm/class-file-structure.md` +- [ ] `java/jvm/class-loading-process.md` +- [ ] `java/jvm/classloader.md` +- [ ] `java/jvm/jvm-parameters-intro.md` +- [ ] `java/jvm/jvm-intro.md` +- [ ] `java/jvm/jdk-monitoring-and-troubleshooting-tools.md` +- [ ] `java/jvm/jvm-in-action.md` +- [ ] `java/jvm/jvm-interview-questions.md` + +### 2.5 IO (5) + +- [ ] `java/io/README.md` +- [ ] `java/io/io-basis.md` +- [ ] `java/io/io-design-patterns.md` +- [ ] `java/io/io-model.md` +- [ ] `java/io/nio-basis.md` + +### 2.6 New Features (18) + +- [ ] `java/new-features/README.md` +- [ ] `java/new-features/java8-common-new-features.md` +- [ ] `java/new-features/java8-tutorial-translate.md` +- [ ] `java/new-features/java9.md` +- [ ] `java/new-features/java10.md` +- [ ] `java/new-features/java11.md` +- [ ] `java/new-features/java12-13.md` +- [ ] `java/new-features/java14-15.md` +- [ ] `java/new-features/java16.md` +- [ ] `java/new-features/java17.md` +- [ ] `java/new-features/java18.md` +- [ ] `java/new-features/java19.md` +- [ ] `java/new-features/java20.md` +- [ ] `java/new-features/java21.md` +- [ ] `java/new-features/java22-23.md` +- [ ] `java/new-features/java24.md` +- [ ] `java/new-features/java25.md` +- [ ] `java/new-features/java26.md` + +--- + +## Giai đoạn 3-7 — Chưa mở + +Sẽ thêm chi tiết khi Giai đoạn 2 hoàn tất. Thứ tự: `cs-basics/` → `database/` → `system-design/` → `distributed-system/` + `high-performance/` + `high-availability/` → `ai/`, `ai-coding/`, `tools/`, còn lại. diff --git a/vi/README.md b/vi/README.md new file mode 100644 index 00000000000..93b075203cc --- /dev/null +++ b/vi/README.md @@ -0,0 +1,79 @@ +--- +home: true +icon: "mdi:home-outline" +title: JavaGuide (Hệ thống kiến thức phỏng vấn Java & backend) +description: JavaGuide là bộ hướng dẫn hệ thống kiến thức phỏng vấn Java và backend với 156K+ Star trên GitHub, miễn phí mã nguồn mở, bao phủ có hệ thống Java, computer science cơ bản, database, distributed, high concurrency, high availability, system design và AI application development, phù hợp cho tuyển dụng sinh viên, tuyển dụng có kinh nghiệm, nhảy việc và ôn tập năng lực backend một cách hệ thống. +heroImage: /logo.svg +heroText: JavaGuide +tagline: Hệ thống kiến thức phỏng vấn Java và backend với 156K+ Star trên GitHub, bao phủ computer science cơ bản, database, distributed, high concurrency, system design và AI application development +sitemap: + changefreq: weekly + priority: 0.9 +head: + - - meta + - name: keywords + content: JavaGuide,phỏng vấn Java,hướng dẫn phỏng vấn Java,lý thuyết phỏng vấn Java,phỏng vấn backend,backend development,phỏng vấn database,phỏng vấn MySQL,phỏng vấn Redis,distributed,high concurrency,high performance,high availability,system design,message queue,cache,computer network,Linux,phỏng vấn AI,AI application development,Agent,RAG,MCP,LLM,AI coding + - - meta + - property: og:image + content: https://javaguide.cn/logo.png +actions: + - text: Bắt đầu đọc + link: /home.md + type: primary + - text: Knowledge Planet + link: /about-the-author/zhishixingqiu-two-years.md + type: default +footer: |- + 鄂ICP备2020015769号-1 | Theme: VuePress Theme Hope +--- + + + +## Lối vào chính + +- **Tuyến chính phỏng vấn backend**: [Hướng dẫn phỏng vấn backend](./home.md) (⭐ lõi của website): tổng hợp có hệ thống lý thuyết phỏng vấn Java và các câu hỏi phỏng vấn backend tần suất cao, bao phủ Java Basics, Collection, Concurrency, JVM, Spring, MySQL, Redis, distributed, high concurrency, high availability và system design. +- **Computer science cơ bản**: [Hướng dẫn phỏng vấn kiến thức nền tảng](./cs-basics/): hệ thống hóa computer network, operating system, data structure và algorithm — nền tảng của phỏng vấn backend, phù hợp để bù đắp lỗ hổng kiến thức cơ bản. +- **AI application development**: [Hướng dẫn phỏng vấn AI application development](./ai/) (⭐ mới): dành cho backend developer, hệ thống hóa kiến thức tần suất cao về nền tảng LLM, Prompt, Agent, RAG, MCP, kỹ thuật LLM API và AI system design; nếu muốn học bài bản, có thể kết hợp với [Lộ trình học AI application development và Agent (bản mới nhất 2026)](./roadmap/java-to-ai-roadmap.md) và [Gợi ý học chuyển hướng từ backend sang AI Agent (bản mới nhất 2026)](./roadmap/backend-to-ai-agent-roadmap.md). +- **AI coding thực chiến**: [Hướng dẫn thực hành AI coding](./ai-coding/) (⭐ mới): tập trung vào Claude Code, Codex, AI IDE, CLI Agent, quản lý context và workflow phát triển có AI hỗ trợ, giúp bạn thực sự đưa AI vào công việc code hằng ngày. +- **Lộ trình học**: [Tổng hợp lộ trình học (bản mới nhất 2026)](./roadmap/): tổng hợp gợi ý học tập có hệ thống cho các hướng Java backend, AI application development, AI Agent và full-stack. +- **Tài liệu mở rộng**: + - [《Java Interview Guide》](https://javaguide.cn/zhuanlan/java-mian-shi-zhi-bei.html): bốn năm mài giũa, bổ sung cho nội dung bản mã nguồn mở JavaGuide, đưa bạn chuẩn bị phỏng vấn backend có hệ thống từ con số không! + - [《Câu hỏi system design & tình huống tần suất cao trong phỏng vấn backend》](https://javaguide.cn/zhuanlan/back-end-interview-high-frequency-system-design-and-scenario-questions.html): 30+ câu hỏi system design và tình huống tần suất cao, giúp bạn ứng phó với xu hướng phỏng vấn hiện nay ở các công ty lớn và vừa. + - [⭐ Nền tảng hỗ trợ phỏng vấn thông minh AI + RAG Knowledge Base](https://javaguide.cn/zhuanlan/interview-guide.html): project thực chiến LLM dựa trên Spring Boot 4.0 + Java 21 + Spring AI 2.0, phù hợp làm project học tập và project ghi vào CV. + +## Bài viết chọn lọc + +- **Lộ trình phỏng vấn backend**: [Kế hoạch vượt qua phỏng vấn Java backend](./interview-preparation/backend-interview-plan.md), [Lộ trình học Java (bản mới nhất 2026)](./interview-preparation/java-roadmap.md), [Tổng hợp trọng điểm phỏng vấn Java backend](./interview-preparation/key-points-of-interview.md). Khi chưa biết bắt đầu ôn từ đâu, hãy ưu tiên xem nhóm này. +- **Câu hỏi tần suất cao về Java và database**: [Java Basics](./java/basis/java-basic-questions-01.md), [Java Collection](./java/collection/java-collection-questions-01.md), [Java Concurrency](./java/concurrent/java-concurrent-questions-01.md), [JVM](./java/jvm/README.md), [MySQL](./database/mysql/mysql-questions-01.md), [Redis](./database/redis/redis-questions-01.md). Phù hợp để luyện tập trung các vấn đề cốt lõi về ngôn ngữ, runtime và lưu trữ dữ liệu. +- **Câu hỏi tần suất cao về kiến trúc và middleware**: [Distributed](./distributed-system/distributed-system-interview-questions.md), [Microservice](./distributed-system/microservices-interview-questions.md), [Message queue](./high-performance/message-queue/message-queue-interview-questions.md), [High performance](./high-performance/high-performance-system-interview-questions.md), [High availability](./high-availability/high-availability-system-interview-questions.md), [System design](./system-design/system-design-questions.md). Phù hợp để chuẩn bị tuyển dụng có kinh nghiệm, vị trí trung–cao cấp và các câu hỏi đào sâu tình huống project. +- **Củng cố kiến thức nền tảng**: [Computer network](./cs-basics/network/other-network-questions.md), [Operating system](./cs-basics/operating-system/operating-system-basic-questions-01.md), [Process và thread](./cs-basics/operating-system/process-and-thread.md), [Data structure và algorithm](./cs-basics/algorithms/). Phù hợp để bù đắp năng lực nền tảng mà tuyển dụng sinh viên, tuyển dụng có kinh nghiệm và phỏng vấn công ty lớn đều không thể né tránh. +- **Nâng cao AI application development**: [Lộ trình học AI application development và Agent (bản mới nhất 2026)](./roadmap/java-to-ai-roadmap.md), [Gợi ý học chuyển hướng từ backend sang AI Agent (bản mới nhất 2026)](./roadmap/backend-to-ai-agent-roadmap.md), [Hệ thống kiến thức AI application development](./ai/), [Thực hành kỹ thuật LLM API](./ai/llm-basis/llm-api-engineering.md), [Khái niệm cơ bản về RAG](./ai/rag/rag-basis.md), [System design cho ứng dụng AI](./ai/system-design/ai-application-architecture.md). Phù hợp để backend developer xác định rõ lộ trình học trước, rồi đi từ việc gọi model đến ứng dụng AI có thể đưa lên production. +- **Tăng hiệu suất bằng AI coding**: [Hướng dẫn AI coding thực chiến](./ai-coding/), [Hướng dẫn sử dụng Claude Code](./ai-coding/practices/claudecode-tips.md), [Hướng dẫn sử dụng Codex](./ai-coding/practices/codex-best-practices.md), [Lựa chọn và thực hành AI IDE](./ai-coding/practices/ai-ide.md). Phù hợp để đưa công cụ AI coding thực sự vào quy trình phát triển, refactor và xử lý sự cố hằng ngày. + +## Về JavaGuide + +JavaGuide là một kho kiến thức mã nguồn mở dành cho Java và backend developer, đã đạt **156K+ Star** trên GitHub. Project khởi đầu từ việc ôn tập phỏng vấn Java, dần mở rộng thành bộ hướng dẫn học tập có hệ thống bao phủ công nghệ backend cốt lõi, thực hành kỹ thuật và AI application development. + +JavaGuide được duy trì liên tục từ khi mã nguồn mở năm 2018, tích lũy **6200+** commit, với hơn **640+** người đóng góp cùng tham gia duy trì và hoàn thiện. + +![Tình hình Star, Fork, Issue và PR hiện tại của JavaGuide](https://oss.javaguide.cn/github/javaguide/intro/javaguide-star-issue-pr.png) + +Nội dung website bao phủ: + +- **Phỏng vấn backend**: kiến thức cốt lõi về Java Basics, Collection, Concurrency, JVM, MySQL, Redis, distributed, system design. +- **AI application development**: công nghệ tiên tiến như nền tảng LLM, Agent, RAG, giao thức MCP. + +Thực lòng mong làm tốt project này, để nó thực sự giúp được những bạn đang cần! + +Nếu thấy nội dung của JavaGuide hữu ích với bạn, hãy tặng một Star miễn phí (hoàn toàn không ép, thấy nội dung tốt và có thu hoạch rồi hãy thích), đó là sự khích lệ lớn nhất với tôi. Cảm ơn mọi người đã đồng hành, cùng cố gắng! Đường dẫn: [GitHub](https://github.com/Snailclimb/JavaGuide) | [Gitee](https://gitee.com/SnailClimb/JavaGuide). + +- [Giới thiệu project](./javaguide/intro.md) (sự ra đời của JavaGuide) +- [Hướng dẫn đóng góp](./javaguide/contribution-guideline.md) (mong chờ đóng góp của bạn, phần thưởng phong phú) +- [Câu hỏi thường gặp](./javaguide/faq.md) (trả lời chung một số thắc mắc của mọi người) + +## Bản PDF & liên hệ WeChat + +- Nếu bạn thích bản **PDF** hơn (ví dụ để đọc khi đi lại / đọc offline / in ra học), hãy quét mã QR bên dưới và nhắn "**PDF**" để nhận bản mới nhất (cập nhật liên tục, xem giới thiệu chi tiết tại: **[Tài liệu PDF phỏng vấn backend mới nhất 2026](./interview-preparation/pdf-interview-javaguide.md)**). +- Nếu bạn muốn kết bạn WeChat với tôi, hãy quét mã QR bên dưới và nhắn "**微信**" (WeChat). Tôi sẽ chia sẻ một số nội dung kỹ thuật chất lượng, tài liệu học tập và cập nhật project trên trang cá nhân. + +Tài khoản công khai JavaGuide diff --git a/vi/home.md b/vi/home.md new file mode 100644 index 00000000000..d37665e1d46 --- /dev/null +++ b/vi/home.md @@ -0,0 +1,413 @@ +--- +icon: "mdi:head-lightbulb-outline" +title: Hướng dẫn phỏng vấn Java (Tổng hợp câu hỏi phỏng vấn backend của JavaGuide) +description: Hướng dẫn phỏng vấn Java của JavaGuide, tổng hợp có hệ thống lý thuyết và câu hỏi phỏng vấn backend, bao phủ Java Basics, Collection, Concurrency, JVM, Spring, MySQL, Redis, system design và distributed, phù hợp cho ôn tập tuyển dụng sinh viên và tuyển dụng có kinh nghiệm. +sitemap: + changefreq: weekly + priority: 1 +head: + - - meta + - name: keywords + content: phỏng vấn Java,hướng dẫn phỏng vấn Java,lý thuyết phỏng vấn Java,câu hỏi phỏng vấn Java,phỏng vấn Java Basics,phỏng vấn JVM,phỏng vấn concurrency,phỏng vấn thread pool,phỏng vấn Spring,phỏng vấn MySQL,phỏng vấn Redis,phỏng vấn system design,phỏng vấn distributed,phỏng vấn backend +--- + + + + + +JavaGuide là bộ **hướng dẫn phỏng vấn Java** và **tài liệu ôn tập phỏng vấn backend** có hệ thống, nội dung bao phủ các điểm kiến thức cốt lõi như Java Basics, Collection, concurrent programming, JVM, Spring/Spring Boot, MySQL, Redis, distributed, high concurrency, high availability và system design. + +Nếu bạn đang chuẩn bị phỏng vấn tuyển dụng sinh viên, tuyển dụng có kinh nghiệm hoặc nhảy việc, hãy bắt đầu từ [Kế hoạch vượt qua phỏng vấn Java backend](./interview-preparation/backend-interview-plan.md), rồi ôn dần các câu hỏi phỏng vấn Java và backend tần suất cao theo từng module bên dưới. + +Toàn bộ nội dung website đã được mã nguồn mở miễn phí, hoan nghênh cùng [duy trì và hoàn thiện](http://localhost:8080/javaguide/contribution-guideline.html), nếu thấy hữu ích thì hoan nghênh Star! + +- **Địa chỉ project**: +- **Đọc online**: + +## Tài liệu mở rộng + +- [Project mã nguồn mở Java chất lượng](./open-source-project/): tuyển chọn các project Java mã nguồn mở trên Gitee/GitHub phù hợp để học, thực chiến và ghi vào CV. +- [Gợi ý sách kỹ thuật chất lượng](./books/): bao phủ các hướng computer science cơ bản, database, search engine, distributed system, kiến trúc high availability. + +## Chuẩn bị phỏng vấn + +- [⭐ Kế hoạch vượt qua phỏng vấn Java backend (bao phủ hệ thống backend chung)](./interview-preparation/backend-interview-plan.md) (nhất định phải xem :+1:) +- [Chuẩn bị phỏng vấn Java hiệu quả thế nào?](./interview-preparation/teach-you-how-to-prepare-for-the-interview-hand-in-hand.md) +- [Tổng hợp trọng điểm phỏng vấn Java backend](./interview-preparation/key-points-of-interview.md) +- [Lộ trình học Java (bản mới nhất, 4w+ chữ)](./interview-preparation/java-roadmap.md) +- [Hướng dẫn viết CV cho lập trình viên](./interview-preparation/resume-guide.md) +- [Hướng dẫn về kinh nghiệm project](./interview-preparation/project-experience-guide.md) +- [Phỏng vấn quá căng thẳng thì làm sao?](./interview-preparation/how-to-handle-interview-nerves.md) +- [Tuyển dụng sinh viên mà không có kinh nghiệm thực tập thì sao? Viết kinh nghiệm thực tập thế nào?](./interview-preparation/internship-experience.md) + +## Java + +### Basics + +**Tổng hợp điểm kiến thức/câu hỏi phỏng vấn**: (bắt buộc xem :+1:) + +- [Tổng hợp điểm kiến thức & câu hỏi phỏng vấn Java Basics thường gặp (phần 1)](./java/basis/java-basic-questions-01.md) +- [Tổng hợp điểm kiến thức & câu hỏi phỏng vấn Java Basics thường gặp (phần 2)](./java/basis/java-basic-questions-02.md) +- [Tổng hợp điểm kiến thức & câu hỏi phỏng vấn Java Basics thường gặp (phần 3)](./java/basis/java-basic-questions-03.md) + +**Giải thích chi tiết các điểm kiến thức quan trọng**: + +- [Tại sao Java chỉ có pass by value?](./java/basis/why-there-only-value-passing-in-java.md) +- [Giải thích chi tiết Java serialization](./java/basis/serialization.md) +- [Giải thích chi tiết generic & wildcard](./java/basis/generics-and-wildcards.md) +- [Giải thích chi tiết cơ chế reflection của Java](./java/basis/reflection.md) +- [Giải thích chi tiết proxy pattern trong Java](./java/basis/proxy.md) +- [Giải thích chi tiết BigDecimal](./java/basis/bigdecimal.md) +- [Giải thích chi tiết class "ma thuật" Unsafe của Java](./java/basis/unsafe.md) +- [Giải thích chi tiết cơ chế Java SPI](./java/basis/spi.md) +- [Giải thích chi tiết syntactic sugar trong Java](./java/basis/syntactic-sugar.md) + +### Collection + +**Tổng hợp điểm kiến thức/câu hỏi phỏng vấn**: + +- [Tổng hợp điểm kiến thức & câu hỏi phỏng vấn Java Collection thường gặp (phần 1)](./java/collection/java-collection-questions-01.md) (bắt buộc xem :+1:) +- [Tổng hợp điểm kiến thức & câu hỏi phỏng vấn Java Collection thường gặp (phần 2)](./java/collection/java-collection-questions-02.md) (bắt buộc xem :+1:) +- [Tổng hợp lưu ý khi sử dụng Java Collection](./java/collection/java-collection-precautions-for-use.md) + +**Phân tích source code**: + +- [Source code cốt lõi ArrayList + phân tích cơ chế resize](./java/collection/arraylist-source-code.md) +- [Phân tích source code cốt lõi LinkedList](./java/collection/linkedlist-source-code.md) +- [Source code cốt lõi HashMap + phân tích cấu trúc dữ liệu bên trong](./java/collection/hashmap-source-code.md) +- [Source code cốt lõi ConcurrentHashMap + phân tích cấu trúc dữ liệu bên trong](./java/collection/concurrent-hash-map-source-code.md) +- [Phân tích source code cốt lõi LinkedHashMap](./java/collection/linkedhashmap-source-code.md) +- [Phân tích source code cốt lõi CopyOnWriteArrayList](./java/collection/copyonwritearraylist-source-code.md) +- [Phân tích source code cốt lõi ArrayBlockingQueue](./java/collection/arrayblockingqueue-source-code.md) +- [Phân tích source code cốt lõi PriorityQueue](./java/collection/priorityqueue-source-code.md) +- [Phân tích source code cốt lõi DelayQueue](./java/collection/priorityqueue-source-code.md) + +### IO + +- [Tổng hợp kiến thức cơ bản về IO](./java/io/io-basis.md) +- [Tổng hợp design pattern trong IO](./java/io/io-design-patterns.md) +- [Giải thích chi tiết các IO model](./java/io/io-model.md) +- [Tổng hợp kiến thức cốt lõi NIO](./java/io/nio-basis.md) + +### Concurrency + +**Tổng hợp điểm kiến thức/câu hỏi phỏng vấn**: (bắt buộc xem :+1:) + +- [Tổng hợp điểm kiến thức & câu hỏi phỏng vấn Java Concurrency thường gặp (phần 1)](./java/concurrent/java-concurrent-questions-01.md) +- [Tổng hợp điểm kiến thức & câu hỏi phỏng vấn Java Concurrency thường gặp (phần 2)](./java/concurrent/java-concurrent-questions-02.md) +- [Tổng hợp điểm kiến thức & câu hỏi phỏng vấn Java Concurrency thường gặp (phần 3)](./java/concurrent/java-concurrent-questions-03.md) + +**Giải thích chi tiết các điểm kiến thức quan trọng**: + +- [Giải thích chi tiết optimistic lock và pessimistic lock](./java/concurrent/optimistic-lock-and-pessimistic-lock.md) +- [Giải thích chi tiết CAS](./java/concurrent/cas.md) +- [Giải thích chi tiết JMM (Java Memory Model)](./java/concurrent/jmm.md) +- **Thread pool**: [Giải thích chi tiết Java thread pool](./java/concurrent/java-thread-pool-summary.md), [Best practice cho Java thread pool](./java/concurrent/java-thread-pool-best-practices.md) +- [Giải thích chi tiết ThreadLocal](./java/concurrent/threadlocal.md) +- [Tổng hợp concurrent collection trong Java](./java/concurrent/java-concurrent-collections.md) +- [Tổng hợp các Atomic class](./java/concurrent/atomic-classes.md) +- [Giải thích chi tiết AQS](./java/concurrent/aqs.md) +- [Giải thích chi tiết CompletableFuture](./java/concurrent/completablefuture-intro.md) + +### JVM (bắt buộc xem :+1:) + +Phần JVM này chủ yếu tham khảo [JVM Specification - Java 8](https://docs.oracle.com/javase/specs/jvms/se8/html/index.html) và cuốn [《Hiểu sâu về Java Virtual Machine (phiên bản 3)》](https://book.douban.com/subject/34907497/) của thầy Chu Chí Minh (rất khuyến khích đọc nhiều lần!). + +- **[⭐ Tổng hợp câu hỏi phỏng vấn JVM thường gặp](./java/jvm/jvm-interview-questions.md)** +- **[Vùng nhớ Java](./java/jvm/memory-area.md)** +- **[Garbage collection trong JVM](./java/jvm/jvm-garbage-collection.md)** +- [Cấu trúc file class](./java/jvm/class-file-structure.md) +- **[Quá trình class loading](./java/jvm/class-loading-process.md)** +- [Class loader](./java/jvm/classloader.md) +- [【Chưa hoàn thành】Tổng hợp các JVM parameter quan trọng nhất (đã dịch xong một nửa)](./java/jvm/jvm-parameters-intro.md) +- [【Bổ sung】Hiểu JVM bằng ngôn ngữ đời thường](./java/jvm/jvm-intro.md) +- [Công cụ giám sát và xử lý sự cố của JDK](./java/jvm/jdk-monitoring-and-troubleshooting-tools.md) + +### Tính năng mới + +- **Java 8**: [Tổng hợp tính năng mới Java 8 (bản dịch)](./java/new-features/java8-tutorial-translate.md), [Tổng hợp tính năng mới thường dùng của Java 8](./java/new-features/java8-common-new-features.md) +- [Tổng quan tính năng mới Java 9](./java/new-features/java9.md) +- [Tổng quan tính năng mới Java 10](./java/new-features/java10.md) +- [Tổng quan tính năng mới Java 11](./java/new-features/java11.md) +- [Tổng quan tính năng mới Java 12 & 13](./java/new-features/java12-13.md) +- [Tổng quan tính năng mới Java 14 & 15](./java/new-features/java14-15.md) +- [Tổng quan tính năng mới Java 16](./java/new-features/java16.md) +- [Tổng quan tính năng mới Java 17](./java/new-features/java17.md) +- [Tổng quan tính năng mới Java 18](./java/new-features/java18.md) +- [Tổng quan tính năng mới Java 19](./java/new-features/java19.md) +- [Tổng quan tính năng mới Java 20](./java/new-features/java20.md) +- [Tổng quan tính năng mới Java 21](./java/new-features/java21.md) +- [Tổng quan tính năng mới Java 22 & 23](./java/new-features/java22-23.md) +- [Tổng quan tính năng mới Java 24](./java/new-features/java24.md) +- [Tổng quan tính năng mới Java 25](./java/new-features/java25.md) + +## Computer science cơ bản + +> Phần computer science cơ bản (computer network, operating system, data structure và algorithm) đã tách thành module riêng, xem tại [Tổng hợp kiến thức computer science cơ bản](./cs-basics/). + +[![Banner](https://oss.javaguide.cn/xingqiu/xingqiu.png)](./about-the-author/zhishixingqiu-two-years.md) + +## Database + +### Cơ bản + +- [Tổng hợp kiến thức cơ bản về database](./database/basis.md) +- [Tổng hợp kiến thức cơ bản về NoSQL](./database/nosql.md) +- [Giải thích chi tiết character set](./database/character-set.md) +- SQL: + - [Tổng hợp kiến thức cơ bản về cú pháp SQL](./database/sql/sql-syntax-summary.md) + - [Tổng hợp câu hỏi phỏng vấn SQL thường gặp](./database/sql/sql-questions-01.md) + +### MySQL + +**Tổng hợp điểm kiến thức/câu hỏi phỏng vấn:** + +- **[Tổng hợp điểm kiến thức & câu hỏi phỏng vấn MySQL thường gặp](./database/mysql/mysql-questions-01.md)** (bắt buộc xem :+1:) +- [Tổng hợp quy tắc tối ưu hiệu năng cao cho MySQL](./database/mysql/mysql-high-performance-optimization-specification-recommendations.md) + +**Điểm kiến thức quan trọng:** + +- [Giải thích chi tiết index trong MySQL](./database/mysql/mysql-index.md) +- [Tổng hợp các tình huống index MySQL mất tác dụng](./database/mysql/mysql-index-invalidation.md) +- [Giải thích chi tiết isolation level của transaction MySQL kèm hình ảnh](./database/mysql/transaction-isolation-level.md) +- [Giải thích chi tiết ba loại log của MySQL (binlog, redo log và undo log)](./database/mysql/mysql-logs.md) +- [Cách storage engine InnoDB hiện thực MVCC](./database/mysql/innodb-implementation-of-mvcc.md) +- [Quá trình thực thi câu lệnh SQL trong MySQL](./database/mysql/how-sql-executed-in-mysql.md) +- [Giải thích chi tiết query cache của MySQL](./database/mysql/mysql-query-cache.md) +- [Phân tích execution plan của MySQL](./database/mysql/mysql-query-execution-plan.md) +- [Primary key tự tăng của MySQL có chắc chắn liên tục không](./database/mysql/mysql-auto-increment-primary-key-continuous.md) +- [Gợi ý lưu trữ dữ liệu kiểu thời gian trong MySQL](./database/mysql/some-thoughts-on-database-storage-time.md) +- [Implicit conversion trong MySQL gây mất tác dụng index](./database/mysql/index-invalidation-caused-by-implicit-conversion.md) + +### Redis + +**Tổng hợp điểm kiến thức/câu hỏi phỏng vấn**: (bắt buộc xem :+1:) + +- [Tổng hợp điểm kiến thức & câu hỏi phỏng vấn Redis thường gặp (phần 1)](./database/redis/redis-questions-01.md) +- [Tổng hợp điểm kiến thức & câu hỏi phỏng vấn Redis thường gặp (phần 2)](./database/redis/redis-questions-02.md) + +**Điểm kiến thức quan trọng:** + +- [Giải thích chi tiết 3 chiến lược đọc/ghi cache thường dùng](./database/redis/3-commonly-used-cache-read-and-write-strategies.md) +- [Redis có làm được message queue không? Hiện thực thế nào?](./database/redis/redis-stream-mq.md) +- [Giải thích chi tiết 5 cấu trúc dữ liệu cơ bản của Redis](./database/redis/redis-data-structures-01.md) +- [Giải thích chi tiết 3 cấu trúc dữ liệu đặc biệt của Redis](./database/redis/redis-data-structures-02.md) +- [Giải thích chi tiết cơ chế persistence của Redis](./database/redis/redis-persistence.md) +- [Giải thích chi tiết memory fragmentation trong Redis](./database/redis/redis-memory-fragmentation.md) +- [Tổng hợp các nguyên nhân gây blocking thường gặp trong Redis](./database/redis/redis-common-blocking-problems-summary.md) +- [Giải thích chi tiết Redis cluster](./database/redis/redis-cluster.md) + +### MongoDB + +- [Tổng hợp điểm kiến thức & câu hỏi phỏng vấn MongoDB thường gặp (phần 1)](./database/mongodb/mongodb-questions-01.md) +- [Tổng hợp điểm kiến thức & câu hỏi phỏng vấn MongoDB thường gặp (phần 2)](./database/mongodb/mongodb-questions-02.md) + +## Search engine + +[Tổng hợp câu hỏi phỏng vấn Elasticsearch thường gặp (trả phí)](./database/elasticsearch/elasticsearch-questions-01.md) + +![Tài khoản công khai JavaGuide](https://oss.javaguide.cn/github/javaguide/gongzhonghaoxuanchuan.png) + +## Công cụ phát triển + +### Maven + +- [Tổng hợp khái niệm cốt lõi của Maven](./tools/maven/maven-core-concepts.md) +- [Best practice cho Maven](./tools/maven/maven-best-practices.md) + +### Gradle + +[Tổng hợp khái niệm cốt lõi của Gradle](./tools/gradle/gradle-core-concepts.md) (tùy chọn, hiện tại ở Trung Quốc Maven vẫn phổ biến hơn) + +### Docker + +- [Tổng hợp khái niệm cốt lõi của Docker](./tools/docker/docker-intro.md) +- [Docker thực chiến](./tools/docker/docker-in-action.md) + +### Git + +- [Tổng hợp khái niệm cốt lõi của Git](./tools/git/git-intro.md) +- [Tổng hợp mẹo hữu ích khi dùng GitHub](./tools/git/github-tips.md) + +## System design + +- [⭐ Tổng hợp câu hỏi phỏng vấn system design thường gặp](./system-design/system-design-questions.md) +- [⭐ Tổng hợp câu hỏi phỏng vấn design pattern thường gặp](https://interview.javaguide.cn/system-design/design-pattern.html) + +### Cơ bản + +- [Hướng dẫn ngắn gọn về RESTful API](./system-design/basis/RESTfulAPI.md) +- [Giáo trình ngắn gọn về software engineering](./system-design/basis/software-engineering.md) +- [Hướng dẫn đặt tên trong code](./system-design/basis/naming.md) +- [Hướng dẫn refactor code](./system-design/basis/refactoring.md) +- [Hướng dẫn unit test](./system-design/basis/unit-test.md) + +### Framework thường dùng + +#### Spring/SpringBoot (bắt buộc xem :+1:) + +**Tổng hợp điểm kiến thức/câu hỏi phỏng vấn**: + +- [Tổng hợp điểm kiến thức & câu hỏi phỏng vấn Spring thường gặp](./system-design/framework/spring/spring-knowledge-and-questions-summary.md) +- [Tổng hợp điểm kiến thức & câu hỏi phỏng vấn SpringBoot thường gặp](./system-design/framework/spring/springboot-knowledge-and-questions-summary.md) +- [Tổng hợp các annotation thường dùng của Spring/Spring Boot](./system-design/framework/spring/spring-common-annotations.md) +- [Hướng dẫn nhập môn SpringBoot](https://github.com/Snailclimb/springboot-guide) + +**Giải thích chi tiết các điểm kiến thức quan trọng**: + +- [Giải thích chi tiết IoC & AOP (hiểu nhanh)](./system-design/framework/spring/ioc-and-aop.md) +- [Giải thích chi tiết transaction trong Spring](./system-design/framework/spring/spring-transaction.md) +- [Giải thích chi tiết các design pattern trong Spring](./system-design/framework/spring/spring-design-patterns-summary.md) +- [Giải thích chi tiết nguyên lý auto configuration của SpringBoot](./system-design/framework/spring/spring-boot-auto-assembly-principles.md) + +#### MyBatis + +[Tổng hợp câu hỏi phỏng vấn MyBatis thường gặp](./system-design/framework/mybatis/mybatis-interview.md) + +### Security + +#### Authentication & authorization + +- [Giải thích chi tiết khái niệm cơ bản về authentication và authorization](./system-design/security/basis-of-authority-certification.md) +- [Giải thích chi tiết khái niệm cơ bản về JWT](./system-design/security/jwt-intro.md) +- [Phân tích ưu/nhược điểm của JWT và giải pháp cho các vấn đề thường gặp](./system-design/security/advantages-and-disadvantages-of-jwt.md) +- [Giải thích chi tiết SSO (Single Sign-On)](./system-design/security/sso-intro.md) +- [Giải thích chi tiết thiết kế hệ thống phân quyền](./system-design/security/design-of-authority-system.md) + +#### An toàn dữ liệu + +- [Tổng hợp các thuật toán mã hóa thường gặp](./system-design/security/encryption-algorithms.md) +- [Tổng hợp giải pháp lọc từ nhạy cảm](./system-design/security/sentive-words-filter.md) +- [Tổng hợp giải pháp che giấu dữ liệu (data desensitization)](./system-design/security/data-desensitization.md) +- [Tại sao cả frontend và backend đều phải validate dữ liệu](./system-design/security/data-validation.md) +- [Tại sao khi quên mật khẩu chỉ có thể reset chứ không cho biết mật khẩu cũ?](./system-design/security/why-password-reset-instead-of-retrieval.md) + +### Scheduled task + +[Giải thích chi tiết scheduled task trong Java](./system-design/schedule-task.md) + +### Đẩy tin nhắn real-time trên Web + +[Giải thích chi tiết việc đẩy tin nhắn real-time trên Web](./system-design/web-real-time-message-push.md) + +## Distributed + +- [⭐ Câu hỏi phỏng vấn distributed tần suất cao](https://interview.javaguide.cn/distributed-system/distributed-system.html) +- [Chuẩn bị phỏng vấn distributed system](./distributed-system/distributed-system-interview-questions.md) +- [Chuẩn bị phỏng vấn microservice](./distributed-system/microservices-interview-questions.md) +- [Nhập môn distributed system](./distributed-system/distributed-system-intro.md) + +### Lý thuyết & thuật toán & giao thức + +- [Phân tích lý thuyết CAP và lý thuyết BASE](./distributed-system/protocol/cap-and-base-theorem.md) +- [Giải thích chi tiết về distributed coordination](./distributed-system/protocol/centralized-and-decentralized.md) +- [Giải thích chi tiết bài toán Byzantine Generals](./distributed-system/protocol/byzantine-generals-problem.md) +- [Phân tích thuật toán Paxos](./distributed-system/protocol/paxos-algorithm.md) +- [Phân tích thuật toán Raft](./distributed-system/protocol/raft-algorithm.md) +- [Phân tích giao thức ZAB](./distributed-system/protocol/zab.md) +- [Giải thích chi tiết giao thức Gossip](./distributed-system/protocol/gossip-protocol.md) +- [Giải thích chi tiết thuật toán consistent hashing](./distributed-system/protocol/consistent-hashing.md) + +### RPC + +- [Tổng hợp kiến thức cơ bản về RPC](./distributed-system/rpc/rpc-intro.md) +- [Tổng hợp điểm kiến thức & câu hỏi phỏng vấn Dubbo thường gặp](./distributed-system/rpc/dubbo.md) + +### ZooKeeper + +> Hai bài này có thể trùng lặp nội dung một phần, khuyến khích đọc cả hai. + +- [Tổng hợp khái niệm liên quan đến ZooKeeper (nhập môn)](./distributed-system/distributed-process-coordination/zookeeper/zookeeper-intro.md) +- [Tổng hợp khái niệm liên quan đến ZooKeeper (nâng cao)](./distributed-system/distributed-process-coordination/zookeeper/zookeeper-plus.md) + +### API Gateway + +- [Tổng hợp kiến thức cơ bản về API gateway](./distributed-system/api-gateway.md) +- [Tổng hợp điểm kiến thức & câu hỏi phỏng vấn Spring Cloud Gateway thường gặp](./distributed-system/spring-cloud-gateway-questions.md) + +### Distributed ID + +- [Tổng hợp điểm kiến thức & câu hỏi phỏng vấn distributed ID thường gặp](./distributed-system/distributed-id.md) +- [Hướng dẫn thiết kế distributed ID](./distributed-system/distributed-id-design.md) + +### Distributed lock + +- [Giới thiệu distributed lock](https://javaguide.cn/distributed-system/distributed-lock.html) +- [Tổng hợp các giải pháp hiện thực distributed lock thường gặp](https://javaguide.cn/distributed-system/distributed-lock-implementations.html) + +### Distributed transaction + +[Tổng hợp điểm kiến thức & câu hỏi phỏng vấn distributed transaction thường gặp](./distributed-system/distributed-transaction.md) + +### Distributed configuration center + +[Tổng hợp điểm kiến thức & câu hỏi phỏng vấn distributed configuration center thường gặp](./distributed-system/distributed-configuration-center.md) + +## High performance + +### Tối ưu database + +- [Read/write splitting và sharding trong database](./high-performance/read-and-write-separation-and-library-subtable.md) +- [Tách dữ liệu nóng/lạnh](./high-performance/data-cold-hot-separation.md) +- [Tổng hợp các cách tối ưu SQL thường gặp](./high-performance/sql-optimization.md) +- [Giới thiệu deep pagination và gợi ý tối ưu](./high-performance/deep-pagination-optimization.md) + +### Load balancing + +[Tổng hợp điểm kiến thức & câu hỏi phỏng vấn load balancing thường gặp](./high-performance/load-balancing.md) + +### CDN + +[Tổng hợp điểm kiến thức & câu hỏi phỏng vấn CDN (Content Delivery Network) thường gặp](./high-performance/cdn.md) + +### Message queue + +- [Chuẩn bị phỏng vấn message queue](./high-performance/message-queue/message-queue-interview-questions.md) +- [Tổng hợp kiến thức cơ bản về message queue](./high-performance/message-queue/message-queue.md) +- [Tổng hợp điểm kiến thức & câu hỏi phỏng vấn Disruptor thường gặp](./high-performance/message-queue/disruptor-questions.md) +- [Tổng hợp điểm kiến thức & câu hỏi phỏng vấn RabbitMQ thường gặp](./high-performance/message-queue/rabbitmq-questions.md) +- [Tổng hợp điểm kiến thức & câu hỏi phỏng vấn RocketMQ thường gặp](./high-performance/message-queue/rocketmq-questions.md) +- [Tổng hợp điểm kiến thức & câu hỏi phỏng vấn Kafka thường gặp](./high-performance/message-queue/kafka-questions-01.md) + +## High availability + +[Hướng dẫn thiết kế hệ thống high availability](./high-availability/high-availability-system-design.md) + +### Thiết kế dự phòng + +[Giải thích chi tiết thiết kế dự phòng (redundancy)](./high-availability/redundancy.md) + +### Rate limiting + +[Giải thích chi tiết rate limiting cho service](./high-availability/limit-request.md) + +### Degradation & circuit breaker + +[Giải thích chi tiết degradation & circuit breaker](./high-availability/fallback-and-circuit-breaker.md) + +### Timeout & retry + +[Giải thích chi tiết timeout & retry](./high-availability/timeout-and-retry.md) + +### Cluster + +Triển khai nhiều bản của cùng một service, tránh single point of failure. + +### Thiết kế disaster recovery và multi-active đa vùng + +**Disaster recovery** = chống thảm họa + backup. + +- **Backup**: sao lưu thêm nhiều bản cho toàn bộ dữ liệu quan trọng mà hệ thống sinh ra. +- **Chống thảm họa**: xây dựng hai hệ thống hoàn toàn giống nhau ở hai vùng địa lý khác nhau. Khi hệ thống ở một nơi đột ngột chết, toàn bộ ứng dụng có thể chuyển sang nơi còn lại, nhờ đó hệ thống vẫn cung cấp dịch vụ bình thường. + +**Multi-active đa vùng** mô tả việc triển khai service ở nhiều vùng địa lý và tất cả đều đồng thời cung cấp dịch vụ ra bên ngoài. Khác biệt chính so với thiết kế disaster recovery truyền thống nằm ở chữ "multi-active", tức mọi site đều đang phục vụ cùng lúc. Multi-active đa vùng nhằm ứng phó với tình huống bất ngờ như hỏa hoạn, động đất và các thảm họa tự nhiên hoặc do con người gây ra. + +## Xu hướng Star + +![Stars](https://api.star-history.com/svg?repos=Snailclimb/JavaGuide&type=Date) + +## Tài khoản công khai + +Nếu mọi người muốn theo dõi real-time các bài viết tôi cập nhật và nội dung giá trị tôi chia sẻ, có thể theo dõi tài khoản công khai "**JavaGuide**" của tôi. + +![Tài khoản công khai JavaGuide](https://oss.javaguide.cn/github/javaguide/gongzhonghaoxuanchuan.png) diff --git a/vi/roadmap/README.md b/vi/roadmap/README.md new file mode 100644 index 00000000000..8c5143f48bf --- /dev/null +++ b/vi/roadmap/README.md @@ -0,0 +1,30 @@ +--- +title: "Tổng hợp lộ trình học (bản mới nhất 2026): Java backend, AI Agent, AI application development, full-stack và test development" +description: Tổng hợp lộ trình học bản mới nhất 2026 của JavaGuide, sắp xếp gợi ý học tập có hệ thống cho các hướng Java backend, AI application development, AI Agent, AI coding, full-stack development và test development. +category: Lộ trình học +head: + - - meta + - name: keywords + content: lộ trình học,lộ trình học 2026,lộ trình học Java,lộ trình học Java backend,lộ trình học AI,lộ trình học Agent,lộ trình học full-stack,lộ trình học test development,lộ trình test development,AI application development,AI coding +--- + +Đây là nơi tổng hợp thống nhất các nội dung trong JavaGuide phù hợp để học theo lộ trình, giúp bạn chọn được lộ trình học bản mới nhất 2026 theo mục tiêu của mình, thay vì phải nhảy qua nhảy lại giữa hàng loạt bài viết. + +Nếu bạn đang chuẩn bị phỏng vấn backend, hãy ưu tiên xem lộ trình Java backend; nếu bạn đã có nền tảng backend Java/Go và muốn bước vào hướng AI application development hoặc Agent, có thể bắt đầu từ lộ trình AI application development; nếu bạn muốn nâng cao năng lực làm việc độc lập, hãy xem gợi ý học full-stack; nếu bạn muốn chuẩn bị cho hướng test development, automation testing hoặc AI testing, hãy xem lộ trình test development. + +## Hướng backend + +- [Lộ trình học Java backend (bản mới nhất 2026)](./java-roadmap.md): từ Java Basics, Collection, Concurrency, JVM, Spring, database, distributed cho đến thực chiến project, phù hợp để học Java backend có hệ thống và chuẩn bị tìm việc. + +## Hướng AI + +- [Lộ trình học AI application development và Agent cho developer Java/Go (bản mới nhất 2026)](./java-to-ai-roadmap.md): dành cho backend developer, chia nhỏ lộ trình học theo nền tảng LLM, LLM API, Prompt, RAG, Agent, kỹ thuật hóa và thực chiến project. +- [Gợi ý học chuyển hướng từ backend sang AI Agent (bản mới nhất 2026)](./backend-to-ai-agent-roadmap.md): trước hết xác định bạn có phù hợp để chuyển hướng không, rồi xem nên chọn Java AI hay Python AI, có thể ứng tuyển vị trí nào và nên học như thế nào. + +## Hướng full-stack + +- [Lộ trình học full-stack cho backend developer (bản mới nhất 2026)](./full-stack-roadmap.md): kết hợp công cụ AI coding, nói rõ backend developer làm sao bù đắp năng lực frontend, từng bước có thể tự làm trọn vẹn một tính năng hoàn chỉnh. + +## Hướng test development + +- [Lộ trình học test development (bản mới nhất 2026)](./test-development-roadmap.md): dành cho hướng test development, automation testing và quality engineering, bao phủ lý thuyết testing, automation cho API/UI/performance, CI/CD, nền tảng testing, testing có AI hỗ trợ và đánh giá ứng dụng AI. diff --git a/vi/roadmap/java-roadmap.md b/vi/roadmap/java-roadmap.md new file mode 100644 index 00000000000..e2a32d05807 --- /dev/null +++ b/vi/roadmap/java-roadmap.md @@ -0,0 +1,1002 @@ +--- +title: Lộ trình học Java backend (bản mới nhất 2026) +description: Lộ trình học Java backend bản mới nhất 2026, bao phủ Java Basics, database, framework, công cụ, JVM, concurrency, distributed, high concurrency, high availability, microservice, AI application development và thực chiến project, phù hợp để học Java backend có hệ thống và chuẩn bị tìm việc. +category: Lộ trình học +head: + - - meta + - name: keywords + content: lộ trình học Java,lộ trình học Java backend,lộ trình học Java 2026,Java backend,phỏng vấn Java,Spring Boot,MySQL,Redis,JVM,Java concurrency,distributed,microservice,AI application development +--- + +Đây là bản mới nhất 2026 của lộ trình học Java, mỗi năm đều được tối ưu và cải tiến toàn diện theo yêu cầu tuyển dụng Java backend mới nhất. + +Bài viết này có thể là lộ trình học Java backend tâm huyết và đầy đủ nhất bạn từng thấy, tổng cộng hơn 4w chữ. Tuy nhiên đừng lo nội dung quá nhiều không học nổi, tôi sẽ chia theo độ khó: phần bắt buộc phải học để tìm được việc ở công ty nhỏ, và lộ trình nâng dần năng lực Java backend một cách tuần tự. + +Với người mới, bạn có thể học có hệ thống theo lộ trình và tài liệu mà bài viết này gợi ý; với developer đã có kinh nghiệm, bạn có thể dựa vào bài viết này để đào sâu hơn về Java backend, nâng cao năng lực cạnh tranh cá nhân. + +Để nội dung không quá tạp, bài viết này sẽ không nói về phương pháp học và lời khuyên phát triển bản thân, phần đó bạn có thể xem vài bài trong mục "Đời lập trình viên" của JavaGuide: + +- [Lập trình viên học công nghệ mới nhanh như thế nào](https://javaguide.cn/high-quality-technical-articles/advanced-programmer/programmer-quickly-learn-new-technology.html) +- [Chiến lược phát triển kỹ thuật của lập trình viên](https://javaguide.cn/high-quality-technical-articles/advanced-programmer/the-growth-strategy-of-the-technological-giant.html) +- [Bảy lời khuyên cho bạn muốn trở thành developer cấp cao](https://javaguide.cn/high-quality-technical-articles/advanced-programmer/seven-tips-for-becoming-an-advanced-programmer.html) + +Bài viết này cũng không đề cập đến phần computer science cơ bản; về kiến thức nền tảng bạn có thể tham khảo phần chia sẻ trên website của tôi: [Gợi ý sách computer science cơ bản](https://javaguide.cn/books/cs-basics.html). + +Nói thêm một câu: với người mới bắt đầu lập trình, tôi không khuyến khích lao vào học qua việc làm project ngay. Thực hành đúng là rất quan trọng, nhưng nếu bạn chưa có nền tảng lập trình mà nhảy thẳng vào thực chiến thì rất dễ học thành "nửa nạc nửa mỡ". Khuyên bạn trong giai đoạn đầu học lập trình nên xem nhiều video chất lượng. Đi theo video từng bước sẽ giúp bạn tránh được rất nhiều cạm bẫy, và sự tự tin khi học lập trình cũng tăng lên. + +## Tổng quan lộ trình học Java backend + +Tôi đã vẽ một sơ đồ, trước hết cùng xem qua toàn cảnh lộ trình học Java backend và thứ tự học mà tôi khuyến nghị. + +Mỗi điểm kiến thức trong sơ đồ bên dưới đều sẽ được giới thiệu chi tiết ở phần sau (kèm gợi ý tài liệu học). + +![Tổng quan lộ trình học Java backend](https://oss.javaguide.cn/github/javaguide/interview-preparation/java-learning-route/java-learning-route-2024.png) + +Bản gốc + bản PDF của ảnh trên có thể lấy bằng cách nhắn "**学习路线**" (lộ trình học) cho tài khoản công khai **「JavaGuide」**. + +![Tài khoản công khai chính thức JavaGuide](https://oss.javaguide.cn/github/javaguide/gongzhonghaoxuanchuan.png) + +**Nội dung hơi nhiều? Thấy nản?** Nếu bạn chỉ muốn tìm một công việc developer ở công ty nhỏ, hãy tập trung vào Java Basics, database, các framework thường dùng và các công cụ thường dùng. + +Còn những điểm kiến thức như JVM, distributed, high concurrency, high availability, microservice, nếu bạn muốn vào công ty lớn hoặc muốn cạnh tranh tốt hơn khi tìm việc thì cũng cần bỏ thêm thời gian học. + +Phỏng vấn bây giờ rất cạnh tranh, muốn tìm được công việc tốt thì bạn cần học nhiều hơn, luyện nhiều hơn. Tuy nhiều kiến thức bạn học hiện tại có thể sẽ không dùng đến khi đi làm, nhưng vòng sàng lọc phỏng vấn lại đòi hỏi bạn phải biết. Suy cho cùng, nhiều vị trí có rất nhiều người cùng cạnh tranh, để đạt hiệu quả sàng lọc thì độ khó phỏng vấn thường khá cao. Đây chính là cái gọi là: "phỏng vấn thì chế tên lửa, vào làm thì vặn ốc vít". + +## Những công nghệ Java đã bị đào thải + +Bài viết [Những công nghệ Java đã bị đào thải, đừng học nữa!](https://javaguide.cn/about-the-author/deprecated-java-technologies.html) đề cập đến các công nghệ đã bị đào thải trong lĩnh vực Java, nhất định nhất định nhất định đừng học nữa! Ai khuyên bạn học những công nghệ dưới đây thì cứ tát cho hai cái. + +**JSP** + +- **Lý do**: JSP đã lỗi thời, không đáp ứng được nhu cầu Web development hiện đại; tách frontend-backend đã trở thành xu hướng chính. +- **Giải pháp thay thế**: template engine (như Thymeleaf, Freemarker) phổ biến hơn trong full-stack truyền thống; còn trong kiến trúc tách frontend-backend, các framework frontend hiện đại như React, Vue, Angular đã thay thế vai trò của JSP. +- **Lưu ý**: một số project cũ của doanh nghiệp nhà nước có thể vẫn dùng JSP, nhưng trường hợp này ngày càng hiếm. + +**Struts (đặc biệt là 1.x)** + +- **Lý do**: cấu hình rườm rà, hiệu suất phát triển thấp, và tồn tại lỗ hổng bảo mật nghiêm trọng (như lỗ hổng Apache Struts 2 nổi tiếng thế giới). Ngoài ra, cộng đồng duy trì không đủ, hệ sinh thái dần teo tóp. +- **Giải pháp thay thế**: Spring MVC và Spring WebFlux mang lại trải nghiệm phát triển gọn gàng hơn, tính năng mạnh hơn và hỗ trợ cộng đồng hoàn thiện, đã thay thế hoàn toàn Struts. + +**EJB (Enterprise JavaBeans)** + +- **Lý do**: EJB quá phức tạp, chi phí phát triển cao, đường cong học tập dốc, dần bị thay thế bởi các framework nhẹ hơn trong project thực tế. +- **Giải pháp thay thế**: Spring/Spring Boot cung cấp giải pháp phát triển cấp doanh nghiệp gọn gàng và mạnh mẽ hơn, gần như đã trở thành tiêu chuẩn thực tế của Java enterprise development. Ngoài ra, các framework như Solon (nội địa Trung Quốc) và Quarkus (thân thiện với cloud native) cũng rất tốt. + +**Java Applets** + +- **Lý do**: các trình duyệt hiện đại (Chrome, Firefox, Edge) đã gỡ bỏ hoàn toàn hỗ trợ Java Applets từ lâu, đồng thời Applets tồn tại vấn đề bảo mật nghiêm trọng. +- **Giải pháp thay thế**: HTML5, WebAssembly cùng các framework JavaScript hiện đại (như React, Vue) có thể mang lại trải nghiệm tương tác an toàn và hiệu quả hơn, không cần plugin. + +**SOAP / JAX-WS** + +- **Lý do**: SOAP và JAX-WS quá phức tạp, định dạng dữ liệu dài dòng (XML), không thân thiện với hiệu suất phát triển và performance. +- **Giải pháp thay thế**: RESTful API và RPC nhẹ hơn, hiệu quả hơn, là lựa chọn hàng đầu của kiến trúc microservice hiện đại. + +**RMI (Remote Method Invocation)** + +- **Lý do**: RMI là công nghệ gọi từ xa đời đầu của Java, nhưng tính tương thích kém, cấu hình rườm rà và performance khá tệ. +- **Giải pháp thay thế**: RESTful API và RPC cung cấp giải pháp gọi từ xa đơn giản, hiệu quả hơn, đã thay thế hoàn toàn RMI. + +**Swing / JavaFX** + +- **Lý do**: ứng dụng desktop giảm mạnh thị phần trong lĩnh vực phát triển, Web và mobile trở thành xu hướng chính. Hệ sinh thái của Swing và JavaFX không phong phú bằng các framework đa nền tảng hiện đại. +- **Giải pháp thay thế**: các framework desktop đa nền tảng (như Flutter Desktop, Electron) mang lại trải nghiệm hiện đại hơn. +- **Lưu ý**: một số project cũ của doanh nghiệp nhà nước có thể vẫn dùng Swing / JavaFX, nhưng trường hợp này ngày càng hiếm. + +**Ant** + +- **Lý do**: Ant là công cụ build dựa trên cấu hình XML, thiếu tính dễ dùng, cấu hình rườm rà. +- **Giải pháp thay thế**: Maven và Gradle cung cấp khả năng quản lý dependency và build project hiệu quả hơn, trở thành lựa chọn hàng đầu trong các công cụ build hiện đại. + +## Tự kiểm tra bằng câu hỏi phỏng vấn + +Học trên giấy rốt cuộc vẫn nông cạn, muốn hiểu thấu phải tự tay làm. Để giúp bạn nội hóa kiến thức tốt hơn, tôi đã chuẩn bị riêng một bộ câu hỏi phỏng vấn tần suất cao khớp hoàn toàn với lộ trình học này: [Bộ câu hỏi phỏng vấn tần suất cao đi kèm lộ trình học Java backend](https://t.zsxq.com/0eM78gbAr) (dành riêng cho [JavaGuide Knowledge Planet](https://javaguide.cn/about-the-author/zhishixingqiu-two-years.html)). + +**Tài nguyên này giúp bạn:** + +- **Tự kiểm tra:** kiểm chứng có hệ thống mức độ nắm vững từng điểm kiến thức. +- **Tìm và bù lỗ hổng:** kịp thời phát hiện điểm yếu của mình để củng cố có trọng tâm. +- **Phỏng vấn mô phỏng:** làm quen trước với nhịp độ phỏng vấn và các điểm thi tần suất cao. + +Rất khuyến khích mọi người đẩy việc học lên mức sâu hơn bằng cách tự kiểm tra. + +## Java core + +### Java Basics + +Nếu trước đây bạn chưa từng học lập trình, tôi khuyên bạn nên xem video hướng dẫn. Ví dụ [《Series giáo trình Java Basics》](https://www.bilibili.com/video/BV1PY411e7J6/) của Shang Silicon Valley và [《Học Java trong 30 ngày từ con số không》](https://www.bilibili.com/video/BV1fh411y7R8) của thầy Hàn Thuận Bình đều rất tốt. + +![](https://oss.javaguide.cn/github/javaguide/interview-preparation/java-learning-route/20210409143842888.png) + +👉 Tôi đã tổng hợp trọn bộ video hướng dẫn & tài liệu series học Java backend mới nhất của Shang Silicon Valley, bạn nào thích xem video có thể tải tại link này: [【Tổng hợp mới nhất】Trọn bộ giáo trình Java backend & project thực chiến của Shang Silicon Valley](https://mp.weixin.qq.com/s/jkZthmOSDgTF1PrCeNus_A) (khuyến nghị). + +![](https://oss.javaguide.cn/github/javaguide/books/88714e9becd0485aae247772b6ed9949.png) + +Vừa xem video vừa kèm một cuốn sách hay cũng rất có tác dụng. + +Sách Java Basics chất lượng rất nhiều, ở đây tôi chỉ giới thiệu 3 cuốn. + +**1. 《Head First Java》** + +![《Head First Java》 - Douban](https://oss.javaguide.cn/github/javaguide/books/image-20220424103035793.png) + +Nội dung cuốn 《Head First Java》 rất nhẹ nhàng thú vị, có thể nói là một trong những cuốn sách tôi thích nhất giai đoạn đầu học lập trình. Đồng thời đây cũng là cuốn sách khai tâm Java của tôi. Giai đoạn đầu học Java, nhờ cuốn sách này mà tôi mới bước được vào cánh cửa ngôn ngữ Java. Việc tôi trụ được với Java có công lớn của cuốn sách này. Rất nhiều bạn bè xung quanh tôi giai đoạn đầu học Java cũng đọc cuốn này. + +Nhiều bạn sẽ hỏi: **cuốn này có phù hợp cho người mới học lập trình không?** + +Cá nhân tôi thấy cuốn này khá phù hợp cho người mới học lập trình, dù sao cũng thuộc series "Head First". + +**2. 《Java Core Technology tập 1 + tập 2》** + +![《Java Core Technology tập 1》 - Douban](https://oss.javaguide.cn/github/javaguide/books/image-20220424101217849.png) + +Hai cuốn 《Java Core Technology tập 1 + tập 2》 nội dung rất nhiều, đọc hết thì khá tốn thời gian, phù hợp làm sách tra cứu khi làm việc. Hồi đại học tôi mua hai cuốn để trong ký túc xá, rảnh thì giở ra xem. Cá nhân khuyên nên có chút nền tảng Java rồi hãy đọc hai cuốn này, chúng giới thiệu khá sâu và toàn diện. + +**3. 《Logic của lập trình Java》** + +《Logic của lập trình Java》 là một cuốn sách hay nhưng khá kín tiếng, so với sách nhập môn thì nội dung có chiều sâu hơn. Phù hợp cho người mới, đồng thời cũng phù hợp để mọi người ôn lại kiến thức Java Basics. Bài viết này có gợi ý cách đọc cuốn sách đó: [Chiêu trò lý thuyết phỏng vấn về Java Basics](https://mp.weixin.qq.com/s/UceEYGWM9qq9WvntV7y-Aw). + +![《Logic của lập trình Java》](https://oss.javaguide.cn/github/javaguide/books/image-20230721153650488.png) + +Sau khi học xong Java Basics, bạn có thể dùng những gì đã học để viết một chương trình Java đơn giản, hoặc thử dùng Java giải một số bài toán lập trình, qua đó đưa kiến thức đã học vào thực hành. + +Không khuyến khích củng cố kiến thức bằng cách làm game sau khi học Java Basics. Tại sao các trung tâm đào tạo lại thích cách này? Nói thẳng ra là để đánh trúng điểm hưng phấn của bạn. Người mới học xong Java Basics mà làm game thì thường không thực tế lắm, chi bằng tìm vài bài toán lập trình đơn giản để giải, ví dụ các bài algorithm đơn giản. + +Nhớ tổng kết nhiều! Xây nền cho chắc! Ghi lại những thứ quan trọng với mình. Để tài liệu API ở nơi mình nhìn thấy được để có thể tra cứu bất cứ lúc nào. Để viết được code tốt hơn, hai cuốn 《Effective Java》 và 《Refactoring》 rảnh cũng nên xem. + +Sau khi học xong phần này, nhất định phải đảm bảo bạn nắm được các điểm kiến thức sau: + +- Cú pháp cơ bản, kiểu dữ liệu cơ bản +- Object, class, interface +- Inheritance, generic +- Method +- Exception, assertion +- Collection +- …… + +Trong quá trình học, rất khuyến khích kết hợp với phần tổng hợp các câu hỏi thường gặp và điểm kiến thức quan trọng của tôi (tiện thể còn chuẩn bị luôn được các câu hỏi phỏng vấn thường gặp): + +- **Java Basics**: + + - [Tổng hợp câu hỏi phỏng vấn Java Basics thường gặp (phần 1)](https://javaguide.cn/java/basis/java-basic-questions-01.html) (khái niệm cơ bản của ngôn ngữ Java, cú pháp, kiểu dữ liệu, biến, method, v.v.) + + - [Tổng hợp câu hỏi phỏng vấn Java Basics thường gặp (phần 2)](https://javaguide.cn/java/basis/java-basic-questions-02.html) (nền tảng OOP, string, so sánh và copy object, v.v.) + + - [Tổng hợp câu hỏi phỏng vấn Java Basics thường gặp (phần 3)](https://javaguide.cn/java/basis/java-basic-questions-03.html) (exception, generic, reflection, SPI, serialization, annotation, v.v.) + +- **Java Collection**: + + - [Tổng hợp câu hỏi phỏng vấn Java Collection thường gặp (phần 1)](https://javaguide.cn/java/collection/java-collection-questions-01.html) (nền tảng Java Collection, `ArrayList`, `LinkedList`, `HashSet`, `ArrayDeque`, `PriorityQueue`, `BlockingQueue`, v.v.) + - [Tổng hợp câu hỏi phỏng vấn Java Collection thường gặp (phần 2)](https://javaguide.cn/java/collection/java-collection-questions-02.html) (`HashMap`, `ConcurrentHashMap`, v.v.) + +### Java Concurrency (nâng cao) + +Phần concurrency hay nói cách khác là multithreading sẽ hơi khó hiểu và khó thực hành một chút. Nếu bạn vừa học xong Java Basics, tôi khuyên khi học phần concurrency bạn có thể tìm hiểu sơ qua kiến thức cơ bản trước, ví dụ so sánh thread và process. Về sau, khi bạn hiểu Java sâu hơn rồi hãy quay lại xem kỹ phần này. + +Về sách Java concurrency, có khá nhiều cuốn viết tốt, ví dụ 《Thực chiến thiết kế chương trình Java high concurrency》, 《Cái đẹp của lập trình concurrent Java》, 《Nguyên lý hiện thực concurrency trong Java: mổ xẻ source code JDK》. + +![《Thực chiến thiết kế chương trình Java high concurrency》 - Douban](https://oss.javaguide.cn/github/javaguide/books/image-20220424112554830.png) + +![《Cái đẹp của lập trình concurrent Java》 - Douban](https://oss.javaguide.cn/github/javaguide/books/image-20220424112413660.png) + +![《Nguyên lý hiện thực concurrency trong Java: mổ xẻ source code JDK》 - Douban](https://oss.javaguide.cn/github/javaguide/books/0b1b046af81f4c94a03e292e66dd6f7d.png) + +Muốn học có hệ thống thì vẫn nên chọn ra một cuốn trong số đó để đọc nghiêm túc. Tất nhiên, bạn cũng có thể chọn nhiều cuốn kết hợp đọc cùng nhau, gặp điểm kiến thức không hiểu thì xem giải thích ở sách khác hoặc tìm bài blog tương ứng. + +Về video, vẫn khuyến nghị bài giảng của thầy Chu Dương ở Shang Silicon Valley: [Video giáo trình lập trình concurrent Java](https://www.bilibili.com/video/BV1ar4y1x727/). + +👉 Tôi đã tổng hợp trọn bộ video hướng dẫn & tài liệu series học Java backend mới nhất của Shang Silicon Valley, bạn nào thích xem video có thể tải tại link này: [【Tổng hợp mới nhất】Trọn bộ giáo trình Java backend & project thực chiến của Shang Silicon Valley](https://mp.weixin.qq.com/s/jkZthmOSDgTF1PrCeNus_A) (khuyến nghị). + +![](https://oss.javaguide.cn/github/javaguide/books/88714e9becd0485aae247772b6ed9949.png) + +Trong quá trình học, rất khuyến khích kết hợp với phần tổng hợp các câu hỏi thường gặp và điểm kiến thức quan trọng của tôi: + +- [Tổng hợp câu hỏi phỏng vấn Java Concurrency thường gặp (phần 1)](https://javaguide.cn/java/concurrent/java-concurrent-questions-01.html) (kiến thức multithreading cơ bản, ví dụ khái niệm thread và process, deadlock) +- [Tổng hợp câu hỏi phỏng vấn Java Concurrency thường gặp (phần 2)](https://javaguide.cn/java/concurrent/java-concurrent-questions-02.html) (các loại lock, ví dụ optimistic lock và pessimistic lock, keyword `synchronized`, `ReentrantLock`) +- [Tổng hợp câu hỏi phỏng vấn Java Concurrency thường gặp (phần 3)](https://javaguide.cn/java/concurrent/java-concurrent-questions-03.html) (`ThreadLocal`, thread pool, `Future`, AQS, virtual thread, v.v.) + +### JVM (nâng cao) + +JVM thuộc nội dung cao cấp hơn concurrency một bậc, thứ tự học có thể lùi lại một chút, ví dụ bạn có thể quay lại xem JVM sau khi học xong phần framework. Ngoài ra, các điểm kiến thức liên quan đến JVM thường chỉ được hỏi khi phỏng vấn công ty lớn (ví dụ Meituan, Alibaba) và một số công ty tầm trung tốt (ví dụ Ctrip, SF Express, CMB Network); phỏng vấn doanh nghiệp nhà nước, công ty tầm trung yếu hơn và công ty nhỏ thì không cần chuẩn bị. + +Tuy nhiên, cá nhân tôi khuyên nếu bạn còn dư sức thì vẫn nên dành thời gian học, vẫn có ích. Đúng như câu nói, chỉ khi hiểu rõ JVM bạn mới có khả năng thực sự "thấm" được ngôn ngữ Java. + +Trong công việc thực tế, công ty vừa và nhỏ thường không làm JVM tuning, nhưng lỡ gặp vấn đề kiểu OOM, nếu bạn biết cách truy vết và xử lý thì chẳng phải tốt hơn sao? + +Học phần JVM này nhất định phải chú ý kết hợp thực chiến với lý thuyết. + +Về sách, 《Hiểu sâu về Java Virtual Machine》 là cuốn đầu tiên cần giới thiệu. + +![《Hiểu sâu về Java Virtual Machine》 - Douban](https://oss.javaguide.cn/github/javaguide/books/20210710104655705.png) + +Cuốn sách này chỉ cần một câu để mô tả: **hàng khủng trong các sách nội địa, xuất sắc thực sự!** (thực lòng mong trong nước có thêm nhiều sách chất lượng như vậy! Cố lên! 💪) + +Phiên bản thứ ba của cuốn này đã ra khá lâu, bổ sung nhiều nội dung hay ví dụ như phân tích nguyên lý của thế hệ GC mới như ZGC. + +Dù bạn đi phỏng vấn hay muốn học sâu hơn trong lĩnh vực Java, bạn đều không thể thiếu cuốn sách này. Cuốn này không chỉ cần đọc mà còn nên đọc nhiều lần, bên trong toàn nội dung giá trị. Trong sách còn có một số thứ cần tự thực hành, tôi khuyên bạn cũng nên thực hành theo. + +Các cuốn tương tự còn có 《Thực chiến Java Virtual Machine》, 《Thiết kế và hiện thực virtual machine: lấy JVM làm ví dụ》, cả hai đều rất tốt! + +![《Thực chiến Java Virtual Machine》 - Douban](https://oss.javaguide.cn/github/javaguide/books/image-20220424113158144.png) + +![《Thiết kế và hiện thực virtual machine: lấy JVM làm ví dụ》 - Douban](https://oss.javaguide.cn/github/javaguide/books/image-20220424113210153.png) + +Nếu bạn hứng thú với thực chiến, muốn tự tay viết một JVM đơn giản, có thể xem cuốn 《Tự tay viết Java Virtual Machine》. + +![《Tự tay viết Java Virtual Machine》 - Douban](https://oss.javaguide.cn/github/javaguide/books/image-20220424113445246.png) + +Code trong sách được hiện thực bằng ngôn ngữ Go; sau khi hiểu nguyên lý, bạn có thể dùng Java mô phỏng viết lại một bản, cũng coi như luyện tay! Nếu hiện tại bạn chưa đủ khả năng tự mình dùng Java viết lại, bạn cũng có thể tìm trên mạng rất nhiều bản hiện thực bằng Java, ví dụ [《Series tự viết JVM của zachaxy》](https://zachaxy.github.io/tags/JVM/). + +Ngoài ra, bài viết [《Học hiện thực JVM từ ngoài vào trong》](https://www.douban.com/doulist/2545443/) mà R đại đăng trên Douban cũng giới thiệu rất nhiều sách hay về JVM, khuyến khích mọi người xem thử. + +Về video, [《Trọn bộ giáo trình JVM》](https://www.bilibili.com/video/BV1PJ411n7xZ) do thầy Tống Hồng Khang của Shang Silicon Valley giảng có nội dung rất chắc, tổng cộng gần 400 phần nhỏ (bản cô đọng tương ứng: [《Giáo trình giảng sâu JVM và GC tuning của Shang Silicon Valley》](https://www.bilibili.com/video/BV1Dz4y1A7FB/)). + +Nội dung khóa học chia làm 3 phần: + +1. 《Bộ nhớ và garbage collection》 +2. 《Bytecode và class loading》 +3. 《Giám sát performance và tuning》 + +![](https://oss.javaguide.cn/github/javaguide/interview-preparation/java-learning-route/20210409181534319.png) + +👉 Tôi đã tổng hợp trọn bộ video hướng dẫn & tài liệu series học Java backend mới nhất của Shang Silicon Valley, bạn nào thích xem video có thể tải tại link này: [【Tổng hợp mới nhất】Trọn bộ giáo trình Java backend & project thực chiến của Shang Silicon Valley](https://mp.weixin.qq.com/s/jkZthmOSDgTF1PrCeNus_A) (khuyến nghị). + +![](https://oss.javaguide.cn/github/javaguide/books/88714e9becd0485aae247772b6ed9949.png) + +Trong quá trình học, rất khuyến khích kết hợp với phần tổng hợp các câu hỏi thường gặp và điểm kiến thức quan trọng của tôi: + +- [Giải thích chi tiết vùng nhớ Java (trọng điểm)](https://javaguide.cn/java/jvm/memory-area.html) +- [Giải thích chi tiết garbage collection trong JVM (trọng điểm)](https://javaguide.cn/java/jvm/jvm-garbage-collection.html) +- [Giải thích chi tiết cấu trúc file class](https://javaguide.cn/java/jvm/class-file-structure.html) +- [Giải thích chi tiết quá trình class loading](https://javaguide.cn/java/jvm/class-loading-process.html) +- [Giải thích chi tiết class loader (trọng điểm)](https://javaguide.cn/java/jvm/classloader.html) + +## Database + +### Cơ bản (tùy chọn) + +Kiến thức cơ bản về database thực ra có thể học tùy chọn. Với sinh viên ngành máy tính, hồi đại học chắc cũng đã học rồi. Tuy nhiên, đa phần học xong cũng như không, ai chưa học cũng không cần lo! + +Ở đây vẫn cung cấp một số tài liệu học cho các bạn muốn học kiến thức cơ bản về database. + +Về sách, rất khuyến nghị 《Database System Concepts》, cuốn này bao phủ trọn bộ khái niệm của database system, hệ thống kiến thức rõ ràng, là giáo trình kinh điển để học database system! Không phải sách tham khảo! + +![](https://oss.javaguide.cn/github/javaguide/booksimage-20220409150441742.png) + +Nếu bạn thấy sách hơi khô khan, tự mình không kiên trì nổi, tôi khuyên bạn nên xem một số video tốt trước. Ví dụ [《Nguyên lý database system》](https://www.icourse163.org/course/BNU-1002842007) của Đại học Sư phạm Bắc Kinh rất hay. + +Giảng viên khóa này giảng rất chi tiết, bài tập mỗi phần nhỏ cũng bám sát kiến thức đã dạy, phía sau còn có nhiều bài thực hành đi kèm. + +![](https://oss.javaguide.cn/github/javaguide/books/up-e113c726a41874ef5fb19f7ac14e38e16ce.png) + +Nếu bạn thích thực hành, khá dị ứng với lý thuyết, tôi khuyên bạn xem [《Làm thế nào để phát triển một database đơn giản》](https://cstack.github.io/db_tutorial/), project này sẽ hướng dẫn bạn từng bước viết một database đơn giản. + +![](https://oss.javaguide.cn/github/javaguide/books/up-11de8cb239aa7201cc8d78fa28928b9ec7d.png) + +Học trên giấy rốt cuộc vẫn nông cạn, muốn hiểu thấu phải tự tay làm! Rất khuyến khích các bạn ngành CS nhất định phải thực hành thật nhiều!!! + +### MySQL + +Với Java development, tuy PostgreSQL cũng khá hot, nhưng MySQL mới là chủ đạo, đa phần doanh nghiệp trong nước vẫn dùng MySQL. + +Nhập môn MySQL có thể tìm vài video xem, ví dụ [《MySQL Database từ nhập môn đến tinh thông》](https://www.bilibili.com/video/BV1Kr4y1i7ru/) của Heima. Trong lúc xem video, có thể kèm một cuốn sách nhập môn MySQL ví dụ [《MySQL Crash Course》](https://book.douban.com/subject/3354490/). + +Giai đoạn đầu không cần học quá sâu, nắm được các điểm kiến thức sau là được: + +1. Các lệnh thường dùng của MySQL: + + - Bảo mật: đăng nhập, thêm/xóa user, backup và restore dữ liệu + - Thao tác database: tạo/xóa database và table, phân quyền user + - …… + +2. Các kiểu dữ liệu và character set encoding thường dùng trong MySQL +3. Truy vấn đơn giản, truy vấn có điều kiện, truy vấn mờ, truy vấn nhiều table cũng như cách sắp xếp, lọc, gom nhóm kết quả truy vấn…… +4. Dùng index, view, stored procedure, cursor, trigger trong MySQL +5. …… + +Đi xa hơn nữa, có thể tìm vài cuốn sách hay để học nguyên lý bên trong và tối ưu performance, ví dụ [《High Performance MySQL》](https://book.douban.com/subject/23008813/) và [《MySQL Technology Insider》](https://book.douban.com/subject/24708143/). + +![](https://oss.javaguide.cn/github/javaguide/books/up-3d31e762933f9e50cc7170b2ebd8433917b.png) + +Ngoài ra, giới thiệu mạnh cuốn [《MySQL vận hành như thế nào》](https://book.douban.com/subject/35231266/), nội dung rất phù hợp để chuẩn bị phỏng vấn. Giảng rất chi tiết nhưng không khô khan, nội dung rất tâm huyết! + +![](https://oss.javaguide.cn/github/javaguide/csdn/20210703120643370.png) + +Nếu bạn muốn hiểu MySQL nhiều hơn, đồng thời cũng để chuẩn bị phỏng vấn, các điểm kiến thức sau cần đặc biệt lưu ý: + +1. Index: ưu/nhược điểm của index, B tree và B+ tree, clustered index và non-clustered index, covering index +2. Transaction: transaction, database transaction, ACID, concurrent transaction, isolation level +3. Storage engine (MyISAM và InnoDB) +4. Cơ chế lock và thuật toán lock của InnoDB + +Trong quá trình học, rất khuyến khích kết hợp với phần tổng hợp các câu hỏi thường gặp và điểm kiến thức quan trọng của tôi: + +- [Tổng hợp câu hỏi phỏng vấn MySQL thường gặp](https://javaguide.cn/database/mysql/mysql-questions-01.html) (MySQL cơ bản, storage engine, transaction, index, lock, tối ưu performance, v.v.) +- [Giải thích chi tiết index trong MySQL](https://javaguide.cn/database/mysql/mysql-index.html) +- [Giải thích chi tiết ba loại log của MySQL (binlog, redo log và undo log)](https://javaguide.cn/database/mysql/mysql-logs.html) +- [Giải thích chi tiết isolation level của transaction MySQL](https://javaguide.cn/database/mysql/transaction-isolation-level.html) +- [Cách storage engine InnoDB hiện thực MVCC](https://javaguide.cn/database/mysql/innodb-implementation-of-mvcc.html) +- [Quá trình thực thi câu lệnh SQL trong MySQL](https://javaguide.cn/database/mysql/how-sql-executed-in-mysql.html) + +### PostgreSQL (tùy chọn) + +Giống MySQL, PostgreSQL cũng là relational database mã nguồn mở, miễn phí và mạnh mẽ. Slogan của PostgreSQL là "**relational database mã nguồn mở tiên tiến nhất thế giới**". + +![](https://oss.javaguide.cn/github/javaguide/books/image-20220702144954370.png) + +Khách quan mà nói, PostgreSQL đúng là ưu việt hơn MySQL. Tuy nhiên, hiện tại ở Trung Quốc MySQL vẫn là chủ đạo, PostgreSQL thuộc diện học tùy chọn. + +Tài liệu tiếng Trung của PostgreSQL nên xem: [Tài liệu tiếng Trung PostgreSQL 14](http://www.postgres.cn/docs/14/index.html). Ngoài ra, về sách PostgreSQL, xem gợi ý tại đây là được: [Gợi ý sách database: PostgreSQL](https://javaguide.cn/books/database.html#postgresql). + +### Redis + +Nếu project backend có dùng distributed cache thì thường là dùng Redis. Tuy nhiên, Redis không chỉ làm được cache, nó còn dùng làm distributed lock, delay queue, message queue, v.v. + +Về video miễn phí, khuyến nghị [Giáo trình Redis một giờ](https://www.imooc.com/learn/839) của GeekHour (rất khuyến nghị, dễ hiểu, giới thiệu ngắn gọn phần lớn điểm kiến thức của Redis) và [《Series video Redis 7 mới nhất》](https://www.bilibili.com/video/BV13R4y1v7sP/) của Shang Silicon Valley (do thầy Dương làm, nội dung toàn diện hơn, phiên bản Redis mới hơn, rất khuyến nghị). + +Về sách, rất khuyến nghị hai cuốn [《Thiết kế và hiện thực Redis》](https://book.douban.com/subject/25900156/) và 《Nguyên lý cốt lõi và thực hành Redis》. Cuốn [《Nguyên lý cốt lõi và thực hành Redis》](https://book.douban.com/subject/26612779/) có ngày xuất bản tương đối gần đây hơn, chủ yếu kết hợp source code để phân tích các điểm kiến thức quan trọng của Redis như các cấu trúc dữ liệu và tính năng nâng cao. + +![《Thiết kế và hiện thực Redis》 và 《Thiết kế và hiện thực Redis》](https://oss.javaguide.cn/github/javaguide/books/redis-books.png) + +Về chuyên mục trả phí, khuyến nghị [《Công nghệ cốt lõi và thực chiến Redis》](https://time.geekbang.org/column/intro/100056701?utm_campaign=geektime_search&utm_content=geektime_search&utm_medium=geektime_search&utm_source=geektime_search&utm_term=geektime_search) của Geek Time. Tuy chưa đề cập nhiều nội dung của Redis phiên bản mới, nhưng thắng ở chỗ nội dung toàn diện và rõ ràng dễ hiểu. Hồi đó tôi xem chuyên mục này đúng là học được không ít, nhất là khu bình luận có rất nhiều bình luận hay từ các cao thủ. + +Trong quá trình học, rất khuyến khích kết hợp với phần tổng hợp các câu hỏi thường gặp và điểm kiến thức quan trọng của tôi: + +- [Tổng hợp câu hỏi phỏng vấn cache cơ bản thường gặp](https://javaguide.cn/database/redis/cache-basics.html) +- [Tổng hợp câu hỏi phỏng vấn Redis thường gặp (phần 1)](https://javaguide.cn/database/redis/redis-questions-01.html) +- [Tổng hợp câu hỏi phỏng vấn Redis thường gặp (phần 2)](https://javaguide.cn/database/redis/redis-questions-01.html) +- [Giải thích chi tiết 5 kiểu dữ liệu cơ bản của Redis](https://javaguide.cn/database/redis/redis-data-structures-01.html) +- [Giải thích chi tiết 3 kiểu dữ liệu đặc biệt của Redis](https://javaguide.cn/database/redis/redis-data-structures-02.html) +- [Giải thích chi tiết cơ chế persistence của Redis](https://javaguide.cn/database/redis/redis-persistence.html) +- [Giải thích chi tiết memory fragmentation trong Redis](https://javaguide.cn/database/redis/redis-memory-fragmentation.html) + +### MongoDB (tùy chọn) + +Với Java backend development, MongoDB thuộc diện học tùy chọn, ít dùng, phỏng vấn thường cũng không hỏi, trừ khi project của bạn có dùng MongoDB. + +Ở đây không giới thiệu video hay sách, chỉ giới thiệu hai bài viết tôi viết: + +- [Tổng hợp câu hỏi phỏng vấn MongoDB thường gặp (phần 1)](https://javaguide.cn/database/mongodb/mongodb-questions-01.html) +- [Tổng hợp câu hỏi phỏng vấn MongoDB thường gặp (phần 2)](https://javaguide.cn/database/mongodb/mongodb-questions-02.html) + +## Công cụ phát triển thường dùng + +Rất quan trọng! Rất quan trọng! Đặc biệt là Git và Docker. + +Ngoài các công cụ bên dưới, tôi rất khuyến khích bạn nhất định phải nắm được cách dùng GitHub. Một số mẹo nhỏ khi dùng GitHub, bạn có thể xem bài [Mẹo nhỏ với GitHub](https://javaguide.cn/tools/git/github-tips.html). + +### IDEA + +Người ta nói: "muốn làm tốt việc, trước hết phải mài sắc công cụ!". Chọn được công cụ phát triển tốt rất có ích cho việc code hiệu suất cao! + +Công cụ phát triển Java thường dùng chỉ có Eclipse và IDEA. Cá nhân tôi thấy IDEA là IDE phù hợp nhất cho Java developer, không có cái thứ hai (đừng cãi, cái bạn thích chính là cái tốt nhất). + +Ngoài việc bản thân IDEA hỗ trợ code rất tốt (ví dụ gợi ý theo ngữ cảnh thông minh), trong IDEA còn có rất nhiều plugin giúp chúng ta phát triển hiệu quả. + +Vài năm gần đây, các AI coding IDE như Cursor nổi lên, đúng là có tác động nhất định đến IDEA. Nhưng nhìn tổng thể, IDEA vẫn khó bị thay thế. Dù là trải nghiệm phát triển hay khả năng refactor code, IDEA đều có ưu thế không gì sánh bằng. Tất nhiên, ở mảng AI hỗ trợ code, IDEA thể hiện đúng là có phần tụt lại. Phải biết rằng trước đây gợi ý code thông minh vốn là sở trường của nó. + +[Tài liệu tiếng Trung chính thức của IntelliJ IDEA năm nay đã chính thức lên sóng](https://mp.weixin.qq.com/s/GT-zQHLOBB25ZRf1nyyt2Q), rất khuyến nghị lấy đây làm tài liệu gốc. + +**Lối vào tài liệu tiếng Trung chính thức của IDEA**: **** + +Ngoài ra, [「Hướng dẫn sử dụng IDEA hiệu quả」](https://idea.javaguide.cn/) là một website tôi tạo ra, bên trên có các nội dung sau: + +- Mẹo sử dụng IDEA +- Plugin IDEA cần có +- Nhập môn phát triển plugin IDEA +- Mẹo refactor bằng IDEA +- Mẹo đọc source code bằng IDEA + +![Trang chủ website 「Hướng dẫn sử dụng IDEA hiệu quả」](https://oss.javaguide.cn/github/awesome-idea-tutorial/awesome-idea-tutorial-website-homepage%20%20%20%20%20%20.png) + +### Maven + +Maven thực ra dùng khá đơn giản, một hai ngày là nhập môn được cách dùng cơ bản. Tuy nhiên, muốn dùng cho giỏi thì khá khó, giai đoạn đầu chỉ cần biết dùng cơ bản là được. + +Nói thêm một câu: trước khi học các framework thường dùng, có thể bỏ thời gian học trước cách dùng Maven, tuyệt đối đừng đi tìm và tải Jar khắp nơi (nếu project bạn đang làm không dùng công cụ quản lý package thì hãy nhanh chóng đổi sang giáo trình mới hơn). + +Phần Maven này không cần giới thiệu video hay sách gì, xem trực tiếp mấy bài dưới đây là được: + +- [Tổng hợp khái niệm cốt lõi của Maven](https://javaguide.cn/tools/maven/maven-core-concepts.html) +- [Best practice cho Maven](https://javaguide.cn/tools/maven/maven-best-practices.html) +- [Bốn mươi lăm hình, mười lăm nghìn chữ! Một bài giúp bạn thoát mê cung và làm chủ Maven!](https://juejin.cn/post/7238823745828405308) + +Sau khi học xong, nhất định phải hiểu được các câu hỏi sau (người mới hiểu hai câu đầu là được): + +1. Project Maven tạo thế nào? Thêm dependency thế nào? +2. Xung đột dependency của Maven giải quyết thế nào? +3. Project Maven nhiều module thì build, chạy, đóng gói thế nào? +4. Private repository của Maven dựng thế nào? + +### Git + +Kỹ năng Git cũng là thứ bắt buộc phải có với lập trình viên! Hãy thử host code của mình lên GitHub trong quá trình học, có một trang GitHub đẹp sẽ cộng điểm rất nhiều khi phỏng vấn xin việc. Hơn nữa, các doanh nghiệp hiện nay đều làm version control dựa trên Git trên nền tảng GitHub hoặc GitLab. + +Về học Git, rất khuyến nghị website học Git tương tác [Learn Git Branching](https://learngitbranching.js.org/ "Learn Git Branching"). Hiệu quả thực sự rất rất tốt, cho bạn học các thao tác Git thường gặp theo kiểu chơi game. + +Toàn bộ giáo trình chia thành nhiều màn, mỗi màn đều có hướng dẫn rất chi tiết, còn có ảnh động minh họa kết quả chi tiết. Hơn nữa, làm sai rồi bạn vẫn có thể dùng lệnh `reset` để bắt đầu lại từ đầu. + +![](https://oss.javaguide.cn/github/javaguide/interview-preparation/java-learning-route/20210423182350378.png) + +Nếu bạn thực sự không biết đáp án, còn có thể dùng lệnh `show solution` để xem đáp án. + +![](https://oss.javaguide.cn/github/javaguide/interview-preparation/java-learning-route/20210423181725451.png) + +Kiểu học có phản hồi tức thì này làm quá trình học trở nên thú vị! Thực lòng cảm ơn tác giả website này, quá thích! + +Ngoài ra, bạn có thể xem bài [Nhập môn Git cực gọn](https://javaguide.cn/tools/git/git-intro.html), các khái niệm về version control và Git, các thao tác Git thường gặp đều được giới thiệu trong bài này. + +Nếu muốn tìm hiểu Git chi tiết, bạn có thể xem cuốn [《Pro Git》](https://www.progit.cn/ "《Pro Git》"), giới thiệu rất toàn diện, miễn phí, hỗ trợ đọc online và có bản tiếng Trung! + +![](https://oss.javaguide.cn/github/javaguide/interview-preparation/java-learning-route/20210423183640734.png) + +![](https://oss.javaguide.cn/github/javaguide/interview-preparation/java-learning-route/20210423183749743.png) + +Đây là một địa chỉ đọc online khác của cuốn sách này: . + +Nếu bạn thích xem video hướng dẫn hơn, có thể xem [《Làm chủ bộ ba Git》](http://gk.link/a/10qcT) của Geek Time, tác giả khóa học là Tô Linh, phụ trách nền tảng code của Ctrip, giảng khá tốt! + +### Docker + +Trong quy trình phát triển truyền thống, project của chúng ta thường cần các môi trường như MySQL, Redis, FastDFS, v.v., những môi trường này đều phải tự tay tải về và cấu hình, quy trình cài đặt cấu hình cực kỳ phức tạp, mà thao tác trên các hệ điều hành khác nhau cũng không giống nhau. + +Sự xuất hiện của Docker đã giải quyết hoàn hảo vấn đề này, chúng ta có thể cài các phần mềm như MySQL, Redis trong container, tách ứng dụng ra khỏi kiến trúc môi trường. Ưu thế của nó là: + +1. Môi trường chạy nhất quán, dễ dàng di chuyển hơn +2. Đóng gói và cô lập process, các container không ảnh hưởng lẫn nhau, tận dụng tài nguyên hệ thống hiệu quả hơn +3. Có thể sao chép ra nhiều container giống hệt nhau thông qua image + +Về giải thích các khái niệm Docker thường gặp, có thể xem bài [Giải thích khái niệm cơ bản về Docker](https://javaguide.cn/tools/docker/docker-intro.html) của JavaGuide; từ con số không đến thực chiến có thể xem bài [Docker từ nhập môn đến làm việc thật](https://javaguide.cn/tools/docker/docker-in-action.html), nội dung rất chi tiết! + +Ngoài ra, giới thiệu thêm một cuốn sách mã nguồn mở chất lượng rất cao [《Docker từ nhập môn đến thực hành》](https://yeasy.gitbook.io/docker_practice/introduction/why), nội dung cuốn này rất mới, dù sao nội dung sách là mã nguồn mở nên có thể cải tiến bất cứ lúc nào. + +![Trang chủ website 《Docker từ nhập môn đến thực hành》](https://oss.javaguide.cn/github/javaguide/tools/docker/docker-getting-started-practice-website-homepage.png) + +Nếu muốn xem video, khuyến nghị cái này: [Giáo trình Docker nhập môn nhanh trong 1 giờ](https://www.bilibili.com/video/BV11L411g7U1/), không có lời thừa, nội dung giá trị khá nhiều. Hơn nữa, slide bài giảng cũng được chia sẻ miễn phí: [Slide giáo trình Docker 1 giờ](https://docker.easydoc.net/doc/81170005/cCewZWoN/lTKfePfP). + +![Giáo trình Docker nhập môn nhanh trong 1 giờ](https://oss.javaguide.cn/github/javaguide/interview-preparation/java-learning-route/docker-1-hour-quick-start-guide.png) + +Cuối cùng, sau khi học xong các thao tác Docker thường gặp, khuyên mọi người lấy một project tách frontend-backend làm ví dụ để thực hành deploy. Ví dụ, bạn có thể chọn deploy chính project CV của mình, như vậy phần kinh nghiệm project có thể đính kèm địa chỉ trải nghiệm online, cũng là một điểm cộng! + +## Design pattern + +Trong phát triển phần mềm có một khái niệm gọi là "**tái sử dụng phần mềm**". Nói đơn giản, tái sử dụng phần mềm là khi xây dựng một phần mềm mới, chúng ta không cần bắt đầu từ con số không; bằng cách tái sử dụng các bánh xe có sẵn (framework, thư viện bên thứ ba, v.v.), **design pattern**, nguyên tắc thiết kế và các vật liệu có sẵn khác, chúng ta có thể xây dựng nhanh hơn một phần mềm đáp ứng yêu cầu. + +Tái sử dụng phần mềm cần sự trợ giúp của design pattern. Bởi vì trong phát triển phần mềm, design pattern có thể nâng cao khả năng mở rộng và khả năng bảo trì của code thông qua việc đóng gói phần thay đổi! + +Trong công việc phát triển nghiệp vụ hằng ngày, nếu bạn không biết design pattern, có lẽ bạn vẫn hoàn thành được yêu cầu chức năng của project. Nhưng! Chỉ CRUD thuần túy thì chán biết bao nhiêu! Chúng ta cần suy nghĩ cách viết code nghiệp vụ chất lượng cao hơn. Ngoài ra, các framework như Spring, MyBatis đều dùng rất nhiều design pattern. Nếu bạn muốn hiểu nguyên lý của chúng, design pattern cũng là vũ khí bắt buộc phải có. + +Design pattern không chỉ cần học mà quan trọng nhất là phải liên tục thực hành và cảm nhận. Nhưng! Design pattern không phải viên đạn bạc, **đừng dùng design pattern chỉ để dùng design pattern**. + +Nếu muốn học design pattern qua sách, ưu tiên hàng đầu là 《Học lại Java Design Pattern》. Ví dụ thú vị, kèm hình ảnh sinh động, cách giảng design pattern qua case thực chiến tuyệt vời! Mỗi chi tiết trong sách đều toát lên sự tâm huyết của tác giả! Thực ra mỗi design pattern đều không khó hiểu, thứ đa số độc giả cần nhất là kinh nghiệm thực chiến với design pattern. Nếu bạn có thể tỉ mỉ suy nghĩ và thực hành từng case trong 《Học lại Java Design Pattern》, tôi tin hiểu biết của bạn về design pattern nhất định sẽ lên một tầm cao mới! + +![](https://p1-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/b4da6f8cc0cf4a8e8238d3d8671e0462~tplv-k3u1fbpfcp-watermark.image) + +Nếu muốn học qua video, ưu tiên hàng đầu là video [《Java Design Pattern của Shang Silicon Valley (giải thích bằng hình + mổ xẻ source code framework)》](https://www.bilibili.com/video/BV1G4411c7N4). + +![](https://p1-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/029687d24c7b4882ba81b5b629c323a1~tplv-k3u1fbpfcp-watermark.image) + +Video này giảng toàn diện các nội dung liên quan đến design pattern thông qua cách giải thích bằng hình + phân tích source code framework, bao gồm bảy nguyên tắc lớn của design pattern, sơ đồ lớp UML - sáu quan hệ lớn giữa các class, 23 design pattern và cách phân loại chúng. + +## Linux + +Với lập trình viên Java, chúng ta cần nắm được cách dùng Linux cơ bản, đặc biệt là các lệnh thường dùng như: lệnh chuyển thư mục, lệnh thao tác thư mục, lệnh thao tác file, lệnh nén hoặc giải nén file, v.v. Còn các nội dung tầng dưới như kiến trúc kernel Linux, nguyên lý bên trong thì không bắt buộc, có thể tùy tình hình bản thân mà quyết định học hay không. + +Với các bạn muốn nhập môn Linux nhanh, khuyên đọc bài [Tổng hợp kiến thức cơ bản về Linux](https://javaguide.cn/cs-basics/operating-system/linux-intro.html) tôi viết, trong đó giới thiệu một số khái niệm Linux mà lập trình viên Java phải biết cùng các lệnh thường gặp. + +Về video, tôi khuyến nghị [Giáo trình nhập môn Linux 30 phút](https://www.bilibili.com/video/BV1cq421w72c) của GeekHour, dễ hiểu, giảng theo hướng thực chiến! Tuy nhiên, nội dung tương đối thiên về cơ bản, phù hợp cho bạn nào muốn nhập môn nhanh. + +Với các bạn muốn học có hệ thống, vẫn khuyên xem sách, ví dụ series 《Bếp riêng Linux của anh Chim》 khá tốt. Tuy nhiên, nội dung hơi quá nhiều, cá nhân tôi khuyên nên xem như sách tra cứu hoặc chọn các chương nội dung mình quan tâm để học. + +![](https://oss.javaguide.cn/github/javaguide/books/linux-private-kitchen-basic-learning.png) + +Đừng quên học Shell programming, đây cũng là thứ bắt buộc phải nắm; nhập môn nhanh có thể đọc bài [Tổng hợp kiến thức cơ bản về Shell programming](https://javaguide.cn/cs-basics/operating-system/shell-intro.html) tôi viết, tổng hợp các điểm kiến thức quan trọng như biến Shell, toán tử cơ bản, điều khiển luồng, function. + +## Nền tảng frontend + +Tác giả chủ yếu làm Java backend, hiểu biết về frontend chỉ ở mức sơ sài, vừa mới nhập môn (từng làm full-stack một năm), ở đây chỉ chia sẻ đơn giản quan điểm của mình. + +Framework frontend thay đổi rất nhanh, hiện tại khá phổ biến là Vue và React. Với các bạn ở Trung Quốc, Vue phù hợp hơn để bỏ công sức học, vì công ty trong nước dùng Vue nhiều hơn. Tuy nhiên, framework frontend không bắt buộc phải học, có thể tùy tình hình bản thân mà quyết định. + +Nhưng dù công nghệ frontend thay đổi thế nào, bộ ba frontend (HTML, CSS, JavaScript) sẽ không đổi, và cũng bắt buộc phải học. + +HTML và CSS so với JS thì đơn giản hơn. Bạn có thể học một số kiến thức cơ bản về HTML, CSS, JS trên [W3school](http://www.w3school.com.cn/). Sau đó thực hành qua một project frontend đơn giản. Ví dụ bạn có thể làm một CV cá nhân hoặc mô phỏng trang chủ chính thức nào đó để viết một trang web tương tự. + +JavaScript thì sâu hơn nhiều, cũng là trọng tâm trong phỏng vấn frontend. + +Học JS thì nội dung liên quan đến JS trên [MDN](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript) là bắt buộc phải xem! Nội dung trên đó rất toàn diện, chất lượng rất cao! + +Ngoài ra, giáo trình JS mã nguồn mở [《The Modern JavaScript Tutorial》](https://javascript.info/) rất tuyệt! Hiện tại, series giáo trình này còn được dịch ra nhiều thứ tiếng. + +![](https://oss.javaguide.cn/github/javaguide/interview-preparation/java-learning-route/20210409151045407.png) + +Nội dung giáo trình này chia làm 3 phần: + +1. Ngôn ngữ lập trình JavaScript: nhập môn JavaScript, còn giới thiệu các khái niệm nâng cao như OOP. +2. Trình duyệt (document, event, interface): học cách quản lý trang trên trình duyệt. +3. Các bài khác: học các kiến thức JavaScript nâng cao khác theo nhu cầu. + +Ngoài ra, ngoại trừ một số project cũ, hiện nay thường là phát triển tách frontend-backend, tức frontend và backend có thể phát triển, test và deploy độc lập, hai bên giao tiếp với nhau qua API. Do đó, lập trình viên backend còn cần nắm: + +- Giao thức HTTP (nội dung thuộc phần computer network, ở đây nhắc thêm) +- Thiết kế và sử dụng RESTful API +- Các cách giao tiếp frontend-backend thường gặp: ví dụ Ajax (kết nối ngắn), WebSocket (kết nối dài, hai chiều) + +## Nền tảng J2EE + +### Servlet + +`Servlet` thuộc công nghệ khá cũ, hiện nay bạn gần như sẽ không trực tiếp dùng các API liên quan đến `Servlet`. Tuy nhiên, học `Servlet` giúp chúng ta hiểu rõ nguyên lý của các Web framework được đóng gói tốt, ví dụ `Spring MVC` chẳng qua là lớp đóng gói của `Servlet`, tầng dưới của nó vẫn phụ thuộc vào `Servlet`. + +Trong chương trình Java Web, `Servlet` chủ yếu chịu trách nhiệm nhận request `HttpServletRequest` của user, xử lý tương ứng trong `doGet()`, `doPost()`, rồi trả response `HttpServletResponse` về cho user. + +Bạn có thể học kiến thức cơ bản về Servlet qua sách 《Head First Servlets & JSP (bản tiếng Trung)》 hoặc 《Hướng dẫn học Servlet và JSP》. + +**Lưu ý**: JSP thì đừng học nữa, công nghệ lỗi thời, đã bị đào thải rồi! + +### Web server + +Tomcat là một project thuộc quỹ Apache, chủ yếu dùng làm Web server. + +Nếu bạn học thẳng Spring Boot thì không học Tomcat cũng không ảnh hưởng gì (vẫn khuyên nên học). Vì Spring Boot (`spring-boot-starter-web`) dùng Tomcat làm embedded `Servlet` container mặc định, bạn dùng mà không hề cảm nhận được. + +Nói đơn giản, Tomcat chủ yếu hiện thực 2 chức năng cốt lõi: + +1. Xử lý kết nối `Socket`, chịu trách nhiệm chuyển đổi giữa network byte stream và các object `Request`, `Response`. +2. Load và quản lý `Servlet`, cũng như xử lý cụ thể request `Request`. + +Nếu bạn muốn nghiên cứu sâu Tomcat, lựa chọn hàng đầu là chuyên mục [《Mổ xẻ sâu Tomcat & Jetty》](http://gk.link/a/10r1C) của Geek Time. Đây là tài liệu giảng về nguyên lý bên trong Tomcat hay nhất tôi từng xem, rất khuyến nghị! + +Chuyên mục này không chỉ giúp bạn hiểu sâu hơn về Tomcat, mà còn nâng cao tư duy của bạn về kiến trúc hệ thống, tối ưu performance và các lĩnh vực khác. + +![](https://oss.javaguide.cn/github/javaguide/interview-preparation/java-learning-route/20210512202540785.png) + +Ngoài Tomcat, Nginx cũng bắt buộc phải học! + +Nginx là một HTTP server và reverse proxy server hiệu năng cao, thường được dùng làm reverse proxy và load balancing. + +Nếu bạn muốn học Nginx, có thể xem [《150 bài giảng kiến thức cốt lõi Nginx》](http://gk.link/a/10r1D). Nội dung rất toàn diện, từ khái niệm, code cho đến thực chiến, từ HTTP đến OpenResty. + +## Framework thường dùng + +Trong phỏng vấn thực tế, kiến thức về framework ít được hỏi; học các framework thường dùng chủ yếu là để đáp ứng nhu cầu phát triển project và yêu cầu công việc. + +### Spring/SpringBoot + +**Chưa học Spring có thể bắt tay học thẳng SpringBoot không?** + +Nói rõ là hoàn toàn được! Hiện đa số doanh nghiệp đều dùng SpringBoot, và Spring cũng không phải nền tảng bắt buộc trước khi học Spring Boot; so với Spring thì Spring Boot dễ bắt đầu hơn! Nếu bạn chỉ muốn dùng Spring Boot để làm project thì học thẳng Spring Boot là được. + +Tuy nhiên, cá nhân tôi vẫn khuyên nên hiểu trước hai khái niệm khá quan trọng là Spring AOP và IoC rồi hãy học SpringBoot. Ngoài ra, nếu chuẩn bị phỏng vấn thì các điểm kiến thức như scope và vòng đời của bean trong Spring, giải thích chi tiết nguyên lý làm việc của SpringMVC, v.v. đều rất quan trọng, nhất định phải hiểu. Khuyến nghị đọc bài này: [Tổng hợp câu hỏi phỏng vấn Spring thường gặp](https://javaguide.cn/system-design/framework/spring/spring-knowledge-and-questions-summary.html). + +Học Spring Boot thì vẫn khuyên nên xem nhiều [**《Tài liệu chính thức của Spring Boot》**](https://spring.io/projects/spring-boot#learn), viết rất chi tiết. + +Việc tích hợp SpringBoot với một số công nghệ thường gặp bạn cũng cần biết cách làm, ví dụ SpringBoot tích hợp MyBatis, ElasticSearch, SpringSecurity, Redis, v.v. Cố gắng thực hành, viết vài Demo. Đến giai đoạn sau, thậm chí có thể tự làm vài project nhỏ để áp dụng hết những kiến thức này. + +Về sách, cá nhân tôi thực ra không có gợi ý nào đặc biệt hay, dù sao đây là kiến thức framework, thay đổi khá nhanh, nội dung nhiều cuốn sách đã lỗi thời. + +Cân nhắc nhiều bạn khá thích đọc sách, ở đây tôi vẫn giới thiệu sơ vài cuốn! + +Với các bạn muốn thực chiến, tôi rất không khuyến khích xem sách, xem thẳng project thực chiến của Shang Silicon Valley là được. Bài này có thể lấy video mới nhất và đã giới thiệu về project thực chiến của Shang Silicon Valley: [【Tổng hợp mới nhất】Trọn bộ giáo trình Java backend & project thực chiến của Shang Silicon Valley](https://mp.weixin.qq.com/s/jkZthmOSDgTF1PrCeNus_A) (khuyến nghị). + +![](https://oss.javaguide.cn/github/javaguide/books/88714e9becd0485aae247772b6ed9949.png) + +Với các bạn nghiên cứu sâu nguyên lý bên trong Spring Boot, có thể xem **[《Tư tưởng lập trình Spring Boot (phần cốt lõi)》](https://book.douban.com/subject/33390560/)**. + +![《Tư tưởng lập trình Spring Boot (phần cốt lõi)》 - Douban](https://oss.javaguide.cn/github/javaguide/books/image-20220424113546513.png) + +Cuốn này hơi dài dòng một chút, tuy nhiên giới thiệu nguyên lý khá rõ ràng (không phù hợp cho người mới). + +Nếu bạn thích xem video hơn, khuyến nghị [**《Spring Boot3 nhập môn từ con số không bản 2023》**](https://www.bilibili.com/video/BV1Es4y1q7Bf/) của thầy Lôi thuộc Shang Silicon Valley. Đây có thể là giáo trình Spring Boot chất lượng cao nhất và miễn phí trên toàn mạng, đánh giá cực tốt! + +Ngoài ra, mảng Spring Boot còn có rất nhiều giáo trình mã nguồn mở chất lượng, tôi đã tổng hợp và đưa vào [Giáo trình kỹ thuật mã nguồn mở Java chất lượng](https://javaguide.cn/open-source-project/tutorial.html#springboot). + +![](https://oss.javaguide.cn/github/javaguide/open-source-project/open-source-project-springboot-technical-course.png) + +### MyBatis + +MyBatis là ORM framework được dùng nhiều nhất ở Trung Quốc. Khi học Spring/Spring Boot, bạn nên học luôn MyBatis, điều này tôi cũng đã nhắc ở trên. + +Ngoài ra, khuyên bạn nên nắm ít nhất một framework tăng cường cho MyBatis, ở đây giới thiệu hai cái do Trung Quốc phát triển: + +1. [MyBatis-Plus](https://baomidou.com/): gọi tắt là MP, trên nền MyBatis chỉ tăng cường chứ không thay đổi, sinh ra để đơn giản hóa phát triển và nâng cao hiệu suất. +2. [MyBatis-Flex](https://mybatis-flex.com/): framework tăng cường MyBatis rất nhẹ, đồng thời có performance và tính linh hoạt cực cao. + +Với các bạn làm project, cũng có thể chọn học và dùng thẳng framework tăng cường MyBatis. + +### Unit test + +Về unit test, các framework unit test thường dùng hiện nay có: JUnit, Mockito, Spock, PowerMock, JMockit, TestableMock, v.v. + +JUnit gần như là lựa chọn mặc định, nhưng nó không hỗ trợ Mock, do đó chúng ta còn cần chọn thêm một công cụ Mock. Mockito và Spock là hai công cụ Mock chủ đạo nhất, thường thì chọn một trong hai. + +Rốt cuộc nên chọn Mockito hay Spock? Ở đây tôi làm vài so sánh đơn giản: + +1. Spock không thể Mock static method và private method; từ Mockito 3.4.0 trở đi đã hỗ trợ Mock static method, chi tiết xem issue này: [mockito/mockito#1013](https://github.com/mockito/mockito/issues/1013), hướng dẫn cụ thể xem bài này: [Mocking Static Methods With Mockito](https://www.baeldung.com/mockito-mock-static-methods). +2. Spock dựa trên Groovy, code test viết ra rõ ràng dễ đọc hơn, khá chuẩn mực (tự mang theo cấu trúc test given-when-then thường dùng). Mockito không có quy chuẩn cấu trúc cụ thể, cần nhóm project tự thống nhất một quy ước hoặc tuân theo các thực hành code test tốt. Thông thường, với cùng một test case, code của Spock sẽ gọn hơn. +3. Mockito có lượng người dùng rộng rãi hơn, ổn định đáng tin cậy. Hơn nữa, Mockito là công cụ Mock được SpringBoot Test tích hợp sẵn mặc định. + +Mockito và Spock đều là công cụ Mock rất tốt; tương đối mà nói, tính ứng dụng của Mockito rộng hơn một chút. + +Ở đây tiện thể giới thiệu một số tài liệu học liên quan đến testing: + +1. [Giáo trình đào tạo unit test nội bộ Alibaba](https://mp.weixin.qq.com/s/wzGxqNv58Zig9_Izi3VhDg) +2. [Unit test rốt cuộc là gì? Nên làm thế nào?](https://javaguide.cn/system-design/basis/unit-test.html) +3. [Integration Testing in Spring](https://www.baeldung.com/integration-testing-in-spring) +4. [Testing the Web Layer](https://spring.io/guides/gs/testing-web/) +5. [Có lẽ là bài nhập môn unit test Spock hay nhất toàn mạng](https://mp.weixin.qq.com/s/axNE8OjFh9V9SGgaCZVgOw) +6. [Chia sẻ thực hành triển khai framework unit test Mockito](https://mp.weixin.qq.com/s/6s_5XSzKp8fckKuojSvXUw) +7. [Làm sao viết được unit test hiệu quả](https://mp.weixin.qq.com/s/Y75fSX92kysSmYrhEH6QFQ) + +### Netty (tùy chọn) + +Netty là framework hot nhất trong lập trình network với Java, mọi người có thể tùy nhu cầu cá nhân mà quyết định có học hay không, trong phát triển doanh nghiệp thực tế dùng không nhiều. + +Tuy nhiên, cá nhân tôi khuyên bạn nào còn dư sức thì vẫn nên dành thời gian học nghiêm túc, rất có ích cho việc nâng cao năng lực phát triển cá nhân. + +1. Netty dựa trên NIO (NIO là một I/O model đồng bộ non-blocking, được đưa vào Java 1.4). Dùng Netty có thể đơn giản hóa rất nhiều việc lập trình network như TCP và UDP socket server, đồng thời performance cũng như bảo mật và nhiều mặt khác đều rất xuất sắc. +2. Các project mã nguồn mở hot mà chúng ta thường tiếp xúc như Dubbo, RocketMQ, Elasticsearch, gRPC, Spark, v.v. đều dùng đến Netty. +3. Phần lớn framework microservice ở tầng dưới, phần liên quan đến giao tiếp network, đều làm dựa trên Netty, ví dụ như gateway Spring Cloud Gateway trong hệ sinh thái Spring Cloud. + +Dưới đây là một số sách/chuyên mục khá đáng giới thiệu. + +[《Netty in Action》](https://book.douban.com/subject/27038538/) + +![《Netty in Action》 - Douban](https://oss.javaguide.cn/github/javaguide/books/image-20220424113715369.png) + +Cuốn này có thể dùng để nhập môn Netty, nội dung đi từ BIO đến NIO, sau đó mới giới thiệu chi tiết tại sao có Netty, tại sao Netty dễ dùng và giảng các điểm kiến thức quan trọng của Netty. + +Cuốn này về cơ bản đã giới thiệu hết các điểm kiến thức quan trọng của Netty, và phần lớn đều giảng theo hình thức thực chiến. + +[《Con đường nâng cao Netty: học Netty qua case》](https://book.douban.com/subject/30381214/) + +![《Con đường nâng cao Netty: học Netty qua case》 - Douban](https://oss.javaguide.cn/github/javaguide/books/image-20220424113747345.png) + +Nội dung đều là các case thực hành về việc dùng Netty, ví dụ memory leak. Nếu bạn thấy mình đã nhập môn Netty hoàn toàn và muốn nắm Netty sâu hơn, khuyên bạn xem cuốn này. + +**[《Học Netty cùng Flash: thực chiến chat real-time với Netty và nguyên lý bên trong》](https://book.douban.com/subject/35752082/)** + +![](https://oss.javaguide.cn/github/javaguide/open-source-project/image-20220503085034268.png) + +Cuốn này chia làm hai phần: phần trên đưa bạn nhập môn Netty qua case thực chiến hệ thống chat real-time, phần dưới đưa bạn hiểu rõ các nguyên lý bên trong khá quan trọng của Netty thông qua phân tích source code Netty. + +Về video, [Trọn bộ giáo trình Netty của Heima Programmer](https://www.bilibili.com/video/BV1py4y1E7oA) khá tốt, bắt đầu từ kiến thức nền tảng NIO của Netty nên tương đối dễ tiếp thu. + +![](https://oss.javaguide.cn/github/javaguide/open-source-project/image-20220503115418795.png) + +### Workflow (tùy chọn) + +Workflow engine mã nguồn mở được dùng nhiều ở Trung Quốc là Flowable và Activiti, tài liệu tham khảo cũng khá nhiều. Camunda cũng tốt, nhẹ hơn, chức năng cũng rất hoàn thiện, performance và độ ổn định cũng rất tốt. Về việc chọn process engine mã nguồn mở, có thể tham khảo bài này: [Tham khảo lựa chọn process engine mã nguồn mở](https://zhuanlan.zhihu.com/p/369761832). + +Ghi chú: Flowable và Camunda đều phát triển từ một nhánh của Activiti5, triết lý của ba cái có khác biệt. + +Workflow engine [LiteFlow](https://liteflow.cc/) khá hot ở Trung Quốc chỉ làm luồng dựa trên logic, chứ không làm luồng dựa trên task theo role. Nếu bạn muốn làm luồng dựa trên task theo role, khuyên dùng hai framework Flowable và Activiti. Tức là, luồng phê duyệt (A duyệt xong thì đến B duyệt, rồi chuyển tiếp đến role C) thì LiteFlow không phù hợp. LiteFlow phù hợp với nghiệp vụ có logic phức tạp, ví dụ price engine, luồng đặt hàng; các nghiệp vụ này thường có rất nhiều bước, và các bước này hoàn toàn có thể tách theo độ mịn nghiệp vụ thành từng component độc lập để lắp ráp, tái sử dụng, thay đổi. + +Ở đây không giới thiệu tài liệu học, bạn nào quan tâm có thể tự tìm. + +## Search engine + +Search engine dùng để nâng cao hiệu quả tìm kiếm, chức năng tương tự search engine của trình duyệt. Search engine khá thường gặp là Elasticsearch (khuyến nghị) và Solr. + +Nếu bạn muốn học Elasticsearch, [Cộng đồng Elastic tiếng Trung](http://www.elasticsearch.cn/) cùng [Blog chính thức của Elastic](https://www.elastic.co/cn/blog/) đều là tài nguyên rất tốt, trên đó chia sẻ rất nhiều case thực hành cụ thể. + +Về video hướng dẫn, có thể xem [《ElasticSearch từ nhập môn đến tinh thông》](https://www.bilibili.com/video/BV1hh411D7sb/) của Shang Silicon Valley, phần đầu giảng dựa trên ElasticSearch 7.x, phần sau bổ sung thêm tính năng mới của Elasticsearch 8.x. + +Về sách, có thể xem 《Một cuốn sách giảng thấu Elasticsearch: nguyên lý, nâng cao và thực hành kỹ thuật》. Cuốn này viết dựa trên phiên bản 8.x, hiện là sách giảng về Elasticsearch mới nhất toàn mạng. Nội dung bao phủ các điểm kiến thức cốt lõi của chứng chỉ Elastic chính thức, xuất phát từ case project thật và lời giải cho các vấn đề cấp doanh nghiệp. + +![](https://oss.javaguide.cn/github/javaguide/books/one-book-guide-to-elasticsearch.png) + +Cuối cùng, giới thiệu thêm một số bài viết và tuyển tập xuất sắc về ElasticSearch để giúp bạn học và dùng ElasticSearch tốt hơn: + +- [Tổng hợp câu hỏi phỏng vấn Elasticsearch thường gặp - JavaGuide](https://javaguide.cn/database/elasticsearch/elasticsearch-questions-01.html) +- [Bài chi tiết nhập môn cơ bản Elasticsearch - Tencent Technology Engineering](https://mp.weixin.qq.com/s/GG_zrQlaiP2nfPOxzx_j9w) +- [Một số quy chuẩn sử dụng ElasticSearch trong công việc](https://juejin.cn/post/7244819106343518268) +- [《Series ES của Didi Technology》](https://mp.weixin.qq.com/mp/appmsgalbum?__biz=MzU1ODEzNjI2NA==&action=getalbum&album_id=3044498415449210882&scene=173&from_msgid=2247560768&from_itemidx=1&count=3&nolastread=1#wechat_redirect) +- [《Series cày nát Elasticsearch》](https://mp.weixin.qq.com/mp/appmsgalbum?__biz=MzI2NDY1MTA3OQ==&action=getalbum&album_id=1340073242396114944&scene=173&from_msgid=2247487667&from_itemidx=1&count=3&nolastread=1#wechat_redirect) (hàng trăm bài lý thuyết + thực chiến về ES, giáo trình ES toàn diện nhất toàn mạng. Video hướng dẫn tương ứng cho một phần nội dung: ) + +## Distributed & microservice (nâng cao) + +Phần này liên quan đến khá nhiều điểm kiến thức, ở đây tôi chỉ liệt kê các phần tương đối quan trọng như thuật toán và giao thức distributed, configuration center, distributed transaction. + +Để học kiến thức distributed, cá nhân tôi khá khuyến khích đọc sách và blog. Tất nhiên, nếu bạn thích xem video hơn thì cũng có thể tìm vài video hướng dẫn hoặc khóa học mở tốt để xem, học theo cách phù hợp với mình là được! + +**Gợi ý sách (thiên lý thuyết)**: + +《Hiểu sâu về distributed system》 rất tốt. Tác giả cuốn này dành rất nhiều dung lượng để giới thiệu consensus algorithm — thứ cực kỳ quan trọng trong lĩnh vực distributed, và còn dựa trên ngôn ngữ Go dẫn bạn hiện thực từ con số không thuật toán Paxos, tổ tiên của các consensus algorithm. + +![](https://oss.javaguide.cn/github/javaguide/books/deep-understanding-of-distributed-system.png) + +《Học kiến trúc từ con số không》 có nội dung khá toàn diện, distributed, microservice, high concurrency, high availability đều có đề cập. Cuốn này tương ứng với chuyên mục [《Học kiến trúc từ con số không》](http://gk.link/a/10pKZ) trên Geek Time, rất nhiều nội dung trong sách đến từ chuyên mục này, chọn một trong hai để đọc là được. + +![](https://oss.javaguide.cn/github/javaguide/books/20210412224443177.png) + +Cuốn [《Thiết kế kiến trúc phần mềm: con đường dung hợp kiến trúc kỹ thuật và kiến trúc nghiệp vụ của website lớn》](https://book.douban.com/subject/30443578/) của thầy Dư tương tự 《Học kiến trúc từ con số không》, nội dung cũng khá toàn diện và cũng rất tốt. + +![img](https://oss.javaguide.cn/github/javaguide/books/20210412232441459.png) + +**Gợi ý khóa học mở (thiên lý thuyết)**: + +MIT6.824: Distributed System là khóa học mở khá kinh điển. Mỗi buổi học của khóa này đều đọc kỹ một bài báo kinh điển trong lĩnh vực distributed system, từ đó truyền đạt các nguyên tắc quan trọng và kỹ thuật then chốt trong thiết kế và hiện thực distributed system. + +- [Làm sao học tốt hơn khóa distributed system MIT6.824?](https://www.zhihu.com/question/29597104) +- [MIT6.824: Distributed System (wiki bản dịch tiếng Trung)](https://mit-public-courses-cn-translatio.gitbook.io/mit6-824/) +- [MIT6.824: Distributed System - Hướng dẫn tự học CS](https://csdiy.wiki/%E5%B9%B6%E8%A1%8C%E4%B8%8E%E5%88%86%E5%B8%83%E5%BC%8F%E7%B3%BB%E7%BB%9F/MIT6.824/) + +**Gợi ý video (thiên thực chiến)**: + +Video có thể học thẳng [Giáo trình Spring Cloud bản mới nhất 2024](https://www.bilibili.com/video/BV1gW421P7RD/) của Shang Silicon Valley, khóa này giới thiệu các component chủ đạo nhất hiện nay trong SpringCloud và SpringCloud Alibaba. Sau khi học xong khóa này, bạn có thể bắt tay ngay vào thực chiến phát triển project microservice. + +![](https://oss.javaguide.cn/github/javaguide/interview-preparation/java-learning-route/shangguigu-springcloud.png) + +### Lý thuyết & thuật toán & giao thức + +Các lý thuyết & thuật toán & giao thức distributed tương đối quan trọng gồm: lý thuyết CAP, lý thuyết BASE, thuật toán Paxos, giao thức Gossip, thuật toán Raft, v.v. + +**Gợi ý bài viết**: + +- [Giải thích chi tiết lý thuyết CAP & BASE](https://javaguide.cn/distributed-system/protocol/cap-and-base-theorem.html) +- [Giải thích chi tiết thuật toán Paxos](https://javaguide.cn/distributed-system/protocol/paxos-algorithm.html) +- [Giải thích chi tiết thuật toán Raft](https://javaguide.cn/distributed-system/protocol/raft-algorithm.html) +- [Giải thích chi tiết giao thức Gossip](https://javaguide.cn/distributed-system/protocol/gossip-protocol.html) + +### Gọi từ xa + +Việc gọi giữa các service khác nhau thường có hai cách: + +- RPC: RPC (Remote Procedure Call) tức gọi thủ tục từ xa; qua RPC, chúng ta có thể gọi method của một service nào đó trên máy tính từ xa, quá trình này đơn giản như gọi method cục bộ. Dubbo là RPC framework do Trung Quốc phát triển, Alibaba mã nguồn mở, được dùng nhiều nhất trong nước. +- HTTP client: gọi RESTful API của service khác thông qua giao thức HTTP. Feign và OpenFeign (do Spring Cloud chính thức phát triển dựa trên Feign, dùng để thay thế Feign vốn đã vào trạng thái ngừng cập nhật) là những HTTP client thường dùng nhất hiện nay. + +OpenFeign và Dubbo đều là framework gọi từ xa được ứng dụng rộng rãi trong kiến trúc microservice hiện nay, nhưng cách hiện thực khác nhau (OpenFeign dựa trên giao thức HTTP, Dubbo hỗ trợ nhiều giao thức và còn có thể tự định nghĩa giao thức), tình huống phù hợp cũng hơi khác. Project microservice Spring Cloud hiện dùng khá nhiều OpenFeign theo phong cách Rest, cá nhân tôi khuyên học cái này. + +Tuy nhiên, nếu project bạn làm theo giáo trình dùng Dubbo hoặc công việc cần dùng Dubbo, thì bạn có thể tập trung học Dubbo. Giới thiệu phần tổng hợp tôi viết: + +- [Tổng hợp kiến thức cơ bản về RPC](https://javaguide.cn/distributed-system/rpc/rpc-intro.html) +- [Tổng hợp các vấn đề thường gặp về Dubbo](https://javaguide.cn/distributed-system/rpc/dubbo.html) + +Ngoài ra, tài liệu chính thức của Dubbo nhất định phải xem, địa chỉ: . + +### Service registry & discovery + +Eureka, Zookeeper, Consul, Nacos đều có thể cung cấp chức năng service registry & discovery. + +Cá nhân tôi khuyên học Nacos, ở Trung Quốc dùng khá nhiều, chức năng cũng mạnh hơn! Ngoài cung cấp chức năng service registry & discovery, nó còn có thể dùng làm configuration center. + +Học Nacos thì tài liệu chính thức nhất định phải xem: . + +Ngoài ra, giới thiệu thêm một số tài liệu học tôi thấy khá tốt: + +- [Kiến trúc & nguyên lý Nacos - Alibaba Tàng Kinh Các](https://developer.aliyun.com/ebook/36) (khuyến nghị, gồm thiết kế kernel Nacos, nguyên lý bên trong, best practice) + +- [55 hình hiểu thấu Nacos - Bất Tài Trần Mỗ](https://www.cnblogs.com/cbvlog/p/15636683.html) + +- [Phân tích bằng hình cách hiện thực configuration center Nacos - Juejin](https://juejin.cn/post/6844904050840993805) (không dán quá nhiều code, nguyên lý giảng rất rõ) + +- [Nacos giúp chúng ta giải quyết vấn đề gì? — Phần quản lý cấu hình - Alibaba Middleware](https://nacos.io/zh-cn/blog/5w1h-what.html) + +### API Gateway + +Gateway có thể cung cấp cho chúng ta các chức năng như chuyển tiếp request, xác thực bảo mật (authentication/authorization), kiểm soát lưu lượng, load balancing, degradation & circuit breaker, log, monitoring, validate tham số, chuyển đổi giao thức, v.v. + +Về kiến thức cơ bản và lựa chọn công nghệ cho API gateway, khuyến nghị đọc bài [Tổng hợp kiến thức cơ bản về API gateway](https://javaguide.cn/distributed-system/api-gateway.html) tôi viết. + +Project microservice Spring Cloud khá khuyến nghị dùng Spring Cloud Gateway làm API gateway, đây là một project hoàn toàn mới của Spring Cloud, nhằm thay thế Netflix Zuul. Để nâng cao performance của gateway, SpringCloud Gateway được hiện thực dựa trên WebFlux. Mục tiêu của Spring Cloud Gateway không chỉ là cung cấp cách routing thống nhất, mà còn cung cấp các chức năng cơ bản của gateway theo kiểu chuỗi Filter, ví dụ: bảo mật, monitoring/metrics và rate limiting. + +Dưới đây là những tài liệu học tôi thấy khá tốt: + +- [Tổng hợp các vấn đề thường gặp về Spring Cloud Gateway - JavaGuide](https://javaguide.cn/distributed-system/spring-cloud-gateway-questions.html) +- [6000 chữ | 16 hình | Hiểu sâu nguyên lý của Spring Cloud Gateway - Ngộ Không bàn kiến trúc](https://mp.weixin.qq.com/s/XjFYsP1IUqNzWqXZdJn-Aw) +- [Spring Cloud Gateway 10 câu hỏi liên hoàn chí mạng? - Bất Tài Trần Mỗ](https://www.cnblogs.com/cbvlog/p/15493160.html) +- [Thực chiến tích hợp Spring Cloud Gateway với Sentinel của Alibaba để rate limiting ở gateway! - Bất Tài Trần Mỗ](https://www.cnblogs.com/cbvlog/p/15512189.html) +- [Thực chiến Spring Cloud Gateway phần rate limiting - aneasystone](https://www.aneasystone.com/archives/2020/08/spring-cloud-gateway-current-limiting.html) (có giới thiệu các thuật toán và component rate limiting thường gặp) + +### Configuration center + +Trong microservice, sự phát triển của nghiệp vụ thường dẫn đến số lượng service tăng lên, kéo theo cấu hình chương trình (địa chỉ service, tham số database, v.v.) cũng tăng. + +Cách dùng file cấu hình truyền thống đã không đáp ứng được nhu cầu hiện tại, chủ yếu vì hai lý do: một là tính bảo mật không được đảm bảo (cấu hình đặt trong code repository dễ bị lộ); hai là tính kịp thời không ổn (sửa cấu hình phải restart service mới có hiệu lực). + +Spring Cloud Config, Nacos, Apollo, K8s ConfigMap đều có thể dùng làm configuration center. + +Cá nhân tôi thích Apollo và Nacos hơn. Nacos dùng thuận tay hơn, còn có thể tiện thể dùng làm registry center; Apollo làm tốt hơn ở mảng quản lý cấu hình. + +Cá nhân vẫn khuyên học Nacos, tài liệu học đã giới thiệu ở phần service registry & discovery bên trên. + +### Distributed ID + +ID là định danh duy nhất của dữ liệu, distributed ID là ID trong distributed system. + +Giải pháp cho distributed ID có rất nhiều, ví dụ: + +- Thuật toán: UUID, Snowflake +- Framework mã nguồn mở: UidGenerator (Baidu), Leaf (Meituan), Tinyid (Didi), IdGenerator (cá nhân) + +Phần này tương đối đơn giản, khuyến nghị đọc hai bài sau để học: + +- [Giới thiệu distributed ID & tổng hợp giải pháp hiện thực](https://javaguide.cn/distributed-system/distributed-id.html) +- [Hướng dẫn thiết kế distributed ID](https://javaguide.cn/distributed-system/distributed-id-design.html) + +### Distributed transaction + +Trong kiến trúc microservice, một hệ thống được tách thành nhiều microservice nhỏ. + +Mỗi microservice đều có thể được deploy trên máy khác nhau, và mỗi microservice có thể có một database riêng cho mình dùng. Trong trường hợp này, một nhóm thao tác có thể liên quan đến nhiều microservice cũng như nhiều database. + +Lấy ví dụ: trong hệ thống thương mại điện tử, khi bạn tạo một đơn hàng thường sẽ liên quan đến order service (số đơn hàng cộng một), inventory service (tồn kho trừ một), v.v., các service này sẽ có database riêng cho mình dùng. + +![Sơ đồ minh họa distributed transaction](https://cdn.jsdelivr.net/gh/javaguide-tech/blog-images-6@main/12-04-1/%E5%88%86%E5%B8%83%E5%BC%8F%E4%BA%8B%E5%8A%A1%E7%A4%BA%E6%84%8F%E5%9B%BE.png) + +**Vậy làm sao đảm bảo nhóm thao tác này hoặc là đều thực thi thành công, hoặc là đều thất bại?** + +Lúc này chỉ dựa vào database transaction là không đủ! Chúng ta cần đưa vào khái niệm **distributed transaction**! + +Giải pháp distributed transaction thường dùng có Seata và Hmily. + +1. [Seata](https://seata.io/zh-cn/index.html "Seata"): Seata là giải pháp distributed transaction mã nguồn mở, hướng tới cung cấp dịch vụ distributed transaction hiệu năng cao và dễ dùng trong kiến trúc microservice. +2. [Hmily](https://gitee.com/shuaiqiyu/hmily "Hmily"): giải pháp distributed transaction cấp tài chính. + +Hiện ở Trung Quốc dùng nhiều là Seata, khuyên học cái này. + +### Distributed tracing + +Khác với kiến trúc monolith, trong kiến trúc distributed, request cần được gọi qua nhiều service, việc truy vết vấn đề sẽ rất phiền. Chúng ta cần hệ thống distributed tracing để giải quyết điểm đau này. + +Hiện các hệ thống distributed tracing về cơ bản đều phát triển từ bài báo 《Dapper, a Large-Scale Distributed Systems Tracing Infrastructure》 của Google; các hệ chủ đạo có Pinpoint, Skywalking, CAT (tất nhiên cũng có sản phẩm khác như Zipkin, Jaeger, nhưng nhìn chung độ hoàn thiện không bằng 3 cái được chọn ở trên). + +Zipkin là công cụ distributed tracing mã nguồn mở của Twitter, Spring Cloud Sleuth thực chất dựa trên Zipkin. + +SkyWalking là công cụ tracing, phân tích, cảnh báo phân tán mã nguồn mở do Ngô Thịnh (Huawei) người Trung Quốc phát triển, hiện là project mã nguồn mở thuộc Apache. + +Hiện ở Trung Quốc dùng nhiều là SkyWalking, khuyên học cái này. + +## High performance (nâng cao) + +### CDN (chỉ cần nắm khái niệm và nguyên lý) + +CDN là việc phân phối tài nguyên tĩnh đến nhiều nơi khác nhau để truy cập từ nơi gần nhất, qua đó tăng tốc độ truy cập tài nguyên tĩnh, giảm gánh nặng cho server cũng như băng thông. + +Chúng ta chỉ cần nắm khái niệm cơ bản, nguyên lý của CDN và biết dùng dịch vụ CDN có sẵn của các nhà cung cấp cloud là được, không tốn quá nhiều thời gian. Khuyến nghị đọc bài [Tổng hợp các vấn đề thường gặp về CDN](https://javaguide.cn/high-performance/cdn.html) tôi viết. + +### Message queue + +Message queue trong distributed system chủ yếu dùng để bất đồng bộ hóa, giảm phụ thuộc và san tải đỉnh. + +Các message queue thường dùng như sau: + +1. [RocketMQ](https://github.com/apache/rocketmq "RocketMQ"): distributed message middleware hiệu năng cao, throughput cao do Alibaba mã nguồn mở. +2. [Kafka](https://github.com/apache/kafka "Kafaka"): Kafka là hệ thống message phân tán dựa trên mô hình publish/subscribe. +3. [RabbitMQ](https://github.com/rabbitmq "RabbitMQ"): message queue phát triển bằng Erlang, hiện thực dựa trên giao thức AMQP (Advanced Message Queue). +4. [Pulsar](https://github.com/apache/pulsar): nền tảng message streaming phân tán cloud native thế hệ mới. + +Khuyên chọn một trong RocketMQ và Kafka để học sâu, các message queue khác chỉ cần biết sơ. + +Về giới thiệu khái niệm cơ bản và lựa chọn công nghệ message queue, khuyến nghị đọc bài [Tổng hợp kiến thức cơ bản về message queue](https://javaguide.cn/high-performance/message-queue/message-queue.html) tôi viết. + +Gợi ý tài nguyên học Kafka, RocketMQ, RabbitMQ xem bài đăng này của [Knowledge Planet](https://javaguide.cn/about-the-author/zhishixingqiu-two-years.html): . + +### Read/write splitting & sharding (chỉ cần nắm khái niệm và nguyên lý) + +Read/write splitting chủ yếu là để phân bổ thao tác đọc và ghi của database sang các node database khác nhau. Master server chịu trách nhiệm ghi, slave server chịu trách nhiệm đọc. Ngoài ra, một master một slave hoặc một master nhiều slave đều được. + +Read/write splitting có thể tăng mạnh performance đọc, tăng nhẹ performance ghi. Do đó, read/write splitting phù hợp hơn với tình huống một máy có nhiều request đọc đồng thời. + +![Sơ đồ minh họa read/write splitting](https://oss.javaguide.cn/github/javaguide/high-performance/read-and-write-separation-and-library-subtable/read-and-write-separation.png) + +Sharding là để giải quyết vấn đề performance database liên tục giảm do lượng dữ liệu trong database và table quá lớn. + +Các công cụ sharding thường gặp: sharding-jdbc (Dangdang), TSharding (Mogujie), MyCAT (dựa trên Cobar), Cobar (Alibaba)... Khuyến nghị dùng sharding-jdbc, vì sharding-jdbc là framework Java nhẹ, cung cấp dịch vụ dưới dạng jar, không cần chúng ta làm thêm việc vận hành, và tính tương thích cũng rất tốt. + +![Sharding](https://oss.javaguide.cn/java-guide-blog/662ea3bda90061d0b40177e3a46fefc3.jpg) + +Hiện nay nhiều công ty dùng relational database phân tán kiểu như TiDB, không cần chúng ta tự tay sharding, do đó chúng ta chỉ cần nắm các khái niệm và nguyên lý thường gặp về read/write splitting & sharding là được, không cần bỏ quá nhiều thời gian thực hành. Khuyến nghị đọc bài [Tổng hợp các vấn đề thường gặp về read/write splitting & sharding](https://javaguide.cn/high-performance/read-and-write-separation-and-library-subtable.html) tôi viết. + +### Load balancing + +Hệ thống load balancing thường dùng để phân bổ các task như xử lý request của user sang nhiều server xử lý nhằm nâng cao performance và độ tin cậy của website, ứng dụng hoặc database. + +Trong quá trình phát triển, load balancing mà chúng ta tiếp xúc có thể chia đơn giản thành **server-side load balancing** và **client-side load balancing**. Server-side load balancing có thể hiện thực bằng phần cứng (như F5, A10, Array) hoặc phần mềm (như LVS, Nginx, HAproxy). Các framework microservice chủ đạo trong lĩnh vực Java như Dubbo, Spring Cloud đều tích hợp sẵn client-side load balancing dùng được ngay. Dubbo mặc định đã mang theo chức năng load balancing, Spring Cloud hiện thực load balancing dưới dạng component, thuộc diện tùy chọn; thường dùng là Spring Cloud Load Balancer (chính thức, khuyến nghị) và Ribbon (Netflix, đã bị deprecated). + +Cá nhân tôi khuyên học Nginx và Spring Cloud Load Balancer. + +Các khái niệm, thuật toán và giải pháp kỹ thuật thường gặp về load balancing có thể xem bài này: [Tổng hợp các vấn đề thường gặp về load balancing](https://javaguide.cn/high-performance/load-balancing.html). + +## High availability (nâng cao) + +High availability mô tả việc một hệ thống trong phần lớn thời gian đều khả dụng, có thể phục vụ chúng ta. High availability nghĩa là ngay cả khi xảy ra lỗi phần cứng hoặc khi nâng cấp hệ thống, dịch vụ vẫn khả dụng. + +### Rate limiting & degradation & circuit breaker + +Rate limiting là cách ứng phó với sự cố hệ thống từ góc độ áp lực truy cập của user. Rate limiting nhằm giới hạn tần suất API phía server nhận request, tránh cho service bị sập. Ví dụ giới hạn một API chỉ nhận 100 request mỗi giây, các request vượt giới hạn thì bỏ qua hoặc đưa vào queue chờ xử lý. Rate limiting có thể ứng phó hiệu quả với lượng request đột biến quá lớn. + +Về giới thiệu rate limiting cho service, khuyến nghị đọc bài [Giải thích chi tiết rate limiting cho service](https://javaguide.cn/high-availability/limit-request.html) tôi viết, trong đó có giới thiệu các thuật toán rate limiting thường gặp cũng như giải pháp rate limiting đơn máy và phân tán. + +Degradation là cách ứng phó với sự cố hệ thống từ góc độ mức độ ưu tiên của chức năng. Service degradation nghĩa là khi áp lực server tăng đột biến, dựa theo tình hình nghiệp vụ và lưu lượng hiện tại mà giảm cấp có chiến lược một số service và trang, qua đó giải phóng tài nguyên server để đảm bảo các task cốt lõi chạy bình thường. + +Circuit breaker và degradation là hai khái niệm khá dễ nhầm lẫn, ý nghĩa của chúng không giống nhau. Mục đích của degradation là ứng phó với sự cố của chính hệ thống mình, còn mục đích của circuit breaker là ứng phó với sự cố của hệ thống bên ngoài hoặc hệ thống bên thứ ba mà hệ thống hiện tại phụ thuộc. + +[Hystrix](https://github.com/Netflix/Hystrix "Hystrix") do Netflix mã nguồn mở và [Sentinel](https://github.com/alibaba/Sentinel "Sentinel") do Alibaba mã nguồn mở đều hiện thực được rate limiting, degradation, circuit breaker. Tuy nhiên, Hystrix đã ngừng bảo trì, khuyến nghị dùng Sentinel với tính năng mạnh hơn. Ngoài ra, Wiki của Sentinel có so sánh các component rate limiting và degradation thường dùng, bạn nào quan tâm có thể xem, đường dẫn: [So sánh các component rate limiting và degradation thường dùng](https://github.com/alibaba/Sentinel/wiki/常用限流降级组件对比). + +[Wiki của Sentinel đã mô tả chi tiết khác biệt giữa nó và Hystrix](https://github.com/alibaba/Sentinel/wiki/Sentinel-与-Hystrix-的对比), bạn có thể xem thử. + +Học Sentinel thì tài liệu chính thức nhất định phải xem: . + +Ngoài ra, giới thiệu thêm một số tài liệu học tôi thấy khá tốt: + +- [Sentinel — thần khí rate limiting của Alibaba, 17 câu hỏi liên hoàn chí mạng? - Bất Tài Trần Mỗ](https://mp.weixin.qq.com/s/w8lhJfhLdh7POpPw2MyPwA) +- [Sentinel tại sao mạnh thế, tôi đã moi ra nguyên lý hiện thực phía sau - Nhật ký java của Tam Hữu](https://mp.weixin.qq.com/s/FewOTrevjiCfooVIVwo4Xg) +- [Thiết kế thuật toán sliding window trong flow control của Sentinel - Lão Chu bàn kiến trúc](https://mp.weixin.qq.com/s/Q3C3DxtCJvTE5CCl3EWF9w) + +### Xếp hàng + +Một dạng rate limiting khác, tương tự như xếp hàng ngoài đời thực. Bạn nào chơi Liên Minh Huyền Thoại chắc có trải nghiệm này, mỗi lần có sự kiện là phải xếp hàng một lúc mới vào được game. + +Có nhiều cách hiện thực xếp hàng, ví dụ chúng ta có thể nhờ message queue, các blocking queue trong JDK. + +### Cluster + +Triển khai nhiều bản của cùng một service, tránh single point of failure. + +### Cơ chế timeout và retry + +**Một khi request của user quá một khoảng thời gian nào đó mà không nhận được phản hồi thì kết thúc request đó và ném exception.** Nếu không đặt timeout có thể dẫn đến tốc độ phản hồi request chậm, thậm chí khiến request dồn ứ và làm hệ thống không xử lý được request nữa. + +Ngoài ra, số lần retry thường đặt là 3 lần, retry nhiều hơn nữa không có lợi mà ngược lại còn làm nặng thêm áp lực cho server (một số tình huống dùng cơ chế retry khi thất bại sẽ không phù hợp lắm). + +## Cloud native (tùy chọn) + +> **Gợi ý**: phát triển cloud native đòi hỏi năng lực khá cao, vị trí Java backend thường cũng không yêu cầu kỹ năng phát triển cloud native. Do đó, phần nội dung này không khuyến nghị cho các bạn không hứng thú hoặc không hiểu về phát triển cloud native, có thể bỏ qua. + +Cloud native là một bộ hệ thống kỹ thuật và phương pháp luận hoàn chỉnh để xây dựng, chạy ứng dụng trên cloud. Hệ thống kỹ thuật và phương pháp luận ở đây, tính đến hiện tại, chỉ microservice + DevOps + continuous delivery + container hóa. + +Ngày càng nhiều ngôn ngữ lập trình, framework bắt đầu đón nhận cloud native, ví dụ Spring đưa ra công nghệ hướng cloud native là Spring Native, RedHat mã nguồn mở framework service cloud native cho Java là Quarkus. + +Nếu bạn khá hứng thú với lĩnh vực cloud native, khuyên bạn tập trung vào các công nghệ sau: + +1. Microservice: SpringCloud hay SpringCloud Alibaba thực ra không cần học, trong cloud native thường xây dựng microservice dựa trên Kubernetes được nhắc phía sau. +2. Gateway: gateway là cửa ngõ lưu lượng của toàn bộ kiến trúc microservice, chịu trách nhiệm authentication & authorization, phân phối request, rate limiting, quản lý API, load balancing, là một component rất quan trọng trong kiến trúc microservice. Do đó, tôi tách riêng gateway ra để nhắc đến ở đây. +3. Log và monitoring cảnh báo: Metrics (nhờ nó chúng ta có thể vẽ ra các bảng điều khiển trực quan trong Grafana, hiểu toàn diện hơn trạng thái vận hành của hệ thống), Trace (nhờ nó chúng ta có thể dựng ra toàn cảnh việc gọi của hệ thống), Logs (một số log cần thiết). +4. Container: công nghệ container là nền tảng phát triển của cloud native, các công cụ container đứng đầu là Docker đã đưa ra khẩu hiệu "build một lần, chạy mọi nơi". +5. Kubernetes: K8s được gọi là hệ điều hành của thời đại cloud native, ưu thế của ứng dụng cloud native gắn liền với các chức năng mà nó cung cấp. +6. DevOps: DevOps quan tâm đến việc tự động hóa quản lý toàn bộ vòng đời ứng dụng (phát triển, test, vận hành), từ đó đạt được việc giao phần mềm nhanh hơn, chất lượng hơn, thường xuyên hơn và ổn định hơn. Nhóm DevOps thường dùng kiến trúc microservice để xây dựng ứng dụng, nhờ continuous integration và continuous deployment (CI/CD) để triển khai DevOps. +7. ServiceMesh: bạn có thể xem Service Mesh như một tầng được trừu tượng hóa riêng để đơn giản hóa công việc phát triển, thường được tích hợp như một tầng trong suốt vào ứng dụng phân tán hiện có. +8. …… + +Trong đó, quan trọng hơn cả là Kubernetes. Nếu bạn làm project, khuyên ưu tiên cân nhắc các project liên quan đến Kubernetes. + +Trước đây tôi có viết một bài giới thiệu về cloud native, bạn có thể xem: [Thời đại cloud native, lập trình viên nên nắm những năng lực nào?](https://mp.weixin.qq.com/s/ZVbwNnvRwXxQqk7A-OA27g). + +Ngoài ra, còn khuyến nghị xem bài này: [Kiến trúc cloud native năm 2024 cần những tech stack nào](https://crossoverjie.top/2024/04/11/ob/2024-cloud-native/). + +## AI application development (lộ trình mở rộng) + +AI đã trở thành một phần trong hệ thống năng lực của Java backend, nhưng không khuyến nghị ngay từ đầu nhét nó vào tuyến chính Java rồi học gồng. Nhịp độ chắc chắn hơn là: xây vững Java Basics, Spring, database, cache, distributed và thực chiến project trước, rồi theo lộ trình dưới đây bổ sung AI application development một cách có hệ thống. + +- [Lộ trình học AI application development và Agent cho developer Java/Go (bản mới nhất 2026)](./java-to-ai-roadmap.md): dành cho backend developer, chia nhỏ lộ trình học theo nền tảng LLM, LLM API, Prompt, RAG, Agent, kỹ thuật hóa và thực chiến project. +- [Gợi ý học chuyển hướng từ backend sang AI Agent (bản mới nhất 2026)](./backend-to-ai-agent-roadmap.md): nếu bạn chưa chắc có nên chuyển sang AI không, chọn Java AI hay Python AI, có thể ứng tuyển vị trí nào, hãy xem bài này trước. +- [Hệ thống kiến thức AI application development](../ai/): lối vào các bài viết hệ thống ngoài lộ trình học, bao phủ nền tảng LLM, Agent, RAG, MCP, Prompt engineering, đánh giá và AI system design. +- [Hướng dẫn thực hành AI coding](../ai-coding/): lộ trình tăng hiệu suất code hằng ngày, tập trung vào Claude Code, Codex, AI IDE, CLI Agent, quản lý context và workflow phát triển có AI hỗ trợ. + +Nếu bạn chỉ đang chuẩn bị phỏng vấn Java backend, phần AI này có thể tìm hiểu khái niệm cơ bản trước; nếu mục tiêu là AI application development hay Agent engineer, khuyên đi thẳng theo lộ trình học AI application development ở trên. + +## Tổng kết + +Đây là một lộ trình học rất chi tiết; học xong những nội dung trên, tìm được một công việc tương đối tốt đã khá dễ dàng. + +Ngoài ra, như tôi đã nói ở trên, nếu bạn thấy nội dung hơi nhiều không học nổi, hoặc nếu bạn chỉ muốn tìm một công việc ở công ty nhỏ, hãy tập trung vào Java Basics, database, các framework thường dùng và các công cụ thường dùng. + +Còn những điểm kiến thức như JVM, distributed, high concurrency, high availability, microservice, nếu bạn muốn vào công ty lớn hoặc muốn cạnh tranh tốt hơn khi tìm việc thì cũng cần bỏ thêm thời gian học. + +Phỏng vấn bây giờ rất cạnh tranh, muốn tìm được công việc tốt thì bạn cần học nhiều hơn, luyện nhiều hơn. Tuy nhiều kiến thức bạn học hiện tại có thể sẽ không dùng đến khi đi làm, nhưng vòng sàng lọc phỏng vấn lại đòi hỏi bạn phải biết. Suy cho cùng, nhiều vị trí có rất nhiều người cùng cạnh tranh, để đạt hiệu quả sàng lọc thì độ khó phỏng vấn thường khá cao. Đây chính là cái gọi là: "phỏng vấn thì chế tên lửa, vào làm thì vặn ốc vít". + +## Tài khoản công khai (khuyến nghị) + +Bản cập nhật mới nhất của lộ trình học sẽ được đồng bộ sớm nhất lên tài khoản công khai, khuyến nghị mọi người theo dõi! + +![Tài khoản công khai chính thức JavaGuide](https://oss.javaguide.cn/github/javaguide/gongzhonghaoxuanchuan.png) + +## Knowledge Planet + +Để giúp nhiều bạn hơn chuẩn bị phỏng vấn Java cũng như học Java, tôi đã tạo một [Knowledge Planet về phỏng vấn Java](https://javaguide.cn/about-the-author/zhishixingqiu-two-years.html) thuần túy. Tuy học phí chỉ bằng một phần trăm lớp đào tạo/bootcamp, nhưng nội dung trong Knowledge Planet chất lượng cao hơn, dịch vụ cũng toàn diện hơn, rất phù hợp cho các bạn đang chuẩn bị phỏng vấn và học Java. + +**Hoan nghênh các bạn đang chuẩn bị phỏng vấn Java và học Java tham gia [Knowledge Planet](https://javaguide.cn/about-the-author/zhishixingqiu-two-years.html) của tôi, nội dung giá trị rất nhiều, không khí học tập cũng rất tốt! Tuy giá rẻ nhưng nội dung trong đó có thể còn chất lượng hơn lớp đào tạo hàng chục nghìn tệ bạn tham gia.** + +[![Dịch vụ của Knowledge Planet](https://oss.javaguide.cn/xingqiu/xingqiufuwu.png)](https://javaguide.cn/about-the-author/zhishixingqiu-two-years.html) diff --git a/vi/snippets/article-footer.snippet.md b/vi/snippets/article-footer.snippet.md new file mode 100644 index 00000000000..3ceaa6a3252 --- /dev/null +++ b/vi/snippets/article-footer.snippet.md @@ -0,0 +1,7 @@ +## Lời cuối + +Nếu nội dung hữu ích với bạn, hãy tiện tay tặng JavaGuide một Star miễn phí để ủng hộ: [GitHub](https://github.com/Snailclimb/JavaGuide) | [Gitee](https://gitee.com/SnailClimb/JavaGuide). + +JavaGuide đã được duy trì gần bảy năm, tích lũy **6100+** commit, với sự đóng góp hoàn thiện từ **620+** người. Star, phản hồi và PR của bạn đều là động lực để dự án tiếp tục cập nhật. + +Nếu bạn đang chuẩn bị phỏng vấn backend / AI application development, có thể tham khảo [Knowledge Planet](https://javaguide.cn/about-the-author/zhishixingqiu-two-years.html) của tôi, bao gồm project thực chiến backend và AI, tối ưu CV, hỏi đáp 1-1 và tài liệu các điểm thi tần suất cao, đã duy trì liên tục sáu năm. diff --git a/vi/snippets/article-header.snippet.md b/vi/snippets/article-header.snippet.md new file mode 100644 index 00000000000..a9816005a6e --- /dev/null +++ b/vi/snippets/article-header.snippet.md @@ -0,0 +1 @@ +[![《Nền tảng phỏng vấn thông minh SpringAI + RAG Knowledge Base》](https://oss.javaguide.cn/xingqiu/interview-guide-banner.png)](../zhuanlan/interview-guide.md) diff --git a/vi/snippets/planet.snippet.md b/vi/snippets/planet.snippet.md new file mode 100644 index 00000000000..db5d07c5512 --- /dev/null +++ b/vi/snippets/planet.snippet.md @@ -0,0 +1,38 @@ +Cuốn [《Java Interview Guide》](../zhuanlan/java-mian-shi-zhi-bei.md) (dùng chung cho phỏng vấn backend) đã được mài giũa nhiều lần, chất lượng rất cao, nhằm giúp mọi ứng viên Java/backend tự tin đối mặt với thử thách phỏng vấn. + +**Nói bằng số liệu:** Tính đến hiện tại, chuyên mục đã đạt **477.1W** lượt đọc, **5,118** lượt thích, **1,657** bình luận tương tác. Đáng nói là khu bình luận không chỉ là nơi để lại lời nhắn mà còn là khu hỏi đáp — gần như mọi câu hỏi tôi đều trả lời tận tâm, đảm bảo không bỏ sót thắc mắc nào. + +![](https://oss.javaguide.cn/xingqiu/java-interview-guide-statistics-2025.png) + +Một phần nội dung của [《Java Interview Guide》](../zhuanlan/java-mian-shi-zhi-bei.md) (nhấn vào link để xem giới thiệu chi tiết) được trình bày bên dưới, bạn có thể xem nó như phần bổ sung hoàn thiện cho [JavaGuide](https://javaguide.cn/#/), hai bên dùng kết hợp với nhau. + +![Tổng quan nội dung 《Java Interview Guide》](https://oss.javaguide.cn/javamianshizhibei/javamianshizhibei-content-overview.png) + +Dưới đây là một phần phản hồi thực tế của các thành viên về [《Java Interview Guide》](../zhuanlan/java-mian-shi-zhi-bei.md): + +![Phản hồi thực tế của một số thành viên về 《Java Interview Guide》](https://oss.javaguide.cn/xingqiu/praise-that-the-mianshizhibei-received.png) + +Nếu bạn cần hỗ trợ phỏng vấn (ví dụ tối ưu CV, hỏi đáp mô phỏng 1-1, tài liệu tủ các điểm thi tần suất cao), hoan nghênh tìm hiểu [Knowledge Planet](../about-the-author/zhishixingqiu-two-years.md) của tôi. Đã duy trì liên tục sáu năm, nội dung cập nhật thường xuyên, giá rẻ (0.4 tệ/ngày) nhưng chất lượng cao, dịch vụ cũng toàn diện. + +Dưới đây là một phần dịch vụ mà Knowledge Planet cung cấp (nhấn vào ảnh bên dưới để xem giới thiệu chi tiết): + +[![Dịch vụ của Knowledge Planet](https://oss.javaguide.cn/xingqiu/xingqiufuwu.png)](../about-the-author/zhishixingqiu-two-years.md) + +Dưới đây là một phần đánh giá tốt nhận được trong năm nay, mỗi dòng đều là thật. Tôi thấy nhiều lớp đào tạo hoặc trung tâm bịa ra những đánh giá không tồn tại để lừa người khác mua dịch vụ giá cao (rất phổ biến trong ngành), thật khó hiểu. + +![Đánh giá thực tế của thành viên về Knowledge Planet](https://oss.javaguide.cn/xingqiu/praise-that-the-planet-received.png) + +**Tôi có nguyên tắc riêng, không lừa gạt, làm nội dung tận tâm, thực lòng mong giúp được bạn!** Nếu bạn quan tâm, hãy dành khoảng 3 phút xem giới thiệu chi tiết: [Giới thiệu chi tiết JavaGuide Knowledge Planet](../about-the-author/zhishixingqiu-two-years.md). + +Ở đây tặng thêm một phiếu giảm giá **30** tệ dành riêng cho Knowledge Planet, số lượng có hạn (giá sắp tăng. Người dùng cũ gia hạn được giảm nửa giá, quét mã WeChat để gia hạn)! + +![Phiếu giảm giá 30 tệ của Knowledge Planet](https://oss.javaguide.cn/xingqiu/xingqiuyouhuijuan-30.jpg) + +🚀 **Việc bắt buộc làm khi vào nhóm** (toàn nội dung giá trị, nhất định phải xem!): + +1. [Hướng dẫn sử dụng Knowledge Planet](https://t.zsxq.com/0d18KSarv) +2. [Tổng hợp các chủ đề chất lượng](https://t.zsxq.com/12uSKgTIm) + +**Không chiêu trò, không có khoản thu phí tiềm ẩn. Làm nội dung tận tâm, không lừa gạt!** + +Tuy nhiên, **hãy chắc chắn là bạn cần rồi mới tham gia**. Ngoài ra, **trong vòng ba ngày nếu thấy nội dung không hài lòng thì được hoàn tiền toàn bộ**. diff --git a/vi/snippets/planet2.snippet.md b/vi/snippets/planet2.snippet.md new file mode 100644 index 00000000000..b2f9c3ba4f6 --- /dev/null +++ b/vi/snippets/planet2.snippet.md @@ -0,0 +1,34 @@ +## Các tài nguyên khác của Knowledge Planet + +[Knowledge Planet](../about-the-author/zhishixingqiu-two-years.md) ngoài việc cung cấp nhiều cuốn sách nhỏ độc quyền như **《Java Interview Guide》**, **《Java Must-Read Source Code Series》** (hiện đã tổng hợp source code của Dubbo 2.6.x, Netty 4.x, SpringBoot 2.1), **《Handwritten RPC Framework》**, **《Tổng hợp câu hỏi phỏng vấn/kiến thức Kafka thường gặp》**, còn có hoạt động đọc sách, điểm danh học tập, sửa CV, hỏi đáp miễn phí, kho tài nguyên phỏng vấn Java chất lượng và nhiều phúc lợi không định kỳ. + +![Tổng quan các chuyên mục của Knowledge Planet](https://oss.javaguide.cn/xingqiu/image-20220211231206733.png) + +![Sổ tay phỏng vấn PDF của Knowledge Planet](https://oss.javaguide.cn/xingqiu/image-20220723120918434.png) + +Dưới đây là một phần dịch vụ mà Knowledge Planet cung cấp (nhấn vào ảnh bên dưới để xem giới thiệu chi tiết): + +[![Dịch vụ của Knowledge Planet](https://oss.javaguide.cn/xingqiu/xingqiufuwu.png)](../about-the-author/zhishixingqiu-two-years.md) + +Dưới đây là một phần đánh giá tốt nhận được trong năm nay, mỗi dòng đều là thật. Tôi thấy nhiều lớp đào tạo hoặc trung tâm bịa ra những đánh giá không tồn tại để lừa người khác mua dịch vụ giá cao (rất phổ biến trong ngành), thật khó hiểu. + +![Đánh giá thực tế của thành viên về Knowledge Planet](https://oss.javaguide.cn/xingqiu/praise-that-the-planet-received.png) + +**Tôi có nguyên tắc riêng, không lừa gạt, làm nội dung tận tâm, thực lòng mong giúp được bạn!** Nếu bạn quan tâm, hãy dành khoảng 3 phút xem giới thiệu chi tiết: [Giới thiệu chi tiết JavaGuide Knowledge Planet](../about-the-author/zhishixingqiu-two-years.md). + +## Tham gia Knowledge Planet (ưu đãi có thời hạn) + +Đã duy trì liên tục **sáu năm**, nội dung cập nhật thường xuyên, tuy giá rẻ (**0.4 tệ/ngày**) nhưng chất lượng rất cao! + +Hiện Knowledge Planet đang có chương trình khuyến mãi, chỉ với giá hai cuốn sách bạn đã có được dịch vụ của lớp đào tạo hàng chục nghìn tệ! Ở đây tặng thêm một phiếu giảm giá **30** tệ (giá sắp tăng, người dùng cũ quét mã gia hạn được giảm nửa giá): + +![Phiếu giảm giá 30 tệ của Knowledge Planet](https://oss.javaguide.cn/xingqiu/xingqiuyouhuijuan-30.jpg) + +🚀 **Việc bắt buộc làm khi vào nhóm** (toàn nội dung giá trị, nhất định phải xem!): + +1. [Hướng dẫn sử dụng Knowledge Planet](https://t.zsxq.com/0d18KSarv) +2. [Tổng hợp các chủ đề chất lượng](https://t.zsxq.com/12uSKgTIm) + +**Không chiêu trò, không có khoản thu phí tiềm ẩn. Làm nội dung tận tâm, không lừa gạt!** + +Tuy nhiên, **hãy chắc chắn là bạn cần rồi mới tham gia**. Ngoài ra, **trong vòng ba ngày nếu thấy nội dung không hài lòng thì được hoàn tiền toàn bộ**. diff --git a/vi/snippets/rag-project.snippet.md b/vi/snippets/rag-project.snippet.md new file mode 100644 index 00000000000..8c009a04a70 --- /dev/null +++ b/vi/snippets/rag-project.snippet.md @@ -0,0 +1,26 @@ +## ⭐️ Gợi ý project RAG thực chiến + +Giới thiệu một project thực chiến mã nguồn mở của tác giả, xây trên Spring Boot 4.0 + Java 21 + Spring AI + PostgreSQL + pgvector + RustFS + Redis, hiện thực các chức năng cốt lõi như phân tích CV thông minh, phỏng vấn mô phỏng bằng AI, truy vấn RAG trên knowledge base. Rất phù hợp làm project học tập và project ghi vào CV, ngưỡng học tương đối thấp. + +**Kiến trúc hệ thống như sau**: + +![Sơ đồ kiến trúc hệ thống](https://oss.javaguide.cn/xingqiu/pratical-project/interview-guide/interview-guide-architecture-diagram.png) + +**Ảnh minh họa:** + +![Sinh câu hỏi theo Skill + phân tích JD](https://oss.javaguide.cn/xingqiu/pratical-project/interview-guide/page-skill-jd-parse.png) + +![Chi tiết phân tích CV](https://oss.javaguide.cn/xingqiu/pratical-project/interview-guide/page-resume-analysis-detail.png) + +Toàn bộ code mã nguồn mở hoàn toàn miễn phí, không có bản Pro hay bản trả phí! + +**Địa chỉ project** (hoan nghênh Star ủng hộ): + +- Github: +- Gitee: + +Giới thiệu chi tiết project và giáo trình học hệ thống (dành riêng cho Knowledge Planet, rất đáng giá): [《Nền tảng phỏng vấn thông minh SpringAI + RAG Knowledge Base》](https://javaguide.cn/zhuanlan/interview-guide.html). + +Nội dung được sắp xếp như sau (đã cập nhật xong, tổng cộng 18w+ chữ) + +![Tổng quan nội dung giáo trình đi kèm](https://oss.javaguide.cn/xingqiu/pratical-project/interview-guide/tutorial-overview.png) diff --git a/vi/snippets/small-advertisement.snippet.md b/vi/snippets/small-advertisement.snippet.md new file mode 100644 index 00000000000..94579506397 --- /dev/null +++ b/vi/snippets/small-advertisement.snippet.md @@ -0,0 +1,11 @@ + + Knowledge Planet chính thức của JavaGuide + diff --git a/vi/snippets/yuanma.snippet.md b/vi/snippets/yuanma.snippet.md new file mode 100644 index 00000000000..ce67fb6b4e6 --- /dev/null +++ b/vi/snippets/yuanma.snippet.md @@ -0,0 +1,25 @@ +Một phần nội dung của [《Java Must-Read Source Code Series》](../zhuanlan/source-code-reading.md) (nhấn vào link để xem giới thiệu chi tiết) được trình bày bên dưới. + +![《Java Must-Read Source Code Series》](https://oss.javaguide.cn/xingqiu/image-20220621091832348.png) + +Để giúp nhiều bạn hơn chuẩn bị phỏng vấn Java cũng như học Java, tôi đã tạo một [Knowledge Planet về phỏng vấn Java](../about-the-author/zhishixingqiu-two-years.md) thuần túy. Tuy học phí chỉ bằng một phần trăm lớp đào tạo/bootcamp, nhưng nội dung trong Knowledge Planet chất lượng cao hơn, dịch vụ cũng toàn diện hơn, rất phù hợp cho các bạn đang chuẩn bị phỏng vấn và học Java. + +**Hoan nghênh các bạn đang chuẩn bị phỏng vấn Java và học Java tham gia [Knowledge Planet](../about-the-author/zhishixingqiu-two-years.md) của tôi, nội dung giá trị rất nhiều, không khí học tập cũng rất tốt! Tuy giá rẻ nhưng nội dung trong đó có thể còn chất lượng hơn lớp đào tạo hàng chục nghìn tệ bạn tham gia.** + +Dưới đây là một phần dịch vụ mà Knowledge Planet cung cấp (nhấn vào ảnh bên dưới để xem giới thiệu chi tiết): + +[![Dịch vụ của Knowledge Planet](https://oss.javaguide.cn/xingqiu/xingqiufuwu.png)](../about-the-author/zhishixingqiu-two-years.md) + +**Tôi có nguyên tắc riêng, không lừa gạt, làm nội dung tận tâm, thực lòng mong giúp được bạn!** + +Nếu bạn quan tâm, hãy dành khoảng 3 phút xem giới thiệu chi tiết: [Giới thiệu chi tiết JavaGuide Knowledge Planet](../about-the-author/zhishixingqiu-two-years.md). + +Ở đây tặng thêm một phiếu giảm giá **30** tệ dành riêng cho Knowledge Planet, số lượng có hạn (giá sắp tăng. Người dùng cũ gia hạn được giảm nửa giá, quét mã WeChat để gia hạn)! + +![Phiếu giảm giá 30 tệ của Knowledge Planet](https://oss.javaguide.cn/xingqiu/xingqiuyouhuijuan-30.jpg) + +Sau khi vào Knowledge Planet, nhớ xem **[Hướng dẫn sử dụng Knowledge Planet](https://t.zsxq.com/0d18KSarv)** (nhất định phải xem!!!) và **[Tổng hợp các chủ đề chất lượng của Knowledge Planet](https://t.zsxq.com/12uSKgTIm)**. + +**Không chiêu trò, không có khoản thu phí tiềm ẩn. Làm nội dung tận tâm, không lừa gạt!** + +Tuy nhiên, **hãy chắc chắn là bạn cần rồi mới tham gia**. Ngoài ra, **trong vòng ba ngày nếu thấy nội dung không hài lòng thì được hoàn tiền toàn bộ**.