<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: jinhua luo</title>
    <description>The latest articles on DEV Community by jinhua luo (@kingluo).</description>
    <link>https://dev.to/kingluo</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F918914%2Fba8460c9-88cc-4b87-ba91-f37cce405a8c.png</url>
      <title>DEV Community: jinhua luo</title>
      <link>https://dev.to/kingluo</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/kingluo"/>
    <language>en</language>
    <item>
      <title>Asynchronous nginx response header/body filter</title>
      <dc:creator>jinhua luo</dc:creator>
      <pubDate>Fri, 05 Jan 2024 07:40:09 +0000</pubDate>
      <link>https://dev.to/kingluo/asynchronous-nginx-response-headerbody-filter-33hi</link>
      <guid>https://dev.to/kingluo/asynchronous-nginx-response-headerbody-filter-33hi</guid>
      <description>&lt;p&gt;As we all know, due to the design limit, nginx header/body filters are synchronous and you should not perform blocking operations such as blocking system calls, decrypting, decompressing, or forwarding responses to other servers for further processing.&lt;/p&gt;

&lt;p&gt;In contrast, envoy supports &lt;a href="https://github.com/envoyproxy/envoy/blob/main/source/docs/async_http_filters.md"&gt;asynchronous HTTP filters&lt;/a&gt; on the response path.&lt;/p&gt;

&lt;p&gt;However, there are some use cases where the response headers and body from the upstream server must be processed asynchronously before being sent downstream, such as by requesting a Vault server to verify the digest of the body, or by scanning the content on a dedicated server for confidentiality reasons.&lt;/p&gt;

&lt;p&gt;How to interpret the upstream response asynchronously? And what’s the performance?&lt;/p&gt;

&lt;p&gt;Please visit my blog for more details:&lt;/p&gt;

&lt;p&gt;&lt;a href="http://luajit.io/posts/asynchronous-lua-filter-for-nginx-response/"&gt;http://luajit.io/posts/asynchronous-lua-filter-for-nginx-response/&lt;/a&gt;&lt;/p&gt;

</description>
    </item>
    <item>
      <title>burl: A simple but flexible HTTP/3 testing framework based on bash and curl</title>
      <dc:creator>jinhua luo</dc:creator>
      <pubDate>Fri, 05 Jan 2024 07:37:37 +0000</pubDate>
      <link>https://dev.to/kingluo/burl-a-simple-but-flexible-http3-testing-framework-based-on-bash-and-curl-26h</link>
      <guid>https://dev.to/kingluo/burl-a-simple-but-flexible-http3-testing-framework-based-on-bash-and-curl-26h</guid>
      <description>&lt;p&gt;A few months ago, when I ported the QUIC patches from nginx mainline to APISIX and tried to test it, I found that &lt;a href="https://metacpan.org/pod/Test::Nginx::Socket"&gt;test::nginx&lt;/a&gt; didn’t work very well. It uses the wrong listen directive parameter “http3” instead of “quic” (probably due to version differences).&lt;/p&gt;

&lt;p&gt;So I was wondering if I could design a simple testing framework because the latest curl already fully supports HTTP/3.&lt;/p&gt;

&lt;p&gt;Many testing frameworks prefer DSLs (Domain Specific Languages) such as test::nginx and &lt;a href="https://hurl.dev/"&gt;Hurl&lt;/a&gt; because it is more user-friendly. And it looks more data-driven. However, this is not good for developers looking to extend its functionality as they would need to learn the language behind it and take the time to understand the core and extension APIs by reading the code base of this testing framework. In other words, DSL and the language behind it create a knowledge gap for developers because it is not intuitive and cannot map the functions corresponding to the DSL to the language itself. Especially when the framework itself lacks capabilities and needs to be expanded, this gap will bring significant learning costs.&lt;/p&gt;

&lt;p&gt;Take test::nignx as an example, which is implemented in Perl. When I need to verify the response body and decrypt it by pre-shared key, The built-in function doesn’t help as it only supports regular expression matching. So I need to write a bunch of &lt;a href="https://metacpan.org/pod/Test::Nginx::Socket#add_response_body_check"&gt;Perl code&lt;/a&gt; to do this. However, I’m not familiar with Perl, so this isn’t an easy task. As we all know, Perl is not a mainstream language and has a steep learning curve. At that time, I was thinking, how great it would be if I could write code in Python.&lt;/p&gt;

&lt;p&gt;The expressive capabilities of test::nginx are limited, even though it already supports sending requests using curl, because it is not a scripting system (Of course, you can extend it using Perl modules, but due to the limitations of the framework design, this is a difficult task for most people).&lt;/p&gt;

&lt;p&gt;It assumes your input is a text string, you cannot use other formats such as protobuf, and you cannot compress or encrypt text.&lt;br&gt;
The only output post-processing supported is regular matching, so you cannot parse and verify other forms of content, such as decryption.&lt;br&gt;
You cannot describe complex test procedures, such as sending three requests to trigger the request quota and verify the rate-limiting results.&lt;br&gt;
You cannot make other types of requests such as GRPC, or SOAP.&lt;br&gt;
In my practice, when I have such needs that exceed test::nginx’s capabilities, I have to delegate them to the lua part, which is very troublesome.&lt;/p&gt;

