Giter Site home page Giter Site logo

line / armeria Goto Github PK

View Code? Open in Web Editor NEW
4.6K 182.0 868.0 195.83 MB

Your go-to microservice framework for any situation, from the creator of Netty et al. You can build any type of microservice leveraging your favorite technologies, including gRPC, Thrift, Kotlin, Retrofit, Reactive Streams, Spring Boot and Dropwizard.

Home Page: https://armeria.dev

License: Apache License 2.0

Java 90.68% CSS 0.01% JavaScript 0.12% HTML 0.01% Thrift 1.17% TypeScript 1.25% Kotlin 0.63% Less 0.16% Scala 1.08% Shell 0.01% MDX 4.90%
http http2 http-server http-client thrift thrift-server thrift-client microservices grpc rpc

armeria's Introduction

Visit the official web site for more information.

Armeria

Revved up by Develocity

Build a reactive microservice at your pace, not theirs.

Armeria is your go-to microservice framework for any situation. You can build any type of microservice leveraging your favorite technologies, including gRPC, Thrift, Kotlin, Retrofit, Reactive Streams, Spring Boot and Dropwizard.

It is open-sourced by the creator of Netty and his colleagues at LINE Corporation.

Requirements

How to reach us โ€” chat, questions and newsletters

Visit the community to chat with us, ask questions and learn how to contribute.

Hall of fame

See the complete list of our contributors.

Contributors

armeria's People

Contributors

adriancole avatar anuraaga avatar armerian avatar delegacy avatar dependabot[bot] avatar dogacel avatar dongjinleekr avatar eisig avatar github-actions[bot] avatar heowc avatar hexoul avatar hyangtack avatar ikhoon avatar imasahiro avatar inch772 avatar injae-kim avatar jongyeol avatar jrhee17 avatar karbonitekream avatar kezhenxu94 avatar kojilin avatar ks-yim avatar mauhiz avatar minwoox avatar mscheong01 avatar okue avatar ta7uw avatar taicki avatar tobias- avatar trustin avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

armeria's Issues

HttpServerIdleTimeoutHandler can keep an idle connection open.

Currently, HttpServerIdleTimeoutHandler is added after HttpObjectAggregator. This yields an interesting behavior; when pendingResCount is 0, the request (usually the first request of a connection) must be received within <idleTimeoutMillis> milliseconds, because HttpObjectAggregator will not produce anything until a full HTTP message is built.

I think this behavior is fine, although it sort of looks like an unintended side effect.

However, I see the following edge case:

  1. The client sends the first request. (pendingResCount becomes 1)
  2. The server did not send the first response yet and the client starts to send the second request
  3. The server did not send the first response yet again, the client sends the second request partially, and then stops sending anything.
  4. HttpServerIdleTimeoutHandler.channelIdle() will be invoked, but it will be ignored because pendingResCount is 1.
  5. The server sends the first response finally (pendingResCount becomes 0), but the client did not send anything since the step (3).
  6. HttpServerIdleTimeoutHandler.channelIdle() will be invoked again, but will be ignored again because it's not the first event this time (see the evt.isFirst() check.)

A quick fix would be to remove the evt.isFirst() check, but I wonder if we had a good reason for having it in the first place. Do you remember why, @inch772?

Ability to send different HTTP header values for the same endpoint

/cc @inch772

Currently, a user can specify custom HTTP headers when creating a new client. However, the user cannot when sending a request. This makes it hard for the user to send a per-request custom HTTP header. This feature would be useful for an API proxy server that relays API calls with different HTTP headers.

Provide a way to adapt CompletableFuture into AsyncMethodCallback and vice versa

#112

  • Provide a method that returns a new AsyncMethodCallback who updates the state of a specified CompletableFuture when its onComplete() or onError() is called.
  • Provide a method that returns a new CompletableFuture who updates the state of a specified AsyncMethodCallback when its complete(), completeExceptionally() or cancel() is called.

e.g.

client.hello("World", FutureConversions.toAsyncMethodCallback(myCompletableFuture));

public void hello(String name, AsyncMethodCallback cb) throws TException {
    otherClient.getGreetings(name, FutureConversions.toCompletableFuture(cb));
}

We might want to add similar methods for Netty promises/futures.

Add something similar to DecodeResult.NOT_FOUND to ServiceInvocationHandler

A ServiceCodec can tell the session layer that the request doesn't actually belong to the enclosing Service so that the other Service takes over, by returning DecodeResult.NOT_FOUND.

It will be nice if ServiceInvocationHandler has something similar. For example, a user could bind an HttpFileService and a TomcatSerivce under the same path prefix. If HttpFileService fails to find the file, TomcatService could handle it, making it possible to serve the static resources using Armeria. (TomcatService could also handle the 404 response from Tomcat specially so that the next service takes over.)

/cc @delegacy

Throttling

Since Armeria is RPC library for client/server, it would be great if the library has integrated throttling feature so that server/client can properly manage incoming traffic if they have to. ( unless Armeria already have one, haven't seen it though )

Provide an easy way to redirect a path with no trailing slash

For example, when a user adds a service under /mypath, the server will only dispatch the request whose path starts with /mypath/ and not /mypath.

A user is expected to write a simple redirection service that redirects /mypath to /mypath/ but it's not very user-friendly. We could provide a service implementation for this, but it's still not user-friendly enough in my opinion.

We could perhaps modify HttpServerHandler so that:

  • if the path does not end with '/' and there's no matching service entry,
    • it checks if there's any matching service entry with path + ''/'
      • if there's a match, respond with a redirect

This is related with #45

Add a repository (or repositories) to contain useful but specialized armeria-related functionality.

We will want the core armeria library to contain everything a normal production server would be expected to have, but we should also have a place we can put useful, but more specialized armeria-related libraries. Examples are armeria-spring or armeria-retrofit.

One idea is to have one more repository, armeria-extensions, which has a bunch of subprojects with each library. They can have independent version numbers and share some dependencies with armeria using a dependency pom.

Implement advanced request mapping

Like various web frameworks, it would be useful to have path variable mappings:

ServerBuilder sb = ...
sb.serviceAt("/a/{foo}/b/{bar}", ....);
...
ctx.mappedVariables().get("foo");

Need to do the survey on well known path mapping notations such as:

Remote address is not set in TomcatServiceInvocationHandler

It looks like at the moment remote address is not passed to coyote request. It's quite easy to do with some thing like this in TomcatServiceInvocationHandler.java (I modified the signature to pass the entire context):

    private static Request convertRequest(ServiceInvocationContext ctx) {

        final Request coyoteReq = new Request();
        InetSocketAddress remote = (InetSocketAddress)ctx.remoteAddress();
        //byte[] address = remote.getAddress().getAddress();

        coyoteReq.setRemotePort (remote.getPort());
        coyoteReq.remoteAddr().setString (remote.getHostName());

I had a servlet that was performing some authorization based on the remote address. With the change above is works fine now :)

JMX integration plan?

I saw armeria LINE_DevDay2015 PDF mentioning the necessary of JMX integration for the library. Do you guys have any follow up on this? Wondering if you guys want to build JMX stuff into library from the scratch or want to use existing JMX libraries like Netflix Servo or Dropwizard Metrics?

Expose control over embedded Tomcat instance configuration

I believe the ability to embed Tomcat is a very useful feature of Armeria which will get a big audience once advertised wide. There is lots of old servlet code that people don't want to touch. So, when moving to Netty/Armeria they get a path for smooth transition.

That said, Armeria needs to find a way to expose control over embedded instance creation to the user. As you know, Tomcat has lots of various global config files with a ton of parameters and options. People use them to customize their deployments. Now that Tomcat is embedded, all this is lost - unless a flexible API is exposed to customize it.

In my case I needed 2 things that forced me to modify TomcatServiceInvocationHandler.

  1. private static final String SERVICE_NAME = "Catalina";//"Tomcat-over-Armeria";
    

    The service name ends up being in the ObjectName of every JMX bean that is registered by Tomcat, and JMX is often used to access some internal Tomcat functionality. We had some old code that was expecting service name to be "Catalina" which is the default in Tomcat. Please make the service name configurable!

  2. Thread pools in Tomcat are not created by default. Our code was using one of these resizable StandardThreadExecutor through the JMX API. I had to do this:

        StandardThreadExecutor executor = new StandardThreadExecutor();
        executor.setName("DefaultThreadPool");
        service.addExecutor(executor);

Consistent with the spirit of Armeria's service builders I would expose decorators for the following instances:

  1. Connector
  2. StandardService
  3. StandardEngine

and let the user modify everything they desire on these instances. That would accommodate the majority of typical requests that you would receive otherwise :)

Add support for header-based detection of thrift serialization format

Currently, we define a single thrift serialization format when registering a thrift service on a particular path. It would be convenient to allow a thrift service to support multiple serialization formats on the same path based on the incoming HTTP content-type or accept header. Armeria client already sets both to an appropriate value per serialization format, so we just need to add support for ThriftServiceCodec to read these and pick the format automatically.

/cc @trustin @inch772

ServiceInvocationContext.current() return type

ServiceInvocationContext.current() currently returns Optional<ServiceInvocationContext>.

I find this inconvenient when I assume there's context available for the current thread:

ServiceInvocationContext.current().get().eventLoop()...

Instead, we could make it just return ServiceInvocationContext and raise an exception when the context is not available.

However, in some cases, the caller does not want the exception raised. (See HttpRemoteInvoker.eventLoop())

Perhaps we should:

  • change the return type of current() and
  • introduce an alternative method?

Use 'PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n' for h1c-to-h2c upgrade

Armeria client currently send a HEAD / HTTP/1.1 request to issue a protocol upgrade, as recommended by the RFC, but it can cause heavy load on the server side if the service bound at / does not implement HEAD method optimally. When many Armeria clients creates a lot of connections concurrently, the server might not respond to the upgrade request in a timely manner.

  • Use PRI * HTTP/2.0 preface string to upgrade so that Armeria client does not generate such load to the server.
  • Keep the old upgrade mechanism for interoperability

Interactive template-based project generator

It would be useful to have an interactive template-based project generator, which is similar to Maven archetypes or Typesafe Activator templates. This doesn't need to be a part of Armeria distribution, but could be some webapp hosted under http://line.github.io/armeria/

The project generator could ask the following questions:

  • What is the name of the project?
  • Which build tool do you prefer? Maven / Gradle?
  • What technology do you want to incorporate? Thrift / Generic JEE webapp / Spring
  • ...

Armeria client should set HOST header on upgrade request

Currently, armeria client does not seem to set HOST header on the upgrade request. When the request is processed by a HTTP1.1 server, like jetty in default settings, the server issues warning logs like

WARN org.eclipse.jetty.http.HttpParser - bad HTTP parsed: 400 No Host for HttpChannelOverHttp@4373aa09{r=0,c=false,a=IDLE,uri=null}

This is reasonable since HTTP1.1 requests, including an upgrade request must have a Host header as in:
https://http2.github.io/http2-spec/#discover-http

Add the FAQs page

Need to address some FAQs such as:

  • What's the difference from similar yet popular project? Why should I choose Armeria instead?
  • What's the origin of the project name 'Armeria'? /cc @tsuna (Howdy, my friend!?)
  • and maybe some more based on the questions we might get when we attract more people

Armeria client does not work with HTTP1 servers when using upgrade.

Currently it seems armeria client does not work when using HTTP upgrade and the server rejects upgrade. When forcing HTTP1 without upgrade using h1c://, it works fine.

Example failing integration test: anuraaga#1

The stacktrace seems to indicate that the issue is the upgrade user event handler is triggered inline, meaning that the session listener is triggered right away and the actual RPC request is sent - however, this is before HttpClientUpgradeHandler has had a chance to remove itself from the pipeline, which happens after the user event is triggered. The RPC request goes through HttpClientUpgradeHandler once again, where it fails since the failed upgrade is still in progress (just a couple of lines of code left to run though...).

I tried moving the markConnectionFinished call in HttpConfigurator from userEventTriggered to channelRead or channelReadCompleted but couldn't figure out a resulting "connection reset by peer".

