gigya / microdot

Microdot: An open source .NET microservices framework
Apache License 2.0
1.54k stars 231 forks source link

questions/discussions #110

Open vicpon opened 6 years ago

vicpon commented 6 years ago

Where is the best place to ask about microdot?

tsibelman commented 6 years ago

For now, this is the best place.

vicpon commented 6 years ago

Okay. May I suggest gitter? https://gitter.im/

So my question is about documentation/support. I'm in the very early stages of implementing a microservices solution and am comparing different things: microdot, orleanka, or a more home grown solution like like the MS eShopOnContainerr sample (https://github.com/dotnet-architecture/eShopOnContainers).

I would really like to use microdot. It looks like you guys have done a great job of building this but I'm a bit wary because it's so new and there isn't much documentation to get started with. Is microdot used in production within gigya? What are the plans for microdot? How many contributors are there? Is it actively being developed or is it waiting for outside contributors to join or show interest?

Thanks

tsibelman commented 6 years ago

Microdot is actively used in Gigya (now part of SAP ) it supports very high scale application in production with dozens of microservices developed on top of it, it constantly evolving you can check release history. We have some documentation in Wiki and we have samples repository you should take a look.

The code base is not very large and not very complex so even if you encounter some bug it would be easy for you to overcome it.

Ultimately you should make a decision that works best for you, you can use Microdot or you can just look at Microdot as a sample and source of ideas for your own solution.

vicpon commented 6 years ago

Thanks for the response. I'll look into the code more and try to understand everything.

Allon-Guralnek commented 6 years ago

Just wanted to add that Microdot is at the core of Gigya's solution and services built upon it are serving paying customers at this very moment. We make changes to Microdot on a daily basis and it is actively maintained.

As far as documentation, we're far behind where we'd like to be. Unfortunately, at this point we can't dedicate as much time as for documentation as it deserves (extremely time consuming task), but I'm hoping this changes in the near future. In any case, if you have any question or need guidance on any topic or subsystem of Microdot, we'll do our best to help you with it. Feel free to ask any questions you have, and mention the most critical parts of the documentation that are missing for you, so we can prioritize them.

vicpon commented 6 years ago

Thank you gentlemen. I really appreciate the quick replies and am hoping microdot becomes widely popular. ++1

vicpon commented 6 years ago

In the sample InventoryService project, the InventoryServiceHost is used as a console application so it uses Source="Local" as the service discovery method. I think the client service discovery would be a priority for me as I would like to implement a polyglot microservices architecture where some of the microservices may not be Orleans/Microdot services. I'm thinking that that this could be accomplished using an HTTP interface to the Microdot service(s).

Allon-Guralnek commented 6 years ago

In order to support certain features such as distributed tracing, host overrides and certain C# language features (such as method overloading), the request to a service uses an "envelope" that contains more than just the parameters to the method, it also contains information about which method to call on which type, tracing and correlation information and more metadata. In order for a non-Microdot client to call methods on a Microdot service, they would have to know the method's metadata (otherwise obtained from the interface assembly that the service publishes to all its clients) and create a compatible envelope (which is not particularly difficult, its simple JSON). Method metadata can be obtained from a metadata endpoint that each Microdot service exposes, or you could write a tool to statically generate that metadata from the interface assembly into your target language and embed it into the client.

A trickier part would be exception serialization which heavily relies on .NET exception types, embedded in the $type JSON property that is used for polymorphism in Json.NET. You may be able to get away with simply deserializing those exceptions into simpler non-exception types on the other side.

At Gigya we do have a Java system that makes very specific calls to a certain Microdot-based service that has an extremely stable API surface, so it can be done. But you'll lose some of the features and the seamless experience which makes Microdot unique.

vicpon commented 6 years ago

Thanks for the high level explanation of how Microdot works. It makes sense that the service API calls are more than the method contract defines given everything Microdot does for you out of the box. I may consider putting one ore more gateways in front of the Microdot services to abstract the envelope and provide simple HTTP contracts to my clients.

Allon-Guralnek commented 6 years ago

At Gigya we have our own API gateway (based on ASP.NET Core) that handles all incoming API requests from the internet at large and translates them into Microdot service calls into our plethora of services. It uses a special feature of Microdot that allows dynamic discovery and dispatch of calls without needing to deploy a new version of the gateway every time a new service is used or its interface changes, instead it queries the aforementioned metadata endpoint of all the services to discover which URLs should be routed to which service methods, all at runtime and responding to any changes immediately without intervention. The metadata is generated by each of the services based on .NET attributes which you use to decorate certain methods in your service that you'd like to make callable through the API gateway.

