您的位置:首页 > 移动开发

Serverless Single Page Apps:Fast, Scalable, and Available(读书笔记)

2016-10-09 16:30 621 查看


Serverless Single Page Apps Fast, Scalable, and Available




目录

 [隐藏
1 Intro
2 Starting
Simple
3 Routing
Views with Hash Events
4 Essentials
of SPA
5 使用Amazon
Cognito作为身份服务
6 存储数据in
DynamoDB
7 用Lambda来构建微服务
8 Serverless
Security
9 Scaling
Up


Intro[编辑]

Avoid Yaks(yak shaving)
Move Faster with Tests(work with confidence)
TDD:Red-Green-Refactor


Starting Simple[编辑]

Benefits of a Serverless Design
无服务器维护开销,focused on app
Easy to Scale(?)
Highly Available
Low Cost(不过这只是相对的,带宽IO和存储会随着用户数上升的)
(Micro)Service Friendly
Less Code

Limitations
Vendor Lock-In
Odd Logs
Different Security Model
Different Identity Model
Loss of Control
Big Scale: Big Money?

本书项目:https://github.com/benrady/learnjs
Running Locally
learnjs $ ./sspa server
https://github.com/livereload/LiveReload 有这个必要吗

Deploying to Amazon S3
$ sudo pip install awscli
$ aws configure --profile admin (需要access key和secret key)
Attach Policy(设置该用户能够访问哪些aws服务)
Creating an S3 Bucket
learnjs $ ./sspa create_bucket learnjs.benrady.com
$ ./sspa deploy_bucket learnjs.benrady.com
这里用户的域名是怎么关联到实际的虚拟主机 http://learnjs.benrady.com.s3-website-us-east-1.amazonaws.com的?
哦,创建一个CNAME项。。。


Routing Views with Hash Events[编辑]

Build seams into the design
Jasmine http://jasmine.github.io
Running Tests in Production(一个不错的想法!)
var learnjs = {};

learnjs.showView = function(hash) {

var problemView = $('<div class="problem-view">').text('Coming soon!');$('.view-container').empty().append(problemView);
Adding Routes
Adding View Parameters
spy?这使用的是ES6 Proxy技术实现的吗?
spyOn(learnjs, 'problemView'); ... expect(learnjs.problemView).toHaveBeenCalledWith('42');

测试:Fast, Informative, Reliable, and Exhaustive
Favor testability over encapsulation!
测试hashchange事件:
learnjs.appOnReady();
spyOn(learnjs, 'showView');
$(window).trigger('hashchange');
expect(learnjs.showView).toHaveBeenCalledWith(window.location.hash);

实现onhashchange:window.onhashchange = function() { learnjs.showView(window.location.hash); };
SPA的核心还是很简单的~

Next Steps:
Jasmine Matchers
Jasmine-jQuery
Test Doubles
Routing Libraries https://github.com/flatiron/director http://visionmedia.github.io/page.js/
JavaScript Testing Alternatives
Hash Change Events
window.history.push/popState


Essentials of SPA[编辑]

var view = $('.templates .problem-view').clone(); //不过这里实际上并不是HTML5模板元素;

CSS:.templates { display: none; }
定义data model:
low object mapping impedance
HTML5 data attributes

Creating an Application Shell
This flash of markup is annoying at best, and confusing at worst.

Using Custom Events
$('.view-container>*').trigger(name, args);
view.bind('removingView', function() { ... })

Next Steps
Web Accessibility
Creating a Home Screen Icon
CSS Animations
Form Validation


使用Amazon Cognito作为身份服务[编辑]

Creating an Identity Pool(基本上可以理解为匿名用户?)
learnjs $ ./sspa create_pool conf/cognito/identity_pools/learnjs
==> learnjs/4001/conf/cognito/identity_pools/learnjs/pool_info.json

"IdentityPoolId": "us-east-1:71958f90-67bf-4571-aa17-6e4c1dfcb67d","SupportedLoginProviders": { "accounts.google.com": "ABC123ADDYOURID.apps.googleusercontent.com", ...

IAM Roles and Policies(?app用户并不是直接使用aws服务,而是通过app代理来间接使用aws吧?)
assume_role_policy.json(内容略)
Cognito uses Amazon's Security Token Service (STS) to generate temporary AWS credentials for our users.
"Arn": "arn:aws:iam::730171000947:role/learnjs_cognito_authenticated" ?

Fetching a Google Identity
You might find Google’s process for adding a Sign-In button a bit...intrusive.(必须在页面上插入一个UI按钮)
<script src="https://apis.google.com/js/platform.js"
async defer></script>
<meta name="google-signin-client_id" content="ABC123ADDYOURID.apps.googleusercontent.com"/>
定义JSONP回调:function googleSignIn() { ... }
最后:<span class="g-signin2" data-onsuccess="googleSignIn"></span>

Requesting AWS Credentials
理论上说来,Cognito可以管理一个全局的identity池,而不是app特定的。。。(因为OAuth2本身是全局的)
function googleSignIn(googleUser) {

var id_token = googleUser.getAuthResponse().id_token;... //请求将得到的token加入到身份池,然后app得到一个Cognito user ID(注意,这里体现的DDD的领域转换原则)
Refreshing Tokens:gapi.auth2.getAuthInstance().signIn(...) --> ...
=> var deferred = new $.Deferred(); ... //这里的代码感觉有点过时了,ES6 Promise现在应该可以用了

Creating a Profile View(略,配图似乎有点问题?)
Next Steps
Developer-Authenticated Identities


存储数据in DynamoDB[编辑]

一维主键:hash primary key
两维:hash attribute + range(排序)

属性名 <= 255 bytes
DynamoDB supports a range of types for attribute values
In addition, you can have attributes with document types, like Lists and Maps(属性值可以嵌套?K-V Store变成了文档数据库?)

... but out of the box it's essentially a big hash map.
Strong vs. Eventual Consistency
conf/dynamodb/tables/learnjs/config.json
3个顶级属性:AttributeDefinitions, KeySchema, and ProvisionedThroughput
read/write unit:每秒<4KB的读(写<1KB??)
app开发者需确保key在hash空间均匀分布,否则可能ProvisionedThroughputExceededException

dynamodb create-table

Secondary Indexes and Query vs. Scan
二级索引:global and local

Authorizing DynamoDB Access:conf/dynamodb/tables/learnjs/role_policy.json
感觉这里基于aws Web服务来写Web应用就是戴着一堆镣铐在跳舞(处处是配额限制、安全过滤等等)

写文档:new AWS.DynamoDB.DocumentClient().put(item)
Data Access and (服务器端)Validation
By expanding the conditions clause in our IAM policy,
We can only enforce rules about the type of request, where it's from, who made it, and things like that.
不能验证数据本身的格式 => 可使用Lambda来创建定制的Web服务...


用Lambda来构建微服务[编辑]

With Lambda, Amazon has jumped firmly onto the microservices bandwagon.
写一个带2个参数的函数,保存为.js,然后与其node依赖打包为zip,上传,OK。
Accessing the underlying Linux environment from your Lambda service is not only permitted but encouraged
exports.echo = function(json, context) { context.succeed(["Echo: " + JSON.stringify(json)]); }
Limits
Each Lambda function gets 512MB of ephemeral disk space on the filesystem, located at /tmp
默认并发请求:100?
a single execution cannot take more than 300 seconds

How we pay?
gigabyte-second
For example, 128MB内存、100ms运行、100,000次调用, 则125GBS, 约$0.02
Making asynchronous requests in parallel, rather than serially(程序越并行执行越节省!)

Deploy First
currently uses Node.js v4.3.2
learnjs $ ./sspa build_bundle
learnjs $ ./sspa create_service conf/lambda/functions/echo
创建IAM role learnjs_lambda_exec,以允许lambda访问DynamoDB:
$ aws --profile admin iam list-policies //列出所有预定义的策略;
$ aws --profile admin iam attach-role-policy \

--role-name learnjs_lambda_exec \--policy-arn arn:aws:iam::aws:policy/AmazonDynamoDBFullAccess

编写
LastEvaluatedKey:超过1MB响应限制(分页)——但是这次每次请求服务都会来全表scan,没有cache层吗?
learnjs $ ./sspa deploy_bundle

调用
new AWS.Lambda().invoke(params); //FunctionName: 'learnjs_popularAnswers', Payload: JSON.stringify({problemNumber: problemId})

Using the Amazon API Gateway
Add API endpoints


Serverless Security[编辑]

Securing Your AWS Account!
Disabling Any Root Access Keys
Managing Users with Profiles
Securing AWS Credentials
本地文件系统加密?OSX FileVault、Ubuntu EncryptedHome

Set Up Multifactor Authentication

Query Injection Attacks
Cross-Site Scripting(XSS)Attacks(导致向目标网站页面注入恶意JS)
Sandboxing JavaScript Using Web Workers
worker.js:postMessage(eval(e.data));

Cross-Site Request Forgery(请求伪造,泄露敏感数据)
要求请求必须是POST,且满足特定编码格式?
Cross-Origin Requests and the Same-Origin Policy(POST请求的url必须与当前origin一致)

Wire Attacks and Transport Layer Security
Sidejacking Attacks(==> 全站https?)

Denial-of-Service Attacks
Protecting S3 with CloudFront
Distributed Denial-of-Service(DDoS)
=> CloudWatch?

Next Steps
Using Signed URLs


Scaling Up[编辑]

Monitor Web Services(CloudWatch的内部原理是什么?http前端反向代理?)
Send the alert to Amazon's Simple Notification Service (SNS)
$ aws --profile admin sns create-topic --name EmailAlerts
$ aws --profile admin sns subscribe \

--topic-arn « your_topic_arn » \--protocol email \--notification-endpoint you@example.com
$ aws --profile admin cloudwatch put-metric-alarm ...

Unexpected Expenses*
Analyze S3 Web Traffic
Logging S3 Requests(创建bucket):
$ aws --profile admin s3 mb s3://learnjs-logging.benrady.com
$ aws s3api put-bucket-acl --bucket learnjs-logging.benrady.com --grant-write URI=http://acs.amazonaws.com/groups/s3/LogDelivery --grant-read-acp
URI=...
$ aws s3api put-bucket-logging --generate-cli-skeleton > conf/s3/ « your.bucket.name » /logging.json
$ aws --profile admin s3api put-bucket-logging --cli-input-json "file://conf/s3/learnjs.benrady.com/logging.json"
$ aws s3 sync s3://learnjs-logging.benrady.com/ logs

分析S3 access logs
Response Code Frequency:$ cat logs/* | cut -d ' ' -f 13 | sort | uniq -c
Popular Resources:$ cat logs/2016-01-1[123]* | cut -d ' ' -f 11 | sort | uniq -c| sort -rn
Usage by Time of Day:

$ cat logs/* | awk '{ print $3 }' | tr ':' ' ' | awk '{ print $2 ":" $3}' | sort | uniq -c

Optimize for Growth
learnjs $ aws s3api put-object --profile admin --acl 'public-read' --bucket learnjs.benrady.com --cache-control 'max-age=31536000' --key vendor.js --body public/vendor.js
不过,感觉这个针对特定url path的静态缓存策略不是很灵活啊?

Invalidating Caches with Versioned Filenames

Costs of the Cloud
... This means the loading cost of our app, including request and transfer fees, will be $0.24 for every 10,000 users
To put this in perspective, the AWS Free Tier for DynamoDB lets us perform up to 2.1 million writes per day at no cost.
Microservice Costs
the Amazon Free Tier provides for 400,000 gigabyte-seconds of execution at no cost.(但不包括额外的read capacity)

!Buying capacity based on average usage means your app will be broken half of the time.
Creating a Client Logging Web Service
捕获window.onerror,发送到CloudWatch(注意,你不能信任这些log,因为它们有可能是hacker伪造的?)
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息