编辑推荐
" w$ s4 Z. F" }4 ?. A/ E 适读人群 :本书适合对AWS感兴趣的运维人员和开发人员,尤其是那些需要将分布式应用向AWS平台迁移的运维人员和开发人员阅读。
3 l7 S( y6 L3 }% q1 p0 C7 r7 @ 物理数据中心需要很多设备,并且需要投入大量的时间和资源来管理。如果用户需要数据中心,又不希望自己搭建,AWS可能是一个不错的解决方案。无论用户是想分析实时数据,搭建软件即服务架构,还是运行一个电子商务的网站,AWS都可以为其提供可靠的基于云平台的可扩展服务。4 y: S0 U8 d, Z9 }
本书为读者介绍AWS 云平台的计算、存储和网络服务。读者将从云计算的概览开始,然后学习如何创建自己的账户,学习如何通过调用AWS API 以编程的方式控制AWS 的每个组件,以实现基础设施的自动化,学习存储数据的选项和相关技巧,学习如何使用私有网络隔离自己的系统来增强安全性。最后,本书还会教读者如何针对高可用性和容错进行设计。
# \6 ~ |+ O1 Q' J9 _3 p- n 本书主要内容
4 `8 m- H3 q) ~: H ● 云计算概念和模式的概述。5 y0 D" u& B, b1 \: _7 B
● 在AWS上部署应用程序。; [, g: \9 H) C# b
● 集成美亚的预构建服务。
6 c2 ~8 e9 j; n1 N: R& j9 |; K ● 管理EC2服务器来降低成本。) x, {7 X2 u# Q& e; i+ s
本书面向将分布式应用迁移到AWS 平台的开发者和DevOps工程师。
$ w5 ?- \% J) _7 B+ |( j% y7 r: U 内容简介
) n; @: l& X2 o2 k AWS是美亚的云计算平台,它提供了一整套基础设施和应用程序服务,可以帮助用户在云中运行几乎一切应用程序。本书介绍了AWS云平台的核心服务,如计算、存储和网络等内容。读者还可以从本书中了解在云上实现自动化、保证安全、实现高可用和海量扩展的系统架构的实践。
7 b& q( M9 ~ ~: ` L3 b* ~7 @ 本书分4个部分,共14章。本书从介绍AWS的基本概念开始,引入具体的应用示例,让读者对云计算和AWS平台有一个整体的了解;然后讲解如何搭建包含服务器和网络的基础设施;在此基础上,深入介绍如何在云上存取数据,让读者熟悉存储数据的方法和技术;然后展开讨论在AWS上如何设计架构,了解实现高可用性、高容错率和高扩展性的实践。0 n- J; a2 p' v c
作者简介: P, V7 w5 L9 V
安德烈亚斯· 威蒂格(Andreas Wittig)和迈克尔· 威蒂格(Michael Wittig)都是软件工程师,也是专注于AWS和Web开发的顾问专家。/ x3 ^. f3 ~" t4 A
目录
5 O- Z( c, O0 D6 |: X1 r. K ^ 第 一部分 AWS云计算起步
+ X1 S3 [% q; M( u& h' k' ? 第 1章 什么是Amazon Web Services 3
F* y$ C. K: R8 } 1.1 什么是云计算 4% E: z- L# D @2 d9 Z6 d
1.2 AWS可以做什么 4
- D! ]; a/ j' R$ ] 1.2.1 托管一家网店 5
6 n; d" t- X4 i0 W 1.2.2 在专有网络内运行一个Java EE应用 61.2.3 满足法律和业务数据归档的需求 7
4 [7 @6 n1 ~$ w/ f$ o6 h3 z- u 1.2.4 实现容错的系统架构 8# ] s h! }+ y$ p% K3 U C W
1.3 如何从使用AWS上获益 90 V* P) s) D5 D9 T3 _5 p5 @7 J
1.3.1 创新和快速发展的平台 9
: s: a p( d5 Y# a7 w 1.3.2 解决常见问题的服务 9' Z* L5 @- _# ?8 I5 `4 P- G
1.3.3 启用自动化 10
9 M" S' V8 I% _ 1.3.4 灵活的容量(可扩展性) 106 ~6 X% ]/ ~4 y8 V2 W
1.3.5 为失效而构建(可靠性) 11
1 J W/ d6 O& u" H" e7 t4 g 1.3.6 缩短上市的时间 11: e* x5 l- E/ c: t( _; n' @
1.3.7 从规模经济中受益 11
% ~% P( m4 r# D3 ]; I |8 {) a 1.3.8 全球化 11
4 e7 o; M' m7 F! ~' F 1.3.9 专业的合作伙伴 12- q; @, H( K* L
1.4 费用是多少 12
1 q! P# w8 ~8 d- j 1.4.1 免费套餐 12
6 K! U+ b; i* e6 O" U$ b6 J 1.4.2 账单样例 13( V ^/ f. ~3 _2 b* W+ k
1.4.3 按使用付费的机遇 14
: i* P4 P' u8 H ` 1.5 同类对比 14
; e+ G+ \3 ~8 V d; H( z 1.6 探索AWS服务 161 n9 z' J, G% M9 S Z+ @
1.7 与AWS交互 18
5 }0 K( T2 U9 K% d, @2 @+ O 1.7.1 管理控制台 19
" l! P6 a) G( o% P; u 1.7.2 命令行接口 20( v" x2 q* b: Y) {, A! r% W
1.7.3 SDK 20
" M7 X) D5 [( S) R/ D j 1.7.4 蓝图 21
/ p6 O( @& d- Q 1.8 创建一个AWS账户 22
% o! |" S! J6 N F8 Q 1.8.1 注册 22
1 d3 l* S' x# S) [ e; B% V 1.8.2 登录 26' N6 T' Y: d9 e% Y+ w" m' Q. u/ ?
1.8.3 创建一个密钥对 28
6 A& P/ t# m% y 1.8.4 创建计费告警 31
( V% C1 r# f6 _1 R, K) q 1.9 小结 317 J2 j8 ~; K, D
第 2章 一个简单示例:5分钟搭建WordPress站点 332.1 创建基础设施 33; c4 U5 R7 S8 B+ \3 F/ f8 A
2.2 探索基础设施 397 K' C1 d( B% m1 G# I; H
2.2.1 资源组 40
; Z$ G& H+ p* b j" f 2.2.2 Web服务器 41+ C/ U+ x4 L* H7 `
2.2.3 负载均衡器 425 n/ m, g% |) L V J' h7 j
2.2.4 MySQL数据库 44
0 f( Q- l3 F/ A. Y, o 2.3 成本是多少 456 a# L' q5 q6 ?2 {: s
2.4 删除基础设施 460 V7 P- ?6 m" D- n
2.5 小结 48# T5 P, I& F9 i+ l+ C, e' E
第二部分 搭建包含服务器和网络的虚拟基础设施第3章 使用虚拟服务器:
W5 W6 V- }0 B1 B$ D/ R/ Z EC2 51
# ?4 r7 g! s ? 3.1 探索虚拟服务器 51
3 X) W' T2 T' W# G 3.1.1 启动虚拟服务器 520 J+ ^7 u: u( D% |# M1 u: h
3.1.2 连接到虚拟服务器 60
9 J# q! G2 A+ P- q* k$ f 3.1.3 手动安装和运行软件 636 j' j. I" x" C9 S+ ?
3.2 监控和调试虚拟服务器 64" h- F$ u9 N L' f6 K6 L
3.2.1 显示虚拟服务器的日志 64/ T2 z# D: B" R/ K8 f- N# X
3.2.2 监控虚拟服务器的负载 65
A7 B. V, ^; ~ 3.3 关闭虚拟服务器 66* S2 O5 X4 a1 W3 [$ h
3.4 更改虚拟服务器的容量 671 }" q3 R1 p2 L% e2 Y, Q1 ?8 y
3.5 在另一个数据中心开启虚拟服务器 69. b" I9 s% K& `! q
3.6 分配一个公有IP地址 722 h! J( Z+ r/ F) m( Y7 l2 f
3.7 向虚拟服务器添加额外的网络接口 743 [$ u% x; F; b' |: |& A
3.8 优化虚拟服务器的开销 77
% t; J% i/ p5 a: |! B 3.8.1 预留虚拟服务器 78( W2 d' W3 e$ Z
3.8.2 对未使用的虚拟服务器竞价 799 S1 v r6 b# g
3.9 小结 82! \. P" R& Y# {) y9 _2 }$ {+ Z4 M
第4章 编写基础架构:命令行、SDK和CloudFormation 834.1 基础架构即代码 84
/ }& v; U z! w 4.1.1 自动化和DevOps运作 852 @4 R- X: A7 K6 x m
4.1.2 开发一种基础架构语言:JIML 85
% J& S2 x) C. _4 n! D 4.2 使用命令行接口 88* d) b% V( _, H# m5 H
4.2.1 安装CLI 88( ?9 w: v( r! L3 i4 ~4 x& i9 T+ F2 c
4.2.2 配置CLI 89" w" d6 r" w2 i
4.2.3 使用CLI 94
: U* w0 e# c3 H# N# R* L$ e& A 4.3 使用SDK编程 98 l3 D7 g/ N# X( J( B
4.3.1 使用SDK控制虚拟服务器:nodecc 98
6 P4 y; z% V+ A' o' K 4.3.2 nodecc如何创建一台服务器 99+ [# J5 Y+ _( C' ?% q5 k
4.3.3 nodecc是如何列出服务器并显示服务器的详细信息 1014.3.4 nodecc如何终止一台服务器 102
& b9 r' E$ e9 d9 k 4.4 使用蓝图来启动一台虚拟服务器 102! U8 y' z+ V; ?# w6 k
4.4.1 CloudFormation模板解析 1032 L: b/ ~; x3 A( y2 F
4.4.2 创建第 一个模板 107, d5 |) b# B' `5 i5 E1 n
4.5 小结 113( ^4 ~9 \& j: {- U8 v
第5章 自动化部署:CloudFormation、Elastic Beanstalk和OpsWorks 1145.1 在灵活的云环境中部署应用程序 115
; G, n' K+ Y/ i3 k6 l1 a, m 5.2 使用CloudFormation在服务器启动时运行脚本 1165.2.1 在服务器启动时使用用户数据来运行脚本 1165.2.2 在虚拟服务器上部署OpenSwan作为VPN服务器 1165.2.3 从零开始,而不是更新已有的服务器 1215.3 使用Elastic Beanstalk部署一个简单的网站应用 1215.3.1 Elastic Beanstalk的组成部分 1216 h' x8 }1 J) s7 e
5.3.2 使用Elastic Beanstalk部署一个Node.js应用Etherpad 1225.4 使用OpsWorks部署多层架构应用 1264 o& n9 K3 o: b7 E) d
5.4.1 OpsWorks的组成部分 1278 M' b8 J" k3 B. z. |
5.4.2 使用OpsWorks部署一个IRC聊天应用 1285.5 比较部署工具 1374 U5 e. d# v# i* _: ^8 t
5.5.1 对部署工具分类 138$ V7 L+ m( m3 P+ f8 f, t
5.5.2 比较部署服务 138
) R- Y2 G; @& K* L1 j2 \7 ~( l 5.6 小结 139
5 M4 y T Y4 G4 d; F' C9 R1 } 第6章 保护系统安全:IAM、安全组和VPC 1406.1 谁该对安全负责 1417 ~$ Z2 t8 O4 K- u) e7 B2 q
6.2 使软件保持最新 142 I% \+ n( q. A" f8 i
6.2.1 检查安全更新 142" l' e( h5 a, b
6.2.2 在服务器启动时安装安全更新 1430 y0 }) _. r. W9 g
6.2.3 在服务器运行时安装安全更新 144. a% O2 m2 B8 K" }1 m
6.3 保护AWS账户安全 145
/ g& H, Y! Q8 ?3 o3 O5 A/ l 6.3.1 保护AWS账户的root用户安全 146) z) c$ E7 v. E* s! I/ N
6.3.2 IAM服务 146% y Q0 m6 j) x' |9 l; x
6.3.3 用于授权的策略 147. j1 u( u1 A9 J
6.3.4 用于身份认证的用户和用于组织用户的组 1496.3.5 用于认证AWS的角色 150# W' g8 v0 h0 d" O8 w( |
6.4 控制进出虚拟服务器的网络流量 152* D8 \& K% L \' T' R
6.4.1 使用安全组控制虚拟服务器的流量 1536.4.2 允许ICMP流量 154+ b0 T3 v1 E# A6 [# P) s0 U
6.4.3 允许SSH流量 155* I) {& K- t' Y- l# P
6.4.4 允许来自源IP地址的SSH流量 156
7 H- D1 m* j- g8 b8 w/ W; k 6.4.5 允许来自源安全组的SSH流量 157
/ v( ]5 N, J3 v1 o 6.4.6 用PuTTY进行代理转发 159) `- ~$ q% T) _
6.5 在云中创建一个私有网络:虚拟私有云 1606.5.1 创建VPC和IGW 1627 f' m7 U( n7 C3 }0 e6 i5 Y
6.5.2 定义公有堡垒主机子网 162 n8 L+ y' _3 o3 V4 N
6.5.3 添加私有Apache网站服务器子网 164: W2 H6 k; b, P1 M; u, B1 t
6.5.4 在子网中启动服务器 165# U( k6 x1 ]6 i; ~
6.5.5 通过NAT服务器从私有子网访问互联网 1666.6 小结 1685 k: Y" M2 k$ B* t( c
第三部分 在云上保存数据
8 L9 d( ^' h5 N/ |( G+ o U 第7章 存储对象:S3和Glacierr 171$ L6 K2 t& d0 R# L
7.1 对象存储的概念 171
5 ?$ t" l. L8 v G. G 7.2 Amazon S3 172+ e' d2 o! H( F
7.3 备份用户的数据 1734 V' k' r e, h7 _1 |. v
7.4 归档对象以优化成本 175
1 Y& w# S/ D( v6 Q 7.4.1 创建S3存储桶配合Glacier使用 175
# ^, X# a: E- B) Y9 ] 7.4.2 添加生命周期规则到存储桶 176: D) ~( g1 n1 V+ o; C% b
7.4.3 测试Glacier和生命周期规则 179' K% n9 T" I0 T! K$ ` O: T
7.5 程序的方式存储对象 181( \1 G$ x% \: ]3 f5 Q4 _( p
7.5.1 设置S3存储桶 181" \. u" P2 J5 L; C
7.5.2 安装使用S3的互联网应用 182
6 n1 z+ C+ o) A0 X9 D# t+ P 7.5.3 检查使用SDK访问S3的代码 1824 a5 P# S0 M) s. m. b5 l
7.6 使用S3来实现静态网站托管 184
4 ~9 U; M) w1 w' ~/ Y0 @4 R 7.6.1 创建存储桶并上传一个静态网站 185
0 p& n( ~8 n5 H; X. ]2 l0 r4 j6 ] 7.6.2 配置存储桶来实现静态网站托管 185* U' F- d' x8 N3 m3 L4 c
7.6.3 访问S3上托管的静态网站 186
7 Z- a1 T2 X3 M3 W 7.7 对象存储的内部机制 187
4 L. h( z/ ^* e/ I 7.7.1 确保数据一致性 1876 E) u; p1 ?" Y9 x
7.7.2 选择合适的键 1886 m8 ~% _7 s; p
7.8 小结 189
7 }! L* o/ B9 p' R/ P- |" R 第8章 在硬盘上存储数据:EBS和实例存储 1908.1 网络附加存储 1918 U$ j- T. L9 o7 U, H5 {
8.1.1 创建EBS卷并挂载到服务器 191- _" K# r8 O" X1 k* W( g
8.1.2 使用弹性数据块存储 192
7 W6 B8 ^$ L6 O# _ 8.1.3 玩转性能 194
, \3 j6 n' G) @5 @" U 8.1.4 备份数据 195/ k( O8 C6 D4 m' q k0 s# a* n
8.2 实例存储 197
1 n7 l) A- t/ f- S3 r6 N8 v 8.2.1 使用实例存储 200 J9 k1 F! j9 h0 h
8.2.2 性能测试 200- }' t u9 h/ q+ v( T- p- `
8.2.3 备份数据 2011 s. h6 {) v; J0 r) n1 _
8.3 比较块存储解决方案 2015 n& Y! b: p! k) c" a8 p
8.4 使用实例存储和EBS卷提供共享文件系统 2028.4.1 NFS的安全组 203. y0 B, `9 K5 K: A7 a
8.4.2 NFS服务器和卷 204( x$ R& j K' `- g& m$ y% j
8.4.3 NFS服务器安装和配置脚本 206
8 Y+ D9 Q& k. K7 Q9 k S# z 8.4.4 NFS客户端 207
+ z4 @5 f) ^' d4 s0 @ 8.4.5 通过NFS共享文件 2087 z! \ z2 r% f1 ~# k- h
8.5 小结 209! `1 |- p9 r3 j @
第9章 使用关系数据库服务:RDS 210
& ]( L; ]6 j# }# A* l/ r7 o 9.1 启动一个MySQL数据库 212
7 g- q: k( \* O! |: N/ e 9.1.1 用Amazon RDS数据库启动WordPress平台 2129.1.2 探索使用MySQL引擎的RDS数据库实例 2159.1.3 Amazon RDS的定价 2172 A) ^& w9 C7 z6 T! u' A
9.2 将数据导入数据库 218
Z, G/ J; w# T$ B; \; r5 I 9.3 备份和恢复数据库 220
; E+ l, J- h( k( F3 U8 s 9.3.1 配置自动快照 220
) W5 @9 {* n' ]$ q2 P; M! E1 a 9.3.2 手动创建快照 2212 Y$ g5 O+ }" K' i: ~3 N
9.3.3 恢复数据库 222
) f2 F' l2 T3 A, a# J5 \7 p' l 9.3.4 复制数据库到其他的区域 223& j. p) h. ?2 C0 y1 Z( z& R0 L5 s
9.3.5 计算快照的成本 224
# t/ g& H1 E( O3 b( B6 r 9.4 控制对数据库的访问 2247 k0 D, p, G W! [
9.4.1 控制对RDS数据库的配置的访问控制 2259.4.2 控制对RDS数据库的网络访问 226
* r# X; g3 y) f% A/ N+ i 9.4.3 控制数据访问 2266 G/ g& q s$ O3 S
9.5 可以依赖的高可用的数据库 227; g: O8 t0 M; S5 d) Y
9.6 调整数据库的性能 229. w1 N p0 n4 ?- t, _3 w$ W$ }
9.6.1 增加数据库资源 2295 _. z5 s5 P; o
9.6.2 使用读副本来增加读性能 231 H+ K2 `3 h7 U- w
9.7 监控数据库 2323 B: ^; h9 Z% D2 ? d @
9.8 小结 234
( n) M5 s6 |: o/ s8 e x 第 10章 面向NoSQL数据库服务的编程:DynamoDB 23510.1 操作DynamoDB 236
! z c- t/ Y D" z. \ 10.1.1 管理 236
( _# x( h' I0 |& }5 _ 10.1.2 价格 237
# T9 d- S3 I @- \6 J 10.1.3 与RDS对比 237& d# h1 }5 i6 a
10.2 开发者需要了解的DynamoDB内容 238; s6 [3 n2 R! C: h4 H3 n+ @) b+ H* b8 x
10.2.1 表、项目和属性 238" u- y, F- z# N/ t; L
10.2.2 主键 238; _2 L& }& ?( D' Z
10.2.3 与其他NoSQL数据库的对比 239
1 X' x0 n1 z& G- t) m2 o 10.2.4 DynamoDB本地版 2391 g- d4 M$ J7 i* x5 O
10.3 编写任务管理应用程序 239
. H( ^/ q4 B5 N 10.4 创建表 241
: e; Y( w9 c/ Y1 F 10.4.1 使用分区键的用户表 242
5 y4 C/ r5 h) j {* E4 k 10.4.2 使用分区键和排序键的任务表 243# n, U! C4 y" X
10.5 添加数据 245% Z9 n2 m+ V c
10.5.1 添加一个用户 246
5 O6 X9 Q1 Z# H+ t 10.5.2 添加一个任务 247. y8 T' u+ W0 X
10.6 获取数据 2474 g3 M6 l: W6 z2 [
10.6.1 提供键来获取数据 248
% ^" Q. X2 i' e% q 10.6.2 使用键和过滤来查询 249
! s" |! M. y3 f5 t2 _. ^" i 10.6.3 更灵活地使用二级索引查询数据 25110.6.4 扫描和过滤表数据 2536 Z( g# v" T6 n3 B% I3 G
10.6.5 最终一致地数据提取 254! V( Q; j4 p; V* E
10.7 删除数据 255
S6 M8 J) G( k 10.8 修改数据 256, R- h" H; @- T) N5 F& ~
10.9 扩展容量 256
9 t* a K) z' w) r 10.10 小结 258
4 f3 h' Y' I, W' @: ? 第四部分 在AWS上搭架构
" x9 V. l7 l: z7 X! P8 b' T 第 11章 实现高可用性:可用区、自动扩展以及CloudWatch 263
1 m2 M2 \ x# F; _ 11.1 使用CloudWatch恢复失效的服务器 26411.1.1 建立一个CloudWatch告警 2666 @' W! m/ Z! k( _1 q% v" @9 ?
11.1.2 基于CloudWatch对虚拟服务器监控与恢复 26711.2 从数据中心故障中恢复 270
- |* B$ b. V- b; j: X* q( J* J 11.2.1 可用区:每个区域有多个数据中心 27111.2.2 使用自动扩展确保虚拟服务器一直运行 27511.2.3 在另一个可用区中通过自动扩展恢复失效的虚拟服务器 27711.2.4 陷阱:网络附加存储恢复 280
+ z) Z" p. G- |4 ?2 b4 N6 |, |9 V( P 11.2.5 陷阱:网络接口恢复 283
" ] D0 ]: [5 Q) ^, j4 k 11.3 分析灾难恢复的需求 287
$ u. k9 O, V' r+ } 11.4 小结 288, h* ^1 p8 r, c9 S5 F I/ w
第 12章 基础设施解耦:ELB与SQS 2894 B( c* M M) f4 O- \
12.1 利用负载均衡器实现同步解耦 290
# ^8 T% _6 z& \' s 12.1.1 使用虚拟服务器设置负载均衡器 29112.1.2 陷阱:过早地连接到服务器 2933 P. \( Y' d/ C, Z% Q* r
12.1.3 更多使用场景 2946 f, o% n: U1 g3 C( X
12.2 利用消息队列实现异步解耦 300. g8 F& [$ u& W& v: W
12.2.1 将同步过程转换成异步过程 300
3 Z7 f1 h* t4 r9 s- v 12.2.2 URL2PNG应用的架构 301' W% F; E) ]3 p; i
12.2.3 创建消息队列 302
1 h# f5 A( V4 Z% z- y L 12.2.4 以程序化的方法处理消息 302% l+ J1 a" g0 d( ]# W3 v) }- x" l
12.2.5 程序化地消费消息 3035 u" \ Z6 J3 c+ `8 O
12.2.6 SQS消息传递的局限性 306" X4 p9 s0 v/ X- L8 [+ B; k
12.3 小结 307- i) X$ X5 Y) H- {
第 13章 容错设计 308/ ^. Z1 s4 @" E% g5 v
13.1 使用冗余EC2实例提高可用性 310" x' C% f) u: h5 `- N( K- C( ?$ Z3 [
13.1.1 冗余可以去除单点故障 310
" P' x- K/ B& `5 ]6 A 13.1.2 冗余需要解耦 312* n' s/ ?1 m- \0 O4 Z
13.2 使代码容错的注意事项 313
, G+ \ E1 y# y2 Y6 h0 X& R6 ^ 13.2.1 让其崩溃,但也重试 313 E( ^& h) `+ o9 @
13.2.2 幂等重试使得容错成为可能 314
3 w$ W+ Y4 H* h 13.3 构建容错Web应用:Imagery 3162 ~( n, r' ?( ?9 ~: R
13.3.1 幂等图片状态机 318
1 ~, g+ L$ y" F/ ]/ f 13.3.2 实现容错Web服务 320" P; E( X" v- t3 X
13.3.3 实现容错的工作进程来消费SQS消息 32613.3.4 部署应用 329
6 S; Z3 J9 R& i, g( @ 13.4 小结 336
: j7 `4 T9 K& s. ] 第 14章 向上或向下扩展:自动扩展和CloudWatch 33714.1 管理动态服务池 339
; j* n U# m( |, I 14.2 使用监控指标和时间计划触发扩展 34214.2.1 基于时间计划的扩展 343. F" ^) c1 v4 g0 J# p5 H+ w
14.2.2 基于CloudWatch参数的扩展 344/ f6 C; s% P, Q$ q
14.3 解耦动态服务器池 347! B0 O" g/ S* M5 U$ k+ E# p* i
14.3.1 由负载均衡器同步解耦扩展动态服务器池 34914.3.2 队列异步解耦扩展动态服务器池 35414.4 小结 356# _( p4 ]: S" x& @
7 e) N w8 b# z
4 D9 l& n: R8 m8 U# F3 I |