什么是 server
在开始打造自己的服务器之前, 我们首先明确一下服务器的定义: 一个管理资源并为用户提供服务的计算机软件
根据功能服务器分为两类
static web server, 例如常见的 nginx apache 等等
dynamic web server, 例如常见的 tomcat,jboss,resin 等等
动静态服务器区别
对于静态服务器来说一般就是读取资源然后返回给 browser; 动态服务器意味着返回给 browser 的文件是经过逻辑处理动态产生的
服务器具有的功能特性
nginx,tomcat 这个两个之前用过, 也研究过, 所以拿这两个举一下示例, 不过现在很少用了, 现在基本上都是使用 node 相关的, 所以最后构建的 serve 会基于 node
nginx
nginx 特点
配置简单, 灵活 (只有一个主配置文件 nginx.conf)
支持高并发 (静态小文件)
占用资源相对较少 (2w 并发, 开启 10 个线程, 内存消耗只有几百 M)
功能种类多 (例如 proxy,cache,Log,Gzip 等等)
nginx 应用场景
静态服务器 (图片, js,CSS 等等)
- server {
- listen 8080;
- server_name localhost;
- location / {
- root html;
- index index.html index.htm;
- }
- }
说明 上面是 nginx 配置, 指定访问根目录和默认主页, 以及监听端口
- ~ clear
- ~ curl -i http://127.0.0.1:8080
- HTTP/1.1 200 OK
- Server: nginx/1.12.2 // 服务器类型和版本
- Date: Fri, 02 Mar 2018 08:49:44 GMT
- Content-Type: text/html
- Content-Length: 11
- Last-Modified: Fri, 02 Mar 2018 08:46:27 GMT // 支持 Last-Modified 缓存机制
- Connection: keep-alive // 支持持久连接
- ETag: "5a990f63-b" // 支持 ETag 缓存机制
- Accept-Ranges: bytes // 支持断点续传
- hello jsdt% // 响应体
说明 上面是本地测试请求, 从响应头中可以看到支持很多功能
反向代理, 负载均衡
说明 上面是试验效果
- upstream jsdt.com {
- server 127.0.0.1:8083 max_fails=3 fail_timeout=30s weight=1;
- server 47.97.xxx.xxx:8084 max_fails=3 fail_timeout=30s weight=2; // 为了安全 隐藏真实 ip 地址
- }
- server {
- listen 8080;
- server_name localhost;
- location / {
- root html;
- # index index.html index.htm;
- proxy_pass http://jsdt.com;
- proxy_redirect default;
- }
- error_page 500 502 503 504 /50x.html;
- location = /50x.html {
- root html;
- }}
说明 上面我摘取了关键的部分配置, 采用了轮训 + weight 算法, 其它还有 ip_hashurl_hash 等算法真实的应用情况, 还需要考虑很多问题, 例如集群的 session 同步, 记得大学实习期间, 当时公司用的是 cookie+memcache 集群的方案
tomcat
tomcat 特点
tomcat 运行在 jvm 上, 跨平台, 是一个 Servlet 容器 (可以运行 Servlet, 编译 jsp), 实现了在 http 请求响应处理中所需要的 http 接口相关实现类除此之外也支持虚拟主机, session 共享, 静态文件处理等等, 只不过没那么专业而已
tomcat 应用场景
说明 如上所示, 我们可以在页面中添加动态的处理逻辑, 返回的数据根据用户可定制化 (相比静态服务器优点), 最终. jsp 被 tomcat 编译为. java, 然后被 javac 编译为通用字节码文件, 最终运行在 jvm 上
如何实现一个自己的服务器
在实现自己的服务器之前, 首先我们明确一下 server 的本质, server 属于应用层的协议, 基于 tcp 的封装, 而 tcp 的应用实现是基于 socket(无论是 node, 还是 java 都有 socket) 的封装
socket 监听某个端口, 获取面向流的数据 data, 我们的 server 所要做的就是对 data 进行解析封装, 以使其符合 http 的规范
接下来实现自己的静态 server
因为有 http 模块, 所以 node 当中实现一个基础 server 很简单但是如果附加额外的功能, 例如压缩, 缓存, 断点续传, 反向代理什么的就需要自己添加了
接下来首先看一下项目结构, bin 目录主要是放启动脚本相关的, 主逻辑在 app.js 中, 然后根据功能将代码拆分成不同的模块 templatet 目录放置编译的原始模板
- |____bin
- | |____.DS_Store
- | |____deamon.js
- | |____start
- | |____yargsConfig.js
- |____node_modules
- |____package-lock.json
- |____package.json
- |____readme.md
- |____src
- | |____.DS_Store
- | |____app.js
- | |____asset
- | |____cacheSupport.js
- | |____config.js
- | |____picGuard.js
- | |____template
- | |____util.js
在 server 运行前, 首先我们通过 yargs 模块获取解析好的命令行参数如下所示
- if(argv.D){
- let sp = cp.spawn(process.execPath, ['deamon.js'],{
- cwd: __dirname,
- stdio: ['ignore','ignore','ignore'],
- env: argv,
- detached: true //http://nodejs.cn/api/child_process.html#child_process_child_process_spawn_command_args_options
- } )
- sp.unref()
- } else {
- let config = Object.assign({}, defautConfig, argv)
- let server = new Server(config);
- server.start();
- console.log('server already started')
- }
说明 如果开启 deamon 模式, 则通过子进程的方式让服务在后台运行, 反之则直接启动 server 实例
在启动 server 之后, 开始接受并处理请求, 下面以断点续传功能模块作为示例
- function byteRangeStream(req, res, filepath, statObj) {
- let start = 0
- let end = statObj.size-1
- let range = req.headers['range']
- if (range){
- res.setHeader('Accept-Range','bytes')
- res.statusCode = 206 //a part of content
- let result = range.match(/bytes=(\d*)-(\d*)/);
- if (result) {
- start = isNaN(result[1]) ? start : parseInt(result[1]);
- end = isNaN(result[2]) ? end : parseInt(result[2]) - 1;
- }
- }
- return fs.createReadStream(filepath,{
- start,
- end
- })
- }
- module.exports ={
- byteRangeStream
- }
说明 在主模块 app.js 中, 导入上述模块, 如代码中所示首先判断客户端是否支持断点续传, 依据 range 请求头, 如果有请求范围, 直接返回请求范围内的数据, 否则全部读取返回, 靠的是 browser 和 server 的协商机制, 需要双方都支持才能完成整个过程
更多功能模块可以参考我的 github, 欢迎 star
总结
写这篇文章, 总结了下 server 的相关知识, 参考了之前大学时做的笔记, 看到之前做的记录, 回忆当时在学校学习和公司实习的经历, 感慨万千时光易逝, 做好当下的自己
来源: https://segmentfault.com/a/1190000013501940