seho 发布的文章

Nginx是一款高性能服务器,最近这几年非常火,以轻量且高并发,高性能著称,那么此笔记将不会从0开始讲解API,而是会从各种问题入手,通过问题学习nginx。


特点:

  1. IO多路复用
  2. 高性能
  3. 高并发
  4. 占用系统资源少

Untitled.png

Nginx作为一个WEB服务器,有着大好的未来,市场份额非常给力,同时也是份额上升速度最快的web服务器。

Untitled.png

Nginx作为前端来说,需要学习什么?我们只需要学习Nginx在应用部署,反向代理,处理资源的进程,亦或者是搭建网站的基础知识,如果你还没有一个blog,那么就从现在开始学习nginx并且搭建你的第一个网站吧。


反向代理与正向代理

Untitled.png

我们在平时上外网的时候,比如谷歌,youtube,twitter,Ins等,如果使用我们内地网络,是访问不成功的,只有在香港台湾或者境外才能访问到类似的外网。那我们需要通过内地网络去访问外网只能通过一个proxy代理去做一个请求的转发,我们的内地网络请求在到达外网地址之前,会经过一层代理,这个代理会去请求外网,请求成功之后会把页面呈现给我们的客户端。

在这个过程中,外网服务器不知道我们的内地网络是谁,只知道代理地址,所以对于外网服务器来说,请求的真实客户端是看不到的。那么这个过程就叫做 正向代理,proxy代理的是客户端。

Untitled.png

反向代理是相反的,代理的是服务端,对于客户端而言,访问的服务器仅仅是多个真实服务器的一个代理而已,所以对于客户端用户而言,真实服务器的信息是不可见的。这样的过程也就是反向代理,proxy代理的是服务端

Nginx如何去做反向代理?

server{
        listen 80;
        server_name nginx.yinzhuoei.com;
        location / {
               proxy_pass http://yinzhuoei.com;
        }
}

其他的proxy配置:

proxy_set_header :在将客户端请求发送给后端服务器之前,更改来自客户端的请求头信息。
proxy_connect_timeout:配置Nginx与后端代理服务器尝试建立连接的超时时间。
proxy_read_timeout : 配置Nginx向后端服务器组发出read请求后,等待相应的超时时间。
proxy_send_timeout:配置Nginx向后端服务器组发出write请求后,等待相应的超时时间。
proxy_redirect :用于修改后端服务器返回的响应头中的Location和Refresh。

解决跨域

通过反向代理解决跨域:

server
{
   listen 3003;
   server_name localhost;
      ##  = /表示精确匹配路径为/的url
   location = / {
       proxy_pass http://localhost:5500;
   }
   ##  若 proxy_pass最后为/ 如http://localhost:3000/;匹配/no/son,则真实匹配为http://localhost:3000/son
   location /no {
       proxy_pass http://localhost:3000;
   }
   ##  /ok/表示精确匹配以ok开头的url,/ok2是匹配不到的,/ok/son则可以
   location /ok/ {
       proxy_pass http://localhost:3000;
   }
}

加header头允许跨域:

server
{
    listen 3002;
    server_name localhost;
    location /ok {
        proxy_pass http://localhost:3000;

        #   指定允许跨域的方法,*代表所有
        add_header Access-Control-Allow-Methods *;

        #   预检命令的缓存,如果不缓存每次会发送两次请求
        add_header Access-Control-Max-Age 3600;
        #   带cookie请求需要加上这个字段,并设置为true
        add_header Access-Control-Allow-Credentials true;

        #   表示允许这个域跨域调用(客户端发送请求的域名和端口) 
        #   $http_origin动态获取请求客户端请求的域   不用*的原因是带cookie的请求不支持*号
        add_header Access-Control-Allow-Origin $http_origin;

        #   表示请求头的字段 动态获取
        add_header Access-Control-Allow-Headers 
        $http_access_control_request_headers;

        #   OPTIONS预检命令,预检命令通过时才发送请求
        #   检查请求的类型是不是预检命令
        if ($request_method = OPTIONS){
            return 200;
        }
    }
}

