简体   繁体   中英

Understanding Elixir

Can Java frameworks like SpringBoot and Play handle multiple requests at the same time by leveraging all the cores on the machine ? If they can do that, then what unique problem is Elixir solving with respect to concurrency? Is it just a different language? Or there is any difference in the computing paradigm itself that brings us lower server costs with Elixir?

Here is the report: https://www.techworld.com/apps-wearables/how-elixir-helped-bleacher-report-handle-8x-more-traffic-3653957/

There are three main differences.

Lightweight processes

Elixir's processes should not be confused with operating system processes. Processes in Elixir are extremely lightweight in terms of memory and CPU (even compared to threads as used in many other programming languages). Because of this, it is not uncommon to have tens or even hundreds of thousands of processes running simultaneously. https://elixir-lang.org/getting-started/processes.html

Fault-tolerance

Erlang and hence Elixir uses the concept of the supervision tree , assuring the crash of a single process would not bring any harm to the whole application; even more, Erlang philosophy explicitly states “Let it crash” as its motto. That basically means you should not care about error handling to some extent.

The supervisors have a built-in mechanism to limit the number of restarts which can occur in a given time interval. This is determined by the values of the two parameters MaxR and MaxT. If more than MaxR number of restarts occur in the last MaxT seconds, then the supervisor shuts down all the children it supervises and dies. http://erlang.org/documentation/doc-4.9.1/doc/design_principles/sup_princ.html

Message passing

Instead of data sharing, Erlang processes are completely isolated and the only mechanism to interchange between is to pass messages; that makes the memory management extremely effective and performant.

Each process has its own input queue for messages it receives. New messages received are put at the end of the queue. When a process executes a receive, the first message in the queue is matched against the first pattern in the receive. If this matches, the message is removed from the queue and the actions corresponding to the pattern are executed. http://erlang.org/doc/getting_started/conc_prog.html#message-passing

One of the difficulties of handling multiple requests on multiple cores is the fact that you now have multiple parts of your program running at the same time and modifying state. So in come the mutexes and you now have to worry about whether or not a function is thread safe, and manage blocking/waiting/resuming on locks and mutexes. Which means you now have a bunch of potential bottle necks, and can that drive up costs and reduce (or even eliminate) what gains you were expecting from concurrency.

Erlang and Elixir have concurrency based on the 'Actor Model' . An actor receives messages and from those messages can create new messages, determine how to handle the next message, modify their own internal state, or create more actors. What that boils down to is each Actor has its own state so you no longer need to worry about locks or mutexes. This helps to ease the mental burden of writing concurrent code, and as an abstraction makes it easier to reason about concurrency. The result is more effective concurrent code that is easier to maintain. The actor model can be implemented in any language, but it's baked into Elixir and Erlang.

Further, Elixir and Erlang are functional languages. In functional languages side-effects, lingering state from prior method calls, is generally avoidable. What that means is that as long as you don't violate functional purity you have the ability to say that a method will return the same thing every time. Which means f(x) + f(x) == f(x)*2. Which means that you can parallelize things to an greater degree without fear. Running f(x) on one thread and g(x) on another CAN'T result in a race condition if they're functionally pure. Because neither one of them impacts state.

I'll try to give a short answer: Erlang/Elixir forces you to write asynchronous, non-blocking code, using actors.

You can see actors as "micro"-microservices. Actors have their own state, they don't share data (in fact all variables are immutable) and therefore need to pass asynchronous messages between each other.

Erlang/Elixir has its own virtual machine that can handle hundred thousands of threads and other optimizations (threads are not operating system threads, but virtual/lightweight ones and the virtual machine avoids copying of data in memory even though all variables are immutable, by only copying data when it is really necessary)

I am not up to date with other Actor Systems, so I cannot tell if eg Akka on the JVM would get a similar performance. Virtual Actor Systems might also be able to handle a lot of work, Orleans is such a framework. (a virtual actor system can be compared to having a dependency injection for Actors, when you need them they are created and when they are not in used they are removed from the memory) https://github.com/dotnet/orleans

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM