OrzLee - Web开发
type: channel
link: https://www.orzlee.com/category/web-development
description: 开发环境配置，开发经验分享。
items: JAVA RMI GDC每小时触发Full GC
items: LXK0301京东签到脚本-京喜工厂解决无法签到异常
items: Vue-videoJS播放m3u8视频
items: Laravel-关系预加载数量限制
items: Laradock-PHPStorm开启XDebug
items: Laradock-部署本地开发环境
items: 实用-模型缓存trait
items: laravel-admin 与 laravel-nova 使用感受
items: 微擎-mysql数据库崩溃
items: 微信-域名被封监测以及自动更换被封域名

JAVA RMI GDC每小时触发Full GC
type: item
date: 2024-05-06T21:16:00+08:00
description: 前言 最近倒腾Java，发现JVM并行收集器在还有大量堆内存的时候就开始触发老年代的回收，通过GC日志发现每小时都会执行GC和Full GC回收，回收是通过System.gc()调用。 那每小时都调用一次Full GC，我添加启动参数-Xms11g -Xmx11g分配11G堆内存只是为了好看吗？每次Full GC耗时在600ms，并行收集器老年代内存使用还不足5%，真是发神经了！ 过程就不做记录了，无聊又蛋疼。以下是在使用了RMI或者其他NIO对象的情况。 RMI DGC每小时触发一次Full GC 原因：DGC代码中调用System.gc()导致并行收集器触发Full GC。 解决办法： 修改DGC调用System.gc()执行时间，默认一小时，单位ms。启动参数添加： -Dsun.rmi.dgc.client.gcInterval=3600000 -Dsun.rmi.dgc.server.gcInterval=3600000 禁止程序中的System.gc()，添加启动参数： -XX:+DisableExplicitGC 使用支持并发的收集器（CMS、G1、ZGC或Shenandoah），并且添加启动参数： -XX:+ExplicitGCInvokesConcurrent 方法1，治标不治本，延长时间触发Full GC会导致STW时间更长，可以配合并行收集器使用-XX:+ExplicitGCInvokesConcurrent来控制频率。 方法3，启用-XX:+ExplicitGCInvokesConcurrent参数可以尝试在垃圾收集时使用并发收集来减少STW时间，从而提高应用程序的响应性。 不建议使用-XX:+DisableExplicitGC来限制System.gc()。 因为从Java 7开始，Java提供了基于NIO的RMI实现，称为NIO-based RMI。这种实现利用了NIO的非阻塞I/O模型，能够更高效地处理大量的并发连接，减少了线程资源的消耗，并提高了性能和可伸缩性。通过使用NIO，NIO-based RMI能够更好地适应高并发和大规模的网络通信场景。 DirectByteBuffer是一种在堆外内存中分配的ByteBuffer，它与NIO密切相关，因为它可以直接映射到操作系统的内存中，从而避免了在Java堆和本地内存之间进行复制。在NIO编程中，使用DirectByteBuffer可以提高I/O操作的性能和效率，特别是在处理大量数据时。在基于NIO的RMI实现中，使用NioServerSocketChannel和NioEventLoopGroup通常涉及到DirectByteBuffer的使用。 虽然 DirectByteBuffer 对象本身是由 JVM 管理的，它们存储在 Java 堆内存中，但是 DirectByteBuffer 对象所持有的实际数据存储在堆外内存中。 JVM 通过 DirectByteBuffer 对象来管理对堆外内存的访问和操作。当 DirectByteBuffer 对象被垃圾回收时，它所持有的堆外内存也会随之被释放。 因此，触发 Full GC 通常是为了回收已经失去引用的 DirectByteBuffer 对象，进而释放掉它们所占用的堆外内存。 Full GC 在回收过程中会扫描整个堆内存，包括其中的对象和引用。当发现 DirectByteBuffer 对象已经没有引用指向时，JVM 就会将其标记为可回收， 待下次 Full GC 执行时进行回收。这样，间接地通过 Full GC 回收了 DirectByteBuffer 对象，也就释放了相应的堆外内存。 如果禁用了System.gc()，那么不会及时的清理 DirectByteBuffer 或者其他 NIO 对象导致堆外内存也不会释放。虽然最后也可能因为JVM堆内内存不足触发Full GC 来释放，但没必要冒险。参阅 监控程序调用System.gc()堆栈 可以使用 async-profiler 跟踪System.gc调用者： 预先开始分析： profiler.sh start -e java.lang.System.gc <pid> 发生一种或多种System.gc情况后，停止分析并打印堆栈跟踪： --- Execution profile --- Total samples : 6 Frame buffer usage : 0.0007% --- 4 calls (66.67%), 4 samples [ 0] java.lang.System.gc [ 1] java.nio.Bits.reserveMemory [ 2] java.nio.DirectByteBuffer.<init> [ 3] java.nio.…