java.lang.IllegalStateException: Attempting to write HTTP request with upgrade in progress
    at io.netty.handler.codec.http.HttpClientUpgradeHandler.write(HttpClientUpgradeHandler.java:161)
    at io.netty.channel.ChannelHandlerInvokerUtil.invokeWriteNow(ChannelHandlerInvokerUtil.java:157)
    at io.netty.channel.DefaultChannelHandlerInvoker.invokeWrite(DefaultChannelHandlerInvoker.java:337)
    at io.netty.channel.AbstractChannelHandlerContext.write(AbstractChannelHandlerContext.java:265)
    at io.netty.handler.timeout.IdleStateHandler.write(IdleStateHandler.java:284)
    at com.linecorp.armeria.client.HttpClientIdleTimeoutHandler.write(HttpClientIdleTimeoutHandler.java:69)
    at io.netty.channel.ChannelHandlerInvokerUtil.invokeWriteNow(ChannelHandlerInvokerUtil.java:157)
    at io.netty.channel.DefaultChannelHandlerInvoker.invokeWrite(DefaultChannelHandlerInvoker.java:337)
    at io.netty.channel.AbstractChannelHandlerContext.write(AbstractChannelHandlerContext.java:265)
    at com.linecorp.armeria.client.HttpSessionHandler.write(HttpSessionHandler.java:126)
    at io.netty.channel.ChannelHandlerInvokerUtil.invokeWriteNow(ChannelHandlerInvokerUtil.java:157)
    at io.netty.channel.DefaultChannelHandlerInvoker.invokeWrite(DefaultChannelHandlerInvoker.java:337)
    at io.netty.channel.AbstractChannelHandlerContext.writeAndFlush(AbstractChannelHandlerContext.java:281)
    at io.netty.channel.DefaultChannelPipeline.writeAndFlush(DefaultChannelPipeline.java:1049)
    at io.netty.channel.AbstractChannel.writeAndFlush(AbstractChannel.java:283)
    at com.linecorp.armeria.client.HttpRemoteInvoker.writeRequest(HttpRemoteInvoker.java:216)
    at com.linecorp.armeria.client.HttpRemoteInvoker.invoke0(HttpRemoteInvoker.java:166)
    at com.linecorp.armeria.client.HttpRemoteInvoker.lambda$invoke$53(HttpRemoteInvoker.java:135)
    at com.linecorp.armeria.client.HttpRemoteInvoker$$Lambda$17/1021436681.operationComplete(Unknown Source)
    at io.netty.util.concurrent.DefaultPromise.notifyListener0(DefaultPromise.java:680)
    at io.netty.util.concurrent.DefaultPromise.notifyListeners(DefaultPromise.java:567)
    at io.netty.util.concurrent.DefaultPromise.setSuccess(DefaultPromise.java:397)
    at com.linecorp.armeria.client.pool.DefaultKeyedChannelPool.notifyConnect(DefaultKeyedChannelPool.java:154)
    at com.linecorp.armeria.client.pool.DefaultKeyedChannelPool.lambda$acquireHealthyFromPoolOrNew$35(DefaultKeyedChannelPool.java:125)
    at com.linecorp.armeria.client.pool.DefaultKeyedChannelPool$$Lambda$21/100945895.operationComplete(Unknown Source)
    at io.netty.util.concurrent.DefaultPromise.notifyListener0(DefaultPromise.java:680)
    at io.netty.util.concurrent.DefaultPromise.notifyListeners(DefaultPromise.java:567)
    at io.netty.util.concurrent.DefaultPromise.setSuccess(DefaultPromise.java:397)
    at com.linecorp.armeria.client.HttpSessionChannelFactory$3.userEventTriggered(HttpSessionChannelFactory.java:137)
    at io.netty.channel.ChannelHandlerInvokerUtil.invokeUserEventTriggeredNow(ChannelHandlerInvokerUtil.java:75)
    at io.netty.channel.DefaultChannelHandlerInvoker.invokeUserEventTriggered(DefaultChannelHandlerInvoker.java:135)
    at io.netty.channel.AbstractChannelHandlerContext.fireUserEventTriggered(AbstractChannelHandlerContext.java:149)
    at io.netty.channel.ChannelInboundHandlerAdapter.userEventTriggered(ChannelInboundHandlerAdapter.java:108)
    at io.netty.channel.ChannelHandlerInvokerUtil.invokeUserEventTriggeredNow(ChannelHandlerInvokerUtil.java:75)
    at io.netty.channel.DefaultChannelHandlerInvoker.invokeUserEventTriggered(DefaultChannelHandlerInvoker.java:135)
    at io.netty.channel.AbstractChannelHandlerContext.fireUserEventTriggered(AbstractChannelHandlerContext.java:149)
    at com.linecorp.armeria.client.HttpSessionChannelFactory$DefaultSessionListener.sessionActivated(HttpSessionChannelFactory.java:152)
    at com.linecorp.armeria.client.HttpConfigurator.markHttpConnectionFinished(HttpConfigurator.java:170)
    at com.linecorp.armeria.client.HttpConfigurator.access$300(HttpConfigurator.java:67)
    at com.linecorp.armeria.client.HttpConfigurator$3.userEventTriggered(HttpConfigurator.java:195)
    at io.netty.channel.ChannelHandlerInvokerUtil.invokeUserEventTriggeredNow(ChannelHandlerInvokerUtil.java:75)
    at io.netty.channel.DefaultChannelHandlerInvoker.invokeUserEventTriggered(DefaultChannelHandlerInvoker.java:135)
    at io.netty.channel.AbstractChannelHandlerContext.fireUserEventTriggered(AbstractChannelHandlerContext.java:149)
    at io.netty.handler.codec.http.HttpClientUpgradeHandler.decode(HttpClientUpgradeHandler.java:213)
    at io.netty.handler.codec.http.HttpClientUpgradeHandler.decode(HttpClientUpgradeHandler.java:38)
    at io.netty.handler.codec.MessageToMessageDecoder.channelRead(MessageToMessageDecoder.java:89)
    at io.netty.channel.ChannelHandlerInvokerUtil.invokeChannelReadNow(ChannelHandlerInvokerUtil.java:83)
    at io.netty.channel.DefaultChannelHandlerInvoker.invokeChannelRead(DefaultChannelHandlerInvoker.java:153)
    at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:157)
    at io.netty.handler.codec.ByteToMessageDecoder.channelRead(ByteToMessageDecoder.java:265)
    at io.netty.channel.ChannelHandlerInvokerUtil.invokeChannelReadNow(ChannelHandlerInvokerUtil.java:83)
    at io.netty.channel.DefaultChannelHandlerInvoker.invokeChannelRead(DefaultChannelHandlerInvoker.java:153)
    at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:157)
    at io.netty.channel.DefaultChannelPipeline.fireChannelRead(DefaultChannelPipeline.java:946)
    at io.netty.channel.nio.AbstractNioByteChannel$NioByteUnsafe.read(AbstractNioByteChannel.java:125)

