draveness / blog-comments

面向信仰编程
https://draveness.me
140 stars 6 forks source link

Elixir 从入门到放弃 · /elixir-or-not #130

Closed draveness closed 2 years ago

draveness commented 5 years ago

https://draveness.me/elixir-or-not

wujunze commented 5 years ago

Elixir 算是 Erlang VM 平台上的一种方言 很有意思
感谢分享👍

leeduckgo commented 5 years ago

左神复盘。

x1angli commented 5 years ago

不错,很高兴看到大神带领团队跳坑了。珍爱绳命,远离小众语言……

zengyuangai commented 5 years ago

为啥不直接erlang?

draveness commented 5 years ago

@zengyuangai 为啥不直接erlang?

太难写了,我们没有人之前有过 erlang 经验,但是以前都写过 rails

1c7 commented 5 years ago

感谢分享~~

fireyang commented 5 years ago

. 你发这篇文章的时候phoenix应该已经支持http2.0了; . 而且提到的二进制序列号的问题,也不是语言的问题,用protobuf序列化都可以 . 其实如果只是用来做api,推荐试试maru这个库;

ps: 方法总比困难多,题目太消极,哈哈!

jobryant commented 5 years ago

@draveness 楼主的流程图十分漂亮,想请教下是用什么工具制作的?

seamon commented 5 years ago

放弃的原因主要是正好对 elixir/erlang/phoenix 扬短避长了,在系统架构上放大了 elixir 生态的缺点,却没有用到多少它的优点。

elixir/erlang 生态目前比较大的问题之一是轮子太少,如果作为异构系统的一部分,与其它模块的交互会有比较大的问题。自己手撸轮子是个费力不讨好的事情,想用到生产环境更是要反复迭代才行。所以在 grpc 和 protobuff 这些问题上就会吃尽苦头。

所以问题并不在于 elixir/erlang 自己不行,而只是轮子不够,如果在异构系统中作为一个模块去配合别人,会比较尴尬,浑身有劲使不上,却把生态环境和基础设施建设不足的缺点完美地放大了出来。

以及,用 elixir 原生函数把数据结构序列化之后,用其它语言反序列化,这本身是系统设计考虑不周,而不是语言的锅。异构系统之间如果一开始就打定主意要共享数据,那么数据最好是通用格式,而不能是某个语言的私有格式。这个从一开始就应该预计到并做出规划。转 json,或者使用数据库作为中介,都比原生二进制要好。

就目前来看,使用 elixir/erlang 这个生态的正确姿势,应该是以它的哲学思想和架构理念为核心,elixir 作为整个系统的基础架构,让别人围着它转,而不是正好相反。

巧得很,我们也是做交易所,但我们是全套的 elixir/phoenix,完全按照它们自己的理念去做系统设计,怎么用着顺,我们就怎么用,而不是我们想怎么用,再反过来看它能不能行。

我们没有用 k8s,用原生的编译打包进行部署,集群使用 erlang 的全联通结构,子集群之间的数据共享通过数据库和缓存完成,服务发现是 where_is,远程调用是 gen_server.call、rpc.call,消息机制就用 erlang 自带的,异步队列的话直接存数据库,可以利用事务来充分保证数据的安全、完整和一致,速度和容量上相对消息队列,也完全没有问题,不会有可感知的差别。子集群可横向扩展,哪里不够加哪里,如果以后真的火到上百亿交易额了,在做到运营提前预判的前提下,手动加服务器也足够了,k8s 之类的快速部署和资源调度在我们这个应用场景里其实意义并不是很大,添了麻烦还没讨好。

我们项目启动时只有 4 个 elxiir 后端,1 个比较熟悉,1 个还算熟悉,2 个从头开始学,几个月后就上线内测了,目前一直稳定运行,没出大的问题。日交易额也上亿过,CPU 都没啥感觉,再加上横向扩展的架构,如果有幸活到上百亿的那天,现有体系也应该能撑得住。所以,怕什么人少,要什么 golang 呢 [手动狗头]

去年 8 月份那次截胡友商,我们周一早上提前对方 4 个小时上线的功能,是在周日早上大家看到对方公告后才决定要干一把的,4个 elixir 后端 24 小时通宵搞定。所以说,elixir/erlang/phoenix 这个语言这个体系,开发和维护效率,其本身是无可置疑的。

使用姿势对了,事倍功半,姿势错了,当然会非常难受。不仅是 elixir,什么都是一样的。elixir 从入门到放弃,很多时候其实并不是语言本身的错。

draveness commented 5 years ago

@seamon 放弃的原因主要是正好对 elixir/erlang/phoenix 扬短避长了,在系统架构上放大了 elixir 生态的缺点,却没有用到多少它的优点。

...

关于生态和基础建设

一个语言的生态本身其实就应该是它的一部分,设计优秀的语言其实非常多,但是生态良好并且丰富的编程语言就没有多少了,学习一门编程语言本身就不止包括这门语言本身,还需要去了解这门语言中常见的框架和思想以及它的生态环境能为我们作什么支持,所以说『轮子不够真的不是语言本身的问题么』?

关于开发效率

