Having gained an understanding of what Bluemix is and what it offers from the previous chapters, and now having gained an understanding of the microservices pattern, you will be able to get a perspective on why Bluemix is a platform that is suitable for microservices-based application development. Let us immediately get into discussing an application use case that we will illustrate in this section, which will also help to bring forth the concept of microservices and help with your understanding of this.
In this section, we will build an application leveraging concepts and applications that we have developed and learned in previous chapters. We will be using the application we developed in
Chapter 4
, Leveraging On-Premise Software for Applications on Bluemix in which we retrieved a certain Twitter feed, translated it into French, did some sentiment analysis on it, and then persisted it in a MongoDB database that existed locally on your workstation. This will form one application, or microservice, for us. Next, we will build another application, which will retrieve persisted negative tweets from MongoDB (which is updated by our first microservice). We will augment the tweets retrieved by first translating them back to English, and then we will use the cognitive function of tone analysis to find the social tone scores for each of the retrieved tweets. We will then create a formatted output of the tweets with their social tone scores, which will be obtained by a user sending an HTTP request. At the same time, we will have this application call out to an OpenWhisk
trigger, which we created in
Chapter 7
, Compute Options on Bluemix. This application will be our microservice 2, and the OpenWhisk
trigger, which will execute a configured action, will be our microservice 3. All of these microservices together will form our microservices based application. As we can see, each of the three microservices in our example are independent deployable units, can be scaled individually, can run without dependencies on each other, and each of them runs on its own infrastructure units.
Let us now go step by step and build our microservices-based application.
The first microservice for this illustration is the application you built in Chapter 4 , Leveraging On-Premise Software for Applications on Bluemix. We will not be going over the steps for building this application again in this section. If you followed Chapter 4 , Leveraging On-Premise Software for Applications on Bluemix, before reaching here, then you will already have this application deployed in your Bluemix account. If not, please go back to Chapter 4 , Leveraging On-Premise Software for Applications on Bluemix, follow the steps to create the application that retrieves tweets from the configured Twitter stream, analyzes the tweet sentiment, separates out the negative-sentiment tweets, and persists them in MongoDB, which is configured on a local system.
Let us now proceed to the next section, assuming you have this step completed and microservice 1 is available to you.
Before going into the development of microservice 2, let us make sure we have microservice 3 available. If you followed
Chapter 7
, Compute Options on Bluemix, before reaching here, you will already have created the OpenWhisk Cloudant
trigger, any action that needs to occur when the trigger occurs, and finally, the rule that associates the trigger and the action.
In the action, we will make a small modification for this example, since the Cloudant
trigger that will invoke this action will not be returning an _id
, which we used to print in the action when we executed it in the example in
Chapter 7
, Compute Options on Bluemix. We will modify the action to just print a string to indicate that the trigger, called from our microservice 2, has executed the action code.
The action code from Chapter 7 , Compute Options on Bluemix, will be as shown in the following screenshot:
We will be modifying it to look like the following screenshot. Click Make it Live once you have edited the action code:
Let us go into the next part of this illustration, which is to create microservice 2.
Please follow the steps in this section only after you have completed the sections on microservice 1 and microservice 3.
Let us now create a new application that will act as microservice 2 in our illustration of a microservices-based application. Follow the steps given here to create the application:
Let us learn to wire and configure the flow:
Query MongoDB
, as this is the node where we will be defining some of the parameters that are necessary to build the query to retrieve stored records from MongoDB. As seen in the following screenshot, we are restricting the number of records returned by the query by defining msg.limit
. Here, we have limited it to 30
records. Next, we want the query to fetch the last few records each time it is run, so we will ensure this happens by defining msg.sort
. We can also choose to just get the payload field from the query and can ignore the other fields by defining msg.projection
, as shown in the following screenshot:
[ { "payload": "Ce clown a manifestementjamaisentenduparler del' expression "vide clang plus les navires" https://t.co/YTM6brhVSQ" }, { "payload": "@revieweroPeut-êtreelleestmelo-dramatizing pour son incapacité à effectueren raison de problèmesd'aptitude sur ce jour @narendramodi" }, { "payload": "RT @PMOIndia: attristépar la perte de vies due à un séismeenItalie. Condoléances aux famillesendeuillées&prières avec les blessés: PM @nar ..." }, { "payload": "@14Mohjas RT: JungleRaj Au Gujarat @narendramodisivous ne pouvez pas contrôler les troubles sociauxdanstoutel'Indevousdevezvolunteerlydémissionner." }, { "payload": "@daveakash2 RT: aucune accusation de sédition sur @narendramodi n @manoharparrikar??? #NavyInfoLeaksParrikarSleeps" }, { "payload": "@HemantNoida2015 RT: @yadavakhileshacheteursjaha Sir ye #kaisaPradeshUttar Pradesh# korahehaiaurpilleraucune action de côtéur. Https:/ ..." }, { "payload": "@narendramodi l ....]
The code entered in the preceding function node is provided as follows:
varstr=""; for (var j = 1; j <msg.payload.length; j++){ str+=msg.payload[j].payload+"~"; } msg.payload=str; return msg;
"~"
(which was set in the code), as shown in the following screenshot of the debug console:
msg.payload
, in the flow:
Now we will make use of value-added services from Bluemix, which in their own capacity can be considered as additional microservices that would make up this application. For the sake of this discussion, we will just call them Bluemix services. So, in this application, we want to convert the tweet text back to English, so we will use the language translation node, such as the one we used in Chapter 4 , Leveraging On-Premise Software for Applications on Bluemix, for our microservice 1 application. Here, the assumption is that you already have the Watson Language Translation service added to your Bluemix account, which you would have done in Chapter 4 , Leveraging On-Premise Software for Applications on Bluemix.
Next let us update the application flow to augment the results we would want to return to the user. We will use the Watson category service on Bluemix, Tone Analyzer, to analyze the tone of each of the tweet retrieved, and we will then display the tweet, along with the social tone score, for each of them.
Follow the steps given to first add the Tone Analyzer service instance to your Bluemix account so that you get the credentials required to configure the service in your application flow:
msg.response
, as shown in the following screenshot:
The code entered in the function node is as follows:
var payload = msg.payload; varstr = msg.response.document_tone.tone_categories[0].tones; varfinalstr = ""; for (vari = 0; i < str.length; i++) { finalstr += str[i].tone_name + " : " + str[i].score + ", "; } finalstr = finalstr.substring(0, finalstr.lastIndexOf(",")); msg.payload = "<tr><td width='60%' style='background-color: #AECD10;> <p><span style='color: '#fff;' font-weight: bold; display: inline-block; padding: 3px 10px; border-radius: 5px;'> <blockquote><em>" + payload + "</em></blockquote></span></p></td> <td width='40%' style='background-color: #CC9FC5;'><p> <span style='color: #000;' display: inline-block; padding: 3px 10px; font-weight: bold; border-radius: 5px;'> <em>" + finalstr + "</em></span></p></tr>"; return msg;
msg.payload
into one msg.payload
:
<style type="text/css"> .tg {border-collapse:collapse;border-spacing:0; border-color:#aaa;margin:0px auto;} .tg td{font-family:Arial, sans-serif;font-size:14px; padding:10px 5px;border-style:solid; border-width:1px;overflow:hidden;word-break:normal; border-color:#aaa;color:#333;background-color:#fff;} .tgth{font-family:Arial, sans-serif;font-size:14px; font-weight:normal; padding:10px 5px;border-style:solid;border-width:1px; overflow:hidden;word-break:normal;border-color:#aaa; color:#fff;background-color:#f38630;} .tg .tg-huo9{font-size:15px;font-family:"Lucida Console", Monaco, monospace !important;;vertical-align:top} .tg .tg-fz6y{font-size:14px;font-family:"Lucida Console", Monaco, monospace !important;;vertical-align:top} .tg .tg-yw4l{vertical-align:top} th.tg-sort-header::-moz-selection { background:transparent; } th.tg-sort-header::selection { background:transparent; } th.tg-sort-header { cursor:pointer; }table th.tg-sort-header:after { content:''; float:right; margin-top:7px; border-width:0 4px 4px; border-style:solid; border-color:#404040 transparent; visibility:hidden; } table th.tg-sort-header:hover:after { visibility:visible; } table th.tg-sort-desc:after,tableth.tg-sort-asc:after, tableth.tg-sort-asc:hover:after { visibility:visible; opacity:0.4; } table th.tg-sort-desc:after { border-bottom:none; border-width:4px 4px 0;} @media screen and (max-width: 767px) {.tg {width: auto !important;} .tg col {width: auto !important;}.tg-wrap {overflow-x: auto; -webkit-overflow-scrolling: touch;margin: auto 0px;}} </style> <div class="tg-wrap"> <h2 style="color: #2e6c80;">Tweets analyzed for their social tones:</h2> <table> </thead> <table id="tg-FUkU8" class="tg"> <thead> <tr> <td style='background-color: #33314B;'> <center> <h2><b><em><font color='#fff';>Tweets</font> </em></b></h2> </center> </td> <td style='background-color: #33314B;'> <center> <h2><b><em><font color='#fff';>Social Tones</font> </em></b></h2> </center> </td> </tr> </thead> {{{payload}}} </table> </div> <script type="text/javascript" charset="utf-8"> varTgTableSort=window.TgTableSort||function(n,t) {"use strict";function r(n,t){for(var e=[],o=n.childNodes,i=0; i<o.length;++i) {var u=o[i];if("."==t.substring(0,1)){var a=t.substring(1); f(u,a)&&e.push(u)} else u.nodeName.toLowerCase()==t&&e.push(u);var c=r(u,t);e=e.concat(c)} return e}function e(n,t) {var e=[],o=r(n,"tr");return o.forEach(function(n) {var o=r(n,"td");t>=0&&t<o.length&&e.push(o[t])}),e}function o(n) {return n.textContent||n.innerText||""}function i(n) {return n.innerHTML||""}function u(n,t){var r=e(n,t);return r.map(o)} function a(n,t){var r=e(n,t);return r.map(i)} function c(n){var t=n.className||"";return t.match(/S+/g)||[]} function f(n,t){return-1!=c(n).indexOf(t)}function s(n,t) {f(n,t)||(n.className+=" "+t)} function d(n,t){if(f(n,t)){var r=c(n),e=r.indexOf(t); r.splice(e,1),n.className=r.join(" ")}} function v(n){d(n,L),d(n,E)}function l(n,t,e) {r(n,"."+E).map(v),r(n,"."+L).map(v),e==T?s(t,E):s(t,L)} function g(n) return function(t,r) {var e=n*t.str.localeCompare(r.str); return 0==e&&(e=t.index-r.index),e}} function h(n){return function(t,r) {var e=+t.str,o=+r.str;return e==o?t.index-r.index:n*(e-o)}} function m(n,t,r){var e=u(n,t),o=e.map(function(n,t) {return{str:n,index:t}}),i=e&&-1==e.map(isNaN).indexOf(!0), a=i?h(r):g(r); return o.sort(a),o.map(function(n){return n.index})} function p(n,t,r,o) {for(vari=f(o,E)?N:T,u=m(n,r,i),c=0;t>c;++c) {var s=e(n,c),d=a(n,c); s.forEach(function(n,t){n.innerHTML=d[u[t]]})}l(n,o,i)} function x(n,t){var r=t.length;t.forEach(function(t,e) {t.addEventListener("click",function(){p(n,r,e,t)}), s(t,"tg-sort-header")})}var T=1,N=-1,E="tg-sort-asc", L="tg-sort-desc";return function(t){var e=n.getElementById(t), o=r(e,"tr"),i=o.length>0?r(o[0],"td"):[]; 0==i.length&&(i=r(o[0],"th")); for(var u=1;u<o.length;++u){var a=r(o[u],"td"); if(a.length!=i.length)return}x(e,i)}} (document);document.addEventListener("DOMContentLoaded",function(n) {TgTableSort("tg-FUkU8")}); </script>
The payload from the previous node is embedded by means of {{{payload}}}
, highlighted in the preceding code. This is a mustache template form for extracting msg.payload from the node input. Specify three pairs of braces to ensure that the HTML content that is part of the incoming msg.payload is not ignored.
The application is almost done, but there is one thing we still want to do, which is to leverage the functionality obtained by using the OpenWhisk
action, which is our microservice 3. Follow the steps given to build this into your application:
OpenWhisk
trigger. We will be using the B05307_07_CloudantTrigger
trigger that we configured in Chapter 7, Compute Options on Bluemix. Click the pencil icon next to Service to configure the OpenWhisk service:
To get Auth Key for the OpenWhisk service, you can use the OpenWhisk CLI wsk, which was explained in Chapter 7 , Compute Options on Bluemix, to execute the following command:
wsk property get --auth
Use the whisk auth key that is returned to configure the auth key in the OpenWhisk node.
Refer to
Chapter 7
, Compute Options on Bluemix, OpenWhisk section, for details on executing the wsk
commands.
Similarly, to get the namespace, execute the following wsk
command:
wsk property get ---namespace
Now we have three applications, two Cloud Foundry applications and one OpenWhisk application, running as independent applications, with their own life cycle and DevOps.
As an application end user, we are giving our microservices-based end users an application URL they can hit to view negative tweets on a monitored Twitter handle, along with their scores against social tone. For this application to function as intended, we need our microservice 1 to be running, microservice 2 to be running, and eventually, to achieve any added value or extended functionality independent of microservices 1 and 2, we need our microservices 3 to be available and running. In the current scenario, microservice 3 does not add value to the existing use case, but it nevertheless provides a handle to implement any function without having to edit or update microservices 1 and 2. Follow the steps given here to see the results:
https://analyse-twitemotion-b05307.mybluemix.net/translatedtweets
in this case.
B05307_07_CloudantTrigger
trigger:
This completes the illustration of microservices-based application development on Bluemix.
18.226.251.206