<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0" xmlns:cc="http://cyber.law.harvard.edu/rss/creativeCommonsRssModule.html">
    <channel>
        <title><![CDATA[Stories by Ha ĐANG on Medium]]></title>
        <description><![CDATA[Stories by Ha ĐANG on Medium]]></description>
        <link>https://medium.com/@ha-dang?source=rss-24f1e197a08f------2</link>
        <image>
            <url>https://cdn-images-1.medium.com/fit/c/150/150/0*r6weidN-gsP8rEIT.png</url>
            <title>Stories by Ha ĐANG on Medium</title>
            <link>https://medium.com/@ha-dang?source=rss-24f1e197a08f------2</link>
        </image>
        <generator>Medium</generator>
        <lastBuildDate>Sun, 15 Nov 2026 19:49:25 GMT</lastBuildDate>
        <atom:link href="https://medium.com/@ha-dang/feed" rel="self" type="application/rss+xml"/>
        <webMaster><![CDATA[yourfriends@medium.com]]></webMaster>
        <atom:link href="http://medium.superfeedr.com/" rel="hub"/>
        <item>
            <title><![CDATA[call vs delegatecall vs staticcall]]></title>
            <link>https://medium.com/agileb-it/call-vs-delegatecall-vs-staticcall-404a27b94d9e?source=rss-24f1e197a08f------2</link>
            <guid isPermaLink="false">https://medium.com/p/404a27b94d9e</guid>
            <category><![CDATA[smartcontractdevelopment]]></category>
            <category><![CDATA[ethereum]]></category>
            <category><![CDATA[blockchain]]></category>
            <dc:creator><![CDATA[Ha ĐANG]]></dc:creator>
            <pubDate>Sun, 20 Jun 2021 17:08:12 GMT</pubDate>
            <atom:updated>2021-06-20T17:08:12.392Z</atom:updated>
            <content:encoded><![CDATA[<p>Khi lập trình smart contract trên <strong>Ethereum</strong> nói riêng và các nền tảng khác nói chung, đặc biệt là ở các dự án phức tạp và đặc thù thì việc <strong><em>call</em></strong> contract từ contract khác là rất hay được sử dụng. Lập trình smart contract hiện nay hầu hết đều liên quan đến số tiền không nhỏ nên chỉ cần một lỗi làm bé tí ti cũng khiến chúng ta phải trả giá rất nặng nề, đơn vị được tính bằng nhiều triệu đô. Các bạn có thể tìm hiểu thêm về các vụ hack hàng chục triệu đô trong vài năm gần đây. Một số quan điểm cho rằng các vụ tấn công này một phần là do lỗi thiết kế hệ thống, cho phép lập trình viên thực hiện những thao tác dẫn đến nhiều lỗ hổng và lỗi logic không đáng có. Tuy nhiên, điều gì cũng có hai mặt của nó, việc này lại cho phép lập trình viên linh động và sáng tạo nhiều hơn. Chính vì vậy, ở phía các lập trình viên thì điều chúng ta có thể làm tốt nhất là hiểu rõ công cụ chúng ta sử dụng để hạn chế tối đa những lỗi nghiêm trọng có thể xảy ra. Giống như khi khi chụp ảnh vậy, khi bạn đã thành thạo rồi thì bạn mong muốn được điều chỉnh các thông số tuỳ ý chứ không bị gò bó và giới hạn bởi các thông số cố định hay đã được cài đặt sẵn.</p><p>Lập trình smart contract trên máy ảo Ethereum–<strong>EVM</strong>– cho phép lập trình viên sử dụng <em>assembly</em> để có thể can thiệp ở mức sâu hơn mà đôi khi sử dụng <strong>solidity</strong> đơn thuần không thực hiện được hoặc một số tính toán có thể tối ưu lượng <em>gas</em> cần sử dụng để giảm chi phí giao dịch. Và nó cũng là con dao hai lưỡi nếu chúng ta sử dụng mà không nắm rõ về nó.</p><p>Quay lại về việc gọi qua lại giữa các smart contract khác nhau, cả 3 cách gọi call, delegatecall và static call đều có thể thực hiện bằng solidity hoặc assembly; tuỳ vào tình huống mà chúng ta sử dụng cách thức hợp lý hơn. Mục đích của bài viết này là phân biệt sự khác nhau và giống nhau giữa 3 cách gọi đã kể ở trên.</p><h3>call</h3><p>Thông thường, nếu chúng ta import một contract này vào source code của contract khác thì việc call trờ nên rất tự nhiên như chúng ta vẫn làm trong các ngôn ngữ lập trình truyền thống khác.</p><p>Một vi dụ đơn giản như sau, chúng ta có một contract để lưu giữ thông tin</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/d4ba8c1e1d35969a2c8cef5f6a24ff94/href">https://medium.com/media/d4ba8c1e1d35969a2c8cef5f6a24ff94/href</a></iframe><p>Chúng ta muốn deploy một contract Machine sẽ gọi đến contract Storage.</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/bd996e50bb486a6972021cacb39ed597/href">https://medium.com/media/bd996e50bb486a6972021cacb39ed597/href</a></iframe><p>Dễ nhận thấy rằng, ở dòng số #3 import &quot;./Storage.sol&quot;; , contract Storage được import trực tiếp vào contract Machine và khi deploy contract Machine thì contract Storage cũng được deploy cùng luôn, contract Machine tham chiếu trực tiếp đến contract Storage nên việc call các method của contract Storage từ contract Machine được thực hiện đơn giản như ở dòng #14 s.setValue(x); và giá trị được thay đổi cho dù được truyền vào qua tham số và được thực thi ở contract Machine nhưng trạng thái thay đổi được phản ánh vào contract Storage.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*MtU4N4q5WQb7X3XnHElaXA.png" /></figure><p>Tuy nhiên, nếu trong trường hợp để tiết kiệm chi phí hoặc có thể do một số yêu cầu đặc biệt của dư án thì contract Storage có thể được deploy một cách độc lập với contract Machine. Tuỳ vào từng trường hợp thì chúng ta có thể lựa chọn 1 trong 2 cách sau đây:</p><ol><li>Sử dụng interface</li><li>Sử dụng hàm call</li></ol><h3>Sử dụng interface</h3><p>Interface của solidity không khác gì interface ở các ngôn ngữ lập trình phổ biến nên cũng không cần thiết phải gỉải thích lại ở bài viết này. Bằng cách sử dụng interface, chúng ta vẫn có thể call các method của contract Storage mà không cần import code của nó vào contract Machine. Cụ thể cách thức implement cho contract Machine có thể như sau:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/2c13bbf471aa7e73d7a304dbaff814ee/href">https://medium.com/media/2c13bbf471aa7e73d7a304dbaff814ee/href</a></iframe><h3>Sử dụng hàm call</h3><p>Không phải lúc nào chúng ta cũng có thể sử dụng interface vì trong nhiều trường hợp, chúng ta không biết trước được ABI của contract, chúng ta chỉ có thông tin này lúc runtime thì gần như bắt buộc chúng ta phải sử dụng đến hàm <strong>call</strong> để gọi đến method của một smart contract độc lập khác.</p><p>Thực chất thì EVM kích hoạt một lời gọi hàm của smart contract qua một chuỗi byte; trong đó, 4 byte đầu tiên của hash từ giá trị của method signature, tiếp sau đó mà các chuỗi có độ dài cố định là 32 bytes. Chúng ta có thể thấy rõ khi xem chi tiết giao dịch trên <a href="https://etherscan.io/">etherscan</a></p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*hxuX-vjLZSI0TF9MSeoBTQ.png" /></figure><p>MethodID: 0x2d2da806 chính là 4 bytes đầu tiên của giá trị hash keccak-256 của method signature depositETH(address). Nếu muốn bạn có thể kiểm tra bằng cách sử dụng <a href="https://emn178.github.io/online-tools/keccak_256.html">công cụ hash keccak-256 online</a></p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*2VoFhfisKgbmkBW2xRVkOw.png" /></figure><p>[0]: 000000000000000000000000e105ba42b66d08ac7ca7fc48c583599044a6dab3 giá trị tiếp sau đó là input data của method dưới dạng chuỗi các số 32 bytes và được đánh số từ 0.</p><p>Chính vì vậy mà khi sử dụng hàm call chúng ta cũng phải tính toán <strong>MethodID</strong> theo đúng mô tả ở trên mà truyền vào tham số tương ứng của hàm call để có thể gọi được method mong muốn của smart contract nào đó. Quay lại ví dụ ở trên thì chúng ta có thể cài đặt contract Machine như sau</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/14765f6499d95aee4bde0241af28ba43/href">https://medium.com/media/14765f6499d95aee4bde0241af28ba43/href</a></iframe><h3>delegatecall</h3><p>Khi sử dụng call thì msg.sender không phải là sender ban đầu mà khi đó sender được thay đổi thành caller contract. Như ở ví dụ trên thì sender là contract Machine và state được thay đổi nằm ở context của contract Storage. Nhưng khi sử dụng delegatecall thì msg.sender được giữ nguyên như ban đầu không thay đổi và state thay đổi ở context của Manchine, chỉ mượn code của Storage rồi thực thi trên context của contract Machine.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*o3NYzhol4Z6QXICrNuFTKw.png" /></figure><p>delegatecall hay được sử dụng trong proxy pattern, khi mà implement logic code chung ở một contract độc lập và contract để lưu state của mỗi người dùng thì được lưu riêng biệt ở contract proxy. Và có thể thay đổi hay upgrade logic code bằng cách thay đổi logic code của contract độc lập và trỏ lại implement contract ở contract proxy.</p><p>Ví dụ một proxy contract có thể được cài đặt sử dụng delegatecall ở dòng thứ 18 như sau</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/c432e0f1f81d62d3826c4bc734bdaa6f/href">https://medium.com/media/c432e0f1f81d62d3826c4bc734bdaa6f/href</a></iframe><h3>staticcall</h3><p>staticcall thì cơ bản giống với callcall chỉ khác là staticcall không cho phép thay đổi state của smart contract.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=404a27b94d9e" width="1" height="1" alt=""><hr><p><a href="https://medium.com/agileb-it/call-vs-delegatecall-vs-staticcall-404a27b94d9e">call vs delegatecall vs staticcall</a> was originally published in <a href="https://medium.com/agileb-it">AgileB.it</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Ngược dòng lịch sử cùng cỗ máy thời gian git reflog]]></title>
            <link>https://medium.com/agileb-it/ng%C6%B0%E1%BB%A3c-d%C3%B2ng-l%E1%BB%8Bch-s%E1%BB%AD-c%C3%B9ng-c%E1%BB%97-m%C3%A1y-th%E1%BB%9Di-gian-git-reflog-d6084651dd61?source=rss-24f1e197a08f------2</link>
            <guid isPermaLink="false">https://medium.com/p/d6084651dd61</guid>
            <category><![CDATA[git]]></category>
            <dc:creator><![CDATA[Ha ĐANG]]></dc:creator>
            <pubDate>Wed, 16 Sep 2020 09:10:23 GMT</pubDate>
            <atom:updated>2020-09-16T11:08:34.090Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="Cỗ máy thời gian của Đô-ra-ê-mon" src="https://cdn-images-1.medium.com/max/720/0*T0OP_APNlDdv_6vo.jpg" /></figure><p>Git là công cụ quản lý mã nguồn tốt và đang được sử dụng miễn phí rộng rãi là điều không phải bàn cãi. Git cung cấp một bộ lệnh cực kỳ mạnh mẽ và tiện dụng để có thể đáp ứng nhu cầu phát triển phần mềm của các dự án từ nhỏ đến lớn.</p><p>Trong cuộc sống nói chung ai cũng có lúc mắc sai lầm; nhưng quan trọng là chúng ta sớm nhận ra được sai lầm đó, cố gắng hết sức để tìm cách khắc phục, sửa chữa và thay đổi để có được kết quả cuối cùng tốt nhất. Và trong việc xây dựng và phát triển phần mềm cũng vậy. Ngày qua ngày, chúng ta vẫn miệt mài tạo ra những thay đổi với mong muốn tạo ra thêm nhiều tính năng hay, tính năng mới và phù hợp hơn cho khách hàng. Những thay đổi đó đôi khi không phải là những thay đổi mà chúng ta cần nên việc có thể quay ngược lại thời gian để có thể nhanh chóng sửa chữa và lấy lại được phần chính xác nhất cho dự án là việc rất cần thiết. Git cho phép chúng ra làm điều đó một cách nhanh chóng và dễ dàng.</p><p>Theo thiển ý của tôi thì chúng ta thường có 3 cách để quay ngược thời gian với Git.</p><ol><li>Sử dụng git commit --amend để thay đổi commit gần nhất</li><li>Sử dụng git revert để huỷ bỏ thay đổi của một commit cụ thể nào đó</li><li>Sử dụng git rebase -i để có thể thay đổi nhiều commit một lúc theo ý muốn.</li></ol><p>Tuy nhiên, cả 3 câu lệnh ở trên chúng ta thường chỉ làm việc trên git logs của repository mà ít khi làm việc với toàn bộ lịch sử thao tác mà chúng ra đã thực hiện trên local repository. Để dễ hình dung hơn thì tôi sẽ đưa ra một tình huống sau đây.</p><p>Giả dụ trong một ngày bấn loạn, trải qua rất nhiều tranh luận và bàn bạc với các đội nhóm khác, bạn đã thực hiện git commit --amend 3 lần cho một commit quan trọng cần được release gấp lên production trong ngày hôm nay. Nhưng đến cuối ngày, sếp chốt một câu xanh rờn là nội dung commit lần thứ 2 là chính xác và cần deliver theo commit đó. Tất nhiên, nếu thay đổi của commit đó nhỏ, không đáng kể thì bạn có thể dễ dàng lấy lại. Nhưng nếu các lần amend code đều là những thay đổi lớn thì có cách nào để có thể nhanh chóng lấy lại commit đã amend ở lần thứ 2 hay không?</p><p>…</p><p>…</p><p>…</p><p>Câu trả lời là có, đó là sử dụng cỗ máy thời gian git reflog, <strong>git reflog</strong> ghi lại toàn bộ thao tác mà chúng ta đã thực hiện trên local repository nên sẽ có nhiều chi tiết và nhiều thời điểm hơn <em>git log </em>thông thường nên chúng ra dễ dàng quay lại được thời điểm mà chúng ta mong muốn.</p><p>Dưới đây là ví dụ minh hoạ cho sự khác biệt giữa git reflog và git log</p><p>git log chỉ có 2 commit như sau và chúng ra không biết được là commit cuối cùng cf5e6c0 là tổng hợp code của 3 lần amend commit.</p><pre>% git log --oneline<br>cf5e6c0 (HEAD -&gt; master) first commit of phase0<br>eb3a695 Initial commit</pre><p>Chúng ta chỉ biết được điều đó nếu chúng ra sử dụng cỗ máy thời gian git reflog mà thôi.</p><pre>% git reflog<br>cf5e6c0 (HEAD -&gt; master) HEAD@{0}: commit (amend): first commit of phase0<br>ef31eda HEAD@{1}: commit (amend): first commit of phase0<br>e0570a6 HEAD@{2}: commit (amend): first commit of phase0<br>a651857 HEAD@{3}: commit: first commit of phase0<br>eb3a695 HEAD@{4}: clone: from <a href="/service/mailto:git@github.com">git@github.com</a>:git/specs.git</pre><p>Các bạn có thể thấy là git reflog có nhiều và đầy đủ thông tin hơn rất nhiều git log.</p><p>Mong rằng kiến thức nho nhỏ này sẽ giúp các bạn có thể dễ dàng hơn trong việc xử lý những thay đổi code bất thường và gấp gáp.</p><p>Happy git, happy code!</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=d6084651dd61" width="1" height="1" alt=""><hr><p><a href="https://medium.com/agileb-it/ng%C6%B0%E1%BB%A3c-d%C3%B2ng-l%E1%BB%8Bch-s%E1%BB%AD-c%C3%B9ng-c%E1%BB%97-m%C3%A1y-th%E1%BB%9Di-gian-git-reflog-d6084651dd61">Ngược dòng lịch sử cùng cỗ máy thời gian git reflog</a> was originally published in <a href="https://medium.com/agileb-it">AgileB.it</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Một số lệnh hay dùng khi làm việc với postgresql]]></title>
            <link>https://medium.com/agileb-it/m%E1%BB%99t-s%E1%BB%91-l%E1%BB%87nh-hay-d%C3%B9ng-khi-l%C3%A0m-vi%E1%BB%87c-v%E1%BB%9Bi-postgresql-769a733fc4f5?source=rss-24f1e197a08f------2</link>
            <guid isPermaLink="false">https://medium.com/p/769a733fc4f5</guid>
            <category><![CDATA[database]]></category>
            <category><![CDATA[postgresql]]></category>
            <category><![CDATA[postgres]]></category>
            <category><![CDATA[psql]]></category>
            <dc:creator><![CDATA[Ha ĐANG]]></dc:creator>
            <pubDate>Wed, 20 May 2020 16:03:11 GMT</pubDate>
            <atom:updated>2020-10-06T02:42:19.994Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*HJVxL2Dj2adnIyR9VNSo4w.jpeg" /></figure><p>Gần đây do đặc thù công việc nên thường xuyên phải kiểm tra dữ liệu trong postgesql db. Bản thân tôi rất ngại cài đặt các công cụ GUI nên vẫn ưu tiên dùng CLI hơn. Mặc định khi cài đặt postgresql thì đã có sẵn psql để kết nối đến database rồi. Để kết nối đến một db đã có sẵn trong postgresql thì bạn chỉ cần chạy lệnh sau trên console</p><pre>$ psql --dbname furbar</pre><p>Nếu không chỉ định --dbname thì psql sẽ kết nối đến db có name trùng với tên đang truy cập, nên nếu db đó không tồn tại thì psql sẽ báo lỗi kiểu kiểu như sau</p><pre>psql: error: could not connect to server: FATAL:  database &quot;guest&quot; does not exist</pre><p>Sau khi kết nối db thành công console sẽ chuyển sang prompt của psql và sẵn sàng nhận các lệnh làm việc với db.</p><p>Tất nhiên, bạn hoàn toàn có thể dùng lệnh \? để xem hướng dẫn sử dụng các lệnh làm việc của psql. Tuy nhiên, khi mới sử dụng thì cũng khá bối rối nên các bạn có thể xem qua các lệnh, mà tôi nghĩ là hay được sử dụng, sau đây để làm việc với postgresql được nhanh hơn.</p><ol><li>\l hoặc \l+ Hiển thị các database đang có trong postgresql db</li><li>\c dbname Kết nối hoặc thay đổi db cần làm việc</li><li>\dn Liệt kê toàn bộ schema có trong db hiện tại</li><li>set search_path to schema_name chỉ định schema làm việc</li><li>\dt Liệt kê toàn bộ tên bảng có trong schema hiện tại</li><li>\d table_name hoặc \d+ table_name Xem chi tiết table definition</li><li>\q hoặc ctrl + d thoát khỏi phiên làm việc cùa psql</li></ol><p>Sau khi đã chỉ định chính xác schema cần làm việc rồi thì chúng ta có thể sử dụng các câu lệnh query để truy vấn và xem dữ liệu ở các table như ý muốn. Nếu dữ liệu dài và khó xem thì bạn có thể sử dụng thêm \x on hoặc \x auto thì dữ liệu sẽ được hiển thị dễ nhìn hơn rất nhiều.</p><p>Hy vọng là các câu lệnh psql trên sẽ giúp các bạn làm quen và làm việc với postgresql được dễ dàng hơn.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=769a733fc4f5" width="1" height="1" alt=""><hr><p><a href="https://medium.com/agileb-it/m%E1%BB%99t-s%E1%BB%91-l%E1%BB%87nh-hay-d%C3%B9ng-khi-l%C3%A0m-vi%E1%BB%87c-v%E1%BB%9Bi-postgresql-769a733fc4f5">Một số lệnh hay dùng khi làm việc với postgresql</a> was originally published in <a href="https://medium.com/agileb-it">AgileB.it</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Xua tan đi những hiểu lầm về lactate và axit lactic]]></title>
            <link>https://medium.com/agileb-it/xua-tan-%C4%91i-nh%E1%BB%AFng-hi%E1%BB%83u-l%E1%BA%A7m-v%E1%BB%81-lactate-v%C3%A0-axit-lactic-8208666802e2?source=rss-24f1e197a08f------2</link>
            <guid isPermaLink="false">https://medium.com/p/8208666802e2</guid>
            <category><![CDATA[physiology]]></category>
            <category><![CDATA[šport]]></category>
            <category><![CDATA[biochemistry]]></category>
            <category><![CDATA[running]]></category>
            <dc:creator><![CDATA[Ha ĐANG]]></dc:creator>
            <pubDate>Wed, 01 Apr 2020 16:47:49 GMT</pubDate>
            <atom:updated>2020-04-02T07:55:52.591Z</atom:updated>
            <content:encoded><![CDATA[<h4><strong><em>Mỗi khi bạn luyện tập ở cường độ cao thì cảm giác mệt mỏi sẽ xuất hiện. Bởi vì khi luyện tập ở cường độ cao thì axit lactic sẽ tích tụ dần ở trong máu; đến một ngưỡng nào đó thì bạn sẽ cảm thấy mệt mỏi. Sai, sự thật xảy ra trong cơ thể con người phức tạp hơn như vậy.</em></strong></h4><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*a6oZ38ms2o0ZCBbyhOh-lw.gif" /></figure><p>Một thách thức thú vị cho các HLV và các nhà khoa học thể thao phải đối mặt là làm sao có thể giải thích được cho các VĐV một cách ngắn gọn và dễ hiểu về mục đích và lợi ích của mỗi bài tập. Các đơn giản và phổ biến nhất là chỉ đưa ra những thông tin hữu ích để tối ưu các bài tập cho các VĐV. Tuy nhiên, nếu chúng ta giải thích quá đơn giản thì thường dẫn đến hiểu sai hoặc hiểu nhầm. Và đáng tiếc là rất nhiều VĐV đã hiểu nhầm về lactate.</p><p>Lactate hay axit lactic được cho là nhân tố gây ra cảm giác mệt mỏi cho các cơ vận động là cách giải thích nghe có vẻ khá hợp lý và dễ hiểu cho các quá trình phức tạp xảy ra bên trong cơ thể. Bởi vì lactate sẽ tăng lên khi các bài tập cường độ cao tăng, và khi hoạt động ở cường độ cao thì cảm giác mệt mỏi cũng sẽ xuất hiện thường xuyên hơn nên một vài người đã cho rằng lactate gây ra cảm giác mệt mỏi cho cơ thể. Nếu như chúng ta không hiểu rõ về nhịp tim thì chúng ta cũng sẽ nghĩ rằng có một sư liên quan tương tự giữa nhịp tim và cảm giác mệt mỏi. Cả nhịp tim lẫn sự mệt mỏi đều tăng lên khi chúng ra hoạt động ở cường độ cao, nhưng chúng ta đều biết rằng nhịp tim không hề gây ra mệt mỏi. Và sự thật là lactate cũng vậy, nó không phải là nguyên nhân gây ra cảm giác mệt mỏi.</p><p>Để hiểu thêm về lactate thì chúng ta cần tìm hiểu sơ qua về sinh hoá và sinh lý học. Tuy nhiên, cũng có khá nhiều cuốn sách giáo khoa về sinh lý học cung cấp kiến thức về lactate đã lỗi thời. Nhưng có một thông tin thú vị là các nghiên cứu gần đây đã giúp chúng ta có thể hiểu rõ hơn và sự trao đổi chất của lactate bên trong cơ thể.</p><p>Cơ thể con người sử dụng một vài quá trình trao đổi chất khác nhau để cung cấp năng lượng cho các cơ hoạt động. Mỗi một quá trình trao đổi chất lại chuyển hoá các dạng năng lượng khác nhau sang phân tử <strong>ATP</strong> (<strong>A</strong>denosine <strong>t</strong>ri<strong>p</strong>hosphate), một dạng phân tử năng lượng cao giúp các sợi cơ có thể co dãn.</p><p>Có ba cách thức trao đổi chất chính trong cơ thể con người là <strong>aerobic</strong> (hiếu khí), <strong>anaerobic</strong> (hiếm khí) và <strong>phosphocreatine</strong> (CP-ATP). Trong đó, <strong>phosphocreatine </strong>là cách thức trao đổi chất diễn ra nhanh nhất, có thể cung cấp năng lượng cho cơ thể rất nhanh chỉ trong vòng vài giây đồng hồ.</p><p>Trao đổi chất <strong>aerobic </strong>thì lại là quá trình phức tạp nhất và cung cấp cho cơ thể nhiều năng lượng nhất. Và quá trình này sử dụng gần như toàn bộ các nguồn cung cấp năng lượng có trong cơ thể con người (chất béo, chất đạm, đường glucoza và đặc biệt là có cả lactate). <em>Chính về thế mà ai muốn giảm béo thì tập môn </em><strong><em>aerobic </em></strong><em>nhé</em>. Sự chuyển đổi các nguồn năng lượng này sang <strong>ATP</strong> trải qua rất nhiều bước bên trong các tế bào cơ. Khí ôxy và năng lượng được truyền đến các ty thể (mitochondria) trong quá trình tổng hợp ATP hiếu khí. Trao đổi chất <strong>aerobic </strong>tuy là quá trình chậm nhất nhưng lại là quá trình trao đổi chất mang lại năng lượng hiệu quá nhất cho cơ thể.</p><p>Mặt khác, quá trình trao đổi chất <strong>anaerobic </strong>lại diễn ra khá nhanh, cung cấp ATP cho các cơ hoạt động mà không cần phải trải qua nhiều bước. Hơn nữa, nguồn năng lượng chính của nó là glycogen được lưu trữ bên trong và xung quanh ngay chính tại các cơ đó làm cho quá trình chuyển đổi sang ATP trở lên nhanh hơn. Quá trình này còn được gọi là quá trình bẻ gẫy đường glucoza (<strong>glycolysis</strong>).</p><p>Quá trình bẻ gãy đường glucoza tạo ra lactate và hydrogen. Nếu như lactate và hydrogen kết hợp với nhau thì tạo thành axit lactic. Tuy nhiên, chúng ta hiếm khi tìm thấy axit lactic trong máu, bởi vì lactate thường được giữ lại trong các tế bào hoặc được vận chuyển đến nhóm cơ và được sử dụng như một nguồn năng lượng tại các nhóm cơ đó. Khả năng sử dụng lactate như là một nguồn cung cấp năng lượng cho cơ thể được tối ưu thông qua các bài tập hiếu khí (<strong><em>aerobic</em></strong><em>), đặc biệt là các bài tập bền (</em>endurance training<em>).</em></p><p>Ngoài ra, lactate còn được vận chuyển đến não bộ, cung cấp năng lượng cho cơ tim, hoặc đến gan để chuyển đổi thành đường glucoza. Khi chúng ta hoạt động, cơ thể cần duy trì một lượng đường glucoza nhất định cho não bộ. Quá trình tổng hợp glucoza (gọi là <strong>gluconeogenesis</strong>) là một chức năng quan trọng của gan khi chúng ta hoạt động, và lactate có vai trò quan trọng nhất trong quá trình tổng hợp glucoza.</p><p>Trong khi đó thì sự tích tụ hyrogen (H+) từ quá trình bẻ gẫy glucoza làm giảm nồng độ pH, tức là tăng tính axit, phần lớn H+ được giữ tạm ở hệ thống đệm bicarbonate (H+ + HCO3- H2CO3 CO2 + H2O), và được chuyển thành H2O và CO2, CO2 thì được đào thải ra khỏi cơ thể thông qua phổi. Nếu tích tụ quá nhiều thì có một vài dấu hiệu chứng tỏ nó gây trở ngại cho các hoạt động co dãn của cơ. Tuy nhiên, các nghiên cứu gần đây thì lại cho thấy điều ngược lại. Quá trình bẻ gãy glucoza không phải là quá trình duy nhất làm tăng nồng độ axit trong cơ thể và sự tích tụ hydrogen cũng không phải là nhân tố tiềm năng duy nhất gây ra cảm giác mệt mỏi. Sự mệt mỏi khi cơ thể hoạt động quá tải là do các quá trình trao đổi chất khác như phốt phát vô cơ, và khả năng các cơ không thể bắt kịp được với tốc độ và áp lực co dãn mạnh do sự thiếu hụt Kali bên trong các tế bào cơ.</p><p>Lược dịch từ bài báo <a href="https://www.cyclingnews.com/features/lactate-and-lactic-acid-dispelling-the-myths/">Lactate and lactic acid — dispelling the myths</a> của tác giả <em>Dario Fredrick.</em></p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=8208666802e2" width="1" height="1" alt=""><hr><p><a href="https://medium.com/agileb-it/xua-tan-%C4%91i-nh%E1%BB%AFng-hi%E1%BB%83u-l%E1%BA%A7m-v%E1%BB%81-lactate-v%C3%A0-axit-lactic-8208666802e2">Xua tan đi những hiểu lầm về lactate và axit lactic</a> was originally published in <a href="https://medium.com/agileb-it">AgileB.it</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Go concurrency rest api]]></title>
            <link>https://medium.com/agileb-it/go-concurrency-rest-api-894f638bc347?source=rss-24f1e197a08f------2</link>
            <guid isPermaLink="false">https://medium.com/p/894f638bc347</guid>
            <category><![CDATA[tech]]></category>
            <category><![CDATA[golang]]></category>
            <category><![CDATA[go]]></category>
            <category><![CDATA[rest]]></category>
            <category><![CDATA[technology]]></category>
            <dc:creator><![CDATA[Ha ĐANG]]></dc:creator>
            <pubDate>Fri, 13 Oct 2017 07:12:48 GMT</pubDate>
            <atom:updated>2017-10-16T08:22:52.087Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/900/1*x4Dj3rJHyXgF3ES2VoIQVA.jpeg" /></figure><p>Đầu năm 2016, tôi và các cộng sự ở <a href="https://medium.com/u/c39386e4cd6e">Innovatube</a> đã rất hứng khởi và thích thú với tìm hiểu và ứng dụng Golang cho các dự án mới. Các lập trình viên khi tiếp cận với Golang thì đều cảm giác rất thân quen và dễ dàng có thể code nhoay nhoáy trong vòng 2–3 ngày hay đến 1 tuần là cùng. Bởi Golang được thừa kế từ ngôn ngữ lập trình C mà ngôn ngữ lập trình Cthì chắc lập trình viên nào cũng được học và sử dụng trong trường đại học cả rồi. Đây cũng là ý kiến chủ quan của một lập trình viên tay ngang và thuộc thế hệ 8x như tôi. Ngôn ngữ C/C++ thì bản thân tôi cũng chưa được học bao giờ. Chẳng là hồi học đại học năm thứ nhất đại học, tôi vẫn tiếp tục hì hục ngồi code các bài toán trên ngôn ngữ lập trình Pascal, ngôn ngữ lập trình mà tôi đã được học từ năm lớp 10 ở trường PTTH, thằng bạn cùng lớp thấy thế nên nó bảo tôi: “Nếu mày muốn thành lập trình viên chuyên nghiệp thì kiểu gì mày cũng phải học C/C++”. Nên tôi và nó có mò lên hiệu sách trên phố sách đình đám nhất Hà Thành, phố <strong>Tràng Tiền</strong>, lê la khắp các hiệu sách để tìm mua 1 quyển lập trình C++ và sau đó có mua thêm một quyển lập trình Assembly của <strong>Peter Norton</strong> khá nổi tiếng và phổ biến những năm đó thì phải. Tôi vẫn nhớ như in là tôi đã rất ấn tượng với bìa sách hình chân dung ở tư thế đang khoanh tay trước ngực của <strong>Peter Norton</strong>. Một phong thái tự tin và rất thông minh.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/379/1*cb7rGXZjz7ja7Qtq1O-Hig.jpeg" /><figcaption>Peter Norton</figcaption></figure><p>Thời của tôi là vậy, các thế hệ Y sau này chắc sẽ không còn học C nữa, mà có thể là <strong>Haskell</strong> hay một ngôn ngữ nào mới mẻ khác mà chúng ta chưa biết. Các ngôn ngữ lập trình hiện đại thì ngày càng dễ dàng cho lập trình viên hơn. Các lập trình viên ở <strong>Google</strong> cho ra đời <strong>Golang</strong> chắc cũng nhằm mục đích đơn giản hoá việc lập trình trên ngôn ngữ <strong>C</strong> mà vẫn đạt được hiệu năng cao.</p><p>Hôm nay, tôi làm một code mẫu tạo <strong>REST API</strong> đơn giản bằng Golang để thấy được việc lập trình <strong><em>concurrency</em></strong> bằng <strong>Go</strong> nó trở nên đơn giản đến mức nào nếu so sánh với việc làm tương tự trên ngôn ngữ lập trình <strong>C/C++</strong> hồi xưa. Tôi xin nhấn mạnh là <strong>C/C++</strong> của thời <strong>ông bà anh</strong> nhé vì bản thân <strong>C/C++ </strong>vẫn đang phát triển và mới cho ra phiên bản năm 2017 có nhiều điểm mới mà tôi cũng không biết cụ thể nó là cái gì? Và ở một cách tiếp cận khác, gần đây các ngôn ngữ lập trình đều có thư viện <strong><em>reactive programming</em></strong> cũng khá thuận tiện cho việc xử lý các yêu cầu <em>asynchronous</em> nhằm tăng độ response của các ứng dụng. Tuy nhiên, tuỳ vào yêu cầu bài toán và cách thiết kế hệ thống mà chúng ta cần lựa chọn sử dụng cách tiếp cận nào cho phù hợp và thuận tiện hơn.</p><p><strong>Yêu cầu</strong> của bài toán là viết một <strong>API</strong> trả về nhiệt độ trung bình từ nhiều nguồn số liệu của một thành phố. Thông thường, nếu sử dụng cách lập trình tuần tự thì chúng ra phải lấy nhiệt độ của thành phố của từng nguồn cung cấp rồi tính ra được nhiệt độ trung bình. Giả dụ chúng ta có 10 nguồn cung cấp số liệu và để lấy nhiệt độ của mỗi nguồn mất 1 giây thì tổng thời gian cho việc lấy nhiệt độ trung bình của <strong>API</strong> sẽ là 10 giây. Opsss, 10 giây, một thời gian khó có thể chấp nhận cho một <strong>API</strong> trả về kết quả.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/800/1*WhjCeluRnltiij-O1kPh-Q.jpeg" /><figcaption>…</figcaption></figure><p>Giải pháp để tăng hiệu năng cho <strong>API</strong> ở đây là sử dụng lập trình <strong><em>go concurrnecy</em></strong> kết hợp với <strong><em>channel</em></strong> để có thể cùng một lúc lấy được nhiệt độ của các nguồn cung cấp trong cùng một thời điểm. Kết quả là chúng ra chỉ cần, tương đối, 1 giây để có thể lấy được kết quả từ 10 hay nhiều nguồn số liệu khác nhau. Một sự khác biệt đáng kinh ngạc?</p><h3>Cụ thể các bước tiến hành cài đặt như sau.</h3><p>Trước tiên thì chúng ta định nghĩa một <strong><em>interface</em></strong> chung để lấy nhiệt độ của một thành phố từ các nguồn số liệu khác nhau</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/233da385a51cdf9aedae0de5755b4738/href">https://medium.com/media/233da385a51cdf9aedae0de5755b4738/href</a></iframe><p>Vì mỗi nguồn số liệu có cách lấy số liệu khác nhau nên chúng ta cần khai báo các <em>struct</em> tương ứng để cài đặt. Ở đây để đơn giản thì tôi lấy từ 2 nguồn số liệu là <strong><em>OpenWeatherMap</em></strong> và <strong><em>apixu</em></strong></p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/9dfaf99c6ba2f03873eae397b85009f9/href">https://medium.com/media/9dfaf99c6ba2f03873eae397b85009f9/href</a></iframe><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/09493074e8ef174565c4f4f558a4d122/href">https://medium.com/media/09493074e8ef174565c4f4f558a4d122/href</a></iframe><p>Sau khi, đã có thể lấy được số liệu từ các nguồn cụ thể thì chúng ta sẽ sử dụng <strong><em>go concurrency</em></strong> và <strong><em>channel</em></strong> để cài đặt cách thức để lấy số liệu của các nguồn được diễn ra cùng một lúc để giảm thiểu thời gian chờ đợi.</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/423e6c750a08c5fac38a38659faa786c/href">https://medium.com/media/423e6c750a08c5fac38a38659faa786c/href</a></iframe><p>Ở đây, các bạn có thể thấy việc gọi một method concurrency bằng Golang khá đơn giản, chỉ cần dùng từ khoá <strong><em>go</em></strong> trước lời gọi hàm mà thôi.</p><pre>go func(p weatherProvider) {...}(provider)</pre><p>Mỗi lời gọi này thì <strong>Go</strong> sẽ tạo riêng một <strong><em>goroutine</em></strong> chạy độc lập nhau nên việc lấy số liệu ở từng nguồn được thực hiện gần như đồng thời. Và các <strong><em>goroutine</em></strong> này sẽ giao tiếp với nhau thông qua <strong><em>channel</em></strong> nên chúng ta cũng định nghĩa một <strong><em>channel</em></strong> chung để có thể lưu trữ số liệu của các nguồn khác nhau ở cùng một <strong><em>channel</em></strong>.</p><pre>temps := make(chan float64, len(w)) <br>... <br>... <br>...  <br>temps &lt;- k</pre><p>Cuối cùng, thì tổng hợp lại chúng ta có thể expose thành một <strong>REST API</strong> thông qua hàm main() như sau</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/6ac05f6677055f09eadcbebfc43636a2/href">https://medium.com/media/6ac05f6677055f09eadcbebfc43636a2/href</a></iframe><p>Như vậy, chúng ta có thể dễ dàng cài đặt một <strong>REST API</strong> hiệu quả bằng cách sử dụng <strong><em>go concurrency</em></strong> và <strong><em>channel</em></strong>. Source code mẫu các bạn có thể xem trên github: <a href="https://github.com/hadv/go-concurrency-rest-api">https://github.com/hadv/go-concurrency-rest-api</a></p><p><em>Đăng ký email </em><a href="https://docs.google.com/forms/d/e/1FAIpQLScnwU2ZAS9LUQahxzEyFPv6-IdKtY1x_VaX-XEoICV2C54tmg/viewform"><em>tại đây</em></a><em> để nhận được bài viết về công nghệ và khởi nghiệp của Innovatube hàng tuần.</em></p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=894f638bc347" width="1" height="1" alt=""><hr><p><a href="https://medium.com/agileb-it/go-concurrency-rest-api-894f638bc347">Go concurrency rest api</a> was originally published in <a href="https://medium.com/agileb-it">AgileB.it</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Sử dụng Protobuf và gRPC để phát triển API hiệu năng cao]]></title>
            <link>https://medium.com/agileb-it/s%E1%BB%AD-d%E1%BB%A5ng-protobuf-v%C3%A0-grpc-%C4%91%E1%BB%83-ph%C3%A1t-tri%E1%BB%83n-api-hi%E1%BB%87u-n%C4%83ng-cao-d40036026035?source=rss-24f1e197a08f------2</link>
            <guid isPermaLink="false">https://medium.com/p/d40036026035</guid>
            <category><![CDATA[tech]]></category>
            <category><![CDATA[protobuf]]></category>
            <category><![CDATA[grpc]]></category>
            <category><![CDATA[golang]]></category>
            <category><![CDATA[programming]]></category>
            <dc:creator><![CDATA[Ha ĐANG]]></dc:creator>
            <pubDate>Wed, 19 Apr 2017 11:26:37 GMT</pubDate>
            <atom:updated>2020-09-24T09:08:47.492Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*fUR37-hx7Q2NvjT1PGSXpA.png" /></figure><p>Trong thời điểm hiện tại thì JSON REST API vẫn đang rất phổ biến và phổ thông nhất bởi tính dễ sử dụng của nó. Tuy nhiên, các hãng công nghệ lớn luôn luôn tìm kiếm những phương pháp mới để nâng cao hiệu năng cho sản phẩm của họ. Trong bài viết này, chúng ta sẽ tìm hiểu và khám phá về một framework RPC mới dựa trên <a href="https://developers.google.com/protocol-buffers/">protobufs</a> và HTTP/2 của <strong>Google</strong> có tên là <a href="http://www.grpc.io/">gRPC</a>.</p><p>Một tính năng ưu việt của gRPC là sử dụng HTTP/2, cho phép chúng ta có thể đồng thời phát triển cả HTTP/1.1 REST+JSON API như thông thường và cả giao tiếp hiệu năng cao thông qua gRPC trên cùng một TCP port. Phiên bản Go1.6 thì đã kèm theo thư viện <em>net/http2</em>.</p><p>Chúng ra sẽ bắt đầu bằng việc sử dụng gRPC để xây dựng một ứng dụng lấy tên là <strong>EchoService</strong> bằng <em>Golang</em>. Ứng dụng này chỉ đơn giản là trả về chính nội dung message mà nó nhận được.</p><h3>Cài đặt</h3><p>Cài đặt ProtoBuffers 3.0</p><pre>$ brew install — devel protobuf</pre><p>Sau đó chạy <em>`go get -u`</em> để cài đặt một số thư viện <em>`Golang`</em> cần thiết khác</p><pre>go get -u github.com/gengo/grpc-gateway/protoc-gen-grpc-gateway<br>go get -u github.com/gengo/grpc-gateway/protoc-gen-swagger<br>go get -u github.com/golang/protobuf/protoc-gen-go</pre><h3>Tạo một dịch vụ gRPC</h3><p>Trước tiên, chúng ta mô tả ứng dụng trong file <em>`service.proto`</em> theo chuẩn <strong>proto3</strong> như sau:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/966ee254bb1153710bd21f97067fde09/href">https://medium.com/media/966ee254bb1153710bd21f97067fde09/href</a></iframe><p>Trước khi cài đặt tính năng cho dịch vụ <strong>EchoService</strong> thì chúng ta cần chạy lệnh sau để tự động sinh ra các stub class cần thiết trong file <em>`service.pb.go`</em></p><pre>$ protoc -I/usr/local/include -I. \<br> -I$GOPATH/src \<br> -I$GOPATH/src/github.com/gengo/grpc-gateway/third_party/googleapis \<br> — go_out=Mgoogle/api/annotations.proto=github.com/gengo/grpc-gateway/third_party/googleapis/google/api,plugins=grpc:. \<br> service.proto<br></pre><p>Sau đó, chúng ta có thể cài đặt <strong>EchoService</strong> như trong file <em>`echo.go`</em> sau.</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/64d8362f9275b4abf835ab77207c7de4/href">https://medium.com/media/64d8362f9275b4abf835ab77207c7de4/href</a></iframe><p>Để kết nối đến với echo server trên thì chúng ra có thể cài đặt <em>`EchoServiceClient`</em> kết nối đến. Tuy nhiên, ở đây tôi bỏ qua bước này và chuyển sang tạo reverse-proxy cho kết nối thông qua REST JSON API. Để tạo ra reverse-proxy <em>`service.pb.gw.go`</em>, chúng ra chạy lệnh sau.</p><pre>$ protoc -I/usr/local/include -I. \<br> -I$GOPATH/src \<br> -I$GOPATH/src/github.com/gengo/grpc-gateway/third_party/googleapis \<br> — grpc-gateway_out=logtostderr=true:. \<br> ./service.proto</pre><p>Để tạo entry-point và khởi động server cho REST JSON API thì chúng ta tạo ra file <em>`main.go`</em> như sau</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/3d49f003e1d2d913cf0055ce956f08fd/href">https://medium.com/media/3d49f003e1d2d913cf0055ce956f08fd/href</a></iframe><p>Sau khi build và khởi động server thì chúng ta có thể gọi REST API bằng lệnh `curl -X POST “<a href="http://localhost:8080/v1/example/echo/grpc">http://localhost:8080/v1/example/echo/grpc</a>&quot;` và nhận được kết quả như sau</p><pre>{“id”:”grpc”}</pre><p>Ngoài ra, chúng ra có thể chạy lệnh sau để generate ra file service.swagger.json mô tả API theo chuẩn của <strong>Swagger</strong>.</p><pre>$ protoc -I/usr/local/include -I. \<br>&gt; -I$GOPATH/src \<br>&gt; -I$GOPATH/src/github.com/gengo/grpc-gateway/third_party/googleapis \<br>&gt; — swagger_out=logtostderr=true:. \<br>&gt; ./service.proto<br></pre><p>Chúng ta có thể xem mô tả API bằng cách mở file service.swagger.json trên <a href="http://editor.swagger.io/#/">Swagger Editor</a> như hình sau.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*bgAPMn3IHSxwGaO8sEvj6w.jpeg" /></figure><p>Như vậy, cùng với <strong>protobuf</strong> và <strong>gRPC</strong> chúng ta có thể nhanh chóng tạo ra các API đồng thời cho HTTP/1.1 REST+JSON API và RPC dựa trên protobuf và HTTP/2.</p><p>Chi tiết về source code các bạn có thể xem trên <a href="https://github.com/hadv/go-grpc">github</a>.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=d40036026035" width="1" height="1" alt=""><hr><p><a href="https://medium.com/agileb-it/s%E1%BB%AD-d%E1%BB%A5ng-protobuf-v%C3%A0-grpc-%C4%91%E1%BB%83-ph%C3%A1t-tri%E1%BB%83n-api-hi%E1%BB%87u-n%C4%83ng-cao-d40036026035">Sử dụng Protobuf và gRPC để phát triển API hiệu năng cao</a> was originally published in <a href="https://medium.com/agileb-it">AgileB.it</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
    </channel>
</rss>