Elixir 这门语言的开发效率相比于一些 C、C++、Java、Golang 这些语言确实会高出一些,不过开发效率有时也同时意味着,我在做这件事情的时候社区中是否已经有开发者做出了相似的功能,如果把这一点考虑进去我并不认为一个有完善、完整生态环境支持的『高级』编程语言能够比 Elixir 差多少。

关于异构系统

我们的系统从最开始设计时全部决定使用 Elixir 编程语言,但是由于招聘问题和业务线的变动,同时后端团队人数达到 20 多人,我们做出了以下的一些变化:

  1. 一些面向 B 端的服务上使用更成熟的 Rails 进行开发;
  2. 数据相关的服务和业务也都使用 Java、Python 进行开发(数据团队);

这些变动导致我们的系统从一个单语言的系统变成了多语言的系统,如果我们之前就深陷 Elixir 编程语言的生态,想要进行这些改变来支持多语言就非常复杂了。

作者并不认为将一个系统绑定到一种语言是一个正确的选择,尤其是一个大型的系统,如果一个团队长期保持 10 人以下的规模并且业务中短期内也非常稳定,这种做法还是可以接受的,但是随着业务变得复杂,当我们需要更专业的数据开发、风控团队时(一般是 Java),我们没有办法保证它们也能接受并使用 Elixir 这门小众的编程语言,而编程语言也没有足够的生态支持类似的场景。

我们的整个开发和部署从最开始就没有遵循,也没有想遵循 Elixir 内的生态进行设计(所以可能一开始就不应该选择 Elixir),我们整个系统的基础是 Google Cloud 和 Kubernetes,所以 Elixir 中本身的服务发现、部署以及远程调用对于我们来说没有任何的用处。

Kubernetes 本身为我们带来了大量的好处,其本身能够保证我们系统的正常运行并在出现错误时及时将服务自动拉起来,它的引入也使得我们团队不需要专门的运维人员,机器的扩容缩容都是由 Kubernetes 自动根据 CPU 占用完成的。

最后

就像文章里说的,Elixir 语言是一门值得学习的语言,GenServer、进程和 Actor 的设计确实非常优秀,我们确实不会使用 Elixir,但是它的生态也并不完善,如果只是在小团队中使用其实也没太大问题,更换的成本非常低,但是在大团队还是要深思熟虑。

seamon commented 5 years ago

@draveness

@seamon 放弃的原因主要是正好对 elixir/erlang/phoenix 扬短避长了,在系统架构上放大了 elixir 生态的缺点,却没有用到多少它的优点。

...

关于生态和基础建设

一个语言的生态本身其实就应该是它的一部分,设计优秀的语言其实非常多,但是生态良好并且丰富的编程语言就没有多少了,学习一门编程语言本身就不止包括这门语言本身,还需要去了解这门语言中常见的框架和思想以及它的生态环境能为我们作什么支持,所以说『轮子不够真的不是语言本身的问题么』?

关于开发效率

Elixir 这门语言的开发效率相比于一些 C、C++、Java、Golang 这些语言确实会高出一些,不过开发效率有时也同时意味着,我在做这件事情的时候社区中是否已经有开发者做出了相似的功能,如果把这一点考虑进去我并不认为一个有完善、完整生态环境支持的『高级』编程语言能够比 Elixir 差多少。

关于异构系统

我们的系统从最开始设计时全部决定使用 Elixir 编程语言,但是由于招聘问题和业务线的变动,同时后端团队人数达到 20 多人,我们做出了以下的一些变化:

  1. 一些面向 B 端的服务上使用更成熟的 Rails 进行开发;
  2. 数据相关的服务和业务也都使用 Java、Python 进行开发(数据团队);

这些变动导致我们的系统从一个单语言的系统变成了多语言的系统,如果我们之前就深陷 Elixir 编程语言的生态,想要进行这些改变来支持多语言就非常复杂了。

作者并不认为将一个系统绑定到一种语言是一个正确的选择,尤其是一个大型的系统,如果一个团队长期保持 10 人以下的规模并且业务中短期内也非常稳定,这种做法还是可以接受的,但是随着业务变得复杂,当我们需要更专业的数据开发、风控团队时(一般是 Java),我们没有办法保证它们也能接受并使用 Elixir 这门小众的编程语言,而编程语言也没有足够的生态支持类似的场景。

我们的整个开发和部署从最开始就没有遵循,也没有想遵循 Elixir 内的生态进行设计(所以可能一开始就不应该选择 Elixir),我们整个系统的基础是 Google Cloud 和 Kubernetes,所以 Elixir 中本身的服务发现、部署以及远程调用对于我们来说没有任何的用处。

Kubernetes 本身为我们带来了大量的好处,其本身能够保证我们系统的正常运行并在出现错误时及时将服务自动拉起来,它的引入也使得我们团队不需要专门的运维人员,机器的扩容缩容都是由 Kubernetes 自动根据 CPU 占用完成的。

最后

就像文章里说的,Elixir 语言是一门值得学习的语言,GenServer、进程和 Actor 的设计确实非常优秀,我们确实不会使用 Elixir,但是它的生态也并不完善,如果只是在小团队中使用其实也没太大问题,更换的成本非常低,但是在大团队了还是要深思熟虑。

