Network Security Internet Technology Development Database Servers Mobile Phone Android Software Apple Software Computer Software News IT Information

In addition to Weibo, there is also WeChat

Please pay attention

WeChat public account

Shulou

How to think about RESTful API, GraphQL and RPC API

2025-02-24 Update From: SLTechnology News&Howtos shulou NAV: SLTechnology News&Howtos > Servers >

Share

Shulou(Shulou.com)05/31 Report--

This article will explain in detail how to think about RESTful API, GraphQL and RPC API. The content of the article is of high quality, so the editor will share it with you for reference. I hope you will have a certain understanding of the relevant knowledge after reading this article.

Today, I pick up my pen and write an article about my thinking about API design.

Now, let's discuss the design of API. I will throw out a few points, which are welcome to discuss.

First, the well-defined specification has been more than half successful.

In general, norms are established standards, and if everyone adheres to this set of standards, then the cost of natural communication will be greatly reduced. For example, everyone wants to learn from Ali's specification and define several domain models in their own business: VO, BO, DO, DTO. Among them, DO (Data Object) corresponds to the database table structure one by one and transmits data source objects upward through the DAO layer. DTO (Data Transfer Object) is a remote invocation object, which is the domain model provided by RPC services. For BO (Business Object), it is an object that the business logic layer encapsulates business logic, and in general, it is a composite object that aggregates multiple data sources. Then, VO (View Object) is usually the object transferred by the request processing layer, and it is often a JSON object after it is transformed through the Spring framework.

In fact, if the domain models of DO, BO, DTO and VO are not clearly divided in Ali's complex business, its internal code is easily confused, and the internal RPC adds a manager layer on the basis of the service layer, thus realizing the internal specification unification. However, if it's just a separate domain without too many external dependencies, don't design it at all unless you expect it to become large and complex. In this regard, it is particularly important to adjust measures to local conditions in the design process.

Another example of a specification is RESTful API. In the REST architecture style, each URI represents a resource. Therefore, URI is the unique resource locator for the address of each resource. The so-called resource is actually an information entity, which can be a piece of text, a file, a picture, a song, or a service on the server. RESTful API provides for the operation of server resources by means of GET, POST, PUT, PATCH, DELETE and so on.

[GET] / users # query user information list [GET] / users/1001 # View a user information [POST] / users # New user information [PUT] / users/1001 # Update user information (all fields) [PATCH] / users/1001 # Update user information (some fields) [DELETE] / users/1001 # Delete user information

In fact, the implementation of RESTful API is divided into four levels. The Web API service at the first tier (Level 0) simply uses HTTP as the transport. The Web API service at the second level (Level 1) introduces the concept of resources. Each resource has a corresponding identifier and representation. The Web API service at the third level (Level 2) uses different HTTP methods to perform different operations and uses HTTP status codes to represent different results. The Web API service at the fourth level (Level 3) uses HATEOAS. Link information is included in the representation of the resource. The client can discover the actions that can be performed based on the link. In general, pseudo-RESTful API is designed based on the first and second levels. For example, we use various verbs in Web API, such as get_menu and save_menu, while the real RESTful API needs to be above the third level. If we follow this set of specifications, we are likely to design an easy-to-understand API.

Note that we have succeeded more than half of the well-defined specifications. If this set of norms is the industry standard, then we can practice boldly, don't worry that others will not use it, just leave the industry standard to him to study hard. For example, Spring already plays an important role in the ecology of Java, and it doesn't make sense for a newcomer who doesn't understand Spring. However, very often, because of business limitations and the company's technology, we may use pseudo-RESTful API based on the first and second levels of design, but it is not necessarily backward and bad, as long as norms are formed within the team to reduce everyone's learning costs. Many times, when we try to change the team's habits to learn a new specification, the benefit (input-output ratio) is very small, and the loss outweighs the gain.

To sum up, the purpose of a well-defined specification is to reduce learning costs and make API as easy to understand as possible. Of course, there are other ways to design an API that is easy to understand, such as the name of the API we define is easy to understand, and the implementation of API is as general as possible.

Second, discuss the compatibility of API interface.

