chash-balance.t 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656
  1. #
  2. # Licensed to the Apache Software Foundation (ASF) under one or more
  3. # contributor license agreements. See the NOTICE file distributed with
  4. # this work for additional information regarding copyright ownership.
  5. # The ASF licenses this file to You under the Apache License, Version 2.0
  6. # (the "License"); you may not use this file except in compliance with
  7. # the License. You may obtain a copy of the License at
  8. #
  9. # http://www.apache.org/licenses/LICENSE-2.0
  10. #
  11. # Unless required by applicable law or agreed to in writing, software
  12. # distributed under the License is distributed on an "AS IS" BASIS,
  13. # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14. # See the License for the specific language governing permissions and
  15. # limitations under the License.
  16. #
  17. use t::APISIX 'no_plan';
  18. repeat_each(1);
  19. log_level('info');
  20. no_root_location();
  21. worker_connections(1024);
  22. no_shuffle();
  23. run_tests();
  24. __DATA__
  25. === TEST 1: set route(two upstream node)
  26. --- config
  27. location /t {
  28. content_by_lua_block {
  29. local t = require("lib.test_admin").test
  30. local code, body = t('/apisix/admin/routes/1',
  31. ngx.HTTP_PUT,
  32. [[{
  33. "uri": "/server_port",
  34. "upstream": {
  35. "key": "remote_addr",
  36. "type": "chash",
  37. "nodes": {
  38. "127.0.0.1:1980": 1,
  39. "127.0.0.1:1981": 1
  40. }
  41. }
  42. }]]
  43. )
  44. if code >= 300 then
  45. ngx.status = code
  46. end
  47. ngx.say(body)
  48. }
  49. }
  50. --- request
  51. GET /t
  52. --- response_body
  53. passed
  54. === TEST 2: hit routes
  55. --- config
  56. location /t {
  57. content_by_lua_block {
  58. local http = require "resty.http"
  59. local uri = "http://127.0.0.1:" .. ngx.var.server_port
  60. .. "/server_port"
  61. local ports_count = {}
  62. for i = 1, 12 do
  63. local httpc = http.new()
  64. local res, err = httpc:request_uri(uri, {method = "GET"})
  65. if not res then
  66. ngx.say(err)
  67. return
  68. end
  69. ports_count[res.body] = (ports_count[res.body] or 0) + 1
  70. end
  71. local ports_arr = {}
  72. for port, count in pairs(ports_count) do
  73. table.insert(ports_arr, {port = port, count = count})
  74. end
  75. local function cmd(a, b)
  76. return a.port > b.port
  77. end
  78. table.sort(ports_arr, cmd)
  79. ngx.say(require("toolkit.json").encode(ports_arr))
  80. ngx.exit(200)
  81. }
  82. }
  83. --- request
  84. GET /t
  85. --- response_body
  86. [{"count":12,"port":"1980"}]
  87. === TEST 3: set route(three upstream node)
  88. --- config
  89. location /t {
  90. content_by_lua_block {
  91. local t = require("lib.test_admin").test
  92. local code, body = t('/apisix/admin/routes/1',
  93. ngx.HTTP_PUT,
  94. [[{
  95. "uri": "/server_port",
  96. "upstream": {
  97. "key": "remote_addr",
  98. "type": "chash",
  99. "nodes": {
  100. "127.0.0.1:1980": 1,
  101. "127.0.0.1:1981": 1,
  102. "127.0.0.1:1982": 1
  103. }
  104. }
  105. }]]
  106. )
  107. if code >= 300 then
  108. ngx.status = code
  109. end
  110. ngx.say(body)
  111. }
  112. }
  113. --- request
  114. GET /t
  115. --- response_body
  116. passed
  117. === TEST 4: hit routes
  118. --- config
  119. location /t {
  120. content_by_lua_block {
  121. local http = require "resty.http"
  122. local uri = "http://127.0.0.1:" .. ngx.var.server_port
  123. .. "/server_port"
  124. local ports_count = {}
  125. for i = 1, 12 do
  126. local httpc = http.new()
  127. local res, err = httpc:request_uri(uri, {method = "GET"})
  128. if not res then
  129. ngx.say(err)
  130. return
  131. end
  132. ports_count[res.body] = (ports_count[res.body] or 0) + 1
  133. end
  134. local ports_arr = {}
  135. for port, count in pairs(ports_count) do
  136. table.insert(ports_arr, {port = port, count = count})
  137. end
  138. local function cmd(a, b)
  139. return a.port > b.port
  140. end
  141. table.sort(ports_arr, cmd)
  142. ngx.say(require("toolkit.json").encode(ports_arr))
  143. ngx.exit(200)
  144. }
  145. }
  146. --- request
  147. GET /t
  148. --- response_body
  149. [{"count":12,"port":"1982"}]
  150. === TEST 5: set route(three upstream node)
  151. --- config
  152. location /t {
  153. content_by_lua_block {
  154. local t = require("lib.test_admin").test
  155. local code, body = t('/apisix/admin/routes/1',
  156. ngx.HTTP_PUT,
  157. [[{
  158. "uri": "/server_port",
  159. "upstream": {
  160. "key": "remote_addr",
  161. "type": "chash",
  162. "nodes": {
  163. "127.0.0.1:1980": 1,
  164. "127.0.0.1:1981": 0,
  165. "127.0.0.1:1982": 0
  166. }
  167. }
  168. }]]
  169. )
  170. if code >= 300 then
  171. ngx.status = code
  172. end
  173. ngx.say(body)
  174. }
  175. }
  176. --- request
  177. GET /t
  178. --- response_body
  179. passed
  180. === TEST 6: hit routes
  181. --- config
  182. location /t {
  183. content_by_lua_block {
  184. local http = require "resty.http"
  185. local uri = "http://127.0.0.1:" .. ngx.var.server_port
  186. .. "/server_port"
  187. local ports_count = {}
  188. for i = 1, 12 do
  189. local httpc = http.new()
  190. local res, err = httpc:request_uri(uri, {method = "GET"})
  191. if not res then
  192. ngx.say(err)
  193. return
  194. end
  195. ports_count[res.body] = (ports_count[res.body] or 0) + 1
  196. end
  197. local ports_arr = {}
  198. for port, count in pairs(ports_count) do
  199. table.insert(ports_arr, {port = port, count = count})
  200. end
  201. local function cmd(a, b)
  202. return a.port > b.port
  203. end
  204. table.sort(ports_arr, cmd)
  205. ngx.say(require("toolkit.json").encode(ports_arr))
  206. ngx.exit(200)
  207. }
  208. }
  209. --- request
  210. GET /t
  211. --- response_body
  212. [{"count":12,"port":"1980"}]
  213. === TEST 7 set route(three upstream node with querystring)
  214. --- config
  215. location /t {
  216. content_by_lua_block {
  217. local t = require("lib.test_admin").test
  218. local code, body = t('/apisix/admin/routes/1',
  219. ngx.HTTP_PUT,
  220. [[{
  221. "uri": "/server_port",
  222. "upstream": {
  223. "key": "query_string",
  224. "type": "chash",
  225. "nodes": {
  226. "127.0.0.1:1980": 1,
  227. "127.0.0.1:1981": 1,
  228. "127.0.0.1:1982": 1
  229. }
  230. }
  231. }]]
  232. )
  233. if code >= 300 then
  234. ngx.status = code
  235. end
  236. ngx.say(body)
  237. }
  238. }
  239. --- request
  240. GET /t
  241. --- response_body
  242. passed
  243. === TEST 8: hit routes with querystring
  244. --- config
  245. location /t {
  246. content_by_lua_block {
  247. local http = require "resty.http"
  248. local uri = "http://127.0.0.1:" .. ngx.var.server_port
  249. .. "/server_port?var=2&var2="
  250. local t = {}
  251. local ports_count = {}
  252. for i = 1, 180 do
  253. local th = assert(ngx.thread.spawn(function(i)
  254. local httpc = http.new()
  255. local res, err = httpc:request_uri(uri..i, {method = "GET"})
  256. if not res then
  257. ngx.log(ngx.ERR, err)
  258. return
  259. end
  260. ports_count[res.body] = (ports_count[res.body] or 0) + 1
  261. end, i))
  262. table.insert(t, th)
  263. end
  264. for i, th in ipairs(t) do
  265. ngx.thread.wait(th)
  266. end
  267. local ports_arr = {}
  268. for port, count in pairs(ports_count) do
  269. table.insert(ports_arr, {port = port, count = count})
  270. end
  271. local function cmd(a, b)
  272. return a.count > b.count
  273. end
  274. table.sort(ports_arr, cmd)
  275. if (ports_arr[1].count - ports_arr[3].count) / ports_arr[2].count > 0.2 then
  276. ngx.say(require("toolkit.json").encode(ports_arr))
  277. else
  278. ngx.say('ok')
  279. end
  280. }
  281. }
  282. --- request
  283. GET /t
  284. --- wait: 5
  285. --- response_body
  286. ok
  287. === TEST 9: set route(three upstream node with arg_xxx)
  288. --- config
  289. location /t {
  290. content_by_lua_block {
  291. local t = require("lib.test_admin").test
  292. local code, body = t('/apisix/admin/routes/1',
  293. ngx.HTTP_PUT,
  294. [[{
  295. "uri": "/server_port",
  296. "upstream": {
  297. "key": "arg_device_id",
  298. "type": "chash",
  299. "nodes": {
  300. "127.0.0.1:1980": 1,
  301. "127.0.0.1:1981": 1,
  302. "127.0.0.1:1982": 1
  303. }
  304. }
  305. }]]
  306. )
  307. if code >= 300 then
  308. ngx.status = code
  309. end
  310. ngx.say(body)
  311. }
  312. }
  313. --- request
  314. GET /t
  315. --- response_body
  316. passed
  317. === TEST 10: hit routes with args
  318. --- config
  319. location /t {
  320. content_by_lua_block {
  321. local http = require "resty.http"
  322. local uri = "http://127.0.0.1:" .. ngx.var.server_port
  323. .. "/server_port?device_id="
  324. local t = {}
  325. local ports_count = {}
  326. for i = 1, 180 do
  327. local th = assert(ngx.thread.spawn(function(i)
  328. local httpc = http.new()
  329. local res, err = httpc:request_uri(uri..i, {method = "GET"})
  330. if not res then
  331. ngx.log(ngx.ERR, err)
  332. return
  333. end
  334. ports_count[res.body] = (ports_count[res.body] or 0) + 1
  335. end, i))
  336. table.insert(t, th)
  337. end
  338. for i, th in ipairs(t) do
  339. ngx.thread.wait(th)
  340. end
  341. local ports_arr = {}
  342. for port, count in pairs(ports_count) do
  343. table.insert(ports_arr, {port = port, count = count})
  344. end
  345. local function cmd(a, b)
  346. return a.count > b.count
  347. end
  348. table.sort(ports_arr, cmd)
  349. if (ports_arr[1].count - ports_arr[3].count) / ports_arr[2].count > 0.2 then
  350. ngx.say(require("toolkit.json").encode(ports_arr))
  351. else
  352. ngx.say('ok')
  353. end
  354. }
  355. }
  356. --- request
  357. GET /t
  358. --- wait: 5
  359. --- response_body
  360. ok
  361. === TEST 11: set route(weight 0)
  362. --- config
  363. location /t {
  364. content_by_lua_block {
  365. local t = require("lib.test_admin").test
  366. local code, body = t('/apisix/admin/routes/1',
  367. ngx.HTTP_PUT,
  368. [[{
  369. "uri": "/server_port",
  370. "upstream": {
  371. "key": "arg_device_id",
  372. "type": "chash",
  373. "nodes": {
  374. "127.0.0.1:1980": 0,
  375. "127.0.0.1:1981": 0
  376. }
  377. }
  378. }]]
  379. )
  380. if code >= 300 then
  381. ngx.status = code
  382. end
  383. ngx.say(body)
  384. }
  385. }
  386. --- request
  387. GET /t
  388. --- response_body
  389. passed
  390. === TEST 12: hit routes
  391. --- config
  392. location /t {
  393. content_by_lua_block {
  394. local http = require "resty.http"
  395. local uri = "http://127.0.0.1:" .. ngx.var.server_port
  396. .. "/server_port?device_id=1"
  397. local httpc = http.new()
  398. local res, err = httpc:request_uri(uri, {method = "GET"})
  399. if not res then
  400. ngx.say(err)
  401. return
  402. end
  403. ngx.status = res.status
  404. ngx.say(res.body)
  405. }
  406. }
  407. --- request
  408. GET /t
  409. --- error_code_like: ^(?:50\d)$
  410. --- error_log
  411. failed to find valid upstream server, no valid upstream node
  412. === TEST 13: set route(ensure retry can try every node)
  413. --- config
  414. location /t {
  415. content_by_lua_block {
  416. local t = require("lib.test_admin").test
  417. local code, body = t('/apisix/admin/routes/1',
  418. ngx.HTTP_PUT,
  419. [[{
  420. "uri": "/server_port",
  421. "upstream": {
  422. "key": "arg_device_id",
  423. "type": "chash",
  424. "nodes": {
  425. "127.0.0.1:1979": 1000,
  426. "127.0.0.1:1980": 1
  427. }
  428. }
  429. }]]
  430. )
  431. if code >= 300 then
  432. ngx.status = code
  433. end
  434. ngx.say(body)
  435. }
  436. }
  437. --- request
  438. GET /t
  439. --- response_body
  440. passed
  441. === TEST 14: hit routes
  442. --- config
  443. location /t {
  444. content_by_lua_block {
  445. local http = require "resty.http"
  446. local uri = "http://127.0.0.1:" .. ngx.var.server_port
  447. .. "/server_port?device_id=1"
  448. local httpc = http.new()
  449. local res, err = httpc:request_uri(uri, {method = "GET"})
  450. if not res then
  451. ngx.say(err)
  452. return
  453. end
  454. ngx.say(res.status)
  455. }
  456. }
  457. --- request
  458. GET /t
  459. --- response_body
  460. 200
  461. --- error_log
  462. Connection refused
  463. === TEST 15: set routes with very big weights
  464. --- config
  465. location /t {
  466. content_by_lua_block {
  467. local t = require("lib.test_admin").test
  468. local code, body = t('/apisix/admin/routes/1',
  469. ngx.HTTP_PUT,
  470. [[{
  471. "uri": "/server_port",
  472. "upstream": {
  473. "key": "arg_device_id",
  474. "type": "chash",
  475. "nodes": {
  476. "127.0.0.1:1980": 1000000000,
  477. "127.0.0.1:1981": 2000000000,
  478. "127.0.0.1:1982": 1000000000
  479. }
  480. }
  481. }]]
  482. )
  483. if code >= 300 then
  484. ngx.status = code
  485. end
  486. ngx.say(body)
  487. }
  488. }
  489. --- request
  490. GET /t
  491. --- response_body
  492. passed
  493. === TEST 16: hit
  494. --- config
  495. location /t {
  496. content_by_lua_block {
  497. local http = require "resty.http"
  498. local uri = "http://127.0.0.1:" .. ngx.var.server_port
  499. .. "/server_port?device_id=1"
  500. local httpc = http.new()
  501. local res, err = httpc:request_uri(uri, {method = "GET"})
  502. if not res then
  503. ngx.say(err)
  504. return
  505. end
  506. -- a `size too large` error will be thrown if we don't reduce the weight
  507. ngx.say(res.status)
  508. }
  509. }
  510. --- request
  511. GET /t
  512. --- response_body
  513. 200
  514. === TEST 17: set routes with very big weights, some nodes have zero weight
  515. --- config
  516. location /t {
  517. content_by_lua_block {
  518. local t = require("lib.test_admin").test
  519. local code, body = t('/apisix/admin/routes/1',
  520. ngx.HTTP_PUT,
  521. [[{
  522. "uri": "/server_port",
  523. "upstream": {
  524. "key": "arg_device_id",
  525. "type": "chash",
  526. "nodes": {
  527. "127.0.0.1:1980": 1000000000,
  528. "127.0.0.1:1981": 0,
  529. "127.0.0.1:1982": 4000000000
  530. }
  531. }
  532. }]]
  533. )
  534. if code >= 300 then
  535. ngx.status = code
  536. end
  537. ngx.say(body)
  538. }
  539. }
  540. --- request
  541. GET /t
  542. --- response_body
  543. passed
  544. === TEST 18: hit
  545. --- config
  546. location /t {
  547. content_by_lua_block {
  548. local http = require "resty.http"
  549. local uri = "http://127.0.0.1:" .. ngx.var.server_port
  550. .. "/server_port?device_id=1"
  551. local httpc = http.new()
  552. local res, err = httpc:request_uri(uri, {method = "GET"})
  553. if not res then
  554. ngx.say(err)
  555. return
  556. end
  557. -- a `size too large` error will be thrown if we don't reduce the weight
  558. ngx.say(res.status)
  559. }
  560. }
  561. --- request
  562. GET /t
  563. --- response_body
  564. 200