1、如果整个系统的基础是 k8s,那一开始或许就不应该选用 erlang 体系,这个选型就是有潜在冲突的。erlang 在架构意义上是一个完整自洽的体系,k8s 的核心理念,除了快速扩容缩容之外,其它的都和 erlang 有类似之处。 k8s 是为那些没有天生支持分布式的体系设立的,是为了补上分布式和弹性伸缩这块短板,而分布式这个概念,erlang 属于玩得很好的之一,如果不是最好的话。如果 elixir 的服务发现、热部署、远程调用这些,对你们都没有用处,那一开始选择使用 elixir 的原因又是什么,这个体系中最精华的东西都不用了,用到的都是它所不擅长的,这不正是我要表达的意思:扬短避长。

2、出现错误之后及时将服务拉起来,如果这是用 k8s 的主要原因,本身和 erlang 也是一种重复性建设。erlang 的 let it crash 哲学思想和 OTP 的 supervision 架构,恰恰就是做这个的。对失败服务的处理和重启,做的比 erlang 好很多的,还真没见过几个。要数 9 的话,什么系统还能比得上电信系统的 9 多?

3、不想绑定到某一个特定的语言,这是完全没有问题的,也是合理的。但如果一开始就是按照一个庞大的,可伸缩的,异构的系统来设计,就要根据异构系统中当前存在的各个模块的特性,取一个都适用的交集,来有序地、渐进地改造整个系统 ,大部分精力要用来处理很多遗留问题。这也是异构系统一开始设计时的必选项。 如果一开始就按照大型异构系统来设计,那么 elixir 原生序列化就是绝对不应该出现的一个方案。这个方案势必会造成极差的可扩展性和互操作性。我们也用到了 term_to_binary 序列化,但我们却都是用 binary_to_term 来解的。在这个问题上 elixir 是完全无辜的。 作为一个大型异构系统的第一个语言体系和初创架构,elixir 确实不是一个很好的选择。你们硬把 elixir 按到一个它并不擅长的位置上,然后再放弃它,那么这个锅并不是语言本身的。任何一门语言如果都这么操作,最终都会 从入门到放弃。

4、关于 k8s 弹性伸缩这块,按照我个人的理念,其实 99% 的场景,都是架构者自己想多了。体系的演化是渐进的,按 需变革的。很多用了 k8s 的项目,其引入的复杂度所造成的成本损耗,是大于其收益的。当然这是个人所好了,没有对错之分。

5、最后说一句,轮子不够,确实是语言自身的问题,这无可逃避,也需要喜欢这个语言的人共同努力。所以明确地看到每个语言的短处,规避它的缺陷,把优点发挥到极致,正是系统架构者的核心任务之一。whatsapp 能够以 erlang 作为核心体系,以那么少的人员做到那么大的规模,这正是架构设计的完美体现。只要有人能做到,就证明这是可行的。

draveness commented 5 years ago

@seamon

...

我们最开始确实不应该使用 Erlang,因为我们对于 Erlang 和 Elixir 的体系并不了解,所以同时使用了 Kubernetes 和 Erlang 这两个技术栈,但是在了解了之后,我们仍然也不会选择使用 Erlang 和 Elixir。

  1. 虽然 Erlang/Elixir 在电信相关的行业确实比较优秀并且它自己的体系也很完善,但是它就是小众,作为一个小众的开发语言,遇到一些奇葩问题的时候很难找到前人的经验,而 Kubernetes 目前已经是容器编排领域的事实标准,并且它提供的不是一套专门语言特定的能力,如果真像你说的最开始用 Elixir 之后再加其他语言的,那么当一个系统需要支持多语言时,终究还是要为其他语言引入一个用于做容器编排、服务发现等功能的组件,类似 Kubernetes;
  2. 当有人说 99% 的时候,大多数都是说这句话的人的主观臆断,没有任何支撑,想多了是哪里想多了?怎么想多了?99% 又是哪来的?
  3. 把轮子不够简单归结成一个语言的缺点,然后说一句要避免它的缺陷,这个逻辑我真不是特别明白,轮子不够多要解决不就是 — 要不自己写,要不就不用;

Elixir 这套内部自恰的逻辑只能在 Elixir 生态内生存,而 Elixir 作为一个小众语言,选择这门语言会带来太多开发和维护上不必要风险,这套逻辑学一学,简单了解一下也就可以了,我们日常接触的服务面对的根本不是电信行业这种类似的场景

Kubernetes 和围绕 Kubernetes 建立起来的生态更加成熟并且背靠大树,而且目前的生态非常成熟稳定,所以当然应该选择相对成熟稳定并且社区支持更好的技术体系。

yummybian commented 5 years ago

@seamon

@draveness

@seamon 放弃的原因主要是正好对 elixir/erlang/phoenix 扬短避长了,在系统架构上放大了 elixir 生态的缺点,却没有用到多少它的优点。

...

关于生态和基础建设

一个语言的生态本身其实就应该是它的一部分,设计优秀的语言其实非常多,但是生态良好并且丰富的编程语言就没有多少了,学习一门编程语言本身就不止包括这门语言本身,还需要去了解这门语言中常见的框架和思想以及它的生态环境能为我们作什么支持,所以说『轮子不够真的不是语言本身的问题么』?

关于开发效率

Elixir 这门语言的开发效率相比于一些 C、C++、Java、Golang 这些语言确实会高出一些,不过开发效率有时也同时意味着,我在做这件事情的时候社区中是否已经有开发者做出了相似的功能,如果把这一点考虑进去我并不认为一个有完善、完整生态环境支持的『高级』编程语言能够比 Elixir 差多少。

关于异构系统

我们的系统从最开始设计时全部决定使用 Elixir 编程语言,但是由于招聘问题和业务线的变动,同时后端团队人数达到 20 多人,我们做出了以下的一些变化:

  1. 一些面向 B 端的服务上使用更成熟的 Rails 进行开发;
  2. 数据相关的服务和业务也都使用 Java、Python 进行开发(数据团队);

这些变动导致我们的系统从一个单语言的系统变成了多语言的系统,如果我们之前就深陷 Elixir 编程语言的生态,想要进行这些改变来支持多语言就非常复杂了。

作者并不认为将一个系统绑定到一种语言是一个正确的选择,尤其是一个大型的系统,如果一个团队长期保持 10 人以下的规模并且业务中短期内也非常稳定,这种做法还是可以接受的,但是随着业务变得复杂,当我们需要更专业的数据开发、风控团队时(一般是 Java),我们没有办法保证它们也能接受并使用 Elixir 这门小众的编程语言,而编程语言也没有足够的生态支持类似的场景。

我们的整个开发和部署从最开始就没有遵循,也没有想遵循 Elixir 内的生态进行设计(所以可能一开始就不应该选择 Elixir),我们整个系统的基础是 Google Cloud 和 Kubernetes,所以 Elixir 中本身的服务发现、部署以及远程调用对于我们来说没有任何的用处。

Kubernetes 本身为我们带来了大量的好处,其本身能够保证我们系统的正常运行并在出现错误时及时将服务自动拉起来,它的引入也使得我们团队不需要专门的运维人员,机器的扩容缩容都是由 Kubernetes 自动根据 CPU 占用完成的。

最后

就像文章里说的,Elixir 语言是一门值得学习的语言,GenServer、进程和 Actor 的设计确实非常优秀,我们确实不会使用 Elixir,但是它的生态也并不完善,如果只是在小团队中使用其实也没太大问题,更换的成本非常低,但是在大团队了还是要深思熟虑。

1、如果整个系统的基础是 k8s,那一开始或许就不应该选用 erlang 体系,这个选型就是有潜在冲突的。erlang 在架构意义上是一个完整自洽的体系,k8s 的核心理念,除了快速扩容缩容之外,其它的都和 erlang 有类似之处。 k8s 是为那些没有天生支持分布式的体系设立的,是为了补上分布式和弹性伸缩这块短板,而分布式这个概念,erlang 属于玩得很好的之一,如果不是最好的话。如果 elixir 的服务发现、热部署、远程调用这些,对你们都没有用处,那一开始选择使用 elixir 的原因又是什么,这个体系中最精华的东西都不用了,用到的都是它所不擅长的,这不正是我要表达的意思:扬短避长。

2、出现错误之后及时将服务拉起来,如果这是用 k8s 的主要原因,本身和 erlang 也是一种重复性建设。erlang 的 let it crash 哲学思想和 OTP 的 supervision 架构,恰恰就是做这个的。对失败服务的处理和重启,做的比 erlang 好很多的,还真没见过几个。要数 9 的话,什么系统还能比得上电信系统的 9 多?

3、不想绑定到某一个特定的语言,这是完全没有问题的,也是合理的。但如果一开始就是按照一个庞大的,可伸缩的,异构的系统来设计,就要根据异构系统中当前存在的各个模块的特性,取一个都适用的交集,来有序地、渐进地改造整个系统 ,大部分精力要用来处理很多遗留问题。这也是异构系统一开始设计时的必选项。 如果一开始就按照大型异构系统来设计,那么 elixir 原生序列化就是绝对不应该出现的一个方案。这个方案势必会造成极差的可扩展性和互操作性。我们也用到了 term_to_binary 序列化,但我们却都是用 binary_to_term 来解的。在这个问题上 elixir 是完全无辜的。 作为一个大型异构系统的第一个语言体系和初创架构,elixir 确实不是一个很好的选择。你们硬把 elixir 按到一个它并不擅长的位置上,然后再放弃它,那么这个锅并不是语言本身的。任何一门语言如果都这么操作,最终都会 从入门到放弃。

4、关于 k8s 弹性伸缩这块,按照我个人的理念,其实 99% 的场景,都是架构者自己想多了。体系的演化是渐进的,按 需变革的。很多用了 k8s 的项目,其引入的复杂度所造成的成本损耗,是大于其收益的。当然这是个人所好了,没有对错之分。