&lt;p&gt;For example, when I need to parse and verify the correctness of the CAS Auth plugin, it involves a lot of client-side CAS protocol details, so I have to implement the entire logic in the lua part, which increases the difficulty of maintaining this mix. As we all know, neither Perl nor Lua are popular languages. ;-(&lt;/p&gt;

&lt;p&gt;You can see the test case below involves additional lua lib development to test CAS. And, to accommodate test::nginx’s lack of response parsing, you have to do the conversion in the Lua environment. Quite annoying, at least to me, because I’ve written a lot of test cases like this over the years.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight lua"&gt;&lt;code&gt;&lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="n"&gt;TEST&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;login&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="n"&gt;logout&lt;/span&gt; &lt;span class="n"&gt;ok&lt;/span&gt;
&lt;span class="c1"&gt;--- config&lt;/span&gt;
    &lt;span class="n"&gt;location&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;content_by_lua_block&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="kd"&gt;local&lt;/span&gt; &lt;span class="n"&gt;http&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;require&lt;/span&gt; &lt;span class="s2"&gt;"resty.http"&lt;/span&gt;
            &lt;span class="kd"&gt;local&lt;/span&gt; &lt;span class="n"&gt;httpc&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;new&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
            &lt;span class="c1"&gt;-- Test-specific CAS lua lib&lt;/span&gt;
            &lt;span class="kd"&gt;local&lt;/span&gt; &lt;span class="n"&gt;kc&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;require&lt;/span&gt; &lt;span class="s2"&gt;"lib.keycloak_cas"&lt;/span&gt;

            &lt;span class="kd"&gt;local&lt;/span&gt; &lt;span class="n"&gt;path&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"/uri"&lt;/span&gt;
            &lt;span class="kd"&gt;local&lt;/span&gt; &lt;span class="n"&gt;uri&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"http://127.0.0.1:"&lt;/span&gt; &lt;span class="o"&gt;..&lt;/span&gt; &lt;span class="n"&gt;ngx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;server_port&lt;/span&gt;
            &lt;span class="kd"&gt;local&lt;/span&gt; &lt;span class="n"&gt;username&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"test"&lt;/span&gt;
            &lt;span class="kd"&gt;local&lt;/span&gt; &lt;span class="n"&gt;password&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"test"&lt;/span&gt;

            &lt;span class="kd"&gt;local&lt;/span&gt; &lt;span class="n"&gt;res&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;cas_cookie&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;keycloak_cookie&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;kc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;login_keycloak&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;uri&lt;/span&gt; &lt;span class="o"&gt;..&lt;/span&gt; &lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;username&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;password&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="ow"&gt;or&lt;/span&gt; &lt;span class="n"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'Location'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;~=&lt;/span&gt; &lt;span class="n"&gt;path&lt;/span&gt; &lt;span class="k"&gt;then&lt;/span&gt;
                &lt;span class="n"&gt;ngx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ngx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ERR&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                &lt;span class="n"&gt;ngx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;exit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;500&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="k"&gt;end&lt;/span&gt;
            &lt;span class="n"&gt;res&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;httpc&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;request_uri&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;uri&lt;/span&gt; &lt;span class="o"&gt;..&lt;/span&gt; &lt;span class="n"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'Location'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="n"&gt;method&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"GET"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="n"&gt;headers&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"Cookie"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;cas_cookie&lt;/span&gt;
                &lt;span class="p"&gt;}&lt;/span&gt;
            &lt;span class="p"&gt;})&lt;/span&gt;
            &lt;span class="nb"&gt;assert&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;status&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="n"&gt;ngx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;say&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;body&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

            &lt;span class="n"&gt;res&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;kc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;logout_keycloak&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;uri&lt;/span&gt; &lt;span class="o"&gt;..&lt;/span&gt; &lt;span class="s2"&gt;"/logout"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;cas_cookie&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;keycloak_cookie&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="nb"&gt;assert&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;status&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="c1"&gt;--- response_body_like&lt;/span&gt;
&lt;span class="n"&gt;uri&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;uri&lt;/span&gt;
&lt;span class="n"&gt;cookie&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;
&lt;span class="n"&gt;host&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;127&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;1984&lt;/span&gt;
&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;agent&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;
&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;real&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;ip&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;127&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Similarly, Hurl also uses a DSL to describe tests. Its backend language is Rust. So in terms of scalability, it seems to be worse than test::nginx because it needs to compile the code. However, Rust’s ecosystem is much better than Perl’s. So you can write code easily. But anyway, you need to write the code in a different language than the DSL.&lt;/p&gt;

&lt;p&gt;Is there a simple testing framework that can satisfy the following requirements?&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;No DSL, just simple shell scripts&lt;/li&gt;
&lt;li&gt;Easy to extend and programming language agnostic&lt;/li&gt;
&lt;li&gt;Can be used to initialize and test any server, not limited to nginx, such as envoy&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Please visit my blog for more details:&lt;br&gt;
&lt;a href="http://luajit.io/posts/a-simple-http3-testing-framework-based-on-bash-and-curl/"&gt;http://luajit.io/posts/a-simple-http3-testing-framework-based-on-bash-and-curl/&lt;/a&gt;&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Implement grpc client in rust language for openresty/Nginx</title>
      <dc:creator>jinhua luo</dc:creator>
      <pubDate>Sun, 27 Nov 2022 07:20:51 +0000</pubDate>
      <link>https://dev.to/kingluo/implement-grpc-client-in-rust-language-for-openrestynginx-4i59</link>
      <guid>https://dev.to/kingluo/implement-grpc-client-in-rust-language-for-openrestynginx-4i59</guid>
      <description>&lt;p&gt;Back in 2019, when I work as individual postgresql/nginx consultant.&lt;br&gt;
Many clients ask me if they could do hybrid programming in nginx.&lt;/p&gt;

&lt;p&gt;They said, nginx/openresty is powerful and high performance, but the most significant&lt;br&gt;
drawback is it lacks of ecosystem of mainstream programming languages, especially&lt;br&gt;
when they have to interact with popular frameworks, e.g. kafka, consul, k8s, etc.&lt;br&gt;
The C/Lua ecosystem is slim, even if they bite the bullet and write down C/Lua modules,&lt;br&gt;
they face the awkward fact that most frameworks do not provide C/Lua API.&lt;/p&gt;

&lt;p&gt;As known, nginx is multi-proceesses and single threading. In the main thread, it does&lt;br&gt;
nonblocking async event processing. Openresty wraps it in Lua API, combines the coroutine&lt;br&gt;
and epoll into coscoket concept.&lt;/p&gt;

&lt;p&gt;Meanwhile, each mainstream programming language has its own threading model. So the problem is,&lt;br&gt;
how to couple with them in coroutine way? For example, when I call the function in Rust&lt;br&gt;
(maybe runs in a thread, or maybe in tokio async task, whatever), how to make it nonblocking in&lt;br&gt;
Lua land, and make the call/return in yield/resume way?&lt;/p&gt;

&lt;p&gt;Of course, the traditional way is to spawn an individual process to run Rust,&lt;br&gt;
and at the lua side, use unix domain socket to do the communication, as known as proxy model.&lt;br&gt;
But that way is low-efficient (either development or runtime).&lt;br&gt;
You need to encode/decode the messsage in TLV (type-length-value) format,&lt;br&gt;
and, you need to maintain nginx and proxy process separately, as well as failover and load-balance topics.&lt;/p&gt;

