Monday, December 23, 2019

Microservices -The Ultimate list of Interview Questions Part 4

How to handle versioning of microservices?

There are different ways to handle the versioning of your REST api to allow older consumers to still consume the older endpoints. The ideal practice is that any nonbackward compatible change in a given REST endpoint shall lead to a new versioned endpoint.

Different mechanisms of versioning are:

Add version in the URL itself
Add version in API request header
Most common approach in versioning is the URL versioning itself. A versioned URL looks like the following:

Versioned URL

  https://<host>:<port>/api/v1/...

  https://<host>:<port>/api/v2/...

As an API developer you must ensure that only backward-compatible changes are accommodated in a single version of URL. Consumer-Driven-Tests can help identify potential issues with API upgrades at an early stage.


How to refresh configuration changes on the fly in Spring Cloud environment?

Using config-server, it's possible to refresh the configuration on the fly. The configuration changes will only be picked by Beans that are declared with @RefreshScope annotation.

The following code illustrates the same. The property message is defined in the config-server and changes to this property can be made at runtime without restarting the microservices.

package hello;

import org.springframework.beans.factory.annotation.Value;

import org.springframework.boot.SpringApplication;

import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.context.config.annotation.RefreshScope; import org.springframework.web.bind.annotation.RequestMapping;

import org.springframework.web.bind.annotation.RestController;

 @SpringBootApplication

public class ConfigClientApplication {

public static void main(String[] args) { SpringApplication.run(ConfigClientApplication.class, args);

} }

@RefreshScope 1 @RestController

class MessageRestController {

@Value("${message:Hello World}") private String message;

@RequestMapping("/message") String getMessage() {

return this.message; }}

1 @RefreshScope makes it possible to dynamically reload the configuration for this bean.


How will you ignore certain exceptions in Hystrix fallback execution?

@HystrixCommand annotation provides attribute ignoreExceptions that can be used to provide a list of ignored exceptions.

Code

import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand;

 import org.springframework.beans.factory.annotation.Autowired;

import org.springframework.cloud.client.ServiceInstance;

import org.springframework.cloud.client.loadbalancer.LoadBalancerClient;

import org.springframework.stereotype.Service;

import org.springframework.web.client.RestTemplate;

import java.net.URI;

@Service

public class HystrixService {

@Autowired

private LoadBalancerClient loadBalancer;

