Web应用架构
- 300 次浏览
【web应用架构】web 框架那个更快
ℹ️ Updated on 2020-07-18 ℹ️
Benchmarking with wrk
- Threads : 8
- Timeout : 8
- Duration : 15s (seconds)
ℹ️ Sorted by max req/s
on concurrency 64 ℹ️
Language | Framework | Speed (64) | Speed (256) | Speed (512) | |
---|---|---|---|---|---|
1 | php (7.4) | mark (1.1) | 167 900 | 172 088 | 173 349 |
2 | nim (1.2) | httpbeast (0.2) | 165 808 | 197 548 | 200 123 |
3 | javascript (13.14) | nanoexpress-pro (2.2) | 159 374 | 203 682 | 209 592 |
4 | go (1.14) | fasthttp (1.15) | 158 244 | 170 931 | 174 931 |
5 | nim (1.2) | whip (0.2) | 157 852 | 185 577 | 186 965 |
6 | go (1.14) | atreugo (11.4) | 153 402 | 165 386 | 169 576 |
7 | go (1.14) | router (1.2) | 152 854 | 164 684 | 169 379 |
8 | go (1.14) | fasthttprouter (0.1) | 152 460 | 164 763 | 168 679 |
9 | javascript (13.14) | sifrr (0.0) | 151 082 | 193 326 | 196 249 |
10 | go (1.14) | gorouter-fasthttp (4.4) | 150 957 | 162 632 | 166 975 |
11 | go (1.14) | gearbox (1.0) | 149 964 | 161 740 | 165 613 |
12 | java (11) | rapidoid (5.5) | 148 731 | 176 813 | 177 967 |
13 | java (11) | jooby (2.8) | 144 563 | 187 072 | 191 087 |
14 | kotlin (1.3) | kooby (2.8) | 143 410 | 184 223 | 188 305 |
15 | c (11) | agoo-c (0.7) | 143 179 | 189 183 | 191 403 |
16 | java (11) | light-4j (2.0) | 141 210 | 176 879 | 181 302 |
17 | crystal (0.35) | router.cr (0.2) | 139 828 | 167 278 | 166 448 |
18 | crystal (0.35) | toro (0.4) | 139 397 | 169 974 | 169 052 |
19 | crystal (0.35) | spider-gazelle (3.3) | 138 223 | 165 592 | 164 228 |
20 | php (7.4) | workerman (4.0) | 137 200 | 167 944 | 170 403 |
21 | crystal (0.35) | kemal (0.26) | 132 422 | 156 420 | 154 940 |
22 | crystal (0.35) | grip (0.28) | 130 657 | 156 505 | 154 662 |
23 | nim (1.2) | jester (0.4) | 130 311 | 152 825 | 154 276 |
24 | rust (1.45) | actix (2.0) | 125 383 | 136 847 | 133 849 |
25 | crystal (0.35) | amber (0.35) | 125 178 | 144 644 | 143 136 |
26 | php (7.4) | simps (1.0) | 106 139 | 161 415 | 178 670 |
27 | go (1.14) | clevergo (0.3) | 104 631 | 106 188 | 110 306 |
28 | go (1.14) | rte (0.0) | 104 620 | 106 149 | 110 679 |
29 | java (11) | act (1.9) | 104 185 | 134 428 | 135 251 |
30 | go (1.14) | echo (4.1) | 103 125 | 105 277 | 109 307 |
31 | go (1.14) | httprouter (1.3) | 103 050 | 104 779 | 108 820 |
32 | go (1.14) | fiber (1.12) | 102 607 | 147 858 | 158 998 |
33 | go (1.14) | gin (1.6) | 101 067 | 106 832 | 110 609 |
34 | go (1.14) | chi (4.1) | 98 634 | 99 431 | 103 165 |
35 | go (1.14) | gorouter (4.4) | 98 633 | 103 964 | 107 547 |
36 | go (1.14) | aero (1.3) | 97 903 | 99 607 | 103 562 |
37 | go (1.14) | violetear (7.0) | 96 608 | 97 463 | 101 120 |
38 | go (1.14) | webgo (4.1) | 95 193 | 96 077 | 99 893 |
39 | csharp (8.0) | aspnetcore (3.1) | 94 733 | 108 017 | 110 905 |
40 | go (1.14) | goroute (0.0) | 94 052 | 93 621 | 97 834 |
41 | go (1.14) | kami (2.2) | 93 292 | 100 148 | 102 552 |
42 | fsharp (4.7) | falco (1.2) | 92 339 | 106 072 | 108 406 |
43 | go (1.14) | gorilla-mux (1.7) | 92 088 | 90 908 | 95 179 |
44 | fsharp (4.7) | frank (6.1) | 91 990 | 104 069 | 106 111 |
45 | go (1.14) | beego (1.12) | 91 186 | 96 653 | 100 384 |
46 | javascript (13.14) | polkadot (1.0) | 90 657 | 102 551 | 103 376 |
47 | crystal (0.35) | athena (0.9) | 86 509 | 105 308 | 104 455 |
48 | c (99) | kore (3.3) | 85 960 | 119 082 | 141 485 |
49 | javascript (13.14) | 0http (2.5) | 85 365 | 98 058 | 99 126 |
50 | javascript (13.14) | restana (4.6) | 81 941 | 91 578 | 91 555 |
51 | cpp (14/17) | drogon (1.0) | 81 662 | 87 368 | 90 524 |
52 | javascript (13.14) | polka (0.5) | 80 601 | 88 104 | 87 295 |
53 | go (1.14) | air (0.19) | 80 321 | 81 983 | 85 383 |
54 | javascript (13.14) | rayo (1.3) | 78 392 | 85 836 | 84 595 |
55 | javascript (13.14) | fastify (3.1) | 77 280 | 86 412 | 85 020 |
56 | elixir (1.1) | cowboy_stream (2.8) | 77 117 | 79 037 | 77 251 |
57 | php (7.4) | one (2.0) | 76 042 | 91 735 | 96 299 |
58 | ruby (2.7) | agoo (2.13) | 74 106 | 110 573 | 123 845 |
59 | go (1.14) | gf (1.13) | 73 254 | 80 074 | 81 884 |
60 | scala (2.13) | akkahttp (10.1) | 73 141 | 85 596 | 82 421 |
61 | csharp (8.0) | carter (5.1) | 73 130 | 74 246 | 66 783 |
62 | swift (5.2) | perfect (3.1) | 72 461 | 82 137 | 87 742 |
63 | php (7.4) | hyperf (2.0) | 71 889 | 90 300 | 94 927 |
64 | java (11) | javalin (3.9) | 71 495 | 79 141 | 78 295 |
65 | javascript (13.14) | muneem (2.4) | 70 256 | 77 801 | 76 235 |
66 | python (3.8) | falcon (2.0) | 68 617 | 75 668 | 76 314 |
67 | kotlin (1.3) | ktor (1.2) | 67 121 | 88 190 | 88 361 |
68 | fsharp (4.7) | saturn (0.14) | 66 497 | 62 731 | 55 831 |
69 | java (11) | spring-boot (2.3) | 65 455 | 71 623 | 71 444 |
70 | javascript (13.14) | foxify (0.1) | 64 148 | 70 024 | 69 199 |
71 | php (7.4) | comet (0.8) | 62 612 | 67 405 | 67 497 |
72 | go (1.14) | mars (1.0) | 59 797 | 60 607 | 64 349 |
73 | javascript (13.14) | koa (2.13) | 59 674 | 65 111 | 63 278 |
74 | java (11) | micronaut (1.2) | 57 662 | 66 616 | 65 711 |
75 | fsharp (4.7) | websharper (4.6) | 56 923 | 58 362 | 54 924 |
76 | javascript (13.14) | iotjs-express (0.0) | 56 004 | 58 967 | 59 745 |
77 | haskell (8.8) | scotty (0.12) | 55 879 | 62 428 | 66 645 |
78 | python (3.8) | bottle (0.12) | 55 116 | 60 823 | 60 007 |
79 | clojure (1.1) | coast (1.0) | 53 021 | 54 762 | 54 992 |
80 | javascript (13.14) | feathersjs (4.5) | 53 012 | 56 219 | 55 119 |
81 | javascript (13.14) | express (4.17) | 52 902 | 56 723 | 55 330 |
82 | rust (1.45) | nickel (0.11) | 52 618 | 48 793 | 51 696 |
83 | python (3.8) | apidaora (0.27) | 51 515 | 59 053 | 59 192 |
84 | java (11) | spring-framework (5.2) | 51 296 | 59 316 | 59 156 |
85 | javascript (13.14) | nestjs-fastify (7.3) | 50 931 | 60 892 | 59 478 |
86 | swift (5.2) | kitura (2.9) | 50 405 | 50 872 | 50 863 |
87 | swift (5.2) | kitura-nio (2.9) | 49 712 | 51 150 | 51 070 |
88 | javascript (13.14) | moleculer (0.14) | 49 456 | 52 663 | 51 879 |
89 | elixir (1.1) | cowboy (2.8) | 47 798 | 48 484 | 48 750 |
90 | python (3.8) | pyramid (1.1) | 47 764 | 50 253 | 50 442 |
91 | python (3.8) | asgineer (0.7) | 47 627 | 54 254 | 54 501 |
92 | rust (1.45) | gotham (0.4) | 46 373 | 52 337 | 54 878 |
93 | php (7.4) | siler-swoole (1.7) | 45 565 | 68 053 | 74 899 |
94 | swift (5.2) | vapor (4.23) | 45 341 | 48 039 | 47 892 |
95 | python (3.8) | blacksheep (0.2) | 44 967 | 50 418 | 50 507 |
96 | cpp (11) | evhtp (1.2) | 43 647 | 44 683 | 44 202 |
97 | scala (2.13) | http4s (0.21) | 42 326 | 48 277 | 47 692 |
98 | javascript (13.14) | hapi (19.1) | 42 248 | 45 758 | 44 916 |
99 | python (3.8) | hug (2.6) | 42 168 | 44 293 | 44 976 |
100 | javascript (13.14) | nestjs-express (7.3) | 41 140 | 43 789 | 42 970 |
101 | php (7.4) | imi (1.2) | 41 017 | 47 086 | 47 684 |
102 | python (3.8) | sanic (20.6) | 40 439 | 48 110 | 47 946 |
103 | python (3.8) | starlette (0.13) | 40 394 | 44 210 | 44 588 |
104 | javascript (13.14) | restify (8.5) | 39 954 | 43 417 | 42 549 |
105 | elixir (1.1) | plug (1.10) | 39 654 | 40 583 | 40 747 |
106 | csharp (8.0) | simplify.web (4.1) | 36 547 | 37 086 | 35 917 |
107 | python (3.8) | emmett (2.0) | 35 263 | 39 568 | 39 663 |
108 | crystal (0.35) | orion (3.0) | 34 914 | 33 918 | 29 528 |
109 | scala (2.13) | play (2.8) | 34 647 | 37 040 | 37 025 |
110 | elixir (1.1) | phoenix (1.5) | 33 992 | 35 518 | 35 721 |
111 | clojure (1.1) | luminus (1.0) | 31 689 | 32 569 | 32 417 |
112 | crystal (0.35) | shivneri (0.15) | 30 797 | 30 161 | 27 481 |
113 | haskell (8.8) | servant (0.17) | 30 434 | 29 284 | 28 323 |
114 | fsharp (4.7) | giraffe (4.1) | 30 235 | 28 822 | 28 120 |
115 | dart (2.8) | aqueduct (3.3) | 29 971 | 30 096 | 29 962 |
116 | fsharp (4.7) | suave (2.5) | 29 197 | 29 977 | 31 845 |
117 | nim (1.2) | rosencrantz (0.4) | 27 634 | 23 877 | 21 571 |
118 | php (7.4) | swoft (2.0) | 27 634 | 33 470 | 34 678 |
119 | python (3.8) | fastapi (0.59) | 26 912 | 29 394 | 29 387 |
120 | nim (1.2) | akane (0.1) | 26 627 | 22 183 | 19 714 |
121 | php (7.4) | yii-swoole (2.0) | 26 277 | 31 291 | 32 132 |
122 | ruby (2.7) | syro (3.2) | 25 741 | 29 593 | 28 479 |
123 | php (7.4) | sw-fw-less (preview) | 25 667 | 31 452 | 31 917 |
124 | ruby (2.7) | hanami-api (0.1) | 25 632 | 28 830 | 27 512 |
125 | ruby (2.7) | roda (3.34) | 24 920 | 28 354 | 27 291 |
126 | python (3.8) | molten (1.0) | 24 593 | 25 860 | 25 732 |
127 | python (3.8) | responder (2.0) | 24 299 | 26 653 | 26 415 |
128 | php (7.4) | chubbyphp-swoole (3.1) | 24 293 | 29 108 | 29 633 |
129 | rust (1.45) | iron (0.6) | 24 175 | 24 341 | 24 340 |
130 | python (3.8) | aiohttp (3.6) | 23 664 | 26 132 | 26 490 |
131 | python (3.8) | clastic (19.9) | 23 404 | 25 203 | 25 107 |
132 | javascript (13.14) | turbo_polka (0.3) | 23 248 | 22 736 | 21 330 |
133 | ruby (2.7) | cuba (3.9) | 23 016 | 25 621 | 25 030 |
134 | python (3.8) | masonite (2.3) | 22 134 | 23 324 | 23 179 |
135 | python (3.8) | flask (1.1) | 22 075 | 23 192 | 23 136 |
136 | go (1.14) | macaron (1.3) | 20 151 | 22 685 | 22 867 |
137 | ruby (2.7) | rack-routing (0.0) | 19 946 | 21 276 | 21 418 |
138 | ruby (2.7) | rack_app (7.6) | 19 540 | 20 793 | 20 692 |
139 | ruby (2.7) | camping (2.1) | 18 954 | 19 060 | 18 964 |
140 | java (11) | blade (2.0) | 15 292 | 17 610 | 17 259 |
141 | dart (2.8) | start (0.4) | 14 224 | 13 842 | 12 911 |
142 | go (1.14) | tango (0.6) | 14 146 | 14 645 | 14 701 |
143 | php (7.4) | spiral (2.4) | 13 078 | 13 360 | 13 230 |
144 | crystal (0.35) | runcobo (0.2) | 13 002 | 12 826 | 11 896 |
145 | ruby (2.7) | sinatra (2.0) | 12 825 | 12 867 | 12 936 |
146 | go (1.14) | gramework (1.7) | 12 526 | 12 784 | 12 771 |
147 | java (11) | struts2 (2.5) | 12 128 | 13 157 | 12 915 |
148 | javascript (13.14) | sails (1.2) | 12 108 | 12 938 | 12 548 |
149 | ruby (2.7) | grape (1.4) | 11 869 | 11 833 | 11 831 |
150 | python (3.8) | quart (0.13) | 11 074 | 11 607 | 10 829 |
151 | php (7.4) | chubbyphp-roadrunner (3.1) | 10 004 | 10 777 | 10 760 |
152 | swift (5.2) | swifter (1.4) | 9 942 | 10 134 | 10 016 |
153 | php (7.4) | sunrise-router-roadrunner (2.4) | 9 743 | 9 456 | 9 244 |
154 | python (3.8) | django (3.0) | 9 480 | 9 487 | 9 184 |
155 | python (3.8) | tornado (6.0) | 9 092 | 9 871 | 9 402 |
156 | python (3.8) | cherrypy (18.6) | 8 804 | 8 765 | 8 703 |
157 | pony (0.35) | jennet (0.1) | 8 516 | 16 636 | 15 585 |
158 | ruby (2.7) | flame (4.18) | 8 030 | 8 004 | 8 002 |
159 | php (7.4) | fastsitephp (1.3) | 7 981 | 7 896 | 7 748 |
160 | ruby (2.7) | hanami (1.3) | 7 803 | 7 920 | 7 958 |
161 | python (3.8) | tonberry (0.2) | 6 888 | 6 603 | 6 290 |
162 | php (7.4) | ubiquity (2.3) | 6 787 | 6 679 | 6 670 |
163 | php (7.4) | one-fpm (2.0) | 6 071 | 5 943 | 5 990 |
164 | php (7.4) | phalcon (4.0) | 5 792 | 5 717 | 5 735 |
165 | php (7.4) | hamlet (3.2) | 5 773 | 5 687 | 5 708 |
166 | php (7.4) | siler (1.7) | 5 685 | 5 636 | 5 658 |
167 | php (7.4) | bearframework (1.2) | 5 142 | 5 097 | 5 129 |
168 | php (7.4) | ice (1.5) | 5 006 | 4 976 | 5 016 |
169 | php (7.4) | sunrise-router (2.4) | 4 945 | 4 913 | 4 946 |
170 | php (7.4) | chubbyphp (3.1) | 4 568 | 4 555 | 4 613 |
171 | php (7.4) | slim (4.5) | 3 819 | 3 843 | 3 895 |
172 | v (0.1) | vape (0.3) | 3 692 | 1 734 | 2 287 |
173 | ruby (2.7) | rails (6.0) | 3 683 | 3 452 | 3 438 |
174 | php (7.4) | nette (3.0) | 3 654 | 3 696 | 3 744 |
175 | php (7.4) | lumen (7.2) | 3 506 | 3 534 | 3 637 |
176 | php (7.4) | yii (2.0) | 3 444 | 3 487 | 3 543 |
177 | julia (1.5) | merly (0.2) | 3 392 | 8 155 | 6 444 |
178 | perl (5.32) | dancer2 (2.0) | 3 235 | 2 528 | 1 359 |
179 | php (7.4) | sunrise-router-annotations (2.4) | 3 130 | 3 177 | 3 226 |
180 | php (7.4) | symfony (5.1) | 2 770 | 2 801 | 2 846 |
181 | php (7.4) | mezzio (3.2) | 2 488 | 2 525 | 2 546 |
182 | python (3.8) | cyclone (1.3) | 2 416 | 2 416 | 2 412 |
183 | ruby (2.7) | pakyow (1.0) | 2 286 | 2 275 | 2 293 |
184 | python (3.8) | klein (20.6) | 1 588 | 1 609 | 1 600 |
185 | python (3.8) | nameko (2.12) | 1 523 | 1 487 | 1 468 |
186 | php (7.4) | laminas (3.1) | 1 339 | 1 358 | 1 380 |
187 | php (7.4) | codeigniter4 (4.0) | 1 039 | 1 059 | 1 122 |
188 | php (7.4) | basicphp (0.9) | 586 | 465 | 548 |
189 | php (7.4) | laravel (7.2) | 320 | 159 | 3 655 |
190 | crystal (0.35) | lucky (0.23) | 306 | 332 | 323 |
原文:https://github.com/the-benchmarker/web-frameworks
本文:
讨论:请加入知识星球【首席架构师圈】或者小号【jiagoushi_pro】
- 118 次浏览
【Web3】什么是Web3?一个新的去中心化网络,或是最新的营销流行语
Web3已成为一种将区块链、智能合约和去中心化应用程序结合在一起的尝试,但其真正的功效难以确定。
Web3,如Web3基金会设想的那样,将是一个公共互联网,数据和内容被注册在块链上,在对等分布式网络上进行令牌化或管理和访问。
Web3有望成为一个去中心化的、不可变的web版本,不存在中间人,并具有与加密货币、不可替代代币(NFT)和以分布式账本(DAPP)为基础的新型去中心化应用程序相同的加密可验证性。
如果所有这些听起来都很复杂,那是因为它是复杂的。就目前而言,Web3是一个有点模糊的概念,它仍然被定义为一个更理想的web外观,而不是一个可供开发人员构建的有形且可访问的技术堆栈。
这种模糊性导致了整个行业在这个术语上的某种程度的分歧,有人称赞Web3是一种革命性的方式,可以让互联网回归自由主义的根基,而著名的怀疑论者,如密码乐观主义者埃隆·马斯克(Elon Musk),则认为Web3是一个“营销时髦词”
Web3的起源
Web3首先由以太坊区块链的创建者之一Gavin Wood描述。在2014年的一篇博文中,伍德设想“Web3.0”是一个加密的在线空间,以应对爱德华·斯诺登(Edward Snowden)2013年披露的全球监控事件所引发的隐私问题。
他写道,在Web3中,“我们假设是公开的信息,我们发布。我们假设是一致同意的信息,我们放在共识账上。我们假设是私人的信息,我们保守秘密,从不泄露。”。
该模型将通过加密技术“在数学上强制实施”,在加密技术中,交易将被验证并添加到区块链中,以实现普遍的透明性和不可撤销性。
在2021年11月的有线采访中,伍德进一步细化了这个定义,以“少信任,多真相”。
Web3这个词直到2021才真正开始流行,然而,当像Andreessen Horowitz合作伙伴Chris Dixon这样的风险资本家开始把Web3作为一个“由建造者和用户拥有的互联网,用令牌编排的时候”。
作为这一领域的大投资者,Andreessen Horowitz在Web3游戏中有着丰富的经验,因此有必要采取一些有益的措施,Twitter联合创始人杰克·多尔西(Jack Dorsey)等批评者非常乐意将Web3称为“一个有着不同标签的中央集权实体”
Web 1.0和Web 2.0
如果Web3是internet的下一个迭代,那么它与Web1.0和Web2.0到底有什么不同?
从1990年到2004年左右,Web1.0是现代互联网的第一次迭代。在Web1.0时代,用户通常使用静态网页,其中只读内容由雅虎和AOL等一小群看门人创建和分发。
Web2.0是一种动态的、交互式的Web,在这种Web中,静态网页由应用程序和用户生成的内容连接在一起。Web2.0由一系列占主导地位的平台统治,比如FAANG公司Facebook(现在的Meta)、亚马逊、苹果、Netflix和谷歌的市场力量,所有这些公司都在一定程度上为个人数据交换服务。
Web3与Web2.0的不同之处在于,它消除了这些强大的把关人,增强了互联网的平等性。在互联网上,用户通过各种代币的所有权获得回报,同时在共享、分布式、防篡改的账本上保持其数据的私密性和安全性。
“本质上,这意味着我个人可以像世界上任何人一样轻松地成为这项整体服务的提供者或共同提供者,”伍德告诉《连线》。
这反映在Web3基金会的任务声明中,通过“去中心化和公平的互联网,用户控制自己的数据、身份和命运”来“培育去中心化式Web软件协议的尖端应用”。
这种想法自然产生了去中心化的自治组织或DAO,它们是高度民主的互联网社区,有着共同的目标,没有领导结构。当然,一个人的平等主义网络可能是另一个人不受管制的野蛮西方。
Web3与Tim Berners Lee早在1999年就将其描述为Web3.0或语义网(semantic Web),后者专注于使互联网机器可读,但这一愿景在很大程度上尚未实现。
Web3堆栈
一些工程师已经尝试定义Web3堆栈,因为它目前存在,但这不是一个简单的练习。
Nader Dabit,前AWS高级开发人员倡导者,现为Web3公司Edge&Node的开发人员关系工程师,试图从高层次概述Web3堆栈,包括:
- 区块链
- 区块链开发环境
- 文件存储
- P2P数据库
- API(索引和查询)
- 身份
- 客户端(框架和库)
- 其他议定书
这显然与当今常用的web开发三层体系结构有很大的不同,三层体系结构包括:
- 前端web服务器
- 中间层应用服务器
- 后端数据库或数据存储
Preethi Kasireddy拥有在风险投资公司Andreessen Horowitz和加密交换Coinbase工作的独特地位。她在Web3体系结构上的博客文章展示了一个开发模型,“您可以编写智能合约,定义应用程序的逻辑,并将其部署到去中心化的状态机[即以太坊区块链]。”
智能合约的编写本身可能需要学习新的编程语言,如Solidity、Vyper,或者越来越多的Rust。然后,您需要了解如何部署到以太坊虚拟机,或您选择的区块链的类似执行机制。
开发人员还需要掌握向区块链“签署”交易的机制,MetaMask将自己确立为这一过程的早期行业标准工具。
接下来,Kasireddy讨论了在以太坊上构建应用程序的扩展问题。她写道:“任何在以太坊上构建应用程序的人都知道,在区块链上存储所有东西会变得非常昂贵,速度非常快。”。这就需要“一个去中心化的链外存储解决方案,如IPFS[星际文件系统]或Swarm。”
随着越来越多的开发人员涌入该领域,以及工具的成熟,这些问题可能会得到解决。像Polygon这样的项目已经在研究区块链缩放问题的解决方案。但就目前而言,在新生的Web3堆栈上开发应用程序看起来很有挑战性。
正如Kasireddy自己所写,“如果所有这些都让你头晕目眩,那么你并不孤单。拼凑所有这些工具是复杂的,可能会带来痛苦的开发体验。但不要担心,我们开始看到新的开发框架,它们真的改善了开发人员的体验。”
Web3应用程序
Web3怀疑论者经常提出的一个问题是,应用程序在哪里?正如上面所概述的那样,随着一个新的、复杂的应用程序栈的出现,我们还没有看到大量杀手级Web3应用程序进入市场也就不足为奇了。
虽然Web3承诺将NFT和加密货币所有权的基础技术带入web的潜在新领域,但这一承诺尚未兑现。
早期的Web3项目往往由它们的不易访问性和微妙的用户体验来定义。要访问大多数Web3应用程序,用户需要一个加密钱包,很可能需要一个新的浏览器,了解全新的术语世界,并愿意支付在以太坊区块链上执行操作所需的不稳定“气体”费用。这些都是普通互联网用户进入互联网的重大障碍。
虽然去中心化的社交网络Mastodon和GitHub clone Radicle是基于一些Web3原则构建的,就像勇敢的浏览器一样,大多数现有的Web3应用程序都是为了交易加密资产或在赌场游戏上下注加密货币。
“如果Web3将成为身份识别或社交媒体的未来,我们需要问问自己,有什么证据表明采用Web3真的比前几代互联网技术更好吗?”Tim O'Reilly问道,他创造了Web2.0这个术语。
Web3基金会在其网站上列出了几个项目,但这些项目主要集中在构建互操作Web3所需的底层协议上。
正如区块链和信任新架构的作者凯文·沃巴赫(Kevin Werbach)对TechCrunch所说:“许多所谓的Web3解决方案并不像看上去那样去中心化,而其他解决方案还没有显示出它们的可扩展性、安全性和可访问性足以满足大众市场的需要。”
Web3入门
准备好开始了吗?您可以在许多地方了解有关Web3核心原则和技术的更多信息。
Edge&Node的Dabit建议从流行的以太坊区块链和用于编写智能合同的Solidity编程语言的文档开始。然后,您可以开始探索Web3工具、库和API的全新世界。你还需要一个加密钱包,比如MetaMask,来管理你的Web3资产。
随着人们对这个术语兴趣的增长,也出现了一些Web3教程,包括Udemy、Coursera、Web3大学、Buildspace等网站上出现的教程,以及通过以太坊网站本身出现的教程。
原文:https://www.infoworld.com/article/3646597/what-is-web3-a-new-decentrali…
本文:
- 48 次浏览
【Web3】我把我的创业公司卖给了 Airbnb,并全力开发 web3——这就是为什么
当我们谈论 web3 时,我怀疑大多数技术人员会立即想到 NFT、加密货币或 DeFi。 我不能责怪他们:从 Twitter 的活动来看,这主要是事实。
但作为开发人员,我真正感兴趣的并不是 NFT、加密货币或 DeFi。事实上,我估计这些项目中有 95% 充其量是高风险的,最坏的情况是骗局——而且绝对没有准备好 供消费者采用。
同时,我也觉得剩下的5%是值得提倡的。 我说作为一个应该成为 Web 2.0 助推器的人:几年前,我的专注于开发人员的初创公司被 Airbnb 收购,如果我选择去,我在传统硅谷框架内的职业选择非常好 那样。
相反,在过去的 6 个月里,我将我的创意和专业精力投入到为 web3 开发人员创建免费的开源工具上:Studio 721
- Studio 721, an open source website for creating and uploading metadata and artwork to IPFS (web3 file hosting), and deploying NFT smart contracts to the Ethereum and Polygon blockchains
我对 web3 作为开发者平台的未来持乐观态度。因为尽管整个平台存在许多缺点,但一些 web3 功能已经令人震惊,对开发人员来说非常好用。以下是其中三个特别适用于基于以太坊的项目的功能:
使用以太坊登录
在以太坊区块链上为应用程序创建注册过程就像添加钱包连接选项一样简单和即时。该系统的构建使登录成为其架构的核心。从隐私和安全的角度来看,泄露个人数据的想法并不存在,因为应用程序只能访问消费者自己已经公开的数据。
将此与创建一个将 Facebook 作为登录选项结合的应用程序进行对比。对于开发人员来说,这是一个耗时的麻烦。你首先需要设置一个开发者账号,同意 Facebook 的服务条款,然后集成官方 SDK。这个过程至少需要几天时间,实际上,您还想为 Apple、Google、Twitter 等添加登录选项。毕竟,如果 Facebook 或其他平台巨头不批准您的应用程序 - 并且围绕它的规则非常模糊——他们可以立即将其关闭。
尽管如此,我们实际上别无选择,只能使用 Facebook 和其他社交网络。实际上,他们已经是我们在现代社会中的许多活动的守门人。尽管 Facebook 不仅“拥有”你的所有数据,而且可以(而且经常拥有!)意外泄露它而几乎没有追索权。以太坊为这一切提供了一个可行的替代方案。
这让我想到了下一点:
全球测试网络
Web 2.0 的另一个挫折是缺乏简单、安全和可互操作的测试网络。假设您正在开展在线零售业务,并且您想制作一个推特应用程序来发布当天的热门商品。您可能希望在构建应用程序时对其进行测试。但是,无法创建虚拟 Twitter 帐户或虚假推文。而且,如果您的应用程序存在错误,则很容易开始使用可能敏感的业务数据向垃圾推文发送垃圾邮件。此示例中的风险很低,因为您的新业务可能没有任何 Twitter 关注者。但是,一旦您的企业收集个人数据或与支付处理器相关联,风险就会变得非常严重;那么一个错误就会威胁到人们的财务或隐私。
即使在 Facebook 和其他科技巨头,这类数据泄露已经经常发生,以至于许多人认为这只是软件开发中不可避免的一部分。一些平台确实支持测试账户,尤其是在风险很高的情况下。但是很多人没有,或者即使他们这样做了,他们也经常受到限制,阻止(例如)Facebook 测试账户和 Stripe 测试账户之间的完全互操作性。
相比之下,以太坊再次闪耀,提供了一个单一的全球网络,区块链上的所有软件都可以无缝通信。作为这种架构的自然结果,可以启动整个网络的测试版本,包括其上的每个应用程序。现在所有结果(包括由错误引起的任何外部性)都可以进行模拟和安全测试。这种力量根本不存在于 web3 之外。它使无畏的发展成为可能。
内置支付处理
最后,支付:作为开发商,收取 IAP 和其他收入一直是一个关键的摩擦点。从理论上讲,集成像 Stripe 这样的处理器应该很容易,但这也意味着设置帐户、签署平台的服务条款、学习其 API 并处理其合规性要求。作为跳过这些障碍的交换,您作为开发人员必须等待付款被处理——但前提是平台首先削减了您的收入。
相比之下,以太坊仍然存在交易费用,但作为开发人员,您通常会在一分钟内收到付款。虽然许多支付处理器并不支持每个国家,但以太坊在全球范围内自动得到支持。是的,交易费用目前可能相当高。但作为开发者,我相信这个可扩展性问题是可以解决的,而不是 web3 的根本缺陷。现实的解决方案正在开发中,就 L2s 而言,已经成为他们自己的创新平台。
—
出于所有这些原因以及更多原因,作为一名开发人员,从 Web 2.0 堆栈转变并尝试这个新环境是非常令人兴奋的。我可以从从事 web3 工作的开发人员那里感受到同样的能量;我会在推特上发布我在业余时间制作的一个有趣的小应用程序,第二天,我的 Discord 中就有 800 人兴奋地谈论它。
这并不是说 web3 是完美的——远非如此。普通消费者还没有为用户体验做好准备,对 NFT 和加密货币的投机炒作只会加剧这个问题,对消费者的采用产生不切实际的期望。
归根结底,对我来说,真正重要的是开发人员的 web3 基础设施。
可以这样想:在 Web 2.0 期间,几乎没有任何消费者将 Linux 作为自己直接个人使用的操作系统。但是对于消费者网络的后台架构和我们现在都认为理所当然的开发人员工具来说,Linux 仍然是不可或缺的。这就是为什么我在未来几年的职业生涯中投资于相信 web3 将沿着类似的路径发展的原因。
原文:https://medium.com/@devinabbott/i-sold-my-startup-to-airbnb-and-went-al…
- 55 次浏览
【Web后端架构】2022年10个最佳Web开发后端框架
这些都是2022年在Java、Ruby、Python、JavaScript、PHP、Scala和Golang中进行web开发的最佳后端开发框架
大家好,如果您是一名应用程序编程人员,正在寻找2022年要学习的最佳后端框架,那么您来对地方了。在前面,我已经分享了最好的全栈开发课程,在本文中,我将与您分享创建web应用程序的10个最好的后端框架。
Web开发通常分为两类:前端开发和后端开发。后端开发人员负责构建web应用程序的服务器端。
当前端部分与用户交互时,后端部分负责内部工作。如果没有后端,前端可能会工作,也可能无法工作,但要创建一个功能齐全的web应用程序,必须有一个与前端连接的适当后端。
后端开发人员的角色不同于前端开发人员。前端开发人员更关注应用程序的外观,而后端开发人员则关注服务器、数据库以及两端的连接和交互方式。
后端开发人员应该具备许多必要的技能。理解后端框架就是其中之一。有几种后端框架,本文将讨论目前使用的顶级后端框架。
该列表包括基于编程语言和技术堆栈的最佳后端框架。这意味着您不需要学习所有这些,而是选择与您选择的编程语言和技术堆栈相匹配的一种。
2022年10个最佳Web开发后端框架
在不浪费任何时间的情况下,这里列出了程序员可以为web开发学习的10个最佳后端框架。虽然在特定的技术堆栈或编程语言(如Java开发人员)中有很多选择,但也有MicroNaut, Quarkus, Vert.X,、 和其他框架,但我只根据受欢迎程度、成熟度和功能选择了最好的框架,比如Spring Boot。
1.面向Java开发人员的Spring框架+Spring Boot
Java被认为是一种常青的编程语言。它的一个常见用途是在后端开发中。Spring框架是最好的Java框架之一。
它于2002年发布,今天,它是最流行的后端框架之一,用于创建基于spring的生产级独立应用程序。
使用Spring boot很容易,因为它省略了Spring框架所具有的各种配置开销。当您使用Spring框架项目(如Spring Boot、Spring Cloud、Spring security等)时,您可以获得许多有用的功能,如缓存、事务管理、配置管理、监控、安全蚀刻。
如果你想在2022年学习Spring框架和Spring Boot,并且需要资源,那么我强烈建议你加入Udemy上的Spring和Hibernate(包括Spring Boot)课程。超过200000名Java开发人员参加了这门课程,学习Spring、Hibernate和Spring Boot,这是Java中最重要的三种后端框架。
2.面向Python开发人员的Django
Django是web开发中最流行的Python框架。Django基于Don't Repeat Yourself(DRY)原则,注重代码重用,从而提高了开发速度。它也是一个非常安全的框架。
作为一个Python框架,Django非常用户友好,易于学习。它也是最流行的全堆栈框架之一,提供了很多现成的功能,比如REST API支持。
如果你想在2022年学习Django,那么我建议你参加由Jose Portilla在Udemy上开设的Python和Django全栈Web开发者训练营课程。
Jose是一位很棒的讲师,这门32小时的Django课程是在线学习Django最全面的资源之一。
3.面向JavaScript开发人员的Express.js
JavaScript是世界上最流行的编程语言。随着节点的出现。js,JavaScript在后端开发社区的受欢迎程度迅速增加,在过去十年中,Node。js已经成为顶级品牌之一。
这就是为什么Express在2010年为节点开发者发布的原因。它是一个极小的节点。js框架用于开发高度灵活的应用程序。
考虑到JavaScript是最流行的web开发语言,而且可能是唯一一种提供从前端到后端到移动学习的全堆栈开发的语言,Expressjs可能是程序员的最佳选择。
如果你想学习表达。2022年的js需要一个资源,我推荐MERN堆栈从前到后:完整堆栈React、Redux和Node。js课程由我最喜欢的Udemy讲师Brad Traversy编写,这门课程非常适合任何想学习使用MERN stack进行全栈web开发的人,MERN stack包括React和Express。js。
4.面向.Net 开发者的ASP.NET core
ASP。NET内核是ASP的继承者。网它是一个开源的、独立于平台的框架,用于在平台上构建web应用程序。NET平台。它被认为是性能最好的后端框架之一。
它基本上是ASP的统一版本。NET MVC和ASP。NET Web API转换为编程模块,从而使其功能更加强大。
如果你想学习ASP。NET核心,并需要一个资源,然后我建议你加入完整的ASP。网络MVC 5课程由Mosh Hamedani在Udemy上完成。这门7.5小时的课程最适合C#and。NET开发人员谁想要学习ASP。网
C#开发者的最佳后端框架
5.面向PHP程序员的Laravel
PHP是后端开发中使用最多的编程语言之一。Larvel是一个基于MVC架构的PHP框架。它是一个初学者友好的框架,具有用户友好的界面、广泛的库和出色的API支持。
使用Laravel可以简化后端开发,同时构建现代安全的web应用程序。如果您正在处理非平凡的应用程序,这一点尤其正确。
如果你想学习幼虫,并为PHP开发者寻找学习Laravel的最佳在线课程,那么你也可以通过Laravel为初学者查看PHP——成为Udemy上Laravel课程的大师。这门43小时的课程是在线学习拉拉瓦尔的最佳课程之一。
6.面向Ruby程序员的Ruby on Rails
Ruby on rails,俗称rails,是一个基于MVC架构的服务器端框架。这是一个初学者友好的框架,易于理解和学习。
然而,它的优点和缺点仍然存在争议,但它因其用户友好而受到世界各地后端开发人员的喜爱。
如果你想在2022年学习Ruby on Rails并需要资源,那么Rob Percival在Udemy上的完整Ruby on Rails开发人员课程是一个很好的课程。它有超过46小时的内容和157篇文章,8个编码练习和1个可下载资源。
Ruby开发者的最佳后端框架
7.面向Golang开发者的Fiber 框架
Fiber是Golang程序员的web开发框架。它构建在Fasthttp之上,Fasthttp是Go最快的HTTP引擎。它的设计目的是在考虑到零内存分配和性能的情况下简化快速开发
Fiber是一个精简但功能极其强大的框架。由于极度精简,很容易将第三方库与马提尼酒整合在一起。该框架提供了更多的可伸缩性和更少的开销问题。
它类似于Express。js和js对路由、服务静态文件、构建restapi、灵活的中间件支持、模板引擎和低内存占用有很好的支持。
如果你想了解更多关于使用Golang和Fiber构建web应用程序的知识,我建议你加入Rob Percival和CodeStars在Udemy上的完整React&Golang课程。这是一门基于项目的课程,你将使用React和Golang Fiber从头开始构建一个管理应用程序。
8.面向PHP开发人员的CakePHP框架
CakePHP是另一个PHP框架,它基于另一个流行的后端框架Ruby on rails的概念。
它遵循MVC体系结构,并提供了一些有用的功能,如模型视图控制器、前端控制器、活动记录、数据映射和配置约定。
如果你想从头开始学习CakePHP,并且需要一个资源,我建议你在Udemy上查看CakePHP for初学者,以完成项目课程。这门6小时的课程是Udemy上评分最高的CakePHP课程,适合初学者和经验丰富的PHP程序员。
最佳PHP后端框架
9.Python开发人员的Flask框架
Flask是另一个可用于后端开发的python框架。它被认为是一个微框架,因为它省略了特定工具和库的使用。此外,没有数据库抽象层、表单验证或外部源依赖。
它是一个简单、高度灵活、高性能的web框架。作为一个轻量级的框架或微框架,它很容易学习和理解。此外,作为一个Python框架,它非常用户友好。
如果你想从头开始学习Flask,并且需要一个资源,那么你也可以在Udemy上通过Flask和Python课程查看REST API。由Jose Salvatierra创建,这门17小时的课程非常适合学习web开发。
最佳Python后端框架
10.Scala开发人员的Play框架
PLAY是用Scala和Java编写的,是最强大的后端web框架之一。它基于MVC架构,提供各种功能,如热代码重新加载、显示程序错误,以及专注于提高开发人员的盈利能力。
此外,它是RESTfull和非阻塞的。
如果你想学习Scala并开始使用Play Framework进行web开发,并且需要一个资源来开始在线学习,那么我建议你加入Scala&Functional Programming for初学者|在Udemy上学习JVM课程。
这是一门在线学习Scala的好课程,是Play框架所必需的,已经有超过3万名学生加入了这门课程。
用于web开发的最佳Scala后端框架
这就是程序员在2022年能学到的最好的后端框架。正如我所说,如果你想成为一名完整的堆栈工程师或后端工程师,学习后端开发是值得的。如今,后端开发依赖于后端框架。
有几种后端框架,有些流行,有些不流行。在本文中,我们讨论了顶级后端框架。所有这些框架在后端社区都非常流行,在就业市场上对它们有巨大的需求。
大多数框架都是用流行的编程语言编写的,比如Java、Python和JavaScript。如果您是初学者,可以从Ruby on rails等对初学者友好的框架或Flask等轻量级框架开始。如果你热衷于学习最受欢迎的,那么你可以选择Spring Boot、Django或Express。
原文:https://medium.com/javarevisited/10-best-backend-frameworks-for-web-dev…
- 616 次浏览
【Web应用】2023年主导的Web开发趋势💥[更新]
视频号
微信公众号
知识星球
“弄清楚下一个大趋势是什么,告诉我们应该关注什么。”-马克·扎克伯格
多年来,Web开发已被证明是任何组织/企业成功的不可或缺的因素。它是您业务的窗口,有助于您在全球范围内发展业务。
现在,Web开发世界在不断变化,趋势也在不断变化。有时,这些趋势的变化速度远远超过了它们的使用速度。为了保持领先,有必要关注正在流行的最新趋势、更新、技术和方法。此外,了解趋势并控制周围发生的事情对于web开发来说是非常重要的。
在分析了2023年网络发展趋势列表中各行业的趋势后,我们收集了一些趋势。在接下来的一年里,您将了解值得您关注的顶级网络技术堆栈。
我们列出这个列表是为了节省时间,并帮助您在即将到来的时代发展业务。所以,让我们开始吧。
1.单页应用程序
都在一个地方。无需切换。无需等待。。!!
嗯,当一次点击就可以从服务器上下载一个网页时,这有点令人恼火。它还会消耗你的时间,也会给用户留下不好的影响。这种情况导致了SPA的趋势。
SPA是近年来的趋势之一,有助于避免与服务器的长时间通信。它提供了更好的页面性能,并提供了高级别的数据保护。
因此,单页应用程序是一种在浏览器内工作的应用程序,在使用过程中不需要重新加载页面。您每天都在使用这种类型的应用程序。例如,Gmail、谷歌地图、Facebook或GitHub。
图片来源:Pinterest
随着2023年JavaScript框架的兴起,SPA将使用这种语言进行内容加载,这种趋势不会很快消失。此外,这些应用程序不需要额外的等待时间,为用户提供即时反馈。此外,它还提供了一些惊人的优势。
优点:
- 原生浏览器环境中出色的UX/UI
- 节省的带宽
- 使用Chrome工具更轻松地设置和调试
- 对API的关注
- 缓存过程的效率
单页应用示例:
例如,您可以查看Sneat Bootstrap 5 HTML管理模板。它是最新的对开发人员最友好的🤘🏻 & 高度可定制✨ 基于Bootstrap 5的管理面板模板。
此外,最高的行业标准被认为会为您带来最好的引导程序管理模板,不仅速度快🚀易于使用,但具有高度可扩展性。
此外,Sneat Bootstrap 5管理模板功能强大,还允许您构建任何类型的web应用程序。例如,您可以创建:
- SaaS平台
- 项目管理应用程序
- 电子商务后端
- CRM系统
- 分析应用程序
- 银行应用程序
- 教育应用程序
- 健身应用程序及更多…。
此外,您可以使用这个最好的创新Bootstrap管理员来创建引人注目、高质量和高性能的Web应用程序。此外,您的应用程序将完全响应,确保它们在台式机、平板电脑和移动设备上看起来令人惊叹,功能完美。
特点:
- 基于Bootstrap 5
- 垂直和水平布局
- 默认、带边框和半暗主题
- 支持明暗模式
- 国际化/i18n和RTL就绪
- 布局生成器
- 主题配置:轻松定制我们的模板。
- SASS供电
- 全响应布局
- 清除注释代码(&C)
- 记录良好
- 享受无忧支持
也可在React Dashboard版本中获得。
2. Progressive Web Apps (PWA)
Image credit: Pinterest
你拇指上的一切…!!我们知道这个概念,对吧?导航方便,工作平稳,装载量少,速度快。是的,这就是PWA,即Progressive Web App的意义。。!!快速、可靠、流畅的用户友好体验。
毫无疑问,PWA并不是什么新鲜事。尽管如此,自从它来到这里以来,它一直是这个小镇的话题,而且它也将在2023年占据主导地位。现代开发商和投资者之所以瞄准它,是因为它能够提供高质量的用户体验。由于它们对移动非常友好,毫无疑问,对PWA的需求将增加。
好吧,Progressive Web Apps(PWA)是用现代API构建和增强的,以提供增强的功能、可靠性和可安装性,同时使用单个代码库访问任何设备上的任何人、任何地方。这使得它适合跨设备兼容性。
它们不仅快速、即时加载,还允许用户离线使用服务,同时执行本地应用程序的所有功能。
优点
- PWA有助于提供流畅且用户友好的网络体验。
- 有助于降低反弹率
- 脱机工作,因为它独立于连接
- 使您的应用程序可靠、功能强大且可安装
使用PWA的一些服务示例:
3. 人工智能开发
根据报告“2019年人工智能市场的价值将达到399亿美元,到2025年可能达到1906.1亿美元,预测期内的复合年增长率为42.2%。”这是一个相当大的数字…!!不是吗?😮
现在,如果网站的内容或布局不吸引人,大约38%的人会跳过网站。超过75%的客户根据网站设计来决定企业的信誉。用户只需0.5秒就可以形成对网站的看法。从那时起,技术介入网络开发的世界是至关重要的。在这里,人工智能进入了2023年的网络发展趋势。
将人工智能应用于网络开发可以帮助您预测客户的选择,从而塑造网站的整体外观。AI让你知道,
- 你的客户下一步打算买什么?
- 客户决定购买产品的理想时间范围。
- 什么样的产品最吸引顾客?
- 哪些产品不受客户欢迎?
- 谁都有兴趣购买你们的产品?
我们生活在一个数字时代,众所周知,即将到来的时代将在各个领域采用更智能的方法。这意味着更少的手动操作。例如聊天机器人、增强现实/虚拟现实。说到这里,许多初创企业、中小企业和大型组织已经在寻求为其业务雇佣人工智能开发人员,以通过人工智能应用程序、软件和工具满足最终用户的未来需求。
Sketch2Code是一种基于web的解决方案,最近推出。它依靠人工智能将手写UI设计从图像转换为有效的HTML标记代码。
许多用户甚至对人工智能在某种程度上参与大多数网站并不感到惊讶。它在网络开发领域创造了一个新的维度。
4. WebAssembly
在开发web应用程序时,首先不能忽视的是性能。现在,JavaScript的限制使繁重的计算变得缓慢。这大大恶化了用户体验。
这是WebAssembly日益流行的主要原因之一。有了这项技术,任何编程语言中的代码都可以编译成在浏览器中运行的字节码。
WebAssembly是一个开放标准,它为可执行程序定义了一种可移植的二进制代码格式,以及相应的文本汇编语言,以及用于促进此类程序与其宿主环境之间交互的接口。
优点:
- 它独立于编程语言和平台
- 独立于硬件
- 快速执行代码
- 提供高级别的安全性。
这一切都归结为三角关系——占地面积小、跨平台脚本和速度。这三个组件都是现代web开发中的关键组件。这就是为什么今天大部分的web开发案例都没有WebAssembly的原因。
For more info check Webassembly concepts
5. 语音搜索和导航
Credit Giphy
嘿Siri…。今天的趋势是什么?
我们人类一天比一天懒…不是吗。。!!?😂 首先,我们制造了手机,然后开发了智能手机来减少体力劳动,现在我们正朝着“语音免提”的方向发展。面向命令的服务是当今的热门话题。
语音搜索和导航将成为2023年最主要的网络发展趋势之一。它们已经成为我们日常生活的一部分。它们非常棒,因为它们让我们搜索信息、做笔记等等的方式变得很简单。
苹果、安卓和谷歌等品牌在其用户界面/用户体验设计过程中实现了这一趋势。因此,这种迅速出现的趋势不容忽视…!!
谷歌报告称,全球27%的在线人口在手机上使用语音搜索。
Voicebot.ai报告称,超过一半的成年人使用过语音搜索,2019年初,33%的人每月使用语音搜索,高于2018年的25%。
通过在web开发中实现语音搜索和导航,您可以确保您的服务提供令人惊叹的用户体验。
- 语音用户界面根据以下算法工作:
- 语音识别软件将输入的模拟波转换为数字格式
- 音频输入被分解为单独的声音、音素
- 该软件分析每一个单词,并将它们与字典中的单词进行比较
- 语音转换为屏幕文本或计算机命令
优点:
- 易用性
- 允许徒手交互
- 任务执行速度快
- 出色的用户体验
- 直观方便
人们喜欢花更少的时间键入命令。这就是他们越来越多地使用语音搜索的原因。因此,网络开发将与此类功能的实现密切相关。
6. 本地应用程序将主导市场
如果你正在工作,突然出现网络问题怎么办。。!!一直依赖互联网有点烦人。这就是Native应用程序的用武之地。它还允许您离线工作。
原生移动应用程序是直接安装在智能手机上的应用程序,在大多数情况下,根据应用程序的性质,它可以在没有互联网连接的情况下工作。
好吧,原生应用程序开发已经很有需求了,未来,软件开发人员可以预计这一趋势将主导2023年的网络开发趋势。
由于它们是为特定平台专门设计的,与混合应用程序相比,它们通常提供更好的用户体验和更强大的性能。这就是为什么,越来越多的企业正在投资于iOS和Android的多个本地应用程序,以使他们能够为用户提供更好的体验。
优点:
- 由于使用了底层设备的功能,因此具有广泛的功能
- 更好地与操作系统的用户体验相匹配的UI
- 响应迅速
- 提醒推送
- 通过应用商店中的评级进行质量保证。
随着智能手机在市场上越来越受欢迎,iOS和Android操作系统在市场上的主导地位越来越大,对本地应用程序开发的投资似乎不会很快减少,事实上,在2023年或可预见的未来都不会减少。
7. Motion design UI
Credit: Vuexy By Anand Patel on Dribbble
运动设计是2023年网页设计和网页发展的主要趋势之一。极简主义的设计加上复杂的互动,给人一种引人入胜的互动外观,吸引了用户的注意力。
顾名思义,动作设计就是设计动作。然而,它远远超出了在构思后简单地为屏幕元素设置动画的范围。
必须在UI/UX设计阶段就已经考虑到动作设计。这背后的原因是,仅仅拥有一个包含动画的屏幕就有点达不到预期目标:添加的动作必须有意义和目的。
由于运动设计,这些元素将被实现:
- 页眉转换;
- 图表
- 弹出窗口
- 下拉菜单
- 滚动
所有这些都将帮助你展示你的独特风格,娱乐用户,改善行为因素,并帮助你的网络应用程序在搜索结果中排名更高。
在AMP的帮助下,可以在不损失下载速度的情况下实现动作设计。因此,如果你想让用户停留在你的页面上,那么不要忽略这个…!!
优点:
- 定义你的结构和互动
- 简化导航
- 减缓加载时间
- 增加会话长度,从而降低反弹率
- 为了提高参与度并为网络应用程序的用户提供更好的UI/UX,请尝试使用动作UI技术进行升级。
通过动画引导用户浏览您的应用程序,演示下一步要采取的步骤;
通过朗朗上口的动画对用户的手势做出反应;
演示应用程序不同组件之间的关系等。
8. 无服务器架构
Credit: Runcloud
运动设计是2023年网页设计和网页发展的主要趋势之一。极简主义的设计加上复杂的互动,给人一种引人入胜的互动外观,吸引了用户的注意力。
顾名思义,动作设计就是设计动作。然而,它远远超出了在构思后简单地为屏幕元素设置动画的范围。
必须在UI/UX设计阶段就已经考虑到动作设计。这背后的原因是,仅仅拥有一个包含动画的屏幕就有点达不到预期目标:添加的动作必须有意义和目的。
由于运动设计,这些元素将被实现:
- 页眉转换;
- 图表
- 弹出窗口
- 下拉菜单
- 滚动
所有这些都将帮助你展示你的独特风格,娱乐用户,改善行为因素,并帮助你的网络应用程序在搜索结果中排名更高。
在AMP的帮助下,可以在不损失下载速度的情况下实现动作设计。因此,如果你想让用户停留在你的页面上,那么不要忽略这个…!!
优点:
- 定义你的结构和互动
- 简化导航
- 减缓加载时间
- 增加会话长度,从而降低反弹率
为了提高参与度并为网络应用程序的用户提供更好的UI/UX,请尝试使用动作UI技术进行升级。
- 通过动画引导用户浏览您的应用程序,演示下一步要采取的步骤;
- 通过朗朗上口的动画对用户的手势做出反应;
- 演示应用程序不同组件之间的关系等。
9. 持续集成和部署
我们生活在一个数字时代,在这个时代,速度和连续性在任何行业和业务中都扮演着残酷的角色,网络开发世界也不例外。事实上,在网络开发的世界里,提供快速、流畅和更新的服务是最重要的。
定期修复、更新、改进UI/UX等错误的频率使您的web服务负责任,成为用户的最爱。此外,大多数现代应用程序都需要在不同的平台和工具中开发代码,团队需要一种机制来集成和验证其更改。它会影响您的整体业务。因此,持续集成和部署在网络世界中至关重要。🧐
好吧,持续集成是指尽早自动测试对代码库所做的每一项更改的过程。Continuous Deployment遵循Continuous Integration过程中发生的测试,并将更改推送到暂存或生产系统。而持续交付意味着应用程序进入生产环境进行彻底测试,并且持续部署会自动更新它们。
优点:
- 较小的代码更改更简单(更原子化)
- 故障隔离更简单、更快。
- 产品通过快速功能介绍快速改进
- 平均解决时间(MTTR)更短,因为代码更改更小,故障隔离更快。
- 可测试性由于较小的、特定的更改而提高。这些较小的变化允许更准确的阳性和阴性检测。
- 检测和纠正生产逃逸所花费的时间更短,发布速度更快。
有了这样的优势,难怪会有更多的软件开发公司在他们的工作中采用这种方法。因为它是网络开发世界中最关键的方面。
10. 云技术将继续存在
Created by Taras Ivanytskyi on dribbble
你知道吗,2020年,云服务行业创造了近400亿美元的收入,使其市值从2019年的2278亿美元增至2664亿美元,在短短一年内增长了17%?
有了这些数字,有一点是肯定的;在web开发服务中,云技术是王者。2023年,由于云技术,预计谷歌、微软和亚马逊等云供应商将继续赚得盆满钵满。
这一热门趋势背后的原因是,云上有几家公司,预计还会有更多的公司搬到那里。从小型创业公司或跨国公司,到多个行业的企业,从医疗保健到情报机构,甚至政府组织,各种规模的企业都将转向数据存储的云提供商。
Credit: Cloudopedia
Following are some cloud services.
- AWS Lambda
- Google Cloud Functions
- Azure Functions
- IBM OpenWhisk
- Alibaba Function Compute
- Iron Functions
- Auth0 Webtask
- Oracle Fn Project
- Kubeless
云技术在数据存储方面提供了强大的安全性、可扩展性和灵活性,考虑到可能导致安全漏洞的黑客威胁一直存在,组织将希望确保采取可衡量的措施来保护其敏感数据,避免数据泄露和其他可能损害数据隐私的活动。
11. 5G技术将无与伦比
Credit: Rpc senate
5G是第五代蜂窝网络,它带来了新的功能,将为人们、企业和社会创造机会。即使在网络开发领域,5G也占据主导地位。由于需要增强服务,因此必须加快连接速度。
2023年,软件开发商可以预计5G技术将作为一种趋势回归,但这一次的理由是正确的。随着新射频的使用和速度达到100 Gbps,5G技术的速度大约是4G网络的100倍,软件开发行业的专家预测,未来五年,它将引领全球14亿台设备的标准数据传输。
优点:
- 变速箱速度更高
- 减少延迟
- 连接的设备数量更多
- 网络切片
- 增强的容量
- 可用性和覆盖范围
Credit: By Kate mangostar on Freepik
5G技术是为增强现实、虚拟现实和4K视频流等计算密集型应用程序设计的。对创建能够提高业务性能的设计和功能感兴趣的软件开发人员会发现这项技术特别有用。
随着更好的数据传输和更快的网络的承诺,5G技术无疑是2023年最佳网络发展趋势之一的有力竞争者。
12. Mixed Reality (MR)
AR/VR市场(即混合现实)将从2017年的10.54亿美元增长到2024年的99.82亿美元,2018-2023年的复合年增长率将达到73.2%。
混合现实(MR)是增强现实(AR)和虚拟现实(VR)的结合。它在企业应用程序中发挥着关键作用。AR将数字内容与用户的物理环境相结合,而VR则为用户创造了身临其境的体验。
在这项技术的帮助下,Web应用程序开发、国防、旅游、建筑、建筑、游戏、医疗保健等许多领域的组织正在实现关键的商业价值。
MR如何扭转局面:
- 企业、政府组织,甚至非营利组织都可以使用AR和VR来培训员工从事复杂的工作。
- 在物理环境上实时覆盖数字信息的能力为增强现实在商业中创造了许多可能性。
- 媒体和娱乐企业正在以一种重要的方式使用AR和VR,为消费者提供引人入胜的内容。
- 企业可以使用AR和VR让潜在客户虚拟地“试用”他们的产品,这增加了他们的销售努力。
MR在网站上的体验将在未来几年不断增加。想想宜家这样的家具网站,展示你房间里的家具。。!!
13. Blockchain: 增强企业解决方案的透明度、效率和安全性
比特币问世时有点革命性…!!它在某种程度上震撼了市场,也为网络开发世界打开了一扇新的大门。Cision PR Newswire的一份报告估计,2025年区块链技术市场将达到576.41亿美元,2019年至2025年的复合年增长率将达到69.4%。
比特币和以太币等加密货币让我们注意到了区块链,这项技术对企业系统有着广泛的影响。基本上,区块链是一个对等(P2P)网络,它提供去中心化、分布式账本、透明和安全功能。
在区块链网络上运行的智能合约是不可篡改和透明的,因此,它们是安全和值得信赖的。此外,它们的执行是不可逆转的,这使得合同管理更容易。
优点:
- 权力下放
- 不可变性
- 安全
- 透明度
- 保护物联网(IoT)
Check “Benefits of blockchain for identity management” for more information
几个区块链用例示例:
- 改善供应链管理
- 身份管理:区块链可以通过其防篡改数据和分布式账本来改进“身份和访问管理”(IAM)流程。
- 更好的分析:区块链提供了不变性和审计跟踪,从而确保了正确的数据质量。这可以帮助所有行业的企业,因为他们可以根据可靠的数据运行分析工具。
- 去中心化颠覆:以太坊和NEO等区块链平台使企业家能够构建“去中心化应用程序”(DApp),因此,他们可以颠覆现有的中心化商业模式。
- 保护物联网(IoT):物联网具有巨大的潜力,因为它可以从数十亿个传感器和设备中获得见解。尽管如此,物联网仍面临风险,因为设备之间的整个通信都是通过互联网进行的。区块链可以通过其强大的安全功能来保护这种通信。
14. IoT: 物联网
Credit: By vectorpouch on Freepik
根据研究,“到2020年,将有超过200亿台物联网设备活跃并连接到互联网”。真是令人印象深刻…!!😎
我们希望一切都更智能。无论是智能手机还是灯泡。。!!你看,对智能设备的需求与日俱增,而且不会停止。而且,多亏了互联网,智能生活正在成为现实。你所要做的就是命令…!!是的,这就是我们喜欢的东西。😁
嗯,没有互联网的生活是不可能想象的,因为现在世界的每一个方面都被连接起来,使一切变得方便、简单和智能。因此,为了让世界更加互联,物联网的出现是一件幸事。
物联网是指能够与其他设备和网络通信的广泛的互联网连接设备。它们可以执行各种功能,但最常用于收集信息和执行特定操作。你会惊讶地发现,每秒钟都有127台设备连接到互联网。
优点:
- 改进监控
- 预测和行动的新能力
- 增加客户对话
- 微调服务和产品
- 改进对操作过程的控制
如今,互联网让生活变得更加轻松,但也让世界变得越来越小。由于不断增强的功能,互联网现在已经成为我们丰富生活的系统和日常生活的一部分。
Iot肯定会在很长一段时间内统治网络开发世界…!!
15. MEAN-React-Vue-Flutter
如果没有一定的技术堆栈,上面列出的大多数趋势都无法实现。但现在有数百个框架和库。它们中的哪一个最常使用?
MEAN是一个免费的开源JavaScript软件栈,用于构建动态网站和web应用程序。由于MEAN堆栈支持程序的所有组件都是用JavaScript编写的,因此MEAN应用程序可以用一种语言编写,用于服务器端和客户端执行环境
MEAN堆栈是最受欢迎的堆栈之一。它包括:
- MongoDB——一个数据库
- React.js——一个web框架
- Vue.js——一个前端框架
- Angular——一个前端框架
- Flutter for web
React库起着重要作用。它经常被用作MEAN堆栈中Angular的替代品。它能够更快、更容易地实现更改,因此越来越多的开发人员选择它而不是Angular。
- React是一个开源的前端JavaScript库,用于构建用户界面或UI组件。它由Facebook和一个由个人开发者和公司组成的社区维护。
您可以查看Sneat MUI React NextJS管理模板和Vuexy React管理模板。这些是最好的React管理模板,用于构建强大而令人敬畏的web应用程序。
您可以查看ReactJS开发人员路线图,了解如何使用ReactJS的详细概述。除此之外,还有一篇关于最佳ReactJS UI框架和组件库的文章会很有帮助。
在小米和阿里巴巴等巨头转向Vue后,Vue已经在全球市场上站稳了脚跟。这是一个非常紧凑的前端解决方案。
您可以查看使用VueJS制作的Materio Vuetify VueJS管理模板。
- Vue.js是一个开源的模型-视图-视图模型前端JavaScript框架,用于构建用户界面和单页应用程序。
此外,还有一篇关于Trending VueJS UI组件库和框架的详细文章,您会发现这些文章很有用。
- Angular是一个基于TypeScript的开源web应用程序框架,由谷歌的Angular团队以及个人和企业社区领导。
为了获得更好的外观,请查看使用Angular制作的Apex Angular Admin Template
最后一个流行的解决方案是Flutter for Web。Flutter是一个开源的用户界面软件开发工具包,由谷歌创建。它用于从单个代码库为Android、iOS、Linux、Mac、Windows、Google Fuchsia和web开发应用程序。它有助于在任何设备上提供高质量的网络应用程序,因此在网络开发人员的评分中名列前茅。
16. API-First Development
Credit: Freepik
API-first方法意味着对于任何给定的开发项目,您的API都被视为“一流公民”。它满足用户的需求。使用这种方法,开发人员可以在用户访问网站时同时执行任务和推送代码。
它还降低了开发费用并提高了交付速度。此外,这种方法还允许新网站成为内容传递源。这种互连性允许开发人员将外部产品添加到他们的应用程序中,以帮助增强他们的功能和性能。
新的物联网设备、软件工具和网络接口都包含API,允许它们相互连接,并为用户提供无缝的数字体验。
以前,许多开发人员可能首先关注该产品,然后将其需要连接到其他设备的事实视为事后考虑。现在,API成为焦点,并成为公司寻找增加与用户互连性的方法的中心。
优点:
- 开发团队可以并行工作
- 更快的上市时间
- 降低开发应用程序的成本
- 良好的客户体验
- 降低故障风险
17. Accelerated Mobile Pages (AMP)
AMP类似于PWA,是web开发中的新兴技术之一。随着互联网越来越以用户为中心,这是一种新的网络发展趋势。因此,许多品牌选择创建本地应用程序来改善用户体验。此外,AMP插件还可以帮助企业节省用户体验费用并接触目标用户。
AMP背后的目的是为用户提供无缝的页面体验,让他们沉迷于网站。不同的功能是谷歌和推特最近开发的开源插件。
AMP是经过优化的页面,可快速加载和操作。此外,它们有一个方便但基本的设计,不同于全范围的网络产品。页面变得便于移动,并增加了内容的可读性。AMP等插件允许不同网速的用户访问页面,并使互联网更加用户友好。
它使公司能够节省用户体验的成本,同时提高其可达性。此外,以下是AMP带来的一些优势。
优点:
- 提高网站参与度
- 提高排名和流量
- 较低的反弹率
- 增加了广告浏览量
- 点击率更高
18. Dark mode:
Credit: Materio Vuetify VueJS Admin Template
嗯,暗模式、夜班和其他弱光用户界面选项是一种趋势,主要是因为它们为用户提供了一个低对比度的网站或应用程序,在弱光环境中更容易查看。
此外,黑色是一种特别强烈的颜色,它会打击人们的强烈情绪,如果使用过量,很容易压倒个人。当您想要突出显示特定类型的内容时,暗模式特别有用。
此外,Spotify、Netflix和Steam都是在黑暗模式下设计的。与此同时,谷歌开始提供暗模式,Instagram、Twitter、苹果和安卓是提供暗模式的一些最受欢迎的应用程序和服务。
根据安卓管理局的评估,81.9%的受访者接受在设备上使用暗模式,9.9%的受访者在亮模式和暗模式之间切换。Polar的另一项调查显示,95%的用户喜欢使用暗模式而不是亮模式。
这一趋势的主要原因如下:
- 它给人一种超现代、酷炫的外观
- 它节省了设备电池电量(在OLED/AMOLED屏幕的情况下)
- 它允许弹出和突出显示其他设计元素
- 它可以减少弱光条件下的眼睛疲劳。
尽管如此,我们不应该忽视这样一个事实:任何过量的东西都是有害的。所以,小心使用它…!!
19. Cybersecurity
这是2023年以及未来最重要的趋势之一。随着新兴技术和对技术的依赖,网络安全威胁的风险正在增加。2020年10月,黑客用恶意软件攻击了德国第二大软件供应商software AG,要求赔偿近2000万美元。
根据Cloudflare的数据,2020年分布式拒绝服务(DDoS)攻击的数量每个季度都翻了一番。在DDoS攻击中,黑客会用大量恶意互联网流量淹没目标服务器。这可能会导致数小时的停机时间,也可能导致收入损失。
客户已经认识到这一新出现的威胁,并希望公司采取强有力的安全措施,确保其个人信息的安全。这就是为什么,保护您的公司和客户信息将是2023年的优先事项,尤其是在网络攻击持续增加的时候。
以下是您可以采取的一些步骤:
- 进行脆弱性评估
- 保护您的远程访问
- 引入角色、权限和多因素身份验证
20. Push Notifications
推送通知是由移动应用程序发送到用户设备的小型弹出消息,即使在应用程序未打开时也会出现。这些通知旨在吸引注意力,并可以传达提醒、更新、促销等信息。
它们可以提供实时更新,或通过必要的细节和推送通知来吸引用户的注意力,例如提醒用户重新访问最近查看的项目或新的博客文章。
推送通知是与受众沟通的好方法。根据Localytics的一项研究,52%的智能手机用户在他们的设备上启用了推送功能,这些通知可以通过以下方式为企业带来好处:
- 引导用户访问您的社交媒体渠道
- 推广产品和服务(特别是特惠)
- 通过提供有价值的内容建立信任和品牌声誉
- 吸引当前不在您网站上的用户
- 恢复电子商务应用程序的废弃购物车
以下应用程序使用此技术来提高销售额:
21. No-Code/Low-Code Apps
无代码/低代码的平台使web开发更加容易。这些工具使用户能够利用预先制作的代码块和管理模板创建软件解决方案。
简言之,它们是可视化的软件开发环境,使开发人员能够拖放软件组件,连接它们,并创建移动或web应用程序。这些平台经常与它们所代表的开发方法互换使用。
因此,它减少了编码所需的时间和精力。此外,无论技术经验如何,任何人都可以开发软件和高需求应用程序。这些工具还使公民开发人员更容易构建有用的应用程序,而无需编写复杂的代码。
以下是您可以使用的一些无代码开发工具。
For more such, No code tools refer to the collection: Best No Code Development Tools
22. Personalization
根据访问者的需求和特点,不同版本的网页会显示给不同的访问者。可以对登录页进行个性化设置,以共享专为特定访问者量身定制的信息。事实上,客户渴望个性化。这就是让他们保持投入和忠诚的原因…!!
因此,个性化正在为2023年的网络开发设定基准。您可以通过多种交互方式向您的网站访问者提供个性化权限。例如,您可以允许用户手动或通过他们的反馈设置他们的首选项。除此之外,你还可以授权他们根据他们的意愿更改你的网站外观,这样他们会感觉更紧密。
机器学习的内容个性化,也称为预测性内容个性化,是一种先进的人工智能驱动方法,可以向每个用户动态显示最相关的内容。
23. CRM Integration
客户关系管理(CRM)系统帮助企业管理与当前和潜在客户的关系和互动。在其潜力中,许多企业没有将其纳入其网络解决方案,从而错过了好处。
通过连接您的网站和CRM,您将能够增强客户体验。通过这种方式,您将能够在客户消费旅程的每个阶段为其提供帮助。此外,您将通过消除手动复制和粘贴或导出和导入的需要来节省管理费用。这会提高你的工作效率,让你的日程安排更加专注于其他目标。
以下是提供CRM集成的一些服务和应用程序:
24. AI & Machine Learning
几年来,人工智能一直在渗透我们的数字生活,但随着越来越多的公司投资于技术,我们可以期待在可能的方面取得重大进展。特别是自然语言处理(NLP)和机器学习(ML)已经从理论概念发展到了成熟的应用。
例如,“YouTube使用NLP从视频音频中自动创建字幕,或者谷歌分析使用ML更好地了解网站用户的行为,或者Alexa使用VR和ML,这样我们就可以与机器进行完整的对话。”
机器学习(ML)用于网络开发,为用户提供改进的体验。开发人员使用ML使软件能够分析传入数据、检测模式和个性化内容。许多领先的公司都使用这项技术来增强用户体验。例如,Airbnb和Netflix使用ML为用户定制搜索结果。
结论
好吧,正如这里谈到的2023年的网络发展趋势一样,很难追逐它们,因为它们变化如此之快。但是,我们必须控制住它们,并尝试它们。。!!
通过跟踪网络开发的最新趋势,您可以用世界级的用户体验来安抚用户,提高您的网络应用排名,并为您的服务打开新市场…!!
通过使用这些趋势,您将能够为您的网络应用程序的用户提供令人惊叹的体验。此外,你将增加成为他们首选的机会!
一般来说,每一个网络发展趋势都值得你关注。其中一些可能会持续到未来十年,例如物联网、语音机器人、MR、ETC。而其中一些将在几个月内成为常态。
所以,不要犹豫,尽快在下一个项目中开始实施它们。
一定要告诉我们你最喜欢的一个,如果我们在这里错过了,我们很乐意听到。发展快乐。🤩
- 74 次浏览
【Web应用技术】GraphQL性能提示:从数据库请求中选择字段
GraphQL的真正威力…从客户端请求一直到数据库指定所请求的字段。
问题
GraphQL最强大的特性之一是客户端可以指定从响应返回的字段,从而减少通过网络传输的数据,从而避免过度获取数据。
然而,我们真的做了更少的工作吗?后端服务器和数据库仍然必须执行查询数据库的所有工作,获取被请求对象的所有字段,然后仅通过GraphQL/Network层返回被请求的字段。
因此,通过发送较小的响应大小,我们只是节省了网络时间,但我们的后端服务器和数据库仍然在做额外的不必要的工作,以获取所请求对象的所有字段。这基本上是浪费了很多时间,我们可以优化。
解决方案
如果有一种方法可以确定客户机请求哪些字段,并且只从数据库层返回这些字段,那该怎么办?
大多数现代数据库都提供了“投影”或“选择”特性,其中数据库驱动程序允许使用者指定应该从与数据库查询匹配的文档中返回哪些字段,以避免返回所有不必要的字段,并加快对数据库的查询。
这对数据库查询性能有相当大的影响,因为它避免了不必要的工作,如果您想知道潜在的节省是什么样子的,可以看看我之前写的这篇文章,它展示了在MongoDB中使用/不使用投影的一些性能指标。
Apollo-Server解析器函数
因此,我们可以从网络层和数据库层实现这一点,唯一缺少的是在我们的服务器/解析器中解决它。
用apollo-server编写的graphQL解析器函数通常接受四个输入参数。根,args,上下文和信息。如果我们要写一个解析器,返回所有用户从数据库举例,它会像这样:
resolvers = { getUsers: function (root, args, context, info) { const User = mongoose.model('User') return User.find({}) } }
根对象通常是Schema中请求的操作/字段的父对象。args是可以传递给该操作的任何参数,而上下文包括传递到上下文对象的任何参数(通常用于身份验证)。但是info对象呢?它似乎包括一些关于传入请求的信息。
所以我在apollographql.com上查阅了它的文档,这是我找到的
Info:这个参数只应该在高级情况下使用,但是它包含关于查询执行状态的信息,包括字段名、从根到字段的路径等等。它只在GraphQL.js源代码中有文档说明。
不是很有用,但我认为它可能包含有关传入请求和请求字段的必要信息,在查看了可能能够执行我想要的操作的在线库之后,我发现了graphql-fields。
graphql-fields
我遇到了graphql-fields,它可以根据info参数返回被请求的字段,它还可以处理高级情况,如联合类型、片段、子字段( union types, fragments, sub-fields)等。
下面是它所能做的,对于这样一个返回所有用户姓名和电子邮件的查询
{
users {
name
}
}
结果就是:[' name ', ' email ']
用法:
const graphqlFields = require(‘graphql-fields’);
function userResolver(root, args, context, info) {
const topLevelFields = Object.keys(graphqlFields(info));
console.log(topLevelFields)
// prints [‘name’, ‘email’]
}
很好,现在我们可以使用这个结果从数据库中只选择这些字段,而不是为每个用户从数据库中请求整个用户文档。
使用结果
在我的例子中,我使用MongoDB和Mongoose作为数据库驱动程序,所以Mongoose投影所需的语法与我从graphql-fields得到的结果有一点不同。
我想实现的等价查询如下所示:
User.find({}).select({name: 1, email: 1}).lean()
将来自graphql-fields的结果转换为猫鼬需要的选择对象的helper函数如下:
export function getMongooseSelectionFromSelectedFields (info, fieldPath = null) {
const selections = graphqlFields(info)
const mongooseSelection = Object
.keys(fieldPath ? selections[fieldPath] : selections)
.reduce((a, b) => ({ …a, [b]: 1 }), {})
return mongooseSelection
}
好了,现在客户端请求的字段就是数据库请求和返回的字段,这使我们的解析器更快了!
这在自动持久化查询(APQ)和缓存中完美地工作。另外两个表现容易获胜。
我添加了一个fieldPath参数来支持响应中的一层嵌套,以防从数据库请求数据的操作不是顶层操作。
这是新的解析器的样子:
resolvers = {
getUsers: function (root, args, context, info) {
const mongooseSelection = getMongooseSelectionFromRequest(info)
return User.find({}).select(mongooseSelection).lean()
}
}
结论
GraphQL是相当强大的,它不仅优化性能的客户端应用,但也可以用于优化后端性能,毕竟我们得到的特定请求字段在我们的解析器免费。
原文:https://itnext.io/graphql-performance-tip-database-projection-82795e434b44
本文:http://jiagoushi.pro/node/1176
讨论:请加入知识星球【首席架构师智库】或者小号【jiagoushi_pro】
- 50 次浏览
【Web应用架构】WebSocket 协议介绍
由HyBi工作组开发的WebSocket有线协议(RFC 6455)由两个高级组件组成:用于协商连接参数的开放HTTP握手和二进制消息帧机制,以实现低开销、基于消息的文本和二进制数据传输。
WebSocket协议试图在现有HTTP基础架构的上下文中解决现有双向HTTP技术的目标;因此,它被设计为在HTTP端口80和443上工作……然而,这种设计并没有将WebSocket限制为HTTP,而且未来的实现可以在专用端口上使用更简单的握手,而无需重新发明整个协议。
----WebSocket协议RFC 6455
WebSocket协议是一个功能完整的独立协议,可以在浏览器之外使用。尽管如此,它的主要应用程序是作为基于浏览器的应用程序的双向传输。
二进制框架层
客户端和服务器端WebSocket应用程序通过一个面向消息的API进行通信:发送方提供一个任意的UTF-8或二进制有效负载,当整个消息可用时,接收方被通知它的传递。为了实现这一点,WebSocket使用了自定义的二进制帧格式(图17-1),它将每个应用程序消息分解成一个或多个帧,将它们传输到目的地,重新组装它们,并在收到整个消息后通知接收者。
Figure 17-1. WebSocket frame: 2–14 bytes + payload
Frame
通信的最小单位,每个单位包含一个可变长度的帧头和一个可携带全部或部分应用程序消息的有效负载。
Message
映射到逻辑应用程序消息的完整帧序列。
决定将应用程序消息分割成多个框架是由客户机和服务器框架代码的底层实现做出的。因此,应用程序仍然很高兴地不知道各个WebSocket帧或者帧是如何执行的。话虽如此,理解每个WebSocket帧在连线上是如何表示的要点仍然是有用的:
- 每个帧(FIN)的第一个比特表示该帧是否为消息的最后一个片段。一条消息可能只包含一帧。
- 操作码(4位)表示传输帧的类型:用于传输应用程序数据的文本(1)或二进制(2)或用于连接活性检查的控制帧,如连接关闭(8)、ping(9)和pong(10)。
- 掩码位指示负载是否被掩码(仅针对从客户机发送到服务器的消息)。
- 有效载荷长度用可变长度字段表示:
- 如果0-125,那么这就是有效载荷长度。
- 如果是126,那么下面的两个字节表示一个16位无符号整数,表示帧长度。
- 如果是127,那么下面的8个字节表示一个64位无符号整数,表示帧长度。
- 屏蔽键包含一个用于屏蔽有效负载的32位值。
- 有效负载包含应用程序数据和自定义扩展数据(如果客户端和服务器在建立连接时协商了扩展)。
所有客户端发起的帧的有效负载都使用帧头中指定的值来隐藏:这可以防止恶意脚本在客户端执行缓存中毒攻击,攻击那些可能不理解WebSocket协议的中介。有关这次攻击的详细信息,请参考2011年W2SP上的 "Talking to Yourself for Fun and Profit"。
因此,每个服务器发送的WebSocket帧都会产生2-10字节的帧开销。客户端还必须发送一个屏蔽密钥,这将在头文件中增加额外的4个字节,导致开销增加6-14个字节。没有其他元数据可用,例如头字段或关于有效负载的其他信息:所有WebSocket通信都是通过交换帧来执行的,这些帧将有效负载视为应用程序数据的不透明blob。
WebSocket多路复用和堆头阻塞( Head-of-Line Blocking)
WebSocket易受行头阻塞的影响:消息可以被分割成一个或多个帧,但是来自不同消息的帧不能交叉,因为在HTTP/2帧机制中没有等效的“流ID”;参见流、消息和帧)。
因此,一个大消息,即使被分割成多个WebSocket帧,也会阻止与其他消息相关联的帧的传递。如果您的应用程序交付的是对延迟敏感的数据,请注意每个消息的有效负载大小,并考虑将大消息拆分为多个应用程序消息!
核心WebSocket规范中缺乏多路复用也意味着每个WebSocket连接都需要一个专用的TCP连接,这可能成为HTTP/1的一个潜在问题。由于浏览器维护的每个源的连接数量有限而进行的部署;请参阅耗尽客户机和服务器资源。
从好的方面来看,由HyBi工作组开发的“WebSockets多路复用扩展”解决了后一个限制:
有了这个扩展,一个TCP连接可以通过封装带有通道ID标签的帧来提供多个虚拟WebSocket连接……这个多路复用扩展维护独立的逻辑通道,每个逻辑通道提供独立WebSocket连接的完全等价逻辑,包括单独的握手头。
--------------WebSocket多路复用(草案10)
有了这个扩展,多个WebSocket连接(通道)可以在同一个TCP连接上多路复用。然而,每个单独的通道仍然很容易被线头阻塞!因此,一种可能的解决方案是使用不同的通道或专用的TCP连接并行地多路传输多个消息。
最后,注意前面的扩展仅对HTTP/1是必要的。x连接。虽然目前还没有官方规范可以使用HTTP/2传输WebSocket帧,但是这样做会容易得多:HTTP/2有内置的流多路复用,通过在HTTP/2帧机制中封装WebSocket帧,可以在一个会话中传输多个WebSocket连接。
协议的扩展
WebSocket规范允许协议扩展:WebSocket协议的有线格式和语义可以通过新的操作码和数据字段进行扩展。虽然有些不寻常,但这是一个非常强大的特性,因为它允许客户端和服务器在基本的WebSocket框架层之上实现额外的功能,而不需要任何来自应用程序代码的干预或合作。
有一些WebSocket协议扩展的例子吗?负责WebSocket规范开发的HyBi工作组列出了两个正在开发中的官方扩展:
“WebSockets的多路复用扩展”
这个扩展为单独的逻辑WebSocket连接提供了一种共享底层传输连接的方法。
“WebSocket的压缩扩展”
一个用于创建WebSocket扩展的框架,为WebSocket协议添加压缩功能。
如前所述,每个WebSocket连接都需要一个专用的TCP连接,这是低效的。多路复用扩展通过为每个WebSocket帧增加一个额外的“通道ID”来允许多个虚拟的WebSocket通道共享一个TCP连接来解决这个问题。
类似地,基本的WebSocket规范没有为传输数据的压缩提供任何机制或规定:每一帧都携带由应用程序提供的有效负载数据。因此,虽然这对于优化的二进制数据结构来说不是问题,但这可能会导致高字节传输开销,除非应用程序实现了自己的数据压缩和解压缩逻辑。实际上,压缩扩展支持HTTP提供的等效传输编码协商。
要启用一个或多个扩展,客户机必须在初始升级握手时公布它们,服务器必须选择并确认在协商的连接的生命周期中将使用的扩展。作为一个实际的示例,现在让我们仔细看看升级顺序。
WebSocket多路复用和压缩在野外
到2013年中期,WebSocket多路复用还没有被任何流行的浏览器支持。类似地,对压缩的支持也很有限:谷歌Chrome和最新的WebKit浏览器可能会在服务器上发布一个“x-webkit-deflate-frame”扩展。然而,deflate-frame是基于一个过时的修订标准,并将在未来过时。
顾名思义,每帧都是逐帧压缩有效负载内容,这对于可能在多个帧之间分割的大消息不是最优的。因此,压缩扩展的最新版本已经切换到每消息压缩——这是一个好消息。坏消息是每条消息的压缩还处于试验阶段,目前还不能在任何流行的浏览器中使用。
因此,应用程序应该密切关注传输数据的内容类型,并在适用的情况下应用自己的压缩。也就是说,至少要等到本地WebSocket压缩支持在所有流行的浏览器上广泛可用。这对于移动应用程序尤其重要,因为在移动应用程序中,每个不必要的字节都会给用户带来很高的代价。
HTTP升级协商
WebSocket协议提供了很多强大的特性:面向消息的通信、它自己的二进制帧层、子协议协商、可选协议扩展等等。因此,在交换任何消息之前,客户机和服务器必须协商建立连接所需的适当参数。
利用HTTP执行握手提供了几个优点。首先,它使WebSocket与现有的HTTP基础设施兼容:WebSocket服务器可以在端口80和443上运行,这两个端口通常是客户端唯一开放的端口。其次,它允许我们重用和扩展HTTP升级流自定义WebSocket头执行协商:
- Sec-WebSocket-Version
- 由客户端发送来指示它想要使用的WebSocket协议的版本(对于RFC6455“13”)。如果服务器不支持客户端版本,则必须使用支持版本列表进行响应。
- Sec-WebSocket-Key
- 客户端发送的自动生成的密钥,它充当服务器的“质询”,以证明服务器支持所请求的协议版本。
- Sec-WebSocket-Accept
- 包含Sec-WebSocket-Key签名值的服务器响应,证明它理解所请求的协议版本。
- Sec-WebSocket-Protocol
- 用于协商应用程序子协议:客户端发布支持协议列表;服务器必须使用单个协议名进行应答。
- Sec-WebSocket-Extensions
- 用于协商用于此连接的WebSocket扩展:客户端声明支持的扩展,服务器通过返回相同的头来确认一个或多个扩展。
有了这些,我们现在就有了执行HTTP升级和协商客户端和服务器之间新的WebSocket连接的所有必要的部分:
GET /socket HTTP/1.1 Host: thirdparty.com Origin: http://example.com Connection: Upgrade Upgrade: websocket Sec-WebSocket-Version: 13 Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ== Sec-WebSocket-Protocol: appProtocol, appProtocol-v2 Sec-WebSocket-Extensions: x-webkit-deflate-message, x-custom-extension
- 请求执行WebSocket协议升级
- 客户端使用的WebSocket协议版本
- 自动生成密钥来验证服务器协议支持
- 应用程序指定的子协议的可选列表
- 客户端支持的可选协议扩展列表
就像浏览器中任何其他客户端发起的连接一样,WebSocket请求遵循同源策略:浏览器自动在升级握手中附加源头,远程服务器可以使用CORS来接受或拒绝跨源请求;参见跨源资源共享(CORS)。要完成握手,服务器必须返回一个成功的“交换协议”响应,并确认客户端公布的选择:
HTTP/1.1 101 Switching Protocols Upgrade: websocket Connection: Upgrade Access-Control-Allow-Origin: http://example.com Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo= Sec-WebSocket-Protocol: appProtocol-v2 Sec-WebSocket-Extensions: x-custom-extension
- 确认WebSocket升级的响应代码
- CORS头指示选择进入跨源连接
- 签名键值证明协议支持
- 由服务器选择的应用程序子协议
- 由服务器选择的WebSocket扩展的列表
所有RFC6455-compatible WebSocket服务器使用相同的算法来计算客户挑战的答案:Sec-WebSocket-Key的内容与独特的GUID连接字符串中定义的标准,SHA1哈希计算,生成的字符串是base - 64编码,并返回给客户端。
一次成功的WebSocket握手至少必须包含协议版本和客户端发送的自动生成的质询值,以及来自服务器的101 HTTP响应代码(交换协议)和一个哈希的质询-响应来确认所选协议版本:
- 客户端必须发送Sec-WebSocket-Version和Sec-WebSocket-Key。
- 服务器必须通过返回Sec-WebSocket-Accept来确认协议。
- 客户端可以通过Sec-WebSocket-Protocol发送应用程序子协议列表。
- 服务器必须选择一个子协议并通过Sec-WebSocket-Protocol返回。如果服务器不支持任何连接,则连接将中止。
- 客户端可以发送一个协议扩展列表在Sec-WebSocket-Extensions。
- 服务器可以确认一个或多个选定的扩展通过Sec-WebSocket-Extensions。如果没有提供扩展,则连接在不提供扩展的情况下继续运行。
最后,一旦前面的握手完成,如果握手成功,连接现在就可以用作交换WebSocket消息的双向通信通道。从这里开始,客户端和服务器之间不再有其他明确的HTTP通信,由WebSocket协议接管。
代理、中介和WebSockets
在实践中,由于安全和策略的原因,许多用户拥有一组受限的开放端口——具体来说端口80 (HTTP)和端口443 (HTTPS)。因此,WebSocket协商是通过HTTP升级流执行的,以确保与现有网络策略和基础设施的最佳兼容性。
然而,正如我们前面提到的代理中介,TLS,和新协议在网络上,许多现有HTTP中介可能不理解新WebSocket协议,从而导致各种各样的失败案例:盲目连接升级,意想不到的WebSocket帧缓冲,内容修改协议的不了解,误分类的WebSocket信息流作为妥协的HTTP连接,等等。
WebSocket密钥和接受握手解决了一些问题:它是一个安全策略,可以防止服务器和中介在没有真正理解WebSocket协议的情况下盲目地“升级”连接。然而,尽管这种预防措施解决了显式代理的一些部署问题,但它对于“透明代理”是不够的,“透明代理”可以在没有通知的情况下分析和修改网络上的数据。
解决方法?建立一个安全的端到端隧道。,用WSS !通过在执行HTTP升级握手之前协商一个TLS会话,客户机和服务器建立一个加密隧道,从而解决前面列出的所有问题。对于移动客户端来说尤其如此,它的流量经常要通过各种代理服务,而这些代理服务在WebSocket中可能不能很好地发挥作用。
本文:http://jiagoushi.pro/node/1111
讨论:请加入知识星球【首席架构师智库】或者小号【jiagoushi_pro】
- 40 次浏览
【Web应用架构】WebSocket介绍和WebSocket API
WebSocket支持在客户端和服务器之间双向的、面向消息的文本和二进制数据流。它是浏览器中最接近原始网络套接字的API。除了WebSocket连接也不仅仅是一个网络套接字,因为浏览器在一个简单的API背后抽象了所有的复杂性,并提供了一些额外的服务:
- 连接协商和同源策略的实施
- 与现有HTTP基础设施的互操作性
- 面向消息的通信和有效的消息框架
- 子协议协商和可扩展性
WebSocket是浏览器中最通用、最灵活的传输方式之一。简单和最小的API使我们能够以流方式在客户机和服务器之间分层和交付任意的应用程序协议——从简单的JSON有效负载到定制的二进制消息格式的任何内容——其中任何一方都可以随时发送数据。
但是,定制协议的代价是它们是定制的。应用程序必须考虑到缺少状态管理、压缩、缓存和其他由浏览器提供的服务。设计约束和性能权衡总是存在的,利用WebSocket也不例外。简而言之,WebSocket不是HTTP、XHR或SSE的替代品,为了获得最佳性能,充分利用每种传输的优势至关重要。
WebSocket是由多个标准组成的:WebSocket API由W3C定义,WebSocket协议(RFC 6455)及其扩展由HyBi工作组(IETF)定义。
WebSocket API
浏览器提供的WebSocket API非常小和简单。同样,连接管理和消息处理的所有低级细节都由浏览器处理。为了启动一个新的连接,我们需要一个WebSocket资源的URL和一些应用程序回调:
var ws = new WebSocket('wss://example.com/socket'); ws.onerror = function (error) { ... } ws.onclose = function () { ... } ws.onopen = function () { ws.send("Connection established. Hello server!"); } ws.onmessage = function(msg) { if(msg.data instanceof Blob) { processBlob(msg.data); } else { processText(msg.data); } }
- 打开一个新的安全WebSocket连接(wss)
- 可选回调,在发生连接错误时调用
- 可选回调,在连接终止时调用
- 可选回调,在建立WebSocket连接时调用
- 客户端发起的消息到服务器
- 为来自服务器的每个新消息调用的回调函数
- 为接收到的消息调用二进制或文本处理逻辑
API不言自明。实际上,它应该与我们在前一章看到的EventSource API非常相似。这是有意为之,因为WebSocket提供了类似的扩展功能。话虽如此,两者之间也有一些重要的区别。让我们一个一个来看看。
效仿WebSocket
WebSocket协议已经经历了许多修订、实现回滚和安全调查。然而,好消息是RFC6455定义的最新版本(v13)现在被所有现代浏览器支持。唯一值得注意的遗漏是Android浏览器。有关最新状态,请参阅http://caniuse.com/websockets。
类似于SSE的polyfill策略(用定制的JavaScript模拟EventSource), WebSocket浏览器API可以通过一个可选的JavaScript库来模拟。然而,模拟WebSockets的难点不是API,而是传输!因此,polyfill库及其回退传输(XHR轮询、EventSource、iframe轮询等)的选择将对模拟WebSocket会话的性能产生重大影响。
为了简化跨浏览器的部署,流行的库(如SockJS)在浏览器中提供了类似WebSocket的对象的实现,而且更进一步地提供了一个定制服务器来实现对WebSocket的支持和各种替代传输。定制服务器和客户机的结合能够实现“无缝回退”:性能下降,但应用程序API保持不变。
其他库,比如Socket。除了多传输回退功能外,IO还实现了其他特性,如心跳、超时、支持自动重新连接等。
当考虑一个polyfill 库或“实时框架”,Socket.IO,密切关注客户端和服务器的底层实现和配置:始终利用本地WebSocket接口获得最佳性能,并确保回退传输满足您的性能目标。
WS和WSS URL方案
WebSocket资源URL使用自己的定制方案:ws用于明文通信(例如,ws://example.com/socket), wss用于需要加密通道(TCP+TLS)。为什么使用自定义模式,而不是熟悉的http?
WebSocket协议的主要用例是在浏览器和服务器上运行的应用程序之间提供一个优化的双向通信通道。然而,WebSocket有线协议可以在浏览器之外使用,并且可以通过非http交换进行协商。因此,HyBi工作组选择采用自定义URL方案。
尽管自定义方案启用了非http协商选项,但在实践中,还没有用于建立WebSocket会话的替代握手机制的现有标准。
接收文本和二进制数据
WebSocket通信由消息和应用程序代码组成,不需要担心缓冲、解析和重构接收到的数据。例如,如果服务器发送了1 MB的有效负载,应用程序的onmessage回调将只在客户机上整个消息可用时被调用。
此外,WebSocket协议对应用程序的有效负载没有任何假设和限制:文本和二进制数据都是公平的。在内部,该协议只跟踪关于消息的两部分信息:作为可变长度字段的有效负载的长度和区分UTF-8和二进制传输的有效负载类型。
当浏览器接收到新消息时,它会自动转换为DOMString对象(用于基于文本的数据)或Blob对象(用于二进制数据),然后直接传递给应用程序。唯一的另一个选项,作为性能提示和优化客户端,是告诉浏览器转换接收到的二进制数据到ArrayBuffer,而不是Blob:
var ws = new WebSocket('wss://example.com/socket'); ws.binaryType = "arraybuffer"; ws.onmessage = function(msg) { if(msg.data instanceof ArrayBuffer) { processArrayBuffer(msg.data); } else { processText(msg.data); } }
- 当收到二进制消息时,强制进行ArrayBuffer转换
用户代理可以将此作为如何处理传入的二进制数据的提示:如果属性设置为“blob”,则将其缓冲到磁盘是安全的,如果属性设置为“arraybuffer”,则可能更有效地将数据保存在内存中。自然,用户代理被鼓励使用更微妙的启发式来决定是否将传入数据保存在内存中……
WebSocket API, W3C候选推荐
Blob对象表示不变的原始数据的类文件对象。如果您不需要修改数据,也不需要将其分割成更小的块,那么它就是最佳格式——例如。,您可以将整个Blob对象传递给图像标记(请参阅使用XHR下载数据中的示例)。另一方面,如果需要对二进制数据执行额外的处理,那么ArrayBuffer可能更适合。
用JavaScript解码二进制数据
ArrayBuffer是一种通用的、固定长度的二进制数据缓冲区。但是,ArrayBuffer可以用来创建一个或多个ArrayBufferView对象,每个对象可以以特定的格式显示缓冲区的内容。例如,假设我们有以下类似c的二进制数据结构:
struct
someStruct
{
char
username
[
16
];
unsigned
short
id
;
float
scores
[
32
];
};
给定这种类型的ArrayBuffer对象,我们可以在同一个缓冲区中创建多个视图,每个视图都有自己的偏移量和数据类型:
var
buffer
=
msg
.
data
;
var
usernameView
=
new
Uint8Array
(
buffer
,
0
,
16
);
var
idView
=
new
Uint16Array
(
buffer
,
16
,
1
);
var
scoresView
=
new
Float32Array
(
buffer
,
18
,
32
);
console
.
log
(
"ID: "
+
idView
[
0
]
+
" username: "
+
usernameView
[
0
]);
for
(
var
j
=
0
;
j
<
32
;
j
++
)
{
console
.
log
(
scoresView
[
j
])
}
每个视图接受父缓冲区、起始字节偏移量和要处理的元素数量——偏移量是根据前面字段的大小计算的。因此,ArrayBuffer和WebSocket为我们的应用程序提供了所有必要的工具来在浏览器中流处理二进制数据。
发送文本和二进制数据
一旦WebSocket连接建立,客户端就可以随意发送和接收UTF-8和二进制消息。WebSocket提供了一个双向通信通道,它允许在相同的TCP连接上双向传递消息:
var ws = new WebSocket('wss://example.com/socket'); ws.onopen = function () { socket.send("Hello server!"); socket.send(JSON.stringify({'msg': 'payload'})); var buffer = new ArrayBuffer(128); socket.send(buffer); var intview = new Uint32Array(buffer); socket.send(intview); var blob = new Blob([buffer]); socket.send(blob); }
- 发送UTF-8编码的文本消息
- 发送一个UTF-8编码的JSON有效负载
- 将ArrayBuffer内容作为二进制有效负载发送
- 将ArrayBufferView内容作为二进制有效负载发送
- 将Blob内容作为二进制有效负载发送
WebSocket API接受一个DOMString对象,该对象在网络上被编码为UTF-8,或者一个用于二进制传输的ArrayBuffer、ArrayBufferView或Blob对象。但是,请注意,后面的二进制选项只是为了API的方便:在网络上,WebSocket框架通过单个位标记为二进制或文本。因此,如果应用程序或服务器需要关于负载的其他内容类型信息,那么它们必须使用额外的机制来通信该数据。
send()方法是异步的:提供的数据由客户机排队,然后函数立即返回。因此,特别是在传输大的有效负载时,不要将快速返回误认为数据已经发送的信号!要监视浏览器排队的数据量,应用程序可以查询套接字上的bufferedAmount属性:
var ws = new WebSocket('wss://example.com/socket'); ws.onopen = function () { subscribeToApplicationUpdates(function(evt) { if (ws.bufferedAmount == 0) ws.send(evt.data); }); };
- 订阅应用程序更新(例如,游戏状态更改)
- 检查客户机上缓冲的数据量
- 如果缓冲区为空,则发送下一个更新
前面的示例尝试将应用程序更新发送到服务器,但前提是前面的消息已从客户机的缓冲区中抽取。为什么要费心去做这样的检查呢?所有WebSocket消息都是按照客户端排队的确切顺序传递的。结果,队列消息的大量积压,甚至单个大消息,都将延迟在it后排队的消息的传递——线头阻塞!
为了解决这个问题,应用程序可以将大消息分割成更小的块,仔细监视bufferedAmount值以避免在行头阻塞,甚至为挂起的消息实现自己的优先级队列,而不是盲目地将它们全部排在套接字上。
许多应用程序生成多种类型的消息:高优先级更新,如控制流量;低优先级更新,如后台传输。为了优化传递,应用程序应该密切关注每种类型的消息如何以及何时在套接字上排队!
Subprotocol谈判
WebSocket协议对每个消息的格式不做任何假设:一个位跟踪消息是否包含文本或二进制数据,这样它可以被客户端和服务器有效地解码,但其他消息的内容是不透明的。
此外,与HTTP或XHR请求不同的是,它们通过每个请求和响应的HTTP头来通信额外的元数据,对于WebSocket消息没有这种等效的机制。因此,如果需要关于消息的额外元数据,那么客户端和服务器必须同意实现它们自己的子协议来通信这些数据:
- 客户端和服务器可以预先就固定的消息格式达成一致。,所有通信都将通过json编码的消息或自定义二进制格式完成,必要的消息元数据将成为编码结构的一部分。
- 如果客户机和服务器需要传输不同的数据类型,那么它们可以就一致的消息头达成一致,该消息头可用于通信指令来解码剩余的有效负载。
- 可以使用文本和二进制消息的混合来传递有效负载和元数据信息。时,文本消息可以与等效的HTTP头通信,然后与应用程序有效负载通信二进制消息。
这个列表只是一个可能的策略的小样本。WebSocket消息的灵活性和低开销是以额外的应用逻辑为代价的。但是,消息序列化和元数据管理只是问题的一部分!一旦我们确定了消息的序列化格式,我们如何确保客户机和服务器相互理解,以及如何保持它们同步?
幸运的是,WebSocket提供了一个简单而方便的子协议协商API来解决第二个问题。作为初始连接握手的一部分,客户端可以向服务器发布它支持的协议:
var ws = new WebSocket('wss://example.com/socket', ['appProtocol', 'appProtocol-v2']); ws.onopen = function () { if (ws.protocol == 'appProtocol-v2') { ... } else { ... } }
- 在WebSocket握手期间发布的子协议数组
- 检查服务器选择的子协议
如前面的示例所示,WebSocket构造函数接受子协议名称的可选数组,它允许客户端发布它理解或愿意为这个连接使用的协议列表。指定的列表被发送到服务器,服务器被允许选择一个由客户机发布的协议。
如果子协议协商成功,那么onopen回调在客户端上被触发,并且应用程序可以查询WebSocket对象上的协议属性来确定所选择的协议。另一方面,如果服务器不支持任何客户端发布的客户端协议,那么WebSocket握手是不完整的:onerror回调被调用,连接被终止。
子协议名称由应用程序定义,并在初始HTTP握手期间按指定的方式发送到服务器。除此之外,指定的子协议对核心WebSocket API没有影响。
本文:http://jiagoushi.pro/node/1110
讨论:请加入知识星球【首席架构师圈】或者小号【jiagoushi_pro】
- 96 次浏览
【Web应用架构】WebSocket用例,性能和性能检查列表
WebSocket API为客户机和服务器之间的文本和二进制数据的双向、面向消息流提供了一个简单的接口:向构造函数传递一个WebSocket URL,设置几个JavaScript回调函数,然后我们就启动并运行了——其余的由浏览器处理。再加上WebSocket协议,它提供了二进制帧、可扩展性和子协议协商,WebSocket成为在浏览器中交付定制应用协议的完美工具。
然而,就像任何关于性能的讨论一样,尽管WebSocket协议的实现复杂性对应用程序是隐藏的,但它仍然对如何以及何时使用WebSocket有重要的性能暗示。WebSocket不是XHR或SSE的替代品,为了获得最佳性能,我们必须充分利用每种传输的优势!
要查看每个传输的性能特征,请参考XHR用例和性能以及SSE用例和性能。
请求和响应流
WebSocket是唯一允许在同一个TCP连接上进行双向通信的传输(图17-2):客户端和服务器可以随意交换消息。因此,WebSocket在两个方向上都提供了文本和二进制应用程序数据的低延迟交付。
图17-2。XHR, SSE, WebSocket的通信流程
- XHR为“事务性”请求-响应通信进行了优化:客户机向服务器发送完整的、格式良好的HTTP请求,服务器以完整的响应响应。不支持请求流,在Streams API可用之前,没有可靠的跨浏览器响应流API。
- SSE实现了基于文本数据的高效、低延迟的服务器到客户端的流化:客户端启动SSE连接,服务器使用事件源协议将更新流化到客户端。在初始握手之后,客户端不能向服务器发送任何数据。
传播和排队延迟
交换从XHR到SSE或WebSocket的传输不会减少客户端和服务器之间的往返!无论传输方式如何,数据包的传播延迟都是相同的。然而,除了传播延迟之外,还有排队延迟:消息在被路由到另一方之前必须在客户机或服务器上等待的时间。
在XHR轮询的情况下,排队延迟是客户端轮询间隔的函数:消息可能在服务器上可用,但直到下一个客户端XHR请求才能发送;请参阅XHR轮询的建模性能。相比之下,SSE和WebSocket都使用一个持久连接,这允许服务器在消息可用的时候就发送消息(以及客户端,在WebSocket的情况下)。
因此,对于SSE和WebSocket来说,“低延迟交付”是指消除消息排队延迟。我们还没有想出如何让WebSocket数据包的传输速度超过光速!
消息开销
一旦WebSocket连接建立,客户端和服务器通过WebSocket协议交换数据:应用消息被分成一个或多个帧,每个帧增加2到14字节的开销。而且,由于框架是通过自定义二进制格式完成的,因此UTF-8和二进制应用程序数据都可以通过相同的机制进行有效编码。与XHR和SSE相比如何?
- SSE只增加了5字节的每条消息,但仅限于UTF-8内容;参见事件流协议。
- HTTP / 1。x请求(XHR或其他)将携带额外的500-800字节HTTP元数据,加上cookie;参见测量和控制协议开销。
- HTTP/2压缩HTTP元数据,这大大降低了开销;看到标题压缩。事实上,如果头在请求之间不改变,开销可以低至8字节!
请记住,这些开销数字不包括IP、TCP和TLS帧的开销,这些开销会增加每条消息60-100字节的综合开销,而与应用程序协议无关;参见优化TLS记录大小。
数据效率和压缩
每个XHR请求可以通过定期的HTTP协商来协商最佳的传输编码格式(例如,基于文本的数据的gzip)。类似地,由于SSE仅限于utf -8传输,所以可以通过在整个会话中应用gzip有效地压缩事件流数据。
使用WebSocket,情况会更加复杂:WebSocket可以传输文本和二进制数据,因此压缩整个会话没有意义。二进制有效负载可能已经被压缩了!因此,WebSocket必须实现自己的压缩机制,并有选择地将其应用于每个消息。
好消息是HyBi工作组正在为WebSocket协议开发每消息压缩扩展。然而,它还没有在任何浏览器中可用。因此,除非应用程序通过仔细优化其二进制有效负载(参见用JavaScript解码二进制数据)来实现自己的压缩逻辑,并为基于文本的消息实现自己的压缩逻辑,否则它可能会对传输的数据产生较高的字节开销!
Chrome和一些基于webkit的浏览器支持WebSocket协议压缩扩展的旧版本(每帧压缩);参见WebSocket多路复用和压缩。
自定义应用程序协议
浏览器针对HTTP数据传输进行了优化:它理解该协议,并提供广泛的服务,如身份验证、缓存、压缩等等。因此,XHR请求免费继承所有这些功能。
相比之下,流允许我们提供定制的客户端和服务器之间的协议,但代价的绕过许多浏览器提供的服务:最初的HTTP握手可以执行一些协商参数的连接,但是一旦建立了会话,所有进一步的客户端和服务器之间的数据流是不透明到浏览器。因此,交付自定义协议的灵活性也有其缺点,应用程序可能必须实现自己的逻辑来填补缺失的空白:缓存、状态管理、消息元数据的交付,等等!
初始的HTTP升级握手允许服务器利用现有的HTTP cookie机制来验证用户。如果验证失败,服务器可以拒绝WebSocket升级。
利用浏览器和中间缓存
使用常规HTTP具有显著的优势。问您自己一个简单的问题:客户机会从缓存接收到的数据中获益吗?或者,如果中介可以缓存资产,它是否可以优化资产的交付?
例如,WebSocket支持二进制传输,这允许应用程序流任意的图像格式而没有开销——好漂亮!然而,图像是在自定义协议中交付的这一事实意味着它不会被浏览器缓存或任何中介(例如CDN)缓存。因此,您可能需要向客户机进行不必要的传输,并向源服务器传输更多的流量。同样的逻辑也适用于所有其他数据格式:视频、文本等等。
因此,确保你为工作选择了合适的交通工具!解决这些问题的一个简单而有效的策略是使用WebSocket会话来传递非缓存数据,比如实时更新和应用“控制”消息,这可以触发XHR请求来通过HTTP协议获取其他资产。
部署WebSocket基础设施
HTTP为短时间和突发传输进行了优化。因此,许多服务器、代理和其他中介经常被配置为主动超时空闲HTTP连接,当然,这正是我们不希望看到的长时间WebSocket会话。为了解决这个问题,有三方面需要考虑:
- 路由器,负载平衡器,和代理在自己的网络
- 外部网络中的透明和显式代理(如ISP和运营商代理)
- 客户网络中的路由器、防火墙和代理
我们无法控制客户端网络的策略。事实上,一些网络可能会完全阻止WebSocket流量,这就是为什么您可能需要一个备用策略。类似地,我们无法控制外部网络上的代理。然而,这正是TLS可以帮助的地方!通过在安全的端到端连接上使用隧道,WebSocket通信可以绕过所有的中间代理。
使用TLS并不会阻止中间层对空闲TCP连接计时。然而,在实践中,它极大地提高了协商WebSocket会话的成功率,并且通常还有助于扩展连接超时间隔。
最后,还有我们自己部署和管理的基础设施,这通常也需要关注和调优。尽管责怪客户端或外部网络很容易,但问题往往是在家里。服务路径中的每个负载均衡器、路由器、代理和web服务器都必须进行调优,以允许长时间连接。
例如,Nginx 1.3.13+可以代理WebSocket流量,但默认为60秒超时!为了增加限制,我们必须明确地定义较长的超时:
location /websocket { proxy_pass http://backend; proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection "upgrade"; proxy_read_timeout 3600; proxy_send_timeout 3600; }
- 设置读取间隔60分钟的超时时间
- 在写之间设置60分钟的超时时间
类似地,在一个或多个Nginx服务器前安装一个负载均衡器也很常见,比如HAProxy。毫不奇怪,我们在这里也需要应用类似的显式配置。HAProxy,因为:
defaults http timeout connect 30s timeout client 30s timeout server 30s timeout tunnel 1h
- 隧道的60分钟不活动超时
前面示例的问题是额外的“隧道”超时。在HAProxy中,连接、客户端和服务器的超时只适用于初始的HTTP升级握手,但是一旦升级完成,超时由隧道值控制。
Nginx和HAProxy只是在我们的数据中心运行的数百个不同的服务器、代理和负载平衡器中的两个。我们不能列举这些页面中所有的配置可能性。前面的示例只是说明了大多数基础设施需要自定义配置来处理长期存在的会话。因此,在实现应用程序保持生命值之前,首先要仔细检查基础设施。
长期会话和空闲会话占用所有中间服务器上的内存和套接字资源。因此,短超时通常被认为是安全、资源和操作方面的预防措施。部署WebSocket、SSE和HTTP/2(它们都依赖于长寿命会话)带来了各自的新操作挑战。
性能检查表
部署高性能WebSocket服务需要在客户端和服务器端进行仔细的调优和考虑。列入议程的标准的简短清单:
- 使用安全的WebSocket (WSS over TLS)进行可靠的部署。
- 密切关注polyfill 的性能(如有必要)。
- 利用子协议协商来确定应用程序协议。
- 优化二进制有效载荷以最小化传输大小。
- 考虑压缩UTF-8内容以最小化传输大小。
- 为接收到的二进制有效负载设置正确的二进制类型。
- 监视客户机上缓冲的数据量。
- 分割大型应用程序消息以避免线头阻塞。
- 在适用的情况下利用其他传输。
最后,但绝对不是最不重要的,优化移动!在手机上,实时推送可能是一个代价高昂的性能反模式,因为电池寿命总是非常宝贵。这并不是说WebSocket不应该在移动设备上使用。相反,它可以是一种高效的交通工具,但一定要考虑到它的要求:
本文:http://jiagoushi.pro/node/1112
讨论:请加入知识星球【首席架构师圈】或者小号【jiagoushi_pro】
- 182 次浏览
【Web应用架构】Web框架基准测试 2020-05-2 8第19轮 按吞吐量排序
Best fortunes responses per second, Dell R440 Xeon Gold + 10 GbE(424 tests)
Rnk | Framework | Best performance (higher is better) | Errors | Cls | Lng | Plt | FE | Aos | DB | Dos | Orm | IA | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|
1 | drogon-core | 678,278 | 100.0% | 0 | Ful | C++ | Non | Non | Lin | Pg | Lin | Raw | Rea |
2 | actix-core | 651,144 | 96.0% | 0 | Plt | Rus | Non | act | Lin | Pg | Lin | Raw | Rea |
3 | actix-pg | 607,052 | 89.5% | 0 | Mcr | Rus | Non | act | Lin | Pg | Lin | Raw | Rea |
4 | drogon | 553,366 | 81.6% | 0 | Ful | C++ | Non | Non | Lin | Pg | Lin | Mcr | Rea |
5 | may-minihttp | 476,965 | 70.3% | 0 | Mcr | Rus | Rus | may | Lin | Pg | Lin | Raw | Rea |
6 | h2o | 411,176 | 60.6% | 0 | Plt | C | Non | h2o | Lin | Pg | Lin | Raw | Rea |
7 | lithium-postgres | 401,783 | 59.2% | 0 | Mcr | C++ | Non | Non | Lin | Pg | Lin | Ful | Rea |
8 | fasthttp-prefork-quicktemplate | 363,587 | 53.6% | 0 | Plt | Go | Non | Non | Lin | Pg | Lin | Raw | Rea |
9 | atreugo-prefork-quicktemplate | 362,342 | 53.4% | 0 | Plt | Go | Non | Non | Lin | Pg | Lin | Raw | Rea |
10 | hyper-db | 358,511 | 52.9% | 0 | Mcr | Rus | Rus | Hyp | Lin | Pg | Lin | Raw | Rea |
11 | php-ngx-pgsql | 356,507 | 52.6% | 0 | Plt | PHP | ngx | ngx | Lin | Pg | Lin | Raw | Rea |
12 | workerman-pgsql | 352,508 | 52.0% | 0 | Plt | PHP | wor | Non | Lin | Pg | Lin | Raw | Rea |
13 | vertx-postgres | 347,356 | 51.2% | 0 | Plt | Jav | ver | Non | Lin | Pg | Lin | Raw | Rea |
14 | ulib-postgres | 344,634 | 50.8% | 0 | Plt | C++ | Non | ULi | Lin | Pg | Lin | Mcr | Rea |
15 | fasthttp-quicktemplate | 319,764 | 47.1% | 0 | Plt | Go | Non | Non | Lin | Pg | Lin | Raw | Rea |
16 | atreugo-quicktemplate | 319,390 | 47.1% | 0 | Plt | Go | Non | Non | Lin | Pg | Lin | Raw | Rea |
17 | greenlightning | 318,601 | 47.0% | 0 | Mcr | Jav | Non | Non | Lin | Pg | Lin | Raw | Rea |
18 | jooby-pgclient | 312,439 | 46.1% | 0 | Ful | Jav | Utw | Non | Lin | Pg | Lin | Raw | Rea |
19 | fiber-prefork | 301,604 | 44.5% | 0 | Plt | Go | Non | Non | Lin | Pg | Lin | Raw | Rea |
20 | go-pgx-prefork-quicktemplate | 298,935 | 44.1% | 0 | Plt | Go | Non | Non | Lin | Pg | Lin | Raw | Rea |
21 | lithium | 296,750 | 43.8% | 0 | Mcr | C++ | Non | Non | Lin | My | Lin | Ful | Rea |
22 | workerman | 291,339 | 43.0% | 0 | Plt | PHP | wor | Non | Lin | My | Lin | Raw | Rea |
23 | php-ngx-mysql | 290,312 | 42.8% | 0 | Plt | PHP | ngx | ngx | Lin | My | Lin | Raw | Rea |
24 | aspcore-rhtx-pg | 285,398 | 42.1% | 0 | Plt | C# | .NE | kes | Lin | Pg | Lin | Raw | Rea |
25 | swoole | 283,728 | 41.8% | 0 | Plt | PHP | swo | Non | Lin | My | Lin | Raw | Rea |
26 | aspcore-ado-pg | 273,121 | 40.3% | 0 | Plt | C# | .NE | kes | Lin | Pg | Lin | Raw | Rea |
27 | proteus | 264,265 | 39.0% | 0 | Mcr | Jav | Utw | Non | Lin | Pg | Lin | Raw | Rea |
28 | kooby | 258,382 | 38.1% | 0 | Ful | Kot | Utw | Non | Lin | Pg | Lin | Raw | Rea |
29 | gemini-postgres | 258,129 | 38.1% | 0 | Ful | Jav | Svt | Res | Lin | Pg | Lin | Mcr | Rea |
30 | zysocket-v | 257,268 | 37.9% | 0 | Ful | C# | .NE | zys | Lin | Pg | Lin | Raw | Rea |
31 | jooby-jaxrs | 254,398 | 37.5% | 0 | Ful | Jav | Utw | Non | Lin | Pg | Lin | Raw | Rea |
32 | jooby | 252,239 | 37.2% | 0 | Ful | Jav | Utw | Non | Lin | Pg | Lin | Raw | Rea |
33 | kumbiaphp-workerman | 249,309 | 36.8% | 0 | Ful | PHP | wor | Non | Lin | Pg | Lin | Raw | Rea |
34 | light-4j | 243,211 | 35.9% | 0 | Plt | Jav | lig | Non | Lin | Pg | Lin | Raw | Rea |
35 | cpoll_cppsp-raw | 242,929 | 35.8% | 0 | Plt | C++ | Non | Non | Lin | My | Lin | Raw | Rea |
36 | beetlex | 241,339 | 35.6% | 0 | Ful | C# | .NE | bee | Lin | Pg | Lin | Raw | Rea |
37 | es4x | 237,751 | 35.1% | 0 | Mcr | JS | ver | Non | Lin | Pg | Lin | Raw | Rea |
38 | ulib-postgres_fit | 235,845 | 34.8% | 0 | Plt | C++ | Non | ULi | Lin | Pg | Lin | Mcr | Rea |
39 | jooby-jetty | 235,161 | 34.7% | 0 | Ful | Jav | Jty | Non | Lin | Pg | Lin | Raw | Rea |
40 | aspcore-mw-ado-pg | 234,321 | 34.5% | 0 | Mcr | C# | .NE | kes | Lin | Pg | Lin | Raw | Rea |
41 | aspcore-vb-mw-ado-pg | 227,638 | 33.6% | 0 | Mcr | vb | .NE | kes | Lin | Pg | Lin | Raw | Rea |
42 | go-pgx-quicktemplate | 225,782 | 33.3% | 0 | Plt | Go | Non | Non | Lin | Pg | Lin | Raw | Rea |
43 | jlhttp-postgres | 222,691 | 32.8% | 0 | Plt | Jav | JLH | Non | Lin | Pg | Lin | Raw | Rea |
44 | undertow-postgresql | 222,598 | 32.8% | 0 | Plt | Jav | Utw | Non | Lin | Pg | Lin | Raw | Rea |
45 | jooby-netty | 222,565 | 32.8% | 0 | Ful | Jav | Nty | Non | Lin | Pg | Lin | Raw | Rea |
46 | vertx-web-scala | 214,768 | 31.7% | 0 | Mcr | Sca | ver | Non | Lin | Pg | Lin | Raw | Rea |
47 | fiber | 210,127 | 31.0% | 0 | Plt | Go | Non | Non | Lin | Pg | Lin | Raw | Rea |
48 | revenj-jvm | 210,121 | 31.0% | 0 | Ful | Jav | Svt | Res | Lin | Pg | Lin | Ful | Rea |
49 | kemal | 208,775 | 30.8% | 0 | Ful | Cry | Non | Non | Lin | Pg | Lin | Mcr | Rea |
50 | rapidoid-postgres | 208,499 | 30.7% | 0 | Plt | Jav | Rap | Non | Lin | Pg | Lin | Mcr | Rea |
51 | crystal | 207,551 | 30.6% | 0 | Plt | Cry | Non | Non | Lin | Pg | Lin | Raw | Rea |
52 | grip | 207,490 | 30.6% | 0 | Mcr | Cry | Non | Non | Lin | Pg | Lin | Raw | Rea |
53 | crystal-radix | 204,596 | 30.2% | 0 | Plt | Cry | Non | Non | Lin | Pg | Lin | Raw | Rea |
54 | raze | 202,714 | 29.9% | 0 | Ful | Cry | Non | Non | Lin | Pg | Lin | Mcr | Rea |
55 | http4k-apache | 202,537 | 29.9% | 0 | Mcr | Kot | apa | Non | Lin | Pg | Lin | Raw | Rea |
56 | kumbiaphp-workerman-mysql | 201,807 | 29.8% | 0 | Ful | PHP | wor | Non | Lin | My | Lin | Mcr | Rea |
57 | aspcore-mw-dap-pg | 198,450 | 29.3% | 0 | Mcr | C# | .NE | kes | Lin | Pg | Lin | Mcr | Rea |
58 | openresty | 198,054 | 29.2% | 0 | Plt | Lua | OpR | ngx | Lin | My | Lin | Raw | Rea |
59 | act-eclipselink-pgsql | 196,017 | 28.9% | 0 | Ful | Jav | Utw | Non | Lin | Pg | Lin | Ful | Rea |
60 | ubiquity-workerman | 190,634 | 28.1% | 0 | Ful | PHP | wor | Non | Lin | Pg | Lin | Ful | Rea |
61 | zebra | 188,840 | 27.8% | 0 | Ful | f# | .NE | kes | Lin | Pg | Lin | Raw | Rea |
62 | hexagon-resin-postgresql | 188,288 | 27.8% | 0 | Mcr | Kot | Svt | Non | Lin | Pg | Lin | Raw | Rea |
63 | http4k-undertow | 188,273 | 27.8% | 0 | Mcr | Kot | Utw | Non | Lin | Pg | Lin | Raw | Rea |
64 | aspcore-ado-my | 187,309 | 27.6% | 0 | Plt | C# | .NE | kes | Lin | My | Lin | Raw | Rea |
65 | cutelyst-thread-pg | 186,461 | 27.5% | 0 | Ful | C++ | cut | Non | Lin | Pg | Lin | Raw | Rea |
66 | cutelyst-pf-pg-nodelay | 186,376 | 27.5% | 0 | Ful | C++ | cut | Non | Lin | Pg | Lin | Raw | Rea |
67 | cutelyst-thread-pg-nodelay | 185,641 | 27.4% | 0 | Ful | C++ | cut | Non | Lin | Pg | Lin | Raw | Rea |
68 | simps | 182,191 | 26.9% | 0 | Mcr | PHP | swo | Non | Lin | My | Lin | Raw | Rea |
69 | cutelyst-pf-pg | 179,972 | 26.5% | 0 | Ful | C++ | cut | Non | Lin | Pg | Lin | Raw | Rea |
70 | ulib-mysql | 179,425 | 26.5% | 0 | Plt | C++ | Non | ULi | Lin | My | Lin | Mcr | Rea |
71 | vertx-web-postgres | 176,093 | 26.0% | 0 | Mcr | Jav | ver | Non | Lin | Pg | Lin | Raw | Rea |
72 | act-hibernate-pgsql | 175,656 | 25.9% | 0 | Ful | Jav | Utw | Non | Lin | Pg | Lin | Ful | Rea |
73 | helidon | 173,306 | 25.6% | 0 | Mcr | Jav | Nty | Non | Lin | Pg | Lin | Raw | Rea |
74 | aspcore-mw-ado-my | 170,573 | 25.1% | 0 | Mcr | C# | .NE | kes | Lin | My | Lin | Raw | Rea |
75 | aspcore-vb-mw-ado-my | 169,856 | 25.0% | 0 | Mcr | vb | .NE | kes | Lin | My | Lin | Raw | Rea |
76 | chi-gojay-prefork | 165,126 | 24.3% | 0 | Mcr | Go | Non | Non | Lin | My | Lin | Raw | Rea |
77 | http4k | 164,684 | 24.3% | 0 | Mcr | Kot | Svt | Non | Lin | Pg | Lin | Raw | Rea |
78 | fasthttp-prefork | 163,706 | 24.1% | 0 | Plt | Go | Non | Non | Lin | Pg | Lin | Raw | Rea |
79 | actix-diesel | 161,892 | 23.9% | 0 | Mcr | Rus | Non | act | Lin | Pg | Lin | Ful | Rea |
80 | atreugo-prefork | 161,479 | 23.8% | 0 | Plt | Go | Non | Non | Lin | Pg | Lin | Raw | Rea |
81 | giraffe | 161,005 | 23.7% | 0 | Ful | f# | .NE | kes | Lin | Pg | Lin | Mcr | Rea |
82 | chi-prefork | 160,869 | 23.7% | 0 | Mcr | Go | Non | Non | Lin | My | Lin | Raw | Rea |
83 | act-hibernate-pgsql-rythm | 159,866 | 23.6% | 0 | Ful | Jav | Utw | Non | Lin | Pg | Lin | Ful | Rea |
84 | chi-sjson-prefork | 159,292 | 23.5% | 0 | Mcr | Go | Non | Non | Lin | My | Lin | Raw | Rea |
85 | aspcore-mw-ef-pg | 157,931 | 23.3% | 0 | Mcr | C# | .NE | kes | Lin | Pg | Lin | Ful | Rea |
86 | aspcore-mw-dap-my | 155,853 | 23.0% | 0 | Mcr | C# | .NE | kes | Lin | My | Lin | Mcr | Rea |
87 | warp-mysql-haskell | 155,711 | 23.0% | 0 | Mcr | Hkl | Wai | wai | Lin | My | Lin | Raw | Rea |
88 | act-hibernate-mysql | 154,441 | 22.8% | 0 | Ful | Jav | Utw | Non | Lin | My | Lin | Ful | Rea |
89 | act-eclipselink-pgsql-rythm | 153,060 | 22.6% | 0 | Ful | Jav | Utw | Non | Lin | Pg | Lin | Ful | Rea |
90 | warp | 152,392 | 22.5% | 0 | Mcr | Hkl | Wai | wai | Lin | Pg | Lin | Raw | Rea |
91 | php-ngx-async | 151,834 | 22.4% | 0 | Plt | PHP | ngx | ngx | Lin | My | Lin | Raw | Rea |
92 | swoole-no-async | 151,102 | 22.3% | 0 | Plt | PHP | swo | Non | Lin | My | Lin | Raw | Rea |
93 | proteus-mysql | 150,380 | 22.2% | 0 | Mcr | Jav | Utw | Non | Lin | My | Lin | Raw | Rea |
94 | act-hibernate-mysql-rythm | 147,057 | 21.7% | 0 | Ful | Jav | Utw | Non | Lin | My | Lin | Ful | Rea |
95 | fasthttp | 146,882 | 21.7% | 0 | Plt | Go | Non | Non | Lin | Pg | Lin | Raw | Rea |
96 | act-eclipselink-mysql | 146,534 | 21.6% | 0 | Ful | Jav | Utw | Non | Lin | My | Lin | Ful | Rea |
97 | atreugo | 144,987 | 21.4% | 0 | Plt | Go | Non | Non | Lin | Pg | Lin | Raw | Rea |
98 | go-my-prefork | 144,289 | 21.3% | 0 | Plt | Go | Non | Non | Lin | My | Lin | Raw | Rea |
99 | hexagon-jetty-postgresql | 141,759 | 20.9% | 0 | Mcr | Kot | Svt | Non | Lin | Pg | Lin | Raw | Rea |
100 | ulib-mongodb | 141,657 | 20.9% | 0 | Plt | C++ | Non | ULi | Lin | Mo | Lin | Mcr | Rea |
101 | gemini-mysql | 140,793 | 20.8% | 0 | Ful | Jav | Svt | Res | Lin | My | Lin | Mcr | Rea |
102 | ubiquity-swoole | 140,604 | 20.7% | 0 | Ful | PHP | swo | Non | Lin | Pg | Lin | Ful | Rea |
103 | act-eclipselink-mysql-rythm | 136,914 | 20.2% | 0 | Ful | Jav | Utw | Non | Lin | My | Lin | Ful | Rea |
104 | go-pgx-prefork | 134,727 | 19.9% | 0 | Plt | Go | Non | Non | Lin | Pg | Lin | Raw | Rea |
105 | ratpack-jdbc | 134,690 | 19.9% | 0 | Mcr | Jav | Nty | Non | Lin | Pg | Lin | Raw | Rea |
106 | rapidoid-mysql | 133,362 | 19.7% | 0 | Plt | Jav | Rap | Non | Lin | My | Lin | Mcr | Rea |
107 | polkadot-postgres | 131,783 | 19.4% | 0 | Plt | JS | Non | Non | Lin | Pg | Lin | Raw | Rea |
108 | ratpack-pgclient | 131,066 | 19.3% | 0 | Mcr | Jav | Nty | Non | Lin | Pg | Lin | Raw | Rea |
109 | cutelyst-nginx-pg | 128,097 | 18.9% | 0 | Ful | C++ | uWS | ngx | Lin | Pg | Lin | Raw | Rea |
110 | fasthttp-mongo-prefork | 124,521 | 18.4% | 0 | Plt | Go | Non | Non | Lin | Mo | Lin | Raw | Rea |
111 | aspcore-mvc-ado-pg | 124,455 | 18.3% | 0 | Ful | C# | .NE | kes | Lin | Pg | Lin | Raw | Rea |
112 | warp-hasql | 122,305 | 18.0% | 0 | Mcr | Hkl | Wai | wai | Lin | Pg | Lin | Raw | Rea |
113 | atreugo-mongo-prefork | 122,256 | 18.0% | 0 | Plt | Go | Non | Non | Lin | Mo | Lin | Raw | Rea |
114 | imi-raw | 121,648 | 17.9% | 0 | Mcr | PHP | swo | Non | Lin | My | Lin | Raw | Rea |
115 | workerman-async | 118,772 | 17.5% | 0 | Plt | PHP | wor | Non | Lin | My | Lin | Raw | Rea |
116 | nodejs-mongodb-raw | 115,477 | 17.0% | 0 | Plt | JS | njs | Non | Lin | Mo | Lin | Raw | Rea |
117 | aspcore-mvc-dap-pg | 115,329 | 17.0% | 0 | Ful | C# | .NE | kes | Lin | Pg | Lin | Mcr | Rea |
118 | ubiquity-swoole-mysql-async | 115,321 | 17.0% | 0 | Ful | PHP | swo | Non | Lin | My | Lin | Ful | Rea |
119 | lwan | 114,729 | 16.9% | 0 | Mcr | C | Lwa | lwa | Lin | My | Lin | Raw | Rea |
120 | vertx-web-susom-postgres | 114,654 | 16.9% | 0 | Mcr | Jav | ver | Non | Lin | Pg | Lin | Raw | Rea |
121 | go-mgo-prefork | 111,343 | 16.4% | 0 | Plt | Go | Non | Non | Lin | Mo | Lin | Raw | Rea |
122 | polkadot-mongodb | 111,286 | 16.4% | 0 | Plt | JS | Non | Non | Lin | Mo | Lin | Raw | Rea |
123 | go-pgx | 111,168 | 16.4% | 0 | Plt | Go | Non | Non | Lin | Pg | Lin | Raw | Rea |
124 | fintrospect | 110,211 | 16.2% | 0 | Mcr | Sca | Nty | Non | Lin | My | Lin | Raw | Rea |
125 | servlet-postgresql | 109,125 | 16.1% | 0 | Plt | Jav | Svt | Res | Lin | Pg | Lin | Raw | Rea |
126 | cutelyst-thread-my | 107,101 | 15.8% | 0 | Ful | C++ | cut | Non | Lin | My | Lin | Raw | Rea |
127 | cutelyst-thread-my-nodelay | 106,740 | 15.7% | 0 | Ful | C++ | cut | Non | Lin | My | Lin | Raw | Rea |
128 | php-unit | 106,582 | 15.7% | 0 | Plt | PHP | uni | Non | Lin | My | Lin | Raw | Rea |
129 | cutelyst-thread-pg-grantlee | 105,911 | 15.6% | 0 | Ful | C++ | cut | Non | Lin | Pg | Lin | Raw | Rea |
130 | php-pgsql-raw | 104,730 | 15.4% | 0 | Plt | PHP | fpm | ngx | Lin | Pg | Lin | Raw | Rea |
131 | aspcore-mvc-ado-my | 104,075 | 15.3% | 0 | Ful | C# | .NE | kes | Lin | My | Lin | Raw | Rea |
132 | polkadot-mysql | 103,016 | 15.2% | 0 | Plt | JS | njs | Non | Lin | My | Lin | Raw | Rea |
133 | cutelyst-pf-my-nodelay | 101,653 | 15.0% | 0 | Ful | C++ | cut | Non | Lin | My | Lin | Raw | Rea |
134 | cutelyst-pf-my | 101,536 | 15.0% | 0 | Ful | C++ | cut | Non | Lin | My | Lin | Raw | Rea |
135 | toro | 101,088 | 14.9% | 282 | Mcr | Cry | Non | Non | Lin | Pg | Lin | Raw | Rea |
136 | chi-gojay | 99,528 | 14.7% | 0 | Mcr | Go | Non | Non | Lin | My | Lin | Raw | Rea |
137 | aspcore-mvc-dap-my | 99,238 | 14.6% | 0 | Ful | C# | .NE | kes | Lin | My | Lin | Mcr | Rea |
138 | akka-http | 99,237 | 14.6% | 0 | Mcr | Sca | Akk | Non | Lin | My | Lin | Raw | Rea |
139 | chi | 98,889 | 14.6% | 0 | Mcr | Go | Non | Non | Lin | My | Lin | Raw | Rea |
140 | hexagon-resin-mongodb | 98,017 | 14.5% | 0 | Mcr | Kot | Svt | Non | Lin | Mo | Lin | Raw | Rea |
141 | php | 97,727 | 14.4% | 0 | Plt | PHP | fpm | ngx | Lin | My | Lin | Raw | Rea |
142 | chi-sjson | 96,765 | 14.3% | 0 | Mcr | Go | Non | Non | Lin | My | Lin | Raw | Rea |
143 | aah-postgresql | 96,699 | 14.3% | 0 | Ful | Go | Non | Non | Lin | Pg | Lin | Raw | Rea |
144 | php-h2o | 96,667 | 14.3% | 0 | Plt | PHP | fpm | h2o | Lin | My | Lin | Raw | Rea |
145 | php-pools | 96,572 | 14.2% | 0 | Plt | PHP | fpm | ngx | Lin | My | Lin | Raw | Rea |
146 | treefrog-mongodb | 96,231 | 14.2% | 0 | Ful | C++ | Non | Non | Lin | Mo | Lin | Mcr | Rea |
147 | chi-scratch | 95,476 | 14.1% | 0 | Mcr | Go | Non | Non | Lin | My | Lin | Raw | Rea |
148 | fastify-postgres | 95,088 | 14.0% | 0 | Mcr | JS | njs | Non | Lin | Pg | Lin | Raw | Rea |
149 | aspcore-mvc-ef-pg | 94,107 | 13.9% | 0 | Ful | C# | .NE | kes | Lin | Pg | Lin | Ful | Rea |
150 | fastify-mysql | 93,665 | 13.8% | 0 | Mcr | JS | njs | Non | Lin | My | Lin | Raw | Rea |
151 | servlet-mysql | 92,967 | 13.7% | 0 | Plt | Jav | Svt | Res | Lin | My | Lin | Raw | Rea |
152 | nodejs | 91,799 | 13.5% | 0 | Plt | JS | njs | Non | Lin | My | Lin | Raw | Rea |
153 | nodejs-chakra | 91,778 | 13.5% | 0 | Plt | JS | njs | Non | Lin | My | Lin | Raw | Rea |
154 | fastify | 91,769 | 13.5% | 0 | Mcr | JS | njs | Non | Lin | Mo | Lin | Raw | Rea |
155 | wt | 90,647 | 13.4% | 0 | Ful | C++ | Non | Non | Lin | My | Lin | Ful | Rea |
156 | micronaut | 90,601 | 13.4% | 0 | Mcr | Jav | Nty | Non | Lin | Pg | Lin | Raw | Rea |
157 | act-morphia-mongo | 89,840 | 13.2% | 0 | Ful | Jav | Utw | Non | Lin | Mo | Lin | Ful | Rea |
158 | redkale-postgres | 89,236 | 13.2% | 0 | Ful | Jav | red | Non | Lin | Pg | Lin | Raw | Rea |
159 | act-morphia-mongo-rythm | 89,013 | 13.1% | 0 | Ful | Jav | Utw | Non | Lin | Mo | Lin | Ful | Rea |
160 | servant-psql-simple | 88,759 | 13.1% | 0 | Mcr | Hkl | Wai | Wrp | Lin | Pg | Lin | Raw | Rea |
161 | pedestal | 88,252 | 13.0% | 0 | Mcr | Clj | Jty | Non | Lin | My | Lin | Mcr | Rea |
162 | go-my | 87,930 | 13.0% | 2 | Plt | Go | Non | Non | Lin | My | Lin | Raw | Rea |
163 | hexagon | 87,718 | 12.9% | 0 | Mcr | Kot | Svt | Non | Lin | Mo | Lin | Raw | Rea |
164 | play2-java-jpa-hikaricp-netty | 85,449 | 12.6% | 0 | Ful | Jav | Nty | Non | Lin | My | Lin | Ful | Rea |
165 | minijax | 85,399 | 12.6% | 0 | Ful | Jav | JAX | Utw | Lin | My | Lin | Ful | Rea |
166 | fasthttp-mongo | 83,915 | 12.4% | 0 | Plt | Go | Non | Non | Lin | Mo | Lin | Raw | Rea |
167 | cutelyst-nginx-my | 83,551 | 12.3% | 0 | Ful | C++ | uWS | ngx | Lin | My | Lin | Raw | Rea |
168 | atreugo-mongo | 81,040 | 11.9% | 0 | Plt | Go | Non | Non | Lin | Mo | Lin | Raw | Rea |
169 | treefrog-postgres | 79,580 | 11.7% | 8 | Ful | C++ | Non | Non | Lin | Pg | Lin | Mcr | Rea |
170 | duct | 79,059 | 11.7% | 0 | Mcr | Clj | Rin | Non | Lin | Pg | Lin | Raw | Rea |
171 | vertx-web-mongodb | 78,538 | 11.6% | 0 | Mcr | Jav | ver | Non | Lin | Mo | Lin | Raw | Rea |
172 | hamlet-workerman | 78,214 | 11.5% | 0 | Ful | PHP | wor | Non | Lin | My | Lin | Mcr | Rea |
173 | compojure-raw | 76,832 | 11.3% | 0 | Mcr | Clj | Svt | Res | Lin | My | Lin | Raw | Rea |
174 | cutelyst-thread-my-grantlee | 75,816 | 11.2% | 0 | Ful | C++ | cut | Non | Lin | My | Lin | Raw | Rea |
175 | http-kit-raw | 75,309 | 11.1% | 0 | Plt | Clj | Rin | Non | Lin | My | Lin | Raw | Rea |
176 | kumbiaphp | 74,171 | 10.9% | 0 | Ful | PHP | fpm | ngx | Lin | My | Lin | Mcr | Rea |
177 | kumbiaphp-raw | 73,714 | 10.9% | 0 | Ful | PHP | fpm | ngx | Lin | My | Lin | Raw | Rea |
178 | duct-mongodb | 73,630 | 10.9% | 0 | Mcr | Clj | Rin | Non | Lin | Mo | Lin | Raw | Rea |
179 | echo | 73,300 | 10.8% | 0 | Mcr | Go | Non | Non | Lin | Pg | Lin | Raw | Rea |
180 | uvicorn | 72,929 | 10.8% | 0 | Plt | Py | Non | Non | Lin | Pg | Lin | Raw | Rea |
181 | ubiquity | 72,844 | 10.7% | 0 | Ful | PHP | fpm | ngx | Lin | My | Lin | Ful | Rea |
182 | play2-java-jooq-hikaricp-netty | 72,685 | 10.7% | 0 | Ful | Jav | Nty | Non | Lin | My | Lin | Ful | Rea |
183 | hamlet-swoole | 70,964 | 10.5% | 0 | Ful | PHP | swo | Non | Lin | My | Lin | Mcr | Rea |
184 | treefrog | 70,769 | 10.4% | 10 | Ful | C++ | Non | Non | Lin | My | Lin | Mcr | Rea |
185 | apidaora-core | 70,659 | 10.4% | 0 | Mcr | Py | Non | Non | Lin | Pg | Lin | Raw | Rea |
186 | duct-aleph | 70,285 | 10.4% | 0 | Mcr | Clj | Nty | Non | Lin | Pg | Lin | Raw | Rea |
187 | aah-mysql | 69,752 | 10.3% | 0 | Ful | Go | Non | Non | Lin | My | Lin | Raw | Rea |
188 | roa-diesel | 68,129 | 10.0% | 0 | Mcr | Rus | Non | Hyp | Lin | Pg | Lin | Ful | Rea |
189 | nestjs-fastify-mysql | 67,920 | 10.0% | 0 | Mcr | typ | Non | Non | Lin | My | Lin | Ful | Rea |
190 | play2-java-jpa-hikaricp | 67,361 | 9.9% | 0 | Ful | Jav | Akk | Non | Lin | My | Lin | Ful | Rea |
191 | nestjs-fastify | 67,331 | 9.9% | 0 | Mcr | typ | Non | Non | Lin | Pg | Lin | Ful | Rea |
192 | blacksheep | 67,321 | 9.9% | 0 | Plt | Py | Non | Non | Lin | Pg | Lin | Raw | Rea |
193 | gin-scratch | 67,227 | 9.9% | 0 | Mcr | Go | Non | Non | Lin | My | Lin | Raw | Rea |
194 | play2-java-ebean-hikaricp-netty | 66,647 | 9.8% | 0 | Ful | Jav | Nty | Non | Lin | My | Lin | Ful | Rea |
195 | luminus | 66,471 | 9.8% | 0 | Mcr | Clj | Rin | Non | Lin | Pg | Lin | Raw | Rea |
196 | ubiquity-roadrunner | 66,418 | 9.8% | 0 | Ful | PHP | roa | Non | Lin | Pg | Lin | Ful | Rea |
197 | ubiquity-roadrunner-mysql | 65,948 | 9.7% | 0 | Ful | PHP | roa | Non | Lin | My | Lin | Ful | Rea |
198 | undertow-jersey-hikaricp | 65,485 | 9.7% | 0 | Plt | Jav | JAX | Non | Lin | My | Lin | Ful | Rea |
199 | sanic | 65,039 | 9.6% | 0 | Mcr | Py | Non | Non | Lin | Pg | Lin | Raw | Rea |
200 | lapis | 64,724 | 9.5% | 0 | Ful | Lua | OpR | ngx | Lin | Pg | Lin | Ful | Rea |
201 | nestjs-fastify-mongo | 64,206 | 9.5% | 0 | Mcr | typ | Non | Non | Lin | Mo | Lin | Ful | Rea |
202 | grizzly-jersey | 63,557 | 9.4% | 0 | Mcr | Jav | JAX | Grz | Lin | My | Lin | Ful | Rea |
203 | apidaora | 63,489 | 9.4% | 0 | Mcr | Py | Non | Non | Lin | Pg | Lin | Raw | Rea |
204 | chubbajs | 63,215 | 9.3% | 0 | Mcr | JS | njs | Non | Lin | Pg | Lin | Mcr | Rea |
205 | starlette | 62,707 | 9.2% | 0 | Mcr | Py | Non | Non | Lin | Pg | Lin | Raw | Rea |
206 | jawn | 62,631 | 9.2% | 0 | Ful | Jav | Utw | Non | Lin | Pg | Lin | Raw | Rea |
207 | dropwizard-postgres | 62,354 | 9.2% | 0 | Ful | Jav | JAX | Jty | Lin | Pg | Lin | Ful | Rea |
208 | php-raw7-tcp | 60,871 | 9.0% | 21,542 | Plt | PHP | fpm | ngx | Lin | My | Lin | Raw | Rea |
209 | play2-scala-anorm-netty | 60,767 | 9.0% | 0 | Ful | Sca | Nty | Non | Lin | My | Lin | Ful | Rea |
210 | quarkus-hibernate | 60,752 | 9.0% | 0 | Ful | Jav | JAX | Non | Lin | Pg | Lin | Ful | Rea |
211 | play2-java-jooq-hikaricp | 59,473 | 8.8% | 0 | Ful | Jav | Akk | Non | Lin | My | Lin | Ful | Rea |
212 | vibed-ldc-pgsql | 59,436 | 8.8% | 0 | Plt | D | Non | Non | Lin | Pg | Lin | Raw | Rea |
213 | gin | 58,974 | 8.7% | 0 | Mcr | Go | Non | Non | Lin | My | Lin | Raw | Rea |
214 | vibed-dmd-pgsql | 58,795 | 8.7% | 0 | Plt | D | Non | Non | Lin | Pg | Lin | Raw | Rea |
215 | vibed-ldc | 56,526 | 8.3% | 0 | Plt | D | Non | Non | Lin | Mo | Lin | Mcr | Rea |
216 | play2-scala-slick-netty | 56,293 | 8.3% | 0 | Ful | Sca | Nty | Non | Lin | My | Lin | Ful | Rea |
217 | nodejs-postgres | 55,925 | 8.2% | 0 | Plt | JS | njs | Non | Lin | Pg | Lin | Ful | Rea |
218 | play2-java-ebean-hikaricp | 55,653 | 8.2% | 0 | Ful | Jav | Akk | Non | Lin | My | Lin | Ful | Rea |
219 | ktor | 54,879 | 8.1% | 0 | Mcr | Kot | Nty | Non | Lin | Pg | Lin | Raw | Rea |
220 | roa-pg | 54,664 | 8.1% | 0 | Mcr | Rus | Non | Hyp | Lin | Pg | Lin | Raw | Rea |
221 | nestjs | 53,905 | 7.9% | 0 | Mcr | typ | Non | Non | Lin | Pg | Lin | Ful | Rea |
222 | dropwizard | 53,256 | 7.9% | 0 | Ful | Jav | JAX | Jty | Lin | My | Lin | Ful | Rea |
223 | vibed | 52,780 | 7.8% | 0 | Mcr | D | Non | Non | Lin | Mo | Lin | Mcr | Rea |
224 | lithium-postgres-1t | 52,667 | 7.8% | 0 | Mcr | C++ | Non | Non | Lin | Pg | Lin | Ful | Rea |
225 | play2-scala-anorm | 52,585 | 7.8% | 0 | Ful | Sca | Akk | Non | Lin | My | Lin | Ful | Rea |
226 | fastapi-orjson | 52,080 | 7.7% | 0 | Mcr | Py | Non | Non | Lin | Pg | Lin | Raw | Rea |
227 | officefloor-tpr | 52,053 | 7.7% | 0 | Ful | Jav | off | woo | Lin | Pg | Lin | Raw | Rea |
228 | fastapi | 51,981 | 7.7% | 0 | Mcr | Py | Non | Non | Lin | Pg | Lin | Raw | Rea |
229 | officefloor-thread_affinity | 51,936 | 7.7% | 0 | Ful | Jav | off | woo | Lin | Pg | Lin | Raw | Rea |
230 | officefloor-micro | 51,760 | 7.6% | 0 | Ful | Jav | off | woo | Lin | Pg | Lin | Raw | Rea |
231 | roda-sequel-postgres | 50,981 | 7.5% | 0 | Mcr | Rby | Rac | Pum | Lin | Pg | Lin | Ful | Rea |
232 | nestjs-mysql | 50,712 | 7.5% | 0 | Mcr | typ | Non | Non | Lin | My | Lin | Ful | Rea |
233 | nodejs-mysql | 50,692 | 7.5% | 0 | Plt | JS | njs | Non | Lin | My | Lin | Ful | Rea |
234 | lithium-mysql-1t | 50,460 | 7.4% | 0 | Mcr | C++ | Non | Non | Lin | My | Lin | Ful | Rea |
235 | revenj | 50,341 | 7.4% | 0 | Ful | C# | Non | Non | Lin | Pg | Lin | Ful | Rea |
236 | sw-fw-less | 49,667 | 7.3% | 2,861 | Mcr | PHP | swo | Non | Lin | My | Lin | Ful | Rea |
237 | phoenix | 49,186 | 7.3% | 0 | Ful | Eli | Cow | Non | Lin | Pg | Lin | Ful | Rea |
238 | roda-sequel | 49,140 | 7.2% | 0 | Mcr | Rby | Rac | Pum | Lin | My | Lin | Ful | Rea |
239 | http-kit | 49,067 | 7.2% | 0 | Plt | Clj | Rin | Non | Lin | My | Lin | Mcr | Rea |
240 | symfony-swoole | 48,130 | 7.1% | 0 | Ful | PHP | swo | Non | Lin | My | Lin | Ful | Rea |
241 | blade | 47,774 | 7.0% | 0 | Ful | Jav | Non | Non | Lin | My | Lin | Ful | Rea |
242 | nestjs-mongo | 47,518 | 7.0% | 0 | Mcr | typ | Non | Non | Lin | Mo | Lin | Ful | Rea |
243 | redstone | 46,969 | 6.9% | 0 | Mcr | Dar | Non | Non | Lin | Pg | Lin | Mcr | Rea |
244 | koa-postgres | 46,451 | 6.8% | 0 | Mcr | JS | njs | Non | Lin | Pg | Lin | Raw | Rea |
245 | compojure | 45,881 | 6.8% | 0 | Mcr | Clj | Svt | Res | Lin | My | Lin | Mcr | Rea |
246 | koa-mysql | 45,846 | 6.8% | 0 | Mcr | JS | njs | Non | Lin | My | Lin | Raw | Rea |
247 | play2-scala-slick | 45,521 | 6.7% | 0 | Ful | Sca | Akk | Non | Lin | My | Lin | Ful | Rea |
248 | roa-sqlx | 44,869 | 6.6% | 0 | Mcr | Rus | Non | Hyp | Lin | Pg | Lin | Raw | Rea |
249 | express-mysql | 44,166 | 6.5% | 0 | Mcr | JS | njs | Non | Lin | My | Lin | Ful | Rea |
250 | responder | 43,647 | 6.4% | 0 | Plt | Py | Non | Non | Lin | Pg | Lin | Raw | Rea |
251 | tapestry | 42,982 | 6.3% | 0 | Ful | Jav | Svt | Res | Lin | My | Lin | Ful | Rea |
252 | officefloor-netty | 42,591 | 6.3% | 0 | Ful | Jav | off | woo | Lin | Pg | Lin | Ful | Rea |
253 | undertow-jersey | 42,567 | 6.3% | 0 | Plt | Jav | JAX | Non | Lin | My | Lin | Ful | Rea |
254 | wicket | 41,489 | 6.1% | 0 | Ful | Jav | Svt | Res | Lin | My | Lin | Ful | Rea |
255 | officefloor | 40,849 | 6.0% | 0 | Ful | Jav | off | woo | Lin | Pg | Lin | Ful | Rea |
256 | start | 40,689 | 6.0% | 0 | Mcr | Dar | Non | ngx | Lin | Mo | Lin | Raw | Rea |
257 | roda-sequel-postgres-passenger-mri | 40,281 | 5.9% | 0 | Mcr | Rby | Rac | Pas | Lin | Pg | Lin | Ful | Rea |
258 | roda-sequel-passenger-mri | 39,824 | 5.9% | 0 | Mcr | Rby | Rac | Pas | Lin | My | Lin | Ful | Rea |
259 | spark | 39,411 | 5.8% | 0 | Mcr | Jav | Svt | Jty | Lin | My | Lin | Ful | Rea |
260 | redstone-mongodb | 38,873 | 5.7% | 0 | Mcr | Dar | Non | Non | Lin | Mo | Lin | Mcr | Rea |
261 | http4k-ktorcio | 38,385 | 5.7% | 0 | Mcr | Kot | kto | Non | Lin | Pg | Lin | Raw | Rea |
262 | aiohttp-pg-raw | 36,432 | 5.4% | 0 | Mcr | Py | asy | Gun | Lin | Pg | Lin | Raw | Rea |
263 | ktor-reactivepg | 36,141 | 5.3% | 0 | Ful | Kot | Non | Non | Lin | Pg | Lin | Raw | Rea |
264 | ffead-cpp-nginx-mongo | 35,466 | 5.2% | 0 | Ful | C++ | ffe | ngx | Lin | Mo | Lin | Ful | Rea |
265 | stream | 35,416 | 5.2% | 0 | Mcr | Dar | Non | ngx | Lin | Mo | Lin | Raw | Rea |
266 | bottle-raw | 35,031 | 5.2% | 0 | Mcr | Py | Mei | Non | Lin | My | Lin | Raw | Rea |
267 | spiral | 34,641 | 5.1% | 0 | Ful | PHP | roa | Non | Lin | My | Lin | Ful | Rea |
268 | roda-sequel-postgres-torquebox-jruby | 34,601 | 5.1% | 0 | Mcr | Rby | Rac | Tor | Lin | Pg | Lin | Ful | Rea |
269 | sinatra-sequel-postgres | 34,114 | 5.0% | 0 | Mcr | Rby | Rac | Pum | Lin | Pg | Lin | Ful | Rea |
270 | imi | 33,593 | 5.0% | 0 | Ful | PHP | swo | Non | Lin | My | Lin | Ful | Rea |
271 | sinatra-sequel | 32,998 | 4.9% | 0 | Mcr | Rby | Rac | Pum | Lin | My | Lin | Ful | Rea |
272 | ktor-cio | 32,514 | 4.8% | 0 | Mcr | Kot | kto | Non | Lin | Pg | Lin | Raw | Rea |
273 | ktor-jasync | 31,618 | 4.7% | 0 | Ful | Kot | Non | Non | Lin | Pg | Lin | Raw | Rea |
274 | slim | 31,441 | 4.6% | 0 | Mcr | PHP | fpm | ngx | Lin | My | Lin | Raw | Rea |
275 | http4s | 31,328 | 4.6% | 0 | Mcr | Sca | NIO | bla | Lin | Pg | Lin | Raw | Rea |
276 | iron | 30,908 | 4.6% | 0 | Mcr | Rus | Rus | Hyp | Lin | Pg | Lin | Raw | Rea |
277 | roda-sequel-torquebox-jruby | 29,168 | 4.3% | 0 | Mcr | Rby | Rac | Tor | Lin | My | Lin | Ful | Rea |
278 | hot-mysql | 28,409 | 4.2% | 75 | Plt | Grv | Jty | Jty | Lin | My | Lin | Raw | Rea |
279 | rocket | 28,343 | 4.2% | 0 | Ful | Rus | Rus | Hyp | Lin | Pg | Lin | Ful | Rea |
280 | sinatra-sequel-postgres-passenger-mri | 28,289 | 4.2% | 0 | Mcr | Rby | Rac | Pas | Lin | Pg | Lin | Ful | Rea |
281 | ffead-cpp-nginx-mysql | 28,035 | 4.1% | 0 | Ful | C++ | ffe | ngx | Lin | My | Lin | Ful | Rea |
282 | sinatra-sequel-passenger-mri | 27,834 | 4.1% | 0 | Mcr | Rby | Rac | Pas | Lin | My | Lin | Ful | Rea |
283 | aqueduct | 27,525 | 4.1% | 0 | Mcr | Dar | Non | Non | Lin | Pg | Lin | Mcr | Rea |
284 | spring | 27,339 | 4.0% | 0 | Ful | Jav | tom | Non | Lin | Pg | Lin | Mcr | Rea |
285 | elixir-plug-ecto | 27,062 | 4.0% | 0 | Mcr | Eli | bea | cow | Lin | Pg | Lin | Ful | Rea |
286 | api_hour | 26,718 | 3.9% | 0 | Mcr | Py | asy | Gun | Lin | Pg | Lin | Raw | Rea |
287 | yii2-raw | 26,652 | 3.9% | 0 | Ful | PHP | fpm | ngx | Lin | My | Lin | Raw | Rea |
288 | spring-mongo | 26,424 | 3.9% | 0 | Ful | Jav | tom | Non | Lin | Mo | Lin | Ful | Rea |
289 | aspcore-mono-pg | 26,280 | 3.9% | 0 | Plt | C# | .NE | kes | Lin | Pg | Lin | Raw | Rea |
290 | quart-uvicorn | 26,082 | 3.8% | 0 | Mcr | Py | Non | uvi | Lin | Pg | Lin | Raw | Rea |
291 | quarkus-pgclient | 25,956 | 3.8% | 0 | Ful | Jav | JAX | Non | Lin | Pg | Lin | Mcr | Rea |
292 | sinatra-sequel-postgres-torquebox-jruby | 25,953 | 3.8% | 0 | Mcr | Rby | Rac | Tor | Lin | Pg | Lin | Ful | Rea |
293 | cppcms-postgres | 25,706 | 3.8% | 0 | Plt | C++ | Non | Non | Lin | Pg | Lin | Raw | Rea |
294 | roda-sequel-postgres-unicorn-mri | 25,407 | 3.7% | 0 | Mcr | Rby | Rac | Uni | Lin | Pg | Lin | Ful | Rea |
295 | lumen-swoole | 25,147 | 3.7% | 0 | Mcr | PHP | swo | Non | Lin | My | Lin | Ful | Rea |
296 | hamlet | 24,781 | 3.7% | 0 | Ful | PHP | fpm | ngx | Lin | My | Lin | Mcr | Rea |
297 | cppcms | 24,372 | 3.6% | 0 | Plt | C++ | Non | Non | Lin | My | Lin | Raw | Rea |
298 | duct-httpkit | 24,358 | 3.6% | 0 | Mcr | Clj | Rin | Non | Lin | Pg | Lin | Raw | Rea |
299 | codeigniter | 24,135 | 3.6% | 0 | Ful | PHP | fpm | ngx | Lin | My | Lin | Raw | Rea |
300 | wildfly-ee | 23,881 | 3.5% | 0 | Ful | Jav | Svt | Wil | Lin | My | Lin | Ful | Rea |
301 | aspcore-mono-my | 23,793 | 3.5% | 0 | Plt | C# | .NE | kes | Lin | My | Lin | Raw | Rea |
302 | fat-free-raw | 23,751 | 3.5% | 0 | Ful | PHP | fpm | ngx | Lin | My | Lin | Raw | Rea |
303 | flask-raw | 23,136 | 3.4% | 0 | Mcr | Py | Mei | Non | Lin | My | Lin | Raw | Rea |
304 | aiohttp | 22,972 | 3.4% | 0 | Mcr | Py | asy | Gun | Lin | Pg | Lin | Ful | Rea |
305 | spring-webflux-pgclient | 22,607 | 3.3% | 0 | Ful | Jav | Nty | Non | Lin | Pg | Lin | Mcr | Rea |
306 | spring-webflux-jdbc | 22,528 | 3.3% | 0 | Ful | Jav | Nty | Non | Lin | Pg | Lin | Mcr | Rea |
307 | sinatra-sequel-postgres-unicorn-mri | 22,308 | 3.3% | 0 | Mcr | Rby | Rac | Uni | Lin | Pg | Lin | Ful | Rea |
308 | sinatra-sequel-unicorn-mri | 22,238 | 3.3% | 0 | Mcr | Rby | Rac | Uni | Lin | My | Lin | Ful | Rea |
309 | aspcore-mono-mw-my | 21,561 | 3.2% | 0 | Mcr | C# | .NE | kes | Lin | My | Lin | Raw | Rea |
310 | sinatra-sequel-torquebox-jruby | 21,509 | 3.2% | 0 | Mcr | Rby | Rac | Tor | Lin | My | Lin | Ful | Rea |
311 | spring-webflux-mongo | 21,503 | 3.2% | 0 | Ful | Jav | Nty | Non | Lin | Mo | Lin | Ful | Rea |
312 | php-eloquent | 21,482 | 3.2% | 0 | Plt | PHP | fpm | ngx | Lin | My | Lin | Ful | Rea |
313 | go-mgo | 21,469 | 3.2% | 0 | Plt | Go | Non | Non | Lin | Mo | Lin | Raw | Rea |
314 | pyramid-py2 | 21,461 | 3.2% | 0 | Ful | Py | Non | Mei | Lin | Pg | Lin | Ful | Rea |
315 | ninja-standalone | 21,454 | 3.2% | 0 | Ful | Jav | Jty | Non | Lin | My | Lin | Ful | Rea |
316 | laravel-swoole | 20,935 | 3.1% | 0 | Ful | PHP | swo | Non | Lin | My | Lin | Ful | Rea |
317 | tornado-py3-uvloop | 20,728 | 3.1% | 0 | Plt | Py | Non | Tor | Lin | Pg | Lin | Raw | Rea |
318 | flask-pypy2-raw | 20,654 | 3.0% | 0 | Mcr | Py | Tor | Non | Lin | My | Lin | Raw | Rea |
319 | api_hour-mysql | 20,054 | 3.0% | 0 | Mcr | Py | asy | Gun | Lin | My | Lin | Raw | Rea |
320 | aspcore-mono-mw-pg | 19,321 | 2.8% | 0 | Mcr | C# | .NE | kes | Lin | Pg | Lin | Raw | Rea |
321 | pyramid | 18,538 | 2.7% | 0 | Ful | Py | Non | Mei | Lin | Pg | Lin | Ful | Rea |
322 | dropwizard-mongodb | 18,221 | 2.7% | 0 | Ful | Jav | JAX | Jty | Lin | Mo | Lin | Ful | Rea |
323 | fat-free | 18,048 | 2.7% | 0 | Ful | PHP | fpm | ngx | Lin | My | Lin | Ful | Rea |
324 | aspcore-mono-mw-ef-pg | 18,022 | 2.7% | 0 | Mcr | C# | .NE | kes | Lin | Pg | Lin | Ful | Rea |
325 | roda-sequel-unicorn-mri | 17,780 | 2.6% | 0 | Mcr | Rby | Rac | Uni | Lin | My | Lin | Ful | Rea |
326 | aspcore-mono-mvc-pg | 17,679 | 2.6% | 0 | Ful | C# | .NE | kes | Lin | Pg | Lin | Raw | Rea |
327 | morepath | 17,502 | 2.6% | 0 | Mcr | Py | Mei | Gun | Lin | Pg | Lin | Ful | Rea |
328 | activeweb | 17,470 | 2.6% | 0 | Ful | Jav | Svt | Non | Lin | My | Lin | Mcr | Rea |
329 | weppy-pypy2 | 17,463 | 2.6% | 0 | Ful | Py | Tor | Non | Lin | Pg | Lin | Ful | Rea |
330 | play2-scala-reactivemongo-netty | 16,736 | 2.5% | 0 | Ful | Sca | Nty | Non | Lin | Mo | Lin | Ful | Rea |
331 | sinatra-unicorn-mri | 16,445 | 2.4% | 0 | Mcr | Rby | Rac | Uni | Lin | My | Lin | Ful | Rea |
332 | yii2 | 16,415 | 2.4% | 0 | Ful | PHP | fpm | ngx | Lin | My | Lin | Ful | Rea |
333 | weppy-py3 | 16,005 | 2.4% | 0 | Ful | Py | Mei | Non | Lin | Pg | Lin | Ful | Rea |
334 | tornado-pypy2 | 15,986 | 2.4% | 0 | Plt | Py | Non | Tor | Lin | Mo | Lin | Raw | Rea |
335 | sinatra-postgres-unicorn-mri | 15,806 | 2.3% | 0 | Mcr | Rby | Rac | Uni | Lin | Pg | Lin | Ful | Rea |
336 | ffead-cpp | 15,622 | 2.3% | 0 | Ful | C++ | ffe | ffe | Lin | Mo | Lin | Ful | Rea |
337 | emmett | 15,547 | 2.3% | 0 | Ful | Py | Non | Non | Lin | Pg | Lin | Ful | Rea |
338 | ktor-jetty | 15,439 | 2.3% | 0 | Mcr | Kot | Jty | Non | Lin | Pg | Lin | Raw | Rea |
339 | hapi-postgres | 15,127 | 2.2% | 0 | Mcr | JS | njs | Non | Lin | Pg | Lin | Ful | Rea |
340 | play2-scala-reactivemongo | 15,100 | 2.2% | 0 | Ful | Sca | Akk | Non | Lin | Mo | Lin | Ful | Rea |
341 | django-postgresql | 15,085 | 2.2% | 0 | Ful | Py | Non | Mei | Lin | Pg | Lin | Ful | Rea |
342 | sinatra-postgres-passenger-mri | 15,015 | 2.2% | 0 | Mcr | Rby | Rac | Pas | Lin | Pg | Lin | Ful | Rea |
343 | sinatra-passenger-mri | 14,920 | 2.2% | 0 | Mcr | Rby | Rac | Pas | Lin | My | Lin | Ful | Rea |
344 | weppy-nginx-uwsgi | 14,751 | 2.2% | 0 | Ful | Py | uWS | ngx | Lin | Pg | Lin | Ful | Rea |
345 | django | 14,699 | 2.2% | 0 | Ful | Py | Non | Mei | Lin | My | Lin | Ful | Rea |
346 | hapi-mysql | 14,678 | 2.2% | 0 | Mcr | JS | njs | Non | Lin | My | Lin | Ful | Rea |
347 | aspcore-mono-mvc-ef-pg | 14,407 | 2.1% | 0 | Ful | C# | .NE | kes | Lin | Pg | Lin | Ful | Rea |
348 | ringojs-convenient | 14,033 | 2.1% | 0 | Mcr | JS | Rin | Non | Lin | My | Lin | Mcr | Rea |
349 | aspcore-mono-mvc-my | 13,904 | 2.0% | 0 | Ful | C# | .NE | kes | Lin | My | Lin | Raw | Rea |
350 | bottle-pypy2 | 13,727 | 2.0% | 0 | Mcr | Py | Tor | Non | Lin | My | Lin | Ful | Rea |
351 | macchiato | 13,493 | 2.0% | 0 | Mcr | Clj | njs | Non | Lin | Pg | Lin | Raw | Rea |
352 | mojolicious | 12,464 | 1.8% | 0 | Ful | Prl | Non | Hyp | Lin | Pg | Lin | Raw | Rea |
353 | tornado-py3 | 12,328 | 1.8% | 0 | Plt | Py | Non | Tor | Lin | Mo | Lin | Raw | Rea |
354 | phalcon | 12,253 | 1.8% | 0 | Ful | PHP | fpm | ngx | Lin | My | Lin | Raw | Rea |
355 | turbogears | 11,931 | 1.8% | 0 | Mcr | Py | Non | Mei | Lin | My | Lin | Ful | Rea |
356 | bottle | 11,880 | 1.8% | 0 | Mcr | Py | Mei | Non | Lin | My | Lin | Ful | Rea |
357 | fuel | 11,759 | 1.7% | 0 | Mcr | PHP | fpm | ngx | Lin | My | Lin | Raw | Rea |
358 | grails | 11,287 | 1.7% | 0 | Ful | Grv | Svt | Non | Lin | My | Lin | Ful | Rea |
359 | web2py-optimized | 11,074 | 1.6% | 0 | Ful | Py | Mei | Non | Lin | My | Lin | Ful | Rea |
360 | flask | 10,101 | 1.5% | 0 | Mcr | Py | Mei | Non | Lin | My | Lin | Ful | Rea |
361 | goframe | 9,869 | 1.5% | 0 | Mcr | Go | Non | Non | Lin | My | Lin | Raw | Rea |
362 | flask-pypy2 | 9,008 | 1.3% | 0 | Mcr | Py | Non | Tor | Lin | My | Lin | Ful | Rea |
363 | rails | 8,764 | 1.3% | 0 | Ful | Rby | Rac | Pum | Lin | My | Lin | Ful | Rea |
364 | tornado | 8,702 | 1.3% | 0 | Plt | Py | Non | Tor | Lin | Mo | Lin | Raw | Rea |
365 | hapi-nginx | 8,624 | 1.3% | 0 | Mcr | JS | njs | ngx | Lin | Pg | Lin | Ful | Rea |
366 | rails-unicorn | 8,528 | 1.3% | 0 | Ful | Rby | Rac | Uni | Lin | My | Lin | Ful | Rea |
367 | lumen | 8,435 | 1.2% | 0 | Mcr | PHP | fpm | ngx | Lin | My | Lin | Ful | Rea |
368 | rails-postgresql | 8,434 | 1.2% | 0 | Ful | Rby | Rac | Pum | Lin | Pg | Lin | Ful | Rea |
369 | cakephp | 7,418 | 1.1% | 0 | Ful | PHP | fpm | ngx | Lin | My | Lin | Ful | Rea |
370 | symfony | 7,137 | 1.1% | 0 | Ful | PHP | fpm | ngx | Lin | My | Lin | Ful | Rea |
371 | bottle-nginx-uwsgi | 6,640 | 1.0% | 0 | Mcr | Py | uWS | ngx | Lin | My | Lin | Ful | Rea |
372 | flask-nginx-uwsgi | 6,597 | 1.0% | 0 | Mcr | Py | Non | ngx | Lin | My | Lin | Ful | Rea |
373 | web2py | 6,014 | 0.9% | 0 | Ful | Py | Mei | Non | Lin | My | Lin | Ful | Rea |
374 | kitura-postgres | 5,682 | 0.8% | 0 | Ful | Swi | kit | kit | Lin | Pg | Lin | Raw | Rea |
375 | kitura-gcd-postgres | 5,605 | 0.8% | 0 | Ful | Swi | kit | kit | Lin | Pg | Lin | Raw | Rea |
376 | express-mongodb | 5,588 | 0.8% | 0 | Mcr | JS | njs | Non | Lin | Mo | Lin | Ful | Rea |
377 | laravel | 5,563 | 0.8% | 0 | Ful | PHP | fpm | ngx | Lin | My | Lin | Ful | Rea |
378 | kitura-nio-postgres | 5,465 | 0.8% | 0 | Ful | Swi | kit | kit | Lin | Pg | Lin | Raw | Rea |
379 | httpserver-postgres | 5,421 | 0.8% | 363,929 | Plt | Jav | htt | Non | Lin | Pg | Lin | Raw | Rea |
380 | kitura-postgres-orm | 5,420 | 0.8% | 0 | Ful | Swi | kit | kit | Lin | Pg | Lin | Ful | Rea |
381 | kitura-gcd-postgres-orm | 5,350 | 0.8% | 0 | Ful | Swi | kit | kit | Lin | Pg | Lin | Ful | Rea |
382 | kitura-nio-postgres-orm | 5,228 | 0.8% | 0 | Ful | Swi | kit | kit | Lin | Pg | Lin | Ful | Rea |
383 | kitura-mongodb | 4,657 | 0.7% | 0 | Ful | Swi | kit | kit | Lin | Mo | Lin | Raw | Rea |
384 | urweb-mysql | 4,600 | 0.7% | 0 | Ful | Ur | Ur/ | Non | Lin | My | Lin | Mcr | Rea |
385 | koa | 4,528 | 0.7% | 0 | Mcr | JS | njs | Non | Lin | Mo | Lin | Raw | Rea |
386 | hapi | 4,341 | 0.6% | 0 | Mcr | JS | njs | Non | Lin | Mo | Lin | Ful | Rea |
387 | padrino-unicorn | 3,275 | 0.5% | 0 | Mcr | Rby | Rac | Uni | Lin | My | Lin | Ful | Rea |
388 | padrino | 3,201 | 0.5% | 0 | Mcr | Rby | Rac | Pum | Lin | My | Lin | Ful | Rea |
389 | urweb | 2,939 | 0.4% | 0 | Ful | Ur | Ur/ | Non | Lin | Pg | Lin | Mcr | Rea |
390 | express-graphql-mysql | 2,817 | 0.4% | 0 | Mcr | JS | njs | Non | Lin | My | Lin | Ful | Rea |
391 | officefloor-spring_data | 2,622 | 0.4% | 0 | Ful | Jav | off | woo | Lin | Pg | Lin | Ful | Rea |
392 | ffead-cpp-nginx-postgresql | 2,403 | 0.4% | 0 | Ful | C++ | ffe | ngx | Lin | Pg | Lin | Ful | Rea |
393 | spyne-nginx-uwsgi | 2,279 | 0.3% | 0 | Mcr | Py | Non | ngx | Lin | Pg | Lin | Ful | Rea |
394 | spyne-raw | 1,892 | 0.3% | 28 | Mcr | Py | spy | Non | Lin | Pg | Lin | Raw | Rea |
395 | phalcon-micro | 1,886 | 0.3% | 53,048 | Mcr | PHP | fpm | ngx | Lin | My | Lin | Raw | Rea |
396 | akka-http-slick-postgres | 1,796 | 0.3% | 0 | Mcr | Sca | Akk | Non | Lin | Pg | Lin | Ful | Rea |
397 | aspnet-mono-ngx-my | 1,545 | 0.2% | 0 | Plt | C# | .NE | ngx | Lin | My | Lin | Raw | Rea |
398 | phpixie | 1,375 | 0.2% | 53,987 | Ful | PHP | fpm | ngx | Lin | My | Lin | Ful | Rea |
399 | express-graphql-postgres | 1,087 | 0.2% | 0 | Mcr | JS | njs | Non | Lin | Pg | Lin | Ful | Rea |
400 | racket | 883 | 0.1% | 0 | Mcr | rac | rac | rac | Lin | Pg | Lin | Raw | Rea |
401 | express-postgres | 718 | 0.1% | 0 | Mcr | JS | njs | Non | Lin | Pg | Lin | Ful | Rea |
402 | klein | 578 | 0.1% | 0 | Mcr | Py | Non | Twi | Lin | My | Lin | Ful | Rea |
403 | express-graphql-mongodb | 492 | 0.1% | 0 | Mcr | JS | njs | Non | Lin | Mo | Lin | Ful | Rea |
404 | sailsjs | 443 | 0.1% | 0 | Ful | JS | njs | Non | Lin | My | Lin | Mcr | Rea |
405 | http4k-netty | 1 | 0.0% | 657,083 | Mcr | Kot | Nty | Non | Lin | Pg | Lin | Raw | Rea |
406 | amber | — | Did not complete | — | Ful | Cry | Non | Non | Lin | Pg | Lin | Ful | Rea |
407 | beetlex-core | — | Did not complete | — | Plt | C# | .NE | bee | Lin | Pg | Lin | Raw | Rea |
408 | falcore | — | Did not complete | — | Mcr | Go | Non | Non | Lin | My | Lin | Raw | Rea |
409 | ffead-cpp-mysql | — | Did not complete | — | Ful | C++ | ffe | ffe | Lin | My | Lin | Ful | Rea |
410 | ffead-cpp-postgresql | — | Did not complete | — | Ful | C++ | ffe | ffe | Lin | Pg | Lin | Ful | Rea |
411 | goji | — | Did not complete | — | Mcr | Go | Non | Non | Lin | My | Lin | Raw | Rea |
412 | hanami | — | Did not complete | — | Ful | Rby | Rac | Pum | Lin | My | Lin | Ful | Rea |
413 | hanami-unicorn | — | Did not complete | — | Ful | Rby | Rac | Uni | Lin | My | Lin | Ful | Rea |
414 | kami | — | Did not complete | — | Mcr | Go | Non | Non | Lin | My | Lin | Raw | Rea |
415 | cherrypy | — | Did not complete | — | Mcr | Py | Non | Non | Lin | My | Lin | Ful | Rea |
416 | cherrypy-py3 | — | Did not complete | — | Mcr | Py | Non | Non | Lin | My | Lin | Ful | Rea |
417 | pronghorn | — | Did not complete | — | Plt | Kot | pro | Non | Lin | Mo | Lin | Raw | Rea |
418 | sailsjs-postgres | — | Did not complete | — | Ful | JS | njs | Non | Lin | Pg | Lin | Ful | Rea |
419 | quart | — | Did not complete | — | Mcr | Py | Non | hyp | Lin | Pg | Lin | Raw | Rea |
420 | sinatra | — | Did not complete | — | Mcr | Rby | Rac | Pum | Lin | My | Lin | Ful | Rea |
421 | sinatra-postgres | — | Did not complete | — | Mcr | Rby | Rac | Pum | Lin | Pg | Lin | Ful | Rea |
422 | spider-gazelle | — | Did not complete | — | Ful | Cry | Non | Non | Lin | Pg | Lin | Ful | Rea |
423 | spring-webflux-rxjdbc | — | Did not complete | — | Ful | Jav | Nty | Non | Lin | Pg | Lin | Mcr | Rea |
424 | spyne | — | Did not complete | — | Mcr | Py | spy | Non | Lin | Pg | Lin | Ful | Rea |
原文:https://www.techempower.com/benchmarks/
本文:http://jiagoushi.pro/node/1118
讨论:请加入知识星球【首席架构师圈】或者小号【jiagoushi_pro】
- 147 次浏览
【Web应用架构】Web框架基准测试 2020-05-2 8第19轮 框架比较
Framework overhead of fortunes responses, Dell R440 Xeon Gold + 10 GbE
Framework best | vs | Platform best | Ratio (higher is better) | ||
---|---|---|---|---|---|
drogon-core | 678,278 | vs | nodejs | 91,799 | 738.9% |
roda-sequel-postgres-passenger-mri | 40,281 | vs | rack-sequel-postgres-passenger-mri | 5,644 | 713.7% |
drogon | 553,366 | vs | nodejs | 91,799 | 602.8% |
roda-sequel-passenger-mri | 39,824 | vs | rack-sequel-passenger-mri | 6,774 | 587.9% |
sinatra-sequel-postgres-passenger-mri | 28,289 | vs | rack-sequel-postgres-passenger-mri | 5,644 | 501.2% |
lithium-postgres | 401,783 | vs | nodejs | 91,799 | 437.7% |
sinatra-sequel-passenger-mri | 27,834 | vs | rack-sequel-passenger-mri | 6,774 | 410.9% |
php-ngx-pgsql | 356,507 | vs | php | 97,727 | 364.8% |
workerman-pgsql | 352,508 | vs | php | 97,727 | 360.7% |
greenlightning | 318,601 | vs | nodejs | 91,799 | 347.1% |
lithium | 296,750 | vs | nodejs | 91,799 | 323.3% |
workerman | 291,339 | vs | php | 97,727 | 298.1% |
php-ngx-mysql | 290,312 | vs | php | 97,727 | 297.1% |
swoole | 283,728 | vs | php | 97,727 | 290.3% |
es4x | 237,751 | vs | nodejs | 91,799 | 259.0% |
tornado-py3-uvloop | 20,728 | vs | tornado | 8,702 | 238.2% |
grip | 207,490 | vs | nodejs | 91,799 | 226.0% |
tornado-pypy2 | 15,986 | vs | tornado | 8,702 | 183.7% |
php-ngx-async | 151,834 | vs | php | 97,727 | 155.4% |
swoole-no-async | 151,102 | vs | php | 97,727 | 154.6% |
undertow-jersey-hikaricp | 65,485 | vs | undertow-jersey | 42,567 | 153.8% |
http-kit-raw | 75,309 | vs | http-kit | 49,067 | 153.5% |
polkadot-postgres | 131,783 | vs | nodejs | 91,799 | 143.6% |
tornado-py3 | 12,328 | vs | tornado | 8,702 | 141.7% |
treefrog-mongodb | 96,231 | vs | treefrog | 70,769 | 136.0% |
nodejs-mongodb-raw | 115,477 | vs | nodejs | 91,799 | 125.8% |
lwan | 114,729 | vs | nodejs | 91,799 | 125.0% |
vertx-web-scala | 214,768 | vs | vertx-web-postgres | 176,093 | 122.0% |
workerman-async | 118,772 | vs | php | 97,727 | 121.5% |
polkadot-mongodb | 111,286 | vs | nodejs | 91,799 | 121.2% |
http4k-ktorcio | 38,385 | vs | ktor-cio | 32,514 | 118.1% |
vibed-ldc-pgsql | 59,436 | vs | vibed | 52,780 | 112.6% |
polkadot-mysql | 103,016 | vs | nodejs | 91,799 | 112.2% |
vibed-dmd-pgsql | 58,795 | vs | vibed | 52,780 | 111.4% |
php-unit | 106,582 | vs | php | 97,727 | 109.1% |
php-pgsql-raw | 104,730 | vs | php | 97,727 | 107.2% |
vibed-ldc | 56,526 | vs | vibed | 52,780 | 107.1% |
aspcore-rhtx-pg | 285,398 | vs | aspcore-ado-pg | 273,121 | 104.5% |
roda-sequel-torquebox-jruby | 29,168 | vs | rack-sequel-torquebox-jruby | 28,046 | 104.0% |
fastify-postgres | 95,088 | vs | nodejs | 91,799 | 103.6% |
fastify-mysql | 93,665 | vs | nodejs | 91,799 | 102.0% |
kemal | 208,775 | vs | crystal | 207,551 | 100.6% |
nodejs-chakra | 91,778 | vs | nodejs | 91,799 | 100.0% |
fastify | 91,769 | vs | nodejs | 91,799 | 100.0% |
php-h2o | 96,667 | vs | php | 97,727 | 98.9% |
php-pools | 96,572 | vs | php | 97,727 | 98.8% |
micronaut | 90,601 | vs | nodejs | 91,799 | 98.7% |
crystal-radix | 204,596 | vs | crystal | 207,551 | 98.6% |
raze | 202,714 | vs | crystal | 207,551 | 97.7% |
spring-mongo | 26,424 | vs | spring | 27,339 | 96.7% |
roda-sequel-postgres-torquebox-jruby | 34,601 | vs | rack-sequel-postgres-torquebox-jruby | 36,847 | 93.9% |
aspcore-mw-ado-my | 170,573 | vs | aspcore-ado-my | 187,309 | 91.1% |
aspcore-vb-mw-ado-my | 169,856 | vs | aspcore-ado-my | 187,309 | 90.7% |
aspcore-mono-mw-my | 21,561 | vs | aspcore-mono-my | 23,793 | 90.6% |
act-eclipselink-pgsql | 196,017 | vs | undertow-postgresql | 222,598 | 88.1% |
duct | 79,059 | vs | nodejs | 91,799 | 86.1% |
aspcore-mw-ado-pg | 234,321 | vs | aspcore-ado-pg | 273,121 | 85.8% |
kumbiaphp-workerman | 249,309 | vs | workerman | 291,339 | 85.6% |
aspcore-vb-mw-ado-pg | 227,638 | vs | aspcore-ado-pg | 273,121 | 83.3% |
aspcore-mw-dap-my | 155,853 | vs | aspcore-ado-my | 187,309 | 83.2% |
spring-webflux-pgclient | 22,607 | vs | spring | 27,339 | 82.7% |
spring-webflux-jdbc | 22,528 | vs | spring | 27,339 | 82.4% |
duct-mongodb | 73,630 | vs | nodejs | 91,799 | 80.2% |
officefloor | 40,849 | vs | officefloor-micro | 51,760 | 78.9% |
act-hibernate-pgsql | 175,656 | vs | undertow-postgresql | 222,598 | 78.9% |
spring-webflux-mongo | 21,503 | vs | spring | 27,339 | 78.7% |
apidaora-core | 70,659 | vs | nodejs | 91,799 | 77.0% |
sinatra-sequel-torquebox-jruby | 21,509 | vs | rack-sequel-torquebox-jruby | 28,046 | 76.7% |
duct-aleph | 70,285 | vs | nodejs | 91,799 | 76.6% |
kumbiaphp | 74,171 | vs | php | 97,727 | 75.9% |
kumbiaphp-raw | 73,714 | vs | php | 97,727 | 75.4% |
ubiquity | 72,844 | vs | php | 97,727 | 74.5% |
nestjs-fastify-mysql | 67,920 | vs | nodejs | 91,799 | 74.0% |
aspcore-mono-mw-pg | 19,321 | vs | aspcore-mono-pg | 26,280 | 73.5% |
nestjs-fastify | 67,331 | vs | nodejs | 91,799 | 73.3% |
aspcore-mw-dap-pg | 198,450 | vs | aspcore-ado-pg | 273,121 | 72.7% |
act-hibernate-pgsql-rythm | 159,866 | vs | undertow-postgresql | 222,598 | 71.8% |
sanic | 65,039 | vs | nodejs | 91,799 | 70.8% |
sinatra-sequel-postgres-torquebox-jruby | 25,953 | vs | rack-sequel-postgres-torquebox-jruby | 36,847 | 70.4% |
nestjs-fastify-mongo | 64,206 | vs | nodejs | 91,799 | 69.9% |
kumbiaphp-workerman-mysql | 201,807 | vs | workerman | 291,339 | 69.3% |
apidaora | 63,489 | vs | nodejs | 91,799 | 69.2% |
act-eclipselink-pgsql-rythm | 153,060 | vs | undertow-postgresql | 222,598 | 68.8% |
aspcore-mono-mw-ef-pg | 18,022 | vs | aspcore-mono-pg | 26,280 | 68.6% |
ubiquity-roadrunner | 66,418 | vs | php | 97,727 | 68.0% |
play2-java-jpa-hikaricp | 67,361 | vs | akka-http | 99,237 | 67.9% |
ubiquity-roadrunner-mysql | 65,948 | vs | php | 97,727 | 67.5% |
aspcore-mono-mvc-pg | 17,679 | vs | aspcore-mono-pg | 26,280 | 67.3% |
roda-sequel-postgres-unicorn-mri | 25,407 | vs | rack-sequel-postgres-unicorn-mri | 38,628 | 65.8% |
ubiquity-workerman | 190,634 | vs | workerman | 291,339 | 65.4% |
simps | 182,191 | vs | swoole | 283,728 | 64.2% |
php-raw7-tcp | 60,871 | vs | php | 97,727 | 62.3% |
nodejs-postgres | 55,925 | vs | nodejs | 91,799 | 60.9% |
sinatra-sequel-unicorn-mri | 22,238 | vs | rack-sequel-unicorn-mri | 36,980 | 60.1% |
play2-java-jooq-hikaricp | 59,473 | vs | akka-http | 99,237 | 59.9% |
nestjs | 53,905 | vs | nodejs | 91,799 | 58.7% |
aspcore-mono-mvc-my | 13,904 | vs | aspcore-mono-my | 23,793 | 58.4% |
servant-psql-simple | 88,759 | vs | warp | 152,392 | 58.2% |
aspcore-mw-ef-pg | 157,931 | vs | aspcore-ado-pg | 273,121 | 57.8% |
sinatra-sequel-postgres-unicorn-mri | 22,308 | vs | rack-sequel-postgres-unicorn-mri | 38,628 | 57.8% |
lithium-postgres-1t | 52,667 | vs | nodejs | 91,799 | 57.4% |
fastapi-orjson | 52,080 | vs | nodejs | 91,799 | 56.7% |
fastapi | 51,981 | vs | nodejs | 91,799 | 56.6% |
play2-java-ebean-hikaricp | 55,653 | vs | akka-http | 99,237 | 56.1% |
aspcore-mvc-ado-my | 104,075 | vs | aspcore-ado-my | 187,309 | 55.6% |
nestjs-mysql | 50,712 | vs | nodejs | 91,799 | 55.2% |
nodejs-mysql | 50,692 | vs | nodejs | 91,799 | 55.2% |
lithium-mysql-1t | 50,460 | vs | nodejs | 91,799 | 55.0% |
aspcore-mono-mvc-ef-pg | 14,407 | vs | aspcore-mono-pg | 26,280 | 54.8% |
play2-scala-anorm | 52,585 | vs | akka-http | 99,237 | 53.0% |
aspcore-mvc-dap-my | 99,238 | vs | aspcore-ado-my | 187,309 | 53.0% |
redstone | 46,969 | vs | dart | 89,867 | 52.3% |
nestjs-mongo | 47,518 | vs | nodejs | 91,799 | 51.8% |
koa-postgres | 46,451 | vs | nodejs | 91,799 | 50.6% |
koa-mysql | 45,846 | vs | nodejs | 91,799 | 49.9% |
ubiquity-swoole | 140,604 | vs | swoole | 283,728 | 49.6% |
toro | 101,088 | vs | crystal | 207,551 | 48.7% |
express-mysql | 44,166 | vs | nodejs | 91,799 | 48.1% |
roda-sequel-unicorn-mri | 17,780 | vs | rack-sequel-unicorn-mri | 36,980 | 48.1% |
play2-scala-slick | 45,521 | vs | akka-http | 99,237 | 45.9% |
aspcore-mvc-ado-pg | 124,455 | vs | aspcore-ado-pg | 273,121 | 45.6% |
start | 40,689 | vs | dart | 89,867 | 45.3% |
redstone-mongodb | 38,873 | vs | dart | 89,867 | 43.3% |
imi-raw | 121,648 | vs | swoole | 283,728 | 42.9% |
aspcore-mvc-dap-pg | 115,329 | vs | aspcore-ado-pg | 273,121 | 42.2% |
ubiquity-swoole-mysql-async | 115,321 | vs | swoole | 283,728 | 40.6% |
stream | 35,416 | vs | dart | 89,867 | 39.4% |
quart-uvicorn | 26,082 | vs | uvicorn | 72,929 | 35.8% |
spiral | 34,641 | vs | php | 97,727 | 35.4% |
aspcore-mvc-ef-pg | 94,107 | vs | aspcore-ado-pg | 273,121 | 34.5% |
lapis | 64,724 | vs | openresty | 198,054 | 32.7% |
slim | 31,441 | vs | php | 97,727 | 32.2% |
rocket | 28,343 | vs | nodejs | 91,799 | 30.9% |
aqueduct | 27,525 | vs | dart | 89,867 | 30.6% |
elixir-plug-ecto | 27,062 | vs | nodejs | 91,799 | 29.5% |
yii2-raw | 26,652 | vs | php | 97,727 | 27.3% |
hamlet-workerman | 78,214 | vs | workerman | 291,339 | 26.8% |
duct-httpkit | 24,358 | vs | nodejs | 91,799 | 26.5% |
hamlet | 24,781 | vs | php | 97,727 | 25.4% |
hamlet-swoole | 70,964 | vs | swoole | 283,728 | 25.0% |
codeigniter | 24,135 | vs | php | 97,727 | 24.7% |
fat-free-raw | 23,751 | vs | php | 97,727 | 24.3% |
php-eloquent | 21,482 | vs | php | 97,727 | 22.0% |
emmett | 15,547 | vs | uvicorn | 72,929 | 21.3% |
fat-free | 18,048 | vs | php | 97,727 | 18.5% |
sw-fw-less | 49,667 | vs | swoole | 283,728 | 17.5% |
symfony-swoole | 48,130 | vs | swoole | 283,728 | 17.0% |
yii2 | 16,415 | vs | php | 97,727 | 16.8% |
hapi-postgres | 15,127 | vs | nodejs | 91,799 | 16.5% |
hapi-mysql | 14,678 | vs | nodejs | 91,799 | 16.0% |
play2-scala-reactivemongo | 15,100 | vs | akka-http | 99,237 | 15.2% |
macchiato | 13,493 | vs | nodejs | 91,799 | 14.7% |
aspcore-mono-my | 23,793 | vs | aspcore-ado-my | 187,309 | 12.7% |
phalcon | 12,253 | vs | php | 97,727 | 12.5% |
fuel | 11,759 | vs | php | 97,727 | 12.0% |
imi | 33,593 | vs | swoole | 283,728 | 11.8% |
aspcore-mono-pg | 26,280 | vs | aspcore-ado-pg | 273,121 | 9.6% |
hapi-nginx | 8,624 | vs | nodejs | 91,799 | 9.4% |
lumen-swoole | 25,147 | vs | swoole | 283,728 | 8.9% |
lumen | 8,435 | vs | php | 97,727 | 8.6% |
cakephp | 7,418 | vs | php | 97,727 | 7.6% |
laravel-swoole | 20,935 | vs | swoole | 283,728 | 7.4% |
symfony | 7,137 | vs | php | 97,727 | 7.3% |
officefloor-spring_data | 2,622 | vs | officefloor | 40,849 | 6.4% |
express-mongodb | 5,588 | vs | nodejs | 91,799 | 6.1% |
laravel | 5,563 | vs | php | 97,727 | 5.7% |
koa | 4,528 | vs | nodejs | 91,799 | 4.9% |
hapi | 4,341 | vs | nodejs | 91,799 | 4.7% |
express-graphql-mysql | 2,817 | vs | nodejs | 91,799 | 3.1% |
phalcon-micro | 1,886 | vs | php | 97,727 | 1.9% |
akka-http-slick-postgres | 1,796 | vs | akka-http | 99,237 | 1.8% |
phpixie | 1,375 | vs | php | 97,727 | 1.4% |
express-graphql-postgres | 1,087 | vs | nodejs | 91,799 | 1.2% |
express-postgres | 718 | vs | nodejs | 91,799 | 0.8% |
express-graphql-mongodb | 492 | vs | nodejs | 91,799 | 0.5% |
sailsjs | 443 | vs | nodejs | 91,799 | 0.5% |
spring-webflux-rxjdbc | 0 | vs | spring | 27,339 | 0.0% |
原文:https://www.techempower.com/benchmarks/
本文:
讨论:请加入知识星球【首席架构师圈】或者小号【jiagoushi_pro】
- 64 次浏览
【Web应用架构】使用WebSocket构建交互式web应用程序
本指南引导您完成创建“Hello, world”应用程序的过程,该应用程序在浏览器和服务器之间来回发送消息。WebSocket是TCP之上的一个轻薄的层。这使得它适合使用“子协议”来嵌入消息。在本指南中,我们使用STOMP消息传递和Spring创建交互式web应用程序。
你将建立什么
您将构建一个接收带有用户名称的消息的服务器。作为响应,服务器将把问候语推送到客户机订阅的队列中。
你需要的东西
- 大约15分钟
- 最喜欢的文本编辑器或IDE
- JDK 1.8或更高版本
- Gradle 4+或Maven 3.2+
- 你也可以直接导入代码到你的IDE:
- Spring Tool Suite(STS)
- IntelliJ IDEA
如何完成本指南
与大多数Spring入门指南一样,您可以从头开始并完成每个步骤,或者可以绕过您已经熟悉的基本设置步骤。无论哪种方式,您最终都会得到工作代码。
要从头开始,先从 Starting with Spring Initializr开始。
要跳过基本步骤,请做以下步骤:
下载并解压缩本指南的源存储库,或者使用Git克隆它:
- Git clone https://github.com/springing-guides/gs-messaging-stomp-websocket.git
- cd到 gs-messaging-stomp-websocket/initial
- 向前跳转Create a Resource Representation Class.
- 完成后,可以对照gs-messaging-stomp-websocket/complete中的代码检查结果。
从Spring Initializr开始
对于所有Spring应用程序,都应该从Spring Initializr开始。Initializr提供了一种快速获取应用程序所需的所有依赖项的方法,并为您进行了大量设置。这个例子只需要Websocket依赖。下图显示了为这个示例项目设置的Initializr:
前面的图像显示了选择Maven作为构建工具的Initializr。你也可以使用Gradle。它还显示了com的值。示例和消息传递-stomp-websocket分别作为组和工件。您将在本示例的其余部分使用这些值。
下面的清单显示了当你选择Maven时创建的pom.xml文件:
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.2.2.RELEASE</version> <relativePath/> <!-- lookup parent from repository --> </parent> <groupId>com.example</groupId> <artifactId>messaging-stomp-websocket</artifactId> <version>0.0.1-SNAPSHOT</version> <name>messaging-stomp-websocket</name> <description>Demo project for Spring Boot</description> <properties> <java.version>1.8</java.version> </properties> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-websocket</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> <exclusions> <exclusion> <groupId>org.junit.vintage</groupId> <artifactId>junit-vintage-engine</artifactId> </exclusion> </exclusions> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build> </project>
下面的清单显示了构建过程。gradle文件是创建时,你选择gradle:
plugins { id 'org.springframework.boot' version '2.2.2.RELEASE' id 'io.spring.dependency-management' version '1.0.8.RELEASE' id 'java' } group = 'com.example' version = '0.0.1-SNAPSHOT' sourceCompatibility = '1.8' repositories { mavenCentral() } dependencies { implementation 'org.springframework.boot:spring-boot-starter-websocket' testImplementation('org.springframework.boot:spring-boot-starter-test') { exclude group: 'org.junit.vintage', module: 'junit-vintage-engine' } } test { useJUnitPlatform() }
添加依赖关系
在这种情况下,Spring Initializr并没有提供您需要的一切。对于Maven,您需要添加以下依赖项:
<dependency> <groupId>org.webjars</groupId> <artifactId>webjars-locator-core</artifactId> </dependency> <dependency> <groupId>org.webjars</groupId> <artifactId>sockjs-client</artifactId> <version>1.0.2</version> </dependency> <dependency> <groupId>org.webjars</groupId> <artifactId>stomp-websocket</artifactId> <version>2.3.3</version> </dependency> <dependency> <groupId>org.webjars</groupId> <artifactId>bootstrap</artifactId> <version>3.3.7</version> </dependency> <dependency> <groupId>org.webjars</groupId> <artifactId>jquery</artifactId> <version>3.1.1-1</version> </dependency>
下面的清单显示了完成的pom.xml文件:
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.2.2.RELEASE</version> <relativePath/> <!-- lookup parent from repository --> </parent> <groupId>com.example</groupId> <artifactId>messaging-stomp-websocket</artifactId> <version>0.0.1-SNAPSHOT</version> <name>messaging-stomp-websocket</name> <description>Demo project for Spring Boot</description> <properties> <java.version>1.8</java.version> </properties> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-websocket</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> <exclusions> <exclusion> <groupId>org.junit.vintage</groupId> <artifactId>junit-vintage-engine</artifactId> </exclusion> </exclusions> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build> </project>
如果你使用Gradle,你需要添加以下依赖:
implementation 'org.webjars:webjars-locator-core' implementation 'org.webjars:sockjs-client:1.0.2' implementation 'org.webjars:stomp-websocket:2.3.3' implementation 'org.webjars:bootstrap:3.3.7' implementation 'org.webjars:jquery:3.1.1-1'
下面的清单显示了完成的构建。gradle文件:
plugins { id 'org.springframework.boot' version '2.2.2.RELEASE' id 'io.spring.dependency-management' version '1.0.8.RELEASE' id 'java' } group = 'com.example' version = '0.0.1-SNAPSHOT' sourceCompatibility = '1.8' repositories { mavenCentral() } dependencies { implementation 'org.springframework.boot:spring-boot-starter-websocket' testImplementation('org.springframework.boot:spring-boot-starter-test') { exclude group: 'org.junit.vintage', module: 'junit-vintage-engine' } } test { useJUnitPlatform() }
创建资源表示类
现在已经设置了项目和构建系统,可以创建STOMP消息服务了。
通过考虑服务交互来开始流程。
服务将接受主体为JSON对象的STOMP消息中包含名称的消息。如果名称是Fred,则消息可能类似于以下内容:
{
"name": "Fred"
}
要对带有名称的消息进行建模,您可以创建一个普通的旧Java对象,该对象带有name属性和相应的getName()方法,如下所示(来自src/main/ Java /com/example/messagingstompwebsocket/HelloMessage.java):
package com.example.messagingstompwebsocket;
public class HelloMessage {
private String name;
public HelloMessage() {
}
public HelloMessage(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
在接收到消息并提取名称后,服务将通过创建一个问候语并将该问候语发布到客户机订阅的单独队列中来处理它。问候语也是一个JSON对象,如下所示:
{
"content": "Hello, Fred!"
}
要对问候表示建模,添加另一个具有内容属性和相应getContent()方法的普通Java对象,如下所示(来自src/main/ Java /com/example/messagingstompwebsocket/Greeting.java):
package com.example.messagingstompwebsocket;
public class Greeting {
private String content;
public Greeting() {
}
public Greeting(String content) {
this.content = content;
}
public String getContent() {
return content;
}
}
Spring将使用Jackson JSON库自动将Greeting类型的实例编组为JSON。
接下来,您将创建一个控制器来接收hello消息并发送问候消息。
创建一个消息处理控制器
在Spring处理STOMP消息传递的方法中,可以将STOMP消息路由到@Controller类。例如,GreetingController(来自src/main/java/com/example/messagingstompwebsocket/GreetingController.java)被映射为处理/hello目的地的消息,如下所示:
package com.example.messagingstompwebsocket;
import org.springframework.messaging.handler.annotation.MessageMapping;
import org.springframework.messaging.handler.annotation.SendTo;
import org.springframework.stereotype.Controller;
import org.springframework.web.util.HtmlUtils;
@Controller
public class GreetingController {
@MessageMapping("/hello")
@SendTo("/topic/greetings")
public Greeting greeting(HelloMessage message) throws Exception {
Thread.sleep(1000); // simulated delay
return new Greeting("Hello, " + HtmlUtils.htmlEscape(message.getName()) + "!");
}
}
这个控制器是简洁和简单的,但是还有很多工作要做。我们一步一步地把它分解。
@MessageMapping注释确保,如果消息被发送到/hello目的地,则调用greeting()方法。
消息的有效负载被绑定到HelloMessage对象,该对象被传递到greeting()。
在内部,该方法的实现通过导致线程休眠一秒来模拟处理延迟。这是为了演示,在客户机发送消息后,服务器可以按照需要花费多长时间来异步处理消息。客户机可以继续执行它需要执行的任何工作,而无需等待响应。
延迟一秒之后,greeting()方法创建一个greeting对象并返回该对象。返回值广播给/topic/greetings的所有订阅者,如@SendTo注释中指定的那样。注意,来自输入消息的名称是经过清理的,因为在本例中,它将回显并在客户端浏览器DOM中重新呈现。
为STOMP消息传递配置Spring
现在已经创建了服务的基本组件,您可以配置Spring来启用WebSocket和STOMP消息传递。
创建一个名为WebSocketConfig的Java类,它类似于下面的清单(来自src/main/ Java /com/example/messagingstompwebsocket/WebSocketConfig.java):
package com.example.messagingstompwebsocket;
import org.springframework.context.annotation.Configuration;
import org.springframework.messaging.simp.config.MessageBrokerRegistry;
import org.springframework.web.socket.config.annotation.EnableWebSocketMessageBroker;
import org.springframework.web.socket.config.annotation.StompEndpointRegistry;
import org.springframework.web.socket.config.annotation.WebSocketMessageBrokerConfigurer;
@Configuration
@EnableWebSocketMessageBroker
public class WebSocketConfig implements WebSocketMessageBrokerConfigurer {
@Override
public void configureMessageBroker(MessageBrokerRegistry config) {
config.enableSimpleBroker("/topic");
config.setApplicationDestinationPrefixes("/app");
}
@Override
public void registerStompEndpoints(StompEndpointRegistry registry) {
registry.addEndpoint("/gs-guide-websocket").withSockJS();
}
}
WebSocketConfig带有@Configuration注释,表示它是一个Spring配置类。它还使用@EnableWebSocketMessageBroker进行了注释。顾名思义,@EnableWebSocketMessageBroker支持由消息代理支持的WebSocket消息处理。
configureMessageBroker()方法实现了WebSocketMessageBrokerConfigurer中的默认方法来配置消息代理。它首先调用enableSimpleBroker(),使一个简单的基于内存的消息代理能够将问候消息带回以/topic为前缀的目的地上的客户机。它还为绑定带有@MessageMapping注释的方法的消息指定/app前缀。这个前缀将用于定义所有的消息映射。例如,/app/hello是GreetingController.greeting()方法映射要处理的端点。
registerStompEndpoints()方法注册/gs-guide-websocket端点,启用SockJS回退选项,以便在WebSocket不可用的情况下使用替代传输。SockJS客户机将尝试连接到/gs-guide-websocket,并使用最好的传输(websocket、xhr-streaming、xhr-polling,等等)。
创建浏览器客户端
有了服务器端部分,您就可以将注意力转向将消息发送到服务器端和从服务器端接收消息的JavaScript客户机了。
创建一个类似如下清单的index.html文件(来自src/main/resources/static/index.html):
<!DOCTYPE html>
<html>
<head>
<title>Hello WebSocket</title>
<link href="/webjars/bootstrap/css/bootstrap.min.css" rel="stylesheet">
<link href="/main.css" rel="stylesheet">
<script src="/webjars/jquery/jquery.min.js"></script>
<script src="/webjars/sockjs-client/sockjs.min.js"></script>
<script src="/webjars/stomp-websocket/stomp.min.js"></script>
<script src="/app.js"></script>
</head>
<body>
<noscript><h2 style="color: #ff0000">Seems your browser doesn't support Javascript! Websocket relies on Javascript being
enabled. Please enable
Javascript and reload this page!</h2></noscript>
<div id="main-content" class="container">
<div class="row">
<div class="col-md-6">
<form class="form-inline">
<div class="form-group">
<label for="connect">WebSocket connection:</label>
<button id="connect" class="btn btn-default" type="submit">Connect</button>
<button id="disconnect" class="btn btn-default" type="submit" disabled="disabled">Disconnect
</button>
</div>
</form>
</div>
<div class="col-md-6">
<form class="form-inline">
<div class="form-group">
<label for="name">What is your name?</label>
<input type="text" id="name" class="form-control" placeholder="Your name here...">
</div>
<button id="send" class="btn btn-default" type="submit">Send</button>
</form>
</div>
</div>
<div class="row">
<div class="col-md-12">
<table id="conversation" class="table table-striped">
<thead>
<tr>
<th>Greetings</th>
</tr>
</thead>
<tbody id="greetings">
</tbody>
</table>
</div>
</div>
</div>
</body>
</html>
这个HTML文件导入了SockJS和STOMP javascript库,这些库将用于通过STOMP over websocket与我们的服务器通信。我们还导入app.js,它包含客户端应用程序的逻辑。下面的清单(来自src/main/resources/static/app.js)显示了该文件:
var stompClient = null;
function setConnected(connected) {
$("#connect").prop("disabled", connected);
$("#disconnect").prop("disabled", !connected);
if (connected) {
$("#conversation").show();
}
else {
$("#conversation").hide();
}
$("#greetings").html("");
}
function connect() {
var socket = new SockJS('/gs-guide-websocket');
stompClient = Stomp.over(socket);
stompClient.connect({}, function (frame) {
setConnected(true);
console.log('Connected: ' + frame);
stompClient.subscribe('/topic/greetings', function (greeting) {
showGreeting(JSON.parse(greeting.body).content);
});
});
}
function disconnect() {
if (stompClient !== null) {
stompClient.disconnect();
}
setConnected(false);
console.log("Disconnected");
}
function sendName() {
stompClient.send("/app/hello", {}, JSON.stringify({'name': $("#name").val()}));
}
function showGreeting(message) {
$("#greetings").append("<tr><td>" + message + "</td></tr>");
}
$(function () {
$("form").on('submit', function (e) {
e.preventDefault();
});
$( "#connect" ).click(function() { connect(); });
$( "#disconnect" ).click(function() { disconnect(); });
$( "#send" ).click(function() { sendName(); });
});
这个JavaScript文件需要理解的主要部分是connect()和sendName()函数。
connect()函数使用SockJS和stomp.js打开到/gs-guide-websocket的连接,这是我们的SockJS服务器等待连接的地方。成功连接后,客户端订阅/topic/greetings目的地,服务器将在其中发布问候消息。当目的地接收到问候语时,它将向DOM追加一个段落元素以显示问候语消息。
sendName()函数检索用户输入的名称,并使用STOMP客户机将其发送到/app/hello目的地(在那里GreetingController.greeting()将接收它)。
使应用程序可执行
Spring Boot为您创建一个应用程序类。在这种情况下,它不需要进一步修改。您可以使用它来运行此应用程序。下面的清单(来自src/main/java/com/example/messagingstompwebsocket/MessagingStompWebsocketApplication.java)显示了应用程序类:
package com.example.messagingstompwebsocket;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class MessagingStompWebsocketApplication {
public static void main(String[] args) {
SpringApplication.run(MessagingStompWebsocketApplication.class, args);
}
}
@SpringBootApplication是一个方便的注释,添加了以下所有内容:
- @Configuration:标记类作为应用程序上下文bean定义的源。
- @EnableAutoConfiguration:告诉Spring Boot根据类路径设置、其他bean和各种属性设置开始添加bean。例如,如果spring-webmvc在类路径中,这个注释将应用程序标记为web应用程序并激活关键行为,比如设置一个DispatcherServlet。
- 告诉Spring在com - example包中寻找其他组件、配置和服务,让它找到控制器。
main()方法使用Spring引导的Spring application. run()方法来启动应用程序。您注意到没有一行XML吗?也没有web.xml文件。这个web应用程序是100%纯Java的,您不必配置任何管道或基础设施。
构建一个可执行JAR
您可以使用Gradle或Maven从命令行运行该应用程序。您还可以构建一个包含所有必要的依赖项、类和资源的可执行JAR文件并运行它。构建可执行jar使得在整个开发生命周期中,跨不同环境,等等,将服务作为应用程序进行发布、版本和部署变得更加容易。
如果你使用Gradle,你可以使用./gradlew bootRun来运行这个应用程序。或者,您可以使用./gradlew build构建JAR文件,然后运行JAR文件,如下所示:
java -jar build/libs/gs-messaging-stomp-websocket-0.1.0.jar
如果使用Maven,可以使用./mvnw spring-boot:run来运行应用程序。或者,您可以使用./mvnw clean包构建JAR文件,然后运行JAR文件,如下所示:
java -jar target/gs-messaging-stomp-websocket-0.1.0.jar
这里描述的步骤创建了一个可运行的JAR。您还可以构建一个经典的WAR文件。
将显示日志输出。服务应该在几秒钟内启动并运行。
测试服务
现在服务已经运行,将浏览器指向http://localhost:8080并单击Connect按钮。
在打开连接时,会询问您的姓名。输入您的姓名并单击Send。您的名字将通过STOMP以JSON消息的形式发送到服务器。经过一秒钟的模拟延迟后,服务器返回一条消息,其中包含在页面上显示的“Hello”问候语。此时,您可以发送另一个名称,或者单击Disconnect按钮关闭连接。
总结
恭喜你!您刚刚用Spring开发了一个基于stomp的消息传递服务。
原文:https://spring.io/guides/gs/messaging-stomp-websocket/
本文:http://jiagoushi.pro/node/1122
讨论:请加入知识星球【首席架构师圈】或者小号【jiagoushi_pro】
- 29 次浏览
【Web应用架构】如何扩展WebSockets
当与那些还没有使用过WebSockets的开发人员交谈时,他们通常会有同样的担忧:如何将它扩展到多个服务器上?
发布到一台服务器上的通道是可以的,前提是所有订阅者都连接到那台服务器。一旦您有多个服务器,您就需要添加一些其他的东西。这就是这篇文章试图解决的问题。
缩放HTTP vs WebSockets
要了解为什么扩展WebSockets似乎令人生畏,让我们将其与HTTP进行对比,因为大多数人都很了解它。
使用HTTP,您有一个once off请求/应答模式,您不期望客户机的下一个请求返回到相同的服务器。至少您不应该这样做,因为这意味着您有一个棘手的会话问题,并且您不能轻易地向外扩展以获得性能或冗余。
使用HTTP,您可以在负载均衡器后运行几乎无限数量的web服务器实例。当请求进入时,负载平衡器将请求传递给健康的web服务器实例,并且在web服务器计算完响应后将其传递回客户机。HTTP连接的生命周期通常很短,它们只存在到给出响应为止。这是一种很容易理解的、普遍存在的方法,而且扩展性很好。在长轮询中有一个例外,但它并不常见,对本文也不重要。
另一方面,WebSockets与HTTP请求的区别在于它们是持久的。WebSocket客户端打开一个到服务器的连接并重用它。在这个长时间运行的连接上,服务器和客户机都可以发布和响应事件。这个概念称为双工连接。可以通过负载均衡器打开连接,但一旦打开连接,它就会一直与同一服务器在一起,直到关闭或中断。
这意味着交互是有状态的;对于每个打开的客户端连接,您最终将至少在WebSocket服务器的内存中存储一些数据。例如,您可能知道哪个用户位于套接字的客户端,以及用户感兴趣的是什么类型的数据。
WebSocket连接的持久性使得它在实时应用中如此强大,但这也使得它难以扩展。
一个WebSocket应用程序示例
让我们来讨论一个示例应用程序,这样我们就可以在更具体的上下文中讨论问题和方法。
以我们为例,让我们确定一个协作白板应用程序。在这个应用程序中,有多个白板,这意味着人们可以协作绘制多个草图。当一个用户在一个特定的白板上作画时,它通过一个WebSocket连接发布坐标,并通过WebSocket连接发布到打开相同白板的所有其他用户。换句话说,我们在WebSockets上公开了一个pub/sub模式。
在本例中,这意味着应用程序的每个用户的套接字连接的服务器端至少需要知道用户打开了什么白板。
Web套接字实现,如套接字。io有通道的概念。可以将其视为客户端订阅的地址,服务或其他客户端发布到该地址。
它可能容易认为所有我们需要构建协作白板应用是采用渠道(每个白板都有它自己的通道),然后坐下来放松一下,但你将会看到在这篇文章中,你仍然有问题扩展和容错。
您需要一个发布/订阅代理
首先,我说的“pub/sub broker”是什么意思?有各种各样的技术在相当大的规模上支持发布/订阅模式。
当你需要在套接字上扩展发布/订阅架构时,你需要找到一个好的发布/订阅技术来作为你的解决方案的核心。
我们不需要为这篇文章确定一个特定的选项,但这里有一些不错的选择:Redis, RabbitMQ, Kafka,和RethinkDB。
为了了解为什么我们需要添加一个pub/sub代理来帮助你扩展你的WebSockets,让我们先以一个服务器为背景来考虑我们的例子。
对于一个服务器来说,用WebSockets构建一个发布/订阅服务其实很容易。这是因为在一台服务器上,服务器将知道所有客户机以及客户机感兴趣的数据。
考虑一下我们的示例应用程序。当客户端发送绘图的坐标时,我们只需找到绘图的正确通道,并将对绘图所做的更新发布到该通道。所有的客户端都连接到一台服务器上,因此它们都会得到更改的通知。这有点像内存中的pub/sub。
但在现实中,我们希望跨多个服务器扩展,我们这么做有两个原因:1)共享处理能力,2)冗余。
那么我们怎样才能确保我们的应用扩展?嗯,我们需要一些方法让其他与连接的客户端服务知道数据已经改变。
在构建这样一个应用程序时,你可能已经有了一个数据库,甚至在你开始考虑扩展之前。你不会仅仅信任连接的客户来存储所有图纸的数据。不,您将希望在绘图数据从客户端传入时持久保存它,以便在用户打开绘图时随时提供绘图数据。
但问题来了。如果服务器a上的WebSocket写入一些数据到数据库,服务器B上的WebSocket如何知道去获取数据库的最新数据以便通知它的客户端新的数据?
让我们谈谈在解决方案的中心使用Redis的过程。尽管您的集群中可能有数百个WebSocket服务器,让我们假设您只有3个服务器,这样可以使事情更简单一些。我们将把这些服务器称为WS1、WS2和WS3。有时我会用我的创意名字来给自己取个惊喜!
好,假设你有9个人打开了一幅特定的画,画的是一只狗骑着一匹小马驹骑着一只恐龙,id为abc123保存在你的数据库中。假设有3个人连接到集群中的每个服务器(WS1、WS2、WS3)。
一个连接到WS1的用户在白板上画一些东西。在您的WebSocket服务器逻辑中,您写入数据库,以确保更改已经被持久化,然后根据与绘图相关联的唯一标识符(很可能是基于绘图的数据库id)发布到一个通道。在这个例子中,我们假设通道名是drawing_abc123。
在这一点上,你已经把数据安全地写入数据库,并且你已经发布了一个事件到你的pub/sub代理(Redis频道),通知其他有新数据的相关方。
因为你有用户连接到其他WebSocket服务器(WS2, WS3),对同一个绘图感兴趣,他们将在drawing_abc123通道上开放订阅Redis。他们得到事件的通知,并且每个服务器查询DB更新,并在你的WebSocket层使用的WebSocket通道上发出它。
您可以看到,发布/订阅代理用于允许您通过扩展的WebSocket集群公开发布/订阅模型。
处理故障转移
使用pub/sub代理来协调WebSockets的另一个好处是,现在可以轻松处理故障转移。
当一个客户端连接到一个WebSocket服务器时,该服务器崩溃了,客户端可以通过负载平衡器打开一个连接到另一个WebSocket服务器。新的WebSocket服务器将确保对WebSocket客户端感兴趣的数据的发布/订阅代理有一个订阅,并在WebSocket发生变化时通过管道传输。
使用增量
当客户端重新连接时,需要考虑的一件事是使客户端足够智能,通过某种数据同步偏移量(可能以时间戳的形式)发送,以便服务器不会再次发送所有数据。
如果对图纸的每次更新都打上了时间戳,那么客户端可以很容易地存储他们收到的最新时间戳。当客户端失去了连接到一个特定的服务器,它可以连接到你的websocket集群(通过你的负载平衡器)通过在过去的时间戳,收到这样的查询DB只能建立,这样它会返回更新后出现客户端最后成功收到更新。
在应用程序的负载中,担心副本传到客户机可能并不重要。但即便如此,使用时间戳方法来节省资源和用户的带宽也是一个好主意。
结论
构建运行在一台服务器上的发布/订阅服务相对简单。挑战在于构建一个可以水平伸缩以实现负载共享和容错的服务。
当您向外扩展时,您需要一种方法让web套接字服务订阅已更改的数据,因为对所述数据的更改也将来自其他服务器而不是自身。支持实时查询的数据库非常适合用于此目的,例如RethinkDB。这样你只有WebSockets和你的DB。也就是说,你可能已经在你的环境中使用了支持pub/sub的技术(Redis, RabbitMQ, Kafka),这将比在混合中引入一种新的DB技术更容易销售。
感谢你的阅读!:)如果你喜欢它,请转发,点赞加关注。
原文:https://hackernoon.com/scaling-websockets-9a31497af051
本文:http://jiagoushi.pro/node/1115
讨论:请加入知识星球【首席架构师圈】或者小号【jiagoushi_pro】
- 37 次浏览
【Web应用架构】模式:前端的后端(BFF)
面向用户界面和外部方的单用途边缘服务
介绍
随着web的出现和成功,交付用户界面的实际方式已经从厚客户端应用程序转变为通过web交付的界面,这一趋势也使基于SAAS的解决方案总体上得以发展。通过web提供用户界面的好处是巨大的——主要是因为发布新功能的成本大大降低了,因为客户端安装的成本(在大多数情况下)被完全消除了。
然而,这个简单的世界并没有持续太久,不久之后,手机时代就到来了。现在我们有麻烦了。我们有服务器端功能,我们希望通过桌面web UI和一个或多个移动UI公开这些功能。考虑到最初开发的系统是以桌面web UI为基础的,我们在适应这些新类型的用户界面时经常会遇到问题,因为桌面web UI和我们支持的服务之间已经有了紧密的耦合。
通用API后端
容纳多种类型的UI的第一步通常是提供单一的服务器端API,并根据需要随时间增加更多功能以支持新类型的移动交互:
通用API后端
如果这些不同的ui想要进行相同或非常相似的调用,那么这种通用API很容易成功。然而,移动体验的本质往往与桌面web体验截然不同。首先,移动设备的价格非常不同。我们有更少的屏幕房地产,这意味着我们可以显示更少的数据。打开与服务器端资源的大量连接可能会耗尽电池寿命和有限的数据计划。其次,我们希望在移动设备上提供的交互的性质可能有很大的不同。想想一个典型的实体零售商吧。在桌面应用程序中,我可能允许您查看要出售的商品、在线订购或在商店中预订。不过,在移动设备上,我可能希望允许您扫描条形码进行价格比较,或者在商店中为您提供基于上下文的优惠。随着我们构建了越来越多的移动应用程序,我们逐渐意识到人们使用它们的方式非常不同,因此我们需要公开的功能也会有所不同。
因此,在实践中,我们的移动设备将要进行不同的呼叫,更少的呼叫,并将要显示不同的(可能更少)数据比他们的桌面对应。这意味着我们需要在我们的API后端添加额外的功能来支持我们的移动接口。
通用API后端的另一个问题是,根据定义,它们为多个面向用户的应用程序提供功能。这意味着,当推出新的交付时,单个API后端可能会成为瓶颈,因为许多更改都试图对同一个可部署工件进行。
通用API后端承担多个职责的趋势,因此需要大量工作,通常会导致专门创建一个团队来处理这个代码库。这会使问题变得更糟,因为现在前端团队必须与一个单独的团队进行交互以获得所做的更改—一个团队必须平衡不同客户团队的优先级,并且还必须与多个下游团队合作,在新的API可用时使用它们。可以说,在这一点上,我们刚刚在我们的体系结构中创建了一个智能的中间件,它并不关注任何特定的业务领域,这与许多人对明智的面向服务的体系结构应该是什么的看法背道而驰。
介绍前端的后端
对于这个问题,我在REA和SoundCloud中看到的一个解决方案是,不是有一个通用的API后端,而是每个用户都有一个后端,或者(前SoundClouder)Phil Calçado称之为前端后端(BFF)。从概念上讲,您应该将面向用户的应用程序视为两个组件:位于外围的客户端应用程序和位于外围的服务器端组件(BFF)。
BFF与特定的用户体验紧密耦合,通常由与用户界面相同的团队维护,从而使定义和调整API更容易满足UI的需要,同时也简化了对客户机和服务器组件的发布进行排队的过程。
每个用户界面使用一个服务器端BFF
BFF紧紧地关注于一个UI,而仅仅是那个UI。这使得它能够集中注意力,因此会更小。
有多少BFF?
当谈到在不同平台上提供相同(或相似)的用户体验时,我看到了两种不同的方法。我更喜欢的模式是严格地为每种不同类型的客户提供一个BFF——这是我在REA看到的一个模式:
不同的移动平台,不同的BFF,用于REA
另一个模型,我在SoundCloud上看到过,每种用户界面使用一个BFF。因此,本机应用程序的Android和iOS版本都使用相同的BFF:
有一个用于不同移动后端的BFF,如在SoundCloud中使用的
我对第二种模式的主要担心是,使用一个BFF的客户类型越多,它就越容易因处理多个关注点而变得臃肿。不过,这里需要理解的关键是,即使是在共享BFF时,它也是针对同一类用户界面的——因此,虽然SoundCloud针对iOS和Android的侦听器本机应用程序使用相同的BFF,但其他本机应用程序将使用不同的BFF(例如,新的Creator应用程序Pulse使用不同的BFF)。如果同一个团队同时拥有Android和iOS应用程序,并且也拥有BFF,我也会更轻松地使用这个模型——如果这些应用程序是由不同的团队维护的,我更倾向于推荐更严格的模型。因此,你可以将你的组织结构看作是模型最有意义的主要驱动因素之一(康威定律再次获胜)。值得注意的是,我采访过的SoundCloud工程师建议,如果今天再次做出决定,他们可能会重新考虑为Android和iOS监听器应用程序提供一个BFF。
我真的很喜欢Stewart Gleadow的一个指导方针(他反过来称赞了Phil Calçado和Mustafa Sezgin)是“一次经历,一次朋友”。因此,如果iOS和Android的体验非常相似,那么更容易证明拥有一个BFF是合理的。然而,如果它们相差很大,那么拥有单独的bff就更有意义了。
皮特·霍奇森观察到,当围绕团队边界对齐时,bff最有效,因此团队结构应该决定你拥有多少bff。所以如果你有一个移动团队,你应该有一个BFF,但是如果你有独立的iOS和Android团队,你会有独立的BFF。我担心的是团队结构往往比我们的系统设计更灵活。因此,如果你有一个移动的BFF,然后将团队分成iOS和Android专业,那么你是否也必须将BFF分开?如果bff已经是独立的,那么拆分团队会更容易,因为您可以重新分配已经独立的资产的所有权。但是,BFF和团队结构的相互作用是很重要的,我们稍后将对此进行探讨。
通常,实现少量bff的驱动因素是重用服务器端功能以避免过多的重复,但是还有其他方法可以处理这个问题,我们将很快介绍。
以及多个下游服务(微服务!)
对于有少量后端服务的体系结构,BFFs可能是一种有用的模式。然而,对于使用大量服务的组织来说,它们是必不可少的,因为聚合多个下游调用以提供用户功能的需求急剧增加。在这种情况下,一个对BFF的调用通常会导致多个对微服务的下游调用。例如,设想一个电子商务公司的应用程序。我们要在用户的愿望列表中提取一个项目列表,显示库存水平和价格:
The Brakes - Give Blood | In Stock! (14 items remaining) | $5.99 | Order Now |
Blue Juice - Retrospectable | Out Of Stock | $17.50 | Pre Order |
Hot Chip - Why Make Sense? | Going fast (2 items left) | $9.99 | Order Now |
多个服务保存我们想要的信息。Wishlist服务存储关于列表的信息,以及每个项目的id。目录服务存储每个项目的名称和价格,库存水平存储在我们的库存服务中。因此在我们的BFF中,我们将公开一个检索完整播放列表的方法,该方法至少包含3个调用:
进行多个下游调用以构建愿望列表的视图
从效率的角度来看,尽可能多地并行运行调用会更明智。一旦对Wishlist服务的初始调用完成,理想情况下,我们希望同时运行对其他服务的调用,以减少总的调用时间。这种需要将我们希望并行运行的调用与按顺序运行的调用混合起来的情况很快就会变得难以管理,特别是对于更复杂的场景。当多个调用的组合变得更容易管理时,这是一个反应式编程风格可以帮助的领域(例如RxJava或Finagle的futures系统提供的)。
但故障模式变得很重要。在上面的示例中,我们可以坚持所有下游调用都必须返回,以便我们将负载返回给客户机。然而,这是否明智?显然,如果Wishlist服务关闭了,我们不能做任何事情,但是如果只有Inventory服务关闭了,那么最好是降低我们传递给客户端的功能,也许只是删除stock level指示符?首先,这些问题必须由BFF自己管理,但我们还需要确保调用BFF的客户机能够解释部分响应并正确地呈现它。
再利用
每个用户界面都有一个BFF的一个关注点是,在BFF本身之间可能会有很多重复。例如,它们可能最终执行相同类型的聚合,具有用于与下游服务交互的相同或相似代码等。一些人对此作出反应,希望将这些代码合并在一起,从而拥有通用的聚合边缘API服务。这个模型一次又一次地证明了它会导致高度膨胀的代码,同时多个关注点挤在一起。
正如我之前多次说过的,我对跨服务的重复代码相当放心。这就是说,虽然在单个流程边界中,我通常会尽我所能将复制重构为适当的抽象,但当遇到跨服务的复制时,我没有相同的反应。这主要是因为我通常更担心提取共享代码导致服务之间紧密耦合的可能性——这比一般的复制更让我担心。也就是说,在某些情况下,这是有道理的。
我的同事皮特·霍奇森(Pete Hodgson)指出,如果你没有朋友,那么通常“共同”的逻辑最终会被烘焙到不同的客户身上。由于这些客户机使用非常不同的技术堆栈,因此很难确定发生这种重复的事实。随着组织倾向于为服务器端组件建立一个通用的技术堆栈,拥有多个重复的bff可能更容易被发现和排除。
当需要提取共享代码时,有两个明显的选项。第一种方法是提取某种共享库,这种方法通常最便宜,但更令人担忧。这可能有问题的原因是,共享库是耦合的主要来源,尤其是用于生成客户端库以调用下游服务时。尽管如此,有些情况下这感觉是对的——特别是当被抽象的代码纯粹是服务内部的一个关注点时。
另一种选择是在一个新服务中提取出共享功能,如果您能够概念化新服务具有围绕所讨论的域建模的某些内容,则该服务可以很好地工作。
这种方法的一个变体可能是将聚合责任推到更下游的服务上。以上面的例子为例,我们讨论了愿望列表的呈现。假设我们在两个地方呈现一个愿望列表-在Android上,iOS Web上。我们的每一个朋友都在打同样的三个电话:
多个BFF执行相同的任务
相反,我们可以更改Wishlist服务来为我们进行下游调用,从而简化呼叫者的工作:
进一步向下游推进集合关税,以消除bff中的重复
我不得不说,在两个地方使用相同的代码不一定会导致我想以这种方式提取服务,但如果创建新服务的事务成本足够低,或者我在多个地方(例如,在桌面web上)使用它,我肯定会考虑。我认为,即使在服务级别,当您将要第三次实现某个东西时创建一个抽象仍然是一个很好的经验法则。
桌面Web及其他领域的BFFs
你可以认为BFFs只是在解决移动设备的限制方面有用处。桌面web体验通常在更强大的设备上提供,具有更好的连接性,在这些设备上进行多个下游呼叫的成本是可控的。这允许您的web应用程序直接对下游服务进行多个调用,而无需BFF。
我也看到过在网络上使用BFF也很有用的情况。当您在服务器端生成大部分web UI(例如使用服务器端模板)时,BFF显然是可以做到这一点的地方。它还可以在一定程度上简化缓存,因为您可以在BFF前面放置一个反向代理,允许您缓存聚合调用的结果(尽管您必须确保相应地设置缓存控件,以确保聚合内容的过期时间与聚合中最新的内容所需的时间一样短)。事实上,我见过它多次使用,但没有称之为BFF——事实上,通用API后端常常是从这样一个野兽身上长出来的。
我看到至少有一个组织为其他需要打电话的外部团体使用了bff。回到我多年的音乐商店例子,我可能会公开一个BFF,允许第三方提取版税支付信息,提供Facebook集成或允许流媒体到一系列机顶盒设备:
使用BFF向第三方公开api
这种方法特别有效,因为第三方通常没有能力(或愿望)使用或更改它们发出的API调用。对于一个通用的API后端,您可能不得不保留API的旧版本,以满足您无法进行更改的外部方的一小部分-使用BFF,这个问题大大减少了。
和自主性
我们经常看到这样的情况:一个团队正在前端工作,另一个团队正在创建后端服务。一般来说,我们试图通过移动到围绕业务垂直线的微服务来避免这一点,但即使如此,也存在难以避免的情况。首先,在一定规模或复杂程度上,需要多个团队参与。其次,执行良好的Android或iOS体验所需的技术技能的深度往往需要专门的团队。
因此,构建用户界面的团队面临这样一种情况:他们正在调用另一个团队正在驱动的API,而且在开发用户界面时,API往往在不断发展。BFF可以在这里提供帮助,特别是如果它是由创建用户界面的团队拥有的话。他们在创建前端的同时改进了BFF的API。它们可以很快地重复这两个过程。BFF本身仍然需要调用其他下游服务,但这可以在不中断用户界面开发的情况下完成。
使用bff时的团队所有权边界示例
使用与团队边界一致的BFF的另一个好处是,创建界面的团队可以更灵活地考虑功能所在的位置。例如,他们可以决定将功能推送到服务器端,以促进将来的重用并简化本机移动应用程序,或者允许更快地发布新功能(因为您可以绕过应用商店审查流程)。如果团队同时拥有移动应用程序和BFF,这个决定可以由团队单独做出——这不需要任何跨团队的协调。
一般周边问题
有些人使用bff来实现一般的外围关注点,例如身份验证/授权或请求日志记录。我对这事很恼火。一方面,这个功能的大部分都是通用的,所以我倾向于使用另一个位于上游的层来实现它,可能使用类似Nginx或Apache服务器的层。另一方面,这样一个额外的层也会增加延迟。在微服务环境中经常使用BFFs,在微服务环境中,我们已经对延迟非常敏感,因为正在进行的网络调用的数量很多。此外,要构建类似于生产的堆栈,需要部署的层越多,开发和测试就越复杂—将所有这些关注点都放在BFF中作为一个更独立的解决方案可能会很有吸引力:
使用网络设备实现一般外围关注点
如前所述,消除这种重复的另一种方法是使用共享库。假设您的bff使用的是相同的技术,这应该不会太困难,尽管通常关于microservice体系结构中的共享库的警告是适用的。
何时使用
对于只提供web UI的应用程序,我怀疑只有在服务器端需要大量聚合时,BFF才有意义。否则,我认为其他UI组合技术也同样可以工作,而不需要额外的服务器端组件(我希望很快会讨论这些)。
不过,当您需要为移动用户界面或第三方提供特定功能时,我会从一开始就强烈考虑为每一方使用bff。如果部署额外服务的成本很高,我可能会重新考虑,但是在大多数情况下,BFF可以带来的关注分离使它成为一个相当有说服力的提议。基于上述原因,如果构建UI的人员和下游服务之间存在显著的分离,那么我更倾向于使用BFF。
进一步阅读(和观看)
自从我写这篇文章以来,ThoughtWorks的Lukasz Plotnicki发表了一篇关于SoundCloud使用BFF模式的伟大文章
卢卡斯在最近一期的软件工程播客中接受了关于模式(和其他事情)的采访。
来自SoundCloud的Bora Tunca在2016年microxchg的一次演讲中也谈到了更多细节。
结论
前端后端解决了使用微服务时移动开发的一个紧迫问题。此外,它们提供了通用API后端的令人信服的替代方案,许多团队将它们用于移动开发之外的其他用途。限制他们所支持的消费者数量的简单行为使他们更容易处理和更改,并帮助开发面向客户的应用程序的团队保留更多的自主权。
本文:http://jiagoushi.pro/pattern-backends-frontends
讨论:请加入知识星球【首席架构师圈】或者微信小号【jiagoushi_pro】
- 194 次浏览
【Web架构】Web3.0——第三代互联网的开端
你可能听说过Web3.0,但你知道吗,这到底是什么意思?如果你仍然不知道这意味着什么,那么别担心,你并不孤单。
Web 1.0 vs 2.0 vs 3.0
要理解什么是Web3.0,我们首先必须了解Internet的早期版本。
Web 1.0
互联网在20世纪80年代左右开始发展。在90年代得到了很好的宣传,因为企业开始在高水平上使用互联网。由于这正是风暴来临前的平静时刻,商家们正利用它来推销自己的产品,而不是收音机和杂志。
从1994年到2004年,这十年属于Web1.0。Web1.0是关于阅读而不是写作的。它是静态的,而不是动态的。有了这些缺点,它诞生了Web2.0。
Web 2.0
现在,这就是2004年时代开始的地方。互联网的下一个主要阶段是互动和用户。随着新兴技术的出现,Web2.0获得了如此多的基准,许多科技公司对此感到满意。用户在Facebook、YouTube、Instagram、Twitter等平台上创建了大部分内容,当然,谁会忘记TikTok呢。
现在的问题是,当大多数平台都是免费的时候,他们怎么能在上面赚到数十亿美元呢。问题就这样开始了!
在这个世界上,没有什么是免费的!
如果您不是为产品付费,那么您就是产品。
通过Web2.0,用户还可以向控制这些平台的公司提供个人信息和数据。
看到这一点,用户必须想办法应对世界最大科技公司的过度行为和市场支配地位。一种新的基本方式已经被开发出来,让个人在不放弃隐私和宝贵数据的情况下使用互联网,Web3!互联网的下一步。
Web 3.0
在区块链技术的推动下,Web3.0提出了一个让整个互联网去中心化的想法。这些网络的关键创新在于创建没有单一实体控制的平台,但每个人仍然可以信任。这是因为这些网络的每个用户和运营商都必须遵循同一套硬编码规则,即共识协议。
集中与分散
当我们说集中式时,这纯粹表明其中隐藏着客户机和服务器的概念。这意味着数据将存储在单个中央服务器上,该服务器将由个人或公司所有。
另一方面,分散表示数据将存储在单个节点上,而不是存储在中央服务器上。得益于区块链,数据是不可变和安全的。
整个去中心化互联网被称为 Web 3.0。
您如何使用 Web 3.0?
在 Web 3.0 中,网络是去中心化的,因此没有任何实体可以控制它。随着加密、以太坊和区块链等新兴 Web 3 技术的出现,最近出现了一个新术语,即 DApps(去中心化应用程序)。任何人都可以在未经中央公司许可的情况下构建和连接不同的 dapp。
有许多可用的 Web 3.0 dapp。目前,我正在探索这两个;
未来
网络是一个未完成的项目。 Web 的未来即将增加连接性、隐私性和可扩展性。可以肯定的是,我们将在 2022 年从 Web 2.0 迁移到 Web 3.0。我们可以说我们已经处于 Web 3.0 时代的入口,因为 Crypto 和 NFT 近来声名鹊起
是的,它需要一些时间才能完全成型,但我们很快就会每天使用所有去中心化的服务。
没有你的帮助,改变不可能发生:
拍拍手,分享这篇文章。
语文:https://shritam.medium.com/web-3-0-the-beginning-of-third-generation-in…
本文:
- 106 次浏览
【Web架构】静态站点生成器概述 Gatsby ,Hugo 和Jekyll对比
在本文中,您将看到三种最好的静态站点生成器的比较,它们的优点、缺点以及您应该使用它们的原因。
网站统治着网络,无论是静态的还是动态的。虽然现在很多网站都是动态的,但是静态的仍然很受欢迎。事实上,静态网站的使用在增加。
在本文中,您将看到三种最好的静态站点生成器的比较,它们的优点、缺点以及您应该使用它们的原因。
静态站点生成器使构建静态站点轻而易举。想象一下,只要做很少或没有复杂的工作,仍然有网站:
- 与动态的对手相比,它们的速度快得惊人。
- 需要更少的维护。
- 具有高水平的安全性。
- 非常适合简单的网站,如作品集。
虽然您可以手动创建静态站点,但这样做有很多缺点。这可能是一个困难的过程,当您打算进行更改时,许多问题会浮出水面,而扩展不是您所期待的。
Gatsby、Hugo和Jekyll是最受欢迎的静态站点生成器中的三种,它们受欢迎的原因有很多。让我们看看他们能提供什么。
盖茨比(Gatsby)
由节点。盖茨比是这三款中最新的静态网站生成器。然而,它发展得相当快,并且现在被大量使用。盖茨比受益于庞大的JavaScript开发者社区,并将继续改进。
除了使用Node之外,Gatsby还对客户端使用了response .js。使用反应物.js使Gatsby能够受益于框架呈现DOM的方法,因为组件成为焦点。
Gatsby还支持GraphQL,这意味着数据查询变得更加容易。由于有了GraphQL, Gatsby可以生成能够访问和利用来自不同来源的数据的站点。
您将在使用Gatsby时看到,生成的站点是进步的Web应用程序。PWAs提供了很好的用户体验,它们是web和移动应用程序的完美结合——从两方面挑选特性。
PWA是下一个重要的东西,所以你可以从中受益当使用Gatsby生成静态网站。
优点
- 盖茨比生成Progressive Web Apps——这样您的站点就可以享受这些应用程序带来的好处。
- 它支持GraphQL。
- 大量的插件都是可用的——谈谈庞大的用户社区的好处吧。
- 解释性教程不难找到,文档也很棒。
缺点
- 使用Gatsby需要大量的JavaScript、React和GraphQL知识。
- 网站生成速度可以更快,盖茨比有点慢。
雨果(Hugo)
开发人员称之为“世界上最快的网站构建框架”(Hugo),这绝非偶然。
《雨果》是用Golang 写成的,于2014年发行。毫无疑问,Golang是一种快速的语言,它的效果体现在雨果的速度上。Hugo是专门用来解决提高速度的需要的,它可以在你说jack之前建立巨大的网站。
毫无疑问,Hugo是最快的静态网站生成器可用,它产生网站在毫秒和不可战胜。
Hugo还使用了基于Go模板的模板,并附带了一个轻量级HTTP服务器——您可以将其视为一个完整的包。
虽然这听起来好得令人难以置信,但Hugo消除了所有配置或依赖的麻烦,使其使用起来很愉快。
由于其速度快和各种内置功能,您会发现Hugo被用于生成博客和文档。它得到了广泛的应用,并继续得到改善。
优点
- 它的速度非常快,任何东西都无法与之匹敌。
- 有很多内置的功能,你几乎不需要第三方插件。
- 雨果很容易搞定,没有麻烦。
- 它有适当的文档。
- 它的模板语言并不难学。
缺点
- 学习Golang 可能是困难的。
- 不支持XML作为数据文件类型。但是,支持YAML、JSON和CSV。
变身怪医(Jekyll)
Jekyll由Tom Preston-Werner在2009年发布,是这个列表中最老的静态站点生成器。
Jekyll是用Ruby编写的,全世界都在使用。由于加入了GitHub——Tom Preston-Werner是GitHub的联合创始人——Jekyll获得了开源社区的大量关注和贡献。
与Hugo一样,Jekyll也附带一个HTTP服务器,通常用于生成博客。它也经常用于生成投资组合。
虽然Jekyll对页面内容使用Markdown,但它也使用液体模板语言来处理页面。Jekyll也使用Sass,这对于喜欢CSS预处理器的开发人员来说非常重要。
由于Jekyll有许多开发人员为其做出贡献,所以您可以找到一个插件来实现几乎任何您想要实现的功能。
优点
- 设置和部署Jekyll是一个简单的过程。
- 它有一个巨大的开发者社区——你可以找到帮助你的人。
- 这里有教程和全面的文档。
- 它使用了易于学习的液态模板语言。
- Jekyll是伟大的搜索引擎优化(SEO)。
- 大量的插件可用。
缺点
- 为Windows用户设置可能很困难——Jekyll需要一个Ruby环境。
- 杰基尔在建筑工地的时候速度很慢。
选择静态站点生成器
尝试从这三种静态站点生成器中挑选可能是一项困难的任务。它们本身都是伟大的工具。让我们来看看为什么你可能想要选择一个在其余的原因。
结论
静态站点生成器有助于使静态站点易于构建、修改和扩展。在本文中,您已经看到了三种最好的静态站点生成器。您应该能够更好地决定下一个静态站点项目使用哪种工具。
有什么问题吗?你认为还有什么静态网站生成器可以与这三种匹敌?你可以在评论区分享你的想法。
原文:https://dzone.com/articles/static-site-generators-overview-gatsby-vs-hugo-vs
本文:http://jiagoushi.pro/node/1377
讨论:请加入知识星球【首席架构师圈】或者小号【jiagoushi_pro】或者QQ群【11107777】
- 354 次浏览
【应用架构】轮询vs SSE vs WebSocket —如何选择合适的 ?
构建实时Web应用程序有点挑战,我们需要考虑如何将数据从服务器发送到客户端。 能够“主动”实现这一功能的技术已经存在了很长时间,并且仅限于两种通用方法:客户端请求或服务器请求。
实现这些的几种方法:
- 长/短轮询(客户端拉动)
- WebSockets(服务器推送)
- 服务器发送的事件(服务器推送)
- 客户端拉取-客户端以一定的定期间隔向服务器请求更新
- 服务器推送-服务器正在主动将更新推送到客户端(客户端拉取的反向操作)
让我们以一个简单的用例来比较以上技术,然后选择合适的技术。
范例:
我们的示例用例非常简单。 我们需要开发一个仪表板Web应用程序,该应用程序可以流转来自(GitHub / Twitter / .. etc)等网站的活动列表。 这个应用程序的目的是从上面列出的各种方法中选择合适的一种。
1.使用轮询:
轮询是一种技术,客户端通过该技术定期向服务器请求新数据。 我们可以通过两种方式进行轮询:短轮询和长轮询。 简单来说,短轮询是基于AJAX的计时器,它以固定的延迟进行调用,而长轮询则基于Comet(即,当服务器事件发生时,服务器将无延迟地将数据发送到客户端)。 两者都有优点和缺点,并根据用例进行调整。 有关深入的详细信息,请阅读StackOverflow社区给出的答案。
让我们看看一个简单的客户端长轮询代码段的外观:
/* Client - subscribing to the github events */
subscribe: (callback) => {
const pollUserEvents = () => {
$.ajax({
method: 'GET',
url: 'http://localhost:8080/githubEvents',
success: (data) => {
callback(data) // process the data
},
complete: () => {
pollUserEvents();
},
timeout: 30000
})
}
pollUserEvents()
}
这基本上是一个长轮询功能,它像往常一样第一次运行,但是它设置了三十(30)秒的超时,并且在每次对服务器进行Async Ajax调用之后,回调都会再次调用Ajax。
AJAX调用可在HTTP协议上运行,这意味着默认情况下,对同一域的请求应进行多路复用。我们发现这种方法存在一些陷阱。
多路复用(轮询响应实际上无法同步)
轮询需要3次往返(TCP SIN,SSL和数据)
超时(如果连接保持空闲时间太长,代理服务器将关闭连接)
您可以在这里阅读更多关于现实世界的挑战。
2.使用WebSockets:
WebSocket只是客户端和服务器之间的持久连接。这是一种通过单个TCP连接提供全双工通信通道的通信协议。
RFC 6455声明WebSocket“旨在在HTTP端口80和443上工作,并支持HTTP代理和中介”,从而使其与HTTP协议兼容。为了实现兼容性,WebSocket握手使用HTTP升级标头将HTTP协议更改为WebSocket协议。 HTTP和WebSocket都位于OSI模型的应用程序层,因此依赖于第4层的TCP。
有一个MDN文档详细解释了WebSocket,我也建议您阅读它。
让我们看看一个非常简单的WebSocket客户端实现的样子:
$(function () {
// if user is running mozilla then use it's built-in WebSocket
window.WebSocket = window.WebSocket || window.MozWebSocket;const connection = new WebSocket('ws://localhost:8080/githubEvents');
connection.onopen = function () {
// connection is opened and ready to use
};connection.onerror = function (error) {
// an error occurred when sending/receiving data
};connection.onmessage = function (message) {
// try to decode json (I assume that each message
// from server is json)
try {
const githubEvent = JSON.parse(message.data); // display to the user appropriately
} catch (e) {
console.log('This doesn\'t look like a valid JSON: '+ message.data);
return;
}
// handle incoming message
};
});
如果服务器支持WebSocket协议,它将同意升级,并将通过响应中的Upgrade标头传达此信息。
让我们看看如何在Node.JS(服务器)中实现:
const express = require('express');
const events = require('./events');
const path = require('path');const app = express();
const port = process.env.PORT || 5001;
const expressWs = require('express-ws')(app);
app.get('/', function(req, res) {
res.sendFile(path.join(__dirname + '/static/index.html'));
});app.ws('/', function(ws, req) {
const githubEvent = {}; // sample github Event from Github event API https://api.github.com/events
ws.send('message', githubEvent);
});app.listen(port, function() {
console.log('Listening on', port);
});
一旦我们从GitHub事件API获得数据,就可以在建立连接后将其流式传输到客户端。 对于我们的场景,这种方法也有一些陷阱。
- 使用WebSockets,我们需要自己处理许多由HTTP处理的问题。
- WebSocket是用于传输数据的另一种协议,它不会通过HTTP / 2连接自动多路复用。 在服务器和客户端上实现自定义多路复用有点复杂。
- WebSocket是基于帧的,而不是基于流的。 当我们打开网络标签。 您可以看到WebSocket消息在frame中列出。
有关WebSocket的详细信息,请查看这篇很棒的文章,在这里您可以阅读有关碎片以及如何在后台进行处理的更多信息。
3.使用SSE:
SSE是一种机制,一旦建立了客户端-服务器连接,服务器就可以将数据异步推送到客户端。 然后,只要有新的“大块”数据可用,服务器就可以决定发送数据。 可以将其视为单向发布-订阅模型。
它还提供了一个标准的JavaScript客户端API,称为EventSource,已在大多数现代浏览器中实现,作为W3C的HTML5标准的一部分。 Polyfills可用于不支持EventSource API的浏览器。
我们可以看到Edge和Opera Mini落后于此实现,对于SSE而言,最重要的案例是针对移动浏览器设备,因为这些浏览器没有可行的市场份额。 Yaffle是事件源的众所周知的pollyfill。
由于SSE是基于HTTP的,因此它很自然地与HTTP / 2相适应,并且可以结合使用以实现两者的最佳选择:HTTP / 2处理基于多路复用流的有效传输层,而SSE为应用程序提供API以实现 推。 因此,开箱即用地通过HTTP / 2实现多路复用。 连接断开时会通知客户端和服务器。 通过使用消息维护唯一的ID,服务器可以看到客户端错过了n条消息,并在重新连接时发送了未完成消息的积压。
让我们看看示例客户端实现的外观:
const evtSource = new EventSource('/events');
evtSource.addEventListener('event', function(evt) {
const data = JSON.parse(evt.data);
// Use data here
},false);
此代码段非常简单。 它连接到我们的源并等待接收消息。 现在,示例NodeJS服务器将如下所示。
// events.js
const EventEmitter = require('eventemitter3');
const emitter = new EventEmitter();function subscribe(req, res) {
res.writeHead(200, {
'Content-Type': 'text/event-stream',
'Cache-Control': 'no-cache',
Connection: 'keep-alive'
});// Heartbeat
const nln = function() {
res.write('\n');
};
const hbt = setInterval(nln, 15000);const onEvent = function(data) {
res.write('retry: 500\n');
res.write(`event: event\n`);
res.write(`data: ${JSON.stringify(data)}\n\n`);
};emitter.on('event', onEvent);
// Clear heartbeat and listener
req.on('close', function() {
clearInterval(hbt);
emitter.removeListener('event', onEvent);
});
}function publish(eventData) {
// Emit events here recieved from Github/Twitter APIs
emitter.emit('event', eventData);
}module.exports = {
subscribe, // Sending event data to the clients
publish // Emiting events from streaming servers
};// App.js
const express = require('express');
const events = require('./events');
const port = process.env.PORT || 5001;
const app = express();app.get('/events', cors(), events.subscribe);
app.listen(port, function() {
console.log('Listening on', port);
});
我们从这种方法中获得的主要好处是:
- 实施更简单,数据效率更高
- 开箱即用地通过HTTP / 2自动多路复用
- 将客户端上数据的连接数限制为一个
如何在SSE,WebSocket和Polling中进行选择?
经过漫长而详尽的客户端和服务器实施之后,SSE似乎是我们解决数据交付问题的最终答案。也有一些问题,但是可以解决。
可以利用服务器发送事件的应用程序的一些简单示例:
- 实时股价流图
- 重要事件的实时新闻报道(发布链接,推文和图片)
- 由Twitter的流API提供的实时Github / Twitter仪表板墙
- 监视服务器统计信息(如正常运行时间,运行状况和正在运行的进程)的监视器。
但是,SSE不仅是其他提供快速更新的方法的可行替代方案。在某些特定情况下,例如在SSE被证明是理想解决方案的情况下,每个人都可以胜过其他人。考虑一个像MMO(大型多人在线)游戏这样的场景,该场景需要来自连接两端的大量消息。在这种情况下,WebSockets将压制SSE。
如果您的用例需要显示实时的市场新闻,市场数据,聊天应用程序等,例如在我们的案例中,依靠HTTP / 2 + SSE将为您提供有效的双向通信渠道,同时又能获得留在其中的好处HTTP世界。
如果您想为我们的用例获取示例客户端-服务器实现,请签出GitHub代码。
资源
- “caniuse.com”
- “使用服务器发送的事件进行流更新”,HTML5 Rocks的Eric Bidelman
- “使用HTML5 SSE的数据推送应用”,O’Reilly Media的Darren Cook
感谢您阅读。如果您认为这篇文章很有用,请在您的圈子中分享。
原文:https://codeburst.io/polling-vs-sse-vs-websocket-how-to-choose-the-right-one-1859e4e13bd9
本文:https://pub.intelligentx.net/polling-vs-sse-vs-websocket-how-choose-right-one
讨论:请加入知识星球或者小红圈【首席架构师圈】
- 380 次浏览
【通讯协议】HTTP/2和GRPC:微服务通信的事实标准
视频号
微信公众号
知识星球
HTTP/2和gRPC允许微服务之间更快、更简单、更健壮的通信。但你有没有想过,我们为什么要使用这种技术堆栈,他们为什么要发明HTTP/2和gRPC?
在本文中,让我们看看HTTP/2与HTTP/1.x的主要区别是什么,它解决了什么问题,以及gRPC如何在后台利用HTTP/2并利用HTTP/2的所有效率。
HTTP协议背景
应用层是开放系统互连(OSI)模型中的最顶层,即第7层。应用层有许多网络协议,HTTP就是其中之一。
HTTP/1.1
HTTP/1.1是1989年作为万维网的通信标准开发的。HTTP使用类似GET POST或DELETE的方法来指定客户端希望对资源执行的操作。
例如,假设您正在访问www.example.com网站。当您导航到此URL时,web浏览器会以基于文本的消息的形式发送HTTP请求。
响应此请求,除了HTML中调用的任何图像、样式表或其他资源外,Web服务器还会向请求客户端返回一个HTML页面。请注意,在第一次数据调用中,并不是所有的资源都返回给客户端。请求和响应将在服务器和客户端之间来回发送,直到web浏览器收到在屏幕上呈现HTML页面内容所需的所有资源。
HTTP/2
HTTP/2于2015年5月发布。从技术角度来看,区分HTTP/1.1和HTTP/2的最重要特征之一是二进制成帧层。与将所有请求和响应保持为纯文本格式的HTTP/1.1不同,HTTP/2使用二进制框架层以二进制格式封装所有消息,同时仍然保持HTTP语义,如谓词、方法和标头。应用程序级API仍会以传统HTTP格式创建消息,但底层会将这些消息转换为二进制。这确保了在HTTP/2之前创建的web应用程序在与新协议交互时可以继续正常运行。
因此,HTTP/1.1和HTTP/2共享语义,确保在这两种协议中服务器和客户端之间传输的请求和响应以具有头和体的传统格式化消息的形式到达目的地,使用GET和POST等熟悉的方法。在下一节中,让我们看看HTTP/1.1如何尝试通过其交付模型优化效率,以及由此产生的问题,然后是HTTP/2的二进制帧层的优势,以及它如何对请求进行优先级排序的描述。
HTTP/1.1问题
Pipelining和线路堵塞头
客户端在HTTPGET请求上收到的第一个响应通常不是完全呈现的页面。相反,它包含指向请求页面所需的其他资源的链接。客户端发现,只有在下载页面后,页面的完整呈现才需要服务器提供这些额外资源。因此,客户端将不得不发出额外的请求来检索这些资源。在HTTP/1.0中,客户端必须断开并重新建立每一个新请求的TCP连接,这在时间和资源方面都是一件代价高昂的事情。
HTTP/1.1通过引入持久连接和流水线来解决这个问题。对于持久连接,HTTP/1.1假设TCP连接应该保持打开,除非直接通知关闭,这大大提高了重新建立TCP连接的成本。通过流水线,客户端可以沿着同一连接一个接一个地发送请求,而无需等待对每个请求的响应。
但是,服务器仍然需要按照传入请求的顺序发送响应。因此HTTP/1.1仍然是一个FIFO队列,在某些情况下,位于队列头部的请求无法检索其所需的资源,将阻塞其后面的所有请求。这被称为行头(HOL)阻塞。添加单独的并行TCP连接可能有助于解决此问题,但客户端和服务器之间可能存在的并发TCP连接数量有限,并且每个新连接都需要大量资源,这与HTTP/1.0的问题相同。
HTTP/2的创建就是为了解决这些问题。让我们看看使用二进制帧层可以如何解决这些问题。
HTTP/2和HTTP/1.x之间的主要区别
二进制成帧层
在HTTP/2连接中,存在多个数据流。每个流都由熟悉的请求/响应格式的多条消息组成。
这些消息中的每一条都被拆分为称为帧的较小单元。
在最细粒度的层面上,通信信道由一堆二进制编码的帧组成,每个帧都标记到一个特定的流。识别标签允许连接在传输期间交错这些帧,并在另一端重新组装它们。交错的请求和响应可以并行运行,而不会阻塞它们后面的消息,这一过程称为多路复用。多路复用通过确保没有消息需要等待另一个消息完成,解决了HTTP/1.1中的行头阻塞问题。这也意味着服务器和客户端可以发送并发请求和响应,从而实现更大的控制和更高效的连接管理。
由于多路复用允许客户端并行构建多个流,因此这些流只需要使用单个TCP连接。通过减少整个网络的内存和处理占用,每个源拥有一个持久连接改进了HTTP/1.1。这导致了更好的网络和带宽利用率,从而降低了总体运营成本。
头压缩
每个HTTP传输都携带一组头,这些头描述了传输的资源及其属性。在HTTP/1.x中,此元数据始终以纯文本形式发送,每次传输会增加500-800字节的开销,如果使用HTTP cookie,则有时会增加千字节。为了减少这种开销并提高性能,HTTP/2使用HPACK压缩格式压缩请求和响应标头元数据。
除了压缩之外,客户端和服务器还维护一个常见字段及其压缩值的列表。因此,当这些字段被重复时,它们只是包括对压缩值的引用。
服务器推送
除了对原始请求的响应之外,服务器还可以向客户端推送额外的资源,而客户端不必显式地请求每个资源。
例如,当浏览器请求页面时,服务器会在响应中发送HTML,然后需要等待浏览器解析HTML并发出对所有嵌入资产的请求,然后才能开始发送JavaScript、图像和CSS。
服务器推送可能允许服务器通过将其认为客户端需要的响应“推送”到其缓存中来避免这种往返延迟。
然而,推动反应并不是“神奇的”——如果使用不当,可能会损害表现。正确使用服务器推送是一个正在进行的实验和研究领域。
总之,与HTTP/1.1相比,HTTP/2:
- 是二进制的,而不是文本的
- 完全多路复用,而不是有序和阻塞
- 因此可以使用一个连接进行并行
- 使用标头压缩来减少开销
- 允许服务器主动将响应“推送”到客户端缓存中
有关HTTP/2的更多详细信息,请访问HTTP2Spec和O'Reilly高性能浏览器网络。
现在您可能想知道HTTP/2和gRPC之间的关系是什么。gRPC使用HTTP/2作为其消息传递协议,因此它固有了HTTP/2的所有效率。在下一节中,让我们更详细地了解gRPC提供了哪些功能,并将其与其他通信类型进行比较。
微服务之间的通信
服务通过消息以同步或异步的方式相互通信。让我们看看服务间通信是如何发展的。
SOAP-简单对象访问协议
- 客户端和服务器以XML的形式交换数据
- 由于它的XML,它可以在完全独立于语言和平台的平台上实现。
- 可以通过HTTP、SMTP、TCP、UDP等任何协议进行操作。
- 至今仍存在于许多遗留服务中。
- 很难处理,有一套严格的编码规则。
- 协议的冗长、XML的解析速度慢以及缺乏标准化的交互模型,导致了更直接地使用HTTP协议的服务占主导地位。例如REST。
REST-重新呈现状态传输
- 交互(创建-读取-更新-删除)资源的标准方式。
- 公开API(URL)作为资源的访问点。
- 只能使用HTTP。
- 仅公开HTTP方法(POST、GET、PUT、PATCH和DELETE)上的CRUD行为。如果我们的需求与这些定义良好的规则稍有不同,我们需要调整这组名称以公开我们的API。
- 没有定义严格的接口类型。即使我们有OpenAPI或Swagger规范,它也没有与带下划线的体系结构或消息协议进行类型绑定。
gRPC-Google远程过程调用
- 由谷歌设计的开源RPC框架。
- 公开要远程调用的其他服务的过程。
- 最大的优点是它可以与HTTP/2协议一起工作,并且因此受益于HTTP/2的许多上述效率。
- 实现协议缓冲区或Protobuf,一种gRPC中的IDL(接口描述语言)。
- 现在,我们了解了几种流行的相互通信协议的背景和总体思想。让我们看看优缺点,以及为什么我们应该选择gRPC而不是REST来进行微服务通信。
gRPC vs REST
HTTP/1.1与HTTP/2
RESTAPI遵循通常基于HTTP1.1构建的请求-响应通信模型。不幸的是,这意味着,如果微服务从客户端接收到多个请求,则模型必须一次处理每个请求,从而降低整个系统的速度。然而,RESTAPI也可以建立在HTTP/2上,但通信的请求-响应模型保持不变,这使得RESTAPI无法充分利用HTTP2的优势,例如流通信和双向支持。
gRPC没有面临类似的障碍。它基于HTTP2构建,支持双向通信和流式通信。gRPC可以通过不断地流式传输信息来同时处理请求,同时也可以处理类似于基于HTTP1.1构建的“一元”交互。
有效载荷数据结构
如前所述,gRPC默认情况下使用协议缓冲区来序列化有效负载数据。此解决方案更轻,因为它启用了高度压缩的格式并减小了消息的大小。此外,Protobuf(或协议缓冲区)是二进制的;因此,它对结构化数据进行序列化和反序列化,以便进行通信和传输。换句话说,强类型消息可以自动从Protobuf转换为客户端和服务器的编程语言。
这可以减少从字符串转换为类型的许多错误,例如,客户端发送一个int64字段类型,该字段类型被转换为JSON字符串并发送到服务器,但服务器期望int32字段类型和各种错误都可能发生。
REST主要依靠JSON或XML格式来发送和接收数据。事实上,尽管JSON没有强制要求任何结构,但它是最流行的格式,因为它具有灵活性和发送动态数据的能力,而不必遵循严格的结构。使用JSON的另一个显著好处是它的可读性级别,Protobuf还无法与之竞争。
尽管如此,JSON在数据传输方面并没有那么轻量级或快速。原因在于,在使用REST时,JSON(或其他格式)必须序列化,并转换为客户端和服务器端使用的编程语言。这给传输数据的过程增加了额外的步骤和开销,从而可能损害性能并导致错误。
生成代码功能
与gRPC不同,REST API不提供内置代码生成功能,这意味着开发人员必须使用Swagger或Postman等第三方工具来为API请求生成代码。
相比之下,gRPC具有原生代码生成功能,因为它的协议编译器与几种编程语言兼容。这对于集成以不同语言和平台开发的各种服务的微服务系统尤其有益。
结论
在本文中,我们了解了HTTP/2,它为什么诞生,它解决了什么问题,与HTTP/1.x相比的关键差异,以及gRPC如何在后台利用HTTP/2并利用HTTP/2的所有效率。我们还比较了REST和gRPC,发现gRPC在性能、有效负载数据结构和本地生成代码的能力方面比REST有很多优势。这些原因使得HTTP/2和gRPC完全适合高性能、可靠性和健壮的微服务通信。
参考文献
- HTTP/1.1 vs HTTP/2: What’s the Difference? (digitalocean.com)
- HTTP/2 and gRPC — The Next Generation of Microservices Interactions (blog.netsil.com)
- Introduction to HTTP/2 (developers.google.com)
- HTTP/2 Frequently Asked Questions (http2.github.io)
- GRPC VS REST: COMPARING APIS ARCHITECTURAL STYLES (www.imaginarycloud.com)
- 134 次浏览