A lightweight REST in-memory discovery service.
Features:
-
periodical task for removing expired records;
-
simple dashboard in web browser.
The service was inspired by Eureka web service and it uses for registration and locating services in your environment.
It uses REST JSON-based API for simple integration.
Start the Luntic discovery server:
$> luntic
__ __ _
/ / __ __ ____ / /_ (_)_____
/ / / / / // __ \ / __// // ___/
/ /___/ /_/ // / / // /_ / // /__
/_____/\__,_//_/ /_/ \__//_/ \___/
I was born!
v1.1.0
Copyright (c) by Artem Labazin
REST requests are serviced on: http://0.0.0.0:8080/
If you need to launch Luntic on different port, with specific path prefix, debug access logs or public net address, you are able to set it:
$> luntic --debug --port=9999 --path-prefix=/register localhost
__ __ _
/ / __ __ ____ / /_ (_)_____
/ / / / / // __ \ / __// // ___/
/ /___/ /_/ // / / // /_ / // /__
/_____/\__,_//_/ /_/ \__//_/ \___/
I was born!
v1.1.0
Copyright (c) by Artem Labazin
REST requests are serviced on: http://localhost:9999/register
2017-07-22T03:20:45+03:00 -- POST /register/popa
2017-07-22T03:21:12+03:00 -- GET /register
2017-07-22T03:21:28+03:00 -- DELETE /register/popa/59729a5d8b7df47200000001
We could also start a simple web browser dashboard on separate port:
$> luntic --dashboard:9876
__ __ _
/ / __ __ ____ / /_ (_)_____
/ / / / / // __ \ / __// // ___/
/ /___/ /_/ // / / // /_ / // /__
/_____/\__,_//_/ /_/ \__//_/ \___/
I was born!
v1.1.0
Copyright (c) by Artem Labazin
REST requests are serviced on: http://0.0.0.0:8080/
Dashboard is available on: http://0.0.0.0:9876/
Open the link http://localhost:9876 in your web browser and you could see something like this:
And see group info:
Also there is a API page:
To see all available switches and keys, type the following:
$> luntic --help
Usage: luntic [OPTIONS] [LISTENING_ADDRESS]
A lightweight REST in-memory discovery service
Options:
-p:PORT --port=PORT sets listening port
default: 8080
--path-prefix=PATH sets server's endpoints path prefix
default: '/'
--heartbeat=SECONDS sets heartbeat service cleaner in seconds,
if it set to 0 - turn off heartbeat scheduler
default: 0s
--dashboard=PORT setups a simple web browser-oriented dashboard
for services monitoring
default: turned off
-d --debug turn on debug mode
-v --version prints program's version
-h --help prints this help menu
After starting Luntic server you could register your first app via REST API. I am using HTTPie for request calls, but you can do it with cURL or Postman.
Registering a new app, which has popa group name:
$> http -v POST :8080/popa
POST /popa HTTP/1.1
Accept: */*
Accept-Encoding: gzip, deflate
Connection: keep-alive
Content-Length: 0
Host: localhost:8080
User-Agent: HTTPie/0.9.9
HTTP/1.1 201 Created
Content-Length: 124
content-type: application/json
location: /popa/597298af0937867200000001
x-expired-time: 0
{
"created": "2017-07-22T03:13:35+03:00",
"group": "popa",
"id": "597298af0937867200000001",
"modified": "2017-07-22T03:13:35+03:00"
}
As we can see, we have created a new record in popa application group. The server have generated the instance id 597298af0937867200000001, creation time and last modified time fields.
We are also able to attach our own JSON object to a record in meta field during its creation:
$> http -v POST :8080/popa one=1 two=2
POST /popa HTTP/1.1
Accept: application/json, */*
Accept-Encoding: gzip, deflate
Connection: keep-alive
Content-Length: 24
Content-Type: application/json
Host: localhost:8080
User-Agent: HTTPie/0.9.9
{
"one": "1",
"two": "2"
}
HTTP/1.1 201 Created
Content-Length: 154
content-type: application/json
location: /popa/5972a20d5b31ed7400000001
x-expired-time: 0
{
"created": "2017-07-22T03:53:33+03:00",
"group": "popa",
"id": "5972a20d5b31ed7400000001",
"meta": {
"one": "1",
"two": "2"
},
"modified": "2017-07-22T03:53:33+03:00"
}
To get a just created record - we need to use a path from Location header in creation reponse:
$> http -v :8080/popa/5972a2e54396247500000001
GET /popa/5972a2e54396247500000001 HTTP/1.1
Accept: */*
Accept-Encoding: gzip, deflate
Connection: keep-alive
Host: localhost:8080
User-Agent: HTTPie/0.9.9
HTTP/1.1 200 OK
Content-Length: 154
content-type: application/json
{
"created": "2017-07-22T03:53:33+03:00",
"group": "popa",
"id": "5972a20d5b31ed7400000001",
"meta": {
"one": "1",
"two": "2"
},
"modified": "2017-07-22T03:53:33+03:00"
}
Also, we could get all available records within one group name:
$> http -v :8080/popa
GET /popa HTTP/1.1
Accept: */*
Accept-Encoding: gzip, deflate
Connection: keep-alive
Host: localhost:8080
User-Agent: HTTPie/0.9.9
HTTP/1.1 200 OK
Content-Length: 282
content-type: application/json
[
{
"created": "2017-07-22T03:13:35+03:00",
"group": "popa",
"id": "597298af0937867200000001",
"modified": "2017-07-22T03:13:35+03:00"
},
{
"created": "2017-07-22T03:53:33+03:00",
"group": "popa",
"id": "5972a20d5b31ed7400000001",
"meta": {
"one": "1",
"two": "2"
},
"modified": "2017-07-22T03:53:33+03:00"
}
]
Or, we can get all data in one object - group -> instances:
$> http -v :8080/
GET / HTTP/1.1
Accept: */*
Accept-Encoding: gzip, deflate
Connection: keep-alive
Host: localhost:8080
User-Agent: HTTPie/0.9.9
HTTP/1.1 200 OK
Content-Length: 426
content-type: application/json
{
"popa": [
{
"created": "2017-07-22T03:13:35+03:00",
"group": "popa",
"id": "597298af0937867200000001",
"modified": "2017-07-22T03:13:35+03:00"
},
{
"created": "2017-07-22T03:53:33+03:00",
"group": "popa",
"id": "5972a20d5b31ed7400000001",
"meta": {
"one": "1",
"two": "2"
},
"modified": "2017-07-22T03:53:33+03:00"
}
],
"zuul": [
{
"created": "2017-07-22T04:03:26+03:00",
"group": "zuul",
"id": "5972a45e4396247500000003",
"modified": "2017-07-22T04:03:26+03:00"
}
]
}
To update last modified time you could type this:
$> http -v PUT :8080/popa/5972a20d5b31ed7400000001
PUT /popa/5972a20d5b31ed7400000001 HTTP/1.1
Accept: */*
Accept-Encoding: gzip, deflate
Connection: keep-alive
Content-Length: 0
Host: localhost:8080
User-Agent: HTTPie/0.9.9
HTTP/1.1 200 OK
Content-Length: 154
content-type: application/json
{
"created": "2017-07-22T03:53:33+03:00",
"group": "popa",
"id": "5972a20d5b31ed7400000001",
"meta": {
"one": "1",
"two": "2"
},
"modified": "2017-07-22T04:10:42+03:00"
}
Why do we want to update last modified time? Luntic can be started with --heartbeat=SECONDS switch, which enables scheduled task for periodical removing expired instances in groups. So, with this switch you need to update modified time, otherwise it removes. By default this scheduled task is disabled.
With PUT method you could also change meta field:
$> http -v PUT :8080/popa/5972a20d5b31ed7400000001 greeting="Hello world" one=1 two=2
PUT /popa/5972a20d5b31ed7400000001 HTTP/1.1
Accept: application/json, */*
Accept-Encoding: gzip, deflate
Connection: keep-alive
Content-Length: 51
Content-Type: application/json
Host: localhost:8080
User-Agent: HTTPie/0.9.9
{
"greeting": "Hello world",
"one": "1",
"two": "2"
}
HTTP/1.1 200 OK
Content-Length: 179
content-type: application/json
{
"created": "2017-07-22T03:53:33+03:00",
"group": "popa",
"id": "5972a20d5b31ed7400000001",
"meta": {
"greeting": "Hello world",
"one": "1",
"two": "2"
},
"modified": "2017-07-22T04:12:16+03:00"
}
And at last, we want to remove the record:
$> http -v DELETE :8080/popa/5972a20d5b31ed7400000001
DELETE /popa/5972a20d5b31ed7400000001 HTTP/1.1
Accept: */*
Accept-Encoding: gzip, deflate
Connection: keep-alive
Content-Length: 0
Host: localhost:8080
User-Agent: HTTPie/0.9.9
HTTP/1.1 204 No Content
Content-Length: 0
You also are able to use already prepared Docker image:
$> docker run \
-p 8080:8080 -p 9876:9876 \
xxlabaza/luntic:1.1.0 \
--debug --dashboard:9876
Creates new record.
-
Request
POST [/pathPrefix]/{group}
pathPrefix
- common path prefix, which was set (default value - /) at the start of Luntic program;group
- name of the registering application group.Examples:
-
POST /popa - we didn't set
pathPrefix
(it is optional) at start time and setgroup
in this request as popa; -
POST /api/popa - we set
pathPrefix
at start time and use it in the request (api), also we setgroup
(popa).
Also, you could send a
JSON
object with a request. It will be attached to server generated instance inmeta
field. -
-
Success Response:
As a result of the request we get a response:
Code: 201
Headers:
-
Location: [/pathPrefix]/{group}/{instanceId}
-
X-Expired-Time: <int_seconds_or_zero_if_disabled>
Content type: application/json
JSON fields:
Field name Optional Description id no generated record id group no the group name you set created no the time you registered modified no the time you last accessed the record meta yes if you send JSON
in request, we get it back in that fieldIMPORTANT: Take a look at
X-Expired-Time
header - if it is not0
value, your record expires after that amount of second. To prevent removing your instance record - make aPUT
request within this timeout. -
-
Error Response:
400 - in case of incorrect group set (absent or contains / sign)
-
Examples:
$> http -v POST :8080/popa POST /popa HTTP/1.1 Accept: */* Accept-Encoding: gzip, deflate Connection: keep-alive Content-Length: 0 Host: localhost:8080 HTTP/1.1 201 Created Content-Length: 124 Content-Type: application/json Location: /popa/597298af0937867200000001 { "created": "2017-07-22T03:13:35+03:00", "group": "popa", "id": "597298af0937867200000001", "modified": "2017-07-22T03:13:35+03:00" }
$> http -v POST :8080/popa one=1 two=2 POST /popa HTTP/1.1 Accept: application/json, */* Accept-Encoding: gzip, deflate Connection: keep-alive Content-Length: 24 Content-Type: application/json Host: localhost:8080 { "one": "1", "two": "2" } HTTP/1.1 201 Created Content-Length: 154 Content-Type: application/json Location: /popa/5972a20d5b31ed7400000001 { "created": "2017-07-22T03:53:33+03:00", "group": "popa", "id": "5972a20d5b31ed7400000001", "meta": { "one": "1", "two": "2" }, "modified": "2017-07-22T03:53:33+03:00" }
Returns all or specific records on the server.
-
Request
Get all
groups
and records:GET [/pathPrefix]/
Get all
group's
records:GET [/pathPrefix]/{group}
Get only specific record:
GET [/pathPrefix]/{group}/{instanceId}
pathPrefix
- common path prefix, which was set (default value - /) at the start of Luntic program;group
- name of the registered application group.instanceId
- service instance id. -
Success Response:
As a result of the request we get a response:
Code: 200
Content type: application/json
Content fields:
Field name Optional Description id no generated record id group no the group name you set created no the time you registered time modified no the time you last accessed the record meta yes if you send JSON
in request, we get it back in that field -
Error Response:
404 - in case of nonexistent
group
orinstanceId
. -
Examples:
Get all records in the services:
$> http -v :8080/ / HTTP/1.1 Accept: */* Accept-Encoding: gzip, deflate Connection: keep-alive Host: localhost:8080 User-Agent: HTTPie/0.9.9 HTTP/1.1 200 OK Content-Length: 397 content-type: application/json { "popa": [ { "created": "2017-07-23T03:41:51+03:00", "group": "popa", "id": "5973f0cfead3c64a00000001", "modified": "2017-07-23T03:41:51+03:00" }, { "created": "2017-07-23T03:41:52+03:00", "group": "popa", "id": "5973f0d0ead3c64a00000002", "modified": "2017-07-23T03:41:52+03:00" } ], "zuul": [ { "created": "2017-07-23T03:41:57+03:00", "group": "zuul", "id": "5973f0d5ead3c64a00000003", "modified": "2017-07-23T03:41:57+03:00" } ] }
Get all records by group name:
$> http -v :8080/popa GET /popa HTTP/1.1 Accept: */* Accept-Encoding: gzip, deflate Connection: keep-alive Host: localhost:8080 User-Agent: HTTPie/0.9.9 HTTP/1.1 200 OK Content-Length: 253 content-type: application/json [ { "created": "2017-07-23T03:41:51+03:00", "group": "popa", "id": "5973f0cfead3c64a00000001", "modified": "2017-07-23T03:41:51+03:00" }, { "created": "2017-07-23T03:41:52+03:00", "group": "popa", "id": "5973f0d0ead3c64a00000002", "modified": "2017-07-23T03:41:52+03:00" } ]
Get record by its group and instanceId:
$> http -v :8080/popa/5973f0cfead3c64a00000001 GET /popa/5973f0cfead3c64a00000001 HTTP/1.1 Accept: */* Accept-Encoding: gzip, deflate Connection: keep-alive Host: localhost:8080 User-Agent: HTTPie/0.9.9 HTTP/1.1 200 OK Content-Length: 125 content-type: application/json { "created": "2017-07-23T03:41:51+03:00", "group": "popa", "id": "5973f0cfead3c64a00000001", "modified": "2017-07-23T03:41:51+03:00" }
This request, first of all, updates modified
time field of the updating record.
-
Request
PUT [/pathPrefix]/{group}/{instanceId}
pathPrefix
- common path prefix, which was set (default value - /) at the start of Luntic program;group
- name of the registered application group.instanceId
- service instance id for updating.Also, you could send a
JSON
object with a request. It will be attached to server generated instance inmeta
field. -
Success Response:
As a result of the request we get a response:
Code: 200
Content type: JSON
Content fields:
Field name Optional Description id no generated record id group no the group name you set created no the time you registered time modified no the time you last accessed the record meta yes if you send JSON
in request, we get it back in that field -
Error Response:
400 - if not
group
orinstanceId
are present;404 - in case of nonexistent
group
orinstanceId
. -
Examples:
Regular modified time update:
http -v PUT :8080/popa/5973f0cfead3c64a00000001 PUT /popa/5973f0cfead3c64a00000001 HTTP/1.1 Accept: */* Accept-Encoding: gzip, deflate Connection: keep-alive Content-Length: 0 Host: localhost:8080 User-Agent: HTTPie/0.9.9 HTTP/1.1 200 OK Content-Length: 125 content-type: application/json { "created": "2017-07-23T03:41:51+03:00", "group": "popa", "id": "5973f0cfead3c64a00000001", "modified": "2017-07-23T03:46:22+03:00" }
Update with attaching new meta field value:
http -v PUT :8080/popa/5973f0cfead3c64a00000001 name=Artem PUT /popa/5973f0cfead3c64a00000001 HTTP/1.1 Accept: application/json, */* Accept-Encoding: gzip, deflate Connection: keep-alive Content-Length: 17 Content-Type: application/json Host: localhost:8080 User-Agent: HTTPie/0.9.9 { "name": "Artem" } HTTP/1.1 200 OK Content-Length: 149 content-type: application/json { "created": "2017-07-23T03:41:51+03:00", "group": "popa", "id": "5973f0cfead3c64a00000001", "meta": { "name": "Artem" }, "modified": "2017-07-23T03:47:28+03:00" }
Deletes the service from the services.
-
Request
DELETE [/pathPrefix]/{group}/{instanceId}
pathPrefix
- common path prefix, which was set (default value - /) at the start of Luntic program;group
- name of the registered application group.instanceId
- service instance id for removing. -
Success Response:
As a result of the request we get a response:
Code: 204
No Content
-
Error Response:
400 - if not
group
orinstanceId
are present;404 - in case of nonexistent
group
orinstanceId
. -
Examples:
$> http -v DELETE :8080/popa/5972a20d5b31ed7400000001 DELETE /app/popa/5973e2c95d89804600000007 HTTP/1.1 Accept: */* Accept-Encoding: gzip, deflate Connection: keep-alive Content-Length: 0 Host: localhost:8080 HTTP/1.1 204 No Content Content-Length: 0
These instructions will get you a copy of the project up and running on your local machine for development and testing purposes.
For building the project you need only a Nim compiler (Windows or OSX/Unix).
IMPORTANT: Luntic requires Nim version 0.17.0
And, of course, you need to clone Luntic from GitHub:
$> git clone https://github.com/xxlabaza/luntic
$> cd luntic
For building routine automation, I am using config.nims file. It contains all needed tasks described in Nim language.
To build the Luntic project, do the following:
$> nim build
...
Hint: operation successful (52249 lines compiled; 4.124 sec total; 106.934MiB peakmem; Debug Build) [SuccessX]
If you need to build a release (less output binnary file size):
$> nim build release
To check, what everything is fine, type the nex command:
$> build/target/luntic -v
v1.1.0
To build a docker image:
$> nim docker
To run the project's test, do the following:
$> nim test
...
[OK] Parsing request parts
Also, if you build a release, the tests launch automatically.
- Nim - is a systems and applications programming language
To see what has changed in recent versions of Luntic, see the changelog file.
Please read contributing file for details on my code of conduct, and the process for submitting pull requests to me.
We use SemVer for versioning. For the versions available, see the tags on this repository.
- Artem Labazin - the main creator and developer
This project is licensed under the Apache License 2.0 License - see the license file for details