-
Notifications
You must be signed in to change notification settings - Fork 914
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Introduce GsonGrpcJsonMarshaller and catch JsonMarshaller initialization failure #4033
Conversation
Codecov Report
@@ Coverage Diff @@
## master #4033 +/- ##
============================================
+ Coverage 73.36% 73.40% +0.03%
- Complexity 15893 15947 +54
============================================
Files 1385 1388 +3
Lines 60755 60883 +128
Branches 7701 7714 +13
============================================
+ Hits 44575 44689 +114
- Misses 12283 12291 +8
- Partials 3897 3903 +6
Continue to review full report at Codecov.
|
grpc/src/main/java/com/linecorp/armeria/internal/common/grpc/GrpcJsonUtil.java
Outdated
Show resolved
Hide resolved
grpc/src/main/java/com/linecorp/armeria/internal/common/grpc/UpstreamJsonMarshaller.java
Outdated
Show resolved
Hide resolved
grpc/src/main/java/com/linecorp/armeria/internal/common/grpc/UpstreamJsonMarshaller.java
Outdated
Show resolved
Hide resolved
} else if (grpcJsonMarshallType == GrpcJsonMarshallType.PROTOBUF_JACKSON) { | ||
return GrpcJsonUtil.protobufJacksonJsonMarshaller(serviceDescriptor, jsonMarshallerCustomizer); | ||
} else if (grpcJsonMarshallType == GrpcJsonMarshallType.UPSTREAM) { | ||
return UpstreamJsonMarshaller.INSTANCE; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
GrpcJsonMarshallType.UPSTREAM
and jsonMarshallerCustomizer
are mutually exclusive.
Originally, this builder is designed for protobuf-jackson
.
How about adding some factory methods instead of GrpcJsonMarshallType
?
// Choose the JSON codec depending on a proto syntax.
GrpcJsonMarshaller.of();
// Always uses protobuf-jackon
GrpcJsonMarshaller.ofJackson(...);
GrpcJsonMarshaller.builderForJackson()
.jsonMarshallerCustomizer(...)
.build(ServiceDescriptor);
// Always uses protobuf-util's JsonFormat
GrpcJsonMarshaller.ofGson(...);
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good point.
I've updated my thoughts on the default implementation in the PR description.
I'm thinking it might be better to leave the default as-is, and just providing a gson
implementation which users can opt-in. This can be slightly inconvenient since users need to add compatibility for both client and server-side, but at least there will be no breaking changes.
GrpcService.builder()
.jsonMarshallerFactory(sd -> GrpcJsonMarshaller.ofGson())
.build();
...
GrpcClients.builder(server.httpUri(GrpcSerializationFormats.JSON))
.jsonMarshallerFactory(sd -> GrpcJsonMarshaller.ofGson())
.build(Proto2ServiceBlockingStub.class);
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm thinking it might be better to leave the default as-is
Sounds good. 👍
5bf6f55
to
f24c861
Compare
f24c861
to
58c0609
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nice work, @jrhee17!
grpc/src/main/java/com/linecorp/armeria/common/grpc/GrpcJsonMarshaller.java
Outdated
Show resolved
Hide resolved
* Sets a {@link Consumer} that can customize the {@link JsonFormat.Parser} for {@link Message} | ||
* used when handling JSON payloads in the service. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
* Sets a {@link Consumer} that can customize the {@link JsonFormat.Parser} for {@link Message} | |
* used when handling JSON payloads in the service. | |
* Adds a {@link Consumer} that can customize the {@link JsonFormat.Parser} | |
* used when deserializing a JSON payload into a {@link Message}. |
grpc/src/main/java/com/linecorp/armeria/common/grpc/GsonGrpcJsonMarshallerBuilder.java
Outdated
Show resolved
Hide resolved
grpc/src/main/java/com/linecorp/armeria/common/grpc/GrpcJsonMarshaller.java
Outdated
Show resolved
Hide resolved
grpc/src/main/java/com/linecorp/armeria/internal/common/grpc/GsonGrpcJsonMarshaller.java
Outdated
Show resolved
Hide resolved
Note: also added a warning log Sample ⬇️
|
4a22f5b
to
1bd8e83
Compare
grpc/src/main/java/com/linecorp/armeria/common/grpc/GrpcJsonUtil.java
Outdated
Show resolved
Hide resolved
Updated Version of the warning log
|
5fcac6b
to
9528f02
Compare
grpc/src/main/java/com/linecorp/armeria/common/grpc/GrpcJsonMarshaller.java
Outdated
Show resolved
Hide resolved
return new GrpcJsonMarshallerBuilder(); | ||
} | ||
|
||
/** | ||
* Returns a newly-created {@link GrpcJsonMarshaller} which serializes and deserializes {@link Message}. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should add notes that the performance is much lower and should only be used if the default has problems, for example enum values in proto2 files. In practical terms, ofGson
is not fast enough for production workloads - actually I don't know if having these factories solves the user's reported issue since it still requires configuration, as opposed to configuring the serialization formats. AFAICT, the user didn't need JSON, but they were having issues because the default serialization formats includes it. Requiring a different option may not actually help them.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should add notes that the performance is much lower
Thanks for looking though this PR! Left an additional note in the javadocs that addresses this.
AFAICT, the user didn't need JSON, but they were having issues because the default serialization formats includes it.
I still think this PR has meaning in that users who are committed to using armeria
still have an option to use the upstream marshaller implementation if they'd like.
Having said this, if we want to avoid breaking changes and avoid user-side extra configuration, I guess the other option we have is to just swallow the exception and warn when building a MessageMarshaller
fails. Let me also consider this.
grpc/src/main/java/com/linecorp/armeria/common/grpc/GrpcJsonMarshaller.java
Outdated
Show resolved
Hide resolved
grpc/src/main/java/com/linecorp/armeria/common/grpc/GrpcJsonUtil.java
Outdated
Show resolved
Hide resolved
3fc0593
to
a54508e
Compare
Note to reviewers: Updated behavior to swallow an exception and warn if constructing |
proto2
.collect(toImmutableMap(ServiceDescriptor::getName, jsonMarshallerFactory)); | ||
} catch (Exception e) { | ||
logger.warn("Failed to instantiate a JSON marshaller. Consider " + | ||
"disabling gRPC-JSON serialization or using {}.ofGson() instead.", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
disabling gRPC-JSON serialization with <method reference> or using ...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks a lot, @jrhee17!
* and {@code Gson} to serialize and deserialize messages. | ||
* Note: | ||
* <a href="https://developers.google.com/protocol-buffers/docs/reference/java/com/google/protobuf/util/JsonFormat">JsonFormat</a> | ||
* has a known performance issue. Use this method only when |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could you leave a link to the performance issue? 😄
@@ -1,5 +1,5 @@ | |||
/* | |||
* Copyright 2020 LINE Corporation | |||
* Copyright 2022 LINE Corporation |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
revert?
Sorry about the last minute change, but I realized that client-side could also benefit from richer exception messages. |
try { | ||
jsonMarshaller = options.get(GrpcClientOptions.GRPC_JSON_MARSHALLER_FACTORY) | ||
.apply(serviceDescriptor); | ||
} catch (RuntimeException e) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
How about catching Exception
like FramedGrpcService
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nice work, @jrhee17 🙇
- grpc + armeria 사용 시 JsonMarshaller 초기화 실패하는 버그가 있는 듯 함. - line/armeria#4033 - jsonMarshallerFactory 이용해서 JsonMarshaller 초기화
Motivation:
Currently, armeria server fails to start up while trying to create a
MessageMarshaller
forproto2
files containingEnum
definitions.It seems like protobuf-jackson doesn't support
proto2
.However, it seems like upstream at least partially supports
proto2
.https://developers.google.com/protocol-buffers/docs/reference/java/com/google/protobuf/util/JsonFormat
I propose that we fallback to use the upstream implementation as the default json marshaller when a service includes anyproto2
defined message.I realized that it is difficult to support such default implementation since
proto2
specific features are used. Even thoughproto2
files are included, users may not be usingproto2
specific features. These users will incur the penalty of using thegson
implementation unnecessarily.GrpcJsonMarshallerBuilder#jsonMarshallerCustomizer
is exposed, it is difficult to introduce a new default json marshalling implementation without breaking changes. (we will need to add a mapping fromMessageMarshaller#Builder
toJsonFormat
options which probably isn't generic/worth it)Considering the above, I believe it is easier just provide an option so that users can opt-in for
gson
serdeModifications:
GsonGrpcJsonMarshaller
which performs json marshalling using google's apis. Also introduce a corresponding builder classGrpcJsonMarshallerBuilder
GrpcJsonMarshaller#ofJackson
,GrpcJsonMarshaller#builderForJackson
,GrpcJsonMarshaller#ofGson
,GrpcJsonMarshaller#builderForGson
Result:
gson
implementation