vps使用google的bbr脚本加速

问题 从去年开始用vultr的vps,先是搭建了ssserver,然后各种web服务,以一个nginx为代理服务器,代理本地不同端口的各种服务,总体来说,vultr家的vps体验很好,除了一点:速度较慢,尤其是晚上8-11点(我买的是东京节点),之前在google上看了很久关于vps加速的方案,基本都是通过锐速,kcptun较多 锐速:国产软件,收费,且不开源,不开源,意味着可能被监控,所以直接pass kcptun:个人觉得麻烦,server端配了还要配client 最后:当时没发现google的bbr,于是将就用着,也还行,就是偶尔想看看youtube的时候只能看480p的,还卡的厉害… 直到前几天,无意中看到google的tcp-bbr拥塞控制技术… 参考链接 配置 确认VPS的虚拟化技术不为Openvz(vultr的服务器都不是OpenvzO(∩_∩)O哈哈~) 下载脚本wget --no-check-certificate https://github.com/teddysun/across/raw/master/bbr.sh 查看脚本支持的系统版本(支持内核版本大于4.9的系统),cat ./bbr.sh 在脚本开始的注释中包含以下信息: System Required: CentOS 6+, Debian7+, Ubuntu12+ 我当前的vps是Ubuntu 18.04 LTS,因此满足开启tcp_bbr的条件 4. 赋予执行权限:chmod +x ./bbr.sh 5. 执行:./bbr.sh 6. 完成后使用lsmod | grep tcp_bbr查看tcp_bbr加速模块是否已经安装成功 7. 重启vps:reboot 效果 直接看YouTube的1080P视频吧,最直观,直接上图 全程无卡顿

June 6, 2018 · 1 min · 40 words

docker搭建gitlab和svn服务

之前公司里的代码都是托管到局域网服务器上的,现在由于部分同事远程办公的需要,计划把git和svn都转到公网的centos服务器上去,但是gitlab的配置是真心费时间,所以决定用docker来做这个事情,以下是一些步骤和总结 使用daocloud给docker加个速先… 可以通过这个链接里面的命令给docker改个源,不然速度慢死… gitlab安装 拉取镜像 docker pull gitlab/gitlab-ce:latest 新建授权用户 useradd -d /home/gitlab -s /bin/sh -m gitlab 后台运行容器,指定域名,端口映射关系,目录映射关系,将容器命名为gitlab,方便后续操作 docker run --detach \ --hostname git.vcs.trycheers.com \ --publish 10443:443 --publish 10080:80 --publish 10022:22 \ --name gitlab \ --restart always \ --volume /home/gitlab/config:/etc/gitlab \ --volume /home/gitlab/logs:/var/log/gitlab \ --volume /home/gitlab/data:/var/opt/gitlab \ gitlab/gitlab-ce:latest 用apache对10080端口进行反代,使之能够通过域名访问 <VirtualHost *:80> ServerName hostname ProxyPreserveHost On ProxyPass / http://localhost:10080/ ProxyPassReverse / http://localhost:10080/ </VirtualHost> svn安装 后台运行容器,指定端口映射关系,目录映射关系,将容器命名为svn,方便后续操作 docker run -d -p 9200:80 -p 9201:443 -v /home/subversion/svn:/var/local/svn -v /home/subversion/svn_backup:/var/svn-backup -v /home/subversion/svn_conf/:/etc/apache2/dav_svn/ --name svn marvambass/subversion 添加svn用户 htdigest /home/subversion/svn_conf/dav_svn.passwd Subversion username 修改仓库/分组/用户权限 直接编辑/home/subversion/svn_conf/dav_svn.authz ...

May 21, 2018 · 1 min · 106 words

树莓派玩耍记