&lt;p&gt;Could I make it simple like FFI call but with high performance?&lt;br&gt;
The answer is yes, I created lua-resty-ffi.&lt;/p&gt;
&lt;h2&gt;
  
  
  lua-resty-ffi
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://github.com/kingluo/lua-resty-ffi" rel="noopener noreferrer"&gt;https://github.com/kingluo/lua-resty-ffi&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;lua-resty-ffi provides an efficient and generic API to do hybrid programming in openresty with mainstream languages&lt;br&gt;
(&lt;a href="https://github.com/kingluo/lua-resty-ffi/examples/go" rel="noopener noreferrer"&gt;Go&lt;/a&gt;, &lt;a href="https://github.com/kingluo/lua-resty-ffi/examples/python" rel="noopener noreferrer"&gt;Python&lt;/a&gt;, &lt;a href="https://github.com/kingluo/lua-resty-ffi/examples/java" rel="noopener noreferrer"&gt;Java&lt;/a&gt;, &lt;a href="https://github.com/kingluo/lua-resty-ffi/examples/rust" rel="noopener noreferrer"&gt;Rust&lt;/a&gt;, etc.).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Features:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;nonblcking, in coroutine way&lt;/li&gt;
&lt;li&gt;simple but extensible interface, supports any C ABI compliant language&lt;/li&gt;
&lt;li&gt;once and for all, no need to write C/Lua codes to do coupling anymore&lt;/li&gt;
&lt;li&gt;high performance, faster than unix domain socket way&lt;/li&gt;
&lt;li&gt;generic loader library for python/java&lt;/li&gt;
&lt;li&gt;any serialization message format you like&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;
  
  
  Architecture
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fuubv168jy6qwi9x9af29.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fuubv168jy6qwi9x9af29.png" width="721" height="544"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The combination of library and configuration would init a new runtime,&lt;br&gt;
which represents some threads or goroutines to do jobs.&lt;/p&gt;

&lt;p&gt;lua-resty-ffi has a high performance IPC mechanism, based on request-response model.&lt;/p&gt;

&lt;p&gt;The request from lua is appended to a queue. This queue is protected by &lt;code&gt;pthread mutex&lt;/code&gt;.&lt;br&gt;
The language runtime polls this queue, if it's empty, then wait on the queue via &lt;code&gt;pthread_cond_wait()&lt;/code&gt;.&lt;br&gt;
In busy scenario, almost all enqueue/dequeue happens in userspace, without &lt;code&gt;futex&lt;/code&gt; system calls.&lt;/p&gt;

&lt;p&gt;lua-resty-ffi makes full use of nginx nonblocking event loop to dispatch the response from the language runtime.&lt;br&gt;
The response would be injected into the global done queue of nginx, and notify the nginx main thread via eventfd&lt;br&gt;
to handle it. In main thread, the response handler would setup the return value and resume the coroutine&lt;br&gt;
waiting on the response.&lt;/p&gt;

&lt;p&gt;As known, linux eventfd is high performance. It's just an accumulated counter, so multiple responses would be folded into one event.&lt;/p&gt;

&lt;p&gt;Both request and response data are exchanged in userspace.&lt;/p&gt;
&lt;h3&gt;
  
  
  What it looks like?
&lt;/h3&gt;

&lt;p&gt;Take python as example.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/kingluo/lua-resty-ffi/blob/main/examples/python/ffi/echo.py" rel="noopener noreferrer"&gt;&lt;code&gt;ffi/echo.py&lt;/code&gt;&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;cffi&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;FFI&lt;/span&gt;
&lt;span class="n"&gt;ffi&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;FFI&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="n"&gt;ffi&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;cdef&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;
char *strdup(const char *s);
void* ngx_http_lua_ffi_task_poll(void *p);
char* ngx_http_lua_ffi_get_req(void *tsk, int *len);
void ngx_http_lua_ffi_respond(void *tsk, int rc, char* rsp, int rsp_len);
&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;C&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ffi&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;dlopen&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;threading&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;State&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;poll&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;tq&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;task&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;C&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ngx_http_lua_ffi_task_poll&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ffi&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;cast&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;void*&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;tq&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;task&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;ffi&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                &lt;span class="k"&gt;break&lt;/span&gt;
            &lt;span class="n"&gt;req&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;C&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ngx_http_lua_ffi_get_req&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;task&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ffi&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="n"&gt;res&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;C&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;strdup&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;req&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="n"&gt;C&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ngx_http_lua_ffi_respond&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;task&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;res&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;exit python echo runtime&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;init&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cfg&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;tq&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;st&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;State&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;t&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;threading&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Thread&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;target&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;st&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;poll&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tq&lt;/span&gt;&lt;span class="p"&gt;,))&lt;/span&gt;
    &lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;daemon&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;True&lt;/span&gt;
    &lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;start&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Calls it like normal function call in your lua coroutine:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight lua"&gt;&lt;code&gt;&lt;span class="kd"&gt;local&lt;/span&gt; &lt;span class="n"&gt;demo&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ngx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;load_ffi&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"ffi_python3"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"ffi.echo?,init"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;is_global&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;unpin&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="kd"&gt;local&lt;/span&gt; &lt;span class="n"&gt;ok&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;res&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;demo&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;echo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"hello"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nb"&gt;assert&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ok&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nb"&gt;assert&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;res&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s2"&gt;"hello"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Benchmark
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fwxtczy87sq2qc7l7of0q.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fwxtczy87sq2qc7l7of0q.png" width="672" height="428"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Send 100,000 requests, in length of 512B, 4096B, 10KB, 100KB respectively.&lt;br&gt;
The result is in seconds, lower is better.&lt;/p&gt;

&lt;p&gt;You could see that lua-resty-ffi is faster than unix domain socket, and the difference&lt;br&gt;
is proportional to the length.&lt;/p&gt;

&lt;p&gt;Check &lt;a href="https://github.com/kingluo/lua-resty-ffi/edit/main/benchmark.md" rel="noopener noreferrer"&gt;benchmark&lt;/a&gt; in detail.&lt;/p&gt;
&lt;h2&gt;
  
  
  Develop GRPC client in Rust
&lt;/h2&gt;

&lt;p&gt;Let's use lua-resty-ffi to do some useful stuff.&lt;/p&gt;

&lt;p&gt;In k8s, GRPC is standard RPC. How to make GRPC calls in lua?&lt;/p&gt;

&lt;p&gt;Let's use Rust to develop a simple but complete GRPC client for lua,&lt;br&gt;
covering unary, client/server/bidirectional streaming calls, as well as TLS/MTLS.&lt;/p&gt;

