IP2Country is a Go-based application that provides an API to determine the country and city based on an IP address. The project includes a rate limiter, caching, and integration with disk-based and MongoDB repositories.
- HTTP Server: Provides an API to get country and city information based on IP.
- Rate Limiter: Limits the number of requests (globally and per client IP) to prevent abuse (Using Token bucket algorithm).
- Local mode: Keeps an internal mapping of client IPs and their request counts.
- Distributed mode: Uses Redis to store client IPs and their request counts.
- Caching: Caches responses to improve performance.
- Repositories:
- Disk Repository: Stores IP to country/city mappings on disk.
- MongoDB Repository: Stores IP to country/city mappings in a MongoDB database.
- Testing: Includes unit and integration tests.
- Go 1.22+
- Docker (for building and running the application in a container)
- Make (for running build and test commands)
- Kind (for running the application in a local Kubernetes cluster)
-
Clone the repository:
git clone https://github.com/ransoor2/ip2country.git cd ip2country
-
Install dependencies:
go mod tidy go mod download
-
Install
swag
for generating Swagger documentation:make install
Configuration is managed via a YAML file and environment variables. The default configuration file is located at config/config.yml
.
- App:
Name
: The name of the application.Version
: The version of the application.
- HTTP:
Port
: The port on which the HTTP server will run.
- Log:
Level
: The logging level (e.g., debug, info, warn, error).
- Cache:
Size
: The size of the cache.
- Repository:
Type
: The type of repository to use (disk/mongo).
- DiskRepository:
RelativePath
: The relative path to the disk-based repository file.
- MongoRepository:
URI
: The URI for connecting to the MongoDB database.DB
: The name of the MongoDB database.Collection
: The name of the MongoDB collection.
- RateLimiter:
Type
: The type of rate limiter to use (local/distributed).MaxRequests
: The maximum number of requests allowed.UserRequests
: The number of allowed requests per IP.Interval
: The interval for rate limiting (buckets refill).BucketTTL
: The time-to-live for rate limiter buckets (local mode only).CleanInterval
: The interval for cleaning up expired rate limiter buckets (local mode only).RedisAddr
: The address of the Redis server (required for distributed rate limiter).
To run the application locally:
-
Generate Swagger documentation:
make swag-v1
-
Run the application:
make run
To run the tests:
make test
To build a Docker image:
make docker-build
To run the application:
make docker-run
Once the docker is running you can access the API at http://localhost:8080/swagger/index.html#
.
Curl command to get country and city by IP:
curl "http://localhost:8080/v1/find-country?ip=8.8.8.8"
To run a distributed rate limiter with Redis change the RateLimiter.Type
to distributed
and provide the RateLimiter.RedisAddr
in the config/config.yml
file.
docker run -d --name redis-stack -p 6379:6379 -p 8001:8001 redis/redis-stack:latest
Redis Insight UI is available at: localhost:8001
Note - in order to run with both Redis and Ip2Country in docker, you need to set up a docker network or use docker bridge. To use docker bridge
docker inspect -f '{{range.NetworkSettings.Networks}}{{.IPAddress}}{{end}}' redis-stack
docker run -e RATE_LIMITER_REDIS_ADDR=172.17.0.2:6379 -p 8080:8080 ip2country:latest
To create a Kubernetes cluster and install the application:
make kind-install
This will create:
- A Kubernetes cluster using KinD with 2 worker nodes and 1 control plane node.
- A deployment with 3 replicas of the application.
- A configmap to provide the configuration to the application + IP2Country database.
- A service to expose the application.
- A Redis deployment and service for the distributed rate limiter.
Once the application is running in the Kubernetes cluster:
curl "http://localhost:30000/v1/find-country?ip=3.3.3.3"
This request will be load balanced across the three replicas of the application. You can inspect the rate limiter behavior according to the mode set - local or distributed.
To delete the Kubernetes cluster:
make kind-delete
Note: Configuration and database data can be provided via the configmap.yaml
file.
-
GET /v1/find-country: Get country and city by IP.
- Query Parameters:
ip
: The IP address to lookup.
- Responses:
200 OK
: Returns the country and city.400 Bad Request
: Invalid IP address.404 Not Found
: IP address not found.429 Too Many Requests
: Rate limit exceeded.
- Query Parameters:
-
GET /healthz: Health check endpoint.
-
GET /metrics: Prometheus metrics endpoint.
To support another kind of database, the following interface needs to be implemented:
type Repository interface {
CountryNCityByIP(context.Context, string) (string, string, error)
}
Then add the implementation in the repository
package, the appropriate config in the config/config.yml
file, and update the initializeRepository
function in the app.go
.
To support another kind of rate limiting, the following interface needs to be implemented:
type RateLimiter interface {
Allow(ctx context.Context, clientIP string) bool
}
Then add the implementation in the ratelimiter
package, the appropriate config in the config/config.yml
file, and update the getRateLimiter
function in app.go
.
-
Observability:
- Add more logs, metrics and traces.
-
Integration Tests:
- Create integration tests for Redis.
- Create integration tests for MongoDB.
-
HTTP Server:
- Modify the HTTP server to support an in-memory listener for testing purposes. This will allow for more efficient and isolated tests without the need for network dependencies.
-
Versioning:
- Add versioning to CICD.
-
Domain Entities:
- Add domain entities for country and city.
-
API:
- Include a
Retry-After
header in the response when the rate limit is exceeded.
- Include a