Pengantar Kong

1. Perkenalan

Kong adalah gateway API sumber terbuka dan lapisan manajemen layanan mikro.

Berdasarkan Nginx dan lua-nginx-module (khususnya OpenResty), arsitektur pluggable Kong membuatnya fleksibel dan bertenaga.

2. Konsep Utama

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

  • Objek API - membungkus properti dari titik akhir HTTP (s) yang menyelesaikan tugas tertentu atau memberikan beberapa layanan. Konfigurasi termasuk metode HTTP, URI titik akhir, URL upstream yang mengarah ke server API kami dan akan digunakan untuk permintaan proxy, penghentian maksimum, batas kecepatan, batas waktu, dll.
  • Objek Konsumen - membungkus properti siapa pun yang menggunakan titik akhir API kami. Ini akan digunakan untuk pelacakan, kontrol akses, dan lainnya
  • Objek Upstream - menjelaskan bagaimana permintaan yang masuk akan diproksikan atau diseimbangkan muatannya, diwakili oleh nama host virtual
  • Objek Target - mewakili layanan yang diimplementasikan dan dilayani, diidentifikasi oleh nama host (atau alamat IP) dan port. Perhatikan bahwa target dari setiap upstream hanya dapat ditambahkan atau dinonaktifkan. Sejarah perubahan target dipertahankan oleh hulu
  • Objek Plugin - fitur yang dapat dicolokkan untuk memperkaya fungsionalitas aplikasi kita selama siklus hidup permintaan dan respons. Misalnya, otentikasi API dan fitur pembatasan kecepatan dapat ditambahkan dengan mengaktifkan plugin yang relevan. Kong menyediakan plugin yang sangat kuat di galeri pluginnya
  • Admin API - Endpoint API RESTful yang digunakan untuk mengelola konfigurasi Kong, titik akhir, konsumen, plugin, dan sebagainya

Gambar di bawah ini menggambarkan perbedaan Kong dari arsitektur lawas, yang dapat membantu kita memahami mengapa Kong memperkenalkan konsep-konsep ini:

(sumber: //getkong.org/)

3. Penyiapan

Dokumentasi resmi memberikan instruksi rinci untuk berbagai lingkungan.

4. Manajemen API

Setelah menyiapkan Kong secara lokal, mari kita coba fitur-fitur hebat Kong dengan mem-proxy endpoint kueri stok sederhana kami:

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

4.1. Menambahkan API

Selanjutnya, mari tambahkan API kueri kami ke Kong.

Admin API dapat diakses melalui // localhost: 8001 , jadi semua operasi pengelolaan API kami akan dilakukan dengan URI dasar 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": "/" }
  • "Nama" adalah pengenal untuk API, yang digunakan saat memanipulasi perilakunya
  • "Host" akan digunakan untuk mengarahkan permintaan yang masuk ke "upstream_url" yang diberikan dengan mencocokkan header "Host"
  • Jalur relatif akan dicocokkan dengan "uris" yang dikonfigurasi

Jika kami ingin menghentikan API atau konfigurasinya salah, kami cukup menghapusnya:

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

Setelah API ditambahkan, mereka akan tersedia untuk dikonsumsi 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 kode di atas, kami mencoba menanyakan harga saham melalui API yang baru saja kami tambahkan ke Kong.

Dengan meminta // localhost: 8000 / stock / btc , kita mendapatkan layanan yang sama seperti meminta langsung dari // localhost: 8080 / stock / btc .

4.2. Menambahkan Konsumen API

Sekarang mari kita bicara tentang keamanan - lebih khusus lagi otentikasi untuk pengguna yang mengakses API kita.

Let's add a consumer to our stock query API so that we can enable the authentication feature later.

To add a consumer for an API is just as simple as adding an API. The consumer's name (or id) is the only required field of all consumer's properties:

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());

Here we added “eugenp” as a new consumer:

{ "username": "eugenp" }

4.3. Enabling Authentication

Here comes the most powerful feature of Kong, plugins.

Now we're going to apply an auth plugin to our proxied stock query API:

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());

If we try to query a stock's price through the proxy URI, the request will be rejected:

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());

Remember that Eugen is one of our API consumers, so we should allow him to use this API by adding an authentication key:

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());

Then Eugen can use this API as before:

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. Advanced Features

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 layanan mikro dan berfokus pada fungsi intinya - mengelola API dan permintaan perutean ke server hulu, serta pada beberapa fitur yang lebih canggih seperti penyeimbangan beban.

Namun, masih banyak lagi fitur yang solid untuk kami jelajahi, dan kami dapat mengembangkan plugin kami sendiri jika perlu - Anda dapat terus menjelajahi dokumentasi resminya di sini.

Seperti biasa, implementasi penuh dapat ditemukan di Github.