Microservices are a hot topic right now, and it seems that no matter the size of the project, the scope of application is moving towards microservices. It also makes it impossible to talk to someone without going out on twitter.

Just from my own work experience, though, our projects are also microservitized. But to be honest, I don’t see a big difference between microservices and individual projects (just business development) in business development.

Microservices learning to write a few separate demo is not useful, the current framework packaging level is very high, even if the code is still running in the fog. Learning about microservices is more about figuring out what each component of a microservice is. Why are there these components? What happens if you don’t? How is the function implemented?

To understand these basically can be said to have a general grasp of micro services.

Why do you need a registry

In microservices, the first question is how do different services communicate with each other? If different services need to communicate with each other in a singleton service, typically the service exposes the interface and then other services make requests over HTTP, it is obvious that this can also be done in a microservice.

But the problem here is that in a singleton service we need to request a target that we write down in the URL of the request, because there aren’t many services that can do that. In microservices, is it appropriate to maintain a list of services manually when there are a large number of services? How do I notify other services when the service scales horizontally? How to log out in a timely manner after a service outage.

Registry functions

The existence of a registry is to better and more convenient management of each service in the application, is the link between each distributed node. The registry provides the following core functions:

  • Service registration and discovery: Service nodes are dynamically added or removed, and service consumers are dynamically notified of the addition or deletion of service nodes without the need for configuration updates by consumers.
  • Service configuration: Dynamically modify the service configuration and push it to the service provider and service consumer without restarting the service.
  • Health check and service Removal: Proactively check the health of services and remove services that are down from the service list.

Implementation of the registry

The main function of a registry is to store specific information about a service, which is then read by the service consumer. In terms of the overall process, it looks like this:

In addition to registering services into the registry, there is actually a de-registration of services

At present, the implementation of the registry is divided into two modes, respectively for the client (in-application registration) and server (out-of-application registration).

Client registry

Currently, the registry of client implementations is representative of Eureka. Although Eureka 2.0 was previously announced as closed source, it does not seem to have much impact so far. Consider Ali’s open source NacOS for a follow-up.

Eureka is a Java language based cost and service discovery and registration component, including server and client parts.

The server is used to store service information and periodically check services. The client is used to register the service information with the server and pull the service information from the server.

The figure above is the architecture diagram of Eureka given by eureka official.

It is clear from the figure that the eureka server is also deployed as a service, while the client is connected to the application through the SDK. The client registers and updates the service to the server. At the same time, the client obtains the service information from the server. Different services make remote calls based on the obtained service information.

In order to meet the high availability of services, multiple Eureka server services are moved and different Eureka servers register with each other to achieve high availability of services.

Server registry

Eureka is the SDK of the server and client provided by the registry. The business side can register and discover services by introducing the SDK provided by the registry. On the other hand, the registry on the server side implements the service registration function through an out-of-application component. Currently, most server-side registry components are used, such as Zookeeper and Consul. The following uses ZooKeeper as an example.

Zookeeper is a subproject of Apache Hadoop. It is a tree directory service that supports change push. It is suitable for Dubbo service registry with high industrial intensity and can be used in production environment.

The basic rationale for zooKeeper as a registry is shown on dubbo’s website.

Using dubbo as the root directory, create a fully qualified name directory for the service interface. Under this directory, create a providers directory that stores the service provider interface information, a consumers directory that stores the service consumer interface information, and a Configurators directory that stores the service configuration information.

When a service provider starts, it creates a service message under providers that can be accessed by consumers. Zk provides the Watcher mechanism to register a Watcher in the providers directory of the service, so that when the service is expanded or down, it can be consumed immediately by consumers.

Problems with the registry

In microservices, the registry as a place to store all the service centers is necessarily not stand-alone. Because if the registry is unavailable, service providers can’t expose their services and consumers can’t get the addresses of the services they need to invoke, and the entire application will crash. The first thing you need to ensure is that the registry is highly available.

Consistency versus usability choices

In terms of high availability, there is no getting around CAP theory. CAP simply means that we need to make a choice between the availability of services and the consistency of services. Do you sacrifice availability for consistency or do you sacrifice availability for consistency?

CP type registry

Zookeeper is a typical CP type of high availability component. Zookeeper is implemented using paxOS algorithm. Zookeeper cluster has a node as the leader. If the leader node fails, ZK will elect the leader through THE ZAB protocol, but ZK cannot provide services during the election.