Incorporate metrics information into DocService

DocService currently shows the list of the available operations, structs, and their debug forms. We could also:

  • display the metrics of the related operations,
  • add a page about global metrics (and settings) and
  • rename DocService to something that fits better

Add more features related to metric collection

As discussed in #156, there are more metric related requirements to consider in Armeria.

  1. It is necessary to add client-induced failure metrics like timeouts or circuit breaker.
  2. Service and Client may need different sets of metrics. Good to consider having separate interfaces.
  3. Armeria currently does not provide a way for a user to define what success/failure is. It's hard-coded - >= 400. Should Armeria determine if an invocation was successful or not? Is it the responsibility of the monitoring system? If Armeria should, should it be customizable? (e.g. if we add HBase/Redis client/server in the future, they will use different code values.)

Make `ServerConfig.blockingTaskExecutor` context-aware

We currently use Executor as the type of ServerConfig.blockingTaskExecutor property. Although Executor provides a nice abstraction for executing long-running tasks to us, we may want more advanced behavior, such as:

  • to isolate certain type of invocations and tasks to a different thread pool.
  • to execute certain type of invocations and tasks directly (no pooling)

To achieve this, we need to define a new interface similar to Executor. To sketch a little bit:

public interface BlockingTaskExecutor {
    void execute(ServiceInvocationContext ctx, Runnable task);
    void scheduleAtFixedDelay(ServiceInvocationContext ctx, ...);
}

We also need to provide a way to compose BlockingTaskExecutors, like we do for Services with PathMapping. In this case, we need something more than just doing a path-matching. e.g. hostname, method name, and remote address

