Pengenalan Kong

1. Pengenalan

Kong adalah gerbang API sumber terbuka dan lapisan pengurusan perkhidmatan mikro.

Berdasarkan Nginx dan modul lua-nginx (khususnya OpenResty), seni bina Kong yang dapat dipasang menjadikannya fleksibel dan kuat.

2. Konsep Utama

Sebelum kita menyelami contoh kod, mari kita lihat konsep utama di Kong:

  • Objek API - membungkus sifat mana-mana titik akhir HTTP yang menyelesaikan tugas tertentu atau memberikan beberapa perkhidmatan. Konfigurasi merangkumi kaedah HTTP, URI titik akhir, URL hulu yang menunjuk ke pelayan API kami dan akan digunakan untuk permintaan proksi, penghentian maksimum, had kadar, waktu tunggu, dll.
  • Objek Pengguna - membungkus sifat sesiapa sahaja yang menggunakan titik akhir API kami. Ia akan digunakan untuk pengesanan, kawalan akses dan banyak lagi
  • Objek Hulu - menerangkan bagaimana permintaan masuk akan diproksi atau dimuat seimbang, diwakili oleh nama hos maya
  • Objek Objektif - mewakili perkhidmatan yang dilaksanakan dan dilayan, dikenal pasti dengan nama host (atau alamat IP) dan port. Perhatikan bahawa sasaran setiap hulu hanya dapat ditambahkan atau dinonaktifkan. Sejarah perubahan sasaran dikekalkan oleh hulu
  • Objek Plugin - ciri yang dapat dipasang untuk memperkaya fungsi aplikasi kami semasa kitaran hidup permintaan dan respons. Sebagai contoh, pengesahan API dan ciri had kadar boleh ditambah dengan mengaktifkan pemalam yang berkaitan. Kong menyediakan pemalam yang sangat hebat di galeri pemalamnya
  • API Pentadbiran - Titik akhir API RESTful yang digunakan untuk menguruskan konfigurasi Kong, titik akhir, pengguna, pemalam, dan sebagainya

Gambar di bawah menggambarkan bagaimana Kong berbeza dari seni bina warisan, yang dapat membantu kita memahami mengapa ia memperkenalkan konsep-konsep ini:

(sumber: //getkong.org/)

3. Persediaan

Dokumentasi rasmi memberikan arahan terperinci untuk pelbagai persekitaran.

4. Pengurusan API

Setelah menubuhkan Kong secara tempatan, mari kita gunakan ciri-ciri kuat Kong dengan memberikan titik akhir pertanyaan stok mudah kami:

@RestController @RequestMapping("/stock") public class QueryController { @GetMapping("/{code}") public String getStockPrice(@PathVariable String code){ return "BTC".equalsIgnoreCase(code) ? "10000" : "0"; } }

4.1. Menambah API

Seterusnya, mari tambahkan API pertanyaan kami ke Kong.

API pentadbir boleh diakses melalui // localhost: 8001 , jadi semua operasi pengurusan API kami akan dilakukan dengan URI asas ini:

APIObject stockAPI = new APIObject( "stock-api", "stock.api", "//localhost:8080", "/"); HttpEntity apiEntity = new HttpEntity(stockAPI); ResponseEntity addAPIResp = restTemplate.postForEntity( "//localhost:8001/apis", apiEntity, String.class); assertEquals(HttpStatus.CREATED, addAPIResp.getStatusCode());

Di sini, kami menambahkan API dengan konfigurasi berikut:

{ "name": "stock-api", "hosts": "stock.api", "upstream_url": "//localhost:8080", "uris": "/" }
  • "Name" adalah pengecam untuk API, yang digunakan ketika memanipulasi perilakunya
  • "Host" akan digunakan untuk merutekan permintaan masuk ke "upstream_url" yang diberikan dengan mencocokkan tajuk "Host"
  • Jalan relatif akan dipadankan dengan "uris" yang dikonfigurasi

Sekiranya kita ingin menghentikan API atau konfigurasi salah, kita boleh menghapusnya:

restTemplate.delete("//localhost:8001/apis/stock-api");

Setelah API ditambahkan, API akan tersedia untuk dimakan melalui // localhost: 8000 :

String apiListResp = restTemplate.getForObject( "//localhost:8001/apis/", String.class); assertTrue(apiListResp.contains("stock-api")); HttpHeaders headers = new HttpHeaders(); headers.set("Host", "stock.api"); RequestEntity requestEntity = new RequestEntity( headers, HttpMethod.GET, new URI("//localhost:8000/stock/btc")); ResponseEntity stockPriceResp = restTemplate.exchange(requestEntity, String.class); assertEquals("10000", stockPriceResp.getBody());

Dalam contoh kod di atas, kami cuba menanyakan harga saham melalui API yang baru kami tambahkan ke Kong.

Dengan meminta // localhost: 8000 / stock / btc , kami mendapat perkhidmatan yang sama seperti membuat pertanyaan terus dari // localhost: 8080 / stock / btc .

4.2. Menambah Pengguna API

Sekarang mari kita bincangkan keselamatan - pengesahan lebih khusus untuk pengguna yang mengakses API kami.

Mari tambahkan pengguna ke API pertanyaan stok kami supaya kami dapat mengaktifkan ciri pengesahan kemudian.

Untuk menambahkan pengguna untuk API sama mudahnya dengan menambahkan API. Nama pengguna (atau id) adalah satu-satunya bidang yang diperlukan dari semua sifat pengguna:

ConsumerObject consumer = new ConsumerObject("eugenp"); HttpEntity addConsumerEntity = new HttpEntity(consumer); ResponseEntity addConsumerResp = restTemplate.postForEntity( "//localhost:8001/consumers/", addConsumerEntity, String.class); assertEquals(HttpStatus.CREATED, addConsumerResp.getStatusCode());

Di sini kami menambahkan "eugenp" sebagai pengguna baru:

{ "username": "eugenp" }

4.3. Mendayakan Pengesahan

Inilah ciri plugin Kong yang paling hebat.

Sekarang kita akan menerapkan pemalam autentikasi ke API pertanyaan stok proksi kami:

PluginObject authPlugin = new PluginObject("key-auth"); ResponseEntity enableAuthResp = restTemplate.postForEntity( "//localhost:8001/apis/stock-api/plugins", new HttpEntity(authPlugin), String.class); assertEquals(HttpStatus.CREATED, enableAuthResp.getStatusCode());

Sekiranya kita cuba menanyakan harga saham melalui URI proksi, permintaan akan ditolak:

HttpHeaders headers = new HttpHeaders(); headers.set("Host", "stock.api"); RequestEntity requestEntity = new RequestEntity( headers, HttpMethod.GET, new URI("//localhost:8000/stock/btc")); ResponseEntity stockPriceResp = restTemplate .exchange(requestEntity, String.class); assertEquals(HttpStatus.UNAUTHORIZED, stockPriceResp.getStatusCode());

Ingat bahawa Eugen adalah salah satu pengguna API kami, jadi kami harus membenarkannya menggunakan API ini dengan menambahkan kunci pengesahan:

String consumerKey = "eugenp.pass"; KeyAuthObject keyAuth = new KeyAuthObject(consumerKey); ResponseEntity keyAuthResp = restTemplate.postForEntity( "//localhost:8001/consumers/eugenp/key-auth", new HttpEntity(keyAuth), String.class); assertTrue(HttpStatus.CREATED == keyAuthResp.getStatusCode());

Kemudian Eugen dapat menggunakan API ini seperti sebelumnya:

HttpHeaders headers = new HttpHeaders(); headers.set("Host", "stock.api"); headers.set("apikey", consumerKey); RequestEntity requestEntity = new RequestEntity( headers, HttpMethod.GET, new URI("//localhost:8000/stock/btc")); ResponseEntity stockPriceResp = restTemplate .exchange(requestEntity, String.class); assertEquals("10000", stockPriceResp.getBody());

5. Ciri Lanjutan

Aside from basic API proxy and management, Kong also supports API load-balancing, clustering, health checking, and monitoring, etc.

In this section, we're going to take a look at how to load balance requests with Kong, and how to secure admin APIs.

5.1. Load Balancing

Kong provides two strategies of load balancing requests to backend services: a dynamic ring-balancer, and a straightforward DNS-based method. For the sake of simplicity, we'll be using the ring-balancer.

As we mentioned earlier, upstreams are used for load-balancing, and each upstream can have multiple targets.

Kong supports both weighted-round-robin and hash-based balancing algorithms. By default, the weighted-round-robin scheme is used – where requests are delivered to each target according to their weight.

First, let's prepare the upstream:

UpstreamObject upstream = new UpstreamObject("stock.api.service"); ResponseEntity addUpstreamResp = restTemplate.postForEntity( "//localhost:8001/upstreams", new HttpEntity(upstream), String.class); assertEquals(HttpStatus.CREATED, addUpstreamResp.getStatusCode());

Then, add two targets for the upstream, a test version with weight=10, and a release version with weight=40:

TargetObject testTarget = new TargetObject("localhost:8080", 10); ResponseEntity addTargetResp = restTemplate.postForEntity( "//localhost:8001/upstreams/stock.api.service/targets", new HttpEntity(testTarget), String.class); assertEquals(HttpStatus.CREATED, ddTargetResp.getStatusCode()); TargetObject releaseTarget = new TargetObject("localhost:9090",40); addTargetResp = restTemplate.postForEntity( "//localhost:8001/upstreams/stock.api.service/targets", new HttpEntity(releaseTarget), String.class); assertEquals(HttpStatus.CREATED, addTargetResp.getStatusCode());

With the configuration above, we can assume that 1/5 of the requests will go to test version and 4/5 will go to release version:

APIObject stockAPI = new APIObject( "balanced-stock-api", "balanced.stock.api", "//stock.api.service", "/"); HttpEntity apiEntity = new HttpEntity(stockAPI); ResponseEntity addAPIResp = restTemplate.postForEntity( "//localhost:8001/apis", apiEntity, String.class); assertEquals(HttpStatus.CREATED, addAPIResp.getStatusCode()); HttpHeaders headers = new HttpHeaders(); headers.set("Host", "balanced.stock.api"); for(int i = 0; i < 1000; i++) { RequestEntity requestEntity = new RequestEntity( headers, HttpMethod.GET, new URI("//localhost:8000/stock/btc")); ResponseEntity stockPriceResp = restTemplate.exchange(requestEntity, String.class); assertEquals("10000", stockPriceResp.getBody()); } int releaseCount = restTemplate.getForObject( "//localhost:9090/stock/reqcount", Integer.class); int testCount = restTemplate.getForObject( "//localhost:8080/stock/reqcount", Integer.class); assertTrue(Math.round(releaseCount * 1.0 / testCount) == 4);

Note that weighted-round-robin scheme balances requests to backend services approximately to the weight ratio, so only an approximation of the ratio can be verified, reflected in the last line of above code.

5.2. Securing the Admin API

By default, Kong only accepts admin requests from the local interface, which is a good enough restriction in most cases. But if we want to manage it via other network interfaces, we can change the admin_listen value in kong.conf, and configure firewall rules.

Or, we can make Kong serve as a proxy for the Admin API itself. Say we want to manage APIs with path “/admin-api”, we can add an API like this:

APIObject stockAPI = new APIObject( "admin-api", "admin.api", "//localhost:8001", "/admin-api"); HttpEntity apiEntity = new HttpEntity(stockAPI); ResponseEntity addAPIResp = restTemplate.postForEntity( "//localhost:8001/apis", apiEntity, String.class); assertEquals(HttpStatus.CREATED, addAPIResp.getStatusCode());

Now we can use the proxied admin API to manage APIs:

HttpHeaders headers = new HttpHeaders(); headers.set("Host", "admin.api"); APIObject baeldungAPI = new APIObject( "baeldung-api", "baeldung.com", "//ww.baeldung.com", "/"); RequestEntity requestEntity = new RequestEntity( baeldungAPI, headers, HttpMethod.POST, new URI("//localhost:8000/admin-api/apis")); ResponseEntity addAPIResp = restTemplate .exchange(requestEntity, String.class); assertEquals(HttpStatus.CREATED, addAPIResp.getStatusCode());

Surely, we want the proxied API secured. This can be easily achieved by enabling authentication plugin for the proxied admin API.

6. Summary

Dalam artikel ini, kami memperkenalkan Kong - platform untuk gateway API microservice dan fokus pada fungsi utamanya - menguruskan API dan permintaan routing ke pelayan hulu, serta pada beberapa ciri yang lebih maju seperti load balancing.

Namun, terdapat banyak lagi ciri yang kukuh untuk kami terokai, dan kami dapat mengembangkan pemalam kami sendiri jika perlu - anda boleh terus meneroka dokumentasi rasmi di sini.

Seperti biasa, pelaksanaan sepenuhnya dapat dilihat di Github.