LXK0301京东签到脚本-京喜工厂解决无法签到异常
type: item
date: 2021-03-03T01:58:00+08:00
description: 前言 LXK0301大佬的京喜工厂自动签到脚本不知怎么一直提示：亲,活动太火爆了,请稍后再试吧!,基本无法使用，任务也不自动做了。 上网查了一下，全是说黑号，真蛋疼。因为脚本无法做任务什么的，但是手动完全没有问题，根本不是黑号，网上一股脑说黑号。 我就不信邪，app能做任务脚本就不行，肯定是脚本有问题。 2021-03-25 第二次更新，已经不需要自己抓包获取签名参数了，脚本会在每次执行时自动获取签名参数。已经联系作者，等作者更新吧！(推荐使用作者脚本，修复了参团问题) 2021-03-25更新，今天更新了下脚本，应该可以获取自己的签名参数使用了，有时间的朋友可以测试一下！仓库地址：jd_scripts-orzlee 2021-03-23更新，自己获取签名的方法可能无效，作者已经跟新脚本，目前使用我提供的签名参数可以正常做任务和领取电力，后续可能还需要研究。更新作者脚本即可！ 原因 最根本的原因就是脚本发送请求链接签名有问题，这个好像只针对了部分用户，有的用户没问题，有的就会一直提示：亲,活动太火爆了,请稍后再试吧!。 起初发现问题是在手机上用QuantumultX抓包京喜APP，点击地上的零件，再抓包脚本，然后找出usermaterial/PickUpComponent请求来对比。开始我还以为是cookie问题，我替换cookie重试请求还是太火爆错误，再重发一下抓包京喜APP的请求，依然成功。再替换替换脚本请求的url参数为抓包app的，莫名奇妙的成功了。这个锅必须脚本背了！ 脚本中decrypt函数是负责请求链接签名的，但是目前脚本有问题，很多签名key都是没有值的，因为根本没有获取值，只有少部分定义了默认值。来看一下代码(目前是1535行)： function decrypt(time, stk, type) { if (stk) { const random = 'pmUmA8IyRcDp'; //这些常量其实每个用户都可能不一样 const token = `tk01wd4571d58a8nT0tkdXczeW94f5x4qjWs44kcPCTXeWKa2xXY+ZxHaOtbRxmyw6vrIF4RDFwwTUfwy1pIqNE0oyWW`; //这些常量其实每个用户都可能不一样 const fingerprint = 8410347712257161; //这些常量其实每个用户都可能不一样 //这个时间转换和现在的不一样，现在是new Date(time).Format("yyyyMMddhhmmssSSS");，转换结果不一样，加密出来也不一样 const timestamp = new Date(time).Format("yyyyMMddhhmmssS"); const appId = 10001; //这个是活动id 是固定的 const str = `${token}${fingerprint}${timestamp}${appId}${random}`; const hash1 = $.CryptoJS.HmacSHA512(str, token).toString($.CryptoJS.enc.Hex); //这里加密算法有可能不一样 let st = ''; stk.split(',').map((item, index) => { //下面的签名参数只有些默认值，很多都是空的，但是链接上又带了有效值，服务器验签估计不合法(但是有的用户却没有问题，搞不懂) st += `${item}:${item === '_time' ? time : item === 'zone' ? 'dream_factory' : item === 'type' ? type || '1' : ''}${index === stk.split(',').length -1 ? '' : '&'}`; }) const hash2 = $.CryptoJS.HmacSHA256(st, hash1).toString($.CryptoJS.enc.Hex); console.log(`st:${st}\n`) // console.log(`hash2:${JSON.stringify(["".concat(timestamp.toString()), "".concat(fingerprint.toString()), "".concat(appId.toString()), "".concat(token), "".concat(hash2)])}\n`) console.log(`h5st:${["".concat(timestamp.toString()), "".concat(fingerprint.toS…

Vue-videoJS播放m3u8视频
type: item
date: 2020-12-24T17:24:00+08:00
description: 前言 找到一部分M3U8视频源，之前用H5 video 标签播放MP4视频，但是支持视频格式很少，索性换掉算了。 M3U8简单了解 引用维基百科： M3U8是Unicode版本的M3U，用UTF-8编码。"M3U"和"M3U8M3U8"文件都是苹果公司使用的HTTP Live Streaming格式的基础，这种格式可以在iPhone和Macbook等设备播放。 引用知乎： 上述文字定义来自于维基百科。可以看到，m3u8 文件其实是 HTTP Live Streaming（缩写为 HLS） 协议的部分内容，而 HLS 是一个由苹果公司提出的基于 HTTP 的流媒体网络传输协议。 HLS 的工作原理是把整个流分成一个个小的基于 HTTP 的文件来下载，每次只下载一些。当媒体流正在播放时，客户端可以选择从许多不同的备用源中以不同的速率下载同样的资源，允许流媒体会话适应不同的数据速率。在开始一个流媒体会话时，客户端会下载一个包含元数据的 extended M3U (m3u8) playlist文件，用于寻找可用的媒体流。 HLS 只请求基本的 HTTP 报文，与实时传输协议（RTP）不同，HLS 可以穿过任何允许 HTTP 数据通过的防火墙或者代理服务器。它也很容易使用内容分发网络来传输媒体流。 简单理解，M3U8其实就是视频文件碎片索引URL（类似于播放列表），播放器会解析并且下载这些视频碎片，拼接成一整个视频。MP4格式对HTTP和Flash都比较友好，但是MP4文件头部结果比较复杂，播放过程对网络带宽要求较高，在网络复杂的场景用户体验较差。 Vue集成 集成之前大致了解了下，网上比较多的是vue-video-player和videojs，我选择了后者（之前video标签转过来还是不能跨度太大，免得拉跨）。 这东西让我折腾挺久的，只能怪自己懒，好好的文档不看，到处搜，结果搜到一堆复制粘贴的垃圾文章，也不知道什么年代的了，结果发布日期全是今年的。 官方文档相当简洁，其实非常简单，文档在此tutorial-vue，一目了然。 说说我的情况吧！，官方文档是在mounted方法中初始化播放器，但是我的视频流是动态加载的，这样直接加载会报错： CODE:4 MEDIA_ERR_SRC_NOT_SUPPORTED) No compatible source was found for this media 因为我的视频地址和视频类型都没有请求，只能在请求完成之后再初始化播放器。 播放器已经封装成组件了，看代码。 安装videojs： npm install --save video.js npm install --save @videojs/http-streaming //这个应该是不需要的，video.js在7.x就已经包含了，报错就还是加上吧，懒得试了 import Video from 'video.js' import '@videojs/http-streaming' import 'video.js/dist/video-js.css' //css要加载 export default { name: "playVideo", data: () => { return { player: null, } }, props: { width: { type: Number, default: 0, }, height: { type: Number, default: 0, }, src: { type:String, default: '', }, video_type: { type: String, default: 'video/mp4', } }, watch: { video_type(){ //监听props赋值，初始化播放器 this.initVideo(); } }, beforeDestroy() { //销毁不能少啊！！！！！ if (this.player) { this.player.dispose() } }, methods: { pause() { this.$nextTick(function () { // DOM 现在更新了 // `this` 绑定到当前实例 this.$refs.video.pause(); }) }, initVideo() { //初始化视频方法 this.player = Video(this.$refs.video, { //确定播放器是否具有用户可以与之交互的控件。没有控件， //启动视频播放的唯一方法是使用autoplay属性或通过Player API。 controls: true, //自动播放属性,muted:静音播放 autoplay: "muted",…

Laravel-关系预加载数量限制
type: item
date: 2020-12-22T15:50:00+08:00
description: 前言 最近开发一个项目，关于用户评论。评论可以被用户再次评论，当然只做了一级限制，没有做太多层级，可以回复某个评论中的特定用户（类似于@功能）。 在输出评论列表中，是应该输出部分评论的回复，但是看似简单，实际情况却相对复杂。 分析 在laravel中查询出两级并不难, 使用预加载可以轻松完成： class Comment extends Model { ... public function comments() { return $this->hasMany($this, 'comment_id'); } ... } Comment::with('comments')->simplePaginate() 当需要限制评论回复的数量时，首先想到的是如下方法： Comment::with( [ 'comments' => function (HasMany $query) { $query->take(5); }, ] )->simplePaginate() 但是返回的模型关系comments被限制小于等5条，并不是每条评论的回复，而是整个查询结果的评论回复。如果第一条评论回复有10条，那么会显示5条，之后其他评论的回复全部为空。 其中的Sql语句是这样: select * from `comments` where `comments`.`comment_id` in (1, 2, ...) limit 5 要实现每条评论显示N条回复并不难，只是回到了N + 1的问题, 查询出列表后再加载关系： $comments = Comment::simplePaginate(); $comments->each(function($comment) { $comment->load( [ 'comments' => function (HasMany $query) { $query->take(5); }, ] ); }); Sql: select * from `comments` where `comments`.`comment_id` in (1) limit 5 select * from `comments` where `comments`.`comment_id` in (2) limit 5 ... 这样确实可以解决问题，但是非常笨。 在laravel-framework的issue中找到了类似问题Eager-loading with a limit on collection...，其中有位开发者通过修改limit和take方法来实现预加载关系数量限制，但是提交PR被laravel作者拒绝，于是封装成扩展eloquent-eager-limit。 我安装后试了下，确实能达到效果，于是看了下源码。作者几乎把所有关系的limit和take方法全部重写，然后封装成Trait,模型中使用该Trait相当于重写了limit和take方法。使用方法和之前一样，但是要注意，一旦使用eloquent-eager-limit的Trait后，该模型就不再有之前关系limit或take的总限制效果了，相信laravel作者拒绝也是因为此原因。 来看看生成的语句： select laravel_table.*, @laravel_row := if(@laravel_partition = `comment_id`, @laravel_row + 1, 1) as laravel_row, @laravel_partition := `comment_id` from (select @laravel_row := 0, @laravel_partition := 0) as laravel_vars, (select * from `comments` where `comments`.`comment_id` in (1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16) order by `comments`.`comment_id` asc) as laravel_table having laravel_row <= 10 order by laravel_row; 从Sql语句看来确实只用两次查询就可以获得结果，比之前load遍历加载方法更优。 来看看最终效果： { "data": [ { "id": 1, "post_id": 2, "comment_id": null, "contents": "contents...", "created_at": "2020-12-21 09:31:26", "comments": [ { "id": 24, "post_id": …

Laradock-PHPStorm开启XDebug
type: item
date: 2020-03-27T14:31:00+08:00
description: 前言 前几天写了一篇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站…

Laradock-部署本地开发环境
type: item
date: 2020-03-21T21:56:00+08:00
description: 前言 之前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，默认是没有任何插件的，皮肤也是…

实用-模型缓存trait
type: item
date: 2019-08-11T16:35:00+08:00
description: 前言 好久没有写博客了，赖癌看来又发作了。也没有很高大上的东西写，就写写自己开发中比较顺手的小知识吧！ 开发中经常会有些模型读取非常频繁，但是又很少做写入修改操作(类似于 分类、品牌等)，这种情况缓存简直不能再好了，既可以大量减少数据库压力，又可以非常快速读取（前提要配置好缓存，比较推荐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){ //模型保存成…

laravel-admin 与 laravel-nova 使用感受
type: item
date: 2019-05-06T20:27:00+08:00
description: 前言 两种后台管理扩展我都使用过，两个扩展既有很多相似点，却又有很多不同。接触过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…

微擎-mysql数据库崩溃
type: item
date: 2019-02-22T14:43:00+08:00
description: 前言 公司目前有两个微擎搭建的商场项目，是购买的插件。就在前几天出现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 作为默认数据库引擎。

微信-域名被封监测以及自动更换被封域名
type: item
date: 2019-01-28T11:50:00+08:00
description: 前言 微信已经成为大多数中国人的一种依赖，发展好了自然管理就更加严格，对于不符合微信营销规范的的网站统统不允许访问，就连对手的网址也无法在微信浏览器中打开。 最近公司有个需求，在各个平台大量投放广告，微信内域名频繁被封，需要域名监测和替换的内部系统功能。微信开发做过不少，但是这种明显是找漏子钻。 想不到就上网找吧！把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\Doma…