Grpc - Passing different objects into grpc service method - grpc

In Chat Service, we get the request from client and send a response based on it.
But My scenario is, Server has to send some different objects from a outside method of the class.
For example,
public StreamObserver<SalaryDetails> message(StreamObserver<Employee> responseObserver) {
observers.add(responseObserver);
return new StreamObserver<SalaryDetails>() {
#Override
public void onCompleted() {
observers.remove(responseObserver);
}
#Override
public void onError(Throwable arg0) {
observers.remove(responseObserver);
}
#Override
public void onNext(SalaryDetails details) {
for(StreamObserver<MetricsToVE> observer : observers) {
**observer.onNext(Employee.newBuilder()
.setName("AA")
.setCity("B")
.build());**
}
}
};
}
In below statement I have hardcoded the fields, how should I pass an object from a different method into the grpc service class.

It depends a bit on why the response values vary:
If the client can predict what the response type is based on the request, then you should probably have the client call different methods based on the type
If the possible options are well-known to the API, then you can use protobuf's oneof.
If the data is arbitrary, then you can use protobuf's Any
It seems like #2 is likely your case.

Related

How to configure client-side load balancing with grpc-java

I've seen some high-level information about load balancing, but am struggling to put the pieces together. Here's what I've reviewed:
Load Balancing in gRPC (gRPC GitHub)
gRPC Load Balancing (gRPC blog)
gRPC on HTTP/2 Engineering a Robust, High-performance Protocol (gRPC blog)
gRPC client-side load balancing (Microsoft guide)
Java gRPC Custom Client-side load balancing (Stack Overflow)
Obviously the core pieces are a resolver and a load balancer. My use case is that I have several static, known addresses. I simply want to prioritize them as primary, secondary, etc. I believe the pick_first policy will work for this.
What I can't figure out is how to set up a custom NameResolver. I've defined a custom NameResolverProvider:
public class StaticResolverProvider extends NameResolverProvider {
#Value("${tls.enabled}")
private boolean isTlsEnabled;
#Override
protected boolean isAvailable() {
return true;
}
#Override
protected int priority() {
return 10;
}
#Override
public NameResolver newNameResolver(URI targetUri, Args args) {
return new StaticResolver();
}
#Override
public String getDefaultScheme() {
return isTlsEnabled ? "https" : "http";
}
}
and (hopefully) registered it while creating my Channel:
new NameResolverRegistry().register(new StaticResolverProvider());
Finally, here is the (currently unimplemented) NameResolver:
public class StaticResolver extends NameResolver {
#Override
public String getServiceAuthority() {
return null;
}
#Override
public void shutdown() {
}
}
These are the only two methods I see that need to be implemented. Neither of these seem to have anything to do with returning an ordered list of known addresses. The getServiceAuthority() mentions authentication, which confuses me because I don't know what the NameResolver has to do with authentication.
Please advise on what I'm missing. Thanks!
Update
I figured out the Name Resolver piece. First off, registering my resolver with my Channel looked a little different:
NameResolverRegistry.getDefaultRegistry().register(new StaticResolverProvider());
In my NameResolverProvider, I updated the getDefaultScheme() method to return "customScheme", which is the piece that would link it to my channel's call to forTarget().
The final piece was to implement the refresh() method in my NameResolver:
#Override
public void refresh() {
ResolutionResult.Builder resolutionResultBuilder = ResolutionResult.newBuilder();
List<EquivalentAddressGroup> servers = new ArrayList<>();
servers.add(new EquivalentAddressGroup(new InetSocketAddress("localhost", 50055)));
servers.add(new EquivalentAddressGroup(new InetSocketAddress("localhost", 50056)));
resolutionResultBuilder.setAddresses(Collections.unmodifiableList(servers));
listener.onResult(resolutionResultBuilder.build());
}
These are the only two methods I see that need to be implemented.
Those are the abstract ones. But the main one you need to implement is refresh() which is defined as no-op but needs to be overridden in your implementation to do anything useful. You can look at UdsNameResolver to see how refresh() is implemented and follow that pattern.
The getServiceAuthority() mentions authentication
You can ignore that for your use-case.

grpc-java CallCredentials without thisUsesUnstableApi?

I've got a gRPC server acting as a proxy. It forwards a few auth-related headers in a ServerInterceptor/ClientInterceptor pair. See below for most of the code.
My question is: the only way I've seen so far to set Metadata headers for outgoing gRPC calls is to make a subclass of CallCredentials. To do that, though, you need to confess your sins by implementing the thisUsesUnstableApi method.
Is there any stable alternative to making a subclass of CallCredentials?
The ServerInterceptor grabs the relevant headers out of Metadata and stuffs them in the Context:
#Override
public <ReqT, RespT> ServerCall.Listener<ReqT> interceptCall(
ServerCall<ReqT, RespT> call,
final Metadata requestHeaders,
ServerCallHandler<ReqT, RespT> next) {
Context contextWithAuth = Context.current();
// Cutting out my utility classes that won't help those looking for an example.
for (...) {
contextWithAuth = contextWithAuth.withValue(Context.key(foo), requestHeaders.get(Metadata.Key.of(...)));
}
return Contexts.interceptCall(contextWithAuth, call, requestHeaders, next);
}
The ClientInterceptor grabs the headers out of the Context and stuffs them in the outgoing Metadata:
#Override
public <ReqT, RespT> ClientCall<ReqT, RespT> interceptCall(
MethodDescriptor<ReqT, RespT> method, CallOptions callOptions, Channel next) {
return next.newCall(
method,
callOptions.withCallCredentials(
new CallCredentials() {
#Override
public void applyRequestMetadata(
RequestInfo requestInfo, Executor appExecutor, MetadataApplier applier) {
try {
Metadata headers = new Metadata();
for (...) {
headers.put(Metadata.Key.of(...), Context.key(...).get());
}
applier.apply(headers);
} catch (RuntimeException e) {
applier.fail(Status.UNAUTHENTICATED.withCause(e));
}
}
#Override
public void thisUsesUnstableApi() {
}
}));
}
Most header processing is handled with ClientInterceptors (and ServerInterceptors). For example, to add a header to a client:
#Override
public <ReqT, RespT> ClientCall<ReqT, RespT> interceptCall(
MethodDescriptor<ReqT, RespT> method, CallOptions callOptions, Channel next) {
return new new SimpleForwardingClientCall<ReqT, RespT>(next.newCall(method, callOptions)) {
#Override
public void start(Listener<RespT> responseListener, Metadata headers) {
headers.put(MY_HEADER_KEY, "someValue");
super.start(responseListener, headers);
}
}
}
CallCredentials is less powerful and more powerful than ClientInterceptors. It can only add request headers, but it is easy to make asynchronous and it has access to precise connection information (security, remote ip, etc). Easy asynchronous is very helpful when needing do I/O to obtain an OAuth token and being able to access security information of the transport is helpful to ensure that it is safe to transmit the bearer token. It is also called just before sending the RPC so the token can be very fresh. This doesn't sound all that relevant for you.
If you are wanting to use CallCredentials and just bothered by the unstable API then you can encourage it to be stabilized on its experimental-tracking issue. It is an older API and hasn't changed much, so may be close to stabilization.
(Aside: the annoying thisUsesUnstableApi() method is because the name CallCredentials is stable, so users can pass them around safely, but implementing it and calling it is unstable. That is a rare situation so the method is there to warn you.)
If you are making a proxy, it may be easier to use ClientCall and ServerCall directly, without the stubs. That carries the cost of needing to manage flow control directly (e.g., calling request()), but overall might be a better fit. You also may be interested in my fully-generic proxy example.

TelemetryProcessor - Multiple instances overwrite Custom Properties

