github.com/ergo-services/ergo@v1.999.224/README.md (about) 1 <h1><a href="https://ergo.services"><img src=".github/images/logo.svg" alt="Ergo Framework" width="159" height="49"></a></h1> 2 3 <!--[![Gitbook Documentation](https://img.shields.io/badge/GitBook-Documentation-f37f40?style=plastic&logo=gitbook&logoColor=white&style=flat)](https://docs.ergo.services) --> 4 [![GoDoc](https://pkg.go.dev/badge/ergo-services/ergo)](https://pkg.go.dev/github.com/ergo-services/ergo) 5 [![MIT license](https://img.shields.io/badge/license-MIT-brightgreen.svg)](https://opensource.org/licenses/MIT) 6 [![Telegram Community](https://img.shields.io/badge/Telegram-Community-blue?style=flat&logo=telegram)](https://t.me/ergo_services) 7 [![Discord Community](https://img.shields.io/badge/Discord-Community-5865F2?style=flat&logo=discord&logoColor=white)](https://discord.gg/sdscxKGV62) 8 [![Twitter](https://img.shields.io/badge/Twitter-ergo__services-1DA1F2?style=flat&logo=twitter&logoColor=white)](https://twitter.com/ergo_services) 9 10 Technologies and design patterns of Erlang/OTP have been proven over the years. Now in Golang. 11 Up to x5 times faster than original Erlang/OTP in terms of network messaging. 12 The easiest way to create an OTP-designed application in Golang. 13 14 [https://ergo.services](https://ergo.services) 15 16 ### Purpose ### 17 18 The goal of this project is to leverage Erlang/OTP experience with Golang performance. Ergo Framework implements [DIST protocol](https://erlang.org/doc/apps/erts/erl_dist_protocol.html), [ETF data format](https://erlang.org/doc/apps/erts/erl_ext_dist.html) and [OTP design patterns](https://erlang.org/doc/design_principles/des_princ.html) `gen.Server`, `gen.Supervisor`, `gen.Application` which makes you able to create distributed, high performance and reliable microservice solutions having native integration with Erlang infrastructure 19 20 ### Cloud ### 21 22 Distributed Cloud is coming. With Ergo Framework you can join your services into a single cluster with transparent networking using our **Cloud Overlay Network** where they can connect to each other smoothly, no matter where they run - AWS, Azure or GCP, or anywhere else. All these connections are secured with end-to-end encryption. Read more in this article [https://blog.ergo.services/cloud-overlay-network-3a133d47efe5](https://blog.ergo.services/cloud-overlay-network-3a133d47efe5). 23 24 ### Quick start ### 25 26 First, you need to install the boilerplate code generation tool `ergo` - https://github.com/ergo-services/tools using command below 27 28 `go install ergo.services/tools/ergo@latest` 29 30 And then, you can create your project with just one command. Here is example: 31 32 Supervision tree 33 ``` 34 mynode 35 |- myapp 36 | | 37 | `- mysup 38 | | 39 | `- myactor 40 |- myweb 41 `- myactor2 42 ``` 43 44 To generate project for this design use the following command: 45 46 `ergo -init MyNode -with-app MyApp -with-sup MyApp:MySup -with-actor MySup:MyActor -with-web "MyWeb{port:8000,handlers:3}" -with-actor MyActor2` 47 48 as a result you will get generated project: 49 50 ``` 51 mynode/ 52 |-- apps/ 53 | `-- myapp/ 54 | |-- myactor.go 55 | |-- myapp.go 56 | `-- mysup.go 57 |-- cmd/ 58 | |-- myactor2.go 59 | |-- mynode.go 60 | |-- myweb.go 61 | `-- myweb_handler.go 62 |-- README.md 63 |-- go.mod 64 `-- go.sum 65 ``` 66 67 to try it: 68 ``` 69 $ cd mynode 70 $ go run ./cmd/ 71 ``` 72 73 You may also read our article about this tool with a great example https://blog.ergo.services/quick-start-1094d56d4e2 74 75 ### Features ### 76 77 ![image](https://user-images.githubusercontent.com/118860/113710255-c57d5500-96e3-11eb-9970-20f49008a990.png) 78 79 * Support Erlang 25 - allows you connect your node to (and accept connection from) any Erlang/Elixir node within a cluster 80 * Spawn Erlang-like processes 81 * Register/unregister processes with simple atom 82 * Set of ready-to-use disign patterns (behaviors) 83 * `gen.Server` behavior with atomic state and Erlang's gen_server support to make sync request `ServerProcess.Call`, async - `ServerProcess.Cast` or `Process.Send` in fashion of `gen_server:call`, `gen_server:cast`, `erlang:send` accordingly 84 * `gen.Supervisor` behavior with all known [restart strategies](https://erlang.org/doc/design_principles/sup_princ.html#restart-strategy) (One For One, One For All, Rest For One, Simple One For One) 85 * `gen.Application` behavior with all known [starting types](https://erlang.org/doc/design_principles/applications.html#application-start-types) (Permanent, Temporary, Transient) 86 * `gen.Pool` a basic design pattern with a pool of workers. All messages/requests received by the pool process are forwarded to the workers using the "Round Robin" algorithm. The worker process is automatically restarting on termination 87 * `gen.TCP` - socket acceptor pool for TCP protocols. This behavior aims to provide everything you need to accept TCP connections and process packets with a small code base and low latency while being easy to use. 88 * `gen.UDP` - acceptor pool for UDP protocols. This behavior provides the same feature set as TCP but for handling UDP packets using pool of handlers 89 * `gen.Web` - Web API Gateway behavior. This behavior allows you to listen HTTP port and handle HTTP-request using pool of workers. 90 * `gen.Stage` behavior support (originated from Elixir's [GenStage](https://hexdocs.pm/gen_stage/GenStage.html)). This is abstraction built on top of `gen.Server` to provide a simple way to create a distributed Producer/Consumer architecture, while automatically managing the concept of backpressure. This implementation is fully compatible with Elixir's GenStage. Example is here [examples/genstage](https://github.com/ergo-services/examples/tree/master/genstage) or just run `go run ./examples/genstage` to see it in action 91 * `gen.Saga` behavior support. It implements Saga design pattern - a sequence of transactions that updates each service state and publishes the result (or cancels the transaction or triggers the next transaction step). `gen.Saga` also provides a feature of interim results (can be used as transaction progress or as a part of pipeline processing), time deadline (to limit transaction lifespan), two-phase commit (to make distributed transaction atomic). Here is example [examples/gensaga](https://github.com/ergo-services/examples/tree/master/gensaga). 92 * `gen.Raft` behavior support. It's improved implementation of [Raft consensus algorithm](https://raft.github.io). The key improvement is using quorum under the hood to manage the leader election process and make the Raft cluster more reliable. This implementation supports quorums of 3, 5, 7, 9, or 11 quorum members. Here is an example of this feature [examples/genraft](https://github.com/ergo-services/examples/tree/master/genraft) 93 * Monitor processes/nodes, local/remote with Erlang support 94 * Link processes local/remote with Erlang support 95 * [embedded EPMD](#epmd) (in order to get rid of erlang' dependencies) with Erlang support 96 * Unmarshalling terms into the struct using `etf.TermIntoStruct`, `etf.TermProplistIntoStruct` or to the string using `etf.TermToString` including custom marshaling/unmarshaling via `Marshal` and `Unmarshal` interfaces. But it's highly recommended to use `etf.RegisterType` so you will be receiving messages in a native Golang-type 97 * Encryption (TLS 1.3) support (including autogenerating self-signed certificates) 98 * Compression support (with customization of compression level and threshold). It can be configured for the node or a particular process. 99 * Proxy support with end-to-end encryption, includeing compression/fragmentation/linking/monitoring features. 100 * Tested and confirmed support Windows, Darwin (MacOS), Linux, FreeBSD. 101 * Zero dependencies. All features are implemented using the standard Golang library. 102 103 ### Requirements ### 104 105 * Go 1.17.x and above 106 107 ### Versioning ### 108 109 Golang introduced [v2 rule](https://go.dev/blog/v2-go-modules) a while ago to solve complicated dependency issues. We found this solution very controversial and there is still a lot of discussion around it. So, we decided to keep the old way for the versioning, but have to use the git tag with v1 as a major version (due to "v2 rule" restrictions). Since now we use git tag pattern 1.999.XYZ where X - major number, Y - minor, Z - patch version. 110 111 ### Changelog ### 112 113 Here are the changes of latest release. For more details see the [ChangeLog](ChangeLog.md) 114 115 #### [v2.2.4](https://github.com/ergo-services/ergo/releases/tag/v1.999.224) 2023-05-01 [tag version v1.999.224] #### 116 117 This release includes fixes: 118 - Fixed incorrect handling of `gen.SupervisorStrategyRestartTransient` restart strategy in `gen.Supervisor` 119 - Fixed missing `ServerBehavior` in [`gen.Pool`, `gen.Raft`, `gen.Saga`, `gen.Stage`, `gen.TCP`, `gen.UDP`, `gen.Web`] behavior interfaces 120 - Introduced the new tool for boilerplate code generation - `ergo` https://github.com/ergo-services/tools. You may read more information about this tool in our article with a great example https://blog.ergo.services/quick-start-1094d56d4e2 121 122 ### Benchmarks ### 123 124 Here is simple EndToEnd test demonstrates performance of messaging subsystem 125 126 Hardware: workstation with AMD Ryzen Threadripper 3970X (64) @ 3.700GHz 127 128 ``` 129 ❯❯❯❯ go test -bench=NodeParallel -run=XXX -benchtime=10s 130 goos: linux 131 goarch: amd64 132 pkg: github.com/ergo-services/ergo/tests 133 cpu: AMD Ryzen Threadripper 3970X 32-Core Processor 134 BenchmarkNodeParallel-64 4738918 2532 ns/op 135 BenchmarkNodeParallelSingleNode-64 100000000 429.8 ns/op 136 137 PASS 138 ok github.com/ergo-services/ergo/tests 29.596s 139 ``` 140 141 these numbers show almost **500.000 sync requests per second** for the network messaging via localhost and **10.000.000 sync requests per second** for the local messaging (within a node). 142 143 #### Compression 144 145 This benchmark shows the performance of compression for sending 1MB message between two nodes (via a network). 146 147 ``` 148 ❯❯❯❯ go test -bench=NodeCompression -run=XXX -benchtime=10s 149 goos: linux 150 goarch: amd64 151 pkg: github.com/ergo-services/ergo/tests 152 cpu: AMD Ryzen Threadripper 3970X 32-Core Processor 153 BenchmarkNodeCompressionDisabled1MBempty-64 2400 4957483 ns/op 154 BenchmarkNodeCompressionEnabled1MBempty-64 5769 2088051 ns/op 155 BenchmarkNodeCompressionEnabled1MBstring-64 5202 2077099 ns/op 156 PASS 157 ok github.com/ergo-services/ergo/tests 56.708s 158 ``` 159 160 It demonstrates **more than 2 times** improvement. 161 162 #### Proxy 163 164 This benchmark demonstrates how proxy feature and e2e encryption impact a messaging performance. 165 166 ``` 167 ❯❯❯❯ go test -bench=NodeProxy -run=XXX -benchtime=10s 168 goos: linux 169 goarch: amd64 170 pkg: github.com/ergo-services/ergo/tests 171 cpu: AMD Ryzen Threadripper 3970X 32-Core Processor 172 BenchmarkNodeProxy_NodeA_to_NodeC_direct_Message_1KB-64 1908477 6337 ns/op 173 BenchmarkNodeProxy_NodeA_to_NodeC_via_NodeB_Message_1KB-64 1700984 7062 ns/op 174 BenchmarkNodeProxy_NodeA_to_NodeC_via_NodeB_Message_1KB_Encrypted-64 1271125 9410 ns/op 175 PASS 176 ok github.com/ergo-services/ergo/tests 45.649s 177 178 ``` 179 180 181 #### Ergo Framework vs original Erlang/OTP 182 183 Hardware: laptop with Intel(R) Core(TM) i5-8265U (4 cores. 8 with HT) 184 185 ![benchmarks](https://raw.githubusercontent.com/halturin/ergobenchmarks/master/ergobenchmark.png) 186 187 sources of these benchmarks are [here](https://github.com/halturin/ergobenchmarks) 188 189 190 ### EPMD ### 191 192 *Ergo Framework* has embedded EPMD implementation in order to run your node without external epmd process needs. By default, it works as a client with erlang' epmd daemon or others ergo's nodes either. 193 194 The one thing that makes embedded EPMD different is the behavior of handling connection hangs - if ergo' node is running as an EPMD client and lost connection, it tries either to run its own embedded EPMD service or to restore the lost connection. 195 196 ### Examples ### 197 198 Code below is a simple implementation of gen.Server pattern [examples/genserver](https://github.com/ergo-services/examples/tree/master/genserver) 199 200 ```golang 201 package main 202 203 import ( 204 "fmt" 205 "time" 206 207 "github.com/ergo-services/ergo/etf" 208 "github.com/ergo-services/ergo/gen" 209 ) 210 211 type simple struct { 212 gen.Server 213 } 214 215 func (s *simple) HandleInfo(process *gen.ServerProcess, message etf.Term) gen.ServerStatus { 216 value := message.(int) 217 fmt.Printf("HandleInfo: %#v \n", message) 218 if value > 104 { 219 return gen.ServerStatusStop 220 } 221 // sending message with delay 1 second 222 fmt.Println("increase this value by 1 and send it to itself again") 223 process.SendAfter(process.Self(), value+1, time.Second) 224 return gen.ServerStatusOK 225 } 226 227 ``` 228 229 here is output of this code 230 231 ```shell 232 $ go run ./examples/simple 233 HandleInfo: 100 234 HandleInfo: 101 235 HandleInfo: 102 236 HandleInfo: 103 237 HandleInfo: 104 238 HandleInfo: 105 239 exited 240 ``` 241 242 See [https://github.com/ergo-services/examples](https://github.com/ergo-services/examples/) for more details 243 244 * [gen.Application](https://github.com/ergo-services/examples/tree/master/application) 245 * [gen.Supervisor](https://github.com/ergo-services/examples/tree/master/supervisor) 246 * [gen.Server](https://github.com/ergo-services/examples/tree/master/genserver) 247 * [gen.Pool](https://github.com/ergo-services/examples/tree/master/genpool) 248 * [gen.Stage](https://github.com/ergo-services/examples/tree/master/genstage) 249 * [gen.Saga](https://github.com/ergo-services/examples/tree/master/gensaga) 250 * [gen.Raft](https://github.com/ergo-services/examples/tree/master/genraft) 251 * [gen.Custom](https://github.com/ergo-services/examples/tree/master/gencustom) 252 * [gen.Web](https://github.com/ergo-services/examples/tree/master/genweb) 253 * [gen.TCP](https://github.com/ergo-services/examples/tree/master/gentcp) 254 * [gen.UDP](https://github.com/ergo-services/examples/tree/master/genudp) 255 * [events](https://github.com/ergo-services/examples/tree/master/events) 256 * [erlang](https://github.com/ergo-services/examples/tree/master/erlang) 257 * [proxy](https://github.com/ergo-services/examples/tree/master/proxy) 258 * [cloud](https://github.com/ergo-services/examples/tree/master/cloud) 259 260 ### Elixir Phoenix Users ### 261 262 Users of the Elixir Phoenix framework might encounter timeouts when trying to connect a Phoenix node 263 to an ergo node. The reason is that, in addition to global_name_server and net_kernel, 264 Phoenix attempts to broadcast messages to the [pg2 PubSub handler](https://hexdocs.pm/phoenix/1.1.0/Phoenix.PubSub.PG2.html) 265 266 To work with Phoenix nodes, you must create and register a dedicated pg2 GenServer, and 267 spawn it inside your node. The spawning process must have "pg2" as a process name: 268 269 ```golang 270 type Pg2GenServer struct { 271 gen.Server 272 } 273 274 func main() { 275 // ... 276 pg2 := &Pg2GenServer{} 277 node1, _ := ergo.StartNode("node1@localhost", "cookies", node.Options{}) 278 process, _ := node1.Spawn("pg2", gen.ProcessOptions{}, pg2, nil) 279 // ... 280 } 281 ``` 282 283 ### Development and debugging ### 284 285 There are options already defined that you might want to use 286 287 * `-ergo.trace` - enable debug info (logging via `lib.Log(...)`) 288 * `-ergo.debug` - enable extended debug info (logging via `lib.Log(...)` and `lib.Warning(...)`) 289 * `-ergo.norecover` - disable panic catching 290 * `-ergo.warning` - enable/disable warnings (logging via `lib.Warning(...)`. Default: enable) 291 292 To enable Golang profiler just add `--tags debug` in your `go run` or `go build` like this: 293 294 ``` 295 go run --tags debug ./examples/genserver/demoGenServer.go 296 ``` 297 298 Now golang' profiler is available at `http://localhost:9009/debug/pprof` 299 300 To check test coverage: 301 302 ``` 303 go test -coverprofile=cover.out ./... 304 go tool cover -html=cover.out -o coverage.html 305 ``` 306 307 To run tests with cleaned test cache: 308 309 ``` 310 go vet 311 go clean -testcache 312 go test -v ./... 313 ``` 314 315 To run benchmarks: 316 317 ``` 318 go test -bench=Node -run=X -benchmem 319 ``` 320 321 ### Companies are using Ergo Framework ### 322 323 [![Kaspersky](.github/images/kaspersky.png)](https://kaspersky.com) 324 [![RingCentral](.github/images/ringcentral.png)](https://www.ringcentral.com) 325 [![LilithGames](.github/images/lilithgames.png)](https://lilithgames.com) 326 327 is your company using Ergo? add your company logo/name here 328 329 ### Commercial support 330 331 please, contact ceo@ergo.services for more information