Support for request/response streaming?

I may have misunderstood the scope of the framework. I see that HttpObjectAggregator is unconditionally inserted into the channel pipeline, and http service invocation handler delivers FullHttpRequest all the time. So, what about large requests with multipart encoding or simply chunked encoding that would benefit from stream processing?
Also, gRPC, for example, supports steaming requests and responses as a part of their IDL definition. This makes it possible to extend the basic "message in/message out" RPC with "message in/many messages out" etc model.
Is this completely out of scope for the Armeria, on you can see this being added some time soon?

Version of bundled Thrift binary

I found that the Thrift binary included in the repo is not a vanilla version:

$ ./src/build/thrift.osx-x86_64 -version
Thrift version 0.9.1-LINE

Is there any difference in the generated code? I know these binaries are only used for generating test code, but was just curious ๐Ÿ˜Š

Added: Linux binary is not a customized version, but from 0.9.2.

$ ./thrift.linux-x86_64 -version
Thrift version 0.9.2

ClassCastException at TTextProtocol.readStructBegin when reading ailased enum.

Currently it seems TTextProtocol do not support reading ailased enum. When we use DocService with enum, got ClassCastException with following stack trace.

https://gist.github.com/imasahiro/2c02f1b27c4a2068dd06597fd77bb101 is a patch which can reproduce this error.

$ mvn test
(snip)
Tests run: 11, Failures: 0, Errors: 1, Skipped: 0, Time elapsed: 0.029 sec <<< FAILURE! - in com.linecorp.armeria.common.thrift.text.TTextProtocolTest
tTextProtocolReadWriteTest(com.linecorp.armeria.common.thrift.text.TTextProtocolTest)  Time elapsed: 0.012 sec  <<< ERROR!

java.lang.ClassCastException: org.apache.thrift.meta_data.FieldValueMetaData cannot be cast to org.apache.thrift.meta_data.EnumMetaData
        at com.linecorp.armeria.common.thrift.text.TTextProtocolTest.tTextProtocolReadWriteTest(TTextProtocolTest.java:90)

Disconnect before HTTP/2 stream ID overflow

When we keep an HTTP/2 connection long enough to handle more than 1G requests, the stream ID will exceed Integer.MAX_VALUE. It's probably a good idea for an Armeria client to close the connection before hitting the limit.

Verify HOST header of upgrade requests

While looking through armeria code in an unrelated matter, I found this method

https://github.com/line/armeria/blob/master/src/main/java/com/linecorp/armeria/client/HttpSessionChannelFactory.java#L81

It computes a value for a map using more parameters than are provided to the key of the map. This is usually wrong, so I tried looking through and it seems that the specified host is only used for specifying the HOST header of the HTTP2 upgrader request.

https://github.com/line/armeria/blob/master/src/main/java/com/linecorp/armeria/client/HttpConfigurator.java#L260

I haven't ran anything to verify the behavior, but looking at the code it seems likely that all upgrade requests will have the host of the very first client to send a request for the session protocol on a given remote invoker factory, which there is usually only one of in a binary.

A failed H2C upgrade causes an invocation failure when server sends 'Connection: close' header.

When an Armeria client sends the HEAD / upgrade request, some HTTP/1 servers send a 404 response with a Connection: close header instead of keeping the connection alive. The Armeria client attempts to send an HTTP/1 request for the current invocation to the closed connection after receiving the rejected upgrade response, which is incorrect and thus raises a SessionClosedException.

To fix this issue, we could make Armeria client handle Connection: close header properly and remember the list of the hosts that rejected the upgrade request.

(Originally reported by @anuraaga)

armeria v0.19 build warnings in Intellij Idea 2016

by using intellij maven integration ,compile generated some annoying warnings:

Environment :
Mac OS x 10.9.5
java 1.8
Intelij Idea 2016.1.3