API interfaces are constantly evolving. Therefore, we need to adapt to the change to a certain extent. In RESTful API, the API interface should be compatible with previous versions as much as possible. However, in the actual business development scenario, with the continuous iteration of business requirements, the existing API interface may not be able to support the adaptation of the old version. At this time, the mandatory upgrade of the server-side API interface will lead to the failure of the old functions of the client. In fact, the Web end is deployed on the server, so it can be easily upgraded in order to adapt to the new API interface of the server. However, other clients such as Android, IOS, PC and other clients are running on the user's machine, so it is difficult for the current product to adapt to the new API interface of the server, resulting in functional failure. In this case, users must upgrade the product to the latest version in order to use it normally. In order to solve the incompatibility of this version, a practical practice in designing RESTful API is to use version numbers. In general, we keep the version number in url and are compatible with multiple versions at the same time.

[GET] / v1/users/ {user_id} / / API interface for querying user list of version v1 [GET] / v2/users/ {user_id} / / API interface for querying user list of version v2

Now, without changing the API interface for querying user list in version v1, we can add the API interface to query user list in version v2 to meet the new business requirements. At this time, the new function of the client product will request the API interface address of the new server. Although the server is compatible with multiple versions at the same time, it is a burden for the server to maintain too many versions at the same time, because the server has to maintain multiple sets of code. In this case, the common practice is not to maintain all compatible versions, but to maintain only the latest compatible versions, such as the latest three compatible versions. After a period of time, when the vast majority of users upgrade to newer versions, discard some of the less used server versions of the API interface, and require users who use very old versions of the product to force upgrades. Note that the "API interface for querying user lists without changing version v1" mainly means that it does not seem to have changed to the caller on the client side. In fact, if the business changes too much, the server developer needs to use the adapter pattern for the old version of the API interface to adapt the request to the new API interface.

Interestingly, GraphQL offers different ideas. In order to solve the problem of service API interface explosion, GraphQL aggregates multiple HTTP requests into one request, and proposes to expose only a single service API interface, and multiple queries can be made in a single request. GraphQL defines the API interface, so we can call it more flexibly at the front end. For example, we can select and load fields that need to be rendered according to different businesses. Therefore, the full number of fields provided by the server can be obtained by the front end on demand. GraphQL can add new functionality by adding new types and new fields based on those types without causing compatibility problems.

In addition, in the process of using RPC API, we need to pay special attention to compatibility issues. Second-party libraries cannot rely on parent. In addition, local developers can use SNAPSHOT, but the online environment forbids it to avoid changes that lead to version incompatibility. We need to define a version number for each interface to ensure that the version can be upgraded in the event of subsequent incompatibility. For example, Dubbo recommends that the third version number usually indicates a compatible upgrade, and the service version needs to be changed only if it is not compatible.

For the specification example, we can look at k8s and github, where K8s uses RESTful API and the github part uses GraphQL.

Https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.10/

Https://developer.github.com/v4/

Third, provide a clear thinking model

My understanding of the so-called thinking model is to aim at the abstract model of the problem domain, to have a unified understanding of the function of the domain model, to construct the realistic mapping of a problem, and to divide the boundary of the model, and one of the values of the domain model is to unify the thought and define the boundary. Assuming that we do not have a clear thinking model, then there is no unified understanding of API, then there are likely to be practical problems in the picture below.

Fourth, shield the business implementation in an abstract way

I think a good API interface is abstract, so you need to shield the business implementation as much as possible. So, the question is, how do we understand abstraction? In this regard, we can think about the design of java.sql.Driver. Here, java.sql.Driver is a specification interface, and com.mysql.jdbc.Driver is the mysql-connector-java-xxx.jar implementation interface to this specification. Then the cost of switching to Oracle is very low.

Under normal circumstances, we will provide services through API. Here, the logic of the interface that API provides the service is fixed, in other words, it is universal. However, when we encounter a scenario with similar business logic, where the core backbone logic is the same and the details of the implementation are slightly different, where do we go? In many cases, we choose to provide multiple API interfaces for different business parties to use. In fact, we can achieve more elegance through the SPI extension point. What is SPI? The full name of SPI is Serivce Provider Interface, that is, service provider interface. It is a dynamic discovery mechanism, which can dynamically discover the implementation class of an extension point in the process of program execution. Therefore, when API is called, the specific implementation method of SPI is dynamically loaded and called.