5、最后说一句,轮子不够,确实是语言自身的问题,这无可逃避,也需要喜欢这个语言的人共同努力。所以明确地看到每个语言的短处,规避它的缺陷,把优点发挥到极致,正是系统架构者的核心任务之一。whatsapp 能够以 erlang 作为核心体系,以那么少的人员做到那么大的规模,这正是架构设计的完美体现。只要有人能做到,就证明这是可行的。

二位说得都非常精彩,是否适用Elixir还是要看使用场景。如果是在异构的系统中,Elixir就不是好的选择。但是如果是围绕着Elixir的生态圈,他基于的OTP能轻松处理分布式的问题。

tt67wq commented 5 years ago

放弃的原因主要是正好对 elixir/erlang/phoenix 扬短避长了,在系统架构上放大了 elixir 生态的缺点,却没有用到多少它的优点。

elixir/erlang 生态目前比较大的问题之一是轮子太少,如果作为异构系统的一部分,与其它模块的交互会有比较大的问题。自己手撸轮子是个费力不讨好的事情,想用到生产环境更是要反复迭代才行。所以在 grpc 和 protobuff 这些问题上就会吃尽苦头。

所以问题并不在于 elixir/erlang 自己不行,而只是轮子不够,如果在异构系统中作为一个模块去配合别人,会比较尴尬,浑身有劲使不上,却把生态环境和基础设施建设不足的缺点完美地放大了出来。

以及,用 elixir 原生函数把数据结构序列化之后,用其它语言反序列化,这本身是系统设计考虑不周,而不是语言的锅。异构系统之间如果一开始就打定主意要共享数据,那么数据最好是通用格式,而不能是某个语言的私有格式。这个从一开始就应该预计到并做出规划。转 json,或者使用数据库作为中介,都比原生二进制要好。

就目前来看,使用 elixir/erlang 这个生态的正确姿势,应该是以它的哲学思想和架构理念为核心,elixir 作为整个系统的基础架构,让别人围着它转,而不是正好相反。

巧得很,我们也是做交易所,但我们是全套的 elixir/phoenix,完全按照它们自己的理念去做系统设计,怎么用着顺,我们就怎么用,而不是我们想怎么用,再反过来看它能不能行。

我们没有用 k8s,用原生的编译打包进行部署,集群使用 erlang 的全联通结构,子集群之间的数据共享通过数据库和缓存完成,服务发现是 where_is,远程调用是 gen_server.call、rpc.call,消息机制就用 erlang 自带的,异步队列的话直接存数据库,可以利用事务来充分保证数据的安全、完整和一致,速度和容量上相对消息队列,也完全没有问题,不会有可感知的差别。子集群可横向扩展,哪里不够加哪里,如果以后真的火到上百亿交易额了,在做到运营提前预判的前提下,手动加服务器也足够了,k8s 之类的快速部署和资源调度在我们这个应用场景里其实意义并不是很大,添了麻烦还没讨好。

我们项目启动时只有 4 个 elxiir 后端,1 个比较熟悉,1 个还算熟悉,2 个从头开始学,几个月后就上线内测了,目前一直稳定运行,没出大的问题。日交易额也上亿过,CPU 都没啥感觉,再加上横向扩展的架构,如果有幸活到上百亿的那天,现有体系也应该能撑得住。所以,怕什么人少,要什么 golang 呢 [手动狗头]

去年 8 月份那次截胡友商,我们周一早上提前对方 4 个小时上线的功能,是在周日早上大家看到对方公告后才决定要干一把的,4个 elixir 后端 24 小时通宵搞定。所以说,elixir/erlang/phoenix 这个语言这个体系,开发和维护效率,其本身是无可置疑的。

使用姿势对了,事倍功半,姿势错了,当然会非常难受。不仅是 elixir,什么都是一样的。elixir 从入门到放弃,很多时候其实并不是语言本身的错。

求问下老哥 服务发现和负载均衡是咋做的啊

draveness commented 5 years ago

@tt67wq

求问下老哥 服务发现和负载均衡是咋做的啊

k8s 会通过 DNS 和 ipvs 做服务发现和负载均衡,在往上一层我们看到的就是 service 对象 https://draveness.me/kubernetes-service

tt67wq commented 5 years ago

@tt67wq 求问下老哥 服务发现和负载均衡是咋做的啊

k8s 会通过 DNS 和 ipvs 做服务发现和负载均衡,在往上一层我们看到的就是 service 对象 https://draveness.me/kubernetes-service

额 我是想问问在otp下的路子,官方文档说的比较简略,一直好奇这里的最佳实践。 k8s确实已经做了很多工作,上面的老哥不是说了吗,k8s与erlang基因有些重叠的地方。

sweetycode commented 5 years ago

入坑两周,介绍OTP的部分很不错,终于有所收获:-D 中间有说到使用了Elixir原生的序列化,这个真的不是语言的锅啊,说到底是使用者目光不够长远。python 的 pickle 也有同样的问题,没人会因为这个选择放弃这门语言吧。

tt67wq commented 5 years ago

入坑两周,介绍OTP的部分很不错,终于有所收获:-D 中间有说到使用了Elixir原生的序列化,这个真的不是语言的锅啊,说到底是使用者目光不够长远。python 的 pickle 也有同样的问题,没人会因为这个选择放弃这门语言吧。

参考这个博客搭建elixir分布式app 协议的部分可以解耦出来

draveness commented 5 years ago

入坑两周,介绍OTP的部分很不错,终于有所收获:-D 中间有说到使用了Elixir原生的序列化,这个真的不是语言的锅啊,说到底是使用者目光不够长远。python 的 pickle 也有同样的问题,没人会因为这个选择放弃这门语言吧。

Python 有多少用户,生态又是什么样的,Elixir 有多少用户,生态又是什么样的?我写两周的时候也觉得很爽

seamon commented 5 years ago

@tt67wq

放弃的原因主要是正好对 elixir/erlang/phoenix 扬短避长了,在系统架构上放大了 elixir 生态的缺点,却没有用到多少它的优点。

elixir/erlang 生态目前比较大的问题之一是轮子太少,如果作为异构系统的一部分,与其它模块的交互会有比较大的问题。自己手撸轮子是个费力不讨好的事情,想用到生产环境更是要反复迭代才行。所以在 grpc 和 protobuff 这些问题上就会吃尽苦头。

所以问题并不在于 elixir/erlang 自己不行,而只是轮子不够,如果在异构系统中作为一个模块去配合别人,会比较尴尬,浑身有劲使不上,却把生态环境和基础设施建设不足的缺点完美地放大了出来。

以及,用 elixir 原生函数把数据结构序列化之后,用其它语言反序列化,这本身是系统设计考虑不周,而不是语言的锅。异构系统之间如果一开始就打定主意要共享数据,那么数据最好是通用格式,而不能是某个语言的私有格式。这个从一开始就应该预计到并做出规划。转 json,或者使用数据库作为中介,都比原生二进制要好。

就目前来看,使用 elixir/erlang 这个生态的正确姿势,应该是以它的哲学思想和架构理念为核心,elixir 作为整个系统的基础架构,让别人围着它转,而不是正好相反。

巧得很,我们也是做交易所,但我们是全套的 elixir/phoenix,完全按照它们自己的理念去做系统设计,怎么用着顺,我们就怎么用,而不是我们想怎么用,再反过来看它能不能行。

我们没有用 k8s,用原生的编译打包进行部署,集群使用 erlang 的全联通结构,子集群之间的数据共享通过数据库和缓存完成,服务发现是 where_is,远程调用是 gen_server.call、rpc.call,消息机制就用 erlang 自带的,异步队列的话直接存数据库,可以利用事务来充分保证数据的安全、完整和一致,速度和容量上相对消息队列,也完全没有问题,不会有可感知的差别。子集群可横向扩展,哪里不够加哪里,如果以后真的火到上百亿交易额了,在做到运营提前预判的前提下,手动加服务器也足够了,k8s 之类的快速部署和资源调度在我们这个应用场景里其实意义并不是很大,添了麻烦还没讨好。

我们项目启动时只有 4 个 elxiir 后端,1 个比较熟悉,1 个还算熟悉,2 个从头开始学,几个月后就上线内测了,目前一直稳定运行,没出大的问题。日交易额也上亿过,CPU 都没啥感觉,再加上横向扩展的架构,如果有幸活到上百亿的那天,现有体系也应该能撑得住。所以,怕什么人少,要什么 golang 呢 [手动狗头]

去年 8 月份那次截胡友商,我们周一早上提前对方 4 个小时上线的功能,是在周日早上大家看到对方公告后才决定要干一把的,4个 elixir 后端 24 小时通宵搞定。所以说,elixir/erlang/phoenix 这个语言这个体系,开发和维护效率,其本身是无可置疑的。

使用姿势对了,事倍功半,姿势错了,当然会非常难受。不仅是 elixir,什么都是一样的。elixir 从入门到放弃,很多时候其实并不是语言本身的错。

求问下老哥 服务发现和负载均衡是咋做的啊

服务发现,可以使用全局注册的 global process name。考虑到高可用,可以有主有备,主的挂了,备的接管。 负载均衡,这个涉及到的模式有好几种,依场景而定。 如果是对外服务且是无状态的,比如 web,节点搞一堆,前面加个四层或七层的分发就好了。 如果服务是有状态的,但可以并行跑,比如异步队列,那就搞几个节点各干各的,状态控制好,别冲突就可以。 如果服务有状态且只能串行,比如交易撮合,那负载均衡就会麻烦点,单服务无法做到很理想的扩展,但可以把多个同类型的服务分布注册到不同的节点上,实现负载均衡。

seamon commented 5 years ago

@tt67wq

@tt67wq 求问下老哥 服务发现和负载均衡是咋做的啊

k8s 会通过 DNS 和 ipvs 做服务发现和负载均衡,在往上一层我们看到的就是 service 对象 https://draveness.me/kubernetes-service

额 我是想问问在otp下的路子,官方文档说的比较简略,一直好奇这里的最佳实践。 k8s确实已经做了很多工作,上面的老哥不是说了吗,k8s与erlang基因有些重叠的地方。

确实有些重叠。erlang/OTP 这套体系是我见过的最牛逼的平台之一。可以说,起码在分布式和高可用这个领域,它领先了这个时代整整几十年。 k8s 它们现在正在做的这些工作,其核心理念只不过是把 erlang 几十年前就已经完成并商用的架构,再重新实现一遍而已。服务注册,发现,负载均衡,水平扩展,系统容错,进程监控和重启,分布式服务和通信。太阳底下无新事。 有兴趣可以拜读一下 Joe Armstrong 十几年前的那篇博士论文《面对软件错误构建可靠的分布式系统》,这里面总结和描述了他对于软件系统和分布式开发的世界观。

他老人家前几天刚刚去世。非常感谢他为这个世界所做的一切。R. I. P. Joe

draveness commented 5 years ago

@tt67wq

@tt67wq 求问下老哥 服务发现和负载均衡是咋做的啊

k8s 会通过 DNS 和 ipvs 做服务发现和负载均衡,在往上一层我们看到的就是 service 对象 https://draveness.me/kubernetes-service

额 我是想问问在otp下的路子,官方文档说的比较简略,一直好奇这里的最佳实践。 k8s确实已经做了很多工作,上面的老哥不是说了吗,k8s与erlang基因有些重叠的地方。

确实有些重叠。erlang/OTP 这套体系是我见过的最牛逼的平台之一。可以说,起码在分布式和高可用这个领域,它领先了这个时代整整几十年。 k8s 它们现在正在做的这些工作,其核心理念只不过是把 erlang 几十年前就已经完成并商用的架构,再重新实现一遍而已。服务注册,发现,负载均衡,水平扩展,系统容错,进程监控和重启,分布式服务和通信。太阳底下无新事。 有兴趣可以拜读一下 Joe Armstrong 十几年前的那篇博士论文《面对软件错误构建可靠的分布式系统》,这里面总结和描述了他对于软件系统和分布式开发的世界观。

他老人家前几天刚刚去世。非常感谢他为这个世界所做的一切。R. I. P. Joe

kubernetes 和 Erlang 在不同层面提供了部分相似的功能,但是 kubernetes 相比 Erlang 来说更加通用,它并不是为某一个语言专门打造的。

Adek06 commented 4 years ago

读完了。多谢大佬分享,之前没接触过,现在想试试函数式语言了

Aetherus commented 4 years ago

@zengyuangai 为啥不直接erlang?

因为erlang没有宏。虽然宏这玩意儿是把双刃剑,可以舞得很帅气,也可以把自己砍得体无完肤,但是有总比没有强。

Hzqi commented 3 years ago

看完了文章,我也是刚入坑,文章里好多的负面点对我来说反倒是亮点诶嘿嘿,因为之前就熟悉FP的语言像Haskell、Clojure这种。说到生态的问题,我倒是持不同的意见吧,Java生态够好啦,spring撑起一片天,招人特别好招啊,但是人员那个参差不齐吧,都懂的。生态虽然是一部分,实际还是要看公司的规划。不过我也好想知道文章里的图使用什么工具写的😃

shaonianche commented 3 years ago

自研中间件的稳定性不经过长期的测试也打不到生产环境的要求。 -> 貌似应该是“达不到”?

https://draveness.me/elixir-or-not/#开源框架支持:~:text=%E8%87%AA%E7%A0%94%E4%B8%AD%E9%97%B4%E4%BB%B6%E7%9A%84%E7%A8%B3%E5%AE%9A%E6%80%A7%E4%B8%8D%E7%BB%8F%E8%BF%87%E9%95%BF%E6%9C%9F%E7%9A%84%E6%B5%8B%E8%AF%95%E4%B9%9F%E6%89%93%E4%B8%8D%E5%88%B0%E7%94%9F%E4%BA%A7%E7%8E%AF%E5%A2%83%E7%9A%84%E8%A6%81%E6%B1%82%E3%80%82


2021-03-26 UPDATES: 已修复

2a588 commented 2 years ago

谢谢您的分享 ,很真实的现实生活中使用编程语言的经历

2a588 commented 2 years ago

@seamon 放弃的原因主要是正好对 elixir/erlang/phoenix 扬短避长了,在系统架构上放大了 elixir 生态的缺点,却没有用到多少它的优点。

elixir/erlang 生态目前比较大的问题之一是轮子太少,如果作为异构系统的一部分,与其它模块的交互会有比较大的问题。自己手撸轮子是个费力不讨好的事情,想用到生产环境更是要反复迭代才行。所以在 grpc 和 protobuff 这些问题上就会吃尽苦头。

所以问题并不在于 elixir/erlang 自己不行,而只是轮子不够,如果在异构系统中作为一个模块去配合别人,会比较尴尬,浑身有劲使不上,却把生态环境和基础设施建设不足的缺点完美地放大了出来。

以及,用 elixir 原生函数把数据结构序列化之后,用其它语言反序列化,这本身是系统设计考虑不周,而不是语言的锅。异构系统之间如果一开始就打定主意要共享数据,那么数据最好是通用格式,而不能是某个语言的私有格式。这个从一开始就应该预计到并做出规划。转 json,或者使用数据库作为中介,都比原生二进制要好。