&lt;p&gt;In this example, you could also see the high development efficiency using lua-resty-ffi.&lt;/p&gt;

&lt;p&gt;Check &lt;a href="https://github.com/kingluo/lua-resty-ffi/tree/main/examples/rust" rel="noopener noreferrer"&gt;https://github.com/kingluo/lua-resty-ffi/tree/main/examples/rust&lt;/a&gt; for code.&lt;/p&gt;
&lt;h3&gt;
  
  
  tonic
&lt;/h3&gt;

&lt;blockquote&gt;
&lt;p&gt;tonic is a gRPC over HTTP/2 implementation focused on high performance, interoperability,&lt;br&gt;
and flexibility. This library was created to have first class support of async/await&lt;br&gt;
and to act as a core building block for production systems written in Rust.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;a href="https://github.com/hyperium/tonic" rel="noopener noreferrer"&gt;tonic&lt;/a&gt; is my favourite rust library.&lt;br&gt;
It's based on &lt;a href="https://github.com/hyperium/hyper" rel="noopener noreferrer"&gt;hyper&lt;/a&gt; and &lt;a href="https://tokio.rs/" rel="noopener noreferrer"&gt;tokio&lt;/a&gt;.&lt;br&gt;
Everything works in &lt;code&gt;async/await&lt;/code&gt; way.&lt;/p&gt;
&lt;h3&gt;
  
  
  Use low-level APIs
&lt;/h3&gt;

&lt;p&gt;Normal steps to develop grpc client:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;write a &lt;code&gt;.proto&lt;/code&gt; file&lt;/li&gt;
&lt;li&gt;compiles it into high-level API for use, via &lt;code&gt;build.rs&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;calls generated API&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For example:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;helloworld.proto&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight protobuf"&gt;&lt;code&gt;&lt;span class="kn"&gt;package&lt;/span&gt; &lt;span class="nn"&gt;helloworld&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// The greeting service definition.&lt;/span&gt;
&lt;span class="kd"&gt;service&lt;/span&gt; &lt;span class="n"&gt;Greeter&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// Sends a greeting&lt;/span&gt;
  &lt;span class="k"&gt;rpc&lt;/span&gt; &lt;span class="n"&gt;SayHello&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;HelloRequest&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;returns&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;HelloReply&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// The request message containing the user's name.&lt;/span&gt;
&lt;span class="kd"&gt;message&lt;/span&gt; &lt;span class="nc"&gt;HelloRequest&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// The response message containing the greetings&lt;/span&gt;
&lt;span class="kd"&gt;message&lt;/span&gt; &lt;span class="nc"&gt;HelloReply&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="kd"&gt;message&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;client.rs&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;tonic&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;Request&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;HelloRequest&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"Tonic"&lt;/span&gt;&lt;span class="nf"&gt;.into&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="nf"&gt;.say_hello&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="k"&gt;.await&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;But it's not suitable for lua, because it's expected to call arbitrary protobuf interface&lt;br&gt;
and does not need to compile &lt;code&gt;.proto&lt;/code&gt; file.&lt;/p&gt;

&lt;p&gt;So we have to look at low-level API provided by tonic.&lt;/p&gt;

&lt;p&gt;Let's check the generated file:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;helloworld.rs&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="nd"&gt;#[derive(Clone,&lt;/span&gt; &lt;span class="nd"&gt;PartialEq,&lt;/span&gt; &lt;span class="nd"&gt;::prost::Message)]&lt;/span&gt;
&lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="n"&gt;HelloRequest&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nd"&gt;#[prost(string,&lt;/span&gt; &lt;span class="nd"&gt;tag=&lt;/span&gt;&lt;span class="s"&gt;"1"&lt;/span&gt;&lt;span class="nd"&gt;)]&lt;/span&gt;
    &lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;prost&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;alloc&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;string&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt;
&lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;say_hello&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;impl&lt;/span&gt; &lt;span class="nn"&gt;tonic&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;IntoRequest&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;super&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;HelloRequest&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;Result&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nn"&gt;tonic&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Response&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;super&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;HelloReply&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nn"&gt;tonic&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Status&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;codec&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;tonic&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;codec&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;ProstCodec&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;default&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;path&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;http&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;uri&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;PathAndQuery&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;from_static&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="s"&gt;"/helloworld.Greeter/SayHello"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="py"&gt;.inner&lt;/span&gt;&lt;span class="nf"&gt;.unary&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="nf"&gt;.into_request&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;codec&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="k"&gt;.await&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  message encode/decode
&lt;/h3&gt;

&lt;p&gt;You could see that the request structure is marked with &lt;code&gt;::prost::Message&lt;/code&gt; attribute.&lt;br&gt;
And it uses &lt;code&gt;tonic::codec::ProstCodec::default()&lt;/code&gt; to do the encoding.&lt;/p&gt;

&lt;p&gt;This part could be done in lua using &lt;a href="https://github.com/starwing/lua-protobuf" rel="noopener noreferrer"&gt;&lt;code&gt;lua-protobuf&lt;/code&gt;&lt;/a&gt;.&lt;br&gt;
It could do encode/decode based on the &lt;code&gt;.proto&lt;/code&gt; file, just like JSON encode/decode.&lt;/p&gt;

&lt;p&gt;Check &lt;a href="https://github.com/kingluo/lua-resty-ffi/blob/main/examples/rust/grpc_client.lua" rel="noopener noreferrer"&gt;&lt;code&gt;grpc_client.lua&lt;/code&gt;&lt;/a&gt; for detail.&lt;/p&gt;

&lt;p&gt;But wait, if you do encoding in lua, then how to tell rust to bypass it?&lt;/p&gt;

