Blog

  • Software Architecture: Bắt đầu từ đâu? –  Phần cuối: Hard skills và tổng kết

    Software Architecture: Bắt đầu từ đâu? – Phần cuối: Hard skills và tổng kết

    Như mình đã nói ở phần đầu hard skill của developer và software architect khá rõ ràng về mặt tiếp cận, có nhiều nguồn tài liệu để đọc mình có thể list ra ở đây

    • Domain-Driven Design: Tackling Complexity in the Heart of Software
    • Software Architecture for Developers
    • Software Systems Architecture
    • Design It! – From Programmer to Software Architect
    • Software Architecture in Practice
    • Design Patterns – Elements of Reusable Object-Oriented Software
    • Domain Driven Design
    • Systems Architecting: Creating & Building Complex Systems
    • Systemantics: How Systems Work and Especially How They Fail
    • Release It!: Design and Deploy Production-Ready Software (Pragmatic Programmers)
    • The Clean Architecture
    • Patterns of Enterprise Application Architecture
    • The Art of Scalability: Scalable Web Architecture, Processes, and Organizations for the Modern Enterprise
    • Martin Fowler – Patterns of Enterprise Application Architecture (2002)

    Trong đó quyển của Martin Fowler khá đầy đủ và chi tiết.

    Vậy chúng ta cần đọc bao nhiêu sách, biết bao nhiêu loại pattern? Câu trả lời luôn là càng nhiều càng tốt

    Chọn architecture cho dự án

    Hay nói đúng hơn là khi bắt đầu dự án, chúng ta sẽ chọn kiến trúc nào: microservice, event-driven hay layered theo cách truyền thống. Đấy là câu hỏi thường gặp và chả có gì sai khi hỏi như vậy cả vì ai cũng biết rằng phần mềm mà đưa ra thị trường kiểu gì chẳng được thiết kế theo kiến trúc nào đó. Tuy nhiên câu trả lời lại thường khiến chúng ta bất ngờ

    Big bang architecture

    Nghe giống như Big Bang Model (một loại mô hình phát triển phần mềm), và đúng là như thế.

    Khi dự án mới bắt đầu, chúng ta chưa hiểu gì về hệ thống cả, chỉ nên thiết kế và làm kiến trúc chỉ những gì cần thiết. Ví dụ chúng ta chỉ nên quyết định có thể cần integrate 2 hệ thống với nhau mà chưa cần quyết định xem sẽ làm việc đó như nào. Về cơ bản, chúng ta cần tiến hóa architecture theo dự án: những đều chúng ta biết thêm về hệ thống.

    Rất lạ là yêu cầu phần mềm, công nghệ có thể sử dụng và đã sử dụng và business của công ty cũng như khách hàng cần thay đổi hằng ngày, nhưng chúng ta thường nghĩ rằng architecture cần phải cố định.

    Thường thì mình sẽ chọn một architecture vừa đủ dùng trong một thời gian phù hợp với dự án và đội phát triển (tất nhiên không phải là phần mềm kiểu enterprise rồi)

    Và gần như kiến trúc này sẽ được cải tiến cùng với CI/CD trong phần trước đó, nếu kiến trúc đó có vấn đề khi tích hợp với Pipeline thì có nghĩa rằng kiến trúc hoặc Pipeline hoặc cả 2 đều có vấn đề và chúng cần được cải tiến.

    Một dự án cần phát triển một ứng dụng như Tiki chẳng hạn, nhưng Architect lại được bắt đầu với multi tier truyền thống và bạn sẽ có một monolithic web application, ai cũng sẽ thấy rằng, khi bạn cần sửa dù chỉ một phần nhỏ trong header của request, bạn sẽ cần deploy lại cả hệ thống, điều đó không sai nhưng rõ ràng có vấn đề. Điều tất yếu xảy ra kiến trúc sẽ được cải tiến.

    Gần như hiện tại trong quá trình làm dự án, chúng ta có thể thấy rằng nếu như hệ thống có hình dáng giống với một hệ thống nào đó trong quá khứ thì chúng ta sẽ gần như kế thừa và giữ nguyên, trong khi đó các hệ thống chưa xuất hiện bao giờ thì sẽ được quyết định bởi trực giác của Architect trong một quá trình khám phá có kiểm soát.

    Các quyết định này có thể đúng, sai, tăng tiến độ hoặc thậm chí phá hỏng mọi thứ và chúng ta phải rework.

    Khi đó chúng ta có một thứ gọi là Accidental Architecture, tuy nhiên, điều này chưa hẳn đã xấu vì gần như chúng ta không thể tránh khỏi trong quá trình làm dự án và xây dựng hệ thống. Chỉ khi bắt đầu biến những kiến trúc ngẫu nhiên này thành những kiến trúc có chủ đích, chúng ta mới nâng cao hiểu biết của mình về kiến trúc phần mềm.

    Final words

    Có lẽ serires này sẽ dừng ở đây, mình sẽ cố gắng sắp xếp lại một số bài viết liên quan đến chủ đề này trong nhưng bài viết khác.

    THE END.

  • Sử dụng Nginx để truy cập tới một Private S3 Bucket

    Sử dụng Nginx để truy cập tới một Private S3 Bucket

    Bài toán

    Mình được giao nhiệm vụ phát triển một ứng dụng quản lý chi tiêu cho khách hàng. Thông tin các hoá đơn và ảnh trong ứng dụng sẽ được lưu trên S3. Khách hàng có yêu cầu có thể xem được ảnh lưu trên S3 bằng domain của họ thay vì bằng đường link trực tiếp từ S3. Hệ thống Backend của họ đang sử dụng Nginx với Python Flask và mong muốn không sử dụng đến dịch vụ CloudFront.

    Giải pháp

    Sau hồi nguyên cứu mình đã tìm ra giải pháp là có thể dùng Nginx để truy cập tới một private S3 bucket.

    Để có thể demo được giải pháp nên mình sẽ lược bỏ hệ thống cho nó đơn giản hơn so với hệ thống của khách hàng. Đầu tiên giả sử hệ thống trên AWS đã được cấu hình như bên dưới:

    • VPC(10.0.0.0/16) với public subnet và private subnet
    • VPC Endpoint cho s3
    • A static web on ECS Fargate with Nginx
    • 1 trang web tĩnh được triển khai trên dịch vụ ECS Fargate với việc dùng Nginx
    • 1 private S3 bucket để lưu ảnh của trang web.

    Alt Text

    Kết quả sau khi cấu hình xong chúng ta sẽ có 1 trang web như bên dưới. Nhưng hình ảnh mọi nguời thấy là hình ảnh được tải từ trên mạng. Alt Text

    Bây giờ mình sẽ cần thay thể ảnh trên mạng này với ảnh trên S3. Để làm được điều đó các bạn theo dõi tiếp bên dưới nhé. Alt Text

    Thiết lập Nginx và S3

    1. Thay đổi S3 bucket policy để chỉ cho phép lấy dữ liệu từ VPC Endpoint.

    {
        "Version": "2008-10-17",
        "Id": "PolicyForCloudFrontPrivateContent",
        "Statement": [
            {
                "Sid": "Access-to-specific-VPCE-only",
                "Effect": "Allow",
                "Principal": "*",
                "Action": "s3:GetObject",
                "Resource": "arn:aws:s3:::demo-static-s3/*",
                "Condition": {
                    "StringEquals": {
                        "aws:sourceVpce": "vpce-0d92e50f230bc8070"
                    }
                }
            }
        ]
    }
    

    2. Tiếp theo là việc quan trọng nhất, chúng ta sẽ cần cấu hình Nginx để có thể tải ảnh từ private S3

    location ~^/image/(.+)$ {
            resolver 10.0.0.2;
            proxy_pass http://s3-ap-southeast-1.amazonaws.com/demo-static-s3/image/$1; 
    }
    
    • resolver: Địa chỉ IP của DNS server trong VPC mà mình đã tạo.

      Ví dụ, VPC của mình tạo có CIRD là 10.0.0.0/16. AWS sẽ dùng 5 IPs bên dưới cho các mục đích bên của họ và mình sẽ không thể sử dụng các IP đó.

      • 10.0.0.0: Network address.
      • 10.0.0.1: Reserved by AWS for the VPC router.
      • 10.0.0.2: Reserved by AWS. The IP address of the DNS server is the base of the VPC network range plus two. For VPCs with multiple CIDR blocks, the IP address of the DNS server is located in the primary CIDR. We also reserve the base of each subnet range plus two for all CIDR blocks in the VPC. For more information, see Amazon DNS server.
      • 10.0.0.3: Reserved by AWS for future use.
      • 10.0.0.255: Network broadcast address. We do not support broadcast in a VPC, therefore we reserve this address.

      => Resolver sẽ có giá trị là 10.0.0.2

      Tham khảo: https://docs.aws.amazon.com/vpc/latest/userguide/VPC_Subnets.html

    • proxy_pass: s3 link

      Định dạng: http://s3-ap-southeast-1.amazonaws.com/[main_bucket]/[sub_bucket]/$1

    3. Thay đổi dường dẫn của tải ảnh của trang web

    <div class="container">
        <h1>Hey there</h1>
        <img src="https://cleandevs.com/image/668c1d479e27bb8750823655c83a6c9bd90263f9_hq.jpg"/>
      </div>
    

    4. Cuối cùng mình sẽ deploy lại trang web lên dịch vụ ECS Fargate. Kết quả sẽ như hình bên dưới. Trang web đã hiển thị ảnh được tải từ private S3 bucket

    Alt Text

  • Infrastructure as Code

    Infrastructure as Code

    Lời nói đầu

    Với sự phát triển manh mẽ của Cloud Computing, các ứng dụng mà đơn vị GST đang phát triển được xây dựng trên cloud như AWS ngày càng nhiều. Việc quản lý resource trên cloud cũng là một vấn đề cực kì quan trọng. Hôm nay mình sẽ giới thiệu về Infrastructure as Code(IaC) và Terraform, những công cụ giúp chúng ta quản lý resource cloud dễ dàng hơn.

    Nội dung

    Vấn đề

    Như đã nói ở phía trên, vấn đề chúng ta bàn đến chính là quản lý resouce cloud(AWS, Azure, GPC..). Tại sao lại phải quản lý? Lý do là mỗi dự án sử dụng khoảng 10-15 services, mỗi service có khoảng 1-10 resources, mỗi resource lại có nhiều config khác nhau. Vì vậy nếu không quản lý tốt, sẽ rất dễ bị miss resource, hoặc config giữa các resource khác nhau giữa các môi trường, lúc tạo môi trường mới mất nhiều thời gian để config, compare các môi trường với nhau, rủi ro khi xử lý manual là rất lớn. Vì vậy việc sử dụng tool để quản lý resource là cần thiết và vô cùng quan trọng. Infrastructure as Code đã được sinh ra để giải quyết các vấn đề đó.

    Infrastructure as Code

    Định nghĩa IaC thì mỗi ông định nghĩa 1 kiểu, sau đây mình sẽ trích dẫn định nghĩa mà mình cho là khá dễ hiểu:

    Infrastructure as code (IaC) is the process of managing and provisioning computer data centers through machine-readable definition files, rather than physical hardware configuration or interactive configuration tools. The IT infrastructure managed by this process comprises both physical equipment, such as bare-metal servers, as well as virtual machines, and associated configuration resources. The definitions may be in a version control system. It can use either scripts or declarative definitions, rather than manual processes, but the term is more often used to promote declarative approaches.

    Refer: https://docs.microsoft.com/en-us/devops/deliver/what-is-infrastructure-as-code

    Hiểu một cách đơn giản hơn thì IaC có nghĩa là chúng ta sử dụng các đoạn code theo format có sẵn(giống như 1 ngôn ngữ lập trình hoặc 1 dạng template: yaml, json..) để quản lý các resource của hệ thống. Khi cần thêm, sửa, xoá 1 resource thì update vào code và apply để thay đổi infra.

    Cụ thể IaC giải quyết vấn đề gì?

    • Giải quyết vấn đề đồng bộ giữa các môi trường, không có chuyện môi trường staging 1 kiểu, môi trường prd config lại khác, vì resource cùng sinh ra từ 1 đoạn code
    • Giảm thiểu chi phí xây dựng môi trường. Ban đầu sẽ hơi mất thời gian 1 chút để tạo các resource bằng code, tuy nhiên sau khi dựng xong 1 môi trường hoàn chỉnh, tạo các môi trường khác thì chỉ cần sử dụng lại code đã tạo cho môi trường đầu tiên. Nếu có 1,2 môi trường thì lợi ích có thể ít, nhưng mình đã làm 1 dự án có 9 môi trường, việc này tiết kiệm rất nhiều thời gian
    • Khả năng tái sử dụng: Ví dụ Project A sử dụng VPC, Subnet, EC2 server, Project B cũng sử dụng các resource như vậy, chúng ta có thể tái sử dụng lại source code infra của A, optimize lại 1 chút để sử dụng luôn cho project B
    • Automation: Việc tự động tạo, update, xoá các resource khiến cho việc thục hiện manual được ít đi rất nhiều, giảm thiểu rủi ro do ngứa tay

    Một số Iac tool phổ biến:

    Terraform

    Để dễ hình dung hơn về IaC, mình sẽ giới thiệu về Terraform, một IaC tool khá phổ biến.
    Terraform là gì?

    Terraform là một IaC tool mã nguồn mở, công cụ phần mềm mã cung cấp quy trình làm việc CLI nhất quán để quản lý hàng trăm dịch vụ đám mây. Terraform mã hóa các API đám mây thành các file khai báo cấu hình.

    Một số điểm đáng chú ý:

    • Phát triển bằng Go
    • Phát triển bời HashiCorp, công ty với mục tiêu cách mạng hóa việc quản lý trung tâm dữ liệu: phát triển, phân phối và bảo trì ứng dụng
    • Multi cloud: Support tạo resource cho nhiều cloud provider khác nhau: AWS, Azure, GPC, Oracle..
    • Cung cấp cơ sở hạ tầng trên hơn 300 đám mây và dịch vụ public bằng cách sử dụng một quy trình làm việc duy nhất

    Terraform hoạt động thế nào?

    Terraform cho phép cơ sở hạ tầng được thể hiện dưới dạng mã bằng một ngôn ngữ đơn giản, con người có thể đọc được gọi là HCL (HashiCorp Configuration Language). Nó đọc các file cấu hình và cung cấp một kế hoạch thực hiện các thay đổi, có thể được xem xét để đảm bảo an toàn, sau đó mới áp dụng các thay đổi.

    Các nhà cung cấp có thể mở rộng cho phép Terraform quản lý nhiều loại tài nguyên, bao gồm IaaS, PaaS, SaaS và các dịch vụ phần cứng.

    Ví dụ Terraform và 1 số câu lệnh đơn giản

    Syntax cơ bản của Terraform

    Khi muốn apply code lên Infra của mình, thực hiện câu lệnh

    terraform apply

    Khi muốn xem sự thay đổi của code hiện tại với trạng thái trước khi thay đổi

    terraform plan

    Kết quả sau khi apply thành công

    Export file terraform thành dạng graph sử dụng lệnh

    terraform graph | dot -Tsvg > graph.svg

    Kết quả sẽ là file svg có tên là graph.svg, dùng extension để view file, ta có kết quả như sau

    CICD với terraform

    Terraform là code, thực hiện quản lý, thay đổi tác động đến Infra của hệ thống. Vậy đã là code thì hoàn toàn có thể apply deploy tự động.

    Cơ bản CICD với Terraform cũng giống như IaC với ngôn ngữ khác, thay vì các lệnh build thì chúng ta thay bằng các command của terraform.

    Kết luận

    Bài viết này mình đã giới thiệu về Infrastructure as Code, tại sao lại cần đến nó, lợi ích khi sử dụng. Mình cũng đã giới thiệu ngắn gọn về một IaC tool khá phổ biến là Terraform. Mong rằng bài viết sẽ cho mọi người thấy được tầm quan trọng của IaC và có thể nghiên cứu apply vào dự án của mình.

  • Case study ứng dụng serverless trên AWS phục vụ Olympic Tokyo

    Case study ứng dụng serverless trên AWS phục vụ Olympic Tokyo

    Lời nói đầu

    Ở đâu đó có thể các bạn đã nghe thấy khái niệm serverless hay chạy ứng dụng không mà không cần sử dụng một server nào (non-server). Hiện nay với sự phát triển mạnh mẽ của các nền tảng public cloud như AWS, Azure, Alibaba.., khái niệm serverless đang dần trở nên thân thuộc hơn với những lập trình viên. Tuy nhiên bạn đã bao giờ tự tay xây dựng một hệ thống API mà không phải sử dụng server bao giờ chưa? Theo mình thấy thì hiện tại sự trải nghiệm của các dev với serverless thực sự chưa nhiều, một phần có lẽ do người ta vẫn tin tưởng ở server truyền thống hơn(Cái gì sờ thấy được cũng chắc chắn hơn). Ở loạt bài này, mình sẽ trình bày về một dự án team mình xây dựngAPI 100% sử dụng serverless . Mình sẽ tập trung vào kiến trúc hệ thống, giải thích các thành phần và framework hỗ trợ deploy serverless nhé!

    Nội dung

    Bối cảnh

    Khách hàng của mình đã xây dựng hệ thống trên môi trường AWS, sử dụng EC2 làm server, ngôn ngữ là Java và sử dụng framework là Struts . Hệ thống hiện tại chi phí đang quá lớn (Bao gồm cả chi phí AWS cũng như các chi phí liên quan khác), thời gian sử dụng và chạy job trong ngày là không cố định(do nghiệp vụ), nhiều khi không có người sử dụng cũng như không có job nào chạy nhưng cũng phải trả tiền cho 1 server API và 1 server Job. Khách hàng đã yêu cầu chuyển hệ thống cũ sang serverless và phát triển thêm tính năng dựa trên kiến trúc mới này. Hệ thống này mình xây dựng hoàn toàn trên Amazon Web Service, nên các dịch vụ cứ mặc định là của AWS nhé!

    Mô hình hệ thống Serverless

    Có lẽ nhiều người cũng đã nhìn qua kiến trúc serverless như thế này:



    Đúng, nó là 1 kiến trúc chung thường thấy của 1 serverless system triển khai trên AWS. Flow sẽ là:

    • App call API qua API Gateway
    • API Gateway trigger lambda
    • Lambda query data từ DB, trả về kết quả
    • API Gateway response data cho client

    Bla…Bla..

    Tuy nhiên, để ứng dụng nó vào 1 dự án cụ thể cần nhiều hơn thế này, theo dõi phần tiếp theo nhé!

    Hệ thống Serverless trong thực tế

    Mỗi hệ thống sẽ có những điểm giống và khác nhau tuỳ thuộc vào bài toán cần giải quyết. Không loằng ngoằng mình sẽ đưa ra kiến trúc mình đã xây dựng luôn (Đã lược bỏ một số chi tiết, tập trung chính vào phần serverless)

    Overview hệ thống này nhé:

    • Phần màu đỏ là hosting cho Frontend(được viết bằng Angular xxx). Phần FrontEnd sẽ bao gồm một S3 Bucket được setting làm static web, 1 Cloudfront Distribution để cache lại các resource tĩnh GLOBAL.
    • Phần màu xanh là hệ thống API serverless. Chúng ta sẽ quan tâm đến phần này nhiều hơn vì nó là trọng tâm của bài viết này. Nó bao gồm những dịch vụ gì, đi lần lượt nhé:
      • WAF: Web Application Firewall – Đây được coi là bức tường lửa đầu tiên để bảo vệ web. Nhiêm vụ của nó là bảo vệ app qua rule do người dùng thiết lập, ví dụ Whitelist IP, Blacklist IP… Quan trọng hơn là nó có thể phát hiện và chặn những request có dấu hiệu tấn công như XSS, SQL Injection….
      • API Gateway: Điểm nhận tất cả các request từ phía client. AWS cho phép route từng path của request đến những handler tương ứng.
      • Cognito: Dịch vụ này cung cấp phương thức xác thực, phân quyền và quản lý người dùng.
      • Lambda (Authenticate): Vì app của mình có tính năng authen hơi đặc biệt, do vậy mình phải dùng lambda function này để add thêm 1 số feature mà Cognito không đáp ứng đủ. Lambda function này sẽ được đính trực tiếp vào API Gateway, đóng vai trò tương tự như 1 middleware, cũng đặt trong private subnet nhé, nhưng vẽ như thế để tránh rối
      • VPC, Public subnet và private subnet: Cái này nếu ai đã làm qua với AWS và network của nó thì có thể nắm được rồi. Public subnet thì có thể internet facing, private subet là nơi đặt các server EC2, RDS, Lambda là private. Không thể truy cập trực tiếp từ internet vào các dịch vụ được đặt trong private subnet.
      • InternetGateway cho phép VPC có thể truy cập Internet, VPC Endpoint cho phép kết nối đến các dịch vụ khác của AWS mà ko qua đường truyền internet
      • Squid Proxy Server: Đóng vai trò là proxy cho phép các resource từ private subet kết nối ra ngoài Internet(Nhiều người sẽ dùng NAT Gateway hoặc NAT Instance).
      • Lambda (Đặt trong private subnet): Đây chính là linh hồn của Serverless, đóng vai trò tương tự 1 server. Mỗi path của API Gateway sẽ được xử lý bởi 1 lambda function. Lambda sẽ nhận request từ API Gateway, xử lý, trả response về API Gateway -> Response về Client
      • S3: Nếu ko có server thì file được lưu trữ ở đâu, up/down thế nào? Thông thường nếu hệ thống sử dụng autoscale thì cũng cần 1 nơi lưu trữ file chung (EFS hoặc S3 ….). Với Lambda cũng vậy, mình chọn S3 để lưu trữ file. Nhưng làm thế nào để upload và download file qua lambda nhỉ. Câu trả lời là sẽ không up/download file qua lambda, lambda chỉ là trung gian, generate Pre-signed URL để client thực hiện upload và download trực tiếp với S3.
      • DynamoDB: Đây là 1 database dạng NoSQL do AWS phát triển. Lưu data dạng Key-Value. Nếu cần thiết phải sử dụng CSDL quan hệ, mình khuyến khích dùng AWS Aurora serverless(MySQL hoặc PostgreSQL), hỗ trợ tốt nếu sử dụng serverless
      • CloudWatch: Phần này có 1 số dịch vụ nhỏ hơn. Tuy nhiên có 2 service chính là Logs và Rules. Logs là nơi xem, truy vấn log mà Lambda function đã ghi ra trong quá trình chạy, Rules được sử dụng để lập lịch cho 1 số job chạy cố định hàng ngày, khi đến thời gian nó sẽ gọi lambda function tương ứng.
      • SQS: Queue được dùng cho sử dụng cho những job muốn chạy ngay lập tức. SQS trigger đến Lambda function(Job) mỗi khi có message mới được đẩy vào queue.
      • X-Ray: Service này khá hay, nó giúp monitor ứng dụng một cách chi tiết hơn, visualize nó lên trên dashboard AWS, giúp gỡ lỗi ứng dụng, phán đoán lỗi cũng như cải tiến ứng dụng tốt hơn. Ví dụ: Thời gian query data từ DynamoDb, thời gian upload file S3…….
      • SNS: Gửi notification.

    Serverless framework

    Nếu đã từng làm việc với lambda, mọi người sẽ biết được rằng mỗi Lambda function là độc lập với nhau, source code vì vậy cũng hoàn toàn riêng biệt. Vậy với 1 project lớn bao gồm hàng trăm API, làm thế nào chúng ta quản lý source code và deploy, không thể build và upload bản build cho từng lambda function được. Vì vậy team đã quyết định sử dụng framework là serverless(https://www.serverless.com)

    Serverless framework là fw hỗ trợ nhiều cloud provider phổ biến như AWS, Azure, GPC, Alibaba… Nó cung cấp cho chúntg ta 1 công cụ để quản lý full life cycle cho ứng dụng serverless. Serverless framework cũng hỗ trợ nhiều ngôn ngữ như Java, Nodejs, Go, Python …

    Cấu hình serverless:
    Tư tưởng của framework hiểu đơn giản là chúng ta cần mapping Path của API Gateway với class, file xử lý logic cho function tương ứng.

    Đây là 1 file cấu hình sample của project serverless. Một số thành phần quan trọng bao gồm:

    • provider
      • name: Tên cloud provider(aws, gpc, azure)
      • runtime: Môi trường thực thi(java8, java11, nodejs12..)
    • package:
      • artifact: Đường dẫn trỏ đến file build
    • functions: List API của project
      • Tên lambda function:
        • handler: class xử lý logic cho API
        • events:
          • http: (nếu là HTTP thì lambda function này sẽ được trigger từ API Gateway)
            • path: Đường dẫn API
            • method: HTTP method(get, post …)

    Mình chỉ giới thiệu qua cấu hình cơ bản của serverless. Lợi ích của nó là giúp chúng ta dễ dàng quản lý life cycle của project serverless, sử dụng các architype có sẵn khi tạo project.

    Tổng kết lại

    Túm lại, để nói về 1 serverless system thì 1 bài viết là không đủ. Ở phần này mình chỉ overview hệ thống, các dịch vụ và vai trò của nó . Mong rằng qua bài viết này các bạn có thể nắm được cơ bản về kiến trúc 1 hệ thống không sử dụng server truyền thống nó như thế nào, đánh giá xem có thể apply trực tiếp vào dự án tiếp theo được không. Mong rằng sẽ có nhiều hơn dự án sử dụng serverless trong đơn vị để mọi người có thêm những trải nghiệm mới.

  • Cost-effective, High Availability Cassandra with AWS EKS and EC2 Spot instance.

    Cost-effective, High Availability Cassandra with AWS EKS and EC2 Spot instance.

    Mở đầu

    Cassandra hay Apache Cassandra, là một hệ thống quản lý cơ sở dữ liệu NoSQL, mã nguồn mở, miễn phí, phân tán dựa trên mạng ngang hàng P2P, hiện tại thường dùng dễ lưu trữ dữ liệu dưới dạng timeseries.

    Bản thân Cassandra đã có khả năng High availability với thiết kế no single point of failure và bản thân Cassandra cũng hỗ trợ việc mở rộng node một cách dễ dàng, vậy tại sao không mang sức mạnh của EC2 Spot Instance (chi phí rẻ cho khả năng tính toán lớn).

    Chúng ta lợi dụng một số tính năng sau của Cassandra để xử lý:

    • Data Center

      image

      • Trong đó data center sẽ đóng vai trò như một cụm node, Cassandra có thể live backups giữa các data center, data sẽ tự động copy async sang DC khác, khi một DC down các DC khác vẫn hoạt động bình thường
    • Seed nodes: Seed nodes sẽ là nơi các node mới connect và thông báo về việc chúng join cluster Seed node hoạt động như các điểm chung chuyển, các node sẽ trao đổi với các node seeds hơn các node khác, và các node này thường sẽ có các thông tin mới nhất và đầy đủ nhất, nhưng chúng sẽ gặp vấn đề overhead nên đừng sử dụng mọi node làm seeds.

    • Data replication: Cassandra lưu trữ dữ liệu trên nhiều node để đảm bảo tính toàn vẹn và fault tolerance (mình khá không thích dịch tiếng việt tự này, có thể dịch là khả năng chịu lỗi). Có 2 strategy: Simple và NetworkTopology, vì chúng ta dự định sử dụng data center nên hãy chọn NetworkTopology

    Như vậy về mặt lý thuyết chúng ta có thể xử lý được toàn bộ vấn đề, hãy mapping chúng với, K8S và AWS thậm chí hoàn toàn có thể xử lý được với trường hợp sử dụng Spot Instance.

    image

    Chúng ta sẽ sử dụng luôn khái niệm Availability Zone của AWS cho tương ứng với data center. Như vậy sẽ có 1 Statefulset cài đặt Cassandra, 1 Service để expose với mỗi DC.

    Cài đặt nào

    Thực ra script đã được chuẩn bị ở đây rồi.

    Mình sẽ giải thích một vài điểm cần chú ý

    Chúng ta add label cho các pod, việc này để các service có thể chọn được các pod của cassandra

      template:
        metadata:
          labels:
            app: cassandra
            interface: cassandraa
    

    Chọn node để cài đặt cassandra, chúng ta có thể dùng các key khác nhưng để cho tiện thì mình dùng tạm key này, việc này đảm bảo node của DC được cài đặt theo AZ của AWS đúng tinh thần High availability

          affinity:
            nodeAffinity:
              requiredDuringSchedulingIgnoredDuringExecution:
                nodeSelectorTerms:
                - matchExpressions:
                  - key: topology.kubernetes.io/zone
                    operator: In
                    values:
                    - ap-southeast-1a
    

    Cassandra seeds node, node đầu tiên của các statefulset được chọn làm seed, ở đây mình xử lý dùng 1 service cassandra thay vì dùng 3 service cho 3 AZ (một điểm nho nhỏ khác biệt), việc này không ảnh hưởng lắm.

      - name: CASSANDRA_SEEDS
        value: cassandraa-0.cassandra.thingsboard.svc.cluster.local,cassandrab-0.cassandra.thingsboard.svc.cluster.local,cassandrac-0.cassandra.thingsboard.svc.cluster.local
    

    Chúng ta không cần tất cả các seed cùng một lúc, nên ngay cả khi seed down thì node vẫn hoạt động bình thường.

    Ở các pod sử dụng cassandra này thì cần chỉ rõ DC nào của Cassandra để kết nối đến, ở đây mình đang cài đặt Thingsboard nên sẽ thêm environment variable sau, tất nhiên sẽ phải xử lý tách application của bạn ra 3 statefulset hoặc deployment khác nhau:

      - name: CASSANDRA_LOCAL_DATACENTER
        value: ap-southeast-1a
    

    Tada

    2021-08-17 18:08:36
    2021-08-17 11:08:36,628 [main] INFO  o.s.o.j.LocalContainerEntityManagerFactoryBean - Initialized JPA EntityManagerFactory for persistence unit 'default'
    2021-08-17 18:08:52
    2021-08-17 11:08:52,732 [main] INFO  c.d.o.d.internal.core.ContactPoints - Contact point cassandra:9042 resolves to multiple addresses, will use them all ([cassandra/10.0.1.112, cassandra/10.0.2.149, cassandra/10.0.1.150])
    2021-08-17 18:08:53
    2021-08-17 11:08:53,734 [main] INFO  c.d.o.d.i.c.DefaultMavenCoordinates - DataStax Java driver for Apache Cassandra(R) (com.datastax.oss:java-driver-core) version 4.10.0
    2021-08-17 18:08:54
    2021-08-17 11:08:54,956 [Thingsboard Cluster-admin-0] INFO  c.d.o.d.internal.core.time.Clock - Could not access native clock (see debug logs for details), falling back to Java system clock
    2021-08-17 18:08:56
    2021-08-17 11:08:56,621 [Thingsboard Cluster-admin-0] WARN  c.d.o.d.i.c.l.h.OptionalLocalDcHelper - [Thingsboard Cluster|default] You specified ap-southeast-1b as the local DC, but some contact points are from a different DC: Node(endPoint=cassandra/10.0.1.112:9042, hostId=a56560a6-1274-43f9-b72e-d8b1e7b33bf8, hashCode=6a745db0)=ap-southeast-1a, Node(endPoint=cassandra/10.0.1.150:9042, hostId=c7f9bc1c-c066-40d6-9def-7fbe58af90bb, hashCode=2c4f4fec)=ap-southeast-1a; please provide the correct local DC, or check your contact points
    

    Nếu dòng log cuối gây confuse thì hãy sử dụng các sevice riêng biệt nhé

    Happy Coding

  • Hướng dẫn tạo Swift Package Manager

    Hướng dẫn tạo Swift Package Manager

    Tạo thư viện bằng Swift Package Manager trong Xcode

    Bạn có tò mò một bàn phím gợi ý suggest ra số tiền khi bạn nhập vào được code như thế nào không? Hôm nay mình sẽ hướng dẫn các bạn custom bàn phím gợi ý và public nó lên Swift Package nhé.

    I .Swift Package Manager là gì ?

    Swift Packager Manager là một công cụ giúp quản lý việc phân phối mã nguồn, giúp cho việc chia sẻ và dùng lại code được dễ dàng. Được Apple tích hợp từ Xcode 11 giúp cho việc chúng ta quản lí dependency một cách đơn giản hơn. Mình chỉ nói sơ qua còn các bạn tìm đọc thêm ở https://swift.org/package-manager nhé.

    II .Hướng dẫn tạo Swift Package Manager

    1. Tạo package suggest tiền Việt Nam Đầu tiên chúng ta mở Xcode ➞ File ➞ New ➞ Swift Package như hình: Image of article đặt tên của Package như trên là “VNDTextField” ở đây chúng ta đã tạo được Swift Package. Image of article Sau khi tạo xong, chúng ta vào file package và sẽ tiến hành config trong như sau:
    import PackageDescription
    
    let package = Package(
        name: "VNDTextField",
        platforms: [
                .iOS(.v10)            
            ],
        products: [
            // Products define the executables and libraries a package produces, and make them visible to other packages.
            .library(
                name: "VNDTextField",
                targets: ["VNDTextField"]),
        ],
        dependencies: [
            // Dependencies declare other packages that this package depends on.
            // .package(url: /* package url */, from: "1.0.0"),
        ],
        targets: [
            // Targets are the basic building blocks of a package. A target can define a module or a test suite.
            // Targets can depend on other targets in this package, and on products in packages this package depends on.
            .target(
                name: "VNDTextField",
                dependencies: []),
            .testTarget(
                name: "VNDTextFieldTests",
                dependencies: ["VNDTextField"]),
        ]
    )
    

    Ở đây chúng ta chú ý: 1.products đây là phần định nghĩa chúng ta có thể thấy các file của package khi dùng vào project khác

    products: [
            // Products define the executables and libraries a package produces, and make them visible to other packages.
            .library(
                name: "VNDTextField",
                targets: ["VNDTextField"]),
        ],
    

    2.platforms chúng ta thêm tham số vào cho hệ điều hành mình muốn build

    platforms: [
                .iOS(.v10)            
            ],
    

    ở đây mình chọn cho hệ điều hành từ iOS 10 Sau khi chỉnh sửa và setup file xong chúng ta tiến hành push code lên GitHub, và sau đó import lại cho project mà bạn muốn sử dụng. Đơn giản phải không nào !

    III. Sử dụng lại package vào project của bạn

    1. Đầu tiên chúng ta nhấn chuột vào fiel .xcodeproj -> chọn Swift Packager, chúng ta sẽ có như hình, ở đây mình đã thêm package custom ở trên vào project demo. Image of article

    2. Sau đó chúng ta nhấm vào dấu cộng(+) ở góc trái phía dưới để thêm package bạn muốn vào project, còn nếu muốn xóa package mà bạn đang sử dụng thì nhấn vào dấu trừ(-) nhé. Đây là lúc chúng ta bấm dấu + Image of article

    3. Ở đây bạn paste vào link package của bạn đã tạo trên github, chờ đợi trong vài giây chúng ta sẽ có kết quả như mình đây: Image of article

    4. Mình đã thêm package VNDTextField vào class của mình, và sử dụng lại nó như sau: Image of article

    5. Đây là class mình demo sử dụng lại VNDTextField. Chúng ta cần import lại package ở class mình muốn sử dụng, sau đó bạn có thể sử dụng thoải những gì package cung cấp. Sau khi sử dụng VNDTextField đây là kết quả mà mình đạt được. Image of article

    IV. Tổng kết

    Thật đơn giản, với Swift Packager bạn có thể tự custom cho mình những class common, tái sử dụng lại nhiều lần, giúp giảm chí phí build lại từ đầu, dynamic hơn. Đồng thời cũng dễ dàng cho bạn thoải sức custom và mong muốn nhiều người sử dụng lại dễ dàng. Từ nay việc sử dụng 1 third party hay class common đã trở nên dễ dàng hơn rất nhiều.

    Qua bài viết, hi vọng các bạn có cái nhìn cơ bản về cách tạo Swift Packager và sử dụng lại nó. Đồng thời cũng thêm vào bộ kĩ năng dev iOS của càng bạn trên hành trang fix bug. Hi vọng các bạn yêu thích bài viết này

    Mình có tạo 1 Package demo tại đây: https://github.com/ios-lib/VNDTextField

    Authors

    [email protected]

  • Cách tiếp cận mới trong tổ chức GIT repository dự án

    Cách tiếp cận mới trong tổ chức GIT repository dự án

    Chắc hẳn phần lớn chúng ta ở đây (suy đoán của mình) chưa từng tham gia đóng góp (contribute) vào một opensource nào đó ở trên Github, kể cả bản thân mình cho đến gần đây. Contribute mình muốn nói đến ở đây không phải về việc chúng ta tự publish các opensource library lên chính repository của chúng ta trên Github, cũng không phải việc trả lời các issues trên các Github repository, và cũng không phải thực hiện review các PR (Pull Request) trên các Github repository. Cái mà mình muốn nói ở đây là công việc chúng ta tạo ra các dòng code cho các feature mới, để fixbug, để refactor,… và rồi đưa những dòng code đó vào trong các opensource trên Github.

    Phần lớn chúng ta đang sử dụng trực tiếp các opensource từ các repository center, hoặc khi gặp bug chúng ta clone về và tự fix bug trong đó, và dùng nó như là một submodule ở trong project (Việc này cần chú ý đến License của opensource, và mục đích project của chúng ta đang làm). Có khá nhiều nhược điểm khi làm bằng cách vừa rồi, nhưng để đảm bảo đúng mục đích của bài này mình xin phép chưa nói đến ở đây, chúng ta có thể thảo luận ở phần comment. Tuy việc contribute cho opensource không phải là mục đích chính của mình trong bài viết này, nhưng hi vọng qua đây bạn sẽ nắm được các bước cơ bản để contribute cho một opensource nào đó trên Github.

    Mục đích chính của mình trong bài này là việc chúng ta cùng cân nhắc sử dụng cách tiếp cận contribute để áp dụng cho các dự án trong của công ty, cùng nhìn các khía cạnh, ưu nhược điểm giữa 2 cách tiếp cận đó là truyền thống, và contribute.

    Cách tiếp cận truyền thống và vấn đề gặp phải

    Đầu tiên mình sẽ summarize về cách tiếp cận truyền thống mà chúng ta đang làm trong một project.

    Chúng ta có một repository trên Gitlab, thêm các thành viên của dự án vào trong dự án theo từng rule, setup một số rule cơ bản về merge/rebase, branches, template,… Sau đó trong giai đoạn phát triển, mỗi thành viên sẽ tạo branch (theo format của team) để thực hiện code trên đó, code, commit, code, commit,… tạo MR/PR.

    Vấn đề gặp phải

    Những step trên đâu đấy là các step cơ bản để đưa các dự án về đích, còn về đích theo kiểu tan tác, hay hùng tráng thì đấy là một câu chuyện khác mà có thể mình sẽ chia sẻ ở một bài viết khác.

    Tất nhiên nếu mọi chuyện đều đẹp đẽ thì chúng ta đã không có bài này để thảo luận. Dưới đây là một số những điều mình nhận thấy sau nhiều tháng, năm nhìn lại project:

    • Git Tree là một cái gì đấy cực kì kinh khủng và không muốn nhìn vào nó.
    • Có hàng chục, hàng trăm branches, già có, trẻ có với muôn hình muôn vẻ.

    Với Git Tree thì đó là một câu chuyện rất hay, nhưng xin phép để lại cho một bài khác đầy đủ hơn để giải quyết nó. Ở đây mình muốn nói đến về branches. Sự hỗn loạn, nhập nhằng của branches trong project là sự đóng góp, cống hiến của mỗi thành viên trong dự án. Ở đây mình bỏ qua các yếu tố như quên không xóa branch sau khi đã được merge vào branch chính, hay branch sai format, branch từa lưa tạo linh tinh xong không xóa, thì mỗi một thành viên trong dự án, vẫn có những branch cá nhân cần thiết như để thử nghiệm, một số branch đang trong quá trình phát triển, fix bug này kia. Với mỗi thành viên đã có số branch cá nhân như vậy, thì liệu một team nhiều người thì lượng branch tại một thời điểm sẽ như nào? Sẽ rất rất lớn phải không?

    Cách tiếp cận mới – Contribute

    Idea của cách tiếp cận này để giải quyết vấn đề về branches đó là:

    Mỗi thành viên trong dự án sẽ không làm việc trực tiếp với repository chính của dự án nữa, mà sẽ làm việc với repository riêng của chính mình (mỗi thành viên), sau đó contribute vào repository chính của dự án.

    Với cách tiếp cận này, tất cả những gì bạn làm, bạn nghịch, bạn vọc trên sourcecode nó thuộc về bạn, và chính bạn mà thôi, không ai biết hay nhìn tới cả (trừ khi người đó muốn). Có nghĩa là tất cả những thứ hổ lốn mà có thể bạn tạo ra sẽ không làm ảnh hưởng đến người khác. Cuối cùng là những cái gì là tinh hoa nhất của bạn, thì bạn contribute (tạo MR/PR) vào trong repository chính của dự án, đến lúc đó ai ai cũng trầm trồ ngưỡng mộ những gì bạn đã đóng góp.

    Lý thuyết thì là vậy, tiếp theo đây, chúng ta sẽ phải thực hiện hóa nó. Các step làm sao để có thể thực hiện contribute được thì các bạn tham khảo và đọc tại đây.

    Mô hình tổng quát có thể biểu diễn qua sơ đồ dưới đây (Mình assume các bạn đã hiểu hoặc đã đọc bài hướng dẫn contribute ở trên):

    Như bạn thấy, tất cả những branches, những đoạn code thử nghiệm, những gì chưa hoàn hảo, bạn gói gọn nó trong repository của bạn mà không làm ảnh hưởng đến toàn bộ dự án. Và chúng ta cũng chỉ quan tâm tới workspace mà mình đang làm việc mà thôi, rất tập trung, và không bị làm phiền bởi người khác.

    Một vài câu hỏi thường được hỏi khi sử dụng cách tiếp cận mới này

    (Mình assume các bạn đã hiểu hoặc đã đọc bài hướng dẫn contribute ở trên)

    1. Nếu source code nằm ở 2 repository khác biệt nhau như vậy, làm sao để source code ở fork repository của mình là mới nhất?

    Trả lời: Ở máy tính của bạn đã được setup 2 remote URL, 1 là fork repository của bạn (origin), 2 là repository nguồn (upstream). Bạn có thể fetch source code ở upstream sau đó rebase sang source code local của bạn, sau đó push lên origin của bạn. Thật ra chúng ta sẽ thường quan tâm nhất trạng thái source code ở branch mà chúng ta đang dùng làm source base đó là branch develop, hoặc master.

    2. Khi làm việc trong dự án, thỉnh thoảng vẫn cần base trên source code của đồng nghiệp để làm, vậy thì phải làm thế nào?

    Trả lời: Tương tự với cách mà chúng ta setup git remote upstream, chúng ta hoàn toàn có thể setup git remote vào fork repository của đồng nghiệp bằng cách git remote add TEN_DONG_NGHIEP FORK_REPOSITOY_URL_DONG_NGHIEP. Ví dụ như git remote add DoanNH3 https://github.com/ngohado/project-a. Sau đó chúng ta hoàn toàn có thể base trên branch nào đó của đồng nghiệp để thực hiện tiếp.

    Sau khi làm việc xong, chúng ta có thể remove remote của đồng nghiệp đi để tránh rối rắm workspace của chúng ta.

    3. Nếu tôi là teamlead của dự án, tôi muốn xem công việc hiện tại của các thành viên xem tình hình code đến đâu rồi, code thế nào, chẳng lẽ tôi phải remote đến tất cả các repository của member à? Liệu có cách nào tốt hơn không?

    Trả lời: Bản thân nếu bạn phải remote sang tất cả repository của member, cũng sẽ không khác gì, chứ không tồi tệ hơn cách cũ. Tuy nhiên nếu là mình, mình sẽ thực theo cách như sau:

    • Member sẽ tạo MR/PR ngay khi bạn ấy push commit lần đầu tiên (mình prefer anh em member luôn luôn push ít nhất 1 lần / ngày làm việc, đó là trước khi rời office).
    • Mình thích review theo từng commit chứ không phải tổng thể MR/PR (tất nhiên cái này cả team phải chung một mindset thì mới làm được), nên nếu commit nào đã hoàn thiện bạn ấy đẩy lên mình có thể review được ngay, còn những commit vẫn in-progress chưa sẵn sàng thì có thể thêm prefix vào commit message để báo với teamlead chưa review commit đó (Bước refine commit sau khi hoàn tất mình sẽ có một bài chia sẻ riêng).

    Với cách trên thì mình có thể view được tình hình của team qua các MR/PR, qua các commit đã hoàn thiện, và các commit vẫn đang trong progress.

    Kết luận

    Bản thân mình chưa trải nghiệm cách tiếp cận này trên một project dài hơi, hay lớn. Mình tin là vẫn sẽ có những question cho cách tiếp cận này, nếu bạn có hãy raise lên ở dưới phần comment để chúng ta cùng nhau nghĩ cách giải quyết. Nếu sắp tới khi mình tham gia vào một dự án nào mới, mình nhất định sẽ đề xuất cách tiếp cận này để trải nghiệm, có đánh giá tốt hơn và rồi sẽ chia sẻ lại cho mọi người. Thật ra thì không hẳn là dự án mới mới có thể áp dụng được, ngay những dự án đang chạy hoàn toàn có thể chuyển sang cách này, nếu thấy hợp lý thì hãy thử đề xuất với teamlead, hay với team của chính các bạn nhé. Cảm ơn các bạn đã đọc đến đây, rất mong nhận được sử phản hồi từ các bạn.

  • Một số tips Debug ứng dụng với Xcode

    Một số tips Debug ứng dụng với Xcode

    Overview

    Debug là một kỹ năng không thể thiếu của một lập trình viên, kỹ năng debug giỏi giúp ích rất nhiều trong việc tìm hiểu và giải quyết vấn đề phát sinh. Thêm vào đó, Xcode cung cấp rất nhiều công cụ hỗ trợ quá trình debug, nếu tận dụng được hết thì bạn có thể dễ dàng xử lý nhiều vấn đề khó. Bài viết tổng hợp lại một số kinh nghiệm và kiến thức của mình trong quá trình debug ứng dụng với Xcode.

    Debugging Process

    Việc có một process cụ thể sẽ giúp ta biết được sẽ phải làm gì tiếp theo trong từng giai đoạn, giúp việc debug diễn ra suôn sẻ. Theo như những gì mình học và thực hành được từ thực tế, một flow debug sẽ có các bước như sau:

    1. Discover: Xác định lỗi, đơn giản là chúng ta cần phải biết được lỗi đang gặp phải là gì, là crash app, freeze app, memory leak, business chạy không đúng, data không
    2. Locate: Xác định vị trí đoạn code gây ra lỗi
    3. Inspect: Kiểm tra flow, dữ liệu, logic để tìm ra nguyên nhân khiến đoạn code chạy sai
    4. Fix: Tất nhiên là sửa lại đoạn code cho đúng
    5. Confirm: Đảm bảo là lỗi đã được giải quyết và không có ảnh hưởng, hậu quả gì để lại

    Debugger Tools

    Tiếp theo chúng ta sẽ đi qua một lượt những gì Xcode cung cấp trong Debugger Tools. Đây là màn hình capture các thành phần Debugger Tools Xcode cung cấp cho chúng ta:

    Debug Navigator

    Debug Navigator là khu vực bên trái, gồm có 2 thành phần chính:

    1. Debug Gauge: Là thước đo các chỉ số của ứng dụng như CPU, Memory, Disk I/O, GPU, … Theo dõi các chỉ số trên Debug Gauge có thể giúp ích trong quá trình Discover các bug liên quan đến Memory Leak, CPU Usage, Read/Write Data, FPS, …
    2. Process View: Hiển thị các Threads đang chạy và Call Stack Trace đến breakpoint tại thời điểm ứng dụng dừng để debug. Sử dụng Process View giúp ích trong việc Locate các đoạn code gây nên lỗi, các bạn có thể dễ dàng tìm được đoạn một đoạn code/hàm được gọi từ đâu chỉ bằng việc nhìn vào Call Stack thay vì đọc và search từng dòng code.
    Nhìn vào CallStack ta có thể thấy, Breakpoint được gọi có nguồn gốc từ viewDidLoad() của ViewController

    Breakpoint

    Breakpoint là khái niệm rất quen thuộc ở mọi Debugger Tools, chính là đánh dấu cho vị trí được đánh dấu để debug, khi ứng dụng chạy đến vị trí code này, Debugger Tools sẽ tự động dừng ứng dụng và thực hiện quá trình debug, hiển thị các thông tin tại thời điểm và vị trí đặt Breakpoint.

    Chúng ta có thể đặt nhiều Breakpoint, ứng dụng sẽ dừng ở Breakpoint đầu tiên chạy tới.

    Source Editor

    Source Editor là khu vực để thay đổi source code, tuy nhiên trong khi ứng dụng đang ở chế độ debug chúng ta có thể di chuyển pointer tới các instance cần kiểm tra, và inspect trực tiếp các thông tin của instance đó. Đối với một số data type đặc biệt như UIImage, chúng ta có thể view trực tiếp ảnh từ Source Editor.

    Inspect UIImage
    Inspect các thông tin của instance

    Debug Bar

    Debug Bar bao gồm bộ các chức năng hỗ trợ trong quá trình debug, dưới đây là một số chức năng mình hay sử dụng:

    1. Hide/Show: Hiển thị hoặc Ẩn khu vực debug bên dưới Debug Bar
    2. Breakpoint Activation: Enable/Disable các Breakpoint, khi disable thì ứng dụng sẽ không dừng lại khi xử lý qua các vị trí đặt Breakpoint
    3. Continue/Pause: Tạm dừng/Tiếp tục việc thực thi code của ứng dụng
    4. Step controls: Thực thi dòng code tại vị trí hiện tại; Nếu vị trí hiện tại là function, jump vào function đó; Jump ra vị trí đang gọi đến function chứ vị trí hiện tại
    5. Debug View Hierarchy: Hiển thị cấu trúc các Controllers, Views dưới dạng 3D. Hỗ trợ việc theo dõi vị trí và mối quan hệ của các UIComponents tại một thời điểm

    Variables View

    Variables View là khu vực bên trái, phía dưới Debug Bar, nơi hiển thị các instance của ứng dụng tại thời điểm debug. Tuy nhiên sẽ chỉ hiển thị các instance được sử gọi đến trong block/function chứa vị trí Breakpoint.

    Ngoài việc hiển thị, chúng ta còn có thể inspect/edit thông tin các instance

    Console & LLBD

    Console View là nơi hiển thị các thông tin chúng ta in ra console. Ngoài ra, Console còn được tích hợp thêm LLDB Command, giúp cho việc Debug đã dễ lại càng trở nên dễ dàng hơn. Mình sẽ liệt kê một số LLDB Command thông dụng thường đươc sử dụng trong khi Debug.

    • po variable: In ra giá trị của một biến
    • p variable: In ra toàn bộ thông tin của một biến
    • fr v: In ra toàn bộ thông tin của các biến
    • e expression: Thực thi một expression, ví dụ: e i=10 sẽ thay đổi giá trị của i bằng 10; e var $x=10 sẽ khai báo thêm một biến có giá trị 10.
    Ví dụ sử dụng LLDB Command

    Ngoài ra còn rất nhiều command hữu ích khác, các bạn có thể tham khảo thêm ở đây.

    Trên đây là một số kiến thức và kinh nghiệm của mình, nếu có gì thiếu xót mong được các bạn góp ý. Cảm ơn đã ủng hộ!

    Authors

    MinhNN44

  • Review Source code

    Review Source code

    Review source code.

    Gần đây khi mình tham gia các buổi phỏng vấn Senior thì nhận ra một điều, hầu hết các bạn "Senior" đều có note trong CV rằng "review source code" là một phần công việc của bạn ý, có điều khi mình hỏi các bạn review source code như thế nào thì đều chỉ nói được rất chung chung theo kiểu: kiểm tra source code có dễ đọc ko, có đúng logic trong file specs ko..etc, rất hiếm người trả lời một cách bài bản.

    Vậy rốt cuộc " Review source code" là task thế nào hoặc chúng ta nên trả lời cái gì khi bị hỏi: review source là review cái gì?

    Table of contents

    Rule khi review source code

    1. Review dựa trên check list.

      Bạn luôn luôn phải xây dựng một bộ check list, một bộ quan điểm cho dòng dự án mình đang tham gia. Mỗi dòng dự án sẽ có độ ưu tiên khác nhau cho từng mục trong checklist, sẽ có những mục là mandatory với dự án này nhưng là optional với dự án khác.

      Check list này phải được training cho dev trong dự án để hiểu từng item một. Điều này hạn chế việc lack quan điểm review, hạn chế các sai lầm trong những lúc mệt mỏi kém tỉnh táo trong ngày làm việc căng thẳng, hạn chế sự xung đột của reviewer với developer.

    2. Mọi quan điểm đúng/sai đều phải dựa trên offical document nào đó.

      Nếu là đúng sai về mặt requirement thì phải có requirement chứng minh lỗi sai của developer, nếu là đúng sai về mặt coding, framework thì phải chỉ ra được lỗi quy định trên ngôn ngữ lập trình hoặc official document của framework.

      Tuyệt đối ko dựa vào "Kinh nghiệm cá nhân" để áp đặt tư tưởng bản thân xuống team. Thậm chí kể cả coding convention cũng phải có document để refer.

    Một số quan điểm review

    1. Coding convention

    Hầu hết các ngon ngữ lập trình, các dòng framework đều có các page define rõ các rule liên quan đến convention, ví dụ:

    https://source.android.com/setup/contribute/code-style

    https://developer.android.com/kotlin/style-guide

    https://dart.dev/guides/language/effective-dart/style

    Việc cần làm của chúng ta là đọc hiểu rồi tuân theo. Điều này khiến source code của dự án trở lên "thân thiện" với người đọc hơn, có vẻ chuyên nghiệp hơn. Bạn đã làm quen với các project chuẩn coding convention mà chuyển sang đọc source một bạn sinh viên lần đầu đi làm, hoặc các bạn làm theo kiểu free style sẽ cảm thấy rất khác biệt, đôi khi có follow coding convention hay không lại là cơ sở để đánh giá liệu rằng coder đã code đủ lâu, đã có kinh nghiệm đủ lâu với lĩnh vực mà họ đang làm hay chưa.

    2. Design

    Quan điểm review liên quan đến design là một quan điểm rất rộng và tốn giấy mực, nó không chỉ đơn thuần việc các module/class làm việc với nhau thế nào, tạo interface ra sao, abstraction ổn chưa..etc mà còn là việc liên quan đến việc thiết kế bao nhiêu luồng chạy cho ứng dụng là đủ, context để các task chạy có đủ lâu, đủ ổn định không, các component đã đủ yêu cầu liên quan đến security chưa… và nhiều thứ phức tạp liên quan nữa

    • Security: Secure coding, việc tổ chức source code thế nào, trong code lưu các api key ra sao, có bao nhiêu module dữ liệu được đi ra ngoài, cơ chế lưu dữ liệu, mã hoá dữ liệu, cơ chế đảm bảo an toàn cho các component khi bị gọi đến, quan điểm này thật sự dài, có thể mình sẽ viết trong các seri tiếp theo tại đây (https://magz.techover.io/2021/08/02/giao-thuc-bao-mat-https-va-mitm-attacksecure-coding-p1/)
    • Performance: Bao nhiêu thread là đủ, có cần pool thread không? đã giải phóng các resource chưa, dùng loại layout này đã tối ưu chưa, dùng loop nào để optimize tốc độ, có cần cache không, dùng cấu trúc dữ liệu nào để optimize đoạn xử lý này? task này chạy trên worker thread hay main thread..etc
    • Cấu trúc: có cực kỳ nhiều thứ để check, vd: sử dụng pattern nào, có nên dùng DI ko, đoạn source này nên do class/moduel nào xử lý..etc Mình rất dị ứng cụm từ "dựa vào kinh nghiệm" nhưng thực sự quan điểm này phần lớn do đến từ kinh nghiệm, đại khái thì chúng ta sẽ quan tâm đến high cohesion, low coupling, tạo ra các boundaries – các ranh giới cho class/ module của chương trình

    3. Mistake liên quan đến Logic code/requirement

    Đây chính là phần mà hầu hết các bạn đi phỏng vấn sẽ trả lời vào, đại khái là check xem có đúng requirement ko, code logic có nhầm đoạn nào ko? có null rồi bị bug ko..etc

    Review thử một đoạn source

    Rồi,đó là lý thuyết, giờ chúng ta thử áp dụng để review đoạn source bên dưới

    public class BroadCastTestJava extends BroadcastReceiver {
        @Override
        public void onReceive(Context context, Intent intent) {
            decodeImage(intent.getStringExtra("imageData"));
            new MyAsyncTask("A").execute();
            new MyAsyncTask("B").executeOnExecutor();
        }
    
        private Bitmap decodeImage(String image) {
            byte[] imgBytes = Base64.decode(image, 0);
            return BitmapFactory.decodeByteArray(imgBytes, 0, imgBytes.length);
        }
    
        public class MyAsyncTask extends AsyncTask {
            private String name;
            public MyAsyncTask(String name) {
                this.name = name;
            }
            @Override
            protected Object doInBackground(Object[] objects) {
                for (int i = 0; i < 10; i++) {
                    Log.d("test", "My name is " + this.name + " " + i);
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                return null;
            }
        }
    

    1. Dùng Tool check.

    Đa số các IDE đều hỗ trợ, ví dụ android studio thì có thể làm như dưới Check by Tool

    Hầu hết các lỗi do tool chỉ ra, đều đúng là lỗi thật và có thể fix được, nên đương nhiên, chúng ta sẽ xử lý nhé 😛

    2. Coding convention Anh em thử xem có các lỗi convention nào :p

    3. Design Có thể thấy có các issue sau:

    • decodeImage() không được chạy trên main thread (task vụ nặng)

    • public class phải chuyển thành private static class(Trong java inner class sẽ refer sang outer class -> leak memory)

    • Không được dùng asynctask trên BroadcastReceiver, context của broadcast không đủ dài để xử lý background, nên phải đưa về context sống lâu hơn, ví dụ service để xử lý tiếp. Đây là vấn đề liên quan đến thiết kế các task chạy ở đâu cho phù hợp. Hoặc nếu task ngắn hạn thì có thể dùng goAsync(https://developer.android.com/reference/android/content/BroadcastReceiver#goAsync())

    • Security: Liệu rằng intent gửi đến(để ra lệnh cho app decode image) có phải intent của app ko? có đúng là được phép để xử lý không? hay do attacker đang cố tình tấn công vào app? liệu rằng Broadcast có được bảo vệ bởi permission không? – phần này mình sẽ break sang một bài viết khác liên quan đến Secure Coding

    4. Logic code

    • decodeImage không verify intent đầu vào/ không handle exception, giả sử intent nhả ra null hoặc rỗng thì sẽ có exception xảy ra, có thể app sẽ crash??

    => gọi coder ra "nhờ" bạn ý fix thôi

    Việc review code easy hơn rồi – đúng ko nào. Chắc sẽ còn nhiều lỗi nữa, anh em thử check tiếp nhé.

    Tổng kết lại:

    1. Chúng ta nên có 1 bộ checklist dùng để review(có thể tuỳ vào dòng dự án, mức độ khó tính của khách hàng, mức độ chặt chẽ dự án yêu cầu)- để hạn chế các mistake về mặt con người
    2. Các quan điểm review đúng/sai về mặt kỹ thuật phải thuần tuý dựa vào quan điểm kỹ thuật, official document để quyết định đúng/sai, không được áp đặt dựa vào "kinh nghiệm cá nhân"

    Code tiếp thôi nào !

    guru

    Authors

    [email protected]

  • iOS Home Screen Action

    iOS Home Screen Action

    Overview

    Khi sử dụng một số ứng dụng phổ biến như Zalo, Telegram hay YouTube, … trên các thiết bị iOS 13 trở lên, hẳn chúng ta đã từng nhìn thấy một số option mới khi long press ở Home Screen như hình dưới. Ở bài viết này, chúng ta sẽ cùng tìm hiểu cách để tạo ra những option đó.

    Các option hay shortcut này được gọi là Home Screen Actions (hay Home Screen Quick Actions), được hỗ trợ từ iOS 13 trở lên. Đúng như cái tên của nó, đây là những shortcut được thiết kế để thực hiện trực tiếp lựa chọn các action của ứng dụng tại màn hình Home thay vì phải mở và thao tác nhiều lần bên trong ứng dụng.

    Hãy lấy ví dụ như chúng ta có ứng dụng Safari, khi muốn mở Techover trên Safari ta có một số lựa chọn như sau:

    1. Mở Safari -> Tap vào thanh địa chỉ -> Nhập magz.techover.io -> ấn Go
    2. Mở Safari -> Tap New Tab -> Chọn BookMark -> Chọn magz.techover.io

    Có thể thấy, để thực hiện công việc trên chúng ta đều cần ít nhất 4 bước, lựa chọn đầu tiên còn yêu cầu người dùng nhập địa chỉ thủ công. Thay vì thế, ta có thể đơn giản hoá bằng cách đưa magz.techover.io vào một shortcut của Safari, chỉ với hai lần chạm, ta đã có thể mở trang web trên Safari một cách nhanh chóng.

    Home Screen Action có hai loại, Static và Dynamic:

    1. Static: Luôn cố định, không thể thay đổi tuỳ chỉnh được
    2. Dynamic: Có thể tuỳ chỉnh, thay đổi tuỳ theo hoạt động của ứng dụng

    Giờ chúng ta sẽ đi tìm hiểu, làm sao để cấu hình cho từng loại Home Screen Action.

    Home Screen Static Action

    Đối với Static Actions, chúng ta sẽ cài đặt ở trong Info.plist với cấu trúc sau:

    <key>UIApplicationShortcutItems</key>
    <array>
        <dict>
            <key>UIApplicationShortcutItemIconType</key>
            <string>UIApplicationShortcutIconTypeHome</string>
            <key>UIApplicationShortcutItemTitle</key>
            <string>Techover</string>
            <key>UIApplicationShortcutItemSubtitle</key>
            <string>Open Techover</string>
            <key>UIApplicationShortcutItemType</key>
            <string>Techover</string>
        </dict>
        <dict>
            <key>UIApplicationShortcutItemIconType</key>
            <string>UIApplicationShortcutIconTypeCompose</string>
            <key>UIApplicationShortcutItemTitle</key>
            <string>New</string>
            <key>UIApplicationShortcutItemSubtitle</key>
            <string>Create New Post</string>
            <key>UIApplicationShortcutItemType</key>
            <string>Create</string>
        </dict>
    </array>

    Trong đó:

    • Key UIApplicationShortcutItemIconType là define Icon cho Action, các bạn có thể tham khảo list value ở đây
    • Key UIApplicationShortcutItemTitle là define Title cho Action
    • Key UIApplicationShortcutItemSubtitle là define SubTitle, dòng chữ nhỏ phía dưới Title
    • Key UIApplicationShortcutItemType là một String dùng để định danh, phải là duy nhất, dùng để handle xử lý Action ở trong code.

    Sau khi setting xong, chúng ta sẽ có kết quả như sau:

    Home Screen Dynamic Action

    Đối với Dynamic Actions, chúng ta có thể thêm bất cứ chỗ nào bên trong xử lý của ứng dụng bằng đoạn code sau:

    let application = UIApplication.shared
    var shortcuts = application.shortcutItems
    shortcuts?.append(UIApplicationShortcutItem(type: "FavoriteAction",
                                                localizedTitle: "Home Screen Action",
                                                localizedSubtitle: "My first Technopedia post",
                                                icon: UIApplicationShortcutIcon(systemImageName: "star.fill"),
                                                userInfo: ["Marked Post": "Minhnn44-1" as NSSecureCoding]))
    shortcuts?.append(UIApplicationShortcutItem(type: "FavoriteAction",
                                                localizedTitle: "iOS Distribution Methods",
                                                localizedSubtitle: "My seconds Technopedia post",
                                                icon: UIApplicationShortcutIcon(systemImageName: "star.fill"),
                                                userInfo: ["Marked Post": "Minhnn44-2" as NSSecureCoding]))
    application.shortcutItems = shortcuts

    Trong đó, UIApplication.shared.shortcutItems là một [UIApplicationShortcutItem], mỗi một UIApplicationShortcutItem tượng trưng cho một Action ở Home Screen với các tham số khởi tạo:

    • type: String định danh của Action
    • localizedTitle: Title
    • localizedSubtitle: SubTitle
    • icon: icon
    • userInfo: Dictionary để lưu các thông tin cần truyền thêm

    Action Handle

    Việc handle khi người dùng ấn các action sẽ được thực hiện bên trong SceneDelegate như sau.
    Đối với trường hợp ứng dụng chưa được mở (not running), chúng ta sẽ handle bên trong hàm scene(_:willConnectTo:options:) như sau:

    func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
        /** Process the quick action if the user selected one to launch the app.
            Grab a reference to the shortcutItem to use in the scene.
        */
        if let shortcutItem = connectionOptions.shortcutItem {
            // Save it off for later when we become active.
            savedShortCutItem = shortcutItem
        }
    }

    Trong trường hợp ứng dụng đang ở background, chúng ta sẽ handle bên trong hàm windowScene(_:performActionFor:completionHandler:) như sau:

    func windowScene(_ windowScene: UIWindowScene,
                     performActionFor shortcutItem: UIApplicationShortcutItem,
                     completionHandler: @escaping (Bool) -> Void) {
        let handled = handleShortCutItem(shortcutItem: shortcutItem)
        completionHandler(handled)
    }
    
    func handleShortCutItem(shortcutItem: UIApplicationShortcutItem) -> Bool {
        /** In this sample an alert is being shown to indicate that the action has been triggered,
            but in real code the functionality for the quick action would be triggered.
        */
        switch shortcutItem.type {
        case "Techover":
            print("Techover Selected")
        case "Create":
            print("Create New Post Selected")
        case "FavoriteAction":
            if let markedPost = shortcutItem.userInfo?["Marked Post"] as? String {
                print("Open \(markedPost)")
            }
            break
        default:
            break
        }
        return true
    }

    Vậy là xong, nếu có thắc mắc hoặc góp ý thì các bạn comment phía dưới để mình giải đáp và cái thiện nhé!

    Authors

    MinhNN44