As a mechanism to connect microservices together and make them work it is usually suggested to use APIs and Service Discovery. But they usually work as their own microservices, but these ones should apparently be "hard-coded" into others, as every microservice is supposed to register with them and query for other microservices' locations. Doesn't this break the idea of loose coupling, since a loss of a discovery service implies others being unable to communicate?
Pretty much yes. If one microservice "knows" about another microservice - it means that they are highly coupled. It doesn't matter where specifically this knowledge about other service is coming from: hardcoded, config file or maybe some service discovery, it still leads to high coupling.
What's important to understand is that many microservice evangelists are just preaching about how to build monolith apps on top of Web APIs. Some of them probably think that the more buzz words they use - the better ... not quite sure why this happens. It is probably easier to fake a language and become an "expert" by using buzzword salad instead of really building fault tolerant and horizontally scalable app.
But there is another way to look at service discovery: when it is used by service clients like SPA application or an API Gateway it may be very useful. Clients and gateways should know about service APIs, otherwise, the whole thing doesn't make sense. But they can use a registry to make it more flexible/dynamic.
So, to summarize:
if services are using discovery to get more information about each other - this is probably a bad thing and design flaw (pretty sure there are corner cases where this may be a valid scenario, please post a comment if you know some)
if discovery is used by other parts of the app, like SPA or API Gateway, this may be useful, but not necessarily it always is.
PS: to avoid high coupling, consider reading series of articles by Jeppe Cramon that illustrate the problem and possible solutions very nicely.
In a distributed system, you will always have some amount of coupling, what you want to do is to reduce all aspects of coupling to a minimum.
I'd argue that is does matter how you design your service location. if your code knows of the other service i.e. OrderService.Send(SubmitOrderMessage); (where 'OrderService' is an instance of the other service's proxy)
as opposed to transportAgent.Send(SubmitOrderMessage); (where 'transportAgent' is an instance of the transport's proxy i.e. the queuing service/agent and the actual address of the queue can be in your config), this reduces the coupling and your business logic code (Service) and delegates the routing to your infrastructure.
Make Sense?
Each microservice is expected to be functionally independent. For interacting with other microservices, it should rely on rest api calls only. Service Discovery plays a role to keep the services fairly loose coupled with each other. Also due to dynamic nature of the service urls, the hard dependencies are removed. Hope this helps
Reduced coupling or fairly loose coupling still has one thing in common; coupling. In my opinion, coupling to any degree will always create rigid communication patterns that are difficult to maintain and difficult to troubleshoot as a platform grows into a large distributed platform. Isn't the idea behind microservices to allow consumers to engage in "Permissionless innovation?" I would suggest that this is only possible by decomposing to microservices that have high cohesion and low coupling and then let the consumer decide how to route, orchestrate or aggregate.
Related
gRPC is a modern open source high performance RPC framework that can
run in any environment. It can efficiently connect services in and
across data centers with pluggable support for load balancing,
tracing, health checking and authentication. It is also applicable in
last mile of distributed computing to connect devices, mobile
applications and browsers to backend services.
I'm finding GRPC is becoming increasingly more pertinent in backend infrastructure, and would've liked to have it in my favorite language/tsdb kdb+/q.
I was surprised to find that kdb+ does not have a grpc implementation. Obviously, the (https://code.kx.com/q/interfaces/protobuf/)
package doesn't support the parsing of rpc's, is there anything quantitatively preventing there being a KDB+ implementation of the rpc requests/services etc. found in grpc?
Why would one not want to implement rpc's (grpc) in kdb+ and would it be a good idea to wrap a c++/c implemetation therin inorder to achieve this functionality.
Thanks for your advice.
Interesting post:
https://zimarev.com/blog/event-sourcing/myth-busting/2020-07-09-overselling-event-sourcing/
outlines event sourcing, which I think might be a better fit for kdb?
What is the main issue with services using RPC calls to exchange information? Well, it’s the high degree of coupling introduced by RPC by its nature. The whole group of services or even the whole system can go down if only one of the services stops working. This approach diminishes the whole idea of independent components.
In my practice I hardly encounter any need to use RPC for inter-service communication. Partially because I often use Event Sourcing, more about it later. But we always use asynchronous communication and exchange information between services using events, even without Event Sourcing.
For example, an order microservice in an e-commerce system needs customer data from the customer microservice. These dependencies between microservices are not ideal. Other microservices can go down and synchronous RESTful requests over https do not scale well due to their blocking nature. If there was a way to completely eliminate dependencies between microservices completely the result would be a more robust architecture with less bottlenecks.
You don’t need Event Sourcing to fix this issue. Event-driven systems are perfectly capable of doing that. Event Sourcing can eliminate some of the associated issues like two-phase commits, but again, not a requirement to remove the temporal coupling from your system.
After reviewing and experimenting with service mesh offerings, isn't Service Mesh a monolithic (ironic) solution for a microservice universe?
Actually, I think they are a logical side effect of a microservice universe. One of the largest drivers for moving to microservice architectures is to separate concerns such that individual services implement very specific functionality very well.
For example, a translation service should take input text in one language and convert it to output text in another language. The team writing that service should consist of generalized developers and specialized linguists. Yet, in many cases we force the developers of this business logic to concern themselves with other things (e.g., transport layer security, access control, metrics collection, announcement and discovery).
The point of a service mesh it to treat the networking of microservices as almost another microservice. It's a specialized concern that, when treated that way, allows others to focus on doing what generates revenue or solves problems.
There is a reason much of the push on microservices is coming out of companies and organizations who are pretty deep in the microservice universe at very high scale. It should also be noted that most service mesh control planes are implemented via microservices, not monoliths.
For some aspects yes, they are. Service mesh clients usually run on containers which handle (possibly) authN, authZ, aggregate logging and load balancing.
On the other side, when we move out from a monolith, we should theoretically duplicate all the business logic and authorization mechanisms. This would introduce replication and increase the maintenance effort.
Service mesh are useful because they extract these concerns out from the dev team.
I have a micro-service project with multiple services in .NET Core. When it comes to placing the controllers, there are 2 approaches:
Place the controllers in respective Micro Services, with Startup.cs in each micro-service.
Place all controllers in a separate project and have them call the individual services.
I think the 1st approach will involve less coding effort but the 2nd one separates controllers from actual services using interfaces etc.
Is there a difference in terms of how they are created and managed in Fabric using both approaches.
This is very broad topic and can raise points for discussion because it all depends on preferences, experiences and tech stacks. I will add my two cents, but do not consider it as a rule, just my view for both approaches.
First approach (APIs for each service isolated from each other):
the services will expose their public APIs themselves and you will need to put a service discovery approach in place to enable clients to call each microservice, a simple one is using the reverse proxy to forward the calls using the service name.
Each service and it's APIs scales independently
This approach is better to deploy individual updates without taking down other microservices.
This approach tends to have more code repetition to handle authorization, authentication, and other common aspects, from there you will end up doing shared libraries using on all services.
This approach increase the points of failures, it is good because failures will affect less services, if one API is failing, other services won't be impacted (if the failure does not affect the machine like memory leak or high CPU usage).
The second approach (Single API to forward the calls to right services):
You have a single endpoint and the service discovery will happen in the API, all work will be handled by each services.
The API must scale for everyone even though one service consumes much more resources than others. just the service will scale independently.
This approach, to add or modify api endpoints, you will likely update the API and the service, taking down the API will affect other services.
This approach reduces the code duplication and you can centralize many common aspects like Authorization, request throttling and so on.
This approach has less points of failures, if one microservices goes down, and a good amount of calls depend on this service, the API will handle more connection and pending requests, this will affect other services and performance. If it goes down, every services will be unavailable. Compared to the first approach, the first approach will offloaded the resilience to the proxy or to the client.
In summary,
both approaches will have a similar effort, the difference is that the effort will be split into different areas, you should evaluate both and consider which one to maintain. Don't consider just code in the comparison, because code has very little impact on the overall solution when compared with other aspects like release, monitoring, logging, security, performance.
In our current project we have a public facing API. We have several individual microservice projects for each domain. Being individual allows us to scale according to the resources each microservice use. For example we have an imaging service that consumes a lot of resources, so scaling this is easier. You also have the chance to deploy them individually and if any service fails it doesn't break the whole application.
In front of all the microservices we have an API Gateway that handles all the authentication, throttles, versioning, health checks, metrics, logging etc. We have interfaces for each microservice, and keep the Request and Response models seperately for each context. There is no business logic on this layer, and you also have the chance to aggregate responses where several services need to be called.
If you would like to ask anything about this structure please feel free to ask.
I have a question about microservice implementation. right now I am using an api gateway to process all get request to my individual services and using kafka to handle asynchronous post put and delete request. Is this a good way of handling of handling request in a microservice architecture?
Your question is too unspecific to give a good answer. What is a good architecture totally depends on the details of your use cases. Are you serving web pages, streaming media, amass data for analysis, or something completely different? We would also need to know what are you requirements in terms of concurrency, consistency and scalability? What are the constraints for budget/size of development teams, ease of development, dev skills, etc?
For example the decisions you have taken may be considered good if you have strong requirements for a highly scalable input of large data sets and very frequent data collection as well as the team to support it. But it may be considered bad if you have a small team only and are trying to get a quick and cheap MVP for a new service that has limited scalability requirements (because the complexity of the solution slows down your development unnecessarily).
It may be good because the development team is familiar with those technologies and can effectively develop with those. Or it may be bad because your team does not know anything about those and the investment in learning those will not be justifiable by long term gains.
Don't forget that one of the ideas of the microservices architectural style is that each service can be owned by a distinct team that makes its own decisions about what technology to use for implementation (for whatever reason: ease of development, business reasons etc). So in other words the microservices style embraces the old wisdom architecture follows organization.
Here a link to a recommended further read.
To anyone with real world experience breaking a monolith into separate modules and services.
I am asking this question having already read the MonolithFirst blog entry by Martin Fowler. When taking a monolith and breaking it into microservices the "size" element of the equation is the one that I ponder over the most. Specifically, how to approach breaking a monolith application (we're talking 2001: A Space Oddessy; as in it is that old and that large) into micro services without getting overly fine grained or staying too monolithic. The end goal is creating separate modules that can be upgraded indepenently and scaled independently.
What are some recommended best practices based on personal experience of breaking a monolith into microservices?
The rule of thumb is breaking the monolith based on bounded context . The most common way of defining the bounded context is using BU ( Business Unit) . For example the module which does actual payment is mostly a separate BU .
The second thing to consider is the overhead micro-services bring. You should analyse the hardware , monitoring , infra pieces before completely breaking the service. What I have seen is people taking smaller microservices out of monolith instead of going and writing say 10 new service and depreciating the monolith.
My advice will be have an incremental approach . Take the first BU which is being worked upon out of monolith. This will also give a goos learning curve for the whole team.
You should clearly distinguish sub-domain areas (bounded contexts) from you domain.
Usually (if everything is fine with your architecture) you already have some separate components in your monolith application which responsible for each sub-domain. These components interact with each other in one process
(in monolith application) and you should to think about how to put them into separate processes. Of course you need to produce a lot of refactoring when moving one by one parts of the monolith to microservices.
Always remember that every microservice is responsible for some sub-domain.
I strongly recommend you to learn Domain Driven Design.
Domain-Driven Design: Tackling Complexity in the Heart of Software by Eric Evans
Implementing Domain-Driven Design by Vaughn Vernon
Also learn CQRS pattern
At the beginning you also should decide how your micservices will interact with each other.
There are several options:
Direct calls from one service to another
Send messages through some dispatcher service
which abstracts the client service from the knowledge where the called (destination) services are located.
This approach is similar to how proxy server like NGINX works.
Interact through some messaging bus (middleware), like RabbitMQ
You can combine these options, for example Query requests can be processed through Dispatcher Service, Commands and Events through message bus.
From my experience the biggest problem will be to go away from a single database,
which monolith applications is usually used.
In addition some good practices:
Put each microservice in own repository - this isolates from the ability to directly use the code of one micro service in another.
You also get faster checkouts and builds of each microservice on CI.
Interactions with any service should occur only through its public contracts.
It is necessary to aspire that each microservice has its own database
Example of the sub-domains (bounded contexts) for some Tourism Industry application.
Each bounded context can be serviced by a microservice.
We also started our journey some time back and i started writing a blog series for exactly the same thing: https://dzone.com/articles/how-i-started-my-journey-in-micro-services-and-how
Basically what i understood is to break my problem in diff. microservices, i need a design framework which Domain Driven Design gives(Domain Driven Design Distilled Book by Vaugh Vernon).
Then to implement the design (using CQRS and Event Sourcing and ...) i need a framework which provides all the above support.
I found Lagom good for this.(Eventuate , Spring Microservices are some other choices).
Sample Microservices Domain analysis using Domain Driven Design by Microsoft: https://learn.microsoft.com/en-us/azure/architecture/microservices/domain-analysis
One more analysis is: http://cqrs.nu/tutorial/cs/01-design
After reading on Domain Driven Design i think lagom and above links will help you to build a end to end application. If still any doubts , please raise :)