I have a very basic http-POST triggered api which creates a TelemetryClient. I needed to provide a custom property in this telemetry for each individual request, so I implemented a TelemtryProcessor.
However, when subsequent POST requests are handled and a new TelemetryClient is created that seems to interfere with the first request. I end up seeing maybe a dozen or so entries in App Insights containing the first customPropertyId, and close to 500 for the second, when in reality the number should be split evenly. It seems as though the creation of the 2nd TelemetryClient somehow interferes with the first.
Basic code is below, if anyone has any insight (no pun intended) as to why this might occur, I would greatly appreciate it.
ApiController which handles the POST request:
public class TestApiController : ApiController
{
public HttpResponseMessage Post([FromBody]RequestInput request)
{
try
{
Task.Run(() => ProcessRequest(request));
return Request.CreateResponse(HttpStatusCode.OK);
}
catch (Exception)
{
return Request.CreateErrorResponse(HttpStatusCode.InternalServerError, Constants.GenericErrorMessage);
}
}
private async void ProcessRequest(RequestInput request)
{
string customPropertyId = request.customPropertyId;
//trace handler creates the TelemetryClient for custom property
CustomTelemetryProcessor handler = new CustomTelemetryProcessor(customPropertyId);
//etc.....
}
}
CustomTelemetryProcessor which creates the TelemetryClient:
public class CustomTelemetryProcessor
{
private readonly string _customPropertyId;
private readonly TelemetryClient _telemetryClient;
public CustomTelemetryProcessor(string customPropertyId)
{
_customPropertyId = customPropertyId;
var builder = TelemetryConfiguration.Active.TelemetryProcessorChainBuilder;
builder.Use((next) => new TelemetryProcessor(next, _customPropertyId));
builder.Build();
_telemetryClient = new TelemetryClient();
}
}
TelemetryProcessor:
public class TelemetryProcessor : ITelemetryProcessor
{
private string CustomPropertyId { get; }
private ITelemetryProcessor Next { get; set; }
// Link processors to each other in a chain.
public TelemetryProcessor(ITelemetryProcessor next, string customPropertyId)
{
CustomPropertyId = customPropertyId;
Next = next;
}
public void Process(ITelemetry item)
{
if (!item.Context.Properties.ContainsKey("CustomPropertyId"))
{
item.Context.Properties.Add("CustomPropertyId", CustomPropertyId);
}
else
{
item.Context.Properties["CustomPropertyId"] = CustomPropertyId;
}
Next.Process(item);
}
}
It's better to avoid creating Telemetry Client per each request, isntead re-use single static Telemetry Client instance. Telemetry Processors and/or Telemetry Initializers should also typically be registered only once for the telemetry pipeline and not for every request. TelemetryConfiguration.Active is static and by adding new Processor with each request the queue of processor only grows.
The appropriate setup would be to add Telemetry Initializer (Telemetry Processors are typically used for filtering and Initializers for data enrichment) once into the telemetry pipeline, e.g. though adding an entry to ApplicationInsights.config file (if present) or via code on TelemetryConfiguration.Active somewhere in global.asax, e.g. Application_Start:
TelemetryConfiguration.Active.TelemetryInitializers.Add(new MyTelemetryInitializer());
Initializers are executed in the same context/thread where Track..(..) was called / telemetry was created, so they will have access to the thread local storage and or local objects to read parameters/values from.

Processing GET Body with Zuul

I am using Zuul to proxy a strange client that sends a body as part of a GET request. There is unfortunately no way I can change the client.
With curl such a request can be sent as:
curl -XGET 'localhost:8765/kibana/index.html' -d' {"key": "value"}'
And the data is really sent in the body. On zuul side, however, when I try to read the body it is empty. Here is my prototype zuul code:
#Configuration
#ComponentScan
#EnableAutoConfiguration
#Controller
#EnableZuulProxy
public class ZuulServerApplication {
#Bean
public ZuulFilter myFilter() {
return new ZuulFilter(){
#Override
public Object run() {
RequestContext ctx = RequestContext.getCurrentContext();
HttpServletRequest request=(HttpServletRequest)ctx.getRequest();
try {
InputStream is=request.getInputStream();
String content=IOUtils.toString(is);
System.out.println("Request content:"+content);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return null;
}
#Override
public boolean shouldFilter() {
return true;
}
#Override
public int filterOrder() {
return 10;
}
#Override
public String filterType() {
return "pre";
}};
}
public static void main(String[] args) {
new SpringApplicationBuilder(ZuulServerApplication.class).web(true).run(args);
}
}
If I send a POST request, the this code prints the request body without problem. However, if I send the above GET request, the body is not printed. Anything I can do to actually get the body sent as part of a GET request?
It seems that some underlying machinery[0], e.g. some built-in Zuul filter with lesser filter order, replaces default "raw" HttpServletRequest with HttpServletRequestWrapper which, under standard circumstances (i.e. not GET method with body), is able to handle multiple acquisition of input stream. But in the case of GET method with body HttpServletRequestWrapper seems to not proxy input stream at all.
Thus solution could be to change filterOrder e.g. to -10.
Then it works for the filter since HttpServletRequest is used - the mentioned machinery did not get to its turn and thus didn't replace HttpServletRequest with HttpServletRequestWrapper yet. But potential issue with this solution is that the filter might exhaust input stream for something else, e.g. filter with higher filter order. But since GET with body is not a good practice anyway, it might be good enough solution after all :)
[0] I've debug into this longer time ago, but did not get to exact point - thus vague definition of "the machinery".

synchronously invoke client side method with SignalR

SignalR does not have the ability to have client methods which returns a value. So I am trying to create a helper class to make this possible.
So this is what I am trying to do:
Server side: Call client method and provide unique request id Client(clientId).GetValue(requestId)
Server side: Save requestId and wait for answer using ManualResetEvent
Client side: Inside void GetValue(Guid requestId) call server method hubProxy.Invoke("GetValueFinished", requestId, 10)
Server side: find waiting method by requestId => set return value => set signal
Server side: Method not longer waiting vor ManualResetEvent and returns retrieved value.
I am able to get it work unfortunately. Here is my code:
public static class MethodHandler
{
private static ConcurrentDictionary<Guid, ReturnWaiter> runningMethodWaiters = new ConcurrentDictionary<Guid,ReturnWaiter>();
public static TResult GetValue<TResult>(Action<Guid> requestValue)
{
Guid key = Guid.NewGuid();
ReturnWaiter returnWaiter = new ReturnWaiter(key);
runningMethodWaiters.TryAdd(key, returnWaiter);
requestValue.Invoke(key);
returnWaiter.Signal.WaitOne();
return (TResult)returnWaiter.Value;
}
public static void GetValueResult(Guid key, object value)
{
ReturnWaiter waiter;
if (runningMethodWaiters.TryRemove(key, out waiter))
{
waiter.Value = value;
}
}
}
internal class ReturnWaiter
{
private ManualResetEvent _signal = new ManualResetEvent(false);
public ManualResetEvent Signal { get { return _signal; } }
public Guid Key {get; private set;}
public ReturnWaiter(Guid key)
{
Key = key;
}
private object _value;
public object Value
{
get { return _value; }
set
{
_value = value;
Signal.Set();
}
}
}
Using this MethodHandler class I need to have two method server side:
public int GetValue(string clientId)
{
return MethodHandler.GetValue<int>(key => Clients(clientId).Client.GetValue(key));
}
public void GetValueResult(Guid key, object value)
{
MethodHandler.GetValueResult(key, value);
}
Client side implementation is like this:
// Method registration
_hubProxy.On("GetValue", new Action<Guid>(GetValue));
public void GetValue(Guid requestId)
{
int result = 10;
_hubConnection.Invoke("GetValueResult", requestId, result);
}
PROBLEM:
if I call server side GetValue("clientid"). The client method will not be invoked. If I comment out returnWaiter.Signal.WaitOne();, client side GetValue is called and server side GetValueResult is called. But of course this time the method has already returned.
I thought is has to do with the ManualResetEvent but even using while(!returnWaiter.HasValue) Thread.Sleep(100); will not fix this issue.
Any ideas how to fix this issue?
Thanks in advance!
First, I think that, rather than asking for help in how to make it synchronous, it would be best if you just told us what it is you're trying to do so we could suggest a proper approach to do it.
You don't show your MethodHandler::Retrieve method, but I can guess pretty much what it looks like and it's not even the real problem. I have to tell you in the nicest possible way that this is a really bad idea. It will simply never scale. This would only work with a single SignalR server instance because you're relying on machine specific resources (e.g. kernel objects behind the ManualResetEvent) to provide the blocking. Maybe you don't need to scale beyond one server to meet your requirements, but this still a terrible waste of resources even on a single server.
You're actually on the right track with the client calling back with the requestId as a correlating identifier. Why can't you use that correlation to resume logical execution of whatever process you are in the middle of on the server side? That way no resources are held around while waiting for the message to be delivered to the client, processed and then the follow up message, GetValueResult in your sample, to be sent back a the server instance.
Problem solved:
The problem only occured in Hub.OnConnected and Hub.OnDisconnected. I don't have an exact explanation why, but probably these methods must be able to finish before it will handle your method call to the client.
So I changed code:
public override Task OnConnected()
{
// NOT WORKING
Debug.Print(MethodHandler.GetValue<int>(key => Clients(Context.ConnectionId).Client.GetValue(key)));
// WORKING
new Thread(() => Debug.Print(MethodHandler.GetValue<int>(key => Clients(Context.ConnectionId).Client.GetValue(key)))).Start();
return base.OnConnected();
}

Resources