At this point, you are not associated with the template method model. The core idea of the template method pattern is to define the skeleton and transfer the implementation, in other words, it delays the implementation of some steps to subclasses by defining the framework of a process. In fact, in the landing process of micro-services, this idea also provides us with a very good theoretical basis.

Now, let's look at a case: unshipped goods in an e-commerce business scenario are refunded only. This situation is very common in e-commerce business, where users may apply for a refund for a variety of reasons after issuing an order for payment. At this point, because the return is not involved, the user only needs to apply for a refund and fill in the reason for the refund, and then let the seller review the refund. Then, since the refund reasons may vary from platform to platform, we can consider implementing it through the SPI extension point.

In addition, we often use factory methods + policy patterns to shield external complexity. For example, if we expose an API interface getTask (int operation), we can create instances through the factory method and define different implementations through the policy method.

@ Componentpublic class TaskManager {private static final Logger logger = LoggerFactory.getLogger (TaskManager.class); private static TaskManager instance; public MapInteger, ITask > taskMap = new HashMap (); public static TaskManager getInstance () {return instance;} public ITask getTask (int operation) {return taskMap.get (operation) } / * initialization process * / @ PostConstruct private void init () {logger.info ("init task manager"); instance = new TaskManager (); / / single chat message task instance.taskMap.put (EventEnum.CHAT_REQ.getValue (), new ChatTask ()) / / Group chat message task instance.taskMap.put (EventEnum.GROUP_CHAT_REQ.getValue (), new GroupChatTask ()); / / heartbeat task instance.taskMap.put (EventEnum.HEART_BEAT_REQ.getValue (), new HeatBeatTask ());}}

Another kind of shielding internal complexity design is the appearance interface, which encapsulates and integrates the interfaces of multiple services and provides a simple calling interface for the client to use. The advantage of this design is that the client no longer needs to know so much about the interface of the service, just call the facade interface. However, the disadvantages are also obvious, that is, it increases the business complexity of the server, the performance of the interface is not high, and the reusability is not high. Therefore, according to local conditions, as far as possible to ensure a single responsibility, and "Lego" assembly on the client side. If there is a SEO-optimized product that needs to be included by a search engine like Baidu, you can generate HTML through server rendering on the first screen so that it can be included by the search engine. If it is not the first screen, you can call the server RESTful API API through the client to render the page.

In addition, with the popularity of microservices, we have more and more services, and many smaller services have more cross-service calls. As a result, the micro-service architecture makes this problem more common. To solve this problem, we can consider introducing a "aggregation service", which is a composite service that can combine the data of multiple micro-services. The advantage of this design is that some information is integrated through an "aggregation service" and then returned to the caller. Note that the aggregation service can also have its own cache and database. In fact, the idea of aggregation services is ubiquitous, such as the Serverless architecture. In practice, we can use AWS Lambda as the computing engine behind Serverless services, and AWS Lambda is a function as a service (Function-as-a-Servcie,FaaS) computing service, we directly write functions that run on the cloud. Then, this function can assemble existing capabilities for service aggregation.

Of course, there are many good designs, and I will continue to supplement and explore them in the official account one after another.

Fifth, consider the performance behind

We need to consider that the various combinations of parameter fields lead to database performance problems. Sometimes, we may expose too many fields for external combination, resulting in a full table scan without a corresponding index in the database. In fact, this situation is particularly common in query scenarios. Therefore, we can only provide a combination of fields with an index for external calls, or in the following case, the caller is required to enter taskId and caseId to ensure the reasonable use of the index in our database and further ensure the service performance of the service provider.

ResultVoid > agree (Long taskId, Long caseId, Configger configger)

At the same time, asynchronous capability should be considered for API such as report operation, batch operation, cold data query and so on.

In addition, although GraphQL solves the problem of aggregating multiple HTTP requests into a single request, schema will recursively get all the data layer by layer. For example, the total number of paging queries, the query that could have been done at one time has evolved into N + 1 queries to the database. In addition, improper writing can lead to poor performance problems, so we need to pay special attention to it in the design process.

VI. Abnormal response and error mechanism

