首页
留言
动态
归档
推荐
音乐
工具
Search
1
Emby公益服-上万部电影电视剧免费看
68,116 阅读
2
openwrt-docker部署lxk0301京东自动签到脚本
13,008 阅读
3
QuantumultX-京东签到撸京东豆
11,250 阅读
4
LXK0301京东签到脚本-自动提交互助码
9,699 阅读
5
微信-域名被封监测以及自动更换被封域名
9,215 阅读
随便写写
科学上网
Web开发
瞎折腾
Search
标签搜索
quantumultx
laravel
openwrt
laravel nova
laradock
telegram
DDC/CL
薅羊毛
google adsense
jd_scripts
京东签到
ubuntu
oh-my-zsh
web开发环境
nginx
工具
shadowsocks shadowsocksR
RBAC
权限管理
内网穿透
orzlee
累计撰写
46
篇文章
累计收到
596
条评论
首页
栏目
随便写写
科学上网
Web开发
瞎折腾
页面
留言
动态
归档
推荐
音乐
工具
搜索到
46
篇与
orzlee
的结果
2019-05-06
laravel-admin 与 laravel-nova 使用感受
前言 两种后台管理扩展我都使用过,两个扩展既有很多相似点,却又有很多不同。接触过laravel nova之后,再接触laravel admin有种莫名的熟悉感,发现在开发中有很多类似的设计思路。 接触laravel admin时间只有一个来月,时间还不长,随便写写吧。有不足之处还请多多指教。 感受 其实laravel nova就是laravel admin的进化版,显然laravel admin是开源扩展,而laravel nova是付费使用的(99刀一个授权可不便宜),在这不考量两者使用授权方面。 laravel admin是一个中国开发者,做出这样的扩展确实非常了不起。扩展设计合理,但是优化上没有laravel nova那么好。 其实最开始接触laravel nova之前是考虑过laravel admin的,由于laravel admin已经封装好了权限管理,自己替换处理又相对麻烦(其实就是懒),才放弃的。laravel admin的权限管理没有做缓存处理,每次使用都要从数据库查询,其实完全可以缓存起来减轻数据库压力。相对于laravel nova这个功能已经交给开发者实现(我还写过一篇关于 laravel nova 权限管理 的文章)。类似于这样需要缓存的还有菜单功能,因为网站上线后这些几乎不变的的数据却又频繁读取,缓存能解决不少数据库压力(有时间自己处理下)。 laravel admin模板还是使用HTML(对于我这种前端弱的一逼的程序员来说真是非常友好),而laravel nova使用vue.js,相对来说vue.js体验感更好,但是技术要求也相对较高。折腾vue.js多多少少还是要翻几次文档。在扩展字段的时候前端模板就相当重要了(反正我写的CSS无法直视)。 laravel admin更加符合国人操作习惯,我使用admin LTE开发过几个后台,基本上不需要教如何使用,大多数功能、按钮一目了然。而laravel nova很多功能、按钮藏的比较深,很多同事反映过找不到地方... 在代码层面,laravel admin我总感觉没有laravel nova那么简洁、灵活。列表页,详情页,编辑页总共需要些三个不同的模板,我是用laravel localization本地化功能,所以需要将三个模板全部修改翻译,而laravel nova只需要编写一次模板,不需要在某个页面显示的字段可以单独关闭: //laravel admin 列表页、详情页、表单,其实还需要三个控制器动作分别输出不同的模板 /** * Make a grid builder. * * @return Grid */ protected function grid() { $grid = new Grid(new ProductSpecification); $this->gridViewPermission($grid); $grid->id('Id')->sortable(); $grid->name(trans('product/specification.name'))->sortable(); $grid->created_at(trans('admin.created_at'))->sortable(); $grid->updated_at(trans('admin.updated_at'))->sortable(); return $grid; } /** * Make a show builder. * * @param mixed $id * @return Show */ protected function detail($id) { $show = new Show(ProductSpecification::findOrFail($id)); $this->showViewPermission($show); $show->id('Id'); $show->name(trans('product/specification.name')); $show->attributes(trans('product/specification_attribute.index'),function (Grid $attributes){ $this->gridViewPermission($attributes); $attributes->setResource(route('admin.product.specification.attribute.index')); $attributes->id()->sortable(); $attributes->name(trans('product/specification_attribute.name'))->sortable(); $attributes->created_at(trans('admin.created_at'))->sortable(); $attributes->updated_at(trans('admin.updated_at'))->sortable(); }); $show->created_at(trans('admin.created_at')); $show->updated_at(trans('admin.updated_at')); return $show; } /** * Make a form builder. * * @return Form */ protected function form() { $form = new Form(new ProductSpecification); $this->formViewPermission($form); $form->text('name', trans('product/specification.name')) ->rules('unique:product_specifications') ->required(); $form->hasMany('attributes', trans('product/specification_attribute.index'), function (Form\NestedForm $form){ $form->text('name',trans('product/specification_attribute.name'))->required(); }); $form->saved(function (Form $form){ $form->model()->forgetCache(); }); return $form; } ... //laravel nova 表单、列表、详情 public function fields(Request $request) { $guardOptions = collect(config('auth.guards'))->mapWithKeys(function ($value, $key) { return [$key => __('nova-permission-tool::roles.guard_names.'.$key)]; }); $userResource = Nova::resourceForModel(getModelForGuard($this->guard_name)); return [ ID::make()->sortable(), Text::make(__('nova-permission-tool::roles.name'), 'name') ->rules(['required', 'string', 'max:255']) ->creationRules('unique:'.config('permission.table_names.roles')) ->updateRules('unique:'.config('permission.table_names.roles').',name,{{resourceId}}'), Text::make(__('nova-permission-tool::roles.guard_name'), function () { return __('nova-permission-tool::roles.guard_names.'.$this->guard_name); }), Select::make(__('nova-permission-tool::roles.guard_name'), 'guard_name') ->options($guardOptions->toArray()) ->rules(['required', Rule::in(array_keys($guardOptions->toArray()))]) ->onlyOnForms(), DateTime::make(__('nova-permission-tool::roles.created_at'), 'created_at')->exceptOnForms(), DateTime::make(__('nova-permission-tool::roles.updated_at'), 'updated_at')->exceptOnForms(), BelongsToMany::make(__('nova-permission-tool::resources.Permissions'), 'permissions', Permission::class)->searchable(), MorphToMany::make($userResource::label(), 'users', $userResource)->searchable(), ]; } 通过以上代码对比可以看出两个扩展在代码简洁性上有蛮大区别的。一个个字段写入翻译蛮麻烦的。在关系字段处理上需要关联表的控制器,然后增删改查又是一套... 当然这样也是有好处的,高度自定义,只不过一个后台管理用处不多,大多数可以自定义字段扩展出来。 laravel admin几乎把所有字段所需的 javaScript放在后端输出,其实这样对前端不友好,现在都前后端开始分离了,前端代码放在PHP中嵌套写蛮乱的,后期维护也相对困难。 结语 laravel nova和laravel admin都是非常好的扩展,只是国内大多数只知道拿人家的用,却很少去贡献,生态不如国外。两个扩展都朝着相同的目标,节省后台开发时间,但是一个收费,一个开源,而且laravel nova还是laravel作者团队开发的(框架加持)。扩展都是好扩展,要按项目需求和自己的熟练程度来选择使用哪种扩展开发后台,有能力两种都试试,深深体会一下。
2019年05月06日
3,534 阅读
0 评论
1 点赞
2019-02-22
微擎-mysql数据库崩溃
前言 公司目前有两个微擎搭建的商场项目,是购买的插件。就在前几天出现MySql数据库崩溃,出现 mysql too many connection 的错误,在修复过程发现这个微擎就是个巨坑。 修复过程 orzlee使用的是 MySql 5.7 最开始以为访问量太大,调整下MySql配置文件中的 max_connections 选项,然后重启数据库。服务器配置8核16G,目所有站点前访问量足够应付。 调整 max_connection 后不到十分钟,问题依旧出现。进入数据库,使用命令查看当前所有连接: show processlist; 一看吓一跳,两个微擎数据库全部锁表,由于站点一直在访问,链接数只增不减,难怪数据库崩掉。锁表问题很好解决,进入数据库解锁就好了: use database; ##database 是数据库名称 unlock tables; 然后并没有解决根本问题,真正的问题是 ims_core_sessions 表索引坏了,微擎自己自检表状态并且执行修复 repair table ims_core_sessions。而且 ims_core_sessions 表读写相当频繁,这样一来后面的查询全部堵塞。 解决办法 解决办法很简单,先把两个微擎系统停掉。orzlee是修改nginx配置文件,停止解析当前两个微擎的业务域名,然后进入数据库手动修复 ims_core_sessions 表索引,大约有个7、8分钟才修复完成: use database ##database 数据库名称 repair table ims_core_sessions; 在数据连接超出之后,已连接的查询全部会挂起,所以微擎根本不可能修复完成。这也是导致所有站点全挂的主要原因。 结语 这种问题以后肯定还会出现,坑已经填上了。数据库引擎还是使用 innoDB 的好。MySql官方已经使用 innoDB 作为默认数据库引擎。
2019年02月22日
4,339 阅读
0 评论
0 点赞
2019-01-28
微信-域名被封监测以及自动更换被封域名
前言 微信已经成为大多数中国人的一种依赖,发展好了自然管理就更加严格,对于不符合微信营销规范的的网站统统不允许访问,就连对手的网址也无法在微信浏览器中打开。 最近公司有个需求,在各个平台大量投放广告,微信内域名频繁被封,需要域名监测和替换的内部系统功能。微信开发做过不少,但是这种明显是找漏子钻。 想不到就上网找吧!把Google翻个底朝天,全TMD是广告,各种出租微信域名防封检测API,真的一条有用的信息都没有。这下真得靠自己了。 那些出售API的到底是用的什么方法? 我琢磨了一会,想不到还能又什么方法能检测域名是否被微信封掉,只有在微信APP或桌面微信内置浏览器才行啊。抓包看看请求域名前有没有什么检测API,也没有发现什么有价值的信息。后来想想也不可能写个程序,人家请求API检测域名就用程序在桌面微信打开链接判断是否被封吧。真的有点悬,这难道有内部接口? 我是如何找到方法的? 在年中的时候我发现一个微信很坑爹的问题,那时候在高铁上改代码历历在目。“微信短链接”把orzlee坑惨了,在公司内部系统做了个短链接功能,每次手动替换被封域名都会自动生成微信短链接,这下倒好,生成的短链接不到一小时就封,替换了好几个域名,最后用长连接明显存活时间更长。 最坑爹的还不是这个,微信短链接跳转域名被封,短链接在任何浏览器打开都会如下提示: 功能编写思路 Nice,有办法了,我把需要检测的域名都生成一个微信短链接,然后隔段时间访问短链接判断是否重定向正确的域名或者判断重定向域名是否weixin110.qq.com。 整套功能编写思路: 域名都在阿里云,充分利用域名分组功能,总共分三组域名池-正常使用域名-弃用域名。整套域名管理使用API处理 域名池 域名池域名无任何指向,完全未使用的正常域名(没有被微信封,有指向也可以,那就再替换域名逻辑要做好处理了)。 正常使用域名 目前正常使用的域名 弃用域名 已经被微信封或者域名备案丢失 每隔一段时间检测域名是否正常,这个有两种情况: 域名正常 皆大欢喜(^_^)。 域名被封(备案丢失) 替换被封域名 说说被封的处理思路,因为公司内部系统已经自动化站点配置,现在加入阿里云API简直如虎添翼。域名替换使用固定短链接,要求使原域名链接依然可以正常访问。这点很好做,302重定向即可。域名使用记录表中记录新域名,旧域名被访问(使用以一个域名专门用作跳转域名,不做其他使用,携带参数能找到数据库中的旧域名。这个域名最好自己注册备案一个),重定向至新域名,固定链接不会变动,使用缓存减轻服务器压力。 移动被封域名至弃用域名分组 域名池取出域名移动至正常使用域名 分组 添加域名解析 修改nginx配置新域名 记录已经替换的新域名(同一条数据多一个new_domain字段,一旦换新域名就替换。也可以直接替换,只是之前配置的旧域名就找不到了,看业务需求吧) 这样整个思路就很清晰了。 核心功能 有了思路就简单多了,准备开搞。 我把监测功能写成laravel command,方便自己使用,任务调度也没问题。 在测试监测代码时发现一个问题,访问频率太快容易403。尝试替换User Agent,client-ip等头部信息没用。最后只有一个办法了,HTTP代理。网上虽然有很多免费的,但是这东西不稳定那就是自己刨坑,公司有几台服务器,全部搭建HTTP代理,都是以前购买的阿里云服务器,就为了备案而买的。 搭建HTTP代理 安装tinyproxy sudo apt-get update sudo apt-get install tinyproxy 编辑配置文件: Port 8888 代理端口可自己修改 Allow 127.0.0.1 填写需要使用代理的服务器IP,只有该IP的服务器才能访问 nano /etc/tinyproxy.conf Ctrl+o 保存 Enter 确认 Ctrl+x 退出编辑器 启动重启停止命令 service tinyproxy start service tinyproxy restart service tinyproxy stop 阿里云服务器无备案检测 阿里云服务器配置未备案服务器会返回403状态码,按照状态码判断是否无备案。这里就需要保证域名池中的域名是无解析或者有解析但是配置正确,绝对不能自己搞出个403误判,这点应该是没问题的。 代码编写 use GuzzleHttp\Client; use GuzzleHttp\Exception\ConnectException; use Illuminate\Console\Command; use Illuminate\Http\Response; use Illuminate\Support\Facades\Event; use Lijianmin\DomainDetection\Events\BanDomainEvent; use Lijianmin\DomainDetection\Models\Domain; class DomainMonitorCommand extends Command { /** * The console command name. * * @var string */ protected $name = 'domain-detection:monitor'; /** * The console command description. * * @var string */ protected $description = 'monitor domain name is ban.'; const SLEEP_TIME = 2; const USER_AGENTS = [ "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; AcooBrowser; .NET CLR 1.1.4322; .NET CLR 2.0.50727)", "Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 6.0; Acoo Browser; SLCC1; .NET CLR 2.0.50727; Media Center PC 5.0; .NET CLR 3.0.04506)", "Mozilla/4.0 (compatible; MSIE 7.0; AOL 9.5; AOLBuild 4337.35; Windows NT 5.1; .NET CLR 1.1.4322; .NET CLR 2.0.50727)", "Mozilla/5.0 (Windows; U; MSIE 9.0; Windows NT 9.0; en-US)", "Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; Win64; x64; Trident/5.0; .NET CLR 3.5.30729; .NET CLR 3.0.30729; .NET CLR 2.0.50727; Media Center PC 6.0)", "Mozilla/5.0 (compatible; MSIE 8.0; Windows NT 6.0; Trident/4.0; WOW64; Trident/4.0; SLCC2; .NET CLR 2.0.50727; .NET CLR 3.5.30729; .NET CLR 3.0.30729; .NET CLR 1.0.3705; .NET CLR 1.1.4322)", "Mozilla/4.0 (compatible; MSIE 7.0b; Windows NT 5.2; .NET CLR 1.1.4322; .NET CLR 2.0.50727; InfoPath.2; .NET CLR 3.0.04506.30)", "Mozilla/5.0 (Windows; U; Windows NT 5.1; zh-CN) AppleWebKit/523.15 (KHTML, like Gecko, Safari/419.3) Arora/0.3 (Change: 287 c9dfb30)", "Mozilla/5.0 (X11; U; Linux; en-US) AppleWebKit/527+ (KHTML, like Gecko, Safari/419.3) Arora/0.6", "Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.8.1.2pre) Gecko/20070215 K-Ninja/2.1.1", "Mozilla/5.0 (Windows; U; Windows NT 5.1; zh-CN; rv:1.9) Gecko/20080705 Firefox/3.0 Kapiko/3.0", "Mozilla/5.0 (X11; Linux i686; U;) Gecko/20070322 Kazehakase/0.4.5", "Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.0.8) Gecko Fedora/1.9.0.8-1.fc10 Kazehakase/0.5.6", "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/535.11 (KHTML, like Gecko) Chrome/17.0.963.56 Safari/535.11", "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_7_3) AppleWebKit/535.20 (KHTML, like Gecko) Chrome/19.0.1036.7 Safari/535.20", "Opera/9.80 (Macintosh; Intel Mac OS X 10.6.8; U; fr) Presto/2.9.168 Version/11.52", ]; const PROXY = [ '', //本机 ... //自己搭建的代理 ]; /** * Execute the console command. * * @return void */ public function handle(Client $client) { //找出正在使用的域名,阿里云域名API获取域名列表,存入数据库。表中'detail'字段我保留了API返回的当前域名原始信息。 //'short_url'字段为保存时生成的微信短链接 $query = Domain::whereNotNull('short_url') ->where(\DB::raw("detail->'$.DomainGroupId'"), config('domain_detection.ali.group_id')) ->where('status', true); foreach ($query->cursor() as $index => $domain) { $result = $client->get($domain->short_url, [ 'allow_redirects' => false, 'headers' => [ 'USER-AGENT' => array_random(self::USER_AGENTS), //获取随机'USER-AGENT' ], 'proxy' => array_get(self::PROXY, ($index % count(self::PROXY))), //循环使用使用代理 'http_errors' => false, //屏蔽4xx 5xx状态码异常 ]); //访问微信短链接,获取重定向域名 $redirect_domain = $this->extractDomain($result->getHeaderLine('Location')); //如果重定向域名是否与当前数据域名匹配,否则被封了 if (strpos($redirect_domain, $domain->domain) !== false) { try { //微信没封再来检测备案 if ($client->get($redirect_domain, [ 'http_errors' => false, 'allow_redirects' => false, //如果无备案域名配置在当前服务器,自己访问是正常的,一定要其他主机访问才会返回403 'proxy' => last(self::PROXY), ])->getStatusCode() != Response::HTTP_FORBIDDEN) { $domain->touch(); //更新updated_at 时间戳,刷新最近检测时间 sleep(self::SLEEP_TIME); //HTTP代理比较少,频率低点比较好 continue; } } catch (ConnectException $exception) { if ( !$exception->getCode()) { $domain->touch(); sleep(self::SLEEP_TIME); continue; }; throw $exception; } } //被封了或者掉备案了,标记状态 $domain->status = false; $domain->save(); //这个事件主要处理域名更换操作,移动阿里云域名分组等等 Event::fire(new BanDomainEvent($domain)); sleep(self::SLEEP_TIME); } } // 提取连接中域名 public function extractDomain($url) { if (empty($url)) { return ''; } return array_get(parse_url($url), 'host'); } } 阿里云API看文档就好了,阿里云API-PHP-composer,对照文档使用。 提醒下API类: 例如http://domain.aliyuncs.com,看域名前缀找API。找到需要的API类后链式调用就API名称就好了,其实蛮友好的,只是文档没有说明。 //http://domain.aliyuncs.com AlibabaCloud::domain()->v20180129()->updateDomainToDomainGroup() //http://domainIntl.aliyuncs.com AlibabaCloud::domainIntl()->v20171218() 结语 幸好被微信短链接坑过,才会长记性,遇到其他问题居然把坑给利用起来了。其实核心就一个,用微信短链接来判断域名是否正常是目前想到的最简单的放法了,也不知道那些出售API的是不是有更好的方法,如果需求量大的话就找更多的HTTP代理,不然还是有点担心代理不够用,到时候全是403,不过403解封状态也很快,但是这种硬杠的做法还是不好,代理越多保证万无一失。
2019年01月28日
9,215 阅读
0 评论
2 点赞
2019-01-28
Google adsense 人工验证PIN-西联汇款-收款
前言 Google adsense邮寄第三封PIN码已经过去三个月了,已经不抱任何希望了。最后还是通过人工验证跳过PIN,这几天去办了张光大银行的储蓄卡,方便接收西联汇款。今天已经收到Google adsense签发的西联汇款,说说具体流程吧。 国内无法收到PIN码,人工验证 国内收到PIN码的几率非常小,有的人可以收到,但是绝大部分我相信这只是个传说,国内情况就不多说了,基本当废纸卖。 在第三封PIN码邮寄超过一个月就可以通过人工验证的方式跳过PIN码了。附加文件就上传身份证正反面好了,拍照扫描都行。广告发布商ID可以在账号-设置-账号信息中查看。 提交人工验证表单后,Google会很快审核,审核通过会发送邮件通知。刷新页面后,页面顶部红色验证PIN警告会消失,但是首页卡片还在,只是PIN输入次数变成了0次。 付款方式 过了PIN就简单了,毕竟PIN耽误了两个多月时间了。在国内最优的方式肯定是西联汇款,速度快还没有任何手续费,皆大欢喜。目前中国可以办理西联汇款的银行蛮多的,不过最方便的orzlee还是推荐中国邮政-西连汇款、光大银行-西联汇款,其中光大银行最方便,网银或柜台收汇一次后,可以手机APP收汇,真的非常方便。orzlee还是推荐先办卡后在来填写收款方式。 点击付款新增付款方式,选择西联汇款,名字和姓名输入自己姓名全拼。 至于大小写问题,以光大银行为例,登陆网银或者手机银行(手机银行搜索西连汇款),网银选择出国金融-西连汇款-西连收汇里面就可以看到收汇信息,这时按照银行填写最准确。如果没有看到信息,打银行电话转人工设置即可。 收汇 orzlee是在下午3点半左右填写的付款方式,Google adsense在次日凌晨2:28分签发汇款。 点击付款,选择最近的交易 点击说明文字查看西联汇款单 以光大银行西联收汇为例,第一次收汇必须使用网银或者前往柜台,网银选择出国金融-西连汇款-西连收汇,仔细看西连汇款单,信息全部都已经给你了,发汇人国家看汇款单最后一行,监控号码和汇款金额,其他信息自己补充填写,收汇人职业就写自己的职业好了。后面需要填写居住信息,非常简单。之后就可以使用手机APP收汇了。 结语 收美金不是第一次,收Google的美金还是第一次,整体体验和效率都很棒,关键Google adsense收入真的非常高。有很多事情只有自己尝试了才能深有体会,orzlee还是希望大家多多动手,实践比理论收益的经验更多。
2019年01月28日
5,039 阅读
0 评论
0 点赞
2019-01-22
Laravel 5.7 Broadcast + laravel echo 实现 WebSocket C/S 实时通信
前言 laravel Broadcast 平时项目中接触得比较少,最近公司年会活动需要做一个抽奖项目。要求用一太手机控制其他在抽奖页面的用户同时开始抽奖以及显示抽奖结果。提出项目需求时第一个想到的就是WebSocket,相对于使用AJAX轮询请求来说,WebSocket的实时性要高出不少,而且AJAX轮询请求服务器有太多不必要的请求,对于搞开发的来说这种不必要的请求就应该避免,不做多余操作。以前在项目中使用的WebSocket,也是抽奖项目,但是不是基于laravel框架,这次项目确实动力十足。 实现流程 使用laravel Broadcast广播系统(使用Redis) 触发Broadcast广播事件 laravel-echo-server 通过Redis收听到Broadcast广播事件 laravel-echo-server 使用socket.io下发给laravel-echo laravel-echo接收事件对象 实现步骤 配置 在app.php中启用广播系统服务提供者 /* * Application Service Providers... */ ... App\Providers\BroadcastServiceProvider::class, ... 修改.env中BROADCAST_DRIVER配置位置redis ... BROADCAST_DRIVER=redis ... laravel redis客户端扩展: 查看database.php中redis['client'] 如果使用predis请安装composer包 composer require predis/predis 如果使用phpredis就需要自己编译PHP的reids扩展。 phpredis效率要高于predis,按自己项目需求取舍 自定义配置文件config/lottery.php: 在该项目中使用的是扩展包开发,配置文件中有些命名空间应该修改为自己对应的,可以先定义,看完文章后再回过头来就明白了。 return [ /* * 频道名称 */ 'channel_name' => env('LOTTERY_CHANNEL_NAME','sweepstakes'), /* * 路由路径 */ 'route' => 'lottery', /* * 控制器 */ 'controller' => \Lijianmin\Lottery\Contollers\LotteryController::class, /* * 频道验证 */ 'broadcast' => \Lijianmin\Lottery\Broadcasting\LotteryChannel::class, /* * 缓存 */ 'cache' => [ 'prefix' => 'lottery-cache', 'expiration_time' => 5, //默认过期时间,小时 'redis_cache_db' => env('REDIS_CACHE_DB',1), ], ]; 注册广播事件 项目中事件包括 活动开始、入场、抽奖开始、开奖。 由于合作开发,我将它写出扩展包,定制接口方便对接。 首先创建出所需事件,如果不需要扩展包开发可以: php artisan make:event event-name 或者 在app/Providers/EventServiceProvider.php成员变量$listen内追加: /** * The event listener mappings for the application. * * @var array */ protected $listen = [ 'App\Events\EventName' => [ 'App\Listeners\ListenserName', ], ... ]; ... 然后执行生成事件命令 php artisan event:generate 事件注册完毕后需要实现Illuminate\Contracts\Broadcasting\ShouldBroadcast接口,需要实现broadcastOn方法,该方法返回一个频道或者一个频道数组。broadcastAs方法自定义广播名称,我希望还是自己定义,不然后面laravel-echo会很尴尬: use Illuminate\Broadcasting\InteractsWithSockets; use Illuminate\Broadcasting\PresenceChannel; use Illuminate\Contracts\Broadcasting\ShouldBroadcast; use Illuminate\Contracts\Queue\ShouldQueue; use Illuminate\Foundation\Events\Dispatchable; use Illuminate\Queue\SerializesModels; class ActivityStart implements ShouldBroadcast { use Dispatchable, InteractsWithSockets, SerializesModels; public $option; public function __construct($option = []) { $this->option = $option; } public function broadcastOn() { return new PresenceChannel(config('lottery.channel_name')); } public function broadcastAs(){ return 'activityStart'; } } config('lottery.channel_name')应该修改为自己定义的频道名称,或者也可以像orzlee一样使用配置文件定义,方便修改。 每个事件都需要创建对应的文件,如果只是为了通知前端可以不需要事件处理器。 对于频道分为三种: Channel-普通频道。 PrivateChannel-私有频道,需要自定义授权认证。 PresenceChannel-类似于私有频道,但是比私有频道更加高级。 频道授权 首先自己创建一个频道: php artisan make:channel LotteryChannel 在routes/channels.php中添加频道(主要是频道授权验证): Broadcast::channel(config('lottery.channel_name'),config('lottery.broadcast')); 实现授权验证方法: PrivateChannel私有频道只需要返回true或者false,PresenceChannel频道在验证成功就需要返回一个用户信息数组,验证失败返回false或者null。 class LotteryChannel { ... /** * Authenticate the user's access to the channel. * * @param \App\User $user * @return array|bool */ public function join() { if(\Auth::check()){ $user = \Auth::user(); return [ 'user' => [ 'id' => $user->id, 'name' => $user->names, ], 'isLotteryDraw' => $this->lotteryService->isLotteryDraw() ]; } return false; } } 搭建laravel-echo-server服务: 安装最新版NodeJS和npm(如果已经安装可以跳过): curl -sL https://deb.nodesource.com/setup_9.x | sudo -E bash - sudo apt-get install -y nodejs ##如果需要指定版本修改链接中的数字即可 curl -sL https://deb.nodesource.com/setup_8.x | sudo -E bash - 进入项目目录执行: 先把需要安装的一次性装完,orzlee已经写成command,部署服务器一行命令就全部安装好了 npm install -g laravel-echo-server npm i --save socket.io-client npm i --save laravel-echo npm install 初始化laravel-echo-server: 按实际情况填写,选错了也不要紧,到时候在修改 laravel-echo-server init 项目下会生成laravel-echo-server.json配置文件: 注意authHost,devMode,port,protocol几个重要参数就好了。 { "authHost": "http://127.0.0.1:8000", "authEndpoint": "/broadcasting/auth", "clients": [], "database": "redis", "databaseConfig": { "redis" : {}, "sqlite": { "databasePath": "/database/laravel-echo-server.sqlite" } }, "devMode": true, "host": null, "port": "6001", "protocol": "http", "socketio": {}, "sslCertPath": "", "sslKeyPath": "", "sslCertChainPath": "", "sslPassphrase": "", "subscribers": { "http": true, "redis": true }, "apiOriginAllow": { "allowCors": false, "allowOrigin": "", "allowMethods": "", "allowHeaders": "" } } 启动laravel-echo-server: laravel-echo-server start 前端laravel-echo对接 修改resources/js/bootstrap.js(5.6文件在public目录下): 默认内容是注释了的,而且使用的是pusher,说实话pusher是付费服务,而且服务器都在国外,对国内实在是不友好。有想法的可以去了解下,每天有免费请求限制,主要是用于开发调试。 /** * Echo exposes an expressive API for subscribing to channels and listening * for events that are broadcast by Laravel. Echo and event broadcasting * allows your team to easily build robust real-time web applications. */ import Echo from 'laravel-echo' window.io = require('socket.io-client'); // window.Pusher = require('pusher-js'); window.Echo = new Echo({ broadcaster: 'socket.io', host: window.location.hostname + ':6001' }); 其实做完这一步就可以打包编译NodeJS了: prod代表生产环境,如果还需要调试可以使用dev或者watch,watch模式文件改动会自动编译。 npm run prod 前端页面配置事件监听: 注意监听事件名称,如果在上文中使用了broadcastAs命名事件名称,那么只需要在名称前加入.就好了,不然你需要把命名空间以.分割,例如:App.Lottery.ActivityStart,实在是太罗嗦了。 <!-- Scripts --> <script src="//{{ Request::getHost() }}:6001/socket.io/socket.io.js"></script> <script src="{{ asset('js/app.js') }}"></script> <script> let lottery_server = Echo.join('{{ config('lottery.channel_name') }}'); lottery_server.here(function (users) { //本来使用私有频道,但是找了半天也没有找到连接成功的事件才改用`PresenceChannel`频道, //在`here`事件会在连接成功返回所有当前频道的用户信息 console.log(e) }); lottery_server.joining(function (user) { //新用户加入频道返回用户信息 console.log(e) }); lottery_server.listen('.activityStart', (e) => { //活动开始 console.log('activityStart',e); }); lottery_server.listen('.admission', (e) => { //入场,其实这个事件在`PresenceChannel`频道下已经多此一举了 console.log('admission',e); }); lottery_server.listen('.newcomerAdmission', (e) => { //新用户加入,这个也是多余的 console.log('newcomerAdmission',e); }); lottery_server.listen('.lottery', (e) => { //摇奖事件 console.log('lottery',e); }); lottery_server.listen('.lotteryResult', (e) => { //摇奖结果事件 console.log('lotteryResult',e); }); </script> 到此整个前对接已经完成。 后端事件触发 其实最简单的就是后端。例如用户入场,用户登陆后触发AdmissionEvent事件,在定义事件时构造函数中设定前端所需的参数,数组也好对象也好,都会传给前端。 发送入场事件: event(new AdmissionEvent()); 每个事件我都会在构造函数中添加option数组,除了必须传递的参数以外方便附加其他参数。 结语 像这种类似于C/S模式开发要考虑的情况非常多,用户已经入场,关闭浏览器再次入场情况,首先名单不能重复添加,已中奖用户在入场时必须实时传递,在开奖状态中用户离开页面,然后在进入页面也应该保持前端正在开奖的效果,所以开奖状态要实时传递。还有很多很多需要考虑的问题,然后经过不停的测试。在年会上需要开31个奖,真是心惊胆战的,测试是还是有些问题的,还好在年会上没有出大漏子(真心不是后端问题,前端开奖结果效果问题),包括后端都去调试前端开奖动画效果,真心累。自从写好laravel-echo部署上线,真的没有出现过问题,还是很稳定的。测试时,最多30多台手机,活动开始之前有个图片遮罩,活动开始事件触发全部向上拉开遮罩,真的很壮观。多台手机之间延迟非常小,体验很不错,年会现场效果也是非常棒。
2019年01月22日
5,365 阅读
0 评论
0 点赞
2019-01-10
广告投放,广告收入
前言 Google给我邮寄的PIN已经发出第三封了,我估计国内能妥投的概率非常低(都当废纸卖了),等月底了上传资料验证身份好了。这几天一直在研究广告,包括广告收入和广告投放。之前没接触过广告这一块,不过多操作几次还是能得到不少经验的。 广告收入 网站有些访问量了,闲着没事干想着申请了Google Adsense投入广告。其实站点访问量并不大,但是好奇想着试试网站盈利是一种怎样的体验... Google Adsense的收入对于orzlee来说只是一个尝试,其中出现了很多简写单词: CPC - Cost PerClick 每次点击费用,即点击单价。 CPM - Cost Per Mile 千次展示费用,广告展示一千次需要支付的费用。 eCPM - Effective Cost Per Mille 千次展示收入。 RPM - Revenue Per Mille 千次展示收入(与eCPM同意,只是不同的广告联盟名称有所不同)。和CPM类似,RPM是针对广告展示商(比如Adsense商户)而言的。 CTR - Click-throughRate 点击率,点击次数占展示次数的百分比。 作为广告流量主来说这些最基本的还是要了解。Google Adsense目前自动广告虽然已经非常聪明了,但是还达不到完美,如果懒得折腾确实非常方便。很多样式并不是特别友好,甚至有些体验很糟糕。 orzlee就在一个以手机流量为主的站点使用过自动广告,经常展示广告会把整个网站向下顶,广告显示在网站顶部。说实话我都不想用了,其实是网站前端代码一塌糊涂,Google也无能为力。自动广告主要还是要靠前端布局规范(说实话我真的不喜欢折腾HTML,特别是css,这些才是真正的无聊),那样自动广告就会非常融洽,怎么也不会那么突兀。 最近也试了一下Google Adsense广告单元,只是可以稍微自定样式,这样出来的广告就比较完美了。而且Google Adsense的广告很聪明,移动端会自适应布局,不会乱七八糟。 Google Adsense几乎是所有广告联盟中收益最高的了,但是也非常严格。orzlee也试过其他的广告商,如popcash、exoclick这些,说真的一天收入不如Google Adsense几次点击。貌似他们只算展示量,点击次数用来计算CTR,而CTR会提高你的eCPM。也就是说点击率高,你的千次展示收入就会越高,纯靠展示量获得收入,如果展示量很高但是点击率低,那你网站流量对该广告没有起到什么作用。归根结底就是展示广告目的就是引导用户广告发布商的网站(貌似现在没什么注册、安装的广告收入了,不过这类广告确实恶心),网站流量大,广告没人点,自然没什么收入。就连Google AdsenseRPM也是如此,一直在不停的波动,只不过Google Adsense计算点击收益,这个收益真的非常可观,我见过一次点击1.x美金,真是豪。 还忽略了一点,是我最喜欢也是最讨厌的,那就是AdBlock。这估计是所有站长的克星,现在用AdBlock的人真的非常多,这减少站长一笔收入,很烦。这东西真的没什么好的办法,Pornhub 使用WebSocket绕过广告屏蔽插件确实很犀利,但是对于一般的站长都是依赖广告联盟,想绕过广告联盟展示广告没什么资源。也有很多站长会提示自己的用户,要求对自己网站关掉AdBlock,甚至有些更加严格,网站都不让用户看。其实做站不容易,屏蔽掉哪些恶心的弹窗,入侵式广告就好了。 广告投放 广告投放只是小试牛刀,体验一下。我在popcash上投放的广告,最低0.5美金千次展示。 刚开始几乎没有怎么过滤流量,但是设置了单日最大金额,我设置的是1美金,创建一个Campaigns,一直在pending。等我过了两分钟再刷新立马1.3美金消费,我都设置单日最大限额1美金了,居然都拦不住,再去看站点流量真是猛,流量非常大,但是因为投放没有过滤,进来的流量几乎都是无效流量,短短几分钟就退回平常值了。我暂停了Campaigns,重新编辑过滤,限制非常严格,让流量更加精准有效。现在几乎一小时花0.01美金,流量不会瞬间上去了。 观察了几天流量小了很多,但是质量要高不少,最起码进来的流量停留时间要比开始长得多。毕竟不是做垃圾站,量多不如量精。首先我限制了流量语言,orzlee的站点并不是多语言站,先限制中文,国家也只选了几个中文用户较多的。还有一个就是流量来源,popcash做的不是很好,只有比较简单的来源筛选,其他的就看自己了。比如说浏览器、操作系统、设备。 其实广告投放还是谨慎点,多花点时间观测流量趋势,如果太多流量质量不好赶紧修改广告计划,不求流量大,只求流量精准。像个人站长广告投放可不能像大公司一样财大气粗,最重要的还是要自己站点质量,要留的住客户才行。 结语 什么事情都自己尝试一下,经验收获比看书看文章强多了。做网站运营广告是第一收入来源,当然不包括收费站点。终于知道站长有多恨AdBlock了,但这个是双向的,如果以前广告不那么恶心,也不至于逼得作者开发AdBlock,友好的广告也没人讨厌。中国垃圾站群真是多如牛毛,超恶心,搜索引擎一搜全是垃圾站。原创文章也越来越少了,写文章很花时间,多尊重作者吧。自己尝试了就明白他人的难处,总之多动手,学到的经验比现在赚到的钱价值高多了。现在学到的知识,以后会用金钱成倍的返还给你。
2019年01月10日
7,150 阅读
2 评论
1 点赞
2018-12-28
逗比根据地打不开,doub.io无法解析,最新逗比根据地镜像地址
前言 doub.io从12月7号就发现已经无法解析域名,显然出事了。在逗比根据地看过不少文章,很可惜。 备份站 orzlee在网上找到一些doub.io的备份站点: 镜像站 doubmirror.cf 镜像站 doubibackup.com 逗比云备份镜像站 softsmirror.cf 逗比云备份镜像站 doubiyunbackup.com 逗比根据地的GitHub地址 ToyoDAdoubi 以上地址可能大多数被墙,需要代理访问。 结语 陆陆续续感觉有好多大佬都销声匿迹了,还记得2013年的时候GoAgent科学上网,每隔几天就找代理IP,到现在自己搭建科学上网服务器,途中大佬们项目都被迫删除,希望平安。
2018年12月28日
7,005 阅读
0 评论
0 点赞
2018-12-18
laravel nova 权限管理工具
前言 前几个月一直在折腾laravel nova,不得不说它的扩展性非常强。laravel nova packages每天都会有很多新鲜的扩展包,生态很好。spatie laravel-permission是非常不错的Role-based access control RBAC扩展包,orzlee在laravel 5.3就在使用了,这里主要说说spatie laravel-permission的laravel nova权限管理工具。 Larval nova packages 目前有三款laravel nova packages包是基于spatie laravel-permission开发的,功能上大同小异,各有特色。 1. vyuldashev/nova-permission: 这是最早出现基于spatie laravel-permission的laravel nova packages。支持自定义资源,本地化支持。 orzlee一开始也是使用的这款扩展包,但是没几天我就发现了一个问题,因为laravel nova的Relationships并不会调用spatie laravel-permission自带的权限或角色附加、分离方法,所以在给用户添加权限时不会触发权限缓存更新。这就必须等待缓存过期或者手动清理缓存。 这个问题一直没有解决,orzlee也关注了Issues:Cache not invalidated on attaching a role or permission to user好长一段时间,然而并没有进展。最后还是提问者重新创建了一个新的包insenseanalytics/laravel-nova-permission。 2. insenseanalytics/laravel-nova-permission: 这款包主要是使用ForgetCachedPermissions中间件来判断当前路由是否为附加或分离请求(attach,detach),然后过滤出附加或分离权限的请求,执行缓存清除。 分离时没有问题,但是附加在路由判断上有点问题。orzlee在测试的时候发现附加怎么也无法清除缓存,经过调试发现url没有匹配到。 class ForgetCachedPermissions { /** * Handle the incoming request. * * @param \Illuminate\Http\Request $request * @param \Closure $next * * @return \Illuminate\Http\Response */ public function handle($request, $next) { $response = $next($request); if ($request->is('nova-api/*/detach') || $request->is('nova-api/*/*/attach/*')) { ##作者判断了attach,但是orzlee的请求路由为:http://127.0.0.1:8000/nova-api/admins/1/attach-morphed/permissions $permissionKey = (Nova::resourceForModel(app(PermissionRegistrar::class)->getPermissionClass()))::uriKey(); if ($request->viaRelationship === $permissionKey) { app(PermissionRegistrar::class)->forgetCachedPermissions(); } } return $response; } } orzlee已经向作者提交Issues了。 orzlee在给作者RP包语言本地化时有一个权限搜索翻译的功能没有被作者合并。作者觉得使用自定义资源功能让用户自行实现,这样也不错,不过度化设计。 权限是由开发者添加,而使用的时候基本上是管理员在操作。现在添加权限是一个搜索选择器,管理员需要给某个用户或管理员添加权限就必须搜索。权限在添加时为了多语言化必用标识,管理员在搜索时按照本地化语言搜索会方便很多。 orzlee还是把实现方法分享出来吧: GuardFilterHandelTrait主要功能是按guard_name过滤掉不属于附加用户的权限,防止多guard_name权限混乱。 use Laravel\Nova\Http\Requests\NovaRequest; trait GuardFilterHandelTrait { /** * Override the applyFilters method to add the guard_name condition when filtering * * @param \Laravel\Nova\Http\Requests\NovaRequest $request * @param \Illuminate\Database\Eloquent\Builder $query * @param array $filters * * @return \Illuminate\Database\Eloquent\Builder */ protected static function applyFilters(NovaRequest $request, $query, array $filters) { $query = parent::applyFilters($request, $query, $filters); if ($model = head($request->__memoized)) { $guard_name = $model->guard_name ?? self::getGuardForModel(get_class($model)); $query->where('guard_name', $guard_name); } return $query; } /** * @param string model * * @return string|null */ public static function getGuardForModel(string $model) { return collect(config('auth.guards')) ->map(function ($guard) { return config("auth.providers.{$guard['provider']}.model"); })->search($model); } } PermissionSearchTranslationTrait搜索权限时会按照本地化语言反向找出对应权限。 trait PermissionSearchTranslationTrait { use GuardFilterHandelTrait; /** * Override the applyFilters method,title field translation */ public function title() { return __('laravel-nova-permission::permissions.display_names.'.$this->name); } /** * Rewrite the applySearch method to apply translation field search * * @param \Illuminate\Database\Eloquent\Builder $query * @param string $search * @return \Illuminate\Database\Eloquent\Builder */ protected static function applySearch($query, $search) { return $query->where(function ($query) use ($search) { if (is_numeric($search) && in_array($query->getModel()->getKeyType(), ['int', 'integer'])) { $query->orWhere($query->getModel()->getQualifiedKeyName(), $search); } $model = $query->getModel(); $connectionType = $query->getModel()->getConnection()->getDriverName(); $likeOperator = $connectionType == 'pgsql' ? 'ilike' : 'like'; $trans_search = array_keys(preg_grep("/$search/",array_dot(__('laravel-nova-permission::permissions.display_names')))); foreach (static::searchableColumns() as $column) { $qualify_column = $model->qualifyColumn($column); foreach ($trans_search as $t_search){ $query->orWhere($qualify_column, $likeOperator, '%'.$t_search.'%'); } $query->orWhere($qualify_column, $likeOperator, '%'.$search.'%'); } }); } } 自定义Role & Permission资源(注意命名空间替换成自己项目对应的): use App\Traits\Resources\GuardFilterHandelTrait; use \Insenseanalytics\LaravelNovaPermission\Role as BaseRole; class Role extends BaseRole { use GuardFilterHandelTrait; } use App\Traits\Resources\PermissionSearchTranslationTrait; use \Insenseanalytics\LaravelNovaPermission\Permission as BasePermission; class Permission extends BasePermission { use PermissionSearchTranslationTrait; } 按照作者文档自定义角色和权限类: // in app/Providers/NovaServiceProvider.php public function tools() { return [ // ... \Insenseanalytics\LaravelNovaPermission\LaravelNovaPermission::make() ->roleResource(CustomRole::class) ->permissionResource(CustomPermission::class), ]; } 如果你还没有发布语言文件请执行: php artisan vendor:publish --provider="Insenseanalytics\LaravelNovaPermission\NovaPermissionServiceProvider" 创建自己本地化语言文件,例如在项目路径./resources/lang/vendor/laravel-nova-permission/创建一个zh-CN文件夹,复制en中的所有文件到zh-CN,然后自己翻译。display_names就是权限name对应的本地化翻译。 'display_names' => [ 'test' => '测试', 'delete' => '删除', 'edit' => '编辑', 'create' => '创建', ], 现在应该支持本地化权限搜索了。或者可以直接使用包jianminlee/laravel-nova-filter。 该包还支持基于laravel nova的权限验证,非常方便,在nova资源中使用PermissionsBasedAuthTrait Trait,然后定义一个静态变量: class YourNovaResource extends Resource { use \Insenseanalytics\LaravelNovaPermission\PermissionsBasedAuthTrait; public static $permissionsForAbilities = [ 'viewAny' => 'view products', 'view' => 'view products', 'create' => 'create products', 'update' => 'update products', 'delete' => 'delete products', 'restore' => 'restore products', 'forceDelete' => 'forceDelete products', 'addAttribute' => 'add product attributes', 'attachAttribute' => 'attach product attributes', 'detachAttribute' => 'detach product attributes', ]; .... } 详细文档可以取项目地址查看。insenseanalytics/laravel-nova-permission 3. DigitalCloud/nova-permission-tool: 今天刚刚发布的的,该包根据资源自动生成对应权限,听上去高大上,但是会多出不少多余的权限。因为我们的资源不一定会实现所有功能,到时候添加权限就会眼花缭乱了。orzlee认为还是自定义的好。 作者还自定义角色和权限的字段,在操作上体验好很多。凡是都有两面性,如果项目比较大(权限多)的情况下体验就可想而知了。 结语 这三款包都各有特色,同一功能的包就有这么多,这些还都只是基于spatie laravel-permission的,还有其他作者自己实现的权限管理工具。回过头来,还是得靠自己,工具再多也不一定都适合自己的项目,好好学Vue吧!
2018年12月18日
5,830 阅读
0 评论
0 点赞
2018-12-05
tmux强大的终端复用软件
前言 tmux功能非常强大,可以让用户在一个终端内管理多个分离的会话,窗口及面板,对于同时使用多个命令行,或多个任务时非常方便。orzlee其实一直把他作为后台工具来使用。 安装 tmux安装非常简单: apt update apt install tmux 像GCP服务器就自带tmux。 基本使用 创建一个新会话: tmux [new -s 会话名 -n 窗口名] ########### tmux new -s frp 大多数情况没有使用过-n参数。 列出所有会话: tmux ls 恢复会话: tmux at [-t 会话名] ########### tmux at -t frp 关闭会话: tmux kill-session -t 会话名 ########### tmux kill-session -t frp 组合键命令(crtl+b) 以下命名都必须在crtl+b组合键按下之后输入: 会话 :new<回车> 启动新会话 s 列出所有会话 $ 重命名当前会话 窗口 (标签页) c 创建新窗口 w 列出所有窗口 n 后一个窗口 p 前一个窗口 f 查找窗口 , 重命名当前窗口 & 关闭当前窗口 调整窗口排序 swap-window -s 3 -t 1 交换 3 号和 1 号窗口 swap-window -t 1 交换当前和 1 号窗口 move-window -t 1 移动当前窗口到 1 号 分割窗格 % 垂直分割 " 水平分割 o 交换窗格 x 关闭窗格 ⍽ 左边这个符号代表空格键 - 切换布局 q 显示每个窗格是第几个,当数字出现的时候按数字几就选中第几个窗格 { 与上一个窗格交换位置 } 与下一个窗格交换位置 z 切换窗格最大化/最小化 同步命令到其他窗格 可以将 :setw synchronize-panes on/off 其他 d 退出 tmux(tmux 仍在后台运行) t 窗口中央显示一个数字时钟 ? 列出所有快捷键 : 命令提示符 其他不常用的就不写了。 一般使用: ##创建会话 tmux new -s test ##ctrl+b % 垂直分割窗口 ##ctrl+b " 水平分割窗口 ... ##ctrl+b q [1/2/3/4/5]切换窗格 ##ctrl+b d 让tmux后台 ##恢复会话 tmux at -t test ##挨个执行 exit 或者 kill tmux kill-session -t test 结语 tmux功能强大,熟练操作后真的很方便。特别是退出会话后恢复,进程不会终止,一直在后台运行。至于更多功能可以去看看Tmux 使用手册。
2018年12月05日
3,324 阅读
0 评论
0 点赞
2018-12-05
frp内网穿透
前言 在项目中经常涉及到需要验证域名的API开发,这些API开发需要在线调试。通常我们可以解析域名到本地,但是国内运营商封端口、无外网ip,线上调试又相对麻烦。frp是一个可用于内网穿透的高性能的反向代理应用,支持 tcp, udp, http, https 协议。让它实现内网穿透,域名解析到服务器,让服务器通过其他端口访问本地web服务,轻松实现API调试。 安装使用 搭建frp服务需要一台服务器,基本无配置要求。 下载frp(frp发布地址): wget https://github.com/fatedier/frp/releases/download/v0.21.0/frp_0.21.0_linux_amd64.tar.gz 解压tar.gz: tar -zxvf frp_0.21.0_linux_amd64.tar.gz 编辑服务端配置文件: cd frp_0.21.0_linux_amd64 nano frps.ini 如果不许要其他功能,frps.ini保留下面三行即可(端口可自行更改) ############### [common] bind_port = 4443 ##frp服务端口 vhost_http_port = 80 ##http协议访问端口 启动服务: ./frps -c ./frps.ini 后台运行可以使用tmux工具,安装使用可以看orzlee的另一篇文章tmux强大的终端复用软件 客户端(windows) windows 32位 windows 64位 包含服务端和客户端。 编辑客户端配置文件: 解压zip后进入目录,编辑frpc.ini(推荐编辑器Sublime Text)。 [common] server_addr = x.x.x.x ###服务端ip地址或域名 server_port = 4443 ###服务端端口 [web] type = http ###http https local_port = 8000 ###本地web服务访问端口 custom_domains = www.yourdomain.com ###服务器域名地址 启动客户端: ./frpc -c ./frpc.ini 现在通过浏览器访问 http://www.yourdomain.com 即可访问到处于内网机器上的 web 服务。 结语 有了内网穿透,对于验证域名的API开发要省事不少,本地调试修改代码都非常方便,提高开发效率。而部署frp比较简单,当然还有更多功能,可以看看中文文档。
2018年12月05日
3,414 阅读
0 评论
1 点赞
1
...
3
4
5