Wednesday, 1 August 2018

Integration with AWS SNS/SQS using Spring Cloud Stream


Spring cloud stream is a framework for building highly scalable event-driven microservices connected with shared messaging systems.
It provides binders to connect with messaging systems like rabbitmq, kafka etc. It also provides capability to connect with AWS SNS/SQS.

Spring Cloud Messaging Binders documentation is really good for setting up message channels & subscribers. In my case I wanted to use SNS/SQS as messaging system, where I faced few issues and documentation doesn’t help much in this regard. In this post, I will share sample code of producer/consumer application & its configurations.


Producer Application:

Gradle Dependencies:
compile('org.springframework.boot:spring-boot-starter-web')
compile('org.springframework.cloud:spring-cloud-stream')
compile('org.springframework.cloud:spring-cloud-aws:1.2.2.RELEASE')
compile('org.springframework.cloud:spring-cloud-aws-core:1.2.2.RELEASE')
compile('org.springframework.cloud:spring-cloud-aws-messaging:1.2.2.RELEASE')

ProducerApplication.java

This code will accept an input TodoEvent POJO and publish the same into SNS.

@RestController
@SpringBootApplication
public class ProducerApplication {
                private AmazonSNSAsync amazonSNSAsync;
    public ProducerApplication(){
        amazonSNSAsync = AmazonSNSAsyncClientBuilder.standard()
                .withCredentials(new AWSStaticCredentialsProvider(new BasicAWSCredentials("<<accessKey>>","<<secretKey>>")))
                .withRegion("<<region>>")
                .build();
    }

                public static void main(String[] args) {
                                SpringApplication.run(ProducerApplication.class, args);
                }

    @PostMapping("/todo")
    public void createTODOEvent(@RequestBody TodoEvent event) throws Exception{
                    amazonSNSAsync.publish("<<SNS TOPIC ARN>>", new ObjectMapper().writeValueAsString(event) , "test message");
    }
}

Consumer Application:

Gradle Dependencies:
compile('org.springframework.boot:spring-boot-starter-web')
compile('org.springframework.cloud:spring-cloud-stream')
compile('org.springframework.cloud:spring-cloud-aws:1.2.2.RELEASE')
compile('org.springframework.cloud:spring-cloud-aws-core:1.2.2.RELEASE')
compile('org.springframework.cloud:spring-cloud-aws-messaging:1.2.2.RELEASE')

ConsumerApplication.java

@SpringBootApplication
@EnableSqs
public class ConsumerApplication {

                private QueueMessagingTemplate queueMessagingTemplate;

                public static void main(String[] args) {
                                SpringApplication.run(ConsumerApplication.class, args);
                }

                @Bean
                public QueueMessagingTemplate queueMessagingTemplate(
                                                AmazonSQSAsync amazonSQSAsync) {
                                QueueMessagingTemplate queueMessagingTemplate = new QueueMessagingTemplate(amazonSQSAsync);
                                return queueMessagingTemplate;
                }

                @Bean
                public QueueMessageHandlerFactory queueMessageHandlerFactory(AmazonSQSAsync amazonSQS, BeanFactory beanFactory, ObjectMapper objectMapper) {
                               
                                QueueMessageHandlerFactory factory = new QueueMessageHandlerFactory();
                                factory.setAmazonSqs(amazonSQS);
                                factory.setBeanFactory(beanFactory);

                                MappingJackson2MessageConverter mappingJackson2MessageConverter = new MappingJackson2MessageConverter();
                                mappingJackson2MessageConverter.setSerializedPayloadClass(String.class);
                                mappingJackson2MessageConverter.setObjectMapper(objectMapper);
                                mappingJackson2MessageConverter.setStrictContentTypeMatch(false);
                                // NotificationMsgArgResolver is used to deserialize the “Message” data from SNS Notification
                                factory.setArgumentResolvers(Arrays.asList(new NotificationMessageArgumentResolver(mappingJackson2MessageConverter)));

                                return factory;

                }

                @Bean(name = "amazonSQS", destroyMethod = "shutdown")
                public AmazonSQSAsync amazonSQSAsyncClient() {
                                AmazonSQSAsync amazonSQSAsync = AmazonSQSAsyncClientBuilder.standard()
                                                                .withCredentials(new AWSStaticCredentialsProvider(new BasicAWSCredentials("<<accessKey>>","<<secretKey>>")))
                                                                .withRegion("<<region>>")
                                                                .build();
                                return amazonSQSAsync;
                }

                @RuntimeUse
                @SqsListener(value = "<SQS-QUEUE-NAME>", deletionPolicy = SqsMessageDeletionPolicy.NEVER)
                // Set deletion policy to NEVER so that you can acknowledge the incoming message post processing. If mentioned as ON_SUCCESS, message is not deleted when method throws an exception else message will be deleted. ALWAYS means message will be deleted irrespective of the message processing state
                //With Acknowledgment you will have control at which point the message is ok to be deleted from the queue
                public void sqsListener(@NotificationMessage TodoEvent message, Acknowledgment acknowledgment){
                                System.out.println("message from SQS "+message);
                                // If the below statement is not provided, then the message will not be deleted from the queue
                                acknowledgment.acknowledge();
                }
}

Raw SNS Messages can be pulled from the queue by defining the argument as “String message”. The message will be in the below defined format.

SNS Message Format:
{
  "Type" : "Notification",
  "MessageId" : "<Message UUID>",
  "TopicArn" : "<SNS ARN>",
  "Subject" : "test subject",
  "Message" : "<Actual Message>",
  "Timestamp" : "2018-05-01T00:00:00.287Z",
  "SignatureVersion" : "1",
  "Signature" : "<PEM Signature>",
  "SigningCertURL" : "",
  "UnsubscribeURL" : ""
}

@NotificationMessage denotes that the application is trying to read the content of Message field from SNS message. NotificationMessageArgumentResolver is used to deserialize the message into corresponding object.
@NotificationSubject retrieves the content from Subject field of SNS message 

Tuesday, 31 July 2018

Twelve Factor Apps

This post is to share my understanding about Twelve Factor Apps by Adam Wiggins.

Twelve Factor Apps is a methodology for building software as a service apps that,

       Use declarative formats, to ease the setup for new developers
       Have clean contract to offer maximum portability between environments
       Adopt modern cloud platforms
       Enable CI & CD
       Can scale up significantly

Motivation of Twelve Factor Apps is to raise awareness about the systematic problems, to provide shared vocabulary & to offer broad conceptual solutions to those problems.

1.    Code Base

§    Use Single Repository to deploy one app in multiple environments using labels/changesets
  • Every app should have dedicated repo, multiple apps sharing a single repo is a violation of Twelve Factor apps
2.       Dependencies

§   Make App dependencies as system wide common packages eg. Npm, yarn, maven modules
§   It will help developers to setup any given project faster
  • Apps should not rely on implicit existence of packages
3.       Configuration

§   Export all configuration parameters as environment variables
§   Configurations that are not going to change between environments can be maintained implicitly in the app (not in the code as hardcoded)
§   Group the parameters based on environments, to configure the same way ahead before different environment deployments (this may not scale as expected, when new env is required manual intervention is required)

4.       Backing Services

§   Backing services are the services/resources used by the app over network (eg. Databases, Queues)
§   Every distinct backing service is a resource, each service/resource should be loosely coupled by using enterprise integration patterns (event-driven).
§   Any change to the resource should not impact the app’s code
§   Eg. If the database server doesn’t respond as expected, then a new instance of the database should be easily attached to the app

5.       Build, Release & Run

§   A code base is transformed into deployable component through the below stages,
       Build Stage – prepares executables
       Release Stage – applies configurations to the built executables
       Run Stage – launches the app in target environment
§   Every release should have a unique ID, a release cannot be mutated, which allows roll backing the release to previous concrete release
§   Have strict separation between the stages

6.       Processes

§   A app can be executed as a single or multiple processes
§   App should be stateless and share-nothing. The state of the app should be persisted in the backing services.
§   Design the app to be stateless, it will help to scale the same

7.       Port Binding

§   An app should be self-contained, it exposes HTTP as a service by binding to a port and listens to serve the incoming requests
§   In environment deployments, routing table will take care of routing the dns name to specific port bounded web process

8.       Concurrency

§   In twelve factor apps, processes are first class citizen
§   Designing an app stateless & share-nothing mode means that adding more concurrency is simple & reliable operation
§   Long running task can be moved to a worker model which can scale independently
§   App should never daemonize instead it should respond to manage streams, crashes, user-initiated restarts & shutdowns

9.       Disposability

§   App process should strive to minimize start up time (this helps to scale faster)
§   Also it should gracefully shutdown (to stop receiving request & process the received request then exit)
§   The app should be architected considering to handle crashes, unhandled, non-graceful terminations due to hardware failures.
§   Recommended approach will be to have a robust queue to backup the request before processing

10.    Dev/Prod Parity

§   Keep development, staging & prod environments as similar as possible
§   App should be designed for continuous deployment by keeping the gap b/w environments as small as possible
Factors
Traditional app
Twelve-factor app
Time between deploys
Weeks
Hours
Code authors vs code deployers
Different people
Same people
Dev vs production environments - Tools
Divergent
As similar as possible
§   Backing services cannot be different between dev & prod environments

11.    Logs

§   Treat Application Logs as Event Streams
§   Log Streams can be used to analyse the behaviour, collect metrics, store for audit purposes
§   Log Streams can be made to flow through another business process mode to find specific events, identifying trends, to introspect over time period

12.    Admin Processes

§   Run admin/management tasks as one-off processes
§   Eg. Gateways which introspects every request for routing, rename context paths
§   Admin code must be shipped with application code to avoid synchronization issues using dependency isolation technique

Reference:
https://www.dynatrace.com/news/blog/twelve-factor-app-now-fifteen-factor

Friday, 1 June 2018

Web Bluetooth based web-application

This post is about sharing my experience with Web Bluetooth capability in Browser.

Web Bluetooth is a javascript module for enabling Browsers communicating to any BLE enabled device (eg. toys, phones, drones) via underlying bluetooth stack.

navigator.bluetooth is the package that enables us to access nearby BLE devices.

To get started, Go to chrome browser, open chrome://flags/#enable-experimental-web-platform-features, enable the highlighted flag, and restart Chrome for now

Basics of Bluetooth:


GAP – Generic Access Profile
                This is the basic advertising packets by any Bluetooth service, which exposes the data like name, mac id & strength.
GAP defines the role of the Bluetooth device,
  • Peripheral role –  In this role, the device as a Slave which exposes a GATT profile. Once peripheral device is connected to a Central device, the advertising stops. Peripheral device can communicate with one central at a time.
  • Central role – In this role, the device acts as Master, it connect to the peripheral devices. A Master can connect with multiple devices.


GATT – Generic Attribute Profile
                This defines the services & characteristics of the peripheral device. A GATT profile can have list of services, every service can have a list of characteristics, each characteristic have a 16-bit value.
A Service can have many characteristics, each characteristic can have optional descriptors & will have a data associated with it.
Each Service is identified by a Unique UUID (eg. 00003105-0000-1000-8000-00805f9b34fb) like-wise each characteristic will be identified by a Unique UUID.

Peripheral Devices are GATT Server which exposes service(s) with appropriate characteristic data value. Central Devices are GATT Client which requests the server for data.


Browser handshake with BLE Peripheral Device



navigator.bluetooth.requestDevice() – Initiates the scanning process based on the specified filter (or) all devices can be scanned by “acceptAllDevices:true” option. This script opens a pop-up with the list of devices around.

Scan Device List in Browser



device.gatt.connect() – After pairing the target device, gatt.connect() will return a GATT server promise.
server.getPrimaryService() – returns a promise of service object
service.getCharacteristic() – returns a promise of requested characteristic object
characteristic.readValue() – returns a ArrayBuffer object, on decoding with Text Decoder, actual data can be retrieved.
Based on the permissions on characteristic, the data can be updated using characteristic.writeValue(<ArrayBuffer>)

Sample HTML Page for Scanning BLE & Connect using Web BLE

Things to be explored:
Pairing based on crypto keys.
Improve performance in accessing BLE services.





Recent Posts

Micro VMs & Unikernels

This post is a follow up of this post . In Previous Post, we discussed about Virtual Machines & Containers architecture. In this post, w...

Older Posts