Master&Woker模式

Nginx启动之后,启动了80端口进行服务监听,那么进程中就存在一个Mater主进程和多个Woker进程;

Untitled.png

Master进程的作用就是:读取&验证nginx.conf配置文件并且管理多个woker进程;接受外部信号;监控Woker,如果Woker挂掉,将自动重启Woker;

Woker进程的作用就是:多个Woker会拦截所有的请求并做出处理;每一个woker进程维护一个线程;woker的个数和CPU有关,从nginx.conf配置woker个数,配置几个就是几个,但是要避免配置过多,要充分利用CPU;

一个请求到响应的流程:

  1. Nginx启动,Matster进程根据nginx.conf初始化;初始化监听socket;fork出多个woker进程;
  2. 发起请求
  3. woker进程们一起竞争,胜出者通过三次握手,建立socket连接,处理请求。

如何做热部署呢?

热部署就和前端热部署一样的性质,即修改配置文件,不需要重启服务器就可以使用最新的配置。

nginx -s reload

通过这样的一个命令即可热部署,无需重启,随时改随时用。

一般情况下,我们做热部署可以有几个方案,比如前端,webpack的本地开发工具,webpack-dev-server,即本地启动一个服务,开启一个websocket,当我们的文件改动,就重新加载这个css/js。

而nginx也是同样的方式么?我们的主进程master去发布一个修改请求,然后woker去订阅这个消息,实现类似这样的热部署?

其实不然,nginx使用的是如下的方案,当master监听到配置文件的更改,会创建一批新的woker去执行新的请求,老的woker进程会在任务处理完毕之后,再由master杀掉进程。


如何做到高并发?

Nginx采用多进程+异步非阻塞方式(IO多路复用):

关于异步和同步,我需要做一些概念上的整理;

同步和异步指的是消息的通信机制,我们做web开发是最能理解同步异步的区别的,因为我们天天和接口打交道;

1)所谓同步指的就是发起一个请求/调用,在没有得到结果之前就不会返回,一旦得到结果就立即返回;

2)所谓异步指的就是发起一个请求/调用,调用者不会主动去care被调用者,而被调用者拿到结果之后会通知调用者

而阻塞非阻塞指的是程序在等待调用结果时的状态

1)阻塞调用指的就是,结果返回之前当前线程被挂起,调用线程在返回之后才返回;那么挂起的这个线程是会被阻塞的;

2)非阻塞调用指的就是,不能立刻得到结果之前,线程是不会被挂起的,仍然可以做其他事情;那么非阻塞调用如何知道得到结果了呢,需要定时去check的;

关于阻塞IO和非阻塞IO等我总结完了再说哈,还有关于Nginx的IO多路复用Epoll模型,这个是延申知识了,我也需要学习整理哈,现在还不清楚这一块的东西。

Nginx后续章节过段时间发,中间要发几篇shadowDom和剑指题解的文章,大家耐心等待...


学习资料如下:

nginx如何做到高并发?
8分钟带你深入浅出搞懂Nginx
理解同步异步&阻塞非阻塞

微信截图_20210331210548.png

微信截图_20201212173122.png
最近一直在调研unicloud云函数开发,所以一直想给js加入类型推导,这篇文章就记录一下我是如何开发TS版本的云函数的吧。

  1. 确定使用TS还是Flow.js

Flow.js在基本语法上和TS很相像,我认为它是一个针对老项目的类型推导方案,因为只需要安装简单的包和给文件加入Flow的标识就可以给对应的文件提供类型推导的功能,所以针对目前我所做的这个业务来讲,不存在老项目,所以既然是新项目就不如直接上TS。