&lt;p&gt;The answer is customized codec, instead of &lt;code&gt;ProstCodec&lt;/code&gt;, so that we could transfer the byte array "AS IS".&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="nf"&gt;EncodedBytes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="nb"&gt;c_char&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;usize&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;bool&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt;
&lt;span class="k"&gt;impl&lt;/span&gt; &lt;span class="n"&gt;Encoder&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;MyCodec&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="n"&gt;Item&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;EncodedBytes&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="n"&gt;Error&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Status&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;encode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;item&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;Self&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Item&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;dst&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;EncodeBuf&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nv"&gt;'_&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;Result&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="k"&gt;Self&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Error&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;slice&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;unsafe&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nn"&gt;slice&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;from_raw_parts&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;item&lt;/span&gt;&lt;span class="na"&gt;.0&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="nb"&gt;u8&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;item&lt;/span&gt;&lt;span class="na"&gt;.1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
        &lt;span class="n"&gt;dst&lt;/span&gt;&lt;span class="nf"&gt;.put_slice&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;slice&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;impl&lt;/span&gt; &lt;span class="n"&gt;Decoder&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;MyCodec&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="n"&gt;Item&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;EncodedBytes&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="n"&gt;Error&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Status&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;decode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;buf&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;DecodeBuf&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nv"&gt;'_&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;Result&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nb"&gt;Option&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;Self&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Item&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;Self&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Error&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;chunk&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;buf&lt;/span&gt;&lt;span class="nf"&gt;.chunk&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;len&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;chunk&lt;/span&gt;&lt;span class="nf"&gt;.len&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;len&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;cbuf&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;unsafe&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nn"&gt;libc&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;malloc&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;len&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
            &lt;span class="k"&gt;unsafe&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nn"&gt;libc&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;memcpy&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cbuf&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;chunk&lt;/span&gt;&lt;span class="nf"&gt;.as_ptr&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="nb"&gt;c_void&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;len&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
            &lt;span class="n"&gt;buf&lt;/span&gt;&lt;span class="nf"&gt;.advance&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;len&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="nf"&gt;Ok&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;Some&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;EncodedBytes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cbuf&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="nb"&gt;c_char&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;len&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;false&lt;/span&gt;&lt;span class="p"&gt;)))&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For encode, we put the original byte array encoded by lua into the request.&lt;/p&gt;

&lt;p&gt;For decode, we copy the byte array to lua and let lua decode it later.&lt;/p&gt;

&lt;p&gt;Then, we could use &lt;a href="https://github.com/hyperium/tonic/blob/933f5607350af983e479219a2ef3688484976968/tonic/src/client/grpc.rs#L140" rel="noopener noreferrer"&gt;&lt;code&gt;unary&lt;/code&gt;&lt;/a&gt; directly.&lt;/p&gt;

&lt;h3&gt;
  
  
  connection management
&lt;/h3&gt;

&lt;p&gt;Each GRPC request is called upon the connection object (interestingly, the connection in tonic has retry mechanism).&lt;br&gt;
I use hashmap to store connections, and assign each connection a unique string, so that the lua side&lt;br&gt;
could index it. In Lua, you could explicitly close it or let the lua gc to handle the close.&lt;/p&gt;

&lt;p&gt;Each connection could be configured with TLS/MTLS info, e.g. CA certificate, private key.&lt;/p&gt;
&lt;h3&gt;
  
  
  message format of request and response
&lt;/h3&gt;

&lt;p&gt;Now, let's look at the message format between lua and rust, which is most important part.&lt;/p&gt;
&lt;h4&gt;
  
  
  request
&lt;/h4&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="nd"&gt;#[derive(Deserialize,&lt;/span&gt; &lt;span class="nd"&gt;Debug)]&lt;/span&gt;
&lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="n"&gt;GrpcCommand&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;cmd&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;i32&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;host&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Option&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;ca&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Option&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;cert&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Option&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;priv_key&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Option&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Option&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Option&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Base64&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Since we need to handle different kinds of functions, e.g. create a connection, GRPC unary call, etc.&lt;br&gt;
So it's obviously we need a structure to describe all information.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;cmd&lt;/code&gt;: the command indicator, e.g. &lt;code&gt;NEW_CONNECTION&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;key&lt;/code&gt;: depends on &lt;code&gt;cmd&lt;/code&gt;, could be connection URL, connection id or streaming id.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;host&lt;/code&gt;: HTTP2 host header, used for TLS&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;ca&lt;/code&gt;, &lt;code&gt;cert&lt;/code&gt;, &lt;code&gt;priv_key&lt;/code&gt;: TLS related stuff&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;path&lt;/code&gt;: full-qualified GRPC method string, e.g. &lt;code&gt;/helloworld.Greeter/SayHello&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;payload&lt;/code&gt;: the based64 and protobuf encoded requset byte array by lua&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Note that, all optional fields use &lt;code&gt;Option&amp;lt;&amp;gt;&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;JSON is high performance in lua, so no need to worry about the overhead.&lt;/p&gt;

&lt;p&gt;Check my &lt;a href="http://luajit.io/post/json-vs-flatbuffers-in-lua/" rel="noopener noreferrer"&gt;blog post&lt;/a&gt;&lt;br&gt;
for benchmark between JSON and protobuf if you are interested in this part.&lt;/p&gt;
&lt;h4&gt;
  
  
  response
&lt;/h4&gt;

&lt;p&gt;This part is more straightforward than the request.&lt;br&gt;
Because for each request type, you could expect only one kind of result.&lt;/p&gt;

&lt;p&gt;For example, for &lt;code&gt;NEW_CONNECTION&lt;/code&gt;, it returns connection id string;&lt;br&gt;
for &lt;code&gt;UNARY&lt;/code&gt;, it returns the protobuf encoded response from the network.&lt;/p&gt;

&lt;p&gt;So, returning the &lt;code&gt;malloc()&lt;/code&gt; byte array is ok, no need to wrap it in JSON.&lt;/p&gt;
&lt;h3&gt;
  
  
  GRPC streaming
&lt;/h3&gt;

&lt;p&gt;Under the hood, no matter which type the call is,&lt;br&gt;
&lt;a href="https://github.com/hyperium/tonic/blob/933f5607350af983e479219a2ef3688484976968/tonic/src/client/grpc.rs#L155" rel="noopener noreferrer"&gt;unary request&lt;/a&gt;,&lt;br&gt;
&lt;a href="https://github.com/hyperium/tonic/blob/933f5607350af983e479219a2ef3688484976968/tonic/src/client/grpc.rs#L159" rel="noopener noreferrer"&gt;client-side streaming&lt;/a&gt;,&lt;br&gt;
&lt;a href="https://github.com/hyperium/tonic/blob/933f5607350af983e479219a2ef3688484976968/tonic/src/client/grpc.rs#L196" rel="noopener noreferrer"&gt;server-side streaming&lt;/a&gt; or&lt;br&gt;
&lt;a href="https://github.com/hyperium/tonic/blob/933f5607350af983e479219a2ef3688484976968/tonic/src/client/grpc.rs#L215" rel="noopener noreferrer"&gt;bidirectional streaming&lt;/a&gt;,&lt;br&gt;
tonic encapsulates the request and response into&lt;br&gt;
&lt;a href="https://docs.rs/futures/latest/futures/stream/trait.Stream.html" rel="noopener noreferrer"&gt;&lt;code&gt;Stream&lt;/code&gt;&lt;/a&gt; object.&lt;/p&gt;

