Microservices

Subdecks (1)

Cards (65)

  • MicroServices
    • Microservices divide a large program into a number of smaller, independent services, as shown on the right, unlike a monolithic application, which implements all features in a single code base with a database for all data, as shown on the left.
    • Microservices are the current industry trend; however, it’s important to ensure that there is a good reason to select this architecture. The primary reason is to enable teams to work independently and deliver through to production at their own cadence. This supports scaling the organization: adding more teams increases speed. There is also the additional benefit of being able to scale the microservices independently based on their requirements.
  • Architecturally, an application designed as a monolith or around microservices should be composed of modular components with clearly defined boundaries. With a monolith, all the components are packaged at deployment time and deployed together. With microservices, the individual components are deployable. Google Cloud provides several compute services that facilitate deploying microservices. These include App Engine, Cloud Run, GKE, and Cloud Functions.
  • Microservices:
    • To achieve independence on services, each service should have its own datastore. This lets the best datastore solution for that service be selected and also keeps the services independent. We do not want to introduce coupling between services through a datastore.
  • Microservice Pros and Cons
  • A properly designed microservice architecture can help achieve the following goals:
    ● Define strong contracts between the various microservices
    ● Allow for independent deployment cycles, including rollback
    ● Facilitate concurrent, A/B release testing on subsystems
    ● Minimize test automation and quality assurance overhead
    ● Improve clarity of logging and monitoring
    ● Provide fine-grained cost accounting
    ● Increase overall application scalability and reliability through scaling smaller units
  • Microservice Challenges:
    • It can be difficult to define clear boundaries between services to support independent development and deployment
    • Increased complexity of infrastructure, with distributed services having more points of failure
    • The increased latency introduced by network services and the need to build in resilience to handle possible failures and delays
    • Due to the networking involved, there is a need to provide security for service-to-service communication, which increases the complexity of infrastructure
  • Microservice Challenges
    • Strong requirement to manage and version service interfaces. With independently deployable services, the need to maintain backward compatibility increases.
  • Microservices:
  • Decomposing Apps:
    • The first step is to decompose the application by feature or functional groupings to minimize dependencies. Consider, for example, an online retail application. Logical functional groupings could be product management, reviews, accounts, and orders. These groupings then form mini applications which expose an API. Each of these mini applications will be implemented by potentially multiple microservices internally. Internally, these microservices are then organized by architectural layer, and each should be independently deployable and scalable.
  • Stateful vs Stateless:
  • Microservices:
    • When you’re designing microservices, services that do not maintain state but obtain their state from the environment or stateless services are easier to manage. That is, they are easy to scale, to administer, and to migrate to new versions because of their lack of state.
    • Understand the implications of having stateful services on the architecture of the system. These include introducing significant challenges in the ability to scale and upgrade the services.
  • Microservice State Best Practices:
  • Microservice State Best Practices:
    • In memory, shared state has implications that impact and negate many of the benefits of a microservice architecture. The auto scaling potential of individual microservices is hindered because subsequent client requests have to be sent to the same server that the initial request was made to. In addition, this requires configuration of the load balancers to use sticky sessions. which in Google Cloud is referred to as session affinity.
  • A recognized best practice for designing stateful services is to use backend storage services that are shared by frontend stateless services. For example, for persistent state, the Google Cloud-managed data services such as Firestore or Cloud SQL may be suitable. Then to improve the speed of data access, the data can be cached. Memorystore for Redis, which is a highly available Redis-based service, is ideal for this.
    • A load balancer distributes the load between the backend and frontend services. This allows the backend to scale if it needs to keep up with the demand from the frontend. In addition, the stateful servers/services are also isolated. The stateful services can make use of persistent storage
    • This layout allows a large part of the application to make use of the scalability and fault tolerance of Google Cloud services as stateless services. By isolation of the stateful servers and services, the challenges of scaling and upgrading are limited to a subset of the overall set of services.
  • Designing Microservices: Here’s an example diagram for microservices for the website and the mobile phone application of an online banking service.
    • One of the most important aspects of microservices-based applications is the ability to deploy microservices completely independent of one another. To achieve this independence, each microservice must provide a versioned, well-defined contract to its clients, which are other microservices or applications
    • Each service must not break these versioned contracts until it’s known that no other microservice relies on a particular, versioned contract. Remember that other microservices may need to roll back to a previous code version that requires a previous contract, so it’s important to account for this fact in your deprecation and turn-down policies.
  • A good microservice design is loosely coupled:
    • At the lower level of detail, services communicate using HTTPS with text-based payloads, for example JSON or XML, and use the HTTP verbs such as GET and POST to provide meaning for the actions requested. Clients should just need to know the minimal details to use the service: the URI, the request, and the response message formats.
  • REST:
    • REST architecture supports loose coupling. REST stands for Representational State Transfer, and is protocol independent. HTTP is the most common protocol, but gRPC is also widely used.
    • REST supports loose coupling but still requires strong engineering practices to maintain that loose coupling. A starting point is to have a strong contract. HTTP-based implementations can use a standard like OpenAPI, and gRPC provides protocol buffers
  • REST architecture supports loose coupling:
    • To help maintain loose coupling, it is vital to maintain backward compatibility of the contract and to design an API around a domain and not particular use cases or clients. If the latter is the case, each new use case or application will require another special-purpose REST API, regardless of protocol.
    • While request-response processing is the typical use case, streaming may also be required and can influence the choice of protocol. gRPC supports streaming, for example.
  • RESTful services communicate over the web using HTTP(S)
    • Resources are identified by URIs or endpoints, and responses to requests return an immutable representation of the resource information.
    • REST applications should provide consistent, uniform interfaces and can link to additional resources. Hypermedia as the Engine of Application State (or HATEOS) is a component of REST that allows the client to require little prior knowledge of a service because links to additional resources are provided as part of responses.
  • RESTful services communicate over the web using HTTP(S)
    • It is important that API design is part of the development process. Ideally, a set of API design rules is in place that helps the REST APIs provide a uniform interface; for example, each service reports errors consistently, the structure of the URLs is consistent, and the use of paging is consistent.
    • Also, consider caching for performance and resource optimization for immutable resources.
  • Resources & Representations:
    • In REST, a client and server exchange representations of a resource. A resource is an abstract notion of information. The representation of a resource is a copy of the resource information. For example, a resource could represent a dog. The representation of a resource is the actual data for a particular dog; for example, Noir who is a schnoodle, or Bree who is a mutt. Two different representations of a resource.
  • Resources and representations
    • The URI provides access to a resource. Making a request for that resource returns a representation of that resource, usually in JSON format. The resources requested can be single items or a collection of items. For performance reasons, returning collections of items instead of individual items can be beneficial. These types of operations are often referred to as batch APIs
  • Passing representations between services is done using standard text-based formats
    • Representations of a resource between client and service are usually achieved using text-based standard formats. JSON is the norm for text-based formats, although XML can be used. For public-facing or external-facing APIs, JSON is the standard. For internal services, gRPC may be used, in particular if performance is key
  • Clients access services using HTTP requests
    • A client accessing HTTP services forms an HTTP request. HTTP requests are built in three parts: the request line, header variables, and request body.
    • The request line has the HTTP verb—GET, POST, PUT etc.—the requested URI, and the protocol version.
  • Clients access services using HTTP requests
    • The header variables contain key-value pairs. Some of these are standard, such as User-Agent, which helps the receiver identify the requesting software agent. Metadata about the message format or preferred message formats is also included here for HTTPS-based REST services. You can add custom headers here.
    • The request body contains data to be sent to the server and is only relevant for HTTP commands that send data, such as POST and PUT.
  • Here we see two examples of HTTP client text-based messages. The first example shows an HTTP GET request to the URL / using HTTP version 1.1 There is one request header variable named Host with the value pets.drehnstrom.com The second example shows an HTTP POST request to the URL /add using HTTP version 1.1 There are three request header variables: Host: Content-Type: set to json Content-Length: set to 35 bytes There is the request body which has the JSON document: {"name":"Noir","breed":"Schnoodle"}. This is the representation of the pet being added.
  • As part of a request, the HTTP verb tells the server the action to be performed on a resource. HTTP as a protocol provides nine verbs, but usually only the four listed here are used in REST.
    • GET is used to retrieve resources.
    • POST is used to request the creation of a new resource. The service then creates the resource and usually returns the unique ID generated for the new resource to the client.
  • As part of a request, the HTTP verb tells the server the action to be performed on a resource.HTTP as a protocol provides nine verbs, but usually only the four listed here are used in REST.
    • PUT is used to create a new resource or make a change to an existing resource. PUT requests should be idempotent, which means that no matter how many times the request is made by the client to a service, the effects on the resource are always exactly the same.
    • Finally, a DELETE request is used to remove a resource.
    • HTTP services return responses in a standard format defined by HTTP. These HTTP responses are built in three parts: the response line, header variables, and response body
  • The response line has the HTTP version and a response code. The response codes are broken on boundaries around the 100s.
    • The 200 range means ok. For example, 200 is OK, 201 means a resource has been created.
    • The 400 range means the client request is in error. For example, 403 means “forbidden due to requestor not having permission.” 404 means “requested resource not found.”
    • The 500 range means the server encountered an error and cannot process the request. For example, 500 is “internal server error.” 503 is “not available,” usually because the server is overloaded.
  • Services return HTTP responses
    • The response header is a set of key-value pairs, such as Content-Type, which indicates to the receiver the type of content the response body contains. The response body has the resource representation requested in the format specified in the Content-Type header and can be JSON, XML, HTML, etc.
  • All services need URIs (Uniform Resource Identifiers)
    • The guidelines listed here focus on achieving consistency on the API. Singular nouns should be used for individual resources, and plural nouns for collections or sets. For an example, consider the following URI /pet. Then: A GET request for the URI /pet/1 should fetch a pet with id 1, whereas GET /pets fetches all the pets. Do not use URIs such as GET /getPets. The URI should refer to the resource, not the action on the resource—that is the role of the verb. Remember that URIs are case-insensitive and that they include version information.
    • It is good practice to diagram services. This diagram shows that there is a service that provides access to a resource known as Pets. The representation of the resource is the Pet. When a request is made for the resource via the service, one or more representations of a Pet are returned.
  • It’s important to design consistent APIs for services
    • Google provides an API design guide with recommendations on items such as names, error handling documentation, versioning, and compatibility. This guide and the API stylebook are linked in the slides.
  • It is important to design consistent APIs for services.
    • For examples of best practices, it is useful to examine the Google Cloud APIs. Each Google Cloud service exposes a REST API. Functions are defined in the form service.collection.verb. The service represents the service endpoint; e.g., for the Compute Engine API, the service endpoint is https://compute.googleapis.com. Collections include instances, instanceGroups and instanceTemplates. The verbs then include LIST, GET, and INSERT, for example