/Library/Java/JavaVirtualMachines/jdk1.8.0_45.jdk/Contents/Home/bin/java "-Dmaven.home=/Applications/IntelliJ IDEA.app/Contents/plugins/maven/lib/maven3" "-Dclassworlds.conf=/Applications/IntelliJ IDEA.app/Contents/plugins/maven/lib/maven3/bin/m2.conf" -Didea.launcher.port=7533 "-Didea.launcher.bin.path=/Applications/IntelliJ IDEA.app/Contents/bin" -classpath "/Applications/IntelliJ IDEA.app/Contents/lib/idea_rt.jar:/Applications/IntelliJ IDEA.app/Contents/lib/util.jar" -Dfile.encoding=UTF-8 com.intellij.rt.execution.CommandLineWrapper /private/var/folders/k3/9mvhtc894gd9_96yp_q2lpnc0000gn/T/classpath com.intellij.rt.execution.application.AppMain org.codehaus.classworlds.Launcher -Didea.version=2016.1.3 org.apache.maven.plugins:maven-compiler-plugin:3.3:compile
[INFO] Scanning for projects...
[INFO] ------------------------------------------------------------------------
[INFO] Detecting the operating system and CPU architecture
[INFO] ------------------------------------------------------------------------
[INFO] os.detected.name: osx
[INFO] os.detected.arch: x86_64
[INFO] os.detected.classifier: osx-x86_64
[WARNING] Failed to inject repository session properties.
java.lang.NoSuchMethodError: org.apache.maven.execution.MavenSession.getRepositorySession()Lorg/eclipse/aether/RepositorySystemSession;
    at kr.motd.maven.os.RepositorySessionInjector.injectRepositorySession(RepositorySessionInjector.java:22)
    at kr.motd.maven.os.DetectExtension.injectSession(DetectExtension.java:148)
    at kr.motd.maven.os.DetectExtension.afterProjectsRead(DetectExtension.java:107)
    at org.apache.maven.DefaultMaven.doExecute(DefaultMaven.java:274)
    at org.apache.maven.DefaultMaven.execute(DefaultMaven.java:156)
    at org.apache.maven.cli.MavenCli.execute(MavenCli.java:537)
    at org.apache.maven.cli.MavenCli.doMain(MavenCli.java:196)
    at org.apache.maven.cli.MavenCli.main(MavenCli.java:141)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:497)
    at org.codehaus.plexus.classworlds.launcher.Launcher.launchEnhanced(Launcher.java:290)
    at org.codehaus.plexus.classworlds.launcher.Launcher.launch(Launcher.java:230)
    at org.codehaus.plexus.classworlds.launcher.Launcher.mainWithExitCode(Launcher.java:409)
    at org.codehaus.plexus.classworlds.launcher.Launcher.main(Launcher.java:352)
    at org.codehaus.classworlds.Launcher.main(Launcher.java:47)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:497)
    at com.intellij.rt.execution.application.AppMain.main(AppMain.java:144)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:497)
    at com.intellij.rt.execution.CommandLineWrapper.main(CommandLineWrapper.java:46)
[INFO]                                                                         
[INFO] ------------------------------------------------------------------------
[INFO] Building Armeria 0.19.0.Final
[INFO] ------------------------------------------------------------------------
[INFO] 
[INFO] --- maven-compiler-plugin:3.3:compile (default-cli) @ armeria ---
[INFO] Changes detected - recompiling the module!
[INFO] Compiling 233 source files to /Users/wangjunfei/projects/armeria/target/classes
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 13.913s
[INFO] Finished at: Tue Jun 28 10:11:53 CST 2016
[INFO] Final Memory: 9M/245M
[INFO] ------------------------------------------------------------------------

Process finished with exit code 0

FieldValueMetaData cannot be cast to org.apache.thrift.meta_data.StructMetaData

Caused by: java.lang.ClassCastException: org.apache.thrift.meta_data.FieldValueMetaData cannot be cast to org.apache.thrift.meta_data.StructMetaData
    at com.linecorp.armeria.common.thrift.ThriftUtil.toJavaType(ThriftUtil.java:82)
    at com.linecorp.armeria.server.thrift.ThriftFunction.<init>(ThriftFunction.java:106)
    at com.linecorp.armeria.server.thrift.ThriftFunction.<init>(ThriftFunction.java:52)
    at com.linecorp.armeria.server.thrift.ThriftServiceCodec.registerFunction(ThriftServiceCodec.java:146)

armeria 's postion in a web application