就目前来看,使用 elixir/erlang 这个生态的正确姿势,应该是以它的哲学思想和架构理念为核心,elixir 作为整个系统的基础架构,让别人围着它转,而不是正好相反。

巧得很,我们也是做交易所,但我们是全套的 elixir/phoenix,完全按照它们自己的理念去做系统设计,怎么用着顺,我们就怎么用,而不是我们想怎么用,再反过来看它能不能行。

我们没有用 k8s,用原生的编译打包进行部署,集群使用 erlang 的全联通结构,子集群之间的数据共享通过数据库和缓存完成,服务发现是 where_is,远程调用是 gen_server.call、rpc.call,消息机制就用 erlang 自带的,异步队列的话直接存数据库,可以利用事务来充分保证数据的安全、完整和一致,速度和容量上相对消息队列,也完全没有问题,不会有可感知的差别。子集群可横向扩展,哪里不够加哪里,如果以后真的火到上百亿交易额了,在做到运营提前预判的前提下,手动加服务器也足够了,k8s 之类的快速部署和资源调度在我们这个应用场景里其实意义并不是很大,添了麻烦还没讨好。

我们项目启动时只有 4 个 elxiir 后端,1 个比较熟悉,1 个还算熟悉,2 个从头开始学,几个月后就上线内测了,目前一直稳定运行,没出大的问题。日交易额也上亿过,CPU 都没啥感觉,再加上横向扩展的架构,如果有幸活到上百亿的那天,现有体系也应该能撑得住。所以,怕什么人少,要什么 golang 呢 [手动狗头]

去年 8 月份那次截胡友商,我们周一早上提前对方 4 个小时上线的功能,是在周日早上大家看到对方公告后才决定要干一把的,4个 elixir 后端 24 小时通宵搞定。所以说,elixir/erlang/phoenix 这个语言这个体系,开发和维护效率,其本身是无可置疑的。

使用姿势对了,事倍功半,姿势错了,当然会非常难受。不仅是 elixir,什么都是一样的。elixir 从入门到放弃,很多时候其实并不是语言本身的错。

说的很好,谢谢您分享。

tianjianyong commented 2 years ago

面对奢侈品的高价格,没钱是自己的问题。

ayanamist commented 2 years ago

最主要的问题还是服务端宕机导致消息大量积压,重启后消费但是接收方已经不再处理这些超时已久的消息

这个应该是团队没有人对RabbitMQ了解吧,其实在队列上设一个TTL就可以避免这个问题了。

draveness commented 2 years ago

@tianjianyong 面对奢侈品的高价格,没钱是自己的问题。

奢侈品的高价格不一定合理,而且我比你想的有钱

@ayanamist

最主要的问题还是服务端宕机导致消息大量积压,重启后消费但是接收方已经不再处理这些超时已久的消息

这个应该是团队没有人对RabbitMQ了解吧,其实在队列上设一个TTL就可以避免这个问题了。

问题太久了,都忘了当时啥情况了,你说的有可能解决

gotnix commented 2 years ago

感谢 @draveness@seamon 两位大佬的精彩评论!

第一次部署 RabbitMQ 的时候就觉得 Erlang 的分布式架构真是先进,k8s 现在才有的功能,Erlang 在语言虚拟机的层面就已经提供了,而且还那么早,比如 Erlang 的 epmd 进程和 k8s 的 Sidecar proxy 的作用简直一模一样。

当然 k8s 提供的功能更加完善,不然就白白比 Erlang 晚生这么多年。Erlang 的服务注册估计是类似 Dubbo 服务治理的功能,只在服务的生产者和消费者之间使用,接入层的负载均衡不能动态发现已注册的 Erlang 服务;k8s 有 Ingress 和服务注册中心集成,可以把注册的服务暴露出去,这个单独靠 Erlang 就做不到了。其实这个也不是 Erlang 应该提供的功能,k8s 之前也有负载均衡自己先办法动态代理后端的服务器,k8s 其实是把需要运维人肉干的一部分活给自动化了,功能和普适性肯定要比 Erlang 一个虚拟机要强。

这里有一系列文章Elixir and Kubernetes: A love story — Part 1: Setting up an Elixir Cluster,介绍了以下内容:

  • how to build a Kubernetes Operator in Elixir
  • why you don’t need leader election
  • out of the box distributed consensus
  • how to rely on other technologies when it’s needed

Kubernetes and the Erlang VM: orchestration on the large and the small 这篇文章也讨论了 Kubernetes 和 Erlang VM 功能重合和冲突的地方,k8s 的部署和重启都是针对节点操作的,Erlang 的热更新在 k8s 的部署流水线里肯定用不了,进程监督重启的进程不一定是整个Pod。

不过我猜 Erlang 提供的软实时特性是不可替代的,如果业务需要这个特性,其它平台提供的功能再好可能也只是锦上添花。

说到底还是 Elixir 太小众,如果早就有人撸出来 Kubernetes Operator 和各种轮子,估计左神也不至于放弃。