Skip to content

Commit

Permalink
gh-2: adds load balancing example
Browse files Browse the repository at this point in the history
  • Loading branch information
pmhsfelix committed Oct 22, 2022
1 parent 2e77425 commit 64f7ebd
Show file tree
Hide file tree
Showing 14 changed files with 210 additions and 9 deletions.
12 changes: 11 additions & 1 deletion code/tic-tac-tow-service/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,4 +21,14 @@ psql -U dbuser -d db
* `\h` - show help.
* `\d <table>` - show table.
* `select ... ;` - execute query.
* `\q` - quit `psql`.
* `\q` - quit `psql`.

* Start the full composition
```
./gradlew composeUp
```

* Loop curl
```
while true; do curl -w "\n" http://localhost:8080/status/hostname; done
```
15 changes: 14 additions & 1 deletion code/tic-tac-tow-service/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,14 @@ tasks.withType<Test> {
useJUnitPlatform()
}

task<Copy>("extractUberJar") {
dependsOn("assemble")
// opens the JAR containing everything...
from(zipTree("$buildDir/libs/${rootProject.name}-$version.jar"))
// ... into the 'build/dependency' folder
into("build/dependency")
}

task<Exec>("dbTestsUp") {
commandLine("docker-compose", "up", "-d", "--build", "--force-recreate", "db-tests")
}
Expand All @@ -72,6 +80,11 @@ task<Exec>("dbTestsDown") {
commandLine("docker-compose", "down")
}

task<Exec>("composeUp") {
commandLine("docker-compose", "up", "--build", "--force-recreate")
dependsOn("extractUberJar")
}

// from https://pinterest.github.io/ktlint/install/integrations/#custom-gradle-integration-with-kotlin-dsl
val outputDir = "${project.buildDir}/reports/ktlint/"
val inputFiles = project.fileTree(mapOf("dir" to "src", "include" to "**/*.kt"))
Expand All @@ -91,4 +104,4 @@ tasks.named("check") {
dependsOn(ktlintCheck)
dependsOn("dbTestsWait")
finalizedBy("dbTestsDown")
}
}
62 changes: 61 additions & 1 deletion code/tic-tac-tow-service/docker-compose.yml
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
version: "3.3"
services:
# The service running the DB
db-tests:
container_name: db-tests
hostname: db-tests
build:
context: .
dockerfile: ./tests/Dockerfile-db-test
Expand All @@ -10,4 +12,62 @@ services:
- POSTGRES_PASSWORD=changeit
- POSTGRES_DB=db
ports:
- 5432:5432
- 5432:5432

# spring-service-1 and spring-service-2 are used to illustrate scenarios with a fixed number of servers
# with static and well know names.
spring-service-1:
container_name: spring-service-1
hostname: spring-service-1
build:
context: .
dockerfile: ./tests/Dockerfile-spring
environment:
PORT: 8081
POSTGRES_URI: "jdbc:postgresql://db-tests:5432/db?user=dbuser&password=changeit"
ports:
- 8081:8081

spring-service-2:
container_name: spring-service-2
hostname: spring-service-2
build:
context: .
dockerfile: ./tests/Dockerfile-spring
environment:
PORT: 8082
POSTGRES_URI: "jdbc:postgresql://db-tests:5432/db?user=dbuser&password=changeit"
ports:
- 8082:8082

# spring-service is used to illustrate scenario with a dynamic number of servers
# without static and well know names.
# We will use docker compose scaling to create multiple instances of this service
spring-service:
build:
context: .
dockerfile: ./tests/Dockerfile-spring
environment:
PORT: 8080
POSTGRES_URI: "jdbc:postgresql://db-tests:5432/db?user=dbuser&password=changeit"

# The service running the load-balancer
nginx:
container_name: nginx
image: nginx
ports:
- 8080:8080
- 8088:8088
volumes:
- ./tests/nginx:/etc/nginx
depends_on:
- spring-service-1
- spring-service-2

# Just a machine running ubuntu, with 'dig' installed so that we can observe the docker compose environment.
ubuntu:
container_name: ubuntu
build:
context: .
dockerfile: ./tests/Dockerfile-ubuntu
tty: true
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
package pt.isel.daw.tictactow

