A Shiny-app Serves as Shiny-server Load Balancer

April 30, 2014

(This article was first published on Category: R | Huidong Tian's Blog, and kindly contributed to R-bloggers)

The Shiny-app on open-source edition Shiny-server has only one concurrent, which means it can run only for one user at a time point. But it can host multiple Shiny-apps, which can run synchronously. So, if we create severl Shiny-apps with different names but same function, then we can let more users use our service at same time. But users don’t how to choose the Shiny-app with small user number. This post will show you how to create a Shiny-app to redirect user to the Shiny-app with lower load.

I have no knowledge about server load balancer, the following method is ONLY what I thought it can be.

  • First, we need to know the load information about Shiny apps on our server, like which apps are running, how many users for each app, etc.

  • Then, create a normal Shiny app to detect which app has little user number than others, and using JavaScript to redirect user to that app.

  • CPU information about Shiny-app

    The following is the R code than generates a data frame containing which Shiny-app are running and the user number of each Shiny-app.

“` ruby
## Setup work directory;
I <- 0
for (i in 1:60) {
system(“top -n 1 -b -u shiny > top.log”)
dat <- readLines(“top.log”)
id <- grep(“R $”, dat)
Names <- strsplit(gsub(“^ +|%|\+”, “”, dat[7]), “ +”)[[1]]
if (length(id) > 0) {
# ‘top’ data frame;
L <- strsplit(gsub(“^ *”, “”, dat[id]), “ +”)
dat <- data.frame(matrix(unlist(L), ncol = 12, byrow = T))
names(dat) <- Names
dat <- data.frame(Time = Sys.time(), dat[, -ncol(dat)], usr = NA, app = NA)
dat$CPU <-as.numeric(as.character(dat$CPU))
dat$MEM <-as.numeric(as.character(dat$MEM))
# Check if connection number changed;
for (i in 1:length(dat$PID)) {
PID <- dat$PID[i]
system(paste(“sudo netstat -p | grep”, PID, “> netstat.log”))
system(paste(“sudo netstat -p | grep”, PID, “» netstat.log2”))
system(paste(“sudo lsof -p”, PID, “| grep /srv > lsof.log”))
netstat <- readLines(“netstat.log”)
lsof <- readLines(“lsof.log”)
dat$usr[i] <- length(grep(“ESTABLISHED”, netstat) & grep(“tcp”, netstat))
dat$app[i] <- regmatches(lsof, regexec(“srv/(.
)”, lsof))[[1]][2]
dat <- dat[, c(“app”, “usr”)]
} else {
dat <- data.frame(app = “app”, usr = 0)
write.table(dat, file = “CPU.txt”)


To make it run automatically, schedule it under /etc/crontab like the following:

“` ruby

          • root Rscript //CPU.R


  • Create the Shiny-app for redirecting.


tags$style("#link {visibility: hidden;}"), # This app doesn't need user interface;
textInput(inputId = "link", label = "", value = ""), # Redirecting link;
tags$script(type="text/javascript", src = "redirect.js") # JavaScript for redirecting;


“` ruby
shinyServer(function(input, output, session) {
CPU <- read.table(“Data/CPU.txt”)
App <- data.frame(app = c(“app1”, “app2”, “app3”, “app4”))
App <- merge(App, CPU, all.x = TRUE)
App$usr[which(is.na(App$usr))] <- 0
Link <- paste(“”, App$app[which.min(App$usr)], sep = “”)
updateTextInput(session, inputId = “link”, value = Link)



“` ruby
setInterval(function() {
var link = document.getElementById(‘link’).value;
if (link.length >1) {
window.open(link, “_top”)
}, 50)


To leave a comment for the author, please follow the link and comment on their blog: Category: R | Huidong Tian's Blog.

R-bloggers.com offers daily e-mail updates about R news and tutorials on topics such as: Data science, Big Data, R jobs, visualization (ggplot2, Boxplots, maps, animation), programming (RStudio, Sweave, LaTeX, SQL, Eclipse, git, hadoop, Web Scraping) statistics (regression, PCA, time series, trading) and more...

If you got this far, why not subscribe for updates from the site? Choose your flavor: e-mail, twitter, RSS, or facebook...

Comments are closed.

Search R-bloggers


Never miss an update!
Subscribe to R-bloggers to receive
e-mails with the latest R posts.
(You will not see this message again.)

Click here to close (This popup will not appear again)