is there a way to place a http proxy stand before armeria server to speak json protocol for remote client ? or am i use choose the wrong tools here ? dose armeria supposed to fit for a intranet micro service ? for internet users how do we use armeria properly?

DocService should provide a way to prepopulate debug forms

It will be very convenient to use debug forms if they can be prepopulated with sane request objects, similar to what's done with Swagger annotations.

I think we should be able to support two types of use cases

  1. Default values by type. Any service method that contains the given type will have that type automatically prepopulated. Think some sort of request context type message containing user id, app version, etc.

  2. Default values by service method. This can allow completely populating a request, just hit send to try out your API.

If possible, would love to have a way that uses normal Java objects instead of annotations - swagger-style JSON strings are really hard to maintain. Not type safe unfortunately, but I personally would still prefer a map from method name to default value object.

Overload Functions.compose() to compose more than 2 functions

/cc @synk @inch772

Currently, Functions.compose() composes two functions only. It would be more useful if it can compose more than 2 functions. We could use varargs, but we would lose compile-time type safety. Perhaps we could add several overloaded methods that accepts the specific numbers of functions. Maybe up to 9?

Zipkin integration

Quite useful feature for understanding the flow of remote calls in a service mesh.

Zipkin integration will also make us consider the user friendliness of our client-side API. Unlike the server-side, the client-side API looks more verbose than the server-side counterpart when decorators are involved:

HelloService.AsyncIface helloService =
    new ClientBuilder("tbinary+http://127.0.0.1:8080/foo")
        .option(ClientOption.REMOTE_INVOKER_DECORATOR,
            HttpTracingRemoteInvoker.decorator(brave))
        .build(HelloService.AsyncIface.class);

Perhaps we could add more builder methods? e.g.

HelloService.AsyncIface helloService =
    new ClientBuilder("tbinary+http://127.0.0.1:8080/foo")
        .decorator(HttpTracingRemoteInvoker.decorator(brave))
        .decorator(anotherDecorator)
        .decorator(someOtherDecorator)
        .build(HelloService.AsyncIface.class);

/cc @synk @anuraaga @inch772

Propose a client side failover strategy

For now,we have a client side load balancer,maybe we need a failover strategy to deal with the request failed.I have some candidate proposals:

  1. Endpoint aliveness monitor
    As @anuraaga mentioned in this comment,a aliveness monitor check endpoints's status and update endpoint group info(like set endpoint status to offline/online),the load balancer only select the alive endpoint.
    Cons:can't ensure the current request success at last,because the monitor check interval

2)CircuitBreaker/EndpointGroupInvoker update endpoint status
In the ResultFuture,those class can update the endpoint's status to offline if the request failed,and the load balancer will only select the alive endpoint when next request is coming.
Cons:same as 1)

3)Catch the network exception when invoking,and select the next endpoint to continue the method request
The following code illustrates this point

public final class EndpointGroupInvoker extends DecoratingRemoteInvoker {
    ....

    @Override
    public <T> Future<T> invoke(EventLoop eventLoop, URI uri,
                                ClientOptions options,
                                ClientCodec codec, Method method,
                                Object[] args) throws Exception {
        for(int time=0;time<tryCount;time++){
            URI uri = selectedEndpoint();
            try {
                return delegate().invoke(eventLoop, uri, options, codec, method, args);
            }catch (NetworkException ex){
                //do some logging
            }
        }
    }
}

Pros:can ensure the request success if the endpoint group has alive instance and the tryCount number is large enough.
Cons:the armeria is async,maybe catching the network exception in this way is not easy(need some sync test?) and has performance impact.

@trustin @anuraaga , what do you think about these candidates,Thanks!

Client-side load balancer

For the time being, simple round-robin load balancer will be enough to test if our client API design needs a change for client-side load balancing support.

Consider how we could provide great user experience when configuring a load-balanced client. For example, a user could use builder pattern to provide a list of remote hosts, weights and other parameters.

One implementation tactic I thought about is to define a custom name resolver:

new StaticServerGroup().add("hostA", portA).add(...).add(...)
        .strategy(ROUND_ROBIN).register("myGroup");
Clients.newClient("tbinary+http://myGroup/...", ...);

/cc @anuraaga @inch772

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.