There has been too much debate in the industry about whether RPC API throws an exception or an error code. Alibaba Java Development Manual recommends that cross-application RPC calls give priority to the use of isSuccess () method, "error code" and "error short message". The reasons why the RPC method returns using the Result method: 1) if the caller does not catch it, a run-time error will be generated if the exception return method is used. 2) if you do not add stack information, just new custom exceptions and add your own understanding of error message, it will not be of much help to the caller to solve the problem. If stack information is added, the performance loss of data serialization and transmission is also a problem in the case of frequent call errors. Of course, I also support the practical advocates of this argument.

Public ResultXxxDTO > getXxx (String param) {try {/ /... Return Result.create (xxxDTO);} catch (BizException e) {log.error ("...", e); return Result.createErrorResult (e.getErrorCode (), e.getErrorInfo (), true);}}

During the Web API design process, we use ControllerAdvice to unify the packaging of error messages. In the complex chain invocation of micro-service, it is more difficult to track and locate the problem than the single architecture. Therefore, special attention should be paid to the design. A better solution is to adopt the global abnormal structure response information when the RESTful API interface has a non-2xx HTTP error code response. The code field is used to indicate the error code of a certain type of error, and the "{biz_name} /" prefix should be added to the microservice to locate which business system the error occurred on. Let's take a look at a case where an error occurs when an interface in "user Center" does not have permission to access resources, our business system can respond to "UC/AUTH_DENIED" and obtain the details of the error in the log system through the request_id field of the automatically generated UUID value.

HTTP/1.1 400 Bad RequestContent-Type: application/json {"code": "INVALID_ARGUMENT", "message": "{error message}", "cause": "{cause message}", "request_id": "01234567-89ab-cdef-0123-456789abcdef", "host_id": "{server identity}", "server_time": "2014-01-01T12:00:00Z"} 7. Think about the idempotency of API.

The core of the idempotent mechanism is to ensure the uniqueness of resources, for example, repeated submissions by the client or multiple retries on the server will only produce one result. Payment scenarios, refund scenarios, transactions involving money can not be deducted multiple times and other problems. In fact, the query interface is used to obtain resources, because it only queries data and does not affect the change of resources, so no matter how many times the interface is called, the resource will not change, so it is idempotent. The new interface is non-idempotent, because if the interface is called many times, it will cause changes in resources. Therefore, we need to do idempotent processing when duplicate commits occur. So, how to ensure the idempotent mechanism? In fact, we have a lot of implementation plans. One of the common scenarios is to create a unique index. Creating a unique index in the database on the resource fields that we need to constrain can prevent duplicate data from being inserted. However, in the case of sub-database and sub-table, the unique index is not so good. At this time, we can first query the database, and then determine whether the constrained resource fields are duplicated, and then insert when there is no repetition. Note that to avoid concurrency scenarios, we can ensure the uniqueness of the data through locking mechanisms, such as pessimistic and optimistic locks. Here, distributed locking is a frequently used solution, which is usually a pessimistic implementation of locking. However, many people often regard pessimistic lock, optimistic lock and distributed lock as the solution of idempotent mechanism, which is incorrect. In addition, we can also introduce the state machine, through the state machine for state constraints and state jump, to ensure the flow of the same business execution, so as to achieve data idempotence. In fact, not all interfaces have to be idempotent, in other words, whether idempotent mechanisms are needed can be considered by considering the need to ensure resource uniqueness, for example, behavior logs can not consider idempotence. Of course, another design scheme is that the interface does not consider the idempotent mechanism, but is guaranteed at the business level when the business is implemented, such as allowing the existence of multiple data, but obtaining the latest version for processing when the business is processed.

On how to think about RESTful API, GraphQL, RPC API to share here, I hope the above content can be of some help to you, can learn more knowledge. If you think the article is good, you can share it for more people to see.

Welcome to subscribe "Shulou Technology Information " to get latest news, interesting things and hot topics in the IT industry, and controls the hottest and latest Internet news, technology news and IT industry trends.

Views: 0

*The comments in the above article only represent the author's personal views and do not represent the views and positions of this website. If you have more insights, please feel free to contribute and share.

Share To

Servers

Wechat

© 2024 shulou.com SLNews company. All rights reserved.

12
Report