首页
留言
动态
归档
推荐
音乐
工具
Search
1
Emby公益服-上万部电影电视剧免费看
56,532 阅读
2
openwrt-docker部署lxk0301京东自动签到脚本
12,137 阅读
3
QuantumultX-京东签到撸京东豆
10,448 阅读
4
LXK0301京东签到脚本-自动提交互助码
9,006 阅读
5
微信-域名被封监测以及自动更换被封域名
8,742 阅读
随便写写
科学上网
Web开发
瞎折腾
登录
Search
标签搜索
quantumultx
laravel
openwrt
laravel nova
laradock
telegram
薅羊毛
google adsense
jd_scripts
京东签到
ubuntu
oh-my-zsh
web开发环境
nginx
工具
shadowsocks shadowsocksR
RBAC
权限管理
内网穿透
Python
orzlee
累计撰写
43
篇文章
累计收到
595
条评论
首页
栏目
随便写写
科学上网
Web开发
瞎折腾
页面
留言
动态
归档
推荐
音乐
工具
搜索到
43
篇与
orzlee
的结果
2020-03-27
Laradock-PHPStorm开启XDebug
前言 前几天写了一篇Laradock-部署本地开发环境,使用了几天发现确实很方便。最近又折腾点东西,没有XDebug确实不顺手,网上找了不少资料和教程,有些坑和解决方案记录一下。 开搞 开启XDebug 需要laradock容器php-fpm、workspace都安装XDebug,如果已经安装则不需要管它,否则需要修改你的laradock目录\.env。 PHP_FPM_INSTALL_XDEBUG=true WORKSPACE_INSTALL_XDEBUG=true PHP_IDE_CONFIG=serverName=laradock PHP_IDE_CONFIGXDebug服务名看你自己,你想改就改,不想改默认就行。 然后再重新build docker-compose build php-fpm workspace。每次build真心漫长,就一个扩展也要整个编译,这里不得不吐槽下。强烈建议科学上网或者修改laradock源地址(修改源地址我没有验证过,不会科学上网你可以试试): # If you need to change the sources (i.e. to China), set CHANGE_SOURCE to true CHANGE_SOURCE=true # Set CHANGE_SOURCE and UBUNTU_SOURCE option if you want to change the Ubuntu system sources.list file. UBUNTU_SOURCE=aliyun 配置xdebug.ini 两处都要修改你的laradock目录\workspace\xdebug.ini、你的laradock目录\php-fpm\xdebug.ini: //xdebug.remote_host="docker.for.win.host.internal" xdebug.remote_connect_back=1 xdebug.remote_port=9000 xdebug.idekey=PHPSTORM xdebug.remote_autostart=0 xdebug.remote_enable=0 xdebug.cli_color=0 xdebug.profiler_enable=0 xdebug.profiler_output_dir="~/xdebug/phpstorm/tmp/profiling" xdebug.remote_handler=dbgp xdebug.remote_mode=req xdebug.var_display_max_children=-1 xdebug.var_display_max_data=-1 xdebug.var_display_max_depth=-1 两种方法: 修改xdebug.remote_host值为Windows:docker.for.win.host.internal,MacOS:docker.for.mac.host.internal。xdebug.remote_connect_back修改为0。 注释掉xdebug.remote_host配置,不指定主机xdebug.remote_connect_back=1,让XDebug自动捕获主机。 网上很多教程说是改成xdebug.remote_host=docker.for.win.localhost或者xdebug.remote_host=docker.for.mac.localhost,这是之前的写法,巨坑。直接使用第二种方法省得坑爹。 默认xdebug.ini配置文件是不能动态修改的,每次修改都必须build,为了方便可以修改docker-compose.yml文件 ### Workspace Utilities ################################## workspace: build: context: ./workspace args: ... volumes: ... - ./workspace/xdebug.ini:/etc/php/${PHP_VERSION}/cli/conf.d/xdebug.ini ###添加磁盘挂载 ### PHP-FPM ############################################## php-fpm: build: context: ./php-fpm args: ... volumes: ... - ./php-fpm/xdebug.ini:/usr/local/etc/php/conf.d/xdebug.ini ###添加磁盘挂载 ... 这样修改后可以热修改文件,就像配置nginx站点一样方便 配置PHPStorm 新建一个服务,服务名称就是这里配置的PHP_IDE_CONFIG=serverName=laradock laradock,Host填写你本地站点访问地址,开启Use path mappings,填写你的映射地址/var/www/you-project,然后OK。 点击Run菜单: 点击+添加一个PHP Remote Debug: 勾选Filter debug connection by IDE key,选择你的刚刚添加的Server,IDE key填写PHPSTORM,这个值是在xdebug.ini配置的。 再点击run, Debug 'xxx'就可以调试了。 我在调试的时候发现PHPStorm时不时断点,提示错误:Remote file path '/var/www/xxxxx artisan' is not mapped to any file path in project,这是因为XDebug也会调试php cli,如果不需要可以关掉: 进入workspace容器docker-compose exec workspace bash。 编辑cli的xdebug.ini,nano /etc/php/7.4/cli/conf.d/xdebug.ini,注意你自己的PHP版本,修改两项配置:xdebug.remote_autostart=0 xdebug.remote_enable=0 保存退出就好了,不会再调试php cli了。需要的时候再打开好了。 配置后所有laradock容器中的站点全部都有效。你可以试着访问容器中其他站点,当前项目也会进入调试模式,而且PHPStorm又出现错误:Remote file path '/var/www/xxxxx' is not mapped to any file path in project。因为当前目录映射找不到。要重新打开其他项目,配置下目录映射,然后再开启动调试,不要开多个PHPStorm一起调试,会疯掉的。 结语 在这里浪费不少时间,确实够折腾的,不过确实长记性了,最近又开始折腾Laravel nova,2018年刚出的时候确实很多不完善,一直使用Laravel admin,总得来说还是没有Laravel nova那么舒服。现在Laravel nova经过那么长时间过度已经很完善了,是时候使用Laravel nova继续折腾了。
2020年03月27日
1,521 阅读
0 评论
0 点赞
2020-03-23
Golang oh-my-zsh 自定义插件批量更新程序
前言 Golang很火啊,引用维基百科词条说明 Go(又称Golang)是Google开发的一种静态强类型、编译型、并发型,并具有垃圾回收功能的编程语言。。并发对于我来说接触很少,几乎没有写过多线程并发之类的程序。之前折腾Python的时候使用过多线程,执行效率要高不少。PHP也有多线程,我真是一次都没有用到过。 oh my zsh(有兴趣可以了解下oh-my-zsh强大的zsh配置管理)会自动提示更新,但是自己安装的一些插件却不会一起更新,最近翻了一遍Go语言官方文档,写了个oh my zsh自定义插件更新程序。 开搞 oh my zsh的自定义插件都安装在了/root/.oh-my-zsh/custom/plugins目录下,我随便找了个已安装的插件试着git pull,果然没有更新。插件也不多,就两个。但是我看了Go语言文档有劲没处使啊,就要折腾下。 我不仅要写,我还要多线程,各种多线程。 第一步,找出需要更新的目录 上网查、看文档,折腾出这么个玩意: func GetDir(path string) ([]string, error) { var s []string rd, err := ioutil.ReadDir(path) if err != nil { fmt.Println("read dir error:", err) return s, err } for _, dir := range rd { if dir.IsDir() { s = append(s, path+"/"+dir.Name()) } } return s, err } ioutil.ReadDir读取传入路径下所有文件和目录,然后找出所有目录返回,这样一个简单的获取oh my zsh自定义插件目录的函数就封装好了。 第二步,进入目录更新 其实写这篇文章的时候我已经修改了好几遍程序了,还是贴最终的吧。 更新函数,遍历目录然后执行pull函数拉代码。 var wg sync.WaitGroup ... func Update(path string) { //不异步不需要执行 defer wg.Done() paths, _ := GetDir(path) if len(paths) != 0 { for _, dir := range paths { //添加进程数 wg.Add(1) fmt.Println("get pull dir:", dir) go Pull(dir) } } } 最开始的版本没有使用sync.WaitGroup,会出现问题。涉及到Channels,主线程也是用多线程,update函数中也有多线程。Channels在使用<- ch接收值时会堵塞,直到ch接收到Channels的值后继续执行。那么不使用sync.WaitGroup堵塞进程,还没等子线程/协程执行完主线程就已经执行完毕退出了。 func Pull(path string) { //每执行一个进程都要标记完成 defer wg.Done() //command := exec.Command("sh", "-c", "cd ", path, "&&", "git", "pull") command := exec.Command("sh", "-c", "cd " + path + "&& git pull") ch := make(chan int) go Stdout(command, ch) fmt.Println("ProcessState PID:", <- ch) } exec.Command有点讲究,起初我直接把命令作为第一个参数传入是不行的,需要把命令拆分成参数才能执行。 后面我又把获取参数封装成了Stdout函数,其实我想折腾下Channels。 第三步,返回命令行输出 折腾起来浑身是劲,编写命令行返回输出函数,我要看到所有git pull的执行结果。 func Stdout(command *exec.Cmd, ch chan int) { stdout, _ := command.StdoutPipe() stderr, _ := command.StderrPipe() if err := command.Start(); nil != err { fmt.Println("command start err:", err) ch <- 0 return } pid := command.Process.Pid output(stdout, pid) output(stderr, pid) err := command.Wait() //等待执行完成 if nil != err { fmt.Printf("Process PID %d command wait err: %s", pid, err) } ch <- command.ProcessState.Pid() return } command.StdoutPipe,command.StderrPipe分别获取命令正确输出返回和错误输出返回,好打印出来。command.Wait()等待命令结束。exec.Command需要好好看看文档。直接执行命令不需要这么麻烦,使用exec.Run()就行,但是我要捕获命令输出,所以相对麻烦。 func outputLine(oReader *bufio.Reader, pid int) { for { line, err := oReader.ReadString('\n') if nil != err{ if io.EOF != err { log.Printf("Process PID %d: err %v", pid, err) } break } log.Printf("Process PID %d: %s", pid, line) } } func output(stdout io.ReadCloser, pid int) { oReader := bufio.NewReader(stdout) outputLine(oReader, pid) } 这两个函数主要是重复代码太多,封装了下,outputLine函数主要是按行输出命令返回信息。 第四部,主函数 主函数我改进接收参数功能,在执行的时候可以接收多个目录参数,然后并发git pull。 func main() { // 接收多目录参数 args := os.Args[1:] if len(args) != 0 { for _, path := range args { fmt.Println(path) wg.Add(1) go Update(path) } } else { path := "/root/.oh-my-zsh/custom/plugins" //添加进程数,相对于Update异步,如果Update不异步可以不需要add wg.Add(1) go Update(path) //等待所有进程全部结束后退出主线程 } wg.Wait() } sync.WaitGroup就是一个计数器,使用Add()添加,Done()就释放,Wait()会等待所有Add()全部Done()后向下执行,否则一直堵塞进程。如果这里我使用Channels无法控制,主线程传入Channels,那么Update()函数何时返回呢?函数中又是多进程/协程,没有找到其他办法,因为Update()函数中还要输出,全部用Channels会乱成一锅粥的。 贴一下完整代码吧,看著挺乱的: package main import ( "bufio" "fmt" "io" "io/ioutil" "log" "os" "os/exec" "runtime" "sync" ) // 更新oh my zsh 自定义插件 //同步 var wg sync.WaitGroup func GetDir(path string) ([]string, error) { var s []string rd, err := ioutil.ReadDir(path) if err != nil { fmt.Println("read dir error:", err) return s, err } for _, dir := range rd { if dir.IsDir() { s = append(s, path+"/"+dir.Name()) } } return s, err } func Update(path string) { //不异步不需要执行 defer wg.Done() paths, _ := GetDir(path) if len(paths) != 0 { for _, dir := range paths { //添加进程数 wg.Add(1) fmt.Println("get pull dir:", dir) go Pull(dir) } } } func Stdout(command *exec.Cmd, ch chan int) { stdout, _ := command.StdoutPipe() stderr, _ := command.StderrPipe() if err := command.Start(); nil != err { fmt.Println("command start err:", err) ch <- 0 return } pid := command.Process.Pid output(stdout, pid) output(stderr, pid) err := command.Wait() //等待执行完成 if nil != err { fmt.Printf("Process PID %d command wait err: %s", pid, err) } ch <- command.ProcessState.Pid() return } func outputLine(oReader *bufio.Reader, pid int) { for { line, err := oReader.ReadString('\n') if nil != err{ if io.EOF != err { log.Printf("Process PID %d: err %v", pid, err) } break } log.Printf("Process PID %d: %s", pid, line) } } func output(stdout io.ReadCloser, pid int) { oReader := bufio.NewReader(stdout) outputLine(oReader, pid) } func Pull(path string) { //每执行一个进程都要标记完成 defer wg.Done() //command := exec.Command("sh", "-c", "cd ", path, "&&", "git", "pull") command := exec.Command("sh", "-c", "cd " + path + "&& git pull") ch := make(chan int) go Stdout(command, ch) fmt.Println("ProcessState PID:", <- ch) } func main() { // 接收多目录参数 args := os.Args[1:] if len(args) != 0 { for _, path := range args { fmt.Println(path) wg.Add(1) go Update(path) } } else { path := "/root/.oh-my-zsh/custom/plugins" //添加进程数,相对于Update异步,如果Update不异步可以不需要add wg.Add(1) go Update(path) //等待所有进程全部结束后退出主线程 } wg.Wait() } 最后使用go build 文件名编译就好了,跨平台。 结语 在看Golang文档的时候语法有点别扭,不过敲了几遍后居然特别顺手。敲这个程序真的试了很多只用Channels的方法,但是达不到想要的效果,还是总线程不等待所有子线程执行完毕的问题。因为这里线程/协程用得太杂乱了。不过这就是我的目的,让线程/协程极致的情况下怎么控制好。各种线程/协程满天飞是怎么样的体验。
2020年03月23日
1,038 阅读
0 评论
0 点赞
2020-03-22
Ubuntu-18.10降级至18.04
前言 不知什么时候买的一台服务器,很久没有管它了,今天无聊上去看看,本想装个oh my zsh(有兴趣的同学可以看看我写的文章oh-my-zsh强大的zsh配置管理),在更新源(apt update)出现错误Failed to fetch http://xxx 404 Not Found,一部分官方源404了,这种错误是由于Ubuntu18.10已经停止维护了,18.10并不是LTS版本,已经停止维护了。 网上找了一通,基本上替换源为old-releases.ubuntu.com即可: ### 一定要备份啊 sudo cp /etc/apt/sources.list /etc/apt/sources.list.bak sudo sed -i -r 's/([a-z]{2}\.)?archive.ubuntu.com/old-releases.ubuntu.com/g' /etc/apt/sources.list sudo sed -i -r 's/security.ubuntu.com/old-releases.ubuntu.com/g' /etc/apt/sources.list sudo apt update 一般这样操作后可以继续使用当前Ubuntu版本了。 但是当前版本的Ubuntu已经停止更新维护了,索性还是降级算了。 降级Ubuntu至18.04LTS 1. 替换源代号 这些cosmic是Ubuntu18.10版本的代号,需要修改成bionic,用命令修改,不然头疼: ### 切记备份 sudo cp /etc/apt/sources.list /etc/apt/sources.list.bak sudo sed -i 's/cosmic/bionic/g' /etc/apt/sources.list 2. apt软件包降级 sudo nano /etc/apt/preferences ### 粘贴以下信息 Package: * Pin: release a=bionic Pin-Priority: 1001 3. 更新 接下来更新软件源,升级 sudo apt update sudo apt upgrade sudo apt dist-upgrade 出现警告很正常,不用管它,不是致命错误。 安装期间有些软件需要替换会堵塞询问,例如nginx,由于我有写小玩意在服务器,就不替换了,使用当前版本,具体看你的需求。默认选项是N,你也可以一路回车下去。 等待更新完成: 最后查看当前版本信息: cat /etc/os-release| grep -i version 到这里就成功啦!!! 还有些小问题 系统算是降级成功了,在执行apt update又出错了。 一堆错误直接退出了。 snap包管理器,从ubuntu 16.04LTS就默认安装。在执行apt update也会执行snap包管理器的更新,反正我没用过,如果有需要那就删了再装吧。 ### 删除snap sudo rm -rf /etc/apt/apt.conf.d/20snapd.conf 结语 又折腾到十一点,降级之前装oh my zsh敲太快,执行了chsh -s /bin/zsh,然后发现服务器内存吃紧,索性升级了一下。这倒好,进不去系统了,登录后弹出,又要登录,死循环。 我以为密码错了,打开服务商控制台后ctrl+alt+del 然后 esc,选择第一个引导项输入e,在linux /boot/...这一行后面加上init=/bin/bash,ctrl +x,系统会启动命令行,修改密码passwd然后exit,重启进入系统,发现还是登录不上去。研究了半天,各种资料一顿查。 这种情况网上都说/.Xauthority文件权限问题,我是用root用户登录能有什么问题呢? 挂载Finnix系统,挂载服务器磁盘mount /dev/vda1 /mnt,上去看auth.log日志,sshd[1192]: User root not allowed because shell /bin/zsh does not exist 发现这行错误,这个zsh真是特别打眼的,就是它的锅,因为18.10版本已经过期,apt update都失败,我都没看就直接装apt install zsh,当然也失败了。而且还直接使用chsh -s /bin/zsh,把密码验证shell给改成zsh了。 改回来试试吧(此时我的环境是在挂载的Finnix系统中): nano /mnt/etc/passwd 果不其然,改成root:x:0:0:root:/root:/usr/bin/bash,退出Finnix,移除Finnix,开机登录上去了。 累死了,不过又GET到新技能。
2020年03月22日
2,566 阅读
0 评论
0 点赞
2020-03-22
QuantumultX-IOS家族又添一员大将
前言 之前用过Suger、Shadowrocket,Suger由于价格昂贵而且特别需要的协议支持太少没有再购买新版本APP。Shadowrocket便宜实惠,协议支持齐全,很不错。但是Suger和QuantumultX有更加强大的功能,定时任务以及完善的MITM支持(Shadowrocket的MITM一直存在BUG)。有了MITM就可以篡改请求,实现一些爆炸的想法。 QuantumultX界面设计不亚于Suger,功能相对Suger相同,但是支持的协议更多,而且价格便宜不少,可惜只能国外Apple Store才能购买(相信大家都懂)。 QuantumultX有反盗版机制,不开启iCloud的情况下只能支持8太设备,开启iCloud多了三个独立授权名额,理论上通过iCloud可以授权无数台。以下是作者twitter说明: 使用 基本使用方法其实网上不少,对于开发者来说其实很简单,反之可能有点难度。分流、重写、MITM这些规则网上很多,像是神机规则、lhie1大神规则等等,都非常好用。神机规则貌似作者精力有限已经停止维护了。 节点自己部署,或者机场订阅,不了解机场可以看看毒药机场测速和简介,有排名先后,先上张图自己理解: 脚本 脚本是QuantumultX非常有可玩性的功能,例如签到脚本,各种VIP脚本,每天推送天气信息,淘宝、京东商品历史价格以及你能想到的其他功能脚本。 淘宝、京东商品历史价格这个蛮有意思的,我也在用,看看历史价格发现蛮多商品相对以往都翻倍的涨,防剁手必备啊。 yichahucha大神脚本中的jd_price.js,tb_price.js分别是京东和淘宝历史价格,Suger和QuantumultX两个版本通用。 还有netflix评分脚本nf_rating.js,用法看项目介绍,蛮有意思的。 NobyDa大神脚本,主要是签到功能很强大,JD_DailyBonus.js 京东自动签到,可以领取20+签到,京东豆,钢镚,红包和优惠券。 是不是很有意思? 上图中task_local是定时任务模块配置,也可以在设置构造请求中添加。 rewrite_local是本地重写脚本模块,主要是匹配链接通过一些脚本修改请求响应内容,达到想要的目的。 Mitm解密Https请求模块,其中多个域名不要重复写hostname = xxx.xxx.xx,应该: hostname = api.xxx.com, www.abc.com, ... nzw9314大佬汇集了上述以及其他大神的脚本,你也可以只关注nzw9314,他会自动同步其他大神的更新。 现在几乎全民Https,要是用以上脚本,务必开启MITM,否则脚本可能无法成功执行。 脚本同步 QuantumultX最好开启iCloud,将脚本存放至iCloud,方便设备间同步,也可以使用Working Copy更新仓库代码。 开启iCloud 点击QuantumultX右下角图标,滑到底部,其他设置中。 借上图说说排除路由 0.0.0.0/31功能就是隐藏VPN图标, 始终开启开机自动打开,简直爆炸 开启iCloud后,文件APP中应该有QuantumultX文件夹,里面是这样: 当然,刚刚开启iCloud应该都是空的。 Image: 图标文件夹 Profiles: 配置文件文件夹 Scripts: 脚本文件夹 在Scripts文件夹下添加一个空格文件夹,待会用来存放大神们的脚本库。 下载Working Copy,然后打开。 点击右上角+,然后点击Setup synced directory。 选择你刚刚新建的文件夹 选择后会弹出test syncing, 点击Done 点击Add Remote添加远程库 填入库地址到URL,库地址为git地址,不是网址。 如果不是自己的仓库或者没有git push权限务必关掉Allow Push 然后点击Save-Fetch获取。 如果报错或者获取失败,请将上个页面中的Branch切换为master, 如果Current branch是空的,就选择分支,一般是master分支,当然也有不同的。 回到主界面,下拉就会更新所有库的代码,更新后记得点击顶部绿色提示框合并代码,不然拉了也白拉。 有了同步之后事情就简单多了,看图片: nzw9314/NobyDa/JD-DailyBonus/JD_DailyBonus.js 这里是相对目录,相对于iCloud/QuantumultX/Scripts目录下的文件。以后需要更新就去Working Copy往下这么一拉,点击绿色提示合并代码就OK了,QuantumultX可能有缓存,需要重新开关一下。 结语 QuantumultX确实挺有意思,我自己也折腾了个机车游侠的自动签到脚本,知乎去内容广告。其实会点javasrcipt就能轻松搞定,QuantumultX-Github有很多示例可以参考。
2020年03月22日
6,571 阅读
0 评论
0 点赞
2020-03-21
Laradock-部署本地开发环境
前言 之前laravel开发环境一直都是homestead部署,最近发现docker蛮火的。这段时间没什么事,稍微研究了一番。 首先需要理解docker给我们解决了什么问题。 对于开发者来说,最重要的就是轻便。docker中的容器将每个进程单独分割,互不影响但又有使用关联。很神奇,试想一下homestead,它是一台虚拟机,一旦启动就会占用固定的资源,哪怕资源在虚拟机中没有使用,你也是无法干预的,在虚拟器启动的那刻就已经分配。这种情况会造成很多资源浪费。 使用docker,把每个应用/服务都单个放入容器中,不会占用固定资源。更多空闲资源可以被系统利用,不会浪费掉。 其实docker最大的特点是解决部署时的方便。项目上线需要生产环境,特别是分布式服务器,每台都要独立安装,但是使用docker部署起来就会相当方便,编写好docker-compose.yml 和各个 服务/应用的 Dockerfile 文件,几行命令就能部署好整个生产环境。 以上是我目前对docker的愚解。 安装docker 这部看docker文档就好了,很详细。 我是用的是windows,安装程序下一步... windows系统和MacOS使用安装程序不需要单独安装docker-compose。 安装laradock 其实laradock文档中也很详细,直接clone代码就好了。 说说cp env-example .env文件吧。 ### 主要是项目目录,你开发项目所在目录,如果在同目录下(多项目配置)则不用动 APP_CODE_PATH_HOST=../ ### 项目在容器中挂载路径 APP_CODE_PATH_CONTAINER=/var/www ### 这个比较重要,数据卷存方位置,例如你的mysql数据库文件,redis持久化文件等(windows在C:\Users\用户名\.laradock\data) DATA_PATH_HOST=~/.laradock/data ... ### workspace这个大项中,很多开发者用不到 ### 像是PYTHON,NODE等我都会不安装 ### 服务器上部署也是,不需要尽量别安装 WORKSPACE_INSTALL_NODE=true ### 开启zsh SHELL_OH_MY_ZSH=true ### 开发环境需要XDebug就开启(反正我是需要) WORKSPACE_INSTALL_XDEBUG=true ... ### php版本 PHP_VERSION=7.4 ... ### 是否修改源,没翻墙还是需要 CHANGE_SOURCE=false ### ubuntu源 UBUNTU_SOURCE=aliyun ... ### php5.6含一下注意,redis扩展不支持,会报错 PHP_FPM_INSTALL_PHPREDIS=true ### 如果WORKSPACE_INSTALL_XDEBUG=true 开启了 ### 那么这边也需要开启 PHP_FPM_INSTALL_XDEBUG=true ... ### 把我坑惨了,Supervisor是php-woker提供的,配置文件也在里面,如果不开启的话,php-woker容器中的php是不包含redis扩展的 PHP_WORKER_INSTALL_REDIS=true ... ### mysql配置,按自己需求该 MYSQL_VERSION=5.7 MYSQL_DATABASE=homestead MYSQL_USER=homestead MYSQL_PASSWORD=secret ### mysql外部端口,本地我已经装了mysql 3306、33060都已经被占用 MYSQL_PORT=33061 MYSQL_ROOT_PASSWORD=root ### 这里是多数据库配置,laradock/mysql/docker-entrypoint-initdb.d目录下对照样本加 MYSQL_ENTRYPOINT_INITDB=./mysql/docker-entrypoint-initdb.d ... ### nginx配置 NGINX_HOST_HTTP_PORT=80 NGINX_HOST_HTTPS_PORT=443 ### 日志 NGINX_HOST_LOG_PATH=./logs/nginx/ ### 多站点配置目录,找样本加 NGINX_SITES_PATH=./nginx/sites/ NGINX_PHP_UPSTREAM_CONTAINER=php-fpm NGINX_PHP_UPSTREAM_PORT=9000 NGINX_SSL_PATH=./nginx/ssl/ ... 如果你开启了oh-my-zsh,默认是没有任何插件的,皮肤也是默认,laradock没有露出.zshrc配置文件文件,只能修改workspace的Dockerfile文件了,在Dockerfile文件找到下面代码(我是按照之前写得一篇文章oh-my-zsh强大的zsh配置管理配置安装的): ########################################################################### # Oh My ZSH! ########################################################################### USER root ARG SHELL_OH_MY_ZSH=false RUN if [ ${SHELL_OH_MY_ZSH} = true ]; then \ apt install -y zsh \ ;fi USER laradock RUN if [ ${SHELL_OH_MY_ZSH} = true ]; then \ sh -c "$(curl -fsSL https://raw.githubusercontent.com/ohmyzsh/ohmyzsh/master/tools/install.sh) --keep-zshrc" && \ git clone https://github.com/zsh-users/zsh-autosuggestions ${ZSH_CUSTOM:-~/.oh-my-zsh/custom}/plugins/zsh-autosuggestions && \ git clone https://github.com/zsh-users/zsh-syntax-highlighting.git ${ZSH_CUSTOM:-~/.oh-my-zsh/custom}/plugins/zsh-syntax-highlighting && \ sed -i -r 's/^plugins=\(.*?\)$/plugins=(laravel5 zsh-syntax-highlighting zsh-autosuggestions)/' /home/laradock/.zshrc && \ sed -i -r 's/^ZSH_THEME=\".*?\"$/ZSH_THEME="ys"/' /home/laradock/.zshrc && \ echo '\n\ bindkey "^[OB" down-line-or-search\n\ bindkey "^[OC" forward-char\n\ bindkey "^[OD" backward-char\n\ bindkey "^[OF" end-of-line\n\ bindkey "^[OH" beginning-of-line\n\ bindkey "^[[1~" beginning-of-line\n\ bindkey "^[[3~" delete-char\n\ bindkey "^[[4~" end-of-line\n\ bindkey "^[[5~" up-line-or-history\n\ bindkey "^[[6~" down-line-or-history\n\ bindkey "^?" backward-delete-char\n' >> /home/laradock/.zshrc \ ;fi 可以自定义安装插件,修改皮肤。git clone ...下载插件,记得在sed -i -r 's/^plugins=\(.*?\)$/plugins=(laravel5 zsh-syntax-highlighting zsh-autosuggestions)/' /home/laradock/.zshrc && \中加载你的插件,sed -i -r 's/^ZSH_THEME=\".*?\"$/ZSH_THEME="ys"/' /home/laradock/.zshrc && \ 修改皮肤(例如我这里修改的是ys皮肤,echo '\n\ ...默认绑定了一些快捷键,不需要可以删掉。有兴趣可以查看我的文章oh-my-zsh强大的zsh配置管理)。 其他没什么安装需要注意的了,执行: ### 启动容器 mysql redis nginx php-worker ### -d参数是后台运行 docker-compose up -d mysql redis nginx php-worker workspace、php 默认会自己启动,因为容器都依赖它们 如果你没有修改COMPOSE_FILE=docker-compose.yml配置文件的话,laradock项目中所有镜像全部会安装 !!!0.0!!!。 启动之后你可以进入容器: ### 就像虚拟机一样,很亲切 ### 像composer,git等命令都有,不需要单独安装 docker-compose exec workspace bash 进入workspace后执行: cd 你的项目名称/ php artisan migrate ... workspace可以换成你想操作的(已经启动)任何容器名称(mysql,nginx...),非常方便操作。 并非完美 laradock在修改配置后重新docker-compose build xxx又会重新安装/编译一次,非常耗时,特别是workspace容器,简直恐怖。这点没有虚拟机来得方便。 docker在MacOS中存在文件读取缓慢的问题。 mysql或者其他数据库在容器中部署还存在一定的安全隐患。 结语 其实自己手动折腾一遍之后并不像想象中那么难。 仔细查看Dockerfile文件后你会发现里面就是各种安装部署命令,只是用docker特定的语法包裹了。docker安装容器就是执行你编写好的命令。 服务器部署直接一套docker就搞定,而且服务器项目比较杂的话非常适合,毕竟各种项目(php,java,python...)环境在服务器上搭建难免出现奇奇怪怪的问题,在容器中的话互不干扰,横向扩展服务器简直不能再方便了。
2020年03月21日
1,898 阅读
0 评论
1 点赞
2019-08-11
实用-模型缓存trait
前言 好久没有写博客了,赖癌看来又发作了。也没有很高大上的东西写,就写写自己开发中比较顺手的小知识吧! 开发中经常会有些模型读取非常频繁,但是又很少做写入修改操作(类似于 分类、品牌等),这种情况缓存简直不能再好了,既可以大量减少数据库压力,又可以非常快速读取(前提要配置好缓存,比较推荐redis)。 Trait 如果你连trait都不认识的话好好看看文档吧。 起初最简单的思路就是能简单方便的让模型可以缓存和读取。laravel已经有了非常好用的缓存系统,非常方便,但是相对还可以封装一下,让模型缓存更加优雅。 代码非常简单,我就不多废话了: namespace App\Traits\Model; use Illuminate\Database\Eloquent\Collection; trait HasCache { protected $ttl; protected function tag(){ return 'model_cache'; } /** * @param null $key * @param \Closure|null $closure * * @return mixed|Collection */ public static function cache($key = null, \Closure $closure = null){ $model = new static(); if($key instanceof \Closure){ $closure = $key; $key = null; } return \Cache::tags($model->tag())->remember($key ?? get_class(),now()->addMinutes($model->getCacheTtl()),function () use ($closure, $model) { if($closure){ return $closure($model); } return $model->get(); }); } //删除缓存 public function forgetCache($key = null){ return \Cache::tags($this->tag())->forget($key ?? get_class()); } //设置缓存时间 public function setCacheTtl($minutes){ $this->ttl = $minutes; return $this; } public function getCacheTtl(){ return $this->ttl ?: 60; } 使用 我这是使用的laravel-admin后台,有兴趣可以了解下,极大节约后台程序开发周期。 在需要缓存的模型中使用HasCachetrait: class ProductCategory extends Model { use SoftDeletes, ModelTree, AdminBuilder, HasCache; ... //也可以自己扩展缓存自己想要的数据 public static function levelCache(){ return self::cache(get_class().'level_cache', function ($model){ $sort_closure = function ($query){ $query->sort(); }; return $model->with(['children' => $sort_closure,'children.children' => $sort_closure])->where('parent_id',0)->sort()->get(); }); } //对应的删除缓存方法 public static function forgetLevelCache(){ return (new static())->forgetCache(get_class().'level_cache'); } } 后台控制缓存,在更新、添加数据后及时清除缓存,防止脏读,虽然是缓存但是还是要保证数据及时性吧!(例子中使用的是laravel-admin扩展) /** * Make a form builder. * * @return Form */ protected function form() { $form = new Form(new ProductCategory); $form->display('id','ID'); ... $form->saved(function (Form $form){ //模型保存成功后的回调函数中删除缓存 $form->model()->forgetLevelCache(); }); return $form; 如果没有使用laravel-admin,可以手动删除缓存或者监听模型保存成功事件,laravel-Eloquent:Events。 平时使用: ProductCategory::levelCache(); ProductCategory::setCacheTtl(30)->cache() ProductCategory::cache(function(ProductCategory $model){ ... return $model->get(); //返回你想保存的数据 }) 结语 模型缓存后使用起来更加简单了,千万别把服务器内存玩爆了。开发中很多小技巧自己都没有好好总结,然后多了再总结就懒癌发作,哎!
2019年08月11日
1,044 阅读
0 评论
0 点赞
2019-06-06
IOS13-深色模式终于来了
前言 本该 iOS12 上的特性遗留到了 iOS13, 不过我确实非常期待。MAC OS-mojave上早已实现深色模式了,确实酷炫。orzlee在昨天把自己的iPhone XS更新了iOS13,这次开发者预览貌似没有描述文件,说说目前为止的体验吧。 升级 orzlee是在MAC下更新系统( 还好一起折腾了黑苹果,具体教程需要自己去远景论坛黑苹果区爬贴了 ),windows目前应该也可以更新了,具体方法可能需要自己找找了( i4助手应该可以更新了 )。 固件下载地址 iOS iOS 13 iPhone XS、iPhone XS Max iOS 13 iPhone XR iOS 13 iPhone X iOS 13 iPhone 7、iPhone 8 iOS 13 iPhone 7 plus、iPhone 8 plus iOS 13 iPhone SE iOS 13 iPhone 6s Plus iOS 13 iPhone 6s iPad OS iPad Pro (11 inch), iPad Pro (12.9-inch)(3rd generation) iPad Pro (10.5 inch), iPad Pro (12.9-inch)(2nd generation) iPad Pro (12.9‑inch)iPad Pro 2018 10.5/12.9 Inch iPad (5th generation), iPad (6th generation) iPad mini (5th generation), iPad Air (3rd generation)、iPad mini 4, iPad Air 2、iPad Pro (9.7‑inch) iPad 2017/2018 $329 one MAC OS MAC OS 描述文件 Watch OS Watch OS Apple TV OS Apple TV OS MAC OS下更新 首先需要安装 MobileDevice.pkg iTunes( 需要更新到最新版 )连接iPhone,按住Option( 黑苹果windows键盘按住 Alt)点击更新,选择下载好的固件。 等待更新完成 深色模式效果 等待IOS13正式上线后,其他APP开发者适配深色模式后就更完美了。 IOS13 已知更新 音量调节提示图标在众多吐槽下发生了改变,始终保持在左侧上半部分,并且会自动缩小,不会再挡在屏幕中间碍眼了,这点真的很赞 照片分类更加友好、详细了,配合深色模式非常酷。照片编辑也更加强大。 面容ID提升30%( 反正orzlee感觉不出来 ) APP体积压缩50%,升级容量压缩60%。启动速度对比iOS12快了两倍。 拟物化图标再次出现,还调整了iOS13截图画面,带有毛玻璃感。 Sign in Apple,第三方App的登入隐私保护。 两个AirPods,听同一首歌。 Memoji可以化妆了,甚至还能用它做联系人头贴。 结语 对于IOS13,orzlee目前在使用上还未发现BUG,一切还是那么丝滑,反正orzlee也只是奔着炫酷的深色模式去的。部分内容还没有完全翻译过来,但是不影响使用。如果不在乎可以升级体验尝鲜。 巨大BUG,非常影响正常使用,升级慎重!升级慎重!升级慎重! 。 2019-06-13更新 使用一段时间后发现巨大BUG: 联通在4G环境被叫几乎全是无法接通,特别是在使用流量的情况下。需要切换3G才可以解决。 经常莫名其妙重启,某些情况只会关机,需要手动开机( 频率有点高,几乎一天好几次 )。 某些APP键盘莫名奇妙无法收回,APP输入框被键盘遮挡。闪退也发现多次。
2019年06月06日
1,209 阅读
0 评论
0 点赞
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,228 阅读
0 评论
0 点赞
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,223 阅读
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日
8,742 阅读
2 评论
1 点赞
1
2
3
4
5