Python/Ruby is single process, and to exploit all the resources on a single machine you need multiple processes of your app (usually #cores or #cores-1).
Now that you have multiple processes, you need something to dispatch between them - intro Nginx/Gunicorn etc.
A single Go process can exploit all the system resources available without the multi-process orchestration required by Python/Ruby and thus does not require the extra layer above.
That's an implementation detail of your Python/Ruby program. I've written plenty of Ruby apps that have been multi-process and multi-threaded, and both at the same time.
> Now that you have multiple processes, you need something to dispatch between them
Yes, that's the case irrespective of language.
> A single Go process can exploit all the system resources available without the multi-process orchestration required by Python/Ruby and thus does not require the extra layer above.
This is no different than for Ruby at least.
Your criticisms are mostly outdated as of Ruby 1.9.x, though you can get bitten by the GIL. (EDIT: to clarify, avoiding this depends on either multi-process or taking advantage of the fact that while Ruby Thread's can only be scheduled one at a time, C-extensions etc. that releases the GIL can run in parallel with other Ruby Thread's; of course all of this is MRI specific in any case)
In practice, if you use a reasonably modern web server like Puma, you get good concurrency without having to think much about it.
I don't need gunicorn or nginx in front of my Java/Go/C# process to achieve concurrency and take advantage of all CPU cores.
>> Your criticisms are mostly outdated as of Ruby 1.9.x
Python 3.x & Ruby 1.9.x have the same limitations - any native code will lock the GIL, so unless all your code is in extensions you're going to run into this problem. Ruby 1.9 introducing OS threads did not change this.
Sure it's great that I can write an extension in C and take advantage of multiple OS threads in my Ruby/Python runtime, but the moment those threads have to deal with ANY native objects you're back to square one.
Your single Go Process is typically #cores Processes. All the orchestration is still handled by you, just there falacities for it in the language for it.
Just because you are using stdlib not an external application does not change the functionality that is happening.
Do you also think that Java is multi-process? I can see the argument that an OS thread is a process but it is basically irrelevant in every way that matters in development and operations.
An example - how do you access the same shared-memory (say, a map/dictionary) from multiple cores in any particular runtime?
- Go: you make a map, you protect the map with a mutex, and you reference it directly.
- Java: same, or ConcurrentHashMap or whatever is available.
- 'Worker'/'forking' runtimes: you can't, right? You move the state to another component (such as Redis), or you use an OS-provided shared memory facility, or you need inter-process RPC etc ...
--
Of course, sometimes workers being separate processes is a virtue rather then a burden ... but I'm not sure this is true in the case of Ruby or Python when it's more purely a limitation of the runtime.
It orchestrates using threads iirc, and the primitives for handling the communication in a more safe fashion are in the box, as you mention... and depending on the OS starting threads may be nearly as expensive as processes. That said, it's definitely less overhead than orchestration across forked threads.
Now that you have multiple processes, you need something to dispatch between them - intro Nginx/Gunicorn etc.
A single Go process can exploit all the system resources available without the multi-process orchestration required by Python/Ruby and thus does not require the extra layer above.