在云函数开发过程中,我们在插件市场选择了一款非常简洁已拓展的explain框架,这个框架目前已经支持单路由和restfulAPI还有基本的过滤拦截器,那么目前这个框架没有做TS的解决方案,我就斗胆替作者大大想一个曲线救国的方案,而且这个方案有以下特点:

  1. TS的编译速度比官方提供的TSC编译要快几十倍到上百倍
  2. 支持重新加载打包
  3. 云函数项目0依赖就可以玩转这套方案

uni官方的云函数大小限制是10M,所以我们不能把依赖都安装在项目中,需要我们全局安装:

npm i -g typescript esbuild-node-tsc nodemon
  1. esbuild-node-tsc是基于esbuild的ts编译器,大名鼎鼎的esbuild由于其出色的编译特性,能够让我们在大型项目中编译ts速度更快。
  2. nodemon帮助我们在文件变更时重新编译ts

2个插件的玩法很多,尤其是nodemon,在我们这个解决方案中我们只需要简单的配置几个文件就可以把我们的项目跑起来了。

微信图片_20201212171411.png

我们的云函数目录是这样的,这是搭配了前面提到的explain.js,在etsc.config.js中我们可以配置一下,输出的js版本规范以及目录和是否进行压缩:

module.exports = {
  outDir: "./dist",
  esbuild: {
    minify: true,
    target: "es2015",
  },
  assets: {
    baseDir: "services",
    filePatterns: ["**/*.json"],
  },
};

在services目录中编写完ts文件之后,esbuild-node-tsc会把js文件放到dist目录之下,我们现在只需要更改explain.js默认配置:

config.init({
  baseDir: __dirname,
  serviceDir: "/dist/"
});

这样explain会从dist下找文件而不是从services文件下找

我们在这个根目录下运行编译命令即可

etsc

这个时候我们运行这个函数就会发现,它已经达到了我们的目标了:

  1. 开发使用了ts
  2. 可以正常的跑通和上传云函数

但是我们需要services下的文件一变更就编译放到dist下,我们就需要nodemon帮助我们做这个事情(nodemon.json):

{
    "watch": ["services"],
    "ext": "ts,js,json",
    "exec": "etsc",
    "legacyWatch": true
  }

监听services目录,包括文件名为ts,js,json,执行命令etsc

然后我们再把这个运行nodemon的命令放到package.json中:

"scripts": {
    "dev": "nodemon"
  },

这样我们就可以启动nodemon这个监听服务,可以很爽的使用ts来开发云函数啦~~
微信截图_20201212172316.png

微信截图_20210331210548.png

从0开始理解Vite的主要新特性(一)

vite这个工具确实尤大在微博上造势很猛,又以各种骚操作着实火了一把,那我们今天就一起了解一下vite吧~



我们可以从vite仓库的readme可以看到安装vite非常的方便

// https://github.com/vitejs/vite
npm init vite-app <project-name>
cd <project-name>
npm install
npm run dev

我们在本地环境运行之后可以看到这样的页面,就说明我们可以开始使用了。




我们首先要理解vite的工作原理,它为什么这么快?



当我们打开工程的index.html的时候,我们可以发现script的type是module

<body>
  <div id="app"></div>
  <script type="module" src="/src/main.js"></script>
</body>


如果你不了解script标签中的module是什么意思,那么MDN解释说如果标示了module的话会把代码当作js模块来执行,一篇关于es6的文章也很好的介绍了:

https://developer.mozilla.org/zh-CN/docs/Web/HTML/Element/script

如果你熟悉es6的模块概念,模块仅仅就是普通的含有js代码的文件而已,我们可以用importexport关键字对变量,对象的导出和导入,而这个机制在高级浏览器已经完全实现了。



不得不说,尤大不仅是技术上的大神,而且富有创造和想象力。让vite变成了一个0捆绑的开发服务器,利用浏览器高级特性让开发体验变得更好

当浏览器解析保留了模块关键字的代码,从而会导致HTTP请求,vite通过koa拦截了这些请求:

.vue => 拦截请求 => 编译 => 返回给客户端

那么没有打包的vite和老东家的基于打包的vue-cli就有了一些明显的优势了:

1. vite利用了客户端能力不用打包其他服务,原生的ES Import直接输出提高了冷启动速度。

2. vite按需编译当前页面需要的组件,而不需要打包整个APP的组件,这样的提升对比cli无疑是项目越大速度差距越大。

3. HMR更新速度不会和模块数量牵扯,vite会让HMR一如既往的保持快速。

使用vite开发应用可能在前期除了启动速度,其他功能是要等到应用慢慢变大才能真实的感受vite的强大。


TS支持

vite内置TS的支持,开箱即用:

<script lang="ts">
import { ref } from "vue";

export default {
  name: 'App',
  setup(){
    let hello = ref<string>("1");
    console.log(hello.value);
  }
}
</script>

值得一提的是,内置的TS不是TS官方出的tsc cli,而是之前就听说过的ESBuild,现在vite的TS支持是ESBuild也不奇怪,毕竟是要一快到底么。

——ESBuild的ReadMe说了一句振奋人心的话

实际上,我们目前的网站构建工具比实际速度慢10-100倍



为什么ESbuild这么快?

  • 因为Esbuild是用GO语言直接编译成原生代码
  • 由于GO语言特点,它的解析和映射并发非常快
  • 避免了不必要的配置
  • 数据转换很简单很快速

虽然ESbuild是一个非常快速的打包器,但是不支持热模块更新和没有开箱即用的工具,而且要像webpack一样想做一款基于ESbuild的插件,我认为目前是非常难的。所以Vite将它的长处用在了处理ts编译上,大型项目中编译TS文件,Vite几乎是一瞬间的事情。


热模块更新

我们要知道热模块更新和我们传统的刷新页面的区别,以webpack的dev-server服务器举例,通过启动开发服务器,页面与服务器建立了websocket,我们修改了代码之后给页面发送消息,页面才会执行刷新命令,本质上这种live-reload机制已经对开发非常友好了,但是在带有状态的页面上,reload不会有更好的开发体验:

当页面存在弹窗或者编辑框等,代码修改之后,liveReload会重载页面,如果刷新代码的同时不会重载页面而是重新加载修改过的文件就完美,所以这个机制就是webpack提出的热替换技术,也就是我们说的热更新

webpack.config.js

module.exports = {
  mode: "development", // 开发环境
  devtool: "cheap-module-eval-source-map",
  devServer: {
    contentBase: "./bundle",
    open: true,
    hot: true, // 开启热模块更新
    hotOnly: true  // 更新失败不会刷新页面配置
  }
  module: {
    rules: [{
      test: /\.css$/,
      use: ["style-loader", "css-loader"]
    }]
  }
}

css的loader中的实现已经做了热更新的处理,通过HMR这个插件中的API

// main.jsif (module.hot) {
  module.hot.accept(function() {
    // 监听变化,则修改
  });
  module.hot.dispose(function() {
    // 移除  });
}

我们了解了HMR基本操作之后就可以看看Vite是如何做HMR的:

vite和webpack的HMR实现机制是一样的,都是通过客户端和服务端建立socket连接,服务端有变化则通知客户端做出改变:

 // server/serverPlugin.ts
 watcher.on('change', (file) => {
    if (!(file.endsWith('.vue') || isCSSRequest(file))) {
      handleJSReload(file);
    }
  })

在handleJSReload函数中递归调用了walkImportChain这个函数,这个函数的作用就是查看当前变化的文件是谁引入了它(JS/Vue),那么在递归中没有找到谁引入它,就Full-reload

 send({
     type: 'full-reload',
     path: publicPath
  })

如果找到了引用这个JS的文件了就热更新:

send({
    type: 'multi',
    updates: boundaries.map((boundary) => {
      return {
        type: boundary.endsWith('vue') ? 'vue-reload' : 'js-update',
        path: boundary,
        changeSrcPath: publicPath,
        timestamp
      }
    })
  })

在客户端中,vite则在核心处理函数handleMessage中定义了消息的类型:

  • connected: websocket连接
  • vue-reload: vue文件的script更新
  • vue-rerender: vue文件的template更新
  • style-update:css样式表变更
  • style-remove: css样式表移除
  • js-update: js文件更新
  • full-reload:刷新页面

客户端接受了不同的消息类型去做不同处理,根据timestamp时间戳去请求新文件,而vue文件则通过HMRRuntime更新。


裸模块导入

vite同样也支持其他家打包器的日常功能,浏览器不允许我们直接引入裸模块,例如:

import { add } from "lodash"

vite在裸模块处理上有着对vue得天独厚的优势,vite不仅仅的可以改写普通模块的路径然后正确的解析,还对vue这个依赖有特殊的处理:

如果项目本地没有安装vue依赖,那么引入vue模块会按照vite依赖的vue版本去执行,这就说明了如果你全局安装了vue,那么在vite项目中能更方便的找到它

vite重写了模块加载路径:

 // src/node/server/serverPluginModuleRewrite.ts
 ctx.body = rewriteImports(
    root,
    content!,
    importer,
    resolver,
    ctx.query.t
  )

引入的模块上下文在经过rewriteImports方法处理body以后,就会造成这样的效果:

import vue from "vue" => import vue from "@/modules/vue.js"

rewriteImports这个方法中使用到了Esbuild中的es-module-lexer进行词法分析,对esbuild本来就不熟悉的我去看了这个插件发现这个词法分析器又小,又可以快速对JS进行分析,这里就简单看一下官网的demo吧。

// 伪代码
// 和vite源码中一样
import {   
  init as initLexer,   
  parse as parseImports,   
  ImportSpecifier   
} from 'es-module-lexer' 

// 需要初始化
await initLexer;
const [import, export] = parseImports(`
  import {a} from "a"
  export const add = 1;
`
console.log(import[0].s); 
// 解析结果的返回有这样几种
// "s" is shorthand for "start"
// "e" is shorthand for "end"
// "ss" is shorthand for "statement start"
// "se" is shorthand for "statement end"

经过这样的词法处理,我们的模块引入路径就被这样替换了,尽管这个插件这么强,对于词法分析这种东西我们开发者平时也用不到,所以大家只需要知道vite的模块路径替换是借助es-module-lexer进行词法分析的就可以啦~


总结

vite和webpack对比,我认为webpack是一个纯正的打包工具,它的生态非常丰富,可以基于插件做各种事情,但是像尤大说的一样,很少有基于webpack上层封装的工具出现,也就是具有很大学习和配置成本,而vite提供了更丝滑的开发体验,以及内置的强大的HMR,TS支持,WebAssembly支持等等,所以使用哪款产品要看业务需要。


由于vite目前还在不断的更新中,但是主要特性的原理应该是不会变的,因为vite有非常多优秀的其他特性,还有我们这篇文章提到的3个特性还有很多值得细细研究的地方,所以这个vite系列会继续做,谢谢大家支持哦

超低价PDD打造无限极简桌面攻略

如题所见,这篇文章可不是软文,我们武林中人不能不讲武德的

1758.jpg

主要是自己因为工作后续的需要,需要配置更多的屏幕,所以参考了网上很多大佬的桌面,桌面上动不动mbp,switch,好几千的键盘,4k+的电脑屏幕,千把块的屏幕灯。好家伙把我卖了都没那么多钱,作为程序萌新没那么多钱又想打造一个实用的桌面,你就该看我这个!

完成效果图:

微信图片_20201112221509.jpg


首先我们从价格低到高排序咯,首先就要介绍的笔记本夹架:

来源:淘宝

价格:20-30

推荐星级:五颗星

推荐链接:

淘宝网

评价:不用笔记本的朋友可以用这个架子,这个架子本来是夹餐巾纸的,但是它足够稳固,夹得住,立起来不会伤害电脑,再加上外观清爽,非常好!

O1CN01tFjUIH24Iaj9FfrX6_!!748817368.gif


桌上的老物件:手机/ipad支架是在线下宜家店淘的,这个支架有充电口而且是方便组装的,颜色也是实木风格搭配黑色非常好。

来源:线下宜家

价格:30

推荐星级:4颗星

推荐链接:

SIGFINN 希格芬 手机支架 - 竹子贴面 - IKEA

sigfinn-xi-ge-fen-shou-ji-zhi-jia-zhu-zi-tie-mian__0534722_PE649256_S5.jpg


对于干净的桌面,我就选择了桌下的埋线置物架,首先插板走线都可以从这个架子上走,由于是吸附在桌子下的,所以不会影响到抖腿(滑稽)

来源:拼多多

价格:30+

推荐星级:5颗星(无线桌面非常核心)

推荐链接:

拼多多商城

微信图片_20201112230241.jpg

对于程序员群体来说,在夜晚工作是家常便饭,那么屏幕挂灯就是我们的必备品,因为它不占空间光线可调节等特性,可以让我们眼睛很舒服,屏幕挂灯有多个品牌,既然我们推崇的是性价比,我们就不得不提到小米的屏幕灯啦!对比明基千元的屏幕挂灯,小米这款灯可谓是性价比拉满,不仅有非对称光源还自带无线遥控,非常适合无线桌面呢

来源:小米有品

价格:200

推荐星级:5颗星

推荐链接:

米家显示器挂灯立即购买-小米商城

pms_1590926574.98465077.jpg


对于电脑桌,如果想承受更多的屏幕支架和书籍/空间,那就必须选用1.5m++的桌子,一般的电脑桌都会自带埋线孔,所以电脑桌我就在pdd随便买的,价格基本都是在200元左右,没有必要去选用带储物功能的桌子。

来源:拼多多

价格:200左右(尺寸不一样价格不一样)

推荐星级:3颗星

推荐链接:

拼多多商城
微信图片_20201112231327.jpg


Oh,My god终于到重头戏了,对于显示器支架真的有太多选择了,这里我买了2款,一款是双屏幕的支架和一个显示器壁挂支架,因为老aoc显示器不支持壁挂所以就在同一家店买了壁挂支架,显示器支架非常便宜,100多块就能拿下oh~

来源:淘宝

价格:100+

推荐星级:5颗星

推荐链接:

淘宝网
微信图片_20201112231717.jpg


接着就是我们的最后一个拼多多商品了,这个商品放到最后一个肯定是有原因的,因为真的是pdd购物血泪史,但是结果还是非常好的。因为预算不够所以把4k显示器降低为2k,价格也降低了一半,所以我就抱着试一试的心态购买了pdd的2k显示器,价格只需500出头就可以拿下24寸2k显示器。

收到货之后,发现屏幕窄边框很舒服,屏幕没坏点,该有的接口都有,可是就是开不了机器,店家阿你不讲武德阿,怎么给我一个坏的呢?然后经过和客服的调试发现是电源坏了,然后店家寄了一个电源给我,发现显示器可以开机了。本以为能体验一下捡漏的显示器发现和笔记本连接不上,经过调试发现是HDMI接口有问题,没办法,因为是壁挂的所以影响了二次销售不能退货,只能花70块快递让店家维修,维修之后,开机!完事大吉~

pdd的显示器买的虽然一波三折,但是结局还是好的,也希望给大家一个教训。

来源:拼多多

价格:510

推荐星级:1颗星(抛开退换货就是五颗星,还是值得一试的)

推荐链接:

拼多多商城

微信图片_20201112233131.jpg


Ok!这次打造无线极简桌面的推荐就结束了,还差无线键盘和鼠标这些大家如果为了无线和极简那就挑着自己喜欢的牌子的无线版本就好了噢~