Oracle Application Container Cloud service allows for microservices to be hosted using programming languages like Java SE, NodeJS, PHP and Python. Future plans include support for Ruby. An earlier post provided an overview and compared this to Oracle Container Cloud Service.
The applications run in a Docker container behind the scenes and is expected to expose a service port. By configuring metadata, access can be setup to other Oracle Cloud services like database, storage, messaging etc.
Use Case
In this blog, we’ll build a weather microservice that will publish weather results for Melbourne. The weather data is retrieved from openweathermap and presented to the consumer as JSON. As the free plan allows for a maximum 2000 requests per day, this microservice will cache the result in memory and schedule weather updates every 2 hours.
Programming language
NodeJS has increased in popularity in the last few years as a server side development language to work alongside AngluarJS and other UI Javascript frameworks. Being a cloud first language with custom extensions available as packages, it’s a natural choice for cloud integration.
First, install node v6 (to match the Oracle version) and ensure that the installed folder has been added to the PATH environment variable. While any text editor may be used to write code, Atom with script package installed was used so that code can be run directly in the editor.
Code
Normally, the folder containing the NodeJS code is initialised using npm init. The resulting package.json will store the name of the package, version and dependencies for its execution.
The dependencies are initialised using npm install with the --save flag updating the package.json to store the dependenciesnpm install --save express memory-cache node-schedule request
You can use the following package.json in a new workspace/folder
{
"name": "server.js",
"version": "1.0.0",
"main": "server.js",
"dependencies": {
"express": "^4.14.0",
"memory-cache": "^0.1.6",
"node-schedule": "^1.2.0",
"request": "^2.79.0"
}
}
And initialise environment with required packages by running the following commandnpm install
You’ll notice a folder called node_modules that stores the dependent packages (and their dependencies)
Next obtain an api key from openweathermap by registering for their free plan (this allows 2000 requests a day).
Create server.js and declare the packages to use and store the API KEY
var schedule = require('node-schedule');
var request = require('request');
var express = require('express');
var cache = require('memory-cache');
var API_KEY = 'd528f800XXXXXXXXXXXXXXX';
Add a function to retrieve weather and store result in a cache with key ‘melbourne’
function get_melbourne_weather() {
console.log('about to invoke weather service');
// openweathermap api url to hit
var url = 'http://api.openweathermap.org/data/2.5/weather?q=Melbourne,AU&units=metric&APPID='+API_KEY;
// invoke openweathermap api
request.post(
url,
function (error, response, body) {
// post invoke processing with response body
if (!error && response.statusCode == 200) {
// successfully processed
console.log('invoked weather service at '+ new Date());
// store weather json object
cache.put('melbourne', body);
}
// if error occurred, record HTTP status code
console.log('response status: '+ response.statusCode);
});
}
Add a recurring schedule to invoke get_melbourne_weather every 2 hours and another once-off job to populate cache on startup.
// run schedule every 2 hours, as api is refreshed every 2 hours
schedule.scheduleJob('0 0 */2 * * *', get_melbourne_weather);
// schedule once on startup in case api is invoked in first 2 hours
schedule.scheduleJob(new Date(Date.now() + 500), get_melbourne_weather);
Frequency may be increased to 5 seconds for testing, in which case the job will be as follows:
// run schedule every 5 secs – FOR TESTING ONLY
schedule.scheduleJob('*/5 * * * * *', get_melbourne_weather);
Next, we’ll configure Express application. This is a package that makes API development easier by providing convenience methods for common functions. process.env.PORT is an environment variable used by Oracle Application Container Cloud to set the runtime port, which can be overridden for local testing.
// initialize express application
var app = express();
var LISTEN_PORT = process.env.PORT || 48484;
// start listening on port
app.listen(LISTEN_PORT);
First define the root to document the service
// root to document micro service
app.get(
'/',
function(req, res) {
res.send('Melbourne Weather API: <p>GET /weather/melbourne to get the current weather');
}
);
Configure the main API endpoint as a GET HTTP method.
// get current weather for melbourne from cache.
app.get(
'/weather/melbourne',
function(req, res) {
if(cache.size() != 1) return res.sendStatus(503);// service unavailable temporarily, try again later
res.set('Content-Type', 'application/json');
res.send(cache.get('melbourne'));
}
);
The entire file server.js will be as follows:
var schedule = require('node-schedule');
var request = require('request');
var express = require('express');
var cache = require('memory-cache');
var API_KEY = 'd528f800XXXXXXXXXXXXXXX';
function get_melbourne_weather() {
console.log('about to invoke weather service');
// openweathermap api url to hit
var url = 'http://api.openweathermap.org/data/2.5/weather?q=Melbourne,AU&units=metric&APPID='+API_KEY;
// invoke openweathermap api
request.post(
url,
function (error, response, body) {
// post invoke processing with response body
if (!error && response.statusCode == 200) {
// successfully processed
console.log('invoked weather service at '+ new Date());
// store weather json object
cache.put('melbourne', body);
}
// if error occurred, record HTTP status code
console.log('response status: '+ response.statusCode);
});
}
// run schedule every 2 hours, as api is refreshed every 2 hours
schedule.scheduleJob('0 0 */2 * * *', get_melbourne_weather);
// run schedule every 5 secs – FOR TESTING ONLY
//schedule.scheduleJob('*/5 * * * * *', get_melbourne_weather);
// schedule once on startup in case api is invoked in first 2 hours
schedule.scheduleJob(new Date(Date.now() + 500), get_melbourne_weather);
// initialize express application
var app = express();
var LISTEN_PORT = process.env.PORT || 48484;
// start listening on port
app.listen(LISTEN_PORT);
// root to document micro service
app.get(
'/',
function(req, res) {
res.send('Melbourne Weather API: <p>GET /weather/melbourne to get the current weather');
}
);
// get current weather for melbourne from cache.
app.get(
'/weather/melbourne',
function(req, res) {
if(cache.size() != 1) return res.sendStatus(503);// service unavailable temporarily, try again later
res.set('Content-Type', 'application/json');
res.send(cache.get('melbourne'));
}
);
Run the code by issuing the following command:node server.js
Test
You can hit the following urls in the browser to see the responses
http://localhost:48484/
http://localhost:48484/weather/melbourne
Test via Postman
Deploy to Application Container Cloud
To deploy to Application Container Cloud Service, a metadata file needs to be placed within the zip package to instruct which file to run, what runtime to use etc.
{
"runtime":{
"majorVersion":"6"
},
"command": "node server.js"
}
Package the folder contents into a zip:
- server.js – main api code
- package.json – NodeJS metadata file containing application name, version and dependencies
- metadata.json – Oracle ACCS metadata file that contains runtime version and file to run
- node_modules – folder containing dependent packages listed in package.json
Go to Oracle Application Container Cloud Service (ACCS) to deploy the NodeJS zip
https://apaas.us.oraclecloud.com/apaas/faces/aPaaSRunner.jspx
Fill in application details and upload the zip. Leave the instances at 1 and memory at minimum value 1GB. Ensure runtime version is updated to 6.3
Navigate to weather-api > Overview > In-Progress Activity section to view progress
After application is created, go to Administraton > Logs and click Get Log
Refresh screen to view zip. Click on zip and enter the ACCS credentials for Cloud Storage Credentials
To get the current weather, hit the url
https://weather-api-identitydomain.apaas.us2.oraclecloud.com/weather/melbourne
where identitydomain is the name of identity domain in your cloud subscription.
You can test the API via Postman
Lifecycle actions can be performed from Applications page
Deployment and lifecycle actions can be automated using ACCS REST API. See docs for further details.
These files are also available in github - https://github.com/rubiconred/weather-api
Using cloud-native languages like NodeJS on the Oracle Cloud is a powerful way to develop microservices. API management tools like Oracle API Platform Cloud allow these services to be governed and secured for external consumption.