Have you got a business-objective?
We have a solution.
Get a consultation right now!
Year by year system administrators increasingly think about leaving old monolithic architectures of web applications and migrating to microservices. This is the approach to make the system scalable in the more flexible way. Let us say, with AWS one can save a great sum of money. For example, they can use more efficient instances only for the microservices that need them.
To date, there are two most used programming languages in the microservices designing: Node.js and Go or Golang, as it is also referenced. I think it’s obvious that there is no universal solution to suit all the cases with the same efficiency. That’s why let us consider the situation when we need to design a microservice for the work with notes. To make this example easier for understanding, suppose that the data is stored in memory. In developing this microservice, we will use the gRPC framework. One can wonder, why RPC, and not REST, which is more familiar to many? In my opinion, REST is already obsolete standard, which has certain conceptual shortcomings:
REST isn’t a specification, which enables certain freedom in the API projecting. This freedom generates many entities and various HTTP methods that carry a meaning, which is often known only to the developer who wrote them.
Parameters are specified in different places: URL, request body, header.
The problem with documenting is solved in two ways: by using a large web framework, which is inappropriate in the microservices realities, or by manually filling a .yml or .json file.
gRPC allows to describe microservices in .proto files and create projects on different platforms, including Golang and Node.js, using the generator. Assume that we will develop applications for storing notes. There will be 2 microservices: User and Note. The User is responsible for storing user data, authentication, and registration, while Note is needed to store user notes. That’s why user notes require CRUD.
Golang is a universal programming language widely used for backend development and creating microservices. Let’s start our example with the description of .proto files.
.proto file the user's microservice will look like this:
.proto file the notes' microservice will look like this:
Next, we need to install packages to work with gRPC and code generation. For this, we need to download and unpack the protoc compiler. One can do it here. Next, we need to add the $PATH to the unpacked archive.
The structure of the project is shown below:
Now, let's create a Makefile to simplify protoc code generation:
In directories with .proto files, we have files with generated code for working with the client and the server of the microservice. For each microservice, there are 2 interfaces: the client and the server. To describe the logic of microservices, we need to implement the server interface. The microservice code of the notes is provided below, and the code for the users’ and the client’s microservice can be found in the repository:
After implementing the Go logic, we can start writing the same services on Node.js to see what is better - Node.js or Golang. I will not describe the implementation in detail since it is almost identical to that for GoLang. However, there are some peculiarities that need explanation.
Due to the asynchronous model, as the code becomes more complex, the callbacks nesting of Node.js will increase. One can prevent it with async/await operators, and there are already a lot of articles and guides about how to do it.
Dynamic code generation
In Golang, static code generation is used, while Node.js uses dynamic one.
Frankly speaking, it was a surprise to me that the error should return the first parameter when using callbacks. The documentation does not say anything about this. I had to poke a little to make it work the way I wanted, so get ready to face the same problem.
Null / undefined
Also, for me as a person, who writes the code on Node.js after Golang, it was not obvious that the operation of taking an array element by a non-existent index returns undefined rather than null.
for-in loop is actually the usual for loop, at least while one works with associative arrays. In Golang, one can get both the key and the value at once using it.
Dynamic type checking
The implicit type coercions is the worst thing I know in programming. This feature sooner or later leads to errors that can only be determined at the stage of product testing.
Go provides a convenient API for developing benchmarks. We will test the performance of the microservices with the help of the clients generated by protoc. To execute a benchmark, one should run the following command:
The comparison was made in a synchronous environment. The research is not fully consistent with the real use case, although it’s rather indicative. As the new request was sent only after the response to the previous one was received, we were able to see a net difference in the performance of the two technologies.
|Feature||Number of passes||Go, rps||Node, rps|
If we compare net performance in a synchronous mode, we’ll get that Golang is faster in 4 tests out of 6.
Summing up everything described above, we shall say: what is the best situation to use Golang and when should one take an advantage of Node.js?
Node.js is great for developing MVP. This is true due to the high development speed. There is a huge number of libraries and solutions for Node.js, which enhance and simplify the work with it. If you choose Go, then it will almost certainly show higher performance and reliability compared to the Node.js solution. However, it will have slower development speed due to strong static typing and a relatively small community.
In conclusion, there is a quote of Ryan Dahl, the creator of Node.js, about the differences of these two languages for you:
Whatever you may choose, let your choice will be successful!