Go through the eyes of a java programmer

    This article is not for those who already write on go.
    It is for programmers in other languages ​​who are not interested in whether to spend time on go.
    What is the difference between go, for example, from java and how it can be useful.

    Go principles


    Once upon a time, you could just take and create a new programming language.
    Now the language has a chance to take some place only if it has clear principles that its creators follow. In other words, your own face.
    Go principles are simplicity and productivity.

    The principle of simplicity is:
    If you can do without something, you need to do without it.

    The principle of productivity:
    The most valuable thing is the time spent by the developer. It must be minimized by all available means.

    I will illustrate the principle of simplicity:
    There are very few language constructs in go. For example, only one cycle. Go can be learned in two evenings.
    Go refused dynamic loading of libraries - the result of compilation is one large executable file
    There are no warning s in go when compiling. Any incorrectness or “verbosity” is a compilation error.
    Go has built-in code format at the level of the language itself. There is only one canonical form of go code.

    Now a few examples of productivity:
    The go code is 20-30 percent shorter than the similar code in Java (there are simply not superfluous words, for example, there is no semicolon at the end of each sentence, there are no parentheses in the condition or loop operators etc).
    Go has a very fast compiler (a few seconds to compile a large project).

    Productivity is not only the language itself, but also the standard tools supplied with it.

    Productivity Tools


    Profilers


    I'll start with a little digression about how I, the java programmer, came to use go.
    I did a game project - a multiplayer space shooter.
    Initially, I wrote the server side in java. She worked.
    But very quickly everything was limited by productivity.
    On one server it was possible to run no more than 300 clients.
    This is too small for the game to be profitable.

    What could I do with this as a java programmer?
    In fact, a little:

    1) Using the method of gaze, look for ineffective places in the code, try to fix them and, after each change, restart the load tests. This method is obviously inefficient.

    2) Immerse yourself in the study of alternative server libraries, try different options. This also does not give any guarantees - there may be problems in my own code, or even in the java language itself.

    3) Finally, I could buy one or more paid profilers and try to get some information from them. But there are problems. If I use the local server in our office, I could not create the necessary load, since we do not have the necessary number of free machines in the office to start several thousand clients. If you use an external server, then a rather complicated configuration was required to run the profiler there. Finally, choosing a profiler in itself is a non-trivial task.

    In go, this problem is solved very conveniently.
    The profiler there is part of the language.
    You can enable it on any server, and the result of its work is a file that can be downloaded to your machine and slowly explored using very convenient tools.
    These tools will show which particular lines of code spend the most memory and processor cycles.
    Their use makes it possible to quickly and efficiently find and fix all problem areas. (Which, by the way, almost always turns out to be not where you expect to find them.)

    In my project, the server copied to go initially “pulled” only 20 clients (much worse than in Java). After working with profilers, this figure increased to 2500.

    Race detector


    Another headache for all multithreaded application writers is race conditions.
    These are such elusive bugs that occur only if the stars come together in a certain way. That is, if the threads started in one order, there is a bug, if in the other - no. And the order is unpredictable.
    There is a standard tool in go to solve this problem - race detector. If you enable it, the program on go will write to the log about all unsafe accesses to the shared memory from different threads.
    Using the race detector, you can quickly, purposefully find and eliminate all problem areas.

    Interesting designs


    I do not have the goal of teaching go programming and I will not analyze all its constructions. I want to dwell only on the three most interesting. They all describe paradigms that are not in pure Java. And these paradigms are very useful.

    These are interfaces, goroutines and channels.

    Interfaces


    The interface in go is similar to the interface in Java or c #. This is a set of method signatures, but without implementations.
    The main difference is that go does not require to declare that an entity implements an interface.
    It is enough that the entity simply had all the necessary methods.
    What does it give? Decoupling.
    You can take someone else's library. Find there an entity with some set of methods, create an interface with the same set, and use this entity as this interface. You do not need to change someone else’s code, you don’t need to tie your code to someone else’s entities, and you don’t need to write adapters, which are classic boilerplate code. Another illustration of the principle of productivity.

    Gorutins


    At first, processes were invented for multitasking applications in programming languages. They were heavy, had their own address space and quickly “ate” the resources of the system. Then threads (or threads) appeared. They were much lighter and worked in the same address space. If processes were usually started by units or dozens, then hundreds could already have threads. However, with careless use, they could take away all the resources of the system. Each thread still occupied some resources, even if it was blocked.
    To limit the number of threads, we started using thread pools.

    Gorutins can be thought of as tasks performed by one common large thread pool. At Go, goroutines are extremely cheap. You can run millions of goroutines without performance issues. The only requirement is that gorutins should be “small”. That is, goroutin should quickly do its job and either exit or block (which is the same thing from the point of view of the scheduler).

    Channels


    Channels are the racially correct means of communication between goroutines.
    There is an important rule in go:

    Don’t communicate by shared state. Share state by communication.

    Its meaning is not to use variables to which more than one goroutine has access.
    Instead, make the goroutines forward data to each other.
    This data transfer is done through channels.

    A channel in go is a queue (buffer) at one end of which you can write and read from the other.
    At the same time, goroutine is blocked when trying to write to a crowded channel or read from an empty channel.

    But the most interesting - go has a design for reading from multiple channels at the same time.
    Gorutin can list several channels on which she will sit and wait for a message to appear in one of them. After receiving this message, goroutine is unlocked, processes it, and (usually) waits for the next message again.
    So, for example, in a chat server, goroutin can wait for a message from the user and a signal to exit the chat. At the same time.

    Dependency management


    Dependency management tools like maven or gradle exist today for all major languages.
    Go went further and made support for dependency management at the language level itself.
    When importing a package (by a construction similar to import in Java), you can specify both the local name and the address of the package in any modern version control system (for example, in git).

    For example, "github.com/gorilla/websocket"

    Go will independently download the necessary package and include it in your project. If some package has been previously downloaded, then it will simply be used. You can also ask go to update all packages to their latest versions.

    Here, however, there is one unpleasant moment - go always downloads the latest version of the package. If several people work on a project, this can lead to different versions of packages from different developers.

    To solve this problem, external tools are used - package managers.
    One of the best today is glide.
    Glide is based on two actions:
    1) Find all the project dependencies and write them to a file
    2) Download these dependencies

    At the same time, the file can be edited manually, specifying, if necessary, other versions of packages (other than the last).

    GIT uses the commit identifier as the version identifier (other version control systems use identifiers specific to them).

    Weaknesses go


    Any principle has not only strengths, but also weaknesses.
    In go, in my opinion, there is one bad and one terrible thing that stem from the principle of simplicity.
    The bad is the lack of generic s. Horrible - error handling by checking the code returned by the function.

    Without generics, one has to refuse strict typing.
    And error handling leads to a lot of the same type of code. Which, in addition, you can completely forget to write and leave the error unprocessed.

    In addition, following the principle of productivity, it was decided to introduce go garbage collector in go. This in itself is not bad. But it is important to understand that this same collector will start every few seconds and noticeably slow down the system. With this, however, you can successfully fight.

    conclusions


    Should a programmer switch to go completely? Not. Anyway, bye. Java is on average faster. It has more useful libraries and proven ways to create applications.

    Should go be used to solve certain problems? Definitely worth it.

    What tasks are best solved on go? Creation of multi-threaded high-load server solutions in which threads communicate a lot with each other.

    What language is better for a beginner to learn - go or Java? There is no definite answer, but over time there will be more arguments in favor of go.

    The age of the tongue plays a role. The older language is overgrown with "historical" things. Something was added a long time ago, now no one needs it, but you can’t throw it away, because backward compatibility is broken.
    Some constructions of the language or standard libraries, previously relevant, now look out of place, it seems more logical to do these unclaimed things outside the standard of the language.
    For some standard problems, many alternative solutions of different quality have accumulated. For a beginner, all this is a large number of useless but necessary knowledge in the style: “You need to be especially careful here. Here we have a Puddle. ”

    Go was created much later and today it contains everything that is in demand now and nothing more.

    Also popular now: