rewrite
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 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 | 在新主机的迁移过程中,最大的困难就是WP permalink rewrite的设置. 因为旧主机是用的Apache, 使用的是WP本身就可以更改的.htaccess,没有太大的难度.而这次在VPS上跑的是Nginx,主要是因为Nginx的速度比Apache要快很多. 但是另一方面就不是那么舒服了,因为Nginx的rewrite跟Apache不同,而且是在服务器上面才能更改. 下面是其间的一些研究笔记.(以下用例如无特别说明均摘自nginx wiki) /1 Nginx rewrite基本语法 Nginx的rewrite语法其实很简单.用到的指令无非是这几个 set if return break rewrite 麻雀虽小,可御可萝五脏俱全.只是简单的几个指令却可以做出绝对不输apache的简单灵活的配置. 1. set set 主要是用来设置变量用的,没什么特别的 2. if if 主要用来判断一些在rewrite语句中无法直接匹配的条件,比如检测文件存在与否,http header,cookie等, 用法: if (条件) {…} - 当 if 表达式中的条件为 true ,则执行 if 块中的语句 - 当表达式只是一个变量时,如果值为空或者任何以0开头的字符串都会当作 false - 直接比较内容时,使用 = 和 != - 使用正则表达式匹配时,使用 ~ 大小写敏感匹配 ~* 大小写不敏感匹配 !~ 大小写敏感不匹配 !~* 大小写不敏感不匹配 这几句话看起来有点绕,总之记住: ~为正则匹配, 后置*为大小写不敏感, 前置!为”非”操作 随便一提,因为nginx使用花括号{}判断区块,所以当正则中包含花括号时,则必须用双引号将正则包起来.对下面讲到的rewrite语句中的正则亦是如此. 比如 “\d{4}\d{2}\.+” - 使用-f,-d,-e,-x检测文件和目录 -f 检测文件存在 -d 检测目录存在 -e 检测文件,目录或者符号链接存在 -x 检测文件可执行 跟~类似,前置!则为”非”操作 举例 if ( $http_user_agent ~ MSIE ) { rewrite ^ (. * )$ /msie / $1 break ; } //如果UA包含”MSIE”,rewrite 请求到/msie目录下 if ( $http_cookie ~ * "id=([^;] +)(?:;|$)" ) { set $id $1;} //如果cookie匹配正则,设置变量$id等于正则引用部分 if ( $request_method = POST ) { return 405; } //如果提交方法为POST,则返回状态405 (Method not allowed) if ( !-f $request_filename ) { break ; proxy_pass http: //127.0.0.1; } //如果请求文件名不存在,则反向代理localhost if ( $args ~ post=140 ) { rewrite ^ http: //example.com / permanent;} //如果query string中包含”post=140″,永久重定向到example.com 3. return return 可用来直接设置HTTP返回状态,比如403,404等(301,302不可用 return 返回,这个下面会在rewrite提到) 4. break 立即停止rewrite检测,跟下面讲到的rewrite的 break flag功能是一样的,区别在于前者是一个语句,后者是rewrite语句的flag 5.rewrite 最核心的功能(废话) 用法: rewrite 正则 替换 标志位 其中标志位有四种 break – 停止rewrite检测,也就是说当含有 break flag的rewrite语句被执行时,该语句就是rewrite的最终结果 last – 停止rewrite检测,但是跟 break 有本质的不同,last的语句不一定是最终结果,这点后面会跟nginx的location匹配一起提到 redirect – 返回302临时重定向,一般用于重定向到完整的URL(包含http:部分) permanent – 返回301永久重定向,一般用于重定向到完整的URL(包含http:部分) 因为301和302不能简单的只单纯返回状态码,还必须有重定向的URL,这就是 return 指令无法返回301,302的原因了. 作为替换,rewrite可以更灵活的使用redirect和permanent标志实现301和302. 比如上一篇日志中提到的Blog搬家要做的域名重定向,在nginx中就会这么写 rewrite ^ (.* )$ http: //newdomain.com/ permanent; 举例来说一下rewrite的实际应用 rewrite ^ (/download /. * ) /media / (. * )\.. *$ $1 /mp3 / $2.mp3 last; 如果请求为 /download/eva/media/op1.mp3 则请求被rewrite到 /download/eva/mp3/op1.mp3 使用起来就是这样,很简单不是么? 不过要注意的是rewrite有很多潜规则需要注意 - rewrite的生效区块为sever, location, if - rewrite只对相对路径进行匹配,不包含hostname 比如说以上面301重定向的例子说明 rewrite ~ * cafeneko\.info http: //newdomain.com / permanent; 这句是永远无法执行的,以这个URL为例 http: //blog.cafeneko.info/2010/10/neokoseseiki_in_new_home/?utm_source=rss&utm_medium=rss&utm_campaign=neokoseseiki_in_new_home 其中cafeneko.info叫做hostname,再往后到?为止叫做相对路径,?后面的一串叫做query string 对于rewrite来说,其正则表达式仅对”/2010/10/neokoseseiki_in_new_home”这一部分进行匹配,即不包含hostname,也不包含query string .所以除非相对路径中包含跟域名一样的 string ,否则是不会匹配的. 如果非要做域名匹配的话就要使用 if 语句了,比如进行去www跳转 if ( $host ~ * ^www\. (cafeneko\.info) ) { set $host_without_www $1; rewrite ^ (. * )$ http: // $host_without_www $1 permanent; } - 使用相对路径rewrite时,会根据HTTP header中的HOST跟nginx的server_name匹配后进行rewrite,如果HOST不匹配或者没有HOST信息的话则rewrite到server_name设置的第一个域名,如果没有设置server_name的话,会使用本机的localhost进行rewrite - 前面提到过,rewrite的正则是不匹配query string 的,所以默认情况下,query string 是自动追加到rewrite后的地址上的,如果不想自动追加query string ,则在rewrite地址的末尾添加? rewrite ^ /users/ (. * )$ /show? user= $1? last; rewrite的基本知识就是这么多..但还没有完..还有最头疼的部分没有说… /2 Nginx location 和 rewrite retry nginx的rewrite有个很奇特的特性 — rewrite后的url会再次进行rewrite检查,最多重试10次,10次后还没有终止的话就会返回HTTP 500 用过nginx的朋友都知道location区块,location区块有点像Apache中的RewriteBase,但对于nginx来说location是控制的级别而已,里面的内容不仅仅是rewrite. 这里必须稍微先讲一点location的知识.location是nginx用来处理对同一个server不同的请求地址使用独立的配置的方式 举例: location = /{ ....配置A } location / { ....配置B } location ^~/images / { ....配置C } location ~ * \. (gif |jpg |jpeg )$ { ....配置D } 访问 / 会使用配置A 访问 /documents/document.html 会使用配置B 访问 /images/1.gif 会使用配置C 访问 /documents/1.jpg 会使用配置D 如何判断命中哪个location暂且按下不婊, 我们在实战篇再回头来看这个问题. 现在我们只需要明白一个情况: nginx可以有多个location并使用不同的配. sever区块中如果有包含rewrite规则,则会最先执行,而且只会执行一次, 然后再判断命中哪个location的配置,再去执行该location中的rewrite, 当该location中的rewrite执行完毕时,rewrite并不会停止,而是根据rewrite过的URL再次判断location并执行其中的配置. 那么,这里就存在一个问题,如果rewrite写的不正确的话,是会在location区块间造成无限循环的.所以nginx才会加一个最多重试10次的上限. 比如这个例子 location /download/ { rewrite ^ ( /download /. * ) /media / (. * )\.. *$ $1 /mp3 / $2.mp3 last; } 如果请求为 /download/eva/media/op1.mp3 则请求被rewrite到 /download/eva/mp3/op1.mp3 结果rewrite的结果重新命中了location /download/ 虽然这次并没有命中rewrite规则的正则表达式,但因为缺少终止rewrite的标志,其仍会不停重试download中rewrite规则直到达到10次上限返回HTTP 500 认真的朋友这时就会问了,上面的rewrite规则不是有标志位last么? last不是终止rewrite的意思么? 说到这里我就要抱怨下了,网上能找到关于nginx rewrite的文章中80%对last标志的解释都是 last – 基本上都用这个Flag ……这他妈坑爹呢!!! 什么叫基本上都用? 什么是不基本的情况? =皿= 有兴趣的可以放狗”基本上都用这个Flag”… 我最终还是在stack overflow找到了答案: last和 break 最大的不同在于 - break 是终止当前location的rewrite检测,而且不再进行location匹配 – last是终止当前location的rewrite检测,但会继续重试location匹配并处理区块中的rewrite规则 还是这个该死的例子 location /download/ { rewrite ^ ( /download /. * ) /media / (. * )\.. *$ $1 /mp3 / $2.mp3 ; rewrite ^ ( /download /. * ) /movie / (. * )\.. *$ $1 /avi / $2.mp3 ; rewrite ^ ( /download /. * ) /avvvv / (. * )\.. *$ $1 /rmvb / $2.mp3 ; } 上面没有写标志位,请各位自行脑补… 如果请求为 /download/acg/moive/UBW.avi last的情况是: 在第2行rewrite处终止,并重试location /download..死循环 break 的情况是: 在第2行rewrite处终止,其结果为最终的rewrite地址. 也就是说,上面的某位试图下载eva op不但没下到反而被HTTP 500射了一脸的例子正是因为用了last标志所以才会造成死循环,如果用 break 就没事了. location /download/ { rewrite ^ ( /download /. * ) /media / (. * )\.. *$ $1 /mp3 / $2.mp3 break ; } 对于这个问题,我个人的建议是,如果是全局性质的rewrite,最好放在server区块中并减少不必要的location区块.location区块中的rewrite要想清楚是用last还是 break . 有人可能会问,用 break 不就万无一失了么? 不对.有些情况是要用last的. 典型的例子就是wordpress的permalink rewrite 常见的情况下, wordpress的rewrite是放在location /下面,并将请求rewrite到/index.php 这时如果这里使用 break 乃就挂了,不信试试. b( ̄▽ ̄)d…因为nginx返回的是没有解释的index.php的源码… 这里一定要使用last才可以在结束location / 的rewrite, 并再次命中location ~ \.php$,将其交给fastcgi进行解释.其后返回给浏览器的才是解释过的html代码. 关于nginx rewrite的简介到这里就全部讲完了,水平及其有限,请大家指出错漏… /3 实战! WordPress的Permalink+Supercache rewrite实现 这个rewrite写法其实是来自supercache作者本家的某个评论中,网上很容易查到,做了一些修改. 先给出该配置文件的全部内容..部分内容码掉了..绝对路径什么的你知道也没啥用对吧? server { listen80; server_name cafeneko.info www.cafeneko.info; access_log***; error_log *** ; root ***; index index.php; gzip_static on ; if (-f $request_filename ) { break ; } set $supercache_file '' ; set $supercache_uri $request_uri; if ( $request_method = POST ){ set $supercache_uri '' ; } if ( $query_string ) { set $supercache_uri '' ; } if ( $http_cookie ~ * "comment_author_|wordpress_logged_|wp-postpass_" ) { set $supercache_uri '' ; } if ( $supercache_uri ~ ^ (.+ )$ ) { set $supercache_file /wp-content /cache /supercache / $http_host / $1index.html; } if (-f $document_root $supercache_file ) { rewrite ^ (. * )$ $supercache_file break ; } if ( !-e $request_filename ) { rewrite . /index.php last; } location ~ \.php$ { fastcgi_pass 127.0.0.1:9000; fastcgi_index index.php; fastcgi_param SCRIPT_FILENAME*** $fastcgi_script_name; include fastcgi_params; } location ~ /\.ht { deny all; } } 下面是解释: gzip_static on ; 如果浏览器支持gzip,则在压缩前先寻找是否存在压缩好的同名gz文件避免再次压缩浪费资源,配合supercache的压缩功能一起使用效果最好,相比supercache原生的Apache mod_rewrite实现,nginx的实现简单的多. Apache mod_rewrite足足用了两套看起来一模一样的条件判断来分别rewrite支持gzip压缩和不支持的情况. if (-f $request_filename ) { break ; } //如果是直接请求某个真实存在的文件,则用break语句停止rewrite检查 set $supercache_file '' ; set $supercache_uri $request_uri; //用$request_uri初始化变量 $supercache_uri. if ( $request_method = POST ) { set $supercache_uri '' ; } //如果请求方式为POST,则不使用supercache.这里用清空$supercache_uri的方法来跳过检测,下面会看到 if ( $query_string ) { set $supercache_uri '' ; } //因为使用了rewrite的原因,正常情况下不应该有query_string(一般只有后台才会出现query string),有的话则不使用supercache if ( $http_cookie ~ * "comment_author_|wordpress_logged_|wp-postpass_" ) { set $supercache_uri '' ; } //默认情况下,supercache是仅对unknown user使用的.其他诸如登录用户或者评论过的用户则不使用. comment_author是测试评论用户的cookie, wordpress_logged是测试登录用户的cookie. wp-postpass不大清楚,字面上来看可能是曾经发表过文章的?只要cookie中含有这些字符串则条件成立. 原来的写法中检测登录用户cookie用的是wordpress_,但是我在测试中发现登入/登出以后还会有一个叫wordpress_test_cookie存在,不知道是什么作用,我也不清楚一般用户是否会产生这个cookie.由于考虑到登出以后这个cookie依然存在可能会影响到cache的判断,于是把这里改成了匹配wordpress_logged_ if ( $supercache_uri ~ ^ (.+ )$ ) { set $supercache_file /wp-content /cache /supercache / $http_host $1index.html; } //如果变量$supercache_uri不为空,则设置cache file的路径 这里稍微留意下$http_host$1index.html这串东西,其实写成 $http_host/$1/index.html 就好懂很多 以这个rewrite形式的url为例 cafeneko.info/2010/09/tsukihime-doujin_part01/ 其中 $http_host = ‘cafeneko.info’ , $1 = $request_uri = ‘/2010/09/tsukihime-doujin_part01/’ 则 $http_host$1index.html = ‘cafeneko.info/2010/09/tsukihime-doujin_part01/index.html’ 而 $http_host/$1/index.html = ‘cafeneko.info //2010/09/tsukihime-doujin_part01//index.html’ 虽然在调试过程中两者并没有不同,不过为了保持正确的路径,还是省略了中间的/符号. 最后上例rewrite后的url = ‘cafeneko.info/wp-content/cache/supercache/cafeneko.info/2010/09/tsukihime-doujin_part01/index.html’ if (-f $document_root $supercache_file ) { rewrite ^ (. * )$ $supercache_file break ;} //检查cache文件是否存在,存在的话则执行rewrite,留意这里因为是rewrite到html静态文件,所以可以直接用break终止掉. if ( !-e $request_filename ) { rewrite . /index.phplast; } //执行到此则说明不使用suercache,进行wordpress的permalink rewrite 检查请求的文件/目录是否存在,如果不存在则条件成立, rewrite到index.php 顺便说一句,当时这里这句rewrite看的我百思不得其解. . 只能匹配一个字符啊?这是什么意思? 一般情况下,想调试nginx rewrite最简单的方法就是把flag写成redirect,这样就能在浏览器地址栏里看到真实的rewrite地址. 然而对于permalink rewrite却不能用这种方法,因为一旦写成redirect以后,不管点什么链接,只要没有supercache,都是跳转回首页了. 后来看了一些文章才明白了rewrite的本质,其实是在保持请求地址不变的情况下,在服务器端将请求转到特定的页面. 乍一看supercache的性质有点像302到静态文件,所以可以用redirect调试. 但是permalink却是性质完全不同的rewrite,这跟wordpress的处理方式有关. 我研究不深就不多说了,简单说就是保持URL不变将请求rewrite到index.php,WP将分析其URL结构再对其并进行匹配(文章,页面,tag等),然后再构建页面. 所以其实这条rewrite rewrite . /index.phplast; 说的是,任何请求都会被rewrite到index.php.因为”.”匹配任意字符,所以这条rewrite其实可以写成任何形式的能任意命中的正则.比如说 rewrite . /index.phplast; rewrite ^ /index.php last; rewrite .* /index.php last; 效果都是一样的,都能做到permalink rewrite. 最后要提的就是有人可能注意到我的rewrite规则是放在server块中的.网上能找到的大多数关于wordpress的nginx rewrite规则都是放在location /下面的,但是上面我却放在了server块中,为何? 原因是WP或某个插件会在当前页面做一个POST的XHR请求,本来没什么特别,但问题就出在其XHR请求的URL结构上. 正常的permalink一般为: domain.com/year/month/postname/ 或者 domain.com/tags/tagname/ 之类. 但这个XHR请求的URL却是 domain.com/year/month/postname/index.php 或者 domain.com/tags/tagname/index.php 这样一来就命中了location ~ \.php$而交给fastcgi,但因为根本没有做过rewrite其页面不可能存在,结果就是这个XHR返回一个404 鉴于location之间匹配优先级的原因,我将主要的rewrite功能全部放进了server区块中,这样就得以保证在进行location匹配之前是一定做过rewrite的. 这时有朋友又要问了,为什么命中的是location ~ \.php$而不是location / ? …望天…长叹…这就要扯到天杀的location匹配问题了…. locatoin并非像rewrite那样逐条执行,而是有着匹配优先级的,当一条请求同时满足几个location的匹配时,其只会选择其一的配置执行. 其寻找的方法为: 1. 首先寻找所有的常量匹配,如location /, location /av/, 以相对路径自左向右匹配,匹配长度最高的会被使用, 2. 然后按照配置文件中出现的顺序依次测试正则表达式,如 location ~ download\/$, location ~* \.wtf, 第一个匹配会被使用 3. 如果没有匹配的正则,则使用之前的常量匹配 而下面几种方法当匹配时会立即终止其他location的尝试 1. = 完全匹配,location = /download/ 2. ^~ 终止正则测试,如location ^~ /download/ 如果这条是最长匹配,则终止正则测试,这个符号只能匹配常量 3. 在没有=或者^~的情况下,如果常量完全匹配,也会立即终止测试,比如请求为 /download/ 会完全命中location /download/而不继续其他的正则测试 总结: 1. 如果完全匹配(不管有没有=),尝试会立即终止 2. 以最长匹配测试各个常量,如果常量匹配并有 ^~, 尝试会终止 3. 按在配置文件中出现的顺序测试各个正则表达式 4. 如果第3步有命中,则使用其匹配location,否则使用第2步的location 另外还可以定义一种特殊的named location,以@开头,如location @thisissparta 不过这种location定义不用于一般的处理,而是专门用于try_file, error_page的处理,这里不再深入. 晕了没? 用前文的例子来看看 location = /{ ....配置A } location / { ....配置B } location ^~/images / { ....配置C } location ~ * \. (gif |jpg |jpeg )$ { ....配置D } 访问 / 会使用配置A -> 完全命中 访问 /documents/document.html 会使用配置B -> 匹配常量B,不匹配正则C和D,所以用B 访问 /images/1.gif 会使用配置C -> 匹配常量B,匹配正则C,使用首个命中的正则,所以用C 访问 /documents/1.jpg 会使用配置D -> 匹配常量B,不匹配正则C,匹配正则D,使用首个命中的正则,所以用D 那么再回头看我们刚才说的问题.为什么那个URL结果奇怪的XHR请求会命中location ~ \.php$而不是location / ? 我相信你应该已经知道答案了. 所以要解决这个问题最简单的方法就是把rewrite规则放在比location先执行的server块里面就可以了哟. 这次的研究笔记就到此为止了. 最后留一个思考题,如果不将rewrite规则放入server块,还有什么方法可以解决这个XHR 404的问题? 原来的location /块包含从location ~ \.php$到root为止的部分. 答案是存在的.在用使用目前的方法前我死脑筋的在保留location /的前提下尝试了很多种方法…请不要尝试为各种permalink构建独立的location.因为wp的permalink种类很多,包括单篇文章,页面,分类,tag,作者,存档等等..欢迎在回复中讨论 / 参考: Nginx wiki -EOF- 更新 @2010.10.23 之前的supercache rewrite规则适用于大部分的WP.但是并不适用于mobile press插件的移动设备支持. 因为其中并没有检测移动设备的user agent,从而导致移动设备也会被rewrite到cache上.这样的结果是在移动设备上也是看到的跟PC一样的完全版blog. 对于性能比较好的手机比如iphone安卓什么的大概没什么问题,但比较一般的比如nokia上用opera mini等看就会比较辛苦了,这次把supercache原本在htaccess中的移动设备检测的代码块也移植了过来. 在前文的配置文件中cookie检测后面加入以下代码段 # Bypass special user agentif ( $http_user_agent ~ * "2.0 MMP|240x320|400X240|AvantGo|BlackBerry|Blazer|Cellphone|Danger|DoCoMo|Elaine/3.0|EudoraWeb|Googlebot-Mobile|hiptop|IEMobile|KYOCERA/WX310K|LG/U990|MIDP-2.|MMEF20|MOT-V|NetFront|Newt|Nintendo Wii|Nitro|Nokia|Opera Mini|Palm|PlayStation Portable|portalmmm|Proxinet|ProxiNet|SHARP-TQ-GX10|SHG-i900|Small|SonyEricsson|Symbian OS|SymbianOS|TS21i-10|UP.Browser|UP.Link|webOS|Windows CE|WinWAP|YahooSeeker/M1A1-R2D2|iPhone|iPod|Android|BlackBerry9530|LG-TU915 Obigo|LGE VX|webOS|Nokia5800") { set $supercache_uri ''; } if ( $http_user_agent ~ * "w3c |w3c-|acs-|alav|alca|amoi|audi|avan|benq|bird|blac|blaz|brew|cell|cldc|cmd-|dang|doco|eric|hipt|htc_|inno|ipaq|ipod|jigs|kddi|keji|leno|lg-c|lg-d|lg-g|lge-|lg/u|maui|maxo|midp|mits|mmef|mobi|mot-|moto|mwbp|nec-|newt|noki|palm|pana|pant|phil|play|port|prox|qwap|sage|sams|sany|sch-|sec-|send|seri|sgh-|shar|sie-|siem|smal|smar|sony|sph-|symb|t-mo|teli|tim-|tosh|tsm-|upg1|upsi|vk-v|voda|wap-|wapa|wapi|wapp|wapr|webc|winw|winw|xda\ |xda-" ) { set $supercache_uri ''; } 这样就可以对移动设备绕开cache规则,而直接使用mobile press产生的移动版的效果了. |
转自:https://blog.csdn.net/xiao_jun_0820/article/details/9397011
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
· 理解Rust引用及其生命周期标识(上)
· 浏览器原生「磁吸」效果!Anchor Positioning 锚点定位神器解析
· DeepSeek 开源周回顾「GitHub 热点速览」
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· AI与.NET技术实操系列(二):开始使用ML.NET
· 单线程的Redis速度为什么快?
2015-03-04 kali安装vmware tools