&lt;p&gt;So we just need to establish the bridge between lua and &lt;code&gt;Stream&lt;/code&gt;, then we could support GRPC streaming in lua.&lt;/p&gt;
&lt;h4&gt;
  
  
  send direction
&lt;/h4&gt;

&lt;p&gt;We create a channel pair, wrap the receiver part into request &lt;code&gt;Stream&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;send_tx&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;send_rx&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;mpsc&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;unbounded_channel&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;EncodedBytes&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;send_stream&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;tokio_stream&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;wrappers&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;UnboundedReceiverStream&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;send_rx&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt;
&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;stream&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;cli&lt;/span&gt;
    &lt;span class="nf"&gt;.streaming&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;tonic&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;Request&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;send_stream&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;MyCodec&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;.await&lt;/span&gt;
    &lt;span class="nf"&gt;.unwrap&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="nf"&gt;.into_inner&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then the call upon the stream would be transfer to the network directly.&lt;/p&gt;

&lt;h4&gt;
  
  
  recv direction
&lt;/h4&gt;

&lt;p&gt;We create an async task to handle the response retrieval.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="nn"&gt;tokio&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;spawn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;move&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt;
    &lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="nf"&gt;Some&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;task&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;recv_rx&lt;/span&gt;&lt;span class="nf"&gt;.recv&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="k"&gt;.await&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="nf"&gt;Some&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;res&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;stream&lt;/span&gt;&lt;span class="nf"&gt;.message&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="k"&gt;.await&lt;/span&gt;&lt;span class="nf"&gt;.unwrap&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;unsafe&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="nf"&gt;ngx_http_lua_ffi_respond&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;task&lt;/span&gt;&lt;span class="na"&gt;.0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;res&lt;/span&gt;&lt;span class="na"&gt;.0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;res&lt;/span&gt;&lt;span class="na"&gt;.1&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nb"&gt;i32&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  close the send direction
&lt;/h4&gt;

&lt;p&gt;Thanks to the channel characteristic, when we drop the send part of the channel pair,&lt;br&gt;
the receiver part would be closed and in turn close the send direction of GRPC stream.&lt;/p&gt;
&lt;h3&gt;
  
  
  Final look
&lt;/h3&gt;

&lt;p&gt;Unary call:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight lua"&gt;&lt;code&gt;&lt;span class="kd"&gt;local&lt;/span&gt; &lt;span class="n"&gt;ok&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;conn&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;grpc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;connect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"[::1]:50051"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;host&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"example.com"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;ca&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;grpc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;readfile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"/opt/tonic/examples/data/tls/ca.pem"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="n"&gt;cert&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;grpc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;readfile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"/opt/tonic/examples/data/tls/client1.pem"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="n"&gt;priv_key&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;grpc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;readfile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"/opt/tonic/examples/data/tls/client1.key"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nb"&gt;assert&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ok&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="kd"&gt;local&lt;/span&gt; &lt;span class="n"&gt;ok&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;res&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;conn&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;unary&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="s2"&gt;"/grpc.examples.echo.Echo/UnaryEcho"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;message&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"hello"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nb"&gt;assert&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ok&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nb"&gt;assert&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;message&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s2"&gt;"hello"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;conn&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;close&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Streaming calls:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight lua"&gt;&lt;code&gt;&lt;span class="kd"&gt;local&lt;/span&gt; &lt;span class="n"&gt;ok&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;conn&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;grpc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;connect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"[::1]:10000"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nb"&gt;assert&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ok&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;-- A Bidirectional streaming RPC.&lt;/span&gt;
&lt;span class="kd"&gt;local&lt;/span&gt; &lt;span class="n"&gt;ok&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;stream&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;conn&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;new_stream&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"/routeguide.RouteGuide/RouteChat"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nb"&gt;assert&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ok&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="kd"&gt;local&lt;/span&gt; &lt;span class="n"&gt;note&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;location&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;latitude&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;409146138&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;longitude&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;746188906&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="mi"&gt;50&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="n"&gt;message&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;string.format&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"note-%d"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="kd"&gt;local&lt;/span&gt; &lt;span class="n"&gt;ok&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;stream&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;note&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nb"&gt;assert&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ok&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="kd"&gt;local&lt;/span&gt; &lt;span class="n"&gt;ok&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;res&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;stream&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;recv&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="nb"&gt;assert&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ok&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;ngx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;say&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cjson&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;encode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;res&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="kd"&gt;local&lt;/span&gt; &lt;span class="n"&gt;ok&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;stream&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;close&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="nb"&gt;assert&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ok&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="kd"&gt;local&lt;/span&gt; &lt;span class="n"&gt;ok&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;conn&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;close&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="nb"&gt;assert&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ok&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Benchmark
&lt;/h3&gt;

&lt;p&gt;The most important question is, since we wrap the tonic, so how much is the overhead?&lt;/p&gt;

&lt;p&gt;I use AWS EC2 t2.medium (Ubuntu 22.04) to do the benchmark.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7wkapa3jlmc36o0g1erc.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7wkapa3jlmc36o0g1erc.png" alt="grpc_client_benchmark" width="674" height="425"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Send 100,000 calls, use &lt;a href="https://github.com/kingluo/lua-resty-ffi/blob/76138fe52860f5d0aa8cb26e28e5f9e52c4c1ac2/examples/rust/nginx.conf#L46" rel="noopener noreferrer"&gt;lua-resty-ffi&lt;/a&gt;&lt;br&gt;
and &lt;a href="https://github.com/hyperium/tonic/blob/master/examples/src/helloworld/client.rs" rel="noopener noreferrer"&gt;tonic helloworld client example&lt;/a&gt; respectively.&lt;/p&gt;

&lt;p&gt;The result is in seconds, lower is better.&lt;/p&gt;

&lt;p&gt;You could see that the overhead is 20% to 30%, depending the CPU affinity. No too bad, right?&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;With lua-resty-ffi, you could use your favourite mainstream programming language,&lt;br&gt;
e.g. Go, Java, Python or Rust, to do development in Openresty/Nginx, so that&lt;br&gt;
you could enjoy their rich ecosystem directly.&lt;/p&gt;

&lt;p&gt;I just use 400 code lines (including lua and rust) to implement a complete GRPC client in OpenResty.&lt;br&gt;
And this is just an example, you could use lua-resty-ffi to do anything you want!&lt;/p&gt;

&lt;p&gt;Welcome to discuss and like my github repo: &lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/kingluo/lua-resty-ffi" rel="noopener noreferrer"&gt;https://github.com/kingluo/lua-resty-ffi&lt;/a&gt;!&lt;/p&gt;

</description>
      <category>emptystring</category>
    </item>
    <item>
      <title>Nginx Blocking</title>
      <dc:creator>jinhua luo</dc:creator>
      <pubDate>Fri, 14 Oct 2022 16:42:26 +0000</pubDate>
      <link>https://dev.to/kingluo/nginx-blocking-2251</link>
      <guid>https://dev.to/kingluo/nginx-blocking-2251</guid>
      <description>&lt;h2&gt;
  
  
  What's blocking?
&lt;/h2&gt;

&lt;p&gt;Nginx has one master process and multiple worker processes, and each worker process is single threading.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;It’s well known that NGINX uses an asynchronous, event‑driven approach to handling connections. This means that instead of creating another dedicated process or thread for each request (like servers with a traditional architecture), it handles multiple connections and requests in one worker process. To achieve this, NGINX works with sockets in a non‑blocking mode and uses efficient methods such as epoll and kqueue.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;However, once the connection is accepted in one worker process, it could not be moved to other processes, even&lt;br&gt;
when that process is busy. Not like golang, it has no work stealing. And, only socket is asynchronous,&lt;br&gt;
the cpu-intensive tasks and file io tasks is still synchronous.&lt;/p&gt;

&lt;p&gt;Every phase handler for an http request runs in a single thread. OpenResty allows you to run lua code&lt;br&gt;
in each phase. The &lt;code&gt;rewrite&lt;/code&gt;, &lt;code&gt;access&lt;/code&gt;, &lt;code&gt;content&lt;/code&gt; phases, as well as the timer (implemented as faked connection)&lt;br&gt;
are running lua code within an individual lua coroutine.&lt;br&gt;
Within the coroutine, you could use cosocket to communicate with the world.&lt;br&gt;
The cosocket is 100% nonblocking out of the box. When you call cosocket API and it needs to wait for&lt;br&gt;
more data, it would yield the coroutine, register corresponding event in &lt;code&gt;epoll&lt;/code&gt; and let the event-loop&lt;br&gt;
handles other events.&lt;/p&gt;

&lt;p&gt;As the name suggests, coroutines use the CPU in cooperative way.&lt;br&gt;
When you block a long time within one coroutine, other coroutines have no way to get executed,&lt;br&gt;
because all coroutines reside in the one thread, which results in uneven request handling and long request delay.&lt;/p&gt;

&lt;p&gt;When I work in &lt;a href="https://en.wikipedia.org/wiki/KuGou"&gt;Kugou Music&lt;/a&gt;, my team encounters blocking issue,&lt;br&gt;
and I wrote a simple tool to diagnose it. This &lt;a href="https://github.com/kingluo/ngx_lua_block_check"&gt;tool&lt;/a&gt;&lt;br&gt;
makes use of &lt;code&gt;LD_PRELOAD&lt;/code&gt; to hook the &lt;code&gt;lua_resume&lt;/code&gt; and check the function execution time, and &lt;br&gt;
print the url and entry lua function for long execution.&lt;/p&gt;

&lt;p&gt;In fact, this tool is not good, because it could not print the backtrace and does not work for compiled lua code.&lt;/p&gt;

&lt;h2&gt;
  
  
  Locate the blocking source
&lt;/h2&gt;

&lt;p&gt;The best tool to check blocking should be systemtap.&lt;/p&gt;

&lt;p&gt;The blocking comes from two things:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;cpu-intensive tasks&lt;/li&gt;
&lt;li&gt;blocking system calls&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So just check the execution time, if the time exceed a threshold, print the backtraces collected during that time.&lt;/p&gt;

&lt;p&gt;...&lt;/p&gt;

&lt;h2&gt;
  
  
  How to slove blocking?
&lt;/h2&gt;

&lt;p&gt;Since the main thread is blocking sensitive, obviously, we should delegate blocking jobs to other threads.&lt;/p&gt;

&lt;p&gt;The nginx provides the thread pool, which is originally used for serving static files more efficiently.&lt;br&gt;
I wrap them in lua, so that you could use nonblocking API to execute blocking stuff.&lt;/p&gt;

&lt;p&gt;I contribute my works to OpenResty official, that is &lt;code&gt;ngx.run_worker_thread()&lt;/code&gt; API:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/openresty/lua-nginx-module#ngxrun_worker_thread"&gt;https://github.com/openresty/lua-nginx-module#ngxrun_worker_thread&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;...&lt;/p&gt;

&lt;p&gt;Please visit my blog site for detail:&lt;br&gt;
&lt;a href="http://luajit.io/post/2022/nginx-blocking/"&gt;http://luajit.io/post/2022/nginx-blocking/&lt;/a&gt;&lt;/p&gt;

</description>
      <category>nginx</category>
      <category>openresty</category>
      <category>systemtap</category>
    </item>
    <item>
      <title>Analyze Lua Memory Leak With Systemtap</title>
      <dc:creator>jinhua luo</dc:creator>
      <pubDate>Thu, 13 Oct 2022 14:59:56 +0000</pubDate>
      <link>https://dev.to/kingluo/analyze-lua-memory-leak-with-systemtap-3h69</link>
      <guid>https://dev.to/kingluo/analyze-lua-memory-leak-with-systemtap-3h69</guid>
      <description>&lt;p&gt;The memory used by lua codes is managed by the GC, not calling &lt;code&gt;malloc/free&lt;/code&gt; directly. The luajit GC uses mark-and-sweep algorithm. In simple words, it will links all gc objects in a global list. When the memory is in pressure, it would trigger recycle procedures. Note that because of single-threading design, the GC is interspersed in the flow of lua codes (either compiled&lt;br&gt;
or interpretered), e.g. after string allocation. GC would try to check if the gc object is still in used, e.g. as an upvalue, reside in a stack. The GC would sweep all dangling gc objects at the end, and the corresponding memory would be freed and returned to C allocator.&lt;/p&gt;

&lt;p&gt;In luajit, &lt;code&gt;lj_alloc_malloc&lt;/code&gt; and &lt;code&gt;lj_alloc_realloc&lt;/code&gt; would allocate gc objects, and &lt;code&gt;lj_alloc_free&lt;/code&gt; would free them. So naturally, we could trace the invocations of these functions to check memory leak.&lt;/p&gt;

