Skip to content

REST and the Cloud

Adarsh Kumar Maurya edited this page Dec 10, 2018 · 1 revision

Overview

In this last module, we're going to take a look at the cloud and make the case for REST as integral part of cloud-based architecture. This will be a shorter module than the others as the purpose here is a bit different. Previous modules were intended more to explain concepts or provide design guidance for restful systems. This module, by contrast, will attempt to make some connections between what you've hopefully learned about REST and the emerging world of cloud computing. In examining REST in the context of cloud computing, we'll start out by taking a looking at some of the goals of the cloud or at the very least, some of the advertised benefits of moving to the cloud. We'll then look at some of the architectural challenges that are inherent in building cloud applications, and we'll see how many of these challenges are really just real-world examples of many of the fallacies of distributed computing that we talked about back in module two. As we go through many of these, my hope is that you will already be making the connections between the properties that REST yields and how those properties address many of the challenges that are inherent in creating a good cloud-based architecture. Regardless, we'll then lineup the characteristics of a good cloud architecture with the RESTful constraints that help to create those characteristics. We'll conclude this module with a little envisioning exercise. I'll tell you about some of the opportunities I've been exploring for optimizing cloud-based applications by taking advantage of the RESTful constraints and I'll close with a challenge for you to do the same. I'll then spend just a couple of minutes reviewing all that we've gone over in this course, as we've covered quite a bit of ground.

The Goal of Moving to the Cloud

At the end of the day, the motivator for a business to do anything, including move its application portfolio to the cloud, is to increase its profit. Note here that I didn't put this in terms of cutting costs, it's very possible that when a business moves its applications to the cloud it ends-up spending more money on IT, but it does this because it can deliver more services faster, and thus make more revenue, which compared with the expenses spent on cloud resources still yields a higher profit. You may remember a quote that I used earlier during this course, that in order to get the benefits of a scalable infrastructure we have to have a scalable architecture that can take advantage of that infrastructure. One thing that I don't think is called out explicitly though is cost. If you look at the statement narrowly enough, you might react by saying, well, I can absolutely scale my current application using the cloud. And I would agree, though I would ask, can you do it cheaply enough to make it worthwhile? Many architectural styles have their cost inefficiencies hidden by the fact that they run on computing resources that are owned by a company. Those computing resources are known as capital expenditures. When a company moves its applications to the cloud, they shift from owning the computing resources to leasing them, in other words, from capital expenditure, to something called operational expenditure. And because of the economics behind how many companies function, this move can be looked on kind of unfavorably by the executives who pay the bills. This means that your application architecture needs to take advantage of the scalable infrastructure of the cloud in a way that keeps operational costs to a minimum. To build cloud applications only thinking in terms of infinite computing resources, is like going shopping with a credit card and thinking only in terms of infinite purchasing ability. Neither case works out very well.

Characteristics of a Good Cloud Architecture

So then, let's start out by looking at some of the characteristics that form a good architecture for the cloud. First, such an architecture should be able to scale in such a manner that an increasing resources, provides a proportional increase in performance. We'll talk more about this in a moment, but I'm really referring to scalability here. Next, a good cloud-based architecture should be operationally efficient. Both in how it uses the infrastructure resources that it's built on, and how it can be administered and managed by human operators. Next, a good cloud architecture should be resilient to failures at all levels. This includes hardware failures, application software failures, network failures and everything in between. The application should be able to take advantage of the clouds supporting services to selfheal as well as ensure that all of the appropriate individuals are made aware of the failure so that the problem can be corrected. Finally, a good cloud architecture is all about realizing economies of scale. We'll talk more about this in a few minutes, but the idea is that as the application scales, it actually costs less.

Scalability

It's hard to think about cloud computing without thinking of scalability. One of the most frequent ways that I've heard people try to sell the cloud is by invoking the Slashdot effect. If you've never heard this term used before, it describes the expediential spike in traffic to a website when it gets mentioned on the popular geek discussion sight Slashdot. In the Microsoft developer world, this is probably more analogous to getting mentioned by Scott Guthrie or Scott Hanselman. At any rate, let's breakdown some of the specific challenges related to scaling. First, scaling has traditionally been a bit more art and luck than science. This is because underlying the decision of when and how to scale is an assumption that you can predict demand on your application at regular time intervals, for example at the start of a budget cycle. The problem is that load is in a constant state of flux and so depending on how budgets are structured, companies are nearly always vacillating between having too much capacity and not having enough capacity. The reality is that it's unreasonable to regularly predict load. We need a better way to provision resources. Second, different component types and different layers have different scale requirements. And like everything else, these requirements change over time. The key design principle to remember here is that each layer should be responsible for managing its own scale requirements. You should design your architecture in such a way that a change in resource scaling in one layer does not cascade down into other layers. Thirdly, scalability is made a great deal simpler when none of the components involved in processing a workflow are aware of one another. Typically, this kind of design is enabled via a mechanism like a queue which is used to broker communication between layers. This is certainly valuable from a resiliency standpoint, but it can also have some profound effects on scaling. For example, in the event that a queue reaches a certain size, the layer using the queue as input can scale up its resources to relieve pressure on the queue without directly impacting any of the layers with which it interacts. Decoupling in this way also makes it much simpler to implement parallelism across parts of the architecture. Finally, for scaling to be most efficient, it should be as dynamic as possible. As I've described in that previous scenario of relieving pressure on a queue, and in order for scaling to be dynamic, the provisioning of new resources must be automated using technologies like scripting.

Operational Efficiency

Now for operational efficiency, this first point that you can see ties into the automated scaling characteristic that we just looked at. It just looks at automation from a slightly different angle. What we're saying here is that for a cloud-based application to be efficient with respect to how it's managed tasks that were once manual tasks, performed by a network administrator should be automated into scripts. This means that rather than bringing a new machine online, one at a time, a network administrator can bring entire clusters online to support a new test initiative or handle a spike in demand on a part of the system. It also lays the foundation for enabling the system to be more responsive in recovering from a failure, which we'll talk about next. The second point here is that a cloud-based architecture should spread the cost of running the system around the entire network of components that make up the architecture. This means letting go of some expectations around the needed power of an individual component and instead, architecting for the entire environment. One example that's used in an Amazon.com white paper on cloud architecture best practices, states that a server may not have as much RAM as you think you need, but you may have access to a distributed cache service such as memcached. As you may already be thinking, in REST we can go a great deal further then distributed server caching, as we want caching to take advantage of the entire network path from the client all the way to the origin server.

Resiliency

As the quote here says, good cloud architectures should be impervious to reboots and re-launches. One of the biggest enemies to this capability is having state such as Session State or user context that's stored in-memory by your application components. Additionally, cloud-based architectures should be easily monitored either by tools supplied by the cloud provider or by any number of open-source or commercial tools. These tools can have visibility into the application components, ideally via their inputs and outputs and they can take action based on administrator configurable triggers. Actions could be anything from recycling a machine; to restarting a process, to bringing new machines on line and putting them into rotation on a virtual load balancer. Finally a good cloud architecture should spread the risk around. At a most basic level, this means to have redundant resources for application components, however, more practically, it means that you should have components diploid into different data centers and ideally, even across different cloud providers. No matter how good their engineering is, each of the major cloud providers has suffered major outages over the last couple years. As such, you can't assume that your cloud host will be 100% reliable.

Realize Economies of Scale

Now let's talk about economies of scale. The basic definition here is that the cost per unit goes down as the number of units goes up. So the questions that we really need to address when it comes to a cloud computing architecture are, how do we measure cost and how do we measure the number of units? We can measure cost pretty simply as the actual monetary cost of running our application. This includes the cost of hardware, software, facilities, utilities, etcetera, if you own and manage your own data center. Or, it includes the cost of service by your cloud provider if you're hosted on the cloud. In my opinion, this also includes the human cost of developing, enhancing, and maintaining the application, since I think that there's many times a hidden cost here that ends up growing significantly over time. The number of units can be measured by the amount of demand or load on the application. There's more than one metric that can be used to measure this, but I tend to start out using requests per second. Therefore, by these definitions, my application has economies of scale if the cost of processing a single request per second goes down as my total number of requests per second goes up.

REST as a Means to Scalable Architecture

Hopefully as we've been going through some of these characteristics, you've been having thoughts like, oh yeah, that should be achievable with the RESTful constraint X. What you can see here is a table showing a basic mapping of the characteristics we just talked about to the RESTful constraints that enable an architecture to exhibit those characteristics. For example, for scalability, applying the stateless constraints, enables state to be maintained on the clients, keeping the server from needing to maintain in-memory stores for user context. The visibility also enables intelligent intermediaries to automatically provision new resources based on the run-time characteristics of the system. The cache constraint helps to keep load off of the origin servers, enabling them to handle more load. And the layered system constraint enables different component layers to scale as they need to, but, independently of one another. All of the other characteristics of a scalable architecture are achieved in much the same way, by applying the RESTful constraints. The visibility achieved through the stateless constraint creates a number of opportunities for intermediaries to optimize component interactions and recover from failures. The uniform interface enables components to be added, removed, or replaced at will, without requiring anything of the components communicating with them. The layered system constraint enables dynamic adding and removing of resources in a way that is transparent to components in other processing layers. A classic example here is adding a new computing node or a thousand behind a load balancer; a component that as a client of this layer knows only of the layer, and not of the individual components within it.

Cloud Architectural Strategies

In that same Amazon.com white paper that I referenced earlier, the author identifies six strategies to use in designing architectures that will run well in the cloud. The first is to design for failure. Included in this strategy are infrastructural tactics such as having an automated backup and restore strategy, implementation tactics, such as building your application to automatically start when a system is booted and architectural tactics such as avoiding in-memory sessions or stateful user context. As we've discussed, a RESTful architecture does a great job at meeting this last tactic as a result of applying the stateless constraint. Next is to decouple your components from one another. The article describes this in terms of using queues. From a REST perspective, however, the architectural constraints of the uniform interface and the layered architecture go a long way toward enabling new layers such as queuing layers, to be introduced over time as the system evolves and as it warrants the cost of the additional layer. From the architectural perspective, implementing elasticity is all about ensuring that the cost of automating scale-up and scale-down is as small as possible, making it much easier to do and in a more granular level. For example, within an architectural layer the RESTful constraints of statelessness and layered architecture can go a long way to help here. Once we've decoupled components and enabled the infrastructure to scale elastically, the opportunities for parallel processing become much more apparent. In addition to the RESTful constraints of statelessness and layered architecture, the uniform interface constraint and more specifically, self-describing messages can help parallel controllers to more easily manage these more complicated workflows. The strategy of keeping dynamic data closer to the compute components while keeping static data closer to the end-user, in many ways seems like simply another way of restating REST cache constraint. And finally, implementing security at every level is really something that's orthogonal to REST; however, RESTful constraints such as statelessness, the uniform interface, and layered architecture create a solid foundation to enable security policies at multiple levels throughout the overall system architecture.

Cloud Opportunities

With all of this in mind, I want to take this last slide to tell you about some of the research interests that I have with regard to optimizing a cloud-based architecture using the RESTful constraints and hopefully inspire you to think of some optimizations that go even much farther. The first is the idea of cloud-overflow or failover. In the first case a client begins a workflow by consuming services diploid on premise, however, as the load on those resources grows; eventually a threshold is reached where the on-premise services need to start directing traffic to the same service hosted in the cloud. The case of failover deals with how components need to behave when a service provider has a catastrophic failure. This could be failing over from your company's datacenter to the cloud or more interesting to me, anyway, you could failover from one cloud provider to another. The way to go about this is to take advantage of two RESTful constraints. Statelessness and the uniform interface, specifically, hypermedia as the engine of applications state. The second category of optimizations involves the creation of different connectors that can be used to auto-tune a service. A few examples, as you can see here, include the automatic creation and management of entity tags or etags based purely on resource identifiers and control data such as the HGDP methods. Second is managing cache control values based on the volume and type of requests that a server receives for a given resource. Finally, as a representation in-lining which means watching for usage patterns in a resource hierarchy and automatically expanding links that happen to be followed frequently. Clients can then be built to follow a collapsed link or they can use an in-line representation directly. Again, these are just a few ideas that I'm working on that are enabled as a result of the REST constraints and I'm sure that there are many more. I would love to know what you come up with as you think more deeply about how you can apply REST constraints in your own designs.

Summary

In this module, we've covered at a very high-level, some goals of the cloud and how those goals must be realized, not just with a scalable cloud infrastructure, but also with a scalable cloud architecture to take advantage of that infrastructure. We then delved into some of the characteristics that make up a good cloud architecture, and described some strategies for achieving it. We also looked back at the RESTful constraints and mapped them on to the desired characteristics for a cloud architecture. Finally, we looked at some opportunities to further optimize and add value to cloud applications. My hope is that this module has started the wheels turning in your head, of all the new ways that you can apply REST in the cloud to deliver new levels of value. And with that, we're at the end of the course. As a final quick recap, we started out by setting the stage and establishing why REST matters in a modern distributed application. We then took a look at the definition of REST and then an even deeper look as we dove into the individual constraints and data elements. We then discussed the principals and design strategies for implementing both RESTful services and RESTful clients. And finally, this module makes the case that REST is an architectural style that is a natural fit for cloud-based computing.