In addition, when zK cluster is split due to network partition, more than half of ZAB nodes are required to participate, so some or all ZK nodes may not be able to provide services. However, ZK can ensure that all available nodes have the same data. Even if a node crashes, it will be consistent with other nodes after recovery. This is the strong consistency guaranteed by ZK at the expense of availability.

AP type registry

Registries like Eureka are of the AP type. Eureka achieves high availability by starting multiple Eureka Server services. Each Eureka Server is both a provider and a consumer, and each Eureka Server registers itself with other Eureka servers as a service. In this way, each Eureka server is a replica of other servers. In Eureka, each node is equal and there is no leader or flower.

When a server node in the cluster goes down, requests can be directly transferred to other nodes. Even if network partitions occur, the nodes within each partition are communicating and providing services normally, ensuring the availability of the registry in the event of a server node failure, even though the different partitions cannot communicate with each other.

The problem is that because different partitions cannot communicate, data on one node may differ from that on another, sacrificing consistency for system availability.

Registry selection

The first thing we should make clear is that the presence or absence of a registry should not directly affect service invocation. When calls between services are made directly over protocols such as HTTP or RPC, the registry simply provides a call address. The availability of services cannot be determined directly by registry node information.

Even if there is a problem with the registry, as long as the service itself is not down, the service should theoretically be available. So it was clear to us that an AP type registry was superior to a CP type registry.

Of course, in the actual implementation process, many frameworks will try their best to fix these problems. For example, Eureka will periodically synchronize the data of each node to achieve data consistency as much as possible. Dubbo’s ZK registry implementation also caches service information locally and does not rely solely on ZK information to decide whether to exclude services, etc. Therefore, the actual choice still needs to be determined according to its own reality and whether there are other requirements.

Mixed-language development

In microservices, each service is an independent whole. Different modules in a single application are generally required to be implemented in the same language between services. Nowadays, the development languages of major companies are diversified, and the development languages of different business lines may be different. Mixed languages are a problem we have to face, so the problem of service invocation between multiple languages appears.

For in-app registries like Eureka. The registration and discovery of services are dependent on SDK, so if you want to use Eureak, you need to implement the SDK of the corresponding language. At present, many languages are provided, such as Python and Node.js. The current SpringClould Sidecar component also makes it possible to incorporate other languages into the SpringClould architecture.

For out-of-application registries, these middleware typically provide client-side operations. The language itself then implements functions such as service registration and governance. Many languages today actually have open source projects.

Node survival judgment

We have already talked about registries that guarantee strong consistency of type CP and registries that guarantee availability of type AP. The most important thing for a registry is to manage node information. The service information must be recorded when the service is started and removed when the service is faulty.

However, the problem is how to implement this operation, the current registry implementation is based on heartbeat detection to determine the health of the service. In normal cases, if the service is down and the heartbeat detection fails to communicate with the service, the service is removed from the registry without any problem. However, if there is a network problem, such as network jitter or network partition, the heartbeat detection will not be able to communicate normally, but if the service is removed directly because of this, it will be a problem, because the node is actually able to provide service normally. There needs to be a mechanism to ensure that this process does not rudely remove nodes from the registry.

Client judgment

Many framework implementations currently cache node information for microservices locally. It is actually the individual services that use these nodes, and it is these nodes that have the most say in whether the services can be used properly.

The client caches the node information. When the server determines that the service has a problem and sends an update request, the client does not delete the node immediately, but can make a mark first. After subsequent business requests come in, the service is still determined to be available and removed only if the outgoing request cannot receive a normal response. It is up to the client to decide whether the node is available, but this is supported by a fault tolerance mechanism.

Dynamically set heartbeat detection

In the case of frequent network jitter, node information in the registry changes rapidly and a large amount of information is sent to clients. In the case of heavy services, a large amount of bandwidth may be occupied and normal requests cannot be processed.

Therefore, a dynamic mechanism is required to adjust the heartbeat detection frequency of the registry. When network jitter is detected frequently, the heartbeat detection frequency is adjusted according to the network condition, for example, 1/2,10/1. The heartbeat detection returns to normal when the network is normal.

summary

Registry is a key component of microservices architecture, which ensures the normal invocation between services and the horizontal expansion of services. Business scenarios need to be considered when selecting a registry.