Mobile communication applications have become a growing trend in the last few years. They allow users to build global virtual communities, reducing the need for the classic carrier while using alternative data channels like WiFi or 4G. Such applications offer phone call features, interactive chats, SMS messages, video, and more for free or a very low cost. Companies are being challenged daily to provide best in class, high quality, over-the-top communication applications. In this session, we discuss the challenges in building a global quality network. We present how Vonage built a Mobile Global architecture using Amazon EC2, developed geo-location algorithms for optimal routing, and implemented iPhone/Android AWS SDKs for secure attachments storage using Amazon S3.
MBL204 Enhancing Quality & Performance for Vonage Mobile VOIP and Messaging Application using AWS - AWS re: Invent 2012
1. Enhancing Quality & Performance for Mobile VOIP & Messaging Apps
Guy Fighel - Director of R&D Vonage Mobile
2. Vonage Mobile
Overview
• High Quality Global VOIP & Messaging Mobile App
• Phone calls to ANY Vonage user == Free!
• Text Messages to ANY Vonage user == Free!
• Phone call to any other destination == low cost
• iPhone & Android
3.
4. Software Architecture
• Telephony
• Vonage Proprietary SIP Proxies & Media Relays (C++)
• Billing/Rating Engine (C++)
• Real Time Call State Managers (C++)
• Application APIs
• REST APIs (JAVA)
• Vonage Push Service to communicate with Apple Push & Google Cloud
Messaging Service
• Messaging APIs
• REST APIs (JAVA)
• Redis (with persistency)
5. Challenges
• Delay/Low Bandwidth/Traffic Blocking for VOIP Calls
• Global users - Peer to Peer vs. “Tromboning”
• Messages Storage/Performance
• Centralize Architecture for PSTN calls vs. Distributed
Network
• Providing Client Properties for multiple environments
8. Using EC2
• Media Relays
• Deployed our Relays in Ireland, Singapore, Virginia, California, Sao
Paulo
• In each zone, we are using ELB with Static IP
• In each zone we start with 4 instances and dynamically can scale
up (up to 10)
• High-CPU Extra Large Instance 7 GiB of memory, 20 EC2
Compute Units (8 virtual cores with 2.5 EC2 Compute Units each),
1690 GB of local instance storage, 64-bit platform
• Monitoring using CloudWatch (NOC dashboard)
• All sites are connected back into Vonage’s Data Centers using
VPC with ACLs & Encryption
9. Closer to the customer
• Client sends SIP Registration Message to Proxy
• Proxy determines client’s IP address (using STUN)
• Proxy performs Geo Location query using external API
• Based on Geo Location, Proxy chooses ELB Public IP in
the relevant region
• SIP continues to flow between Mobile client and Vonage’s
DC, but Media flows directly into Amazon EC2 instance
• (In Beta) - Client always puts IP address record using
Route53 as the Relay. Dynamically gets best closest route
• Override table has been implemented as well
12. Using S3
• Mobile Messages API
• Using S3 instances in US multiple regions (Always in full
redundancy mode)
• Messages are stored in Vonage internal Redis NoSQL DB
• When Mobile client sends an attachment, it generates a
unique ID for the file
• Mobile Client uses S3 SDK for iPhone/Android in order to
save the object, encrypt it and sends to Redis the
attachment’s reference ID
• When the Mobile client uses the ‘getMessage’ API, it fetches
from Redis the S3 Attachment ID of a specific Message ID,
and directly retrieves it from S3
13. Mobile Messages
* saves a file into S3 storage using an input stream.
* @param attachment the attachment to save
* @return key the key used when sending to AWS
* Note: this method should not close the input stream.
* @throws MessagingException
public String save(Attachment attachment) {
FileMetaData metaData = attachment.getFileMetaData();
String key = makeKey(metaData);
InputStream istream = attachment.getInputStream();
if (istream == null)
throw new MessagingException("Unable to process attachment " +
metaData.getFileName(),
" - attachment meta data error: no stream" );
ObjectMetadata awsMetadata = new ObjectMetadata();
awsMetadata.setContentType(metaData.getMimeType());
awsMetadata.setContentLength(metaData.getSize());
14. Mobile Messages with Encryption
if (ServiceProperties.getBoolean(SERVER_SIDE_ENCRYPTION)) {
awsMetadata.setServerSideEncryption(ObjectMetadata.AES_256_SERVER_SIDE_ENCRYPTIO
N);
}
PutObjectRequest request = new PutObjectRequest(bucket, key, istream, awsMetadata);
attachment.setStoreType(type);
try {
getAwsClient().putObject(request);
} catch (Exception e) {
throw new MessagingException("Unable to store attachment " +
metaData.getFileName(),
" - Aws S3 error: " + e.getMessage());
}
return key; }
15. Retrieve Messages
public InputStream get(FileMetaData metaData)
{
String key = metaData.getKey();
try
{
S3Object object = getAwsClient().getObject(new GetObjectRequest(bucket, key));
if (object == null)
{
throw new ClientRequestException("File " + metaData.getFileName() + " does not
exist",
" - Sws S3 returns null " + metaData.toString());
}
return object.getObjectContent();
16. Client Properties Using SimpleDB
• SimpleDB Domains per number of environments
ISO_CODE CLIENT_TAG DOMAIN
1 BETA BetaDomain
1 PROD ProdDomain
1 QA QaDoamin
44 PROD ProdDomain
If mobile client has no ‘CLIENT_TAG’, then:‘DOMAIN’
always overrides
17. Client Properties Using SimpleDB - cont.
• ProdDomain
Attribute 1 Attribute 2 Client_Type
YES Server Address 1 iPhone
YES Server Address 2 Android
No Server Address 1 Windows Phone
18. SimpleDB
public class SimpleDB {
public static void main(String[] args) throws Exception {
AmazonSimpleDB sdb = new AmazonSimpleDBClient(new PropertiesCredentials(
SimpleDBSample.class.getResourceAsStream("AwsCredentials.properties")));
try {
String myDomain = "Domain0";
// Select data from a domain
String selectExpression = "select * from `" + myDomain + "`;
SelectRequest selectRequest = new SelectRequest(selectExpression);
for (Item item : sdb.select(selectRequest).getItems()) {
System.out.println(" Item");
System.out.println(" Name: " + item.getName());
for (Attribute attribute : item.getAttributes()) {
System.out.println(" Attribute");
System.out.println(" Name: " + attribute.getName());
System.out.println(" Value: " + attribute.getValue());
}
}
System.out.println();