ipjohnson / Grace

Grace is a feature rich dependency injection container library
MIT License
336 stars 33 forks source link

Have some problem with RawRabbit Instantiation #182

Closed shahabganji closed 5 years ago

shahabganji commented 5 years ago

I was creatng a sample using RawRabbit and as always I used Grace to handle DI. however, I faced an issue in which the publisher and subscriber do not work as expected. After investigating through we got suspicious to how the objects are created so I commented the Grace parts and used the default IOC, surprisingly everything worked just fine,

I have a sample repository here

this commit uses Grace and this one with default IOC

I would be grateful if you could take a look.

Regards, -Shahab

ipjohnson commented 5 years ago

Hi @shahabganji

I ran your sample without Grace and I get the following error

HelloMicroServices> 
HelloMicroServices> 05:16:42 [Information] () Unable to connect to broker
HelloMicroServices> 
HelloMicroServices> 05:16:42 [Fatal] () Host terminated unexpectedly
HelloMicroServices> RabbitMQ.Client.Exceptions.BrokerUnreachableException: None of the specified endpoints were reachable ---> RabbitMQ.Client.Exceptions.ConnectFailureException: Connection failed ---> System.Net.Internals.SocketExceptionFactory+ExtendedSocketException: A connection attempt failed because the connected party did not properly respond after a period of time, or established connection failed because connected host has failed to respond 192.168.99.100:5672
HelloMicroServices>    at System.Net.Sockets.Socket.EndConnect(IAsyncResult asyncResult)
HelloMicroServices>    at System.Net.Sockets.Socket.<>c.<ConnectAsync>b__272_0(IAsyncResult iar)
HelloMicroServices> --- End of stack trace from previous location where exception was thrown ---
HelloMicroServices>    at RabbitMQ.Client.TcpClientAdapter.ConnectAsync(String host, Int32 port)
HelloMicroServices>    at RabbitMQ.Client.Impl.TaskExtensions.TimeoutAfter(Task task, Int32 millisecondsTimeout)
HelloMicroServices>    at RabbitMQ.Client.Impl.SocketFrameHandler.ConnectOrFail(ITcpClient socket, AmqpTcpEndpoint endpoint, Int32 timeout)
HelloMicroServices>    --- End of inner exception stack trace ---

I'm not familiar with RawRabbit so I'm not really sure what I'm looking at. My guess is that the problem has to do with Grace picking a different constructor or activating a type that wasn't expected (it automatically resolves concrete types).

Do you know the specific type that's being constructed incorrectly?

shahabganji commented 5 years ago

@ipjohnson

Your error is due totally to the absence of RabbitMQ, you need to either have it on your machine or docker, if the latter, bear in mind to change the Hostnames and port to what docker exposes.

Do you know the specific type that's being constructed incorrectly?

AFAIK it uses RawRabbit.Instantiation.RawRabbitFactory to instantiate IBusClient instances and the return value is of Type RawRabbit.Instantiation.Disposable.BusClient.

RawRabbitFactory and BusClient

 this.Client = RawRabbitFactory.CreateSingleton(new RawRabbitOptions() {
                ClientConfiguration = new RawRabbitConfiguration() {
                    Hostnames = new System.Collections.Generic.List<string>() { "localhost" },
                        Password = "guest",
                        VirtualHost = "/",
                        Port = 5672,
                        Username = "guest"

                }
            });

A code like that :point_up: works with Grace, as mentioned here

I am wondering( not sure ) how docs for v2 can be fruitful.

ipjohnson commented 5 years ago

@shahabganji I installed RabbitMQ last night but I'm still having connection issues. Is there something more I need to do?

shahabganji commented 5 years ago

@ipjohnson Have you changed these parts in the appsettings.json file to match your machine?

Port and Virtual Host are set to the default values, I think you only need to change the Hostnames to localhost or the IP address of your machine, on which you installed RbbitMQ.

Additional inforamtions are availbale here and here

shahabganji commented 5 years ago

Hi @ipjohnson,

Any success? Have you installed it on your local machine or on docker?

ipjohnson commented 5 years ago

@shahabganji so I'm able to run the example app and call the product api with and without Grace. What error should I be looking for?

shahabganji commented 5 years ago

Hi @ipjohnson

Firstly, I should mention that there is no error, there is a misbehavior of the library( at least I guess so :see_no_evil: )

When using default IOC and posting to the api, e.g. /api/shopping-cart/1?products=1&products=2

you should have some logs like

08:38:46 [Information] () Message received from rabbitmq : ShoppingCartItemAdded {UserId=1, ProductId=1}

08:38:46 [Information] () Recieived ack for 1

08:38:46 [Information] () Message received from rabbitmq : ShoppingCartItemAdded {UserId=1, ProductId=2}

08:38:46 [Information] (707ddb7a-0d5a-4a70-84df-9a2a4713fff5) Data ShoppingCartItemAdded {UserId=1, ProductId=1} published

however, when using Grace as IOC and posting to the same API, this will be found in the logs


08:45:55 [Warning] (dfd989cd-51f4-4aa5-b520-4ce8115eeeac) No body found in the Pipe context.

the latter indicates that the messages published with 0 payload which means the RawRabbit's IBusClient did not perform as it should be, and there is no subscriber in the Pipe, the former shows that everything works seamlessly.

At first I thought that may be my configuratuions here is wrong so I added:

 this.Client = RawRabbitFactory.CreateSingleton(new RawRabbitOptions() {
                ClientConfiguration = new RawRabbitConfiguration() {
                    Hostnames = new System.Collections.Generic.List<string>() { "localhost" },
                        Password = "guest",
                        VirtualHost = "/",
                        Port = 5672,
                        Username = "guest"
                }
            });

where I create the IBusClient in the constructor of subscriber and publisher, surprisingly it worked, that was the reason I got suspicious to DI system and tried to change from grace to the built-in one.

ipjohnson commented 5 years ago

@shahabganji when I run the solution and hit that url I actually don't get the logging output you mention there isn't anything about raw rabbit mq. The output below is just running it with out grace

06:29:18 [Information] (509590d2-2844-4010-ab30-72dc61c6a5e1) Incoming Request: "GET", PathString {Value="/api/shopping-cart/1", HasValue=True}, [KeyValuePair`2 {Key="Connection", Value=["keep-alive"]}, KeyValuePair`2 {Key="Accept", Value=["text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8"]}, KeyValuePair`2 {Key="Accept-Encoding", Value=["gzip, deflate, br"]}, KeyValuePair`2 {Key="Accept-Language", Value=["en-US,en;q=0.9"]}, KeyValuePair`2 {Key="Host", Value=["localhost:5001"]}, KeyValuePair`2 {Key="User-Agent", Value=["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/68.0.3440.106 Safari/537.36"]}, KeyValuePair`2 {Key="Upgrade-Insecure-Requests", Value=["1"]}]

06:29:18 [Information] (509590d2-2844-4010-ab30-72dc61c6a5e1) Route matched with "{action = \"Get\", controller = \"ShoppingCart\"}". Executing action "HelloMicroServices.Controllers.ShoppingCartController.Get (HelloMicroServices)"

06:29:18 [Information] (509590d2-2844-4010-ab30-72dc61c6a5e1) Executing action method "HelloMicroServices.Controllers.ShoppingCartController.Get (HelloMicroServices)" with arguments (["1"]) - Validation state: Valid

06:29:18 [Information] (509590d2-2844-4010-ab30-72dc61c6a5e1) Executed action method "HelloMicroServices.Controllers.ShoppingCartController.Get (HelloMicroServices)", returned result "Microsoft.AspNetCore.Mvc.OkObjectResult" in 2.4329ms.

06:29:18 [Information] (509590d2-2844-4010-ab30-72dc61c6a5e1) Executing ObjectResult, writing value of type '"HelloMicroServices.Datastores.Models.ShoppingCart"'.

06:29:18 [Information] (509590d2-2844-4010-ab30-72dc61c6a5e1) Executed action "HelloMicroServices.Controllers.ShoppingCartController.Get (HelloMicroServices)" in 198.242ms

06:29:18 [Information] (509590d2-2844-4010-ab30-72dc61c6a5e1) Request: "GET", PathString {Value="/api/shopping-cart/1", HasValue=True} executed in 295 ms

06:29:18 [Information] (509590d2-2844-4010-ab30-72dc61c6a5e1) Outgoing Response: 200, [KeyValuePair`2 {Key="Date", Value=["Wed, 15 Aug 2018 12:29:18 GMT"]}, KeyValuePair`2 {Key="Transfer-Encoding", Value=["chunked"]}, KeyValuePair`2 {Key="Content-Type", Value=["application/json; charset=utf-8"]}, KeyValuePair`2 {Key="Server", Value=["Kestrel"]}]
shahabganji commented 5 years ago

Sorry my bad, the url I mentioned above is an HTTP POST.

ipjohnson commented 5 years ago

@shahabganji ok I was able to get messages published by turning off the auto registration of unknown types.

Program.cs

.UseGrace(new InjectionScopeConfiguration{ AutoRegisterUnknown = false })

Startup.cs

        public void ConfigureContainer(IInjectionScope scope)
        {
           // empty as everything is already registered in the service collection
        }
shahabganji commented 5 years ago

@ipjohnson, thanks for your response and solution. Is it kinda a mixed mode between Grace and Microsoft.Extensions.DependencyInjection; using the built-in DI to register and Grace to instantiate.

May I ask what was the problem? and what if I move the registration of services to ConfigureContainer method? When I do, MVC pipeline fails at runtime.

and one thing more is it possible to have access to IServiceCollection in ConfigureContainer, since it would be a clearer code if we had all related stuff at one place.

ipjohnson commented 5 years ago

@shahabganji correct the way Microsoft designed DI for asp.net core is that all of the framework dependencies are registered in the IServiceCollection (and 3rd party dependencies). Then all of those definitions are loaded into the external container (this case it's Grace). Then finally the ConfigureContainer method is called to do any registration for the 3rd party container using the specific registration interface for the container.

The problem you were having related to the fact that Grace will auto register concrete types by default so I think one of the optional configuration classes was being instantiated when it wasn't supposed to.

You have to keep the ConfigureContainer method but you can leave it empty and do all of your registration against the IServiceCollection in the ConfigureServices method.

shahabganji commented 5 years ago

That's what I expected from external containers, however, I expect that by turning off the AutoRegisterUnknown property and moving the registration of services to ConfigureContainer, the application still work. but to my surprise it doesn't !!!

One more thing, how could we find out that which dependency causes the problem? Is there any flag or log that reveals such information?

ipjohnson commented 5 years ago

@shahabganji you'd have to ask the raw rabbit folks what specifically didn't work in raw rabbit, from my perspective Grace functioned properly as it resolved all dependencies without exceptions.

I look at a container as something you use to construct your application. Not all containers function the same. I've made Grace a container that can be configured for many different scenarios. In this case it had to be configured to turn off auto registration because of the way RawRabbit is written not because it's an issue with Grace. Not saying that what they did was wrong in any way just that Grace had to be configured for the specific use case.

As for the ConfigureContainer method having to be there, that's part of the Microsoft DI conventions and is true of all 3rd party container not just Grace. I believe the method signature is used by the framework to figure out what container to use. The UseGrace method just adds a dependency to the service collection which is then resolved by the framework.

shahabganji commented 5 years ago

@ipjohnson Thanks for your time.

ipjohnson commented 5 years ago

@shahabganji glad it worked it out. To answer your question of is there a log for Grace. There isn't a regular log but there is a Trace function that you can attach to and it will call during construction of the activation delegate.

var container = new DependencyInjectionContainer(c => c.Trace = s => Console.WriteLine(s));
shahabganji commented 5 years ago

@ipjohnson Good to know about that :+1: