Using Armeria to write your RPC
koji lin
Armeria
● 非同步 RPC library
● LINE 的開放原始碼專案
○ Trustin Lee
● Java 8
● HTTP/1・2
● Thrift/gRPC
● 尤其適合在 Microservices 架構
Synchronous? Asynchrnous?
● 在同步下,如果一個處理需要等待,thread 必須等
待完成才能繼續執行
● 同步處理在大量存取下不易 scale up
○ Concurrent requests 受限於 max threads
○ CPU 花費成本在 contention & context switch
○ 較快或優先程度較高的存取會被較慢的存取拖累
Armeria is Asynchronous
● 建立於 Netty 之上
● Max concurrent requests >>> max threads
● 較慢的存取不會阻礙到其他存取
● 程式庫內部也非同步化
○ DNS 查詢
○ Connection pool
● 0.20.x reactive stream 化
Why HTTP/2 ?
● 一個連線上可以同時來往多個 request / response
● 不需要一個存取就建一個連線
from https://www.nginx.com/blog/7-tips-for-faster-http2-performance/
Why HTTP/2 ? (cont'd)
● 解決了 HTTP/1 的許多問題
○ Binary header compression
○ 更少的連線
● 比較容易從 HTTP/1 轉換
● 可以利用各種現存的工具
○ cURL,瀏覽器,Wireshark
Netty 夠穩定嗎?
● HTTP/1
○ Apple
● HTTP/2
○ gRPC
○ LINE internal use
Thrift● 跨平台跟語言的服務開發框架
○ 透過 compiler 自動生成程式,支援 C++, Java,
Python, Ruby, Perl, C#, Go, Javascript ...等等
○ 支援各種傳輸格式/協定
● 原本是 Facebook 開發,現為 Apache Thrift 開源
● 定義好 service method 就可以快速開發 RPC
client 和 server
○ .thrift 本身就是良好的文件
● 比 text-based 格式更有效率
Thrift hello world on Armeria
● 撰寫 .thrift 定義 service method1. namespace java idv.koji.example.service2. service HelloService {3. string hello(1:string name)4. }
Thrift hello world on Armeria (cont’d)
● 使用 thrift compiler 產生好 client/server 溝通部分
● 實作邏輯
1. public class Foo implements HelloService.AsyncIface {
2. public void hello(String name, AsyncMethodCallback cb) {
3. cb.onComplete(name + ", hello world!");4. }5. }
● 利用 Armeria 建立 Server1. ServerBuilder sb = new ServerBuilder();2. sb.port(8080, SessionProtocol.HTTP);3. sb.serviceAt(4. "/hello",5. ThriftService.of(fooHandler,6. SerializationFormat.THRIFT_BINARY)7. .decorate(LoggingService::new));8. sb.build().start();
Thrift hello world on Armeria (cont’d)
● 利用 Armeria 建立 Client1. HelloService.Iface helloService = 2. Clients.newClient(3. "tbinary+http://127.0.0.1:8080/hello",4. HelloService.AsyncIface.class);5.6. helloService.hello("Armeria", 7. new AsyncMethodCallback<String>() {8. ...9. });
Thrift hello world on Armeria (cont’d)
● 跟 Swagger 類似
1. ServerBuilder sb = new ServerBuilder();2. sb.serviceAt("/foo/", THttpService.of(...))3. .serviceAt("/bar/", THttpService.of(...))4. .serviceUnder("/docs/", new 5. DocService());
文件產生器
和 legacy webapp 共存
● 將 legacy 的程式透過 Tomcat Servlet Engine 執行
● Tomcat 不需綁定 port,還可以透過 armeria 得到
HTTP/2 的好處
1. ServerBuilder sb = new ServerBuilder(); 2. sb.serviceUnder("/legacy/", 3. TomcatService.forFileSystem(4. "/var/example.war"));
靜態頁面
1. ServerBuilder sb = new ServerBuilder(); 2. sb.serviceUnder("/images/", 3. HttpFileService.forFileSystem(4. "/var/lib/www/images"));
HTTP Server
1. ServerBuilder sb = new ServerBuilder();2. sb.port(8080, SessionProtocol.HTTP);3. builder.serviceAt("/http", 4. new AbstractHttpService() {5. @Override protected void 6. doPost(ServiceRequestContext ctx,7. HttpRequest req,8. HttpResponseWriter res)... {9. res.respond(HttpStatus.OK, 10. MediaType.PLAIN_TEXT_UTF_8, 11. "test");12. }13. }).build().start();
HTTP Client
1. HttpClient httpClient = Clients.newClient(2. "none+http://example.com/", 3. HttpClient.class);4. AggregatedHttpMessage textResponse = 5. httpClient6. .get("/foo/bar.txt") // Publisher7. .aggregate() // CompletableFuture8. .join();
Client side load balancing
● Armeria 使用單一 HTTP/2 連線,所以在某些狀況
如 L4 下,需要自己的 LB 機制
1. EndpointGroupRegistry.register(2. "myGroup", 3. new StaticEndpointGroup( 4. Endpoint.of("1.2.3.4", 8080), ...),5. EndpointSelectionStrategy.6. WEIGHTED_ROUND_ROBIN); 7. Clients.newClient(8. "none+http://group:myGroup/foo/bar", 9. SimpleHttpClient.class);
Metrics
● Dropwizard Metrics
● 使用 DropwizardMetricCollectingService decorate1. builder.serviceAt(2. "/hello", 3. THttpService.of(new HelloHandler())4. .decorate( 5. DropwizardMetricCollectingService 6. .newDecorator(metricRegistry, 7. "hello-service")));
Zipkin 整合
● Twitter 的開放源碼專案
● 分散式服務追蹤系統,追蹤跨網路存取的一連串
API 呼叫過程
● 在存取產生時放入一些資訊在 request 中,送到後
台可視化
Zipkin 整合 (cont’d)
1. Brave brave = new2. Brave.Builder("HelloService") 3. .spanCollector(4. new ScribeSpanCollector("127.0.0.1", 5. 9410)) 6. .traceSampler(Sampler.create(1f)) 7. .build();8. server.serviceAt("/hello",9. ThriftService.of(new HelloService())
10. .decorate(HttpTracingService11. .decorator(brave));
Circuit Breaker
● 有多個遠端呼叫的系統最怕超時的服務所造成的連
鎖反應
● http://martinfowler.com/bliki/CircuitBreaker.html
http://developers.linecorp.com/blog/ja/?p=3684
Circuit Breaker (cont’d)
1. decorator = 2. CircuitBreakerClient.newDecorator(3. new CircuitBreakerBuilder("hello")4. .failureRateThreshold(0.1).build());5.6. new ClientBuilder("tbinary+http://...")7. .decorator(ThriftCall.class, 8. ThriftReply.class, 9. decorator)
10. .build(HelloService.Iface.class);
Circuit Breaker (cont’d)
● Circuit Breaker 開啟時會丟出 FailFastException
● 可以針對 host, method 等級去做設定
○ newPerMethodDecorator
○ newPerHostDecorator
○ newPerHostAndMethodDecorator
gRPC support
● https://github.com/line/armeria/pull/2471. ServerBuilder sb = new ServerBuilder(); 2. sb.port(8080, SessionProtocol.HTTP); 3. GrpcService grpcService = new 4. GrpcServiceBuilder()5. .addService(new HiService())6. .build();7. sb.serviceUnder("/", grpcService); 8. sb.build().start();
● 理應可以支援 HTTP/1,但現在沒有 HTTP/1 client
● 尚未支援文件產生器 DocService
● 尚未支援 json 格式
○ 類似 gRPC gateway
gRPC support (cont’d)
Retrofit with armeria client
● https://github.com/line/armeria/pull/2971. HttpClient httpClient = 2. Clients.newClient(ClientFactory.DEFAULT,3. "none+http://localhost:8080",4. HttpClient.class);5. Retrofit retrofit = 6. ArmeriaRetrofit.builder(httpClient)7. .addConverterFactory(...)8. .build();9. MyApi api = retrofit.create(MyApi.class);
Armeria 之於應用程式開發者
● 良好的抽象設計
○ 一樣的方式模式建立 http/thrift client 和 server
● 方便的 decorator 模式
○ 預設已提供 logging, metrics, tracing, circuit
breaker…
○ 依需求可以用同模式實作各種功能 例如: ACL,
rate-limiting...等等
Is Armeria production ready?
Shop architecture using armeria
● 內部與外部都從 thrift socket client/server 實作切
換到 armeria
○ 內部都是 HTTP/2
○ 外部則 HTTP/2 & HTTP/1 都有
● 與 Spring MVC 共存
● Circuit breaker
● Metrics + Zipkin tracing
# of backend requests
但是...
● 非同步化不只是更換框架/程式庫就完成
○ 非同步化的程式不易撰寫跟維護
○ 我們使用 Dagger Producers 去實作非同步的相依
流程
● server/client 都使用 armeria,記得不要使用 client
的 future#get 之類的阻塞方法
○ event loop 下造成同 thread 等待跑在同個 thread
下的 task, 造成永遠阻塞
但是... (cont’d)
● 非同步出錯時,exception 的 stack trace 資訊不足
更多資訊
● http://line.github.io/armeria/
● https://github.com/line/armeria
● 歡迎直接到 issue 提出建議跟需求
Q&A
Top Related