 @Autowired

private RestTemplate restTemplate;

@HystrixCommand(fallbackMethod = "reliable", ignoreExceptions = IllegalStateException.class, MissingServletRequestParameterException.class, TypeMismatchException.class)

public String readingList() {

ServiceInstance instance = loadBalancer.choose("product-service"); URI uri = URI.create("http://product-service/product/recommended"); return this.restTemplate.getForObject(uri, String.class);}

public String reliable(Throwable e) { return "Cloud Native Java (O'Reilly)";

In the above example, if the actual method call throws IllegalStateException, MissingServletRequestParameterException or TypeMismatchException then hystrix will not trigger the fallback logic (reliable method), instead the actual exception will be wrapped inside HystrixBadRequestException and re-thrown to the caller. It is taken care by javanica library under the hood.


Is it a good idea to share a common database across multiple microservices?

In a microservices architecture, each microservice shall own its private data which can only be accessed by the outside world through owning service. If we start sharing microservice’s private datastore with other services, then we will violate the principle of Bounded Context.

Practically we have three approaches -

Database server per microservice - Each microservice will have its own database server instance. This approach has the overhead of maintaining database instance and its replication/backup, hence its rarely used in a practical environment.
Schema per microservice - Each microservice owns a private database schema which is not accessible to other services. Its most preferred approach for RDMS database (MySql, Postgres, etc.)
Private Table per microservice - Each microservice owns a set of tables that must only be accessed by that service. It’s a logical separation of data. This approach is mostly used for the hosted database as a service solution (Amazon RDS).


What are best practices for microservices architecture?

Microservices Architecture can become cumbersome & unmanageable if not done properly. There are best practices that help design a resilient & highly scalable system. The most important ones are

Partition correctly

Get to know the domain of your business, that's very very important. Only then you will be able to define the bounded context and partition your microservice correctly based on business capabilities.

DevOps culture

Typically, everything from continuous integration all the way to continuous delivery and deployment should be automated. Otherwise,   a big pain to manage a large fleet of microservices.

Design for stateless operations

We never know where a new instance of a particular microservice will be spun up for scaling out or for handling failure, so maintaining a state inside service instance is a very bad idea.

Design for failures

Failures are inevitable in distributed systems, so we must design our system for handling failures gracefully. failures can be of different types and must be dealt with accordingly, for example -

Failure could be transient due to inherent brittle nature of the network, and the next retry may succeed. Such failures must be protected using retry operations.
Failure may be due to a hung service which can have cascading effects on the calling service. Such failures must be protected using Circuit Breaker Patterns. A fallback mechanism can be used to provide degraded functionality in this case.
A single component may fail and affect the health of the entire system, bulkhead pattern must be used to prevent the entire system from failing.
Design for versioning

We should try to make our services backward compatible, explicit versioning must be used to cater different versions of the RESt endpoints.

Design for asynchronous communication b/w services

Asynchronous communication should be preferred over synchronous communication in inter microservice communication. One of the biggest advantages of using asynchronous messaging is that the service does not block while waiting for a response from another service.

Design for eventual consistency

Eventual consistency is a consistency model used in distributed computing to achieve high availability that informally guarantees that, if no new updates are made to a given data item, eventually all accesses to that item will return the last updated value.

Design for idempotent operations

Since networks are brittle, we should always design our services to accept repeated calls without any side effects. We can add some unique identifier to each request so that service can ignore the duplicate request sent over the network due to network failure/retry logic.

Share as little as possible

In monolithic applications, sharing is considered to be a best practice but that's not the case with Microservices. Sharing results in a violation of Bounded Context Principle, so we shall refrain from creating any single unified shared model that works across microservices. For example, if different services need a common Customer model, then we should create one for each microservice with just the required fields for a given bounded context rather than creating a big model class that is shared in all services. The more dependencies we have between services, the harder it is to isolate the service changes, making it difficult to make a change in a single service without affecting other services. Also, creating a unified model that works in all services brings complexity and ambiguity to the model itself, making it hard for anyone to understand the model.
In a way are want to violate the DRY principle in microservices architecture when it comes to domain models.


How will you implement caching for microservices?

Caching is a technique of performance improvement for getting query results from a service. It helps minimize the calls to network, database, etc. We can use caching at multiple levels in microservices architecture -

Server-Side Caching - Distributed caching software like Redis/MemCache/etc are used to cache the results of business operations. The cache is distributed so all instances of a microservice can see the values from the shared cache. This type of caching is opaque to clients.
Gateway Cache - central API gateway can cache the query results as per business needs and provide improved performance. This way we can achieve caching for multiple services at one place. Distributed caching software like Redis or Memcache can be used in this case.
Client-Side Caching - We can set cache-headers in http response and allow clients to cache the results for a pre-defined time. This will drastically reduce the load on servers since the client will not make repeated calls to the same resource. Servers can inform the clients when information is changed, thereby any changes in the query result can also be handled. E-Tags can be used for client-side load balancing. If the end client is a microservice itself, then Spring Cache support can be used to cache the results locally

What is a good tool for documenting Microservices?

Swagger is a very good open-source tool for documenting   APIs provided by microservices. It provides very easy to use interactive documentation.

By the use of swagger annotation on REST endpoint, api documentation can be auto-generated and exposed over the web interface. An internal and external team can use web interface, to see the list of APIs and their inputs & error codes. They can even invoke the endpoints directly from web interface to get the results.

Swagger UI is a very powerful tool for your microservices consumers to help them understand the set of endpoints provided by a given microservice.


Why Basic Authentication is not suitable in the Microservices Context?

Basic Authentication is natively supported by almost all servers and clients, even Spring security has very good support for it and its configured out of the box. But it is not a good fit for Microservices due to many reasons, including -

We need credentials (username and password) every time we authenticate. This may be fine where all the participants can share the secrets securely, but Users may not be willing to share their credentials with all the applications.
There is no distinction between Users and Client Apps (an application that is making a request). In a realistic environment, we often need to know if a real user is making a request or a client app is making a request (for inter-service communication).
It only covers authentication. what about scopes, Authorizations? Basic Auth does not support adding additional attributes in the authentication headers. There is no concept of Tokens in basic auth.
Performance reasons for BCrypt Matching. Passwords are often stored in the database using one-way hash i.e. Bcrypt, it takes a lot of cpu cycles depending upon the strength (a.k.a. log rounds in BCrypt) to compare the user’s plain password with db saved bcrypt password, so it may not be efficient to match password on every request. The larger the strength parameter the more work will have to be done (exponentially) to hash the passwords. If you set the strength to 12, then in total 212 iterations will be done in Bcrypt Logic. Usually, 4-8 passwords can be matched per second on a T2.Micro instance on Amazon AWS instance. See BCryptPasswordEncoder for more info.
If we use Basic Auth for a mobile application client, then we might have to store user’s credentials on the device to allow remember me feature. This is quite risky as anyone getting access to the device may steal the plain credentials.


How does JWT look like?

There are 3 parts in every JWT claim - Header, Claim and Signature. These 3 parts are separated by a dot. The entire JWT is encoded in Base64 format.

JWT = {header}.{payload}.{signature}

A typical JWT is shown here for reference.

Encoded JSON Web Token
Entire JWT is encoded in Base64 format to make it compatible with HTTP protocol. Encoded JWT looks like the following:



Decoded JSON Web Token

Header

Header contains algorithm information e.g. HS256 and type e.g. JWT

{
"alg": "HS256", "typ": "JWT"

}

Claim

claim part has an expiry, issuer, user_id, scope, roles, client_id etc. It is encoded as a JSON object. You can add custom attributes to the claim. This is the information that you want to exchange with the third party.

{
"uid": "2ce35360-ef8e-4f69-a8d7-b5d1aec78759", "user_name": "user@mail.com",
"scope": ["read"],
"exp": 1520017228,
"authorities": ["ROLE_USER","ROLE_ADMIN"], "jti": "5b42ca29-8b61-4a3a-8502-53c21e85a117", "client_id": "acme-app"

}

Signature

Signature is typically a one way hash of (header + payload), is calculated using HMAC SHA256 algorithm. The secret used for signing the claim should be kept private. Pubic/private key can also be used to encrypt the claim instead of using symmetric cryptography.

HMACSHA256(base64(header) + "." + base64(payload), "secret")



How OAuth2 Works?

OAuth2.0 is a delegation protocol where the Client (Mobile App or web app) does not need to know about the credentials of Resource Owner (end-user).

Oauth2 defines four roles.

Resource Owner - The person or the application that owns the data to be shared. When a resource owner is a person, it is called as an end-user.
Resource Server - The application that holds the protected resources. It is usually a microservice.
Authorization Server - the application that verifies the identity of the resource owner (users/clients). These server issues access tokens after obtaining the authorization.
Client - the application that makes a request to Resource Server on behalf of Resource Owner. It could be a mobile app or a web app (like stackoverflow).


What are the tools and libraries available for testing microservices?

Important Tools and Libraries for testing Spring-based Microservices are -

JUnit

the standard test runners

TestNG
the next generation test runner

Hemcrest
declarative matchers and assertions

Rest-assured
for writing REST Api driven end to end tests

Mockito
for mocking dependencies

Wiremock
for stubbing thirdparty services

Hoverfly
Create API simulation for end-to-end tests.

Spring Test and Spring Boot Test
for writing Spring Integration Tests - includes MockMVC, TestRestTemplate, Webclient like features.

JSONassert
An assertion library for JSON.

Pact
The Pact family of frameworks provide support for Consumer Driven Contracts testing.

Selenium
Selenium automates browsers. Its used for end-to-end automated ui testing.

Gradle
Gradle helps build, automate and deliver software, fastr.

IntelliJ IDEA
IDE for Java Development

Using spring-boot-starter-test
We can just add the below dependency in project’s build.gradle

testCompile('org.springframework.boot:spring-boot-starter-test')

This starter will import two spring boot test modules spring-boot-test & spring-boot-test- autoconfigure as well as Junit, AssertJ, Hamcrest, Mockito, JSONassert, Spring Test, Spring Boot Test and a number of other useful libraries.

What are the use cases for JWT?

There are many useful scenarios for leveraging the power of JWT-
Authentication

Authentication is one of the most common scenarios for using JWT, specifically in microservices architecture (but not limited to it). In microservices, the oauth2 server generates a JWT at the time of login and all subsequent requests can include the JWT AccessToken as the means for authentication. Implementing Single Sign-On by sharing JWT b/w different applications hosted in different domains.
Information Exchange

JWT can be signed, using public/private key pairs, you can be sure that the senders are who they say they are. Hence JWT is a good way of sharing information between two parties. An example use case could be -

Generating Single Click Action Emails e.g. Activate your account, delete this comment, add this item to favorites, Reset your password, etc. All required information for the action can be put into JWT.
Timed sharing of a file download using a JWT link. Timestamp can be part of the claim, so when the server time is past the time-coded in JWT, the link will automatically expire. 

No comments:

Post a Comment