这篇文章是自己入手树莓派之后的一些使用记录 前些天看 v2ex 上有人讨论树莓派,于是出于好奇在淘宝上淘了一只树莓派来玩玩 体积超级小… 一个板子,一个塑料盒子装上完工 刚开始的时候尝试了一下官方的NOOBS工具安装raspbian系统,这个系统是为树莓派定制的基于Debian的 linux 系统,但不知什么原因总是间歇性卡死,加之个人偏好 ubuntu 一点,于是安装了Ubuntu_MATE 系统安装基本步骤: 下载系统镜像 将下载的ubuntu-mate-16.04.2-desktop-armhf-raspberry-pi.img镜像解压后使用dd命令将镜像写入到 sd 卡中(我的环境为 macOS),注意写入完成后一定要使用unmount将 sd 卡推出,然后拔出 sd 卡 将 sd 卡插入树莓派,并连接好所有外设后开机,然后像安装 QQ 一样完成了Ubuntu_MATE的安装过程 简易播报系统: 当时冒出一个想法:写一个每天定时播放未来两天天气预报的小程序(非彼小程序),涉及的功能点:天气预报接口;一个基于轻量级的 api 框架实现的 api;一个定时任务;一个文字转语音脚本;一个音频文件播放器;当然还需要一个外接小音箱… 于是安装了 lnmp 环境(当下没有用到 mysql),mplayer,git,文字转音频使用的百度sdk(php),通过 git 安装了lumen 接口开发 路由 $router->get('/get_weather','WeatherController@getWeather'); 控制器WeatherController.php <?php namespace App\Http\Controllers; use GuzzleHttp\Client; use Illuminate\Support\Facades\Cache; class WeatherController extends Controller { /** * Create a new controller instance. * * @return void */ public function __construct() { // } //基于树莓派的简易天气预报系统 public function getWeather() { //获取天气信息 $client = new Client(); // 实例化 $city = '成都'; $city_code = urlencode($city); $aqi = [ '好', '中等', '不适于敏感人群', '不健康', '非常不健康', '危险', ]; if (Cache::has('report') === false) { $url = 'https://www.sojson.com/open/api/weather/json.shtml?city=' . $city_code; // 设置一个可访问的 url $http = $client->request('GET', $url); // 执行 // 判断 http 状态码为 200 的时候,执行成功 $aqi_content = '未知'; if ($http->getStatusCode() == 200) { $weather = json_decode($http->getBody()->getContents(), true); if ($weather['data']['forecast'][0]['aqi'] <= 50) { $aqi_content = $aqi[0]; } else if ($weather['data']['forecast'][0]['aqi'] <= 100) { $aqi_content = $aqi[1]; } else if ($weather['data']['forecast'][0]['aqi'] <= 150) { $aqi_content = $aqi[2]; } else if ($weather['data']['forecast'][0]['aqi'] <= 200) { $aqi_content = $aqi[3]; } else if ($weather['data']['forecast'][0]['aqi'] <= 300) { $aqi_content = $aqi[4]; } else if ($weather['data']['forecast'][0]['aqi'] <= 500) { $aqi_content = $aqi[5]; } else { } //拼装字符串 $report = '现在预报,' . $city . '未来两天天气情况,' . $city . ',' . date('Y年m月', time()) . $weather['data']['forecast'][1]['date'] . ',天气情况,' . $weather['data']['forecast'][1]['high'] . ',' . $weather['data']['forecast'][1]['low'] . ',' . $weather['data']['forecast'][1]['type'] . ',' . $weather['data']['forecast'][1]['fx'] . ',风力,' . $weather['data']['forecast'][1]['fl'] . ',日出时间,' . $weather['data']['forecast'][1]['sunrise'] . ',日落时间,' . $weather['data']['forecast'][1]['sunset'] . ',空气污染指数,' . $aqi_content; $report .= ',' . $city . ',' . date('Y年m月', time()) . $weather['data']['forecast'][2]['date'] . ',天气情况,' . $weather['data']['forecast'][2]['high'] . ',' . $weather['data']['forecast'][2]['low'] . ',' . $weather['data']['forecast'][2]['type'] . ',' . $weather['data']['forecast'][2]['fx'] . ',风力,' . $weather['data']['forecast'][2]['fl'] . ',日出时间,' . $weather['data']['forecast'][2]['sunrise'] . ',日落时间,' . $weather['data']['forecast'][2]['sunset'] . ',空气污染指数,' . $aqi_content; Cache::add('report', $report, 60); } } $report = Cache::get('report'); generate_audio($report); } } 以上的generate_audio()即是自行封装后的百度的文字转语音sdk ...

April 14, 2018 · 2 min · 392 words

关于mysql查询语句的一次问题记录

最近在开发一个公司内部使用的财务报表系统,在一次查询时数据库报了一个错误,在网上查看原因并解决问题之后顺便记录一下,该sql的目标是从日报主表,日报月数据表,日报模块表中查询到指定经营公司,指定日期的当日汇总数据(包含部分当月数据用于后续计算) 问题sql如下: SELECT A1.depcode AS depcode, SUM(d_dd_xcddsntq) AS sntq, ( SELECT ( SUM(d_jk_scddldl) + SUM(d_jk_zcddldl) + SUM(d_jk_hdjkl) ) AS bysj FROM mall_daily A LEFT JOIN mall_daily_jk B ON A.id = B.d_id LEFT JOIN mall_daily_month C ON A.depcode = C.depcode AND A.d_datetime >= C. START AND A.d_datetime <= C. END WHERE A.d_date >= DATE_FORMAT('2018-03-01', '%Y-%m-%d') AND A.d_date <= DATE_FORMAT('2018-03-21', '%Y-%m-%d') AND A.depcode = A1.depcode ) AS aj13, ( SELECT SUM(B.d_dd_xcztdd) AS bysj FROM mall_daily A LEFT JOIN mall_daily_dd B ON A.id = B.d_id LEFT JOIN mall_daily_month C ON A.depcode = C.depcode AND A.d_datetime >= C. START AND A.d_datetime <= C. END WHERE A.d_date >= DATE_FORMAT('2018-03-01', '%Y-%m-%d') AND A.d_date <= DATE_FORMAT('2018-03-21', '%Y-%m-%d') AND C.table_name = 'DailyDd' AND A.depcode = A1.depcode ) AS aj25, TRUNCATE ( (SELECT aj25) / (SELECT aj13), 2 ) AS bysj, D.d_month_byjh AS byjh, TRUNCATE ( (SELECT bysj) / (SELECT byjh), 2 ) AS jhdcl, TRUNCATE (30 / 31, 2) AS sjjd, TRUNCATE ( (SELECT bysj) / (SELECT sntq), 2 ) AS tb FROM mall_daily A1 LEFT JOIN mall_daily_dd B ON A1.id = B.d_id LEFT JOIN mall_daily_jk C ON A1.id = C.d_id LEFT JOIN mall_daily_month D ON A1.depcode = D.depcode AND A1.d_datetime >= D. START AND A1.d_datetime <= D. END WHERE A1.d_date >= DATE_FORMAT('2018-03-01', '%Y-%m-%d') AND A1.d_date <= DATE_FORMAT('2018-03-21', '%Y-%m-%d') GROUP BY A1.depcode; 上述sql在执行时提示 ...

April 3, 2018 · 3 min · 448 words

frp+nginx实现内网穿透

写在前面:上一篇文章写了关于ngrok+nginx实现内网穿透的流程,并提出了一些存在的问题,昨天试过frp之后,将之前ngrok存在的无法映射本地域名的问题解决了 frp 是一个可用于内网穿透的高性能的反向代理应用,支持 tcp, udp, http, https 协议。 先决条件 有一个域名,并解析到自己服务器上,如:*.frp.lestat.me 有一个具备固定ip的公网服务器 系统环境 假设环境为: 服务器OS:ubuntu17.10 客户端OS:macOS High Sierra 以下内容将按照上述环境进行搭建 部署 相对于ngrok还需要编译源码,frp方便很多,真正的开箱即用 服务器 下载对应操作系统的frp服务端&客户端至服务器和内网电脑 wget https://github.com/fatedier/frp/releases/download/v0.16.1/frp_0.16.1_linux_amd64.tar.gz 解压 tar zxf ./frp_0.16.1_linux_amd64.tar.gz cd ./frp_0.16.1_linux_amd64 ll 目录结构如下(不同版本可能有差异,但大致相似): -rw-rw-r-- 1 kcptun kcptun 11358 Mar 21 10:11 LICENSE -rwxrwxr-x 1 kcptun kcptun 6154432 Mar 21 10:10 frpc* -rw-rw-r-- 1 kcptun kcptun 126 Mar 21 10:11 frpc.ini -rw-rw-r-- 1 kcptun kcptun 5306 Mar 21 10:11 frpc_full.ini -rwxrwxr-x 1 kcptun kcptun 7586848 Mar 21 10:10 frps* -r--r----- 1 root root 11207 Mar 25 15:38 frps.2018-03-25.log -rw-rw-r-- 1 kcptun kcptun 2127 Mar 25 13:42 frps.ini -rw-rw---- 1 root root 723 Mar 26 05:06 frps.log -rw-rw-r-- 1 kcptun kcptun 2300 Mar 21 10:11 frps_full.ini -rw------- 1 root root 0 Mar 25 11:12 nohup.out 编辑配置文件 vim ./frps.ini ...

March 26, 2018 · 2 min · 383 words

ngrok+nginx实现内网穿透

写在前面: 前天在qq群里看到有人在讨论替代花生壳的工具,说到了ngrok,说是可以实现花生壳一样的内网穿透,个人认为主要有以下几个用处: 可以在公司测试服务器上搭建一个服务,实现测试站点的本地访问(公网访问本地服务器),在这之前通常是上传网站到服务器并解析一个子域名,相对比较费时 微信接口开发的时候优势更明显,因为微信的OAuth一类认证需要一个公网域名且端口必须是80/443(也是本文需要用到nginx做反向代理的原因之一) 欢迎补充… ngrok1.x介绍(2.x没有开源官网) ngrok1.x源码github地址 如上封面图所示 橘色屏幕的笔记本是你的工作机器,安装了ngrok客户端 ngrok.com所在的服务器安装了ngrok的服务端(ngrokd) 利用ngrok 8080命令可以将你本机的8080端口暴露给反向代理至ngrok.com的某个二级域名如:.ngrok.com 公网用户可以通过.ngrok.com就可以访问你本机8080端口上的站点内容了。 由此可见,借助ngrok,可以解决web项目(尤其是微信接口相关)开发过程经常遇到的“本地开发,外网调试”问题。 先决条件 有一个域名,并解析到自己服务器上,如:*.ngrok.lestat.me 有一个具备固定ip的公网服务器 部署 基本步骤:安装go环境->下载ngrok源码->使用go编译ngrok以及相关环境变量的设置->证书配置->运行ngrok服务器端并指定监听的http/https端口->nginx配置文件中对上一步中相关端口做反向代理配置->重启nginx->生成对应OS(linux,darwin,windows)的客户端->本地机器下载上一步生成的客户端->本地新建配置文件ngrok.cfg->本地运行客户端并指定配置文件->出现online则说明穿透成功 一个例子 数据准备 本机地址 IP:127.0.0.1,HTTP 为 80 外网地址 IP:45.77.14.6,HTTP 为 80(NGINX监听该端口,并对*.ngrok.lestat.me域名进行转发到服务器的60端口) 域名为:http://*.ngrok.lestat.me 预期结果 外网访问 http://*.ngrok.lestat.me可以访问到本机上80端口提供的网站 下文按照前面的例子来搭建 Go环境的安装 下载并解压GOLANG wget -c https://storage.googleapis.com/golang/go1.8.3.linux-amd64.tar.gz tar -C /usr/local -zxvf go1.8.3.linux-amd64.tar.gz 设置相关环境变量 export GOROOT=/usr/local/go export PATH=$PATH:$GOROOT/bin export GOPATH=$HOME/go export GOROOT_BOOTSTRAP=/usr/local/go 检查安装是否成功 go version 安装ngrok 下载并配置参数 cd /usr/local/ git clone https://github.com/inconshreveable/ngrok.git export GOPATH=/usr/local/ngrok/ export NGROK_DOMAIN="ngrok.lestat.me" cd ngrok 生成证书 openssl genrsa -out rootCA.key 2048 openssl req -x509 -new -nodes -key rootCA.key -subj "/CN=$NGROK_DOMAIN" -days 5000 -out rootCA.pem openssl genrsa -out server.key 2048 openssl req -new -key server.key -subj "/CN=$NGROK_DOMAIN" -out server.csr openssl x509 -req -in server.csr -CA rootCA.pem -CAkey rootCA.key -CAcreateserial -out server.crt -days 5000 将源码下的证书复制到指定位置 cp rootCA.pem assets/client/tls/ngrokroot.crt cp server.crt assets/server/tls/snakeoil.crt cp server.key assets/server/tls/snakeoil.key 编译服务器&客户端(linux64位),如果是32位系统则是amd386 cd /usr/local/go/src GOOS=linux GOARCH=amd64 ./make.bash cd /usr/local/ngrok/ GOOS=linux GOARCH=amd64 make release-server release-client 编译Mac64位客户端 ...

March 24, 2018 · 2 min · 358 words

浅谈shadowsocks中的pac配置

大概是从去年开始使用的shadowsocks实现科学上网,当时在配置完代理服务器之后能够用了就没管其他的配置。 直到最近想在维基百科上注册一个账号的时候发现 由于一些原因,当前使用代理的ip被封禁了 由于GFW是通过dns污染的方式屏蔽了zh.wikipedia.org(其他语言的wikipedia其实是可以在国内直接访问的比如英文站),这个问题可以通过关闭代理并修改hosts文件解决,但这样太麻烦,因为需要定期更新hosts文件的ip地址,后来google了一下发现shadowsocks里面有一个名为pac的文件,这个文件的域名列表来自于GFWlist,而正是这个文件决定了shadowsocks处于pac模式时哪些域名需要被代理,在这个文件之外的域名都会直接访问。因此,解决维基的ip封禁且要继续使用代理上网只需要2个步骤 修改本地hosts为wikipedia中文当前的ip(解决dns污染) 修改pac文件中的配置,将wikipedia.org相关的配置去掉即可 PAC的优势 PAC自动代理属于智能判断模式,相比全局代理,它的优点有: 不影响国内网站的访问速度,防止无意义的绕路 节省Shadowsocks服务的流量,节省服务器资源 控制方便

February 14, 2018 · 1 min · 11 words

近期工作中的收获

眼下快要过年了,手头的项目也终于接近尾声,抽点时间来记录下近段时间的工作心得 前段时间在工作之余我抽空看了一些ECMA6的语法,简单学习了一下webpack,了解了babel,后面也尝试着使用vue-cli搭建自己的demo并熟悉其中的结构 说说最近做的一个项目:一个接口开发(前端)项目,接口由客户提供(java开发,json数据格式),我方负责移动端web开发和后台页面的开发,并调试好所有接口(后端接口情况:权限相关[14],会员模块[10],片库[14],,出品[19],媒体[15],资讯[11],直通车[14],ip活动[12],营销联盟[13]) 刚拿到需求的时候我还是比较担心能否搞定,毕竟作为一个phper之前的确没有搞过spa的开发,但之前做了那么多spa的功课,也觉得是时候真正的实践一下了,于是很快找到了一个基于vue+elementui的后台框架vue-element-admin,据我最近观察,这个项目在github上每天平均增加将近100个star… 这个框架替我们完成了很多基本方法的封装,路由访问权限,页眉标签切换,常用的各种表格,表单等;开发起来效率相对较高,在我后来实际的开发中也应证了这点 最后花了两周时间,搭建了一个小型后台管理系统并完成所有接口的调试 使用vue做开发的感觉就是代码比曾经用jq的项目更规范(也可能是因为框架中引入了eslint,配合vscode的eslint+prettier一键格式化,实现简单操作就能遵循eslint代码规范);由于vue-cli已经将webpack,babel打包好,并且实现了ctrl+s自动刷新页面预览且不会清空console里面的内容,开发的体验变得非常好。 强烈推荐chrome的Vue DevTools插件,安装完成后可以在chrome右上方看到vue的logo,在dev模式下的页面可以直接看到开发者工具的标签栏多出一个Vue标签,点击即可查看到当前页面的信息! 目前为止,自己对vue的了解还不够深入,对vuex,组件的使用还不够灵活,还需要更多的实践 新的一年,继续加油!

February 7, 2018 · 1 min · 11 words

Vue开发中的一些总结

关于axios的使用细节 基于vue做spa开发,个人很多时候使用的请求扩展是axios,这个扩展会把常用的请求封装好发送出去,使用的时候只需要传参数即可。今天遇到的一个问题是后端接口接收get方式传参,我这边有一个数组需要通过get方式传递过去,假设数组名称是:ids,请求中默认就是ids[]的形式,接口需要提供ids的形式,此时需要引入qs扩展,并在请求位置添加一项配置,以转换参数格式,示例代码如下: /* * todo :会员审核列表的通过与驳回(批量和单个为同一个方法), 需要管理员登陆 * @param data object * */ export function userInfoCheck(data) { return request({ url: '/backend/userInfoCheck', method: 'get', params: data, paramsSerializer: function(params) { return qs.stringify(params, { arrayFormat: 'repeat' }) } }) }

January 26, 2018 · 1 min · 37 words

关于vue中$nextTick的一点使用心得

当下公司在做一个媒体门户网站,后台由另一家公司使用java开发并提供接口,本人负责开发后台页面,使用的是vue-element-admin开发 下面说一下问题场景,在开发过程中有一个后台管理员角色页面,其中包含一个表单dialog,在其中使用了el-tree组件,相关 代码结构如下: <div class="filter-container"> <el-button class="filter-item" style="margin-left: 10px;" v-waves @click="handleCreate" type="primary" icon="el-icon-edit">新增角色 </el-button> </div> <el-dialog :title="textMap[dialogStatus]" :visible.sync="dialogFormVisible" width="50%"> <el-form :rules="rules" ref="dataForm" :model="temp" label-position="top" label-width="90px" style='width: 400px; margin-left:50px;'> <el-form-item label="选择权限" prop="sysPermission"> <el-tree ref="tree" :data="sysPermission" :props="formProps" show-checkbox @check-change="handleCheckChange" node-key="id"></el-tree> </el-form-item> </el-form> </el-dialog> 相关的js如下: export default { name: 'sysRoleList', data() { return { tableKey: 0, list: null, total: null, listLoading: true, formLoading: true, listQuery: { page: 1, limit: 20, importance: undefined, title: undefined, type: undefined, sort: '+id' }, dialogFormVisible: false, dialogStatus: 'update', textMap: { update: '编辑角色', create: '新增角色' }, rules: { sysRoleName: [{required: true, message: '必须填写角色名称', trigger: 'blur'}] }, // 表单数据 temp: { id: '', sysPermissionList: [], sysRoleName: '' }, currentKeys: [], // 表单权限字段映射 formProps: { label: 'sysPermissionName' }, sysPermission: {} } }, created() { // 列表数据 // this.getList() // 获取所有权限 // this.findAllSysPermission() }, methods: { /* * todo:checkbox状态变更监听 * */ handleCheckChange(data, checked, indeterminate) { const idObj = {id: data.id} this.temp.sysPermissionList.push(idObj) }, resetTemp() { this.temp = { id: '', sysRoleName: '', sysPermissionList: [] } }, handleCreate() { this.resetTemp() this.dialogStatus = 'create' this.currentKeys = [] this.dialogFormVisible = true this.$nextTick(() => { this.$refs['dataForm'].clearValidate() this.$refs.tree.setCheckedKeys(this.currentKeys) }) }, handleUpdate(row) { this.resetTemp() this.dialogStatus = 'update' this.dialogFormVisible = true this.temp = Object.assign({}, row) // copy obj this.currentKeys = [] row.sysPermissionList.forEach((value, index) => { this.currentKeys.push(value[0]) }) this.$nextTick(() => { this.$refs['dataForm'].clearValidate() this.$refs.tree.setCheckedKeys(this.currentKeys) }) } } } 需求: 需要在每次编辑数据的时候触发<el-tree>的方法setCheckedKeys 问题: 之前没有把this.$refs.tree.setCheckedKeys()写在this.$nextTick的callback之中,因此会提示: ...

January 24, 2018 · 2 min · 272 words