整体框架
1.复习http
2.部署HTTP服务端 nginx
3.部署HTTP服务端 workflow(第三方框架)
4.使用workflow实现一个网盘,基于HTTP协议
5.实现备份 阿里云OSS 或者 ceph
6.消息队列 rabbitmq(erlang) 跨越网络进行通信
7.容器 轻量级的虚拟机 docker
8.RPC 远程过程调用 —》微服务
9.注册中心 consul
10.搜索引擎项目
HTTP
HTTP超文本 协议头是一个文本协议,让网络通信用起来像函数调用一样,让客户端对应主调函数,客户端对应被调函数,请求被看作参数,响应被看作返回值。
http协议的性质
两个http事务在应用层面没有关联
REST设计方案
目标:让HTTP接口用起来像函数调用一样
函数调用分为三部分
1.对象(path,URI的路径) 找到它的成员函数:增(POST)删(DELETE)改(PUT)查(GET)
2.传递参数 报文体(序列化:把复杂二点数据结构转换为普通的字节流(方案:xml/json))
3.收返回值 报文体 (同样需要序列化(XML/json))
POST和PUT的区别
幂等性:出现了多次重复请求如果结果还是一致的说明是幂等的。
想要实现幂等性,,要有唯一id,不同的请求有不同的id,重复的请求有重复的id
实现方式:
MySQL/redis 自动增长id
雪花算法:支持在大规模分布式系统中生成唯一id
使用HTTP服务端的三种方案
从berkeley Socket库开始开发:修改reactor:1.把eadLine改为read报文 2.httpparser
使用现成的产品,Apache Httpd/nginx
基于框架来写,boost.asio(支持两种范式:1.异步回调 2.协程) 再在这个基础上构建boost.beast
brpc(协程方案)
workflow(异步回调模型)
nginx vs apache httpd
apache是一种基于线程的模型
代码框架
1 2 3 4 5
| while(1){ accpet; pthread_create/fork; TODO; }
|
特点:
- 业务开发很简单,但是效率低
- 内存:每一个连接都要占用一个线程,内存占用率很高,低并发
- CPU:线程数太多,大量的时间进行上下文切换
- 调度是内核态
nginx特点
nginx是事件驱动模型,使用进程池
好处:
- 高并发量:内存,一个线程占有多个文件对象(IO多路复用 select/epoll)
- 单线程:没有上下文切换
- 调度是用户态的
缺点:
1 2 3 4 5 6 7
| while(1){ select/epoll --wait; switch(fd){ case fd: hander(fd); } }
|
- 要把一个完整的业务拆分一个个的事件,开发效率低
两者的使用场景
业务简单,并发量高 事件驱动
- 静态资源,把磁盘的内容原封不动的返回给客户端
- 代理
业务复杂,并发量低 基于进程/线程
一般组合使用(反向代理服务器:更靠近客户端的叫正向代理,靠近服务器的叫反向代理):

nginx可以放缓存,如果请求来的话,请求的资源如果存在就直接返回,否则就交给业务服务器(apache,tomcat)
也可以做负载均衡
如何使用nginx
安装
启动(安装完成会自启动)
配置文件目录
当前工作目录(配置文件的搜索的起始目录)
查看系统已经存在的nginx进程
关掉已经存在的nginx进程
启动nginx进程(不可在普通用户下执行)
切换到root下执行
1
| nginx -c /etc/nginx/nginx.conf
|
更改配置文件
1
| vim /etc/nginx/nginx.conf
|
- worker_process 1 工作进程的个数
- user 用户名
- events{ … } 单次可以处理epoll的个数
- http{…}
- listen 是监听的端口号
- server_name 主机名
- location()
重启nginx服务端指令
1 2
| nginx -s quit -c /etc/nginx/nginx.conf nginx -c /etc/nginx/nginx.conf
|
部署一个产品文档
下载文档,存在服务端的某个目录下
- 更改nginx.conf 的http下的root 后面换成资源路径(表示从这个资源路径开始寻找文件)
把起始目录作为root的参数
重启nginx
访问的时候,只需要再root后面资源路径加上资源的路径,就可以拼接出来一个完整的路径
网盘系统

反向代理和负载均衡
反向代理:
1.轮询 不能用缓存
2.哈希 最后一下取余数会导致缓存大面积失效
3.环形哈希 一致性哈希

框架
1.事件驱动模型 reactor libevent libev libhv
recv用户代码去完成
2.异步回调 和事件驱动的区别在于 操作系统会把recv也做好,而事件驱动不做
并且操作系统recv的时候,主线程不会阻塞,会去干其他的事情,有一个执行流等待数据,等数据取完并执行完相关的业务以后(回调函数)再告知主线程
3.协程(随之可以暂停和恢复的函数)例如有好几个recv,如果执行阻塞的时候就直接切换函数,但只能处理io密集型,不能做cpu密集型
workflow框架
是一个异步回调框架
业务的最小单元:任务 recv+hanlder

使用workflow的一般流程
- 将任务分解成任务 类型,handler
- 创建任务,设置任务的属性以及不同任务之间的关联
- 将任务交给框架(异步)
安装workflow
1.解压
2.进入解压好的文件夹,可以看到使用cmake代码构建系统
1 2 3 4
| sudo apt install cmake mkdir build cd build/ cmake ..
|
cmake ..可能会报错
1
| sudo apt install lib库 -dev
|
3.编译&链接
4.安装
1 2
| sudo make install /user/local/include/ sudo make install /user/local/lib/
|
5.sudo ldconfig
6.验证库是否安装成功
1
| g++ *.cc -o 可执行文件名 -lworkflow
|
使用第三方库的编程
1.头文件(*.cc *.h) 编译
2.库文件(*.so/*.a) 链接
waitGroup
让某一个线程处于等待状态,等待n件事情发生
里面要用到三个函数
1 2 3
| WaitGroup(int n); void done(); void wait();
|
示例
1 2 3 4 5 6 7
| #include <workflow/WFFailities.h>
static WFFacilities:WAitGrou waitGroup(1);
int main(){ waitGroup.wait(); }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| #include <workflow/WFFailities.h> #include <signal.h> static WFFacilities:WAitGrou waitGroup(1);
void sigHandler(int signum){ waitGroup.done(); }
int main(){ signal(SIGINT,siganlder); waitGroup.wait(); return 0; }
|
客户端任务
任务执行流程:发请求,收响应,解析响应–>callback
前三部分由框架实现,基本工作,后面的callback用户自定义的回调函数
HTTP类型的任务
http任务是使用工厂函数创建的,不直接使用构造函数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| #include <workflow/WFFailities.h>
#include <workflow/WFTaskFactory.h> #include <signal.h> static WFFacilities:WAitGrou waitGroup(1);
void sigHandler(int signum){ waitGroup.done(); }
int main(){ signal(SIGINT,siganlder); WFHttpTask * httpTask = WFTaskFactory::create_http_task( "http://www.baidu.com", 10, 10, nullptr); waitGroup.wait(); return 0; }
|
创建好任务以后要交给框架(启动任务,由框架进行调度)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| #include <workflow/WFFailities.h>
#include <workflow/WFTaskFactory.h> #include <signal.h> static WFFacilities:WAitGrou waitGroup(1);
void sigHandler(int signum){ waitGroup.done(); }
int main(){ signal(SIGINT,siganlder); WFHttpTask * httpTask = WFTaskFactory::create_http_task( "http://www.baidu.com", 10, 10, nullptr); protoco::HttpRequest *rq = httpTask->get_req(); req->add_header_pair("ky1","value1"); httpTask->start(); waitGroup.wait(); return 0; }
|
交给框架以后:
- 检查一下,资源是否就绪
- 任务的基本工作:建立连接–发送请求–收响应–解析响应(对于所有的http任务都是一样的,框架代码处理)
- 收到响应之后,执行用户设计的代码(回调函数,可以读请求和响应,可以根据响应执行后续的代码)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36
| #include <workflow/WFFailities.h>
#include <workflow/WFTaskFactory.h> #include <signal.h> static WFFacilities:WAitGrou waitGroup(1);
void sigHandler(int signum){ waitGroup.done(); } void httpCallBack(WFHttpTask *httpTask){ protocol::HttpRequest *req = httpTask->get_req(); cout << req->get_method() << '\n'; cout << req->get_request_uri() << '\n'; cout << req->get_http_version() << '\n'; protocol::HttpResponse *resp = httpTask->get_resp(); cout << resp->get_ttp_version() << '\n'; cout << resp->get_status_code() << '\n'; cout << resp->get_resaon_phrase() << '\n'; }
int main(){ signal(SIGINT,siganlder); WFHttpTask * httpTask = WFTaskFactory::create_http_task( "http://www.baidu.com", 10, 10, nullptr); protoco::HttpRequest *rq = httpTask->get_req(); req->add_header_pair("ky1","value1"); httpTask->start(); waitGroup.wait(); return 0; }
|
如果网址错误,那么请求就会失败,所以要检查报错
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65
| #include <workflow/WFFailities.h>
#include <workflow/WFTaskFactory.h> #include <signal.h> static WFFacilities:WAitGrou waitGroup(1);
void sigHandler(int signum){ waitGroup.done(); } void httpCallBack(WFHttpTask *httpTask){ int state = httpTask->get_state(); int error = httpTask->get_error(); switch(state){ case WFT_STATE_SYS_ERROR: cerr << "system error:" << strerror(error) << "\n"; break; case WFT_STATE_DNS_ERROR: cerr << "DNS error:" << gai_strerror(error) << '\n'; break; case WFT_STATE_SSL_ERROR: cerr << "SSL error :" << error << '\n'; break; case WFT_STATE_TASK_ERROR: cerr << "Task error :" << error << '\n'; break; case WFT_STATE_SUCCESS: break; } if(state != WFT_STATE_SUCCESS){ cerr << "Failed. Press Ctrl-C to exit.\n"; return; } protocol::HttpRequest *req = httpTask->get_req(); cout << req->get_method() << '\n'; cout << req->get_request_uri() << '\n'; cout << req->get_http_version() << '\n'; protocol::HttpResponse *resp = httpTask->get_resp(); cout << resp->get_ttp_version() << '\n'; cout << resp->get_status_code() << '\n'; cout << resp->get_resaon_phrase() << '\n'; }
int main(){ signal(SIGINT,siganlder); WFHttpTask * httpTask = WFTaskFactory::create_http_task( "http://www.baidu.com", 10, 10, nullptr); protoco::HttpRequest *rq = httpTask->get_req(); req->add_header_pair("ky1","value1"); httpTask->start(); waitGroup.wait(); }
|
处理首部字段和报文体
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78
| #include <workflow/WFFailities.h>
#include <workflow/HttpUtil.h> #include <workflow/WFTaskFactory.h> #include <signal.h> static WFFacilities:WAitGrou waitGroup(1);
void sigHandler(int signum){ waitGroup.done(); } void httpCallBack(WFHttpTask *httpTask){ int state = httpTask->get_state(); int error = httpTask->get_error(); switch(state){ case WFT_STATE_SYS_ERROR: cerr << "system error:" << strerror(error) << "\n"; break; case WFT_STATE_DNS_ERROR: cerr << "DNS error:" << gai_strerror(error) << '\n'; break; case WFT_STATE_SSL_ERROR: cerr << "SSL error :" << error << '\n'; break; case WFT_STATE_TASK_ERROR: cerr << "Task error :" << error << '\n'; break; case WFT_STATE_SUCCESS: break; } if(state != WFT_STATE_SUCCESS){ cerr << "Failed. Press Ctrl-C to exit.\n"; return; } protocol::HttpRequest *req = httpTask->get_req(); cout << req->get_method() << '\n'; cout << req->get_request_uri() << '\n'; cout << req->get_http_version() << '\n'; protocol::HttpHeaderCursor reqCursor(req); string key,value; while(reqursor.next(key,value)){ cout << key << value << '\n'; } protocol::HttpResponse *resp = httpTask->get_resp(); cout << resp->get_ttp_version() << '\n'; cout << resp->get_status_code() << '\n'; cout << resp->get_resaon_phrase() << '\n'; protocol::HttpHeaderCursor reqCursor(resp); while(reqursor.next(key,value)){ cout << key << value << '\n'; } }
int main(){ signal(SIGINT,siganlder); WFHttpTask * httpTask = WFTaskFactory::create_http_task( "http://www.baidu.com", 10, 10, nullptr); protoco::HttpRequest *rq = httpTask->get_req(); req->add_header_pair("ky1","value1"); httpTask->start(); waitGroup.wait(); }
|
workflow框架里面 http报文头和报文体是不再一起的
获取报文体
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83
| #include <workflow/WFFailities.h>
#include <workflow/HttpUtil.h> #include <workflow/WFTaskFactory.h> #include <signal.h> static WFFacilities:WAitGrou waitGroup(1);
void sigHandler(int signum){ waitGroup.done(); } void httpCallBack(WFHttpTask *httpTask){ int state = httpTask->get_state(); int error = httpTask->get_error(); switch(state){ case WFT_STATE_SYS_ERROR: cerr << "system error:" << strerror(error) << "\n"; break; case WFT_STATE_DNS_ERROR: cerr << "DNS error:" << gai_strerror(error) << '\n'; break; case WFT_STATE_SSL_ERROR: cerr << "SSL error :" << error << '\n'; break; case WFT_STATE_TASK_ERROR: cerr << "Task error :" << error << '\n'; break; case WFT_STATE_SUCCESS: break; } if(state != WFT_STATE_SUCCESS){ cerr << "Failed. Press Ctrl-C to exit.\n"; return; } protocol::HttpRequest *req = httpTask->get_req(); cout << req->get_method() << '\n'; cout << req->get_request_uri() << '\n'; cout << req->get_http_version() << '\n'; protocol::HttpHeaderCursor reqCursor(req); string key,value; while(reqursor.next(key,value)){ cout << key << value << '\n'; } protocol::HttpResponse *resp = httpTask->get_resp(); cout << resp->get_ttp_version() << '\n'; cout << resp->get_status_code() << '\n'; cout << resp->get_resaon_phrase() << '\n'; protocol::HttpHeaderCursor reqCursor(resp); while(reqursor.next(key,value)){ cout << key << value << '\n'; } const void *body; size_t size; resp->get_parsed_body(&body,&size); cout << static_cast<const har *>(body) << '\n'; }
int main(){ signal(SIGINT,siganlder); WFHttpTask * httpTask = WFTaskFactory::create_http_task( "http://www.baidu.com", 10, 10, nullptr); protoco::HttpRequest *rq = httpTask->get_req(); req->add_header_pair("ky1","value1"); httpTask->start(); waitGroup.wait(); }
|