IPC (Interprocess Communication)
- REST w/ JSON - the fashionable choice
- Messaging
Considerations
- transaction management
- author favors lolsely coupled services that communicate over asynchronous messaging
- synchronous request/response: HTTP REST or gRPC
- asynchonous, message-based AMQP or STOMP
- text-based JSON or XML or binary format Avro / Protocol Buffers
Styles
- one-to-one
- one-to-many
- publish/subscribe
- publish / async responses
- synchronous (request-response tends to be tightly coupled, often blocking)
- asynchronous
- one-way notifications - no reply expected or sent
Defining APIs
- A service's API is a contract between the service and its clients
- Regardless of IPC mechanism, preciely define API using some kind of interface definition language (IDL)
- API-first approach? Q: What would that look like for us? Could there be a software design phase? Does that work with Agile?
- Robustness principle: “Be conservative in what you do, be liberal in what you accept from others.”
- APIs evolve. In monoliths, it's somewhat easy to change if typed, rolling upgrades, no downtime.
- Semantic versioning
3.2 Communicating using the synchronous remote procedure invocation pattern RPI: Remote procedure invocation
- business logic in client invokes proxy interface, which makes a request to the service
- proxy interface encapsulates the underlying communication protocol
Using REST
- different levels of maturity for REST
- Level 0 -- HTTP POST to sole URL endpoint with action to perform, target, and params
- Level 1 -- resources, POST request with action to perform and params
- Level 2 -- HTTP verbs to perform actions ono resources
- Level 3 - HATEOAS - representation of resource returned by GET request contains links for performing actions, no hardcoded URLs in client code
- familiar, but synchronous.
- hard to query multiple resources
- hard to map operations to HTTP verbs
Using gRPC
- cross language clients and servers
- binary message-based protocol
- uses protocol buffers as message format
- bidirectional streaming enables both RPI and messaging styles of communication
- harder for JS clients to consume API
- synchronous communication mechanism, partial failure problem
Circut breaker pattern
- RPI proxy that immediately rejects invocations for a timeout period after the number of consecutive failures exceeds a specified threshold
Using service discovery
- Your code needs to know the network location (IP address and port) of server instance
3.3 Communicating using the Asynchronous messaging pattern
- messaging-based application typically uses a message broker, which acts as an intermediary between the services
- alternative is brokerless architecture
- sharding related messages so that they go to the same consumer instance (Kafka, Kinesis)
- Idempotent message handlers OR discarding duplicates
- transactional messaging
- transational outbox pattern: using the database table as a message queue
- polling publisher: publish messages by polling the outbox in the database
- publishing events by tailing the transaction log
- eliminating synchronous interaction
- ex) define services that only have asynchronous APIs
- exchange messages with other services and eventually send a reply message to client
- ACID (Atomicity, Consistency, Isolation, Durability)
- saga - message-driven sequence of local transactions to maintain data consistency
- sagas are ACD, they lack isolation
- "Many applications use a lower transaction isolation level in order to improve performance" -- many apps only give the illustion of isolation
- traditional approach is to use distributed transactions, they are not widely supported and they are a form of synchronous IPC
- each additional service involved in a distributed transaction further reduces availability
using the saga pattern to maintain data consistency
- pattern: saga. maintain data consistency across services using a series of local transactions that are coordinated using asynchronous messaging
- completion of local transaction triggers execution of the next one
- a saga must be rolled back using compensating transactions
- a service publishes a message when a local transaction completes.
- if recipient is unavailable, broker buffers message until it can be delivered
sagas use compensating transactions to roll back changes
- saga executes the compensation transactions in reverse order of the forward transactions
- choreography - distribute the decision making and sequencing among the participants. they primarily communicate by exchanging events
- orchestration - centralize a saga's coordination logic in a saga orchestrator class. sends command messages to saga participants by telling them which operations to perform
choreography
- database must update and publish event atomically
- saga participants must use transactional messaging
- each saga participant must be able to map each event that it receives to its own data
- saga must publish events containing a correlation id, get an id to look up the right resource
- simplicity & loose coupling, but more difficult to understand, cyclic dependencies, risk of tight coupling
orchestration
- orchestrator class whose sole responsibility is to tell saga participants what to do
- command async reply-style interaction
- model saga orchestrator as state machine, set of states and set of transitions between states that are triggered by events
- simplier dependencies (no cyclic dependencies), less coupling, separation of concerns, but risk of centralizing too much business logic in orchestrator
anomalies caused by lack of isolation
- lost updates
- dirty reads
- fuzzy / nonrepeatable reads
countermeasures
-
compensatable transactions - can be rolled back
-
pivot transaction - go/no-go point in a saga
-
retriable transactions - follow the pivot transaction and are guaranteed to succeed
-
semantic lock - flag in any record that it updates or creates, indicates record isn't committed and could change (ex. order state field)
-
commutative updates: design the update operations to be commutative, executed in any order
-
pessimistic view - reorder the steps to minimize business risk due to dirty read
-
reread value - prevents lost updates, rereads a record before updating it and verify unchanged
-
version file - record operations as they arrive and execute them in the correct order
-
by value - select concurrency mechanisms based on business risk