This API gateway is very Gigya-specific, performing specialized authentication and authorization, so it's not suitable to open-source it at this point (but we're thinking about it). Nevertheless, the dynamic discovery and dispatch is already in Microdot, and you might find it useful if you'll go down that path. Between services there is less of a need for dynamic dispatch, so we still use strongly- and statically-typed interfaces for ease of development allowing cross-service communication at a higher level of abstraction, without concerning yourself with the underlying protocol or serialization used (HTTP and JSON, currently) like you do in an API gateway.

vicpon commented 6 years ago

I am really curious on the dynamic discovery and dispatch of calls feature of Microdot. Would it be possible to add a simple demo in the samples repo?

daniel-lamberger commented 6 years ago

Victor, we've been meaning to open-source dynamic dispatching, we'll try to do so in the next couple of weeks, or at least to provide a code sample. I'll let you know.

daniel-lamberger commented 6 years ago

@victor-ponce Sorry for not getting back to you, we didn't prioritize open-sourcing that part for now. Did you manage to get things going? Need assistance?

vicpon commented 6 years ago

@daniel-lamberger. I took a look at the code but wasn't able to figure it out before I had to move on to something else. I'll have to wait until more examples and documentation area available. Thanks

sabernar commented 6 years ago

Would there be any difficulty using microdot with Docker?

Allon-Guralnek commented 6 years ago

@sabernar: No, there shouldn't be any difficulty. In fact, before Docker was supported by Windows, we ran Microdot inside a Docker container with Linux and Mono, managed by Kubernetes. That is, until we found out the Mono GC tends to crash under certain high-load situations (this issue might have been resolved since), and so we returned to Windows. Our next attempt at Linux will be with .NET Core 2.0 when Orleans supports it in it's 2.0 version which is currently in beta.

quinvit commented 6 years ago

I am facing with HttpService and basePort issue. I have a multi tenants software, each tenant is a group of micro services. The first problem is I don't know which port is in used by other software or not. The second problem is how to manage and assign the port range for each group of micro services in a tenant in order they are not conflicted together. Do we have any best practice for this case?

Allon-Guralnek commented 6 years ago

@quinvit, you can use the --BasePortOverride:1234 command line argument (see this wiki article) to assign base ports dynamically for each service in each group. So you could have the first group start at port 20000, and assign the services within it ports 20000, 20010, 20020, 20030, etc. Then you can have the next group of services use ports 21000, 21010, 21020, etc. This leaves room for 10 ports per service, and 100 services per groups, and you can have tens of groups per machine.

Then you'll either have to assign all of these statically and record them in the Discovery section of the configuration file, or you can use a service discovery mechanism such as Consul which is natively supported by Microdot (or write your own interface implementations to use a different mechanism).

quinvit commented 6 years ago

I am using a .NET Core 2 web app and a group of micro-services, can I use my web app as API Gateway, as Client to consume multiple micro-services with microdot directly via kernel.Get()? I tried but it doesn't work. It looks like a communication issue.

Allon-Guralnek commented 6 years ago

@quinvit Could you please post the complete exception here?

quinvit commented 6 years ago

Sorry late reply. Because my .NET Core 2 Web App (target .netcore2.0) said that incompatible assembly issue so I give up without try to re-produce the exception. I used another standalone console app to host API Gateway with NancyFx. Where I can find out how to apply authentication between micro services using Microdot? Thank @Allon-Guralnek .

quinvit commented 6 years ago

Can I implement another logging module for microdot and create a PR for it? Serilog is my favorite one.

Allon-Guralnek commented 6 years ago

@quinvit: Out of the box, Microdot only support authentication and encryption between services using HTTPS and both client and server certificates (which must be issued by the same CA, so they're effectively "pinned"). It involves a lot of certificate installing on the machines involved, and I'm not sure that's the kind of inter-service authentication you're looking for.

As for a new logging module, feel free to implement one for your favorite logger and we'll be happy to accept it as a PR!

quinvit commented 6 years ago

Is there anyway to override base port of [HttpService(8020, UseHttps = false)] for non Orleans service? I tried passing BasePortOverride via command line but it doesn't work for non Orleans service.

daniellm commented 6 years ago

Make sure you're using this syntax: --BasePortOverride:1234

quinvit commented 6 years ago

Thank @daniellm and yes, I used that syntax. The service runs well under that port but at client code, it is still discovered under 8020 port. Is there anyway to set it to 8020 too at client code?

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
   <Discovery>
    <Services>
      <MyService Source="Local"  />
    </Services>
  </Discovery> 

</configuration>
Allon-Guralnek commented 6 years ago

Try this:

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  <Discovery>
    <Services>
      <MyService Source="Config" Hosts="localhost:8020" />
    </Services>
  </Discovery> 
</configuration>

If you want the HTTP call to be visible in Fiddler, use your machine name instead of localhost. Basically, Source="Local" is meant only for testing on the local machine with the default port.

quinvit commented 6 years ago

It works great. Thank @Allon-Guralnek .

Cowraider commented 6 years ago

Is there another way of calling a method in my ServiceInterface using the HttpServiceListener class? I want to call my service and use the response in a WebClient.

When I do a POST call with the following data in the body

 { "Target" : 
    {
        "TypeName" : "SomeNamespace.Interface.IMyService",
        "MethodName" : "MyMethod"
    }   
}

I get the results I want. Is there any other (easier) way to accomplish this?

Allon-Guralnek commented 6 years ago

@Cowraider: Currently there is no other way. But we are accepting merge requests, so if you have another way you'd like to call methods, feel free to bounce the idea around and even submit some code.

Cowraider commented 6 years ago

@Allon-Guralnek Alright, thanks!

Cowraider commented 6 years ago

I have another question regarding the HttpServiceListener. I always get a NullReferenceException in the GetParametersByName method when I am passing some arguments. Following data is in the payload body:

{ 
"Target" : 
    {
        "TypeName" : "SomeNamespace.Interface.IMyService",
        "MethodName" : "Test",
        "Arguments" : { "param" : 42 } 
    } 
}

The Method signature in the IMyService interface looks like this: Task<int> Test(int param);

Without passing arguments it works just fine but I am having troubles with arguments. Am I doing something wrong here?

Allon-Guralnek commented 6 years ago

@Cowraider Could you post the stack trace of the exception?

Cowraider commented 6 years ago

here it is:

{
   "$type": "System.NullReferenceException:System.SystemException, mscorlib:mscorlib",
   "ClassName": "System.NullReferenceException",
   "Message": "Object reference not set to an instance of an object.",
   "Data": null,
   "InnerException": null,
   "HelpURL": null,
   "StackTraceString": "--- End of stack trace from MemberService v1.0.0.0 on Andreas-HP in global-dev ---\r\n  
       at Gigya.Microdot.Hosting.HttpService.HttpServiceListener.<>c__DisplayClass63_0.<GetParametersByName>b__0(ParameterInfo p)\r\n   
         at System.Linq.Enumerable.WhereSelectArrayIterator`2.MoveNext()\r\n   
         at System.Linq.Buffer`1..ctor(IEnumerable`1 source)\r\n   at
     System.Linq.Enumerable.ToArray[TSource](IEnumerable`1 source)\r\n   
         at Gigya.Microdot.Hosting.HttpService.HttpServiceListener.<GetResponse>d__62.MoveNext()\r\n   
         at Gigya.Microdot.Hosting.HttpService.HttpServiceListener.<HandleRequest>d__54.MoveNext()",
   "RemoteStackTraceString": null,
   "RemoteStackIndex": 0,
   "ExceptionMethod": "8\n<GetParametersByName>b__0\nGigya.Microdot.Hosting, Version=1.10.5.841, Culture=neutral, PublicKeyToken=null\nGigya.Microdot.Hosting.HttpService.HttpServiceListener+<>c__DisplayClass63_0\nSystem.Object <GetParametersByName>b__0(System.Reflection.ParameterInfo)",
   "HResult": -2147467261,
   "Source": "Gigya.Microdot.Hosting",
   "WatsonBuckets": null
}
Allon-Guralnek commented 6 years ago

@Cowraider: Ah, Arguments needs to be outside Target, i.e. at the root of the JSON. Target can only contain TypeName, MethodName and ParameterTypes (which is used for overload resolution, not needed in your case since Test has no overloads).

You can see how the layout of the JSON needs to be by inspecting the HttpServiceRequest class.

Cowraider commented 6 years ago

@Allon-Guralnek oh wow, I inspected the HttpServiceRequestclass but I must have overlooked it. Thank you very much. It's working.