&lt;p&gt;Note that triggering gc is a bit uncertain and the gc steps would be splited in different time slices.  So it would not free unused memory immediately. Moreover, the lua codes may use caches. So it's necessary to check the codes to distinguish the real memory leak.&lt;/p&gt;

&lt;p&gt;Please check my blog site:&lt;/p&gt;

&lt;p&gt;&lt;a href="http://luajit.io/post/2022/analyze-lua-memory-leak-with-systemtap/"&gt;http://luajit.io/post/2022/analyze-lua-memory-leak-with-systemtap/&lt;/a&gt;&lt;/p&gt;

</description>
      <category>luajit</category>
      <category>openresty</category>
      <category>nginx</category>
      <category>systemtap</category>
    </item>
    <item>
      <title>Luajit String Interning</title>
      <dc:creator>jinhua luo</dc:creator>
      <pubDate>Wed, 12 Oct 2022 15:35:33 +0000</pubDate>
      <link>https://dev.to/kingluo/luajit-string-interning-81n</link>
      <guid>https://dev.to/kingluo/luajit-string-interning-81n</guid>
      <description>&lt;h2&gt;
  
  
  Why string interning?
&lt;/h2&gt;

&lt;p&gt;String Interning is a common optimization in most of programming languages.&lt;/p&gt;

&lt;p&gt;It has below advantages in luajit:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;compare string via pointers directly&lt;/li&gt;
&lt;li&gt;use string as table key efficiently, because the hash is calculated at alloc stage and use pointer as uniqueness&lt;/li&gt;
&lt;li&gt;save memory for duplicated strings&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Drawback
&lt;/h2&gt;

&lt;p&gt;Meanwhile, it brings in extra time-consuming tasks:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Hash&lt;/li&gt;
&lt;li&gt;when collision, memcmp all strings in the same hash slot&lt;/li&gt;
&lt;li&gt;rehash when collision is high and/or load factor is low&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Analyze the overhead of string interning
&lt;/h2&gt;

&lt;h3&gt;
  
  
  How expensive is memcmp?
&lt;/h3&gt;

&lt;p&gt;In a signle CPU (Intel(R) Xeon(R) Platinum 8269CY CPU @ 2.50GHz), for each string with 1MB, only the last char is different, it could only do memcmp 90 times per second at most.&lt;/p&gt;

&lt;h3&gt;
  
  
  How expensive is hash collision?
&lt;/h3&gt;

&lt;p&gt;We construct a workload to simulate the cache usecase (only set kv, no get).&lt;/p&gt;

&lt;p&gt;Each http request inserts a key-value in the lua table. The key is integer and the value is string of 1MB size. Note that the key is not string, but luajit still does string interning for each string.&lt;/p&gt;

&lt;p&gt;The http client would choose to use unique random strings or similar strings.&lt;/p&gt;

&lt;p&gt;We use systemtap script to check the lj_str_new time cost differences.&lt;/p&gt;

&lt;h2&gt;
  
  
  Could we do dynamic string interning?
&lt;/h2&gt;

&lt;p&gt;We could do string interning only when the string is used to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;use as table key&lt;/li&gt;
&lt;li&gt;string comparison&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Please check my blog post for detail:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="http://luajit.io/post/2022/luajit-string-interning/"&gt;http://luajit.io/post/2022/luajit-string-interning/&lt;/a&gt;&lt;/p&gt;

</description>
      <category>openresty</category>
      <category>nginx</category>
      <category>systemtap</category>
      <category>luajit</category>
    </item>
    <item>
      <title>Gdb Black Magics</title>
      <dc:creator>jinhua luo</dc:creator>
      <pubDate>Wed, 12 Oct 2022 15:07:06 +0000</pubDate>
      <link>https://dev.to/kingluo/gdb-black-magics-3m4i</link>
      <guid>https://dev.to/kingluo/gdb-black-magics-3m4i</guid>
      <description>&lt;p&gt;Without questioin, gdb is a brilliant weapon to debug our program. However, the reality is complex. We need black magics to handle these cases.&lt;/p&gt;

&lt;h2&gt;
  
  
  Magic1: optimized out?
&lt;/h2&gt;

&lt;p&gt;Your program is normally compiled in optimized mode (e.g. -O2), sometimes even without debug info!&lt;/p&gt;

&lt;p&gt;With optimized option, gcc would most likely use registers instead of the stack to store the function arguments and local variables. In some circumstances, gdb would said they are , then you could not inspect their values. Even worse, in the flow of the code, those registers would be overrided, so it’s impossible to check their original values anymore!&lt;/p&gt;

&lt;p&gt;Normally, to tackle these issues, you have to recompile your program without optimized options.&lt;/p&gt;

&lt;p&gt;But what if you could not? Think about you’re facing your client’s production environment.&lt;/p&gt;

&lt;p&gt;You would be frustrated, isn’t it?&lt;/p&gt;

&lt;p&gt;Then the only way is to read the assembly codes and inspect everything you need at the right places!&lt;/p&gt;

&lt;h2&gt;
  
  
  Magic2: gdb script
&lt;/h2&gt;

&lt;p&gt;gdb has builtin scripting. You could use it to define variables to r/w the program symbols and do some control flows upon them. It’s really useful when you want to inspect complex context infomation.&lt;/p&gt;

&lt;h2&gt;
  
  
  Magic3: gdb python
&lt;/h2&gt;

&lt;p&gt;Besides basic scripting, if you want more magic, use python!&lt;/p&gt;

&lt;p&gt;Python could be used to define customized command, breakpoint or function.&lt;/p&gt;

&lt;h2&gt;
  
  
  Magic4: set source path
&lt;/h2&gt;

&lt;p&gt;Mostly it’s necessary to map the debug steps to the lines of source codes.&lt;/p&gt;

&lt;p&gt;In fact, normally, your binary, if not stripped, would contains DWARF sections, which contains the path of the source codes. So all you need to do is to download the matching source codes and specify a correct perfix.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Please check the my blog post for detail:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="http://luajit.io/post/2022/gdb-black-magics/"&gt;http://luajit.io/post/2022/gdb-black-magics/&lt;/a&gt;&lt;/p&gt;

</description>
      <category>gdb</category>
      <category>assembly</category>
    </item>
  </channel>
</rss>