import org.jdbi.v3.core.Jdbi
import org.postgresql.PGProperty
import org.postgresql.ds.PGSimpleDataSource
import org.slf4j.Logger
import org.slf4j.LoggerFactory
import org.springframework.boot.autoconfigure.SpringBootApplication
import org.springframework.boot.runApplication
import org.springframework.context.annotation.Bean
Expand All @@ -19,11 +22,24 @@ import java.time.Instant
@SpringBootApplication
class TicTacTowApplication {
@Bean
fun jdbi() = Jdbi.create(
PGSimpleDataSource().apply {
setURL("jdbc:postgresql://localhost:5432/db?user=dbuser&password=changeit")
fun jdbi(): Jdbi {
val postgresUri =
System.getenv("POSTGRES_URI")
?: "jdbc:postgresql://localhost:5432/db?user=dbuser&password=changeit"

val dataSource = PGSimpleDataSource().apply {
setURL(postgresUri)
}
).configure()

// Be careful not to disclose the credentials.
logger.info(
"Using PostgreSQL located at '{}:{}'",
dataSource.getProperty(PGProperty.PG_HOST),
dataSource.getProperty(PGProperty.PG_PORT),
)

return Jdbi.create(dataSource).configure()
}

@Bean
fun passwordEncoder() = BCryptPasswordEncoder()
Expand All @@ -35,6 +51,10 @@ class TicTacTowApplication {
fun clock() = object : Clock {
override fun now() = Instant.now()
}

companion object {
private val logger: Logger = LoggerFactory.getLogger(TicTacTowApplication::class.java)
}
}

// QUESTION: why cannot this be in TicTacTowApplication
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,11 @@ import pt.isel.daw.tictactow.infra.siren
class HomeController {

@GetMapping(Uris.HOME)
fun getHome() = siren(HomeOutputModel("Made for teaching purposes by P. Félix")) {
fun getHome() = siren(
HomeOutputModel(
credits = "Made for teaching purposes by P. Félix",
)
) {
link(Uris.home(), Rels.SELF)
link(Uris.home(), Rels.HOME)
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package pt.isel.daw.tictactow.http

import org.springframework.web.bind.annotation.GetMapping
import org.springframework.web.bind.annotation.RestController
import pt.isel.daw.tictactow.http.model.StatusOutputModel
import pt.isel.daw.tictactow.infra.siren
import pt.isel.daw.tictactow.repository.TransactionManager
import java.net.InetAddress

@RestController
class StatusController(
val transactionManager: TransactionManager
) {

@GetMapping(Uris.STATUS)
fun getStatus() = transactionManager.run { transaction ->
siren(
StatusOutputModel(
hostname = System.getenv("HOSTNAME"),
gamesCount = transaction.gamesRepository.count()
)
) {
// For now, nothing more to add.
}
}

@GetMapping(Uris.STATUS_HOSTNAME)
fun getStatusHostname(): String = System.getenv("HOSTNAME")

@GetMapping(Uris.STATUS_IP)
fun getStatusIp(): String = InetAddress.getLocalHost().hostAddress
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@ import java.net.URI
object Uris {

const val HOME = "/"
const val STATUS = "/status"
const val STATUS_HOSTNAME = "/status/hostname"
const val STATUS_IP = "/status/ip"
const val GAME_BY_ID = "/games/{gid}"

fun home(): URI = URI(HOME)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package pt.isel.daw.tictactow.http.model

data class StatusOutputModel(
val hostname: String,
val gamesCount: Int,
)
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,5 @@ interface GamesRepository {
fun insert(game: Game)
fun getById(id: UUID): Game?
fun update(game: Game)
fun count(): Int
}
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,11 @@ class JdbiGamesRepository(
.execute()
}

override fun count(): Int =
handle.createQuery("select count(*) as count from dbo.Games")
.mapTo<Int>()
.single()

companion object {
private fun Update.bindBoard(name: String, board: Board) = run {
bind(
Expand Down
Original file line number Diff line number Diff line change
@@ -1 +1 @@

server.port=${port:8080}
9 changes: 9 additions & 0 deletions code/tic-tac-tow-service/tests/Dockerfile-spring
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
FROM openjdk:17
ARG DEPENDENCY=build/dependency
# first layer with the external libs (i.e. the files that change the least).
COPY ${DEPENDENCY}/BOOT-INF/lib /app/lib
# second layer with the 'META-INF' contents.
COPY ${DEPENDENCY}/META-INF /app/META-INF
# last layer with the application JARs (i.e. the files that change the most).
COPY ${DEPENDENCY}/BOOT-INF/classes /app
ENTRYPOINT ["java","-cp","app:app/lib/*","pt.isel.daw.tictactow.TicTacTowApplicationKt"]
3 changes: 3 additions & 0 deletions code/tic-tac-tow-service/tests/Dockerfile-ubuntu
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
FROM ubuntu
RUN apt -y update
RUN apt -y install dnsutils
35 changes: 35 additions & 0 deletions code/tic-tac-tow-service/tests/nginx/nginx.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
events {
worker_connections 1024;
}

http {

upstream static-spring-service {
server spring-service-1:8081 max_fails=3 fail_timeout=10s;
server spring-service-2:8082 max_fails=3 fail_timeout=10s;
}

upstream dynamic-spring-service {
server spring-service:8080 max_fails=3 fail_timeout=10s;
}

server {
listen 8080;

location / {
proxy_pass http://static-spring-service;
proxy_connect_timeout 5s;
proxy_next_upstream error timeout http_500;
}
}

server {
listen 8088;

location / {
proxy_pass http://dynamic-spring-service;
proxy_connect_timeout 5s;
proxy_next_upstream error timeout http_500;
}
}
}

0 comments on commit 64f7ebd

Please sign in to comment.