暂时未有相关云产品技术能力~
serverless的本质,不是算力资源的简单堆砌,而是池化——它将大量的零散算力资源(廉价的算力资源)进行打包、汇聚,实现更高可靠性、更高性能、更低成本的算力。 具体来说,在云计算中,CPU、GPU、内存、硬盘等计算资源被集合起来,通过软件的方式,组成一个虚拟的可无限扩展的“算力资源池”。如果用户有算力需求,“算力资源池”就会动态地进行算力资源的分配,构建一个虚拟的“计算机”。用户按需使用、付费,即可。 相比于用户自购设备、自建机房、自己运维,云计算有明显的成本优势,可以节约大量资金和人力。
1. 为什么要迁移到阿里云函数?我的项目是一个节日礼品领取项目,过节的时候会有短时间的流量洪峰。平时访问量很低。之前的架构是购买的阿里云alb+多台ecs+云msyql+云redis。最大的问题就是成本问题。平时流量低的时候ecs成本也无法缩减。阿里云函数计算是serverless,即无服务架构,就比如你的业务流量短时间突然很多。函数计算就会毫秒级别启动多个实例(阿里云函数计算 FC 用来运行函数的最小单元),如果没人访问可以没有实例运行,做到0费用。但是有人访问的时候第一次冷启动就稍微慢一点,可以根据实际情况设置最少保留一个实例。部署到阿里云函数计算,还能减少运行环境搭建的成本。之前的模式需要在ecs安装nginx,然后安装php,以及安装php的驱动程序redis等。看了下阿里云函数计算官方文档,目前custom runtime Debian 9内置php7.4并且看了下内置的php的扩展整好也支持到了我整好需要的redis。不仅剩下了买服务器钱,而且还不用安装php环境了,不仅如此每个月还有免费的算力额度。迁移原因总结下:1成本降低了很多2免去了环境部署3自动扩容,天生应对高并发2. 改造旧项目适配函数计算。代码改动:虽然说免去了环境部署,但是我之前的代码还是有些不适配的地方,比如之前代码的日志都是存放到服务器的某个目录的。如果迁移到函数计算的话,实例会随时销毁重建,导致日志丢失。解决办法就是把日志写入到阿里云oss上面,或者使用阿里云的日志服务写到那个里面去。这里还有一点要注意,我的项目不是前后端分离的,鉴权还是穿透的session和cookie模式。如果session是保留在服务端的文件的话也会存在上面的问题,建议存储的redis里面,我的项目本省就是存到redis里面的,所以这块不需要改动,如果你的项目存在这样的问题那就需要改进下了。函数计算和云msyql和云redis通讯的时候一定要采用vpc内网互通的原则减少链路传输的开销以及链路劫持风险。3. 增加s.yml以及启动shell脚本配置s.yml使用Serverless Devs客户端工具发布到阿里云函数计算,Serverless Devs这个工具并非阿里云的客户端工具,而是一个开源开放的 Serverless 开发者平台,致力于为开发者提供强大的工具链体系。通过该平台,开发者不仅可以一键体验多云 Serverless 产品,极速部署 Serverless 项目,还可以在 Serverless 应用全生命周期进行项目的管理,并且非常简单快速的将 Serverless Devs 与其他工具/平台进行结合,进一步提升研发、运维效能。它的官网地址:https://www.serverless-devs.com/ 然后看下我的s.yml里面的配置信息,具体的说下重要项是干嘛的。edition: 1.0.0name: compoent-testaccess: 'default'services: cn-hangzhou-test1002-func-3i3c0f95: component: devsapp/fc props: region: cn-hangzhou service: logConfig: enableRequestMetrics: true enableInstanceMetrics: true logBeginRule: DefaultRegex project: aliyun-fc-cn-hangzhou-ae3ef8b8-db4a-5b7a-a040-7012789ad20f logstore: function-log role: acs:ram::1621341641365186:role/AliyunFcDefaultRole internetAccess: true name: test1002 function: customRuntimeConfig: command: - bash args: - '-c' - 'chmod 777 /code/start.sh && /code/start.sh' handler: index.handler instanceType: e1 runtime: custom timeout: 5 instanceConcurrency: 20 memorySize: 512 caPort: 9000 environmentVariables: {} internetAccess: true name: func-3i3c0f95 asyncConfiguration: {} codeUri: ./test1002/func-3i3c0f95 triggers: - name: defaultTrigger description: '' type: http qualifier: LATEST config: methods: - GET - POST - PUT - DELETE authType: anonymous codeUri: ./test1002/func-3i3c0f95这个指定的是我的项目代码的位置,会把这个目录下面的代码拷贝到 debain系统的 /code目录下面。 customRuntimeConfig: command: - bash args: - '-c' - 'chmod 777 /code/start.sh && /code/start.sh'这句话的是项目启动脚本,其实就是执行这个start.sh的shell脚本,先给予一个777的权限,然后在执行。翻译成shell脚本其实就是 bash -c 'chmod 777 /code/start.sh && /code/start.sh' caPort: 9000监听端口9000一定要和启动脚本start.sh里面的一样#!/usr/bin/env bashcd /code/tp5/publicphp -S 0.0.0.0:9000 router.php 这里我觉得我还是要说下这个启动脚本,先cd到public目录,thinkphp5的入口在public下面这个和项目框架有关系。然后就是这个启动脚本,上面这是thinkphp5特有的写法。其他项就不详细说了大概看看应该能看懂。 4使用客户端工具发布工具的安装就忽略了不说了,看下官方文档说的很详细https://docs.serverless-devs.com/serverless-devs/quick_startServerless Devs这个工具安装好后,配置配置上阿里云的AccessKey ID和AccessKey Secret,在项目根目录建立s.yml,以及在代码目录建立上面的启动脚本start.sh然后就可以使用客户端工具的 s deploy 部署到阿里云函数计算了。发布成功了4. 绑定自己的域名https://fcnext.console.aliyun.com/cn-hangzhou/domains/create把自己的域名cname到上图的 “公网cname”,然后 服务名称 测试函数 版本都对应选择正确。创建即可。部署成功了,哈哈。5.谈下自己的感受函数计算serverless是以后的趋势,开发者能够有更多的精力去关注业务层。从开始预计迁移到代码的修改以及阿里云函数计算文档查阅,到迁移成功,花费了大概3天的时间,对阿里云函数计算有了更深层次的认知,同时也期待迁移过来的项目在下次使用高峰的时候能够稳定运行。我后面也会随时关注阿里云函数计算的动态,同时感谢阿里云函数计算团队能做出来这么优秀的产品。
https自签证书说明1. https的流程示意图 https安全通信机制流程详解:客户端发送 https 请求,把自身支持的秘钥算法套件(SSL 指定版本、加密组件列表)发送给服务器服务器判断自身是否支持该算法套件,如果支持则返回证书信息(本质为公钥,包含了证书颁发机构,网址,过期时间等) ,否则断开连接,客户端解析证书(通过 TLS 协议来完成),验证证书是否有效。如果异常,则会提示是否安装证书,常见的就是浏览器搜索栏左侧出现“X”告警按钮等。 如果证书有效、或者是授信安装证书后,开始传送加密信息(用证书加密后的随机值,供加解密使用)服务端通过私钥解密加密信息,得到客户端发送来的随机值,然后把内容通过该值进行对称加密。这样一来,除非知道私钥,否则是无法获取加密内容的。服务端返回加密后的内容客户端通过前面提到的随机值对加密信息进行解密 证书验证过程SSL 证书中包含的具体内容有证书的颁发机构、有效期、公钥、证书持有者、签名,通过第三方的校验保证了身份的合法检验基本信息:首先浏览器读取证书中的证书所有者、有效期等信息进行一一校验校验 CA 机构:浏览器开始查找操作系统中已内置的受信任的证书发布机构 CA,与服务器发来的证书中的颁发者 CA 比对,用于校验证书是否为合法机构颁发;如果找不到,浏览器就会报错,说明服务器发来的证书是不可信任的。解密证书:如果找到,那么浏览器就会从操作系统中取出 颁发者 CA 的公钥,然后对服务器发来的证书里面的签名进行验证。比对 hash 值:浏览器使用相同的 hash 算法计算出服务器发来的证书的 hash 值,将这个计算的 hash 值与证书中签名做对比对比结果一致,则证明服务器发来的证书合法,没有被冒充此时浏览器就可以读取证书中的公钥,用于后续加密了扩展:CA 证书CA 是证书颁发机构的简称,它会给自己签发一个根证书 Root CA,并且 CA 会通过根证书来签发中间证书,授权中间证书颁发机构签发证书的权限,最后由中间证书颁发机构向用户签发用户证书。之所以多一层中间证书是为了保护根证书,减少根证书被攻击或者被破解的风险。当然中间证书可能不止一个。因此通常用户收到的证书是 3 个:根证书、中间证书、用户证书。事实上,申请到的证书只是用户证书,其他 2 个很早就被签发了。浏览器为何新任 CA 证书呢?因为 CA 是被 WebTrust 信任的第三方组织,且只有通过 WebTrust 国际安全审计认证的证书颁发机构 CA,其签发的证书才会被各大浏览器信任。根证书库包含浏览器信任的证书颁发机构 CA 的根证书,有的浏览器会自建根证书库,比如 Mozilla Firefox,有的浏览器会使用其他浏览器的根证书库。浏览器如何校验证书合法性呢?由于用户证书被中间证书信任,而中间证书被根证书信任,根证书又被浏览器信任,这样一个完整的证书链使得浏览器可以在根证书库内一次检索用户证书、中间证书和根证书,如果能匹配到根证书,那么这一信任链上的所有证书都是合法的。2. 在windows客户端通过openssl命令生成下面是生成自签名证书,仅为测试使用,需要在访问者浏览器导入自己生成的ca根证书。生产环境请像ca申请证书。 下面红色文字其实就是我们像ca申请证书我们需要做的,黑色部分是ca实现,并且ca的私钥自己保存不会泄露给第三方,ca用私钥生成的带签名证书会内置到浏览器里面。// 生成服务器端私钥 openssl genrsa -out server.key 2048 //生成服务端公钥 openssl rsa -in server.key -pubout -out server.pem //生成CA私钥 openssl genrsa -out ca.key 2048 //生成ca的csr文件 openssl req -config "F:\greensoft\openssl\openssl.cnf" -new -key ca.key -out ca.csr //生成ca的自签名证书 openssl x509 -req -in ca.csr -signkey ca.key -out ca.crt //生成server.csr文件 openssl req -config "F:\greensoft\openssl\openssl.cnf" -new -key server.key -out server.csr //生成带有ca签名的证书 Openssl x509 -req -days 365 -sha256 -CA ca.crt -CAkey ca.key -CAcreateserial -in server.csr -out server.crt 3.浏览器导入ca的自签名证书ca.crt自签名证书需要导入about:preferences#privacy 隐私与安全 证书 查看证书 个人 导入 4.然后在浏览器打开https发现就可以了导入之后可以忽略警告访问,不导入没法忽略警告继续访问。 ssh公钥和私钥以及私钥登录服务器的配置1. 传统的登录centos服务器一般我使用xshell,然后使用密码登录。本文主要介绍如何使用秘钥登录。2. 创建密钥对,秘钥对指的是公钥和私钥。秘钥对在哪里生成无所谓,比如我可以在centos服务器上面生成,我也可以在我本地windows生成。最后服务端centos配置下公钥文件,本地的xshell客户端配置下私钥文件。这样就可以登录了。一般默认centos公钥文件保存位置在/root/.ssh/authorized_keys里面,一行一个,可以有多个公钥。多个公钥对应多个私钥,也就是能够给不同的人分配不同的私钥来使用这台服务器了。如下这种格式。这种公钥格式和我们传统的公钥格式不一样,你可以大概的认为这是ssh专属公钥格式。ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQDr3vyxyQ/XHj3TkmjhheY0JAG6lLEUyDSdksNQ+a4meC7Jlhj+bAi7RGcJO1JxpewsgIC9dbl9lDWcrJm6ZXVwqevIbbzVIQsfpGPxJJaElI6oDPaQ/T24yVbULUoXTvYxldzMsU/6l2RNe+PwtWz0MeQl6jdTqqEk6T1rBvgA4PIymhndBcq3RCYBNl4ghIL1HiK9nyXtD4n58qhawEreBepQYDAydaRW8+zVspPlfhYUnktAacPaCRmmNxtk/RgA7cWF5XHsBuP4YXWhpvG2f4CYAZRNcaLvIbdvPgxeqV4axis83zcaeYBW88n4YEe6dj9wpand71b1FHFmzZbO90oVTf+rXFMLNBJ+COOOVFlIgHzyZ4h70Y5YfOrpxBHHqF7WOsQ9W3oOluNWOgxkRjXW7be8fqFMJ8wDvw/hG2jmS9GOpLFn3xgVmK9/WRUWzyfqrG579bCRGf1/gIu3Qc57T8CmU/8haFhL9TGMTF6xYDLITXSBSBOXT55ZA6E= root@wltestssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQDU2JIqkAVQTlO0Pia8/7yp4gnIu1uTJ4Hq07/Jvd0yYD9C/30Khm4wY0gw8RGNU/D7eP57qHdGM6KCcZ75DwR6rO5owJmZQW7QGohem8GRJF58PrRqEhCvP6XOaKyTQJr+T4uq1aVJWHBYaeyVh80nwnREsky8tFSPGos2X/qhrlue0jX+hDu4hvWFJgP0oLbYBs4QJoH6TIcdeos2gxK1/U7/saru5Q3YDuU54/mA4RcF7izI4koKcBLSAilaZBys0OtBEx95Y/eGuIPEunDLf1Q6JBBK+pE0Jz0orS6Kdt8S7QsMBO2Z2pt8ZvPeuI6k5c0H7ThmX56ZaL6Q5H5FTbDoLz6TM4QGbwgbDv/HXUc50cHJPKFAoo0R5S4QSEptzL8B719L2FYCX27fS9ni3O7+3A/RDvThdmQXaLYeyucJHGpL1I5wlBnUrOXH2Q4sn5xQmZWldDjt7+zsjwXvPpwy2YNrlqp4XLPKcqFDGnqK/oZjdfA+pkAOfnYBgu0= 613154514@qq.com3. windows下如何生成ssh秘钥对ssh-keygen -t rsa -C "613154514@qq.com"注意:这里的 xxxxx@xxxxx.com 只是生成的 sshkey 的名称,并不约束或要求具体命名为某个邮箱。现网的大部分教程均讲解的使用邮箱生成,其一开始的初衷仅仅是为了便于辨识所以使用了邮箱。-t参数还支持这些 'ssh-rsa', 'ssh-dss', 'ssh-ed25519', 'ecdsa-sha2-nistp256', 'ecdsa-sha2-nistp384' or 'ecdsa-sha2-nistp521' 比如 ssh-keygen -t ed25519 -C "xxxxx@xxxxx.com" 上图文件.pub是公钥需要放到centos服务器的这个文件里面/root/.ssh/authorized_keys,一行一个多多个,该完记得重启sshd服务。systemctl restart sshd.service4. 然后本地在xshell里面使用私钥+私钥密码形式登录就可以了。Xshell客户写的是public key这里需要注意下,这里是私钥。公钥保存在服务器上面的,私钥在自己手机保存。5. 如何通过私钥导出公钥,id_rsa是私钥文文件,new.pub 是导出的公钥文件。私钥有密码保护,所以需要输入正确的密码才能导出成功。 ssh-keygen -y -f id_rsa > new.pubnew.pub 和id_rsa.pub公钥内容是一样的。
在 github 创建一个公开的仓库托管代码。本地下载安装 hexonpm install hexo-cli -ghexo init blog_hexocd blog_hexonpm install第一个命令全局安装 hexo 命令,第二步下载 hexo 到本地,第三步进入 hexo 目录,第四部安装依赖,第三步和第四部如果不是本地跑起来非必须。然后把.travis.yml 这个拷贝到根目录。把 cname 文件拷贝到 source 目录,.travis.yml 这是自动化部署流水线脚本。cname 是 github pages 绑定的域名,需要提前做解析 cname 到 wlphp.github.io,wlphp 是我的仓库地址,githubpages 是根据这个 cname 文件做域名绑定的,cname 文件里面就只写一个你 cname 到 仓库地址.github.io 的一个域名,而且自定义的域名支持一键 https 的也是。为啥 came 文件要放到 source 目录呢?因为在流水线部署的时候执行 hexo generate 命令会构建打包后的静态文件放到根目录的 public 文件夹,放到 source 里面会自动被打包到 public 文件夹,部署的时候是部署 public 文件夹。.travis.yml 这个文件内容如下sudo: falselanguage: node_jsnode_js:14 # use nodejs v10 LTScache: npmbranches:only:- master # build master branch only复制代码script:hexo generate # generate static filesdeploy:provider: pagesskip-cleanup: truegithub-token: $GH_TOKENkeep-history: trueon:branch: master复制代码local-dir: public把本地的代码推送到仓库Git init 初始化仓库Git remote add origin https://github.com/wlphp/blog_hexo.gitGit add .Git commit -m ‘first commit’Git push -u origin master 推送到远程 origin 的 master 分支去配置 Travis CI打开设置 githubapps https://github.com/wlphp/blog_hexo/settings/installations设置成允许读取所有仓库代码即可,因为他要拉取你仓库代码,构建成静态文件的。这里就是授权的意思。然后我们需要在 travis ci 的 wlphp/blog_hexo 设置下环境变量,也就是为了能够在构建的时候把 github-token: $GH_TOKEN 替换下。https://github.com/settings/tokens在浏览器内新建一个标签页,前往 GitHub 新建 Personal Access Token,只勾选 repo 的权限并生成一个新的 Token。Token 生成后请复制并保存好。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x回到 Travis CI,前往你的 repository 的设置页面,在 Environment Variables 下新建一个环境变量,Name 为 GH_TOKEN,Value 为刚才你在 GitHub 生成的 Token。确保 DISPLAY VALUE IN BUILD LOG 保持 不被勾选 避免你的 Token 泄漏。点击 Add 保存。公钥不直接放到.travis.yml 里面也是为了防止泄露。5.重新推送到仓库下如果没有出现错误就部署成功了http://hexo.github.wlphp.com/https 的和 http 的都可以通过自己的域名访问了。我们来到仓库的设置 pages 看下这个域名是根据 cname 文件里面的域名自动绑定的。下面还有一个强制 https 访问。这样我们就部署成功了。后续在本地 hexo 添加好新的文章之后,我们只需要把代码推送到 github 的远程 master 仓库,就会自动触发 Travis CI 的流水线,根据他的配置文件.travis.yml 自动拉取仓库的 master 代码,然后构建到根目录的 public 文件夹,同时 cname 域名绑定文件也会到 public 文件夹下面。然后把 public 下面的编译好的静态文件部署到 github pages。说下 github pages 的优势Github pages 上面能够运行纯静态的网站,比如 hexo 构建之后的产物,比如纯静态的 html,css,js 等。Gitee pages 飞付费版不支持自定义域名,只能使用他的域名,虽然他在国内速度快但是,要求使用自己的域名的时候 gitee pages 就没办法了。Github pages 采用了 cdn 虽然是海外的 ip 但是国内也能打开。不需要考虑费用,域名还不用备案。前后端分离的个人测试项目,前端部分可以放上去。上文用到的名词解释下:Github 是啥?GitHub 是一个面向开源及私有 软件项目的托管平台,因为只支持 Git 作为唯一的版本库格式进行托管,故名 GitHub。 GitHub 于 2008 年 4 月 10 日正式上线,除了 Git 代码仓库托管及基本的 Web 管理界面以外,还提供了订阅、讨论组、文本渲染、在线文件编辑器、协作图谱(报表)、代码片段分享(Gist)等功能。目前,其注册用户已经超过 350 万,托管版本数量也是非常之多,其中不乏知名开源项目 Ruby on Rails、jQuery、python 等。GitHub 去年为漏洞支付了 16.6 万美元赏金。 2018 年 6 月,GitHub 被微软以 75 亿美元的价格收购。Github pages 是啥?Github Pages 是 github 公司提供的免费的静态网站托管服务,用起来方便而且功能强大,不仅没有空间限制,还可以绑定自己的域名。https://docs.github.com/cn/pages是他的使用文档。https://pages.github.com/ 这是他的官网介绍说明页面。Hexo 是啥?Hexo 是一款基于 Node.js 的静态博客框架,可以方便的生成静态网页托管在 GitHub 和 Coding 上,是搭建博客的首选框架。Travis CI 是啥?Travis CI 提供的是持续集成服务(Continuous Integration,简称 CI)。它绑定 Github 上面的项目,只要有新的代码,就会自动抓取。然后,提供一个运行环境,执行测试,完成构建,还能部署到服务器。持续集成指的是只要代码有变更,就自动运行构建和测试,反馈运行结果。确保符合预期以后,再将新代码"集成"到主干。持续集成的好处在于,每次代码的小幅变更,就能看到运行结果,从而不断累积小的变更,而不是在开发周期结束时,一下子合并一大块代码。Travis 要求项目的根目录下面,必须有一个.travis.yml 文件。这是配置文件,指定了 Travis 的行为。该文件必须保存在 Github 仓库里面,一旦代码仓库有新的 Commit,Travis 就会去找这个文件,执行里面的命令。这个文件采用 YAML 格式。下面是一个最简单的 Python 项目的.travis.yml 文件。language: pythonscript: true上面代码中,设置了两个字段。language 字段指定了默认运行环境,这里设定使用 Python 环境。script 字段指定要运行的脚本,script: true 表示不执行任何脚本,状态直接设为成功。Travis 默认提供的运行环境,请参考官方文档 。目前一共支持 31 种语言,以后还会不断增加。下面是一个稍微复杂一点的.travis.yml。language: pythonsudo: requiredbefore_install: sudo pip install fooscript: py.test上面代码中,设置了四个字段:运行环境是 Python,需要 sudo 权限,在安装依赖之前需要安装 foo 模块,然后执行脚本 py.test。具体参考官方文档:https://docs.travis-ci.com/user/languages/python/Git 是啥?Git 是一款分布式源代码管理工具(版本控制工具)我们写的代码需要使用 Git 进行管理。源代码有必要管理起吗?有必要,因为人工的去处理不同的版本,做相应备份会很麻烦。Git 是 linux 之父当年为了维护 linux—linus 之前也是手动维护合并把文件发给 Linuslinus 自己写了一个版本管理的工具(Git)特点:Git 易于学习, 占用空间小,性能快如闪电。它优于 Subversion、CVS、Perforce 和 ClearCase 等 SCM 工具,具有便宜的本地分支、方便的暂存区和 多个工作流等功能。git 分为哪几个区?分别是什么?①工作区(Workspace)是电脑中实际的目录。②暂存区(Index)类似于缓存区域,临时保存你的改动。③仓库区(Repository),分为本地仓库和远程仓库。通常提交代码分为几步:①git add 从工作区提交到暂存区②git commit 从暂存区提交到本地仓库③git push 或 git svn dcommit 从本地仓库提交到远程仓库git 与 svn 的区别?svn 集中式代码版本控制管理工具git 分布式代码版本控制管理工具 git 最流行集中存放在服务器端 传统 url 地址: 账号名:密码svn 集中式的 如果出现 svn 服务器出现故障 每个用户都不能访问服务器 代码无法同步 git 就没有这种问题git 与 github、码云、gitlab 的关系github、码云、gitlab 都是在线的代码托管平台他们都支持 git 管理代码的方式github.com: 全球最大免费代码托管平台 码云: 国内免费代码托管平台 gitlab:企业项目开发使用广泛
1. 为什么要迁移到阿里云函数?我的项目是一个节日礼品领取项目,过节的时候会有短时间的流量洪峰。平时访问量很低。之前的架构是购买的阿里云alb+多台ecs+云msyql+云redis。最大的问题就是成本问题。平时流量低的时候ecs成本也无法缩减。阿里云函数计算是serverless,即无服务架构,就比如你的业务流量短时间突然很多。函数计算就会毫秒级别启动多个实例(阿里云函数计算 FC 用来运行函数的最小单元),如果没人访问可以没有实例运行,做到0费用。但是有人访问的时候第一次冷启动就稍微慢一点,可以根据实际情况设置最少保留一个实例。部署到阿里云函数计算,还能减少运行环境搭建的成本。之前的模式需要在ecs安装nginx,然后安装php,以及安装php的驱动程序redis等。看了下阿里云函数计算官方文档,目前custom runtime Debian 9内置php7.4并且看了下内置的php的扩展整好也支持到了我整好需要的redis。不仅剩下了买服务器钱,而且还不用安装php环境了,不仅如此每个月还有免费的算力额度。迁移原因总结下:1成本降低了很多2免去了环境部署3自动扩容,天生应对高并发2. 改造旧项目适配函数计算。代码改动:虽然说免去了环境部署,但是我之前的代码还是有些不适配的地方,比如之前代码的日志都是存放到服务器的某个目录的。如果迁移到函数计算的话,实例会随时销毁重建,导致日志丢失。解决办法就是把日志写入到阿里云oss上面,或者使用阿里云的日志服务写到那个里面去。这里还有一点要注意,我的项目不是前后端分离的,鉴权还是穿透的session和cookie模式。如果session是保留在服务端的文件的话也会存在上面的问题,建议存储的redis里面,我的项目本省就是存到redis里面的,所以这块不需要改动,如果你的项目存在这样的问题那就需要改进下了。函数计算和云msyql和云redis通讯的时候一定要采用vpc内网互通的原则减少链路传输的开销以及链路劫持风险。3. 增加s.yml以及启动shell脚本配置s.yml使用Serverless Devs客户端工具发布到阿里云函数计算,Serverless Devs这个工具并非阿里云的客户端工具,而是一个开源开放的 Serverless 开发者平台,致力于为开发者提供强大的工具链体系。通过该平台,开发者不仅可以一键体验多云 Serverless 产品,极速部署 Serverless 项目,还可以在 Serverless 应用全生命周期进行项目的管理,并且非常简单快速的将 Serverless Devs 与其他工具/平台进行结合,进一步提升研发、运维效能。它的官网地址:https://www.serverless-devs.com/然后看下我的s.yml里面的配置信息,具体的说下重要项是干嘛的。edition: 1.0.0name: compoent-testaccess: 'default'services: cn-hangzhou-test1002-func-3i3c0f95: component: devsapp/fc props: region: cn-hangzhou service: logConfig: enableRequestMetrics: true enableInstanceMetrics: true logBeginRule: DefaultRegex project: aliyun-fc-cn-hangzhou-ae3ef8b8-db4a-5b7a-a040-7012789ad20f logstore: function-log role: acs:ram::1621341641365186:role/AliyunFcDefaultRole internetAccess: true name: test1002 function: customRuntimeConfig: command: - bash args: - '-c' - 'chmod 777 /code/start.sh && /code/start.sh' handler: index.handler instanceType: e1 runtime: custom timeout: 5 instanceConcurrency: 20 memorySize: 512 caPort: 9000 environmentVariables: {} internetAccess: true name: func-3i3c0f95 asyncConfiguration: {} codeUri: ./test1002/func-3i3c0f95 triggers: - name: defaultTrigger description: '' type: http qualifier: LATEST config: methods: - GET - POST - PUT - DELETE authType: anonymous codeUri: ./test1002/func-3i3c0f95这个指定的是我的项目代码的位置,会把这个目录下面的代码拷贝到 debain系统的 /code目录下面。 customRuntimeConfig: command: - bash args: - '-c' - 'chmod 777 /code/start.sh && /code/start.sh'这句话的是项目启动脚本,其实就是执行这个start.sh的shell脚本,先给予一个777的权限,然后在执行。翻译成shell脚本其实就是 bash -c 'chmod 777 /code/start.sh && /code/start.sh' caPort: 9000监听端口9000一定要和启动脚本start.sh里面的一样#!/usr/bin/env bashcd /code/tp5/publicphp -S 0.0.0.0:9000 router.php这里我觉得我还是要说下这个启动脚本,先cd到public目录,thinkphp5的入口在public下面这个和项目框架有关系。然后就是这个启动脚本,上面这是thinkphp5特有的写法。其他项就不详细说了大概看看应该能看懂。 4使用客户端工具发布工具的安装就忽略了不说了,看下官方文档说的很详细https://docs.serverless-devs.com/serverless-devs/quick_startServerless Devs这个工具安装好后,配置配置上阿里云的AccessKey ID和AccessKey Secret,在项目根目录建立s.yml,以及在代码目录建立上面的启动脚本start.sh然后就可以使用客户端工具的 s deploy 部署到阿里云函数计算了。发布成功了4. 绑定自己的域名https://fcnext.console.aliyun.com/cn-hangzhou/domains/create把自己的域名cname到上图的 “公网cname”,然后 服务名称 测试函数 版本都对应选择正确。创建即可。部署成功了,哈哈。5.谈下自己的感受函数计算serverless是以后的趋势,开发者能够有更多的精力去关注业务层。从开始预计迁移到代码的修改以及阿里云函数计算文档查阅,到迁移成功,花费了大概3天的时间,对阿里云函数计算有了更深层次的认知,同时也期待迁移过来的项目在下次使用高峰的时候能够稳定运行。我后面也会随时关注阿里云函数计算的动态,同时感谢阿里云函数计算团队能做出来这么优秀的产品。
什么是函数计算?函数计算是事件驱动的全托管计算服务。使用函数计算,您无需采购与管理服务器等基础设施,只需编写并上传代码。函数计算为您准备好计算资源,弹性地可靠地运行任务,并提供日志查询、性能监控和报警等功能。借助函数计算,您可以快速构建任何类型的应用和服务,并且只需为任务实际消耗的资源付费。背景说明:之前网上有好多的ip转地址的接口,但是后来好多都商用了,或者有的转化的不理想,不经意间在gitee上看见有个老哥有个开源的项目https://gitee.com/lionsoul/ip2region 觉得挺不错的,因为我的好多项目都需要这样一个功能,于是想做一个中枢的接口,给我的其他项目通过api的方式调用。这样也方便我后期统一更新这个ip2region的地址库,同时也可以方便的给提供其他小伙伴使用。后来了解到阿里云函数计算有免费的额度,能够弹性扩容,免运维等好处,于是把这位老哥开源的ip转化服务改成了阿里云的函数计算代码。下面说下步骤:https://fcnext.console.aliyun.com/cn-beijing/services1.根据页面的提示先创建一个服务。2.创建函数,选择使用标准runtime从0创建,因为我只是一个接口服务,用最轻量的形式创建即可。运行环境选择php7.2,目前只有7.2,如果你是把已有的项目迁移进来,或者是php的其他版本,选择后面的自定义运行时。代码包通过我上面的gitee地址下载即可。这里给官方一个建议直接可以拉去开源的gitee或者github地址的代码,那样更方便些。3.如何访问。点击下图里面的url,提示一个文件下载,打开下载的文件,里面是返回的转化后的地址的json,这里为啥是下载呢?使用的阿里云的域名会在头部增加强制下载,绑定自己的域名,通过自己的域名访问就好了。4.绑定自己的域名根据下图填写域名然后自己去cname,这里给官方提个小建议,本账号下面的域名添加的时候最好能支持自动cname那样就更方便了。下面是路由配置 /* 指的是根目录下面所有 指向 这个ip2region这个服务下面的ip2region这个函数下面的最新版本。5.可以通过自定义域名访问了就。是不是很简单。6.使用阿里云的pts对其进行压测看下业务成功率能到达99%以上,这个结果还是非常满意的。7.说下自己的使用感受首先从成本角度来说,相对于传统的自己去购买服务器,函数计算能够节省很多,尤其是那种短时间高流量大并发的场景,真正能够实现没有业务的情况下零费用。而且每个月还有一定的免费额度。从安全性角度来说对于之前把代码部署到ecs上面的情况不用由于系统的bug导致中毒导致业务瘫痪了,足够安全,稳定。运维角度来讲基本没有什么额外的运维工作量,基本做到了免运维。从系统的业务并发承载量来讲,由于后端能动态扩容,基本也不会产生什么瓶颈了。从上面的压力测试上面看,比自己在ecs上面运行业务代码要可靠的多了还能够配合阿里生态集成持续交付部署。虚拟主机时代=>独立主机走红=》vps爆发=》云服务器崛起函数计算serverless应该是后端服务提供的最终形态。源代码地址以及说明:https://gitee.com/wlphp/aliyun-fc-ip2regioin
最近由于需要大概研究了一下MYSQL的随机抽取实现方法。举个例子,要从tablename表中随机提取一条记录,大家一般的写法就是:SELECT * FROM tablename ORDER BY RAND() LIMIT 1。但是,后来我查了一下MYSQL的官方手册,里面针对RAND()的提示大概意思就是,在ORDER BY从句里面不能使用RAND()函数,因为这样会导致数据列被多次扫描。但是在MYSQL 3.23版本中,仍然可以通过ORDER BY RAND()来实现随机。但是真正测试一下才发现这样效率非常低。一个15万余条的库,查询5条数据,居然要8秒以上。查看官方手册,也说rand()放在ORDER BY 子句中会被执行多次,自然效率及很低。You cannot use a column with RAND() values in an ORDER BY clause, because ORDER BY would evaluate the column multiple times.搜索Google,网上基本上都是查询max(id) * rand()来随机获取数据。SELECT * FROM `table` AS t1 JOIN (SELECT ROUND(RAND() * (SELECT MAX(id) FROM `table`)) AS id) AS t2 WHERE t1.id >= t2.id ORDER BY t1.id ASC LIMIT 5;MysqlCopy但是这样会产生连续的5条记录。解决办法只能是每次查询一条,查询5次。即便如此也值得,因为15万条的表,查询只需要0.01秒不到。下面的语句采用的是JOIN,mysql的论坛上有人使用SELECT * FROM `table` WHERE id >= (SELECT FLOOR( MAX(id) * RAND()) FROM `table` ) ORDER BY id LIMIT 1;MysqlCopy我测试了一下,需要0.5秒,速度也不错,但是跟上面的语句还是有很大差距。总觉有什么地方不正常,于是我把语句改写了一下。SELECT * FROM `table` WHERE id >= (SELECT floor(RAND() * (SELECT MAX(id) FROM `table`))) ORDER BY id LIMIT 1;MysqlCopy这下,效率又提高了,查询时间只有0.01秒最后,再把语句完善一下,加上MIN(id)的判断。我在最开始测试的时候,就是因为没有加上MIN(id)的判断,结果有一半的时间总是查询到表中的前面几行。完整查询语句是:SELECT * FROM `table` WHERE id >= (SELECT floor( RAND() * ((SELECT MAX(id) FROM `table`)-(SELECT MIN(id) FROM `table`)) + (SELECT MIN(id) FROM `table`))) ORDER BY id LIMIT 1;MysqlCopySELECT * FROM `table` AS t1 JOIN (SELECT ROUND(RAND() * ((SELECT MAX(id) FROM `table`)-(SELECT MIN(id) FROM `table`))+(SELECT MIN(id) FROM `table`)) AS id) AS t2 WHERE t1.id >= t2.id ORDER BY t1.id LIMIT 1;MysqlCopy最后在php中对这两个语句进行分别查询10次,前者花费时间 0.147433 秒后者花费时间 0.015130 秒看来采用JOIN的语法比直接在WHERE中使用函数效率还要高很多。
终于可以开始本篇 Laravel 教程了;如果对 OAuth 和 JWT 还不是很了解;建议先出门左转阅读上面链接中的系列文章;我们先来回顾一下之前讲的 OAtuh 的四个角色;资源服务器(resource server):github 服务器授权服务器(authorization server):github 服务器资源所有者(resource owner):用户你客户端(client):第三方白俊遥博客资源服务器(resource server)和授权服务器(authorization server)我们统称为 server ;server 提供 OAuth 的认证服务;client 则是使用 OAauth 服务;在 PHP 社区中有一个以开发高质量扩展包著称的组织 league ;他们提供了两个扩展包 oauth2-server 和 oauth2-client ;分别实现了 server 和 client 服务;Laravel 官方在 oauth2-client 的基础上开发了 socialite 用于实现 OAuth 第三方登录功能;在 oauth2-server 基础上开发了 Passport 用于实现 OAuth 的认证服务;Passport 提供了以下功能:管理 clientsOAuth 的四种授权类型个人访问令牌 (personal access tokens)管理令牌作用域 (scope)铺垫完毕下面正式进入 Passport 环节;Passport 默认没有安装;使用前需要先 require 引入包;composer require laravel/passportBashCopy运行迁移生成表:php artisan migrateBashCopy迁移命令会生成如下表:表名作用oauth_clients管理 clinet , 对应于前面的例子中的 白俊遥博客oauth_auth_codes管理授权码模式 (Authorization Code) 中的 codeoauth_access_tokens管理用于认证的 access_tokenoauth_refresh_tokens管理用于刷新 access_token 的 refresh_tokenoauth_personal_access_clients用于 personal access tokenPassport 提供了 3 个命令;生成用于加密的 keyphp artisan passport:keysBashCopy执行后会在 storage 目录下生成 key 文件;创建 Client 的命令php artisan passport:clientBashCopy生成 key 并创建一个密码类型的 client 和个人令牌这个命令是组合调用了上面的命令1 + 命令2php artisan passport:installBashCopy我们这里执行安装命令:php artisan passport:installBashCopy接着需要把 Laravel\Passport\HasApiTokens Trait 添加到 App\User 模型中;app/User.php<?php namespace App; use Laravel\Passport\HasApiTokens; use Illuminate\Notifications\Notifiable; use Illuminate\Contracts\Auth\MustVerifyEmail; use Illuminate\Foundation\Auth\User as Authenticatable; class User extends Authenticatable { use Notifiable, HasApiTokens; // ... }PHPCopy在 AuthServiceProvider 的 boot 方法中调用 Passport::routes() ;<?php namespace App\Providers; use Laravel\Passport\Passport; use Illuminate\Support\Facades\Gate; use Illuminate\Foundation\Support\Providers\AuthServiceProvider as ServiceProvider; class AuthServiceProvider extends ServiceProvider { //.. public function boot() { $this->registerPolicies(); Passport::routes(); } // ... }PHPCopy这一步主要是注册路由的;我们可以通过 route:list 命令查看注册的路由;如果我们有需要修改这些方法的时候;我们可以在 routes/api.php 覆盖这些路由即可;最后一步配置把 config/auth.php 中 guards 的 api 的 driver 选项改为 passport ;'guards' => [ 'web' => [ 'driver' => 'session', 'provider' => 'users', ], 'api' => [ 'driver' => 'passport', 'provider' => 'users', ], ],PHPCopyPassport 还提供了 vue 前端组件;php artisan vendor:publish --tag=passport-componentsBashCopy在 resources/js/app.js 文件中注册组件;resources/js/app.js// ... Vue.component( 'passport-clients', require('./components/passport/Clients.vue').default ); Vue.component( 'passport-authorized-clients', require('./components/passport/AuthorizedClients.vue').default ); Vue.component( 'passport-personal-access-tokens', require('./components/passport/PersonalAccessTokens.vue').default ); // ...JavaScriptCopy这里写一个管理 Client 的页面;routes/web.php// ... Route::view('clients', 'clients');PHPCopyresources/views/clients.blade.php@extends('layouts.app') @section('content') <passport-clients></passport-clients> @endsectionHTMLCopy访问 /clients 路由创建测试 Client ;创建 Client 需要在登录状态下;这里可以使用 Laravel 的用户认证模块;php artisan make:authBash接下来就是 OAuth 的 4 种类型了;Passport 的流程都是标准的 OAuth ;只是有些请求参数会稍微不同;这里就不赘述了;Passport 的 token 使用的是 JWT 格式;简单提一点的是这个 JWT 中是有一个用户 id 的 ;如果你的系统中需要加密用户 id 的话;除了标准的 OAuth 的 4 种类型;Passport 还提供了个人访问令牌功能;如果对 个人访问令牌不了解的话;我们再次拿出之前举的例子:OAtuh 的四个角色;资源服务器(resource server):github 服务器授权服务器(authorization server):github 服务器资源所有者(resource owner):用户你客户端(client):第三方白俊遥博客在讲 OAuth 的时候一直都是 github 和白俊遥博客在操作;那作为资源所有者的你如果也想使用 token 访问你自己的资源;这时候就可以使个人访问令牌功能了;个人访问令牌是没有过期时间的;自然也就没有刷新 token 的功能;Passport 同样提供了前端组件;跟之前的管理 Clients 一样;这里就业不再啰嗦了;
介绍作为开发者我们可能都有过这样的经历:Laravel v7 都已经发布了,而自己维护的项目仍然是公司祖传的 v5.3,迟迟不敢升级。修复了一个注册功能的 bug,结果把登录功能搞崩了,直到用户反馈才知道。新增功能或者修改代码都束手束脚,生怕对项目造成破坏性影响。而这些困境很大部分的原因在于项目缺少完善的测试,无法保障项目的健壮,接下来我们就来分享我司经过数年的实践,锤炼出来的一套成熟的测试工艺。利用 Laravel 提供的测试工具Laravel 对请求进行了封装,我们可以非常方便地在测试中发起各种请求,比如说测试用户列表。 下面是 Laravel 自带的示例代码:public function testBasicTest() { $response = $this->get('/users'); $response->assertStatus(200); }PHPCopy这里有几个问题:在运行测试前需要创建数据表。需要填充一些测试数据,否则测试空的用户列表没有太大意义。希望能验证请求返回的 response 数据是否符合预期。下面我们就来一一解决以上问题。MigrationsLaravel 提供了 migration 来创建和更改数据表结构,在测试启动前可以先运行 migrate 命令。随着时间的推移,表结构变化积累得越多,migration 耗时就会越长。Migration 对数据库做增量修改的做法并没有给测试带来任何好处,相反,在运行测试之前如果能对数据库进行全量构造,性能会提高很多。所以我们创建了自动化工具,对于每次添加新的 migration,都会自动生成全量的表结构描述文件用于测试。Seeding有了上一步的 migration 生成的数据表,我们还需要往数据表中插入测试数据,Laravel 再次贴心的提供了 seeding 功能,但是 seed 文件是通过手写的数组来存放数据,比如像下面这个样子:<?php use Illuminate\Database\Seeder; use DB; class UsersTableSeeder extends Seeder { public function run() { DB::table('users')->insert([ 'name' => 'baijunyao', ]); } }PHPCopy我们的业务逻辑比较复杂,需要多套测试数据集,每套测试数据集中的数据又各有依赖,所以我们维护数套 YAML 格式存储的数据集,并实现了 YamlSeeder 来将 YAML 数据用于 seed 测试数据库。users: - name: baijunyaoYAMLCopyAssert ResponseLaravel 提供了一系列的 assert 方法,但是一个一个的手动调用这些方法比较繁琐,手动硬编码 response 的数据就更加痛苦了。public function testBasicTest() { $response = $this->get('/users'); $response->assertStatus(200); $response->assertJson([ [ 'name' => 'baijunyao', ] ]); }PHPCopy我们的方案是开发了一套自动 snapshot 测试工具。我们的测试用例有两种模式运行测试:创建 snapshot 和执行测试。前者可以自动捕捉所有 API response 并以 JSON 格式存储,后者则会将该次测试输出和 snapshot 进行比较以做断言。以 JSON 格式存在的 snapshot 结果集随代码一同 commit 到代码仓库中,可以方便地追踪每次的代码修改对 response 造成的影响。{ "status_code": 200, "headers": { "cache-control": [ "no-cache, private" ], "date": "Mon, 06-Jan-2020 00:00:00 GMT", "content-type": [ "application\/json" ], "x-ratelimit-limit": [ 60 ], "x-ratelimit-remaining": [ 59 ], "set-cookie": [ "foo=bar; expires=Mon, 06-Jan-2020 01:00:00 GMT; max-age=3600; path=\/; secure; httponly" ], }, "content": [ { "name": "baijunyao" } ] }JSONCopy这种比对整个 response 的方案中有一些细节需要注意,比如:变量有一些变量比如日期,可能造成每次的 response 都不一样,我们可以使用 Carbon 在测试模式中设置一个固定的当前日期。Carbon::setTestNow(Carbon::create(2020, 1, 1, 0, 0, 0));PHPCopy对于其他一些变量数据采用Mockery,无法 mock 的则忽略变量部分。重置测试数据因为新增数据的功能测试会真实的向数据库中插入一条数据,为了清理脏数据,Laravel 提供了 Illuminate\Foundation\Testing\RefreshDatabase trait,它使用的是数据库的事务,存在的问题是数据虽然重置了,但是并没有重置自增型的主键,会造成每次运行测试时 id 不确定的问题。我们的解决方案是通过 DB 监听执行的 SQL,然后通过 TRUNCATE 来清理被污染的数据。app('db')->listen(function ($query) { // ... });PHPCopy数据库Laravel 默认是使用 SQLite 作为数据库,我们使用的则是 MySQL,在每次启动测试前创建一个临时数据库,测试结束后销毁这个临时的数据库,借助于 Docker 我们可以最大程度的保障测试环境跟生产环境的一致性。结语编写测试代码是一个必要而且有价值的工作内容,它可以让我们有底气的进行功能开发,快速的进行迭代,希望我们的测试方案对您有所帮助,围绕着测试我们开发了一系列的扩展包来简化我们编写测试代码的工作,如果对我们的测试方案有兴趣,欢迎留言,我们会逐步进行更深入更详细的讲解并开源这些工具。
宝塔,让运维简单高效。面板支持Linux与Windows系统。一键配置:LAMP/LNMP、网站、数据库、FTP、SSL,通过Web端轻松管理服务器。宝塔Linux面板是提升运维效率的服务器管理软件,支持一键LAMP/LNMP/集群/监控/网站/FTP/数据库/JAVA等100多项服务器管理功能。有30个人的专业团队研发及维护,经过200多个版本的迭代,功能全,少出错且足够安全,已获得全球百万用户认可安装。简单来说就是在服务器先安装宝塔面板,然后大部分软件安装都通过宝塔的web可视化界面去处理。本文介绍如何安装宝塔宝塔官网:https://www.bt.cn/下面开始介绍如何把宝塔安装在数据盘本文中的磁盘/dev/vdb为笔者测试服务器上的命名,在您的服务器中可能是/dev/xdb、/dev/sdb、/dev/xvdb等等 请根据实际情况进行修改1、创建挂载目录(www为宝塔默认安装目录)mkdir -p /www2、确认是否没有分区的磁盘,如下图,没有分区的磁盘是/dev/vdb,在您的服务器中可能是/dev/sdb,请注意按照实际名称修改fdisk -l3、为磁盘分区,若已分区,可跳过fdisk /dev/vdb4、输入n开始创建分区5、输入p创建主分区6、选择分区号 输入17、输入分区开始位置,直接回车8、输入分区结束位置,直接回车9、输入wq 回车退出10、检查是否分区成功(带有vdb1/sdb1/xvb1说明成功)fdisk -l11、格式化分区,这里输入看到的磁盘加分区号 如下图为/dev/vdb1 已格式化的可跳过mkfs.ext4 /dev/vdb112、将分区挂载信息添加到配置文件/etc/fstab中,实现开机/重启自动挂载echo "/dev/vdb1 /www ext4 defaults 0 0" >> /etc/fstab13、重新挂载所有分区mount -a14、检查是否挂载成功df -h挂载后即可安装面板
1. 第一步使用阿里云代码托管平台(云效codeup)把代码托管到仓库https://codeup.aliyun.com/2. 项目是vue-cli纯前端的项目需要在根目录新建build.sh 里面编写构建命令#!/bin/bashrm -rf dist# 静态文件npm installnpm run build 3. 使用阿里云云开发平台https://workbench.aliyun.com/建立前端应用选择codeup->自己的仓库->然后找到阿里云云效codeup里面的项目。4. 然后分支管理,如下图填写5. 然后部署配置填写自己的域名需要注意自己的域名需要cname解析到指定的域名6.最后点击部署6. 部署成功就可以使用自己域名访问了。项目成功上线啦!是不是超级简单呢?最后在让我看看阿里云云效的优势:企业级一站式DevOps平台,支持公共云、专有云和混合云多种部署形态,通过人工智能、自动化技术的应用提升开发者的研发效能,持续交付有效价值。包含产品:云效项目协作提供对需求、迭代、缺陷各个维度的协同管理以及相关的统计报告,让研发团队高效协作。云效知识库通过可协作的结构化文档,将知识积累和沉淀下来,并在团队中有效流动,由此提升企业的创造力。云效代码管理提供代码托管、评审和扫描、质量检测等功能,保护企业代码资产,实现安全、稳定、高效的研发生产。云效测试管理标准化管理测试用例,快速搭建一体化「开发->测试->反馈」流程,有效提升交付效率和质量。云效流水线提供灵活易用的持续集成、持续验证、 持续发布功能,帮助企业高质量、高效率的交付业务。云效制品仓库提供基于Maven、Gradle、Helm等软件包管理工具的企业级私有仓库服务,用于管理企业级依赖托管。优势所在:源自阿里自研平台 历经「双11」项目实践历经6万开发者实践检验,持续集成周构建超50万次,周代码扫描超30万次;2019年天猫双11订单峰值达到54.4万笔/秒,单日数据处理量达到970PB。应用人工智能技术 助力企业降本提效采用人工智能技术进行敏感信息监测,防止密钥意外泄露;「AI代码评审员」使用强大的算力挖掘代码隐藏缺陷,从多维度为代码评分,大幅提升评审效率。安全稳定的代码库 更加适合企业使用多副本高可用架构自动备份免运维;AI技术实现敏感信息检测,多层风控模型预测异常行为;企业间数据隔离及三级权限管控;完善的安全机制实现事后可追溯。专业项目管理平台 研发协作科学高效公开透明的任务看板,研发协作更轻松;强大的统计报表,绩效评定更合理;可视化的时间视图,科学调配研发资源;跨项目的全局视野,随时把控全局。自动化研发流水线 交付过程简单高效提供数十种通用的流水线模版快速创建流水线,提供可视化编排和结果展现;内置代码扫描、安全扫描和各种自动化测试能力,实现持续集成、验证、发布功能。简单灵活开箱即用 小微企业免费使用通过向导快速搭建一套一站式研发协作环境;支持公共云、专有云和混合云多种部署形态;扶持创新创业小微企业,30人以下研发团队0元享一站式研发套餐。
一、ipv6的重要性IPv6作为下一代网络的基础技术协议,于2012年6月正式商用,成为人们拥抱新技术的曙光。到2020年底我国IPv6终端设备将达到5亿,到2025年,我国在网络规模、用户规模和流量规模三个关键性指标方面将成为世界第一工业互联网、物联网、5G等新技术的发展对IP地址空间的需求极大,IPv6成为这些新兴领域的刚需。据预测未来5 年间全球将有500 亿物联网终端接入中共中央办公厅和国务院办公厅联合印发了《推进互联网协议第六版(IPv6)规模部署行动计划》将网络全面改造支持IPv6上升为国家级战略支持ipv6是必然也是趋势,阿里云开源镜像站在国内企业镜像站中率先支持IPv6访问! 阿里云镜像站官网:https://developer.aliyun.com/mirror/随便放一个ipv6站点检测工具:https://www.bt.cn/tools/ipv6.html 二、阿里云镜像站包含哪些镜像?开源镜像站是一个放置开源系统镜像文件的站点,免费提供镜像文件下载。众所周知,从开源软件官网的下载软件速度慢,稳定性不足,所以开源镜像站成了国内开发者的香饽饽。作为国内最大的开源镜像站之一的阿里云官方镜像站,也一直都在为国内的开源事业发展贡献力量,持续为用户提供免费、快速、稳定的镜像分发服务。目前阿里云镜像站覆盖了主流操作系统 CentOS、Ubuntu,Fedora,Gentoo,Debian,FreeBSD、优麒麟、Rocky Linux、OpenAnolis等,常见的编程语言和构建依赖包和工具,例如npm、Maven、PyPI、Composer、Jenkins等,以及云原生等领域的主流软件Kubernetes、Docker、MangoDB、MariaDB等,已经累计收录了超过150个开源软件的镜像。开发过程中经常需要使用第三方包或下载某些国外服务器上的资源,因为众所周知的原因,慢的一批。通过镜像站很轻松的解决这个问题。更多镜像持续更新中,如果找不到你需要的镜像还可以提交新增镜像需求。https://survey.aliyun.com/apps/zhiliao/5FTHFkGet
一、为啥用使用CentOS StreamCentOS项目今后将只专注于CentOS Stream,作为红帽企业Linux的上游/开发分支。发行方鼓励CentOS 8用户开始向CentOS Stream 8过渡。同时,红帽还表示,英特尔将在CentOS Stream上与他们和社区合作。红帽还谈到Facebook在他们的数据中心使用CentOS Stream的衍生产品。官方主页: https://www.centos.org/centos-stream/CentOS Stream并不是CentOS Linux的替代品;相反,它是一个自然的、不可避免的下一步,从而实现项目的目标,即进一步推进企业Linux创新。Centos Stream 是 Centos 8 之后一个滚动发布的 Linux 发行版。在2021年年底,Centos 8 将会停止维护,届时 Centos Stream 将作为RHEL的上游分支进行持续更新。在红帽开源开发模式中,红帽在发布红帽企业 Linux 新版本前,会先在 CentOS Stream 上开发红帽企业 Linux 源代码,使 CentOS Stream 成为红帽企业 Linux 未来版本的预览。因此我们在学习centos系统的时候要下载:CentOS Stream阿里云镜像站官网:https://developer.aliyun.com/mirror/阿里云centos Stream镜像地址:https://mirrors.aliyun.com/centos-stream/二、为啥要使用阿里云提供镜像地址?阿里云提供的镜像地址在国内,下载速度上肯定要比国外的地址快很多。阿里云镜像地址会定期同步官网镜像,能够保持一个比较新的镜像。阿里云镜像地址足够安全,镜像里面不会有一些非法脚本。相对于不不知名的第三方小网站对比,你不知道镜像里面会不会放一些后门啥的。https://mirrors.aliyun.com/centos-stream/9-stream/https://mirrors.aliyun.com/centos/8-stream/下载的时候注意下8-stream和9-stream的路径不同。
一、Putty介绍PuTTY是一个Telnet、SSH、rlogin、纯TCP以及串行接口连接软件。较早的版本仅支持Windows平台,在最近的版本中开始支持各类Unix平台,并打算移植至Mac OS X上。除了PuTTY官方版本外,有许多第三方的团体或个人将PuTTY移植到其他平台上,像是以Symbian为基础的移动电话。PuTTY为一开放源代码软件,主要由Simon Tatham维护,使用MIT licence授权。Putty官网:https://www.chiark.greenend.org.uk/简单的来说putty是一个开源免费的ssh工具,算是一个轻量级的工具,包虽然很小,但是通过国外网站下载还是很慢。有些国内的网站也提供putty下载,但是软件版本老旧,更有甚者里面带有很多的广告,里面被植入了后门也说不定呢?为此我推荐使用阿里云镜像站下载putty。阿里云镜像站官网:https://developer.aliyun.com/mirror/阿里云putty镜像地址:https://mirrors.aliyun.com/putty/二、Putty软件下载https://mirrors.aliyun.com/putty/最新版本地址:https://mirrors.aliyun.com/putty/latest.html下载的时候需要注意有windows的安装版本:putty-64bit-0.76-installer.msi有免安装的绿色版本:putty.exe有源代码,也有适配好平台的编译后版本。如果你是windows的系统,选择上面版本就够了。如果是其他系统根据说明下载即可。这是一个镜像网站,会定时同步putty官网软件包,会有一定的延迟。
一、openwrt介绍OpenWRT(曾用名 LEDE)是一款广泛应用于路由器的嵌入式操作系统。由于其开源,所以在不同硬件上面自己修改源码就可以适配,所以得到了很广泛的使用。官方地址:https://openwrt.org/官方论坛:https://forum.openwrt.org/与Ubuntu的apt-get、Centos的yum类似,Openwrt也有类似的包管理器opkg,所以后台也维护着软件源。但是因为源在国外,所以下载十分的慢,按照Linux使用习惯,我们将它切换为国内源。我个人在使用OpenWrt官方的软件源时,其实没有朋友们所说的慢到抓狂,但偶尔还是会出现延迟很高的情况,如果有更高速更稳定的需求,国内有很多镜像源可供使用。那么如何切换成阿里源呢? 阿里云镜像站官网:https://developer.aliyun.com/mirror/阿里云openwrt源地址:https://mirrors.aliyun.com/openwrt二、Openwrt更换阿里云源方法手工替换登录到路由器,并编辑 /etc/opkg/distfeeds.conf 文件,将其中的 downloads.openwrt.org 替换为 mirrors.aliyun.com/openwrt 即可。自动替换执行如下命令自动替换 sed -i 's_downloads.openwrt.org_mirrors.aliyun.com/openwrt_' /etc/opkg/distfeeds.conf 更新源opkg update三、openwrt其他推荐源地址清华大学:mirrors.tuna.tsinghua.edu.cn/openwrt中科大:mirrors.ustc.edu.cn/openwrt腾讯:mirrors.cloud.tencent.com/openwrt
一.为什么要切换使用阿里云的公共NTP服务器呢?NTP 是网络时间协议 (Network Time Protocol),它是用来同步网络中各个计算机的时间的协议。为什么修改云服务器的DNS呢,中国时间服务器,国内阿里云时间服务器很多用户使用的是国外VPS使用过程中常常遇到时间与国内不同步的情况好在阿里提供了NTP服务器,地址如下:阿里云镜像站官网:https://developer.aliyun.com/mirror/阿里云NTP服务器官网:https://developer.aliyun.com/mirror/NTP二.如何切换使用阿里云NTP服务器。Linux 服务器上快速配置阿里巴巴 OPSX NTP服务编辑文件 "/etc/ntp.conf",根据情况修改文件内容为:· 互联网上的服务器:driftfile /var/lib/ntp/driftpidfile /var/run/ntpd.pidlogfile /var/log/ntp.logrestrict default kod nomodify notrap nopeer noqueryrestrict -6 default kod nomodify notrap nopeer noqueryrestrict 127.0.0.1server 127.127.1.0fudge 127.127.1.0 stratum 10server ntp.aliyun.com iburst minpoll 4 maxpoll 10restrict ntp.aliyun.com nomodify notrap nopeer noquery · 阿里云 ECS 服务器:driftfile /var/lib/ntp/driftpidfile /var/run/ntpd.pidlogfile /var/log/ntp.logrestrict default kod nomodify notrap nopeer noqueryrestrict -6 default kod nomodify notrap nopeer noqueryrestrict 127.0.0.1server 127.127.1.0fudge 127.127.1.0 stratum 10server ntp.aliyun.com iburst minpoll 4 maxpoll 10restrict ntp.aliyun.com nomodify notrap nopeer noqueryserver ntp1.cloud.aliyuncs.com iburst minpoll 4 maxpoll 10restrict ntp1.cloud.aliyuncs.com nomodify notrap nopeer noqueryserver ntp2.cloud.aliyuncs.com iburst minpoll 4 maxpoll 10restrict ntp2.cloud.aliyuncs.com nomodify notrap nopeer noqueryserver ntp3.cloud.aliyuncs.com iburst minpoll 4 maxpoll 10restrict ntp3.cloud.aliyuncs.com nomodify notrap nopeer noqueryserver ntp4.cloud.aliyuncs.com iburst minpoll 4 maxpoll 10restrict ntp4.cloud.aliyuncs.com nomodify notrap nopeer noqueryserver ntp5.cloud.aliyuncs.com iburst minpoll 4 maxpoll 10restrict ntp5.cloud.aliyuncs.com nomodify notrap nopeer noqueryserver ntp6.cloud.aliyuncs.com iburst minpoll 4 maxpoll 10restrict ntp6.cloud.aliyuncs.com nomodify notrap nopeer noquery三.其他公共推荐的NTP服务器使用以前请先ping相应的域名查看网络是否可达,和相应的访问速度 ntp.ntsc.ac.cn 中国国家授时中心 cn.ntp.org.cn 中国授时 阿里云NTP时间源服务器ntp1.aliyun.comntp2.aliyun.comntp3.aliyun.comntp4.aliyun.comntp5.aliyun.comntp6.aliyun.comntp7.aliyun.com 国内一些大学NTP时间源服务器s1a.time.edu.cn 北京邮电大学s1b.time.edu.cn 清华大学s1c.time.edu.cn 北京大学s1d.time.edu.cn 东南大学s1e.time.edu.cn 清华大学s2a.time.edu.cn 清华大学s2b.time.edu.cn 清华大学s2c.time.edu.cn 北京邮电大学s2d.time.edu.cn 西南地区网络中心s2e.time.edu.cn 西北地区网络中心s2f.time.edu.cn 东北地区网络中心s2g.time.edu.cn 华东南地区网络中心s2h.time.edu.cn 四川大学网络管理中心s2j.time.edu.cn 大连理工大学网络中心s2k.time.edu.cn CERNET桂林主节点s2m.time.edu.cn 北京大学 另外,如果服务器在国外,苹果提供的公共NTP服务,以下7个域名http://time1.apple.comhttp://time2.apple.comhttp://time3.apple.comhttp://time4.apple.comhttp://time5.apple.comhttp://time6.apple.comhttp://time7.apple.com Google(谷歌)提供的NTP服务,以下4个域名http://time1.google.comhttp://time2.google.comhttp://time3.google.comhttp://time4.google.com
一.为什么要切换使用阿里云的公共dns服务器呢?为什么修改云服务器的DNS呢,这样是为了更好的提升网站解析能力,从而提升网站打开的速度。我用的就是阿里云默认的公共DNS服务器(223.5.5.5、223.6.6.6),由于是阿里人自己的产品,心理上觉得可能会快点,毕竟服务器在他们那里。 阿里云镜像站官网:https://developer.aliyun.com/mirror/阿里云DNS公共服务器官网:https://developer.aliyun.com/mirror/DNS 二.如何切换使用阿里云公共dns服务器。Linux 快速配置ipv4编辑文件 "/etc/resolv.conf",根据情况修改文件内容。互联网上的服务器只需要添加以下两行:nameserver 223.5.5.5nameserver 223.6.6.6三.其他公共推荐的dns服务器境内公共DNS 名称首选地址备选地址 114 DNS114.114.114.114114.114.115.115 阿里DNS223.5.5.5223.6.6.6 百度DNS180.76.76.76 腾讯DNS119.29.29.29182.254.116.116
一、为啥centos8要换源?当前时间是2022年2月22号2021年12月31日CentOS 8操作系统版本结束了生命周期(EOL),Linux社区已不再维护该操作系统版本。后续新的服务器建议使用CentOS Stream,或者其他linux版本,按照社区规则,CentOS 8的源地址http://mirror.centos.org/centos/8/内容已移除,目前第三方的镜像站中均已移除CentOS 8的源。但是对于业务过渡期仍然需要使用centos8,并且在上面安装软件,那我们需要更新备用源,之前第三方的源都是同步centos官方的,并随之更新,官方的源去掉后,第三方也就无法同步到了,所以第三方的源也就不复存在了,但是大部分第三方都把最后的源做了个备份弄了个新的地址,方便过渡期使用。当然切换到阿里云的源速度也会更快些。 阿里云镜像站官网:https://developer.aliyun.com/mirror/阿里云centos源地址:https://developer.aliyun.com/mirror/centos 二、如何更换centos8源的阿里云镜像地址两种情况,阿里云ecs和非阿里云ecs,非阿里云ecs要求可以连接上公网,如果是阿里云ecs使用阿里云提供的内网链接源,不是阿里云的服务器,使用的公网源,肯定内网源的速度会更快些。仔细观察就能发现内网链接:mirrors.cloud.aliyuncs.com公网链接:mirrors.aliyun.com非阿里云服务器用户:mv /etc/yum.repos.d/CentOS-Base.repo /etc/yum.repos.d/CentOS-Base.repo.backupwget -O /etc/yum.repos.d/CentOS-Base.repo https://mirrors.aliyun.com/repo/Centos-vault-8.5.2111.repoyum clean all && yum makecache阿里云服务器用户:mv /etc/yum.repos.d/CentOS-Base.repo /etc/yum.repos.d/CentOS-Base.repo.backupwget -O /etc/yum.repos.d/CentOS-Base.repo https://mirrors.aliyun.com/repo/Centos-vault-8.5.2111.reposed -i 's/mirrors.cloud.aliyuncs.com/url_tmp/g' /etc/yum.repos.d/CentOS-Base.repo && sed -i 's/mirrors.aliyun.com/mirrors.cloud.aliyuncs.com/g' /etc/yum.repos.d/CentOS-Base.repo && sed -i 's/url_tmp/mirrors.aliyun.com/g' /etc/yum.repos.d/CentOS-Base.repoyum clean all && yum makecache三、其他工具换源推荐使用linux一键换源脚本,https://supermanito.github.io/LinuxMirrors/#/不仅支持centos,还支持其他linux发行版本,感谢原作者的无私奉献。Gitee地址:https://gitee.com/SuperManito/LinuxMirrors
登录msyqlmysql -u root -p123456查询数据库 show databases;切换数据库use bakery;查看表中数据show columns from customer;show columns from product; select * from customer;select * from product;
一、安装树莓派系统首先下载树莓派的系统,镜像官网(https://www.raspberrypi.com/)速度下载比较慢,推荐使用阿里云镜像站下载。阿里云镜像站官网:https://developer.aliyun.com/mirror/阿里云树莓派系统镜像地址:https://mirrors.aliyun.com/raspberrypi/然后我们找到下载地址下载:https://mirrors.aliyun.com/raspberry-pi-os-images/raspios_full_arm64/images/raspios_full_arm64-2022-01-28/2022-01-28-raspios-bullseye-arm64-full.zip基于Debian11,当前日期是2022-02-16,目前最新的系统。下载完毕后解压,然后使用Win32DiskImager刷入内存卡,烧录系统这里就不多说了。把内存卡插入树莓派,通电开机,然后通过hdmi线接入显示器。就可以进入系统了。二、Raspberry Pi 更换阿里云源方法自带的源在海外,访问速度比较慢,更换成阿里云的源后安装软件速度会提升很大。阿里云raspberrypi 镜像源地址:https://developer.aliyun.com/mirror/raspberrypi?spm=a2c6h.13651102.0.0.5f851b11F6UTeG其他源:http://www.raspbian.org/RaspbianMirrors 编辑 /etc/apt/sources.list 文件,这里推荐就用系统自带的 nano 命令编辑,命令如下:sudo nano /etc/apt/sources.list进入编辑界面,删除原有的内容,粘贴如下内容:deb http://mirrors.aliyun.com/raspbian/raspbian/ bullseye main non-free contrib rpi deb-src http://mirrors.aliyun.com/raspbian/raspbian/ bullseye main non-free contrib rpi更新软件索引清单sudo apt-get update比较索引清单更新依赖关系sudo apt-get upgrade -y
1.什么是npm?npm(node package manager)是随同NodeJS一起安装的包管理工具,能解决NodeJS代码部署上的很多问题,常见的使用场景有以下几种:允许用户从NPM服务器下载别人编写的第三方包到本地使用。允许用户从NPM服务器下载并安装别人编写的命令行程序到本地使用。允许用户将自己编写的包或命令行程序上传到NPM服务器供别人使用。由于新版的nodejs已经集成了npm,所以之前npm也一并安装好了。同样可以通过输入 "npm -v" 来测试是否成功安装。命令如下,出现版本提示表示安装成功Npm可以理解为一个命令行工具,它的使命就是帮你为项目自动安装所依赖的开发包。composer是php包依赖管理工具,node.js包管理工具是npm。可以这么去理解。http://registry.npmjs.org是 npm 的默认的开发包仓库,在npm命令行工具里面我们可以指定使用不同的仓库。http://registry.npmjs.org是官方的仓库。其他的仓库都是定时同步官方的仓库。也就是说如果切换到其他仓库代码包有一定的延迟。但是切换成国内的镜像源包的下载速度会很快。npm安装插件过程:从http://registry.npmjs.org下载对应的插件包(该网站服务器位于国外,所以经常下载缓慢或出现异常)阿里云官方镜像站:https://developer.aliyun.com/mirror/2.查看npm源地址设置:(默认官方镜像地址)npm config get registryhttps://registry.npmjs.org/3.配置阿里巴巴镜像地址:(推荐,速度够快,同步频率快,稳定)阿里云官方镜像站:https://developer.aliyun.com/mirror/Npm阿里云地址:http://www.npmmirror.comnpm config set registry https://registry.npmmirror.com原淘宝 npm 域名即将停止解析,正如在《淘宝 NPM 镜像站喊你切换新域名啦》 中预告的那样:http://npm.taobao.org 和 http://registry.npm.taobao.org 将在 2022.06.30 号正式下线和停止 DNS 解析。域名切换规则:http://npm.taobao.org => http://npmmirror.comhttp://registry.npm.taobao.org => http://registry.npmmirror.com4.如果需要解除镜像并恢复到官方源,请执行以下命令:npm config set registry https://registry.npmjs.org
如果使用云桌面时有访问互联网的需求,您可以为工作区开通互联网访问。开通后,该工作区下的云桌面均可以访问互联网。本文介绍如何开通、管理和关闭互联网访问。如果不开启互联网访问的话,无影云桌面内不能通过浏览器上网,也不能通过互联网安装软件,所以一般情况下是需要我们开启外网访问的。在左侧导航栏,选择安全办公网络 > 互联网访问。注意下地域,和桌面在同一个地方。点击开通互联网访问,白嫖一个月。需要等创建成功,在创建成功之前无影云桌面是无法访问互联网的。等待创建成功以后我们去无影里面打开浏览器访问百度测试下。然后我们在安装一个qq玩一下:注意事项在购买流量包的时候:如果您的互联网访问包采用按使用流量计费,建议您购买无影流量包来节约流量费用。无影流量包是一种预付费的流量套餐包,可以抵扣按使用流量计费的互联网访问流量。您可以在互联网访问页面单击购买无影流量包,然后单击购买流量包,在弹出页面进行购买。相关说明如下:同一阿里云账号下的工作区可以共享使用无影流量包,抵扣中国内地(不含中国香港)地域的互联网访问流量。流量包购买后立即生效,不支持退订,购买前请预估好要使用的流量。流量包有一定的有效期,支持选择1个月、3个月、6个月或者12个月。到期后,未使用的流量将被自动清零。每个阿里云账号下最多可以存在20个未到期的无影流量包,多个流量包根据有效期可以叠加使用,先购买的流量包优先生效。购买后,在无影流量包页面,您可以单击查看流量包消费明细,跳转到资源包管理页面查看流量包的基本信息和使用明细。
1.什么是composer?Composer 是一个新的安装包管理工具,服务于 PHP 生态系统。它实际上包含了两个部分:Composer 和 Packagist。下面我们就简单说一下他们各自的用途。composer可以理解为一个命令行工具,它的使命就是帮你为项目自动安装所依赖的开发包。node.js包管理工具是npm,composer是php包依赖管理工具。Packagist是 Composer 的默认的开发包仓库,在composer命令行工具里面我们可以指定使用不同的仓库。Packagist是官方的仓库。其他的仓库都是定时同步官方的仓库。也就是说如果切换到其他仓库代码包有一定的延迟。但是切换成国内的镜像源包的下载速度会很快。阿里云官方镜像站:阿里巴巴开源镜像站-OPSX镜像站-阿里云开发者社区2.查看composer全局设置:(默认官方镜像地址) composer config -gl[repositories.packagist.org.type] composer[repositories.packagist.org.url] https://repo.packagist.org此处省略若干3.全局配置阿里巴巴镜像地址:(推荐,速度够快,同步频率快,稳定)阿里云官方镜像站:阿里巴巴开源镜像站-OPSX镜像站-阿里云开发者社区Composer阿里云文档地址:阿里云Composer镜像站-阿里云开发者社区-阿里云官网开发者社区_云计算社区composer config -g repo.packagist composer https://mirrors.aliyun.com/composer/4.全局配置国内其他镜像站:(不稳定,不推荐)composer config repo.packagist composer https://packagist.phpcomposer.com5.如果需要解除镜像并恢复到 packagist 官方源,请执行以下命令:Composer config -g --unset repos.packagist
1.比如在app内需要嵌入一个h5的商城系统,app自己有会员系统,跳转到h5页面的时候需要把会员信息传入给h5,h5判断会员是否存在,不存在注册并登录,存在直接无感登录。2.app端跳转到h5,首先app需要封装一个json里面是会员的唯一标识以及其他一些信息,然后把这个json通过aes加密,aes后的值在base64编码,base64后的值如下面的loginParams参数值,里面不会包含空格,但是会有+号。http://域名?loginParams=+uzJvHcVFgqKAB9vnf5+y4JeXwVH6cInhOA8lZAruLU4J1Yrawt02n+YYXiOhhz+9h9KQa1sl5tydJE4KoiH3+Yd8XKEKAq8KZrITF4bJMtdKkjAcjQRi7cLXO4Uckz+MXSfeAamCThTAD2i40lbIvVhcaDEnCTt9ImhxBVIasCfp4txDZxcp3XH59hleN0b/cIIzM9gvZXtcIG2BdYWTolremqFER8wvruiUnFJqbUI46gHSUlmOXEKK8aPx+YUi4ILtOjOF27VRIN4Q8A4jg/Dj6oOY8iZUX7bu+aShv6Z9TlXaapJ7gODO8UwMhFta7w+Q7Anj3Uxhr/unNkT0MBdn1J0G5pvJi+USb8IKZqSUjYdrFKQRT3LvuzRgr/ujhhFd2tesZzBBVQhtJ04LqbAQHmxfEgAlLf/azoZT23GzvOLAk8LUE/nu+ziJiZQg0r7Yb0kZ/BRORWXrh4iqdozHE4h8aozeBHuIervJ6nHSPABeSaOzaD6MRmcYWMvhQDBEq/vPQp1OtZ7SvB3dCfRZQo9SIWJu2/SURXtPnEMY30rB+rwmS6Ddt1sj1bvrxgVRhOucrhb0GnpbvGQ97NkokwJLU3+r6VIbiZzUwHVO7F2p3fHhJc4ZGaAXPSkLrH9G5K76EsF7E7ZprMdbHf1i6MVZMbkkuVqCihDVyWIkhXgp+BT5ZwO+lj/VqLyoxvBJVNLxV7BljClqWNAXGxeEwZbmlLPnmKk9hKqQk0Vl/yFjaXZr3aV5DCpp8Xt3.h5接受到loginparams参数值,然后aes解密,解密后在进行base64解码得到原始json,然后做业务逻辑处理。4.但是这里会遇到一个问题,h5在接受到loginparams参数值后发现里面的+不见了,变成了空格,以为base64编码后的字符串不会有空格,所有可以采用字符串替换方法,把所有的空格,替换成+就可以了php代码实现: $loginParams = str_replace(' ','+',$loginParams);方案2:让app方把参数传递之前先url编码,这样+号会被编码,然后php接受到的loginParams值里面就会有+号了,当然了这个需要对方配合,如果配合不了只能采用方案1。为什么参数值里面的+get接受不到呢?你记住+特殊,get传输之前要url编码才行。
1.mysql的事务可以实现,要么事务块内代码全部执行,要么全部都不执行。如果都是自己写sql实现,是完全没有任何问题的。2.如果要实现第三方支付的退款,比如微信支付订单,退款微信支付的金额,那么该怎么实现呢?代码如下 // 启动事务 Db::startTrans(); try { //订单日志 $order_log['order_sn'] = time(); $order_log['action_user'] = '前台客户'; $order_log['action_note'] = '客户取消已付款订单,返还到账户余额'; $order_log['add_time'] = time(); $order_log['merchantkeynum'] = "3333"; $order_log['clientkeynum'] = "4444"; $log_id = Db::table( 'client_order_log' )->insertGetId( $order_log ); if(!$log_id){ throw new \Exception( 'insert log失败!'); } //退款操作,如果退款失败,则回滚上面的sql $rt=onecard_refund ("AE2C976E79ABBF1C8BAFB19A3A78723A"); if($rt['sta']!=1){ throw new \Exception( '调用扣款接口失败'.$rt['msg'] ); } // 提交事务 Db::commit(); echo "成功!"; } catch ( \Exception $e ) { // 回滚事务 Db::rollback(); echo "失败!". $e->getMessage(); }3.像订单日志,修改订单状态(示例里面没有)等这些代码块放到退款处理的上面,并且可以根据条件手动抛出异常,为什么要把退款的代码块放到最后呢?原因是如果订单日志代码块失败,则直接到捕获异常里下面的代码就不执行了。如果订单日志执行成功,那么看退款的代码块,如果退款代码块手动抛出了异常,证明退款失败,则整体回滚,如果退款代码块没有异常,则全部提交sql。也就是说这种调用第三方接口的地方一定要放到代码块最底部,如果放到最顶部,会出现,退款接口成功,但是下面的自己业务逻辑代码抛出了异常,导致了所有sql的回滚!最终钱还是退款了~
1.首先看见这个24位字符串心里很慌,因为php的版本的openssl函数的aes加密密钥超过16位后面的就不参与加密了,如果是这样银行加密后的密文,php肯定无法解密。后来通过看java版本sdk才发现,这个24位字符串并不是aes的最终密钥。而是先把这个24位字符串base64解码得到一个16位的包含乱码的字符串。2.我们把这个24位字符串base64解码后的16位key,拆成16位,10进制的askii码,看下代码和运行结果。<?php$miyao = base64_decode("hnVjMkdWsINzZYbDlRWbQQ==");var_dump($miyao);$arr = array();foreach(str_split($miyao) as $v) {undefined $arr[] = sprintf("%d", ord($v));}print_r($arr);?>结果:
1.在php的世界里面mcrypt的生命肯定是已经走到了尽头,从php7.+开始已经不再支持mcrypt函数,需要使用openssl代替。但是这里有个坑,mcrypt对秘钥的长度没有限制,传入多少长度都会参加加密,但是在openssl_encrypt中。key长度只能是16长度,>16长度后,只有前16位参与加密。2.aes在php7以下用mcrypt实现代码:<?php class AES{undefined/** * This was AES-128 / CBC / PKCS5Padding * return base64_encode string * @author Terry * @param string $plaintext * @param string $key * @return string */ public static function AesEncrypt($plaintext,$key = null) {undefined $plaintext = trim($plaintext); if ($plaintext == '') return ''; $size = mcrypt_get_block_size(MCRYPT_RIJNDAEL_128, MCRYPT_MODE_CBC); //PKCS5Padding $padding = $size - strlen($plaintext) % $size; // 添加Padding $plaintext .= str_repeat(chr($padding), $padding); $module = mcrypt_module_open(MCRYPT_RIJNDAEL_128, '', MCRYPT_MODE_CBC, ''); $key=self::substr($key, 0, mcrypt_enc_get_key_size($module)); //var_dump($key);die; $iv = str_repeat("\0", $size); //java里面的16个空数组对应的是\0. //var_dump($iv);die; //echo $key;die; /* Intialize encryption */ mcrypt_generic_init($module, $key, $iv); /* Encrypt data */ $encrypted = mcrypt_generic($module, $plaintext); /* Terminate encryption handler */ mcrypt_generic_deinit($module); mcrypt_module_close($module); return base64_encode($encrypted); } /** * Returns the length of the given string. * If available uses the multibyte string function mb_strlen. * @param string $string the string being measured for length * @return integer the length of the string */ private static function strlen($string) {undefined return extension_loaded('mbstring') ? mb_strlen($string,'8bit') : strlen($string); } /** * Returns the portion of string specified by the start and length parameters. * If available uses the multibyte string function mb_substr * @param string $string the input string. Must be one character or longer. * @param integer $start the starting position * @param integer $length the desired portion length * @return string the extracted part of string, or FALSE on failure or an empty string. */ private static function substr($string,$start,$length) {undefined return extension_loaded('mbstring') ? mb_substr($string,$start,$length,'8bit') : substr($string,$start,$length); } /** * This was AES-128 / CBC / PKCS5Padding * @author Terry * @param string $encrypted base64_encode encrypted string * @param string $key * @throws CException * @return string */ public static function AesDecrypt($encrypted, $key = null) {undefined if ($encrypted == '') return ''; $ciphertext_dec = base64_decode($encrypted); $module = mcrypt_module_open(MCRYPT_RIJNDAEL_128, '', MCRYPT_MODE_CBC, ''); $key=self::substr($key, 0, mcrypt_enc_get_key_size($module)); $iv = str_repeat("\0", 16); //解密的初始化向量要和加密时一样。 /* Initialize encryption module for decryption */ mcrypt_generic_init($module, $key, $iv); /* Decrypt encrypted string */ $decrypted = mdecrypt_generic($module, $ciphertext_dec); /* Terminate decryption handle and close module */ mcrypt_generic_deinit($module); mcrypt_module_close($module); $a = rtrim($decrypted,"\0"); return rtrim($decrypted,"\0"); }} $plain="111111"; echo "加密后:".$miwen=AES::AesEncrypt($plain,'mkeymkeymkeymkey'); echo "<br/>"; echo "解密后:".AES::AesDecrypt($miwen,'mkeymkeymkeymkey');?>运行结果:加密后:CEBqq9fGmsu4G87+MA2BWg== 解密后:1111113.php7.+以上openssl实现aes加密代码实现:<?phpclass Aes {undefined public $key = ''; public $iv = ''; public function __construct( $config ) {undefined foreach ( $config as $k => $v ) {undefined $this->$k = $v; } } //加密 public function aesEn( $data ) {undefined return base64_encode( openssl_encrypt( $data, $this->method, $this->key,OPENSSL_RAW_DATA, $this->iv ) ); } //解密 public function aesDe( $data ) {undefined return openssl_decrypt( base64_decode( $data ), $this->method, $this->key,OPENSSL_RAW_DATA, $this->iv ); }}$config = [ 'key' => 'mkeymkeymkeymkey', //加密key 'iv' => str_repeat("\0", 16),//str_repeat("\0", 16)保证偏移量为16位,这里是16位空字符串,也可以和key一样16字符串,还可以是变化的,比如md5成16位原文,substr(md5("haha"),8,16),变化的需要保证同一个字符串加解密的iv保持一致。 'method' => 'AES-128-CBC' //加密方式 # AES-256-CBC等];//openssl_encrypt的第四个参数为1或者OPENSSL_RAW_DATA时填充方式为pks5padding或者pks7padding的结果一样,其他待验证$obj = new Aes( $config );//加密数据$miwen = $obj->aesEn( '111111' );echo "加密后:".$miwen;echo "<br/>";echo "解密后:".$obj->aesDe( $miwen );?>运行结果:加密后:CEBqq9fGmsu4G87+MA2BWg==解密后:1111114.结果发现低版本的php的mcrypt和高版本php的openssl,在秘钥都是16位的情况下,加密和解密的结果是一致的。5.给mcrypt和openssl的秘钥都增加一位x看下结果:mcrpyt结果:加密后:6TyYm0gpdVOrEFDOXSy+bA==解密后:111111openssl结果:加密后:CEBqq9fGmsu4G87+MA2BWg==解密后:111111 6.总结密钥长度增加一个x,变成17位后,mcrpyt加密后的值发生了变化,但是可以通过相同的密钥解密,openssl加密的值没有发生变化,说明增加到17位的x,根本没有参与加密。7.如果php程序由低版本mcrypt的aes加密升级到高版本的openssl的aes加密,在密钥不是16位的情况下,就会出现悲剧的情况。还有就是和一些其他语言接口对接,如果别的语言key不是16位,那么他加密后的串,用openssl无法解密。
1.微信小程序带参跳转h5代码.jsPage( { data :{ openid : ' 123456789x ' }, onLoad ( options ){ var openid = this . data . openid ; this . setData( { src : ' https://www.wlphp.com/test/test.php?openid= ' + openid }) }}).wxml< web-view src =" {{src}} "></ web-view >2.h5页面php代码.php <?php print_r($_GET);?><meta name="viewport" content="width=device-width, initial-scale=1.0,maximum-scale=1.0, user-scalable=no"/><input type="button" value="返回到小程序toolbar" οnclick="goback()"><script type="text/javascript" src="https://res.wx.qq.com/open/js/jweixin-1.3.2.js"></script><script> function goback(){ wx.miniProgram.reLaunch({url: '/pages/sorts/sorts'}); }</script>如果要跳转非toolbar页面可以用 wx.miniProgram.navigateTo如果带参数可以在url的值后面?id=1传参到小程序关于小程序内嵌h5支付问题,h5支付不支持,jsapi待研究,native扫码支付可以在h5显示出来二维码,但是无法在页面长按识别二维码支付(不知道后续是否会支持),可以采用带参数跳转到小程序原生页面,然后调用小程序支付,然后支付完毕再带参数跳转到h5页面的方案。微信官方的web-view说明文档地址:https://developers.weixin.qq.com/miniprogram/dev/component/web-view.html
案例:当页面上含有href属性的a标签被点击的时候,增加加载动画效果。$(function (){ $("a[href]").click(function (){ //loading带文字 layer.open({ type: 2 ,content: '加载中,请稍后' }); }); });
//rsa私钥 $pkey="MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCeZ1SzplMWFgkZC0R3fmYeMfAkW+R5gr2adOhnu37pGLwV/59w9LUl4x2NSBXyTCTT/WuCyynVaJ6gaUd5puFZLNBgvq0OTvIGt/rRt5DFkgXoKrjZaTPAxXchVbivMvDPLMzmI9OSlrUuSJ52E657mxM6acR+TRyngEzYy86zfC/OHurM4+vOlC7nt5U3SLR0aN659DrTBOxLhZitGiwF3Vvs/pEMydmHzIu3FRSnJVnQb111Xu+2WgVai9bvvvGFGUSpkwq+M7eAneCHxMA8Eq0oN4Y+oKECtac58XDoIVbA3GyAgWfDcmlqjNcgaw/uj2kDYYkruJ/KiafCnhGFAgMBAAECggEAEAyIGGuRn3s8EL2DglfcINMOcaCd1XHZo/yUT24/vX40EEaF8PvaHqyUIexbzODKGnBipC3RXz3qVHZWN40g59gShLrM7dYxz1x0nSrsEBhrYMpzUBPImo9nMols3NNccqEnYWnf1cpPMRFKfMwhsYRUvmZY8jL95DqrjouXtbeGacs/K4B4niqU4nD2NCO+FoeRvwjY2GW0X2qm3s2YeHIOtyZkh3FA+UQy7yhRY0rsbCl7JzOyMBDfM86RO1geblvErzi0a+WnVCvPGSc2mAciQTSjyOQATVC/NjhhfUP9s8wv0rcXwhkou6CpWBkXudKlssarlDbmh0U8gowuAQKBgQDb33+JQXG3qFJMm5gRK9jgODHMzxVLWMrH5p8/YMm/fEtrA2KBJnmvq1YzbzjPGBbN86xXTGW2pLeC2QR2jhUHEVW3f1XLWp9zR6eNer73UJBNeDa2UB7ZnySudbFzQJLq8qQb63rqgm5i82WdHRi4y7PwmVpMi7x3l9/ESnWImQKBgQC4bkAJsHjUlmdlMCOAJZQjakMWmQkyDRj0sqmnRpcVU38osUcZ2l6k1D5bPFKwcjTNmA/ZuCGS/IsY11wAYol2OzxXEuYg/CyCJbflDqE+xVnhhmhA6eOBodRYHMlBStCetIwRtDgNPyE3oVraD5CmmYNG2w4V9aJMC/bZ6o+HzQKBgQC1eA9A71y14hya0ch+U5wZ4wdnLHs8xroGptdEbdPVUPbGCfMg+OpyDI2GwgM3GYCo1ralDo7cYbJ8MjU2zCmavbnrSxM6VT9I3Bjz8hzu7fVLmy485n+vyWNzAfxFTUR02XKNAJtgNep5jL2PINaonqeSql8W0jXo1tZ+oZ3D8QKBgHXoNhR7t6PhS9TLA8cnPx+tDZri15gh+3RkzEVewbQmn0K6o0QJ8TvbXMojNJINUWim8Gs2ZnB798k31V+Bn3PwxtC8ClfIIARMbNaJldh1ftLJhNkLLiFv/jsenCOb2ue7cVjYKIV/eJMCGv0Rul5oAh9IaCbt1IZdl8EVThF5AoGAU7zQKlbJHA4bPMqu970hhDGdk47UQPBlLvT54eyF1hqR7irFozJRwvZfh9j14SyRzfc3MwojKRrN3Dqd65v18k7d12R4HiJf6+MUomcGwcKoQJitaD2rY0IKc5pv/2jnlwuGL2DZhgmgRMQGSHlsTOv6LVFY6jH16ztc65rKT2o="; echo $private_key = "-----BEGIN PRIVATE KEY-----\n" . wordwrap($pkey, 64, "\n", true) . "\n-----END PRIVATE KEY-----"; echo "<br/>"; echo $private_key1="-----BEGIN RSA PRIVATE KEY-----\n".$pkey."\n-----END RSA PRIVATE KEY-----";die;运行后的结果:总结:私钥的中间文本内容部分php的openssl_verify可以接受一行的或者多行的,但是头部还有尾部的BEGIN RSA PRIVATE KEY和 END RSA PRIVATE KEY必须得有,但是里面的RSA可以有可以没有。头部和尾部也必须保持5个-加字母加空格加字母最后再加5个-
1.都是使用openssl_sign,和openssl_verify函数,只是最后一个参数不同rsa为OPENSSL_ALGO_SHA1,rsa2为OPENSSL_ALGO_SHA256,下面是PHP本rsa签名实现类<?php //rsa和rsa2 PHP版签名生成以及验证类 //$content,$signature,$publicKey,$type,$content原文,$signature被验证的签名,$publicKey和$privateKey公私钥都是只有内容的一行字符串,$type为rsa,或者rsa2 class RSA{ public static function sign($content,$privateKey,$type){ if($type == "rsa"){ openssl_sign($content,$signature,"-----BEGIN PRIVATE KEY-----\n".$privateKey."\n-----END PRIVATE KEY-----", OPENSSL_ALGO_SHA1); }elseif ($type == "rsa2") { openssl_sign($content,$signature,"-----BEGIN PRIVATE KEY-----\n".$privateKey."\n-----END PRIVATE KEY-----", OPENSSL_ALGO_SHA256); }else{ throw new Exception("Only support OPENSSL_ALGO_SHA1 or OPENSSL_ALGO_SHA256 algorithm signature!"); } return base64_encode($signature); } public static function verify($content,$signature,$publicKey,$type){ if($type == "rsa"){ return openssl_verify($content,base64_decode($signature),"-----BEGIN PUBLIC KEY-----\n".$publicKey."\n-----END PUBLIC KEY-----", OPENSSL_ALGO_SHA1); }elseif ($type == "rsa2") { return openssl_verify($content,base64_decode($signature),"-----BEGIN PUBLIC KEY-----\n".$publicKey."\n-----END PUBLIC KEY-----", OPENSSL_ALGO_SHA256); }else{ throw new Exception("Only support OPENSSL_ALGO_SHA1 or OPENSSL_ALGO_SHA256 algorithm signature verify!"); } } } ?>
1.mcrypt 函数php的7以上已经废弃该方法,所以这里的aes是基于openssl_encrypt实现。代码如下 class Aes { public $key = ''; public $iv = ''; public function __construct( $config ) { foreach ( $config as $k => $v ) { $this->$k = $v; } } //加密 public function aesEn( $data ) { return base64_encode( openssl_encrypt( $data, $this->method, $this->key, OPENSSL_RAW_DATA, $this->iv ) ); } //解密 public function aesDe( $data ) { return openssl_decrypt( base64_decode( $data ), $this->method, $this->key, OPENSSL_RAW_DATA, $this->iv ); } } $config = [ 'key' => 'mkeymkeymkeymkey', //加密key 'iv' => str_repeat("\0", 16),//保证偏移量为16位,这里是16位空字符串,也可以和key一样16字符串,还可以是变化的,比如md5成16位原文,substr(md5("haha"),8,16),变化的需要保证同一个字符串加解密的iv保持一致。 'method' => 'AES-128-CBC' //加密方式 # AES-256-CBC等 ]; //openssl_encrypt的第四个参数为1或者OPENSSL_RAW_DATA时填充方式为pks5padding或者pks7padding的结果一样,其他参数值待验证 $obj = new Aes( $config ); //加密数据 $res = $obj->aesEn( 'haha' ); echo $res; echo '<hr>'; //解密 echo $obj->aesDe( $res );
1.对方提供的东西:test-pfx.pfx 密码:000000 xdzf_cfca_prd.cerxdzf_cfca_prd.cer这是一个公钥文件,可以通过notepad++打开,看见公钥内容,但是pfx文件无法直接打开,而且pfx文件还有一个密码,于是想把pfx文件里面的内容获取到。2.使用php获取pfx里面的内容,打印出来,实验证明php是可以直接使用pfx文件的,没必要把pfx拆出pem在使用。 //路径 $filePath = './test-pfx.pfx'; $pkcs12 = file_get_contents( $filePath ); //000000为pfx的密码 if ( openssl_pkcs12_read( $pkcs12, $certs, '000000' ) ) { print_r($certs); } die;运行结果:发现里面是一对公私钥。3.于是才弄明白了整体对接的流程。php获取pfx里面私钥,然后把数据通过私钥签名发送给java,java通过和我这边一对的公钥验证(也就是pfx里面的公钥)签名,然后给php返回数据,java那边也有一对公钥和私钥,提供给php的只是xdzf_cfca_prd.cer公钥部分。也就是java通过他的私钥签名,然后返回给php,php利用xdzf_cfca_prd.cer公钥验证签名。也就是整个流程有两对公钥私钥。php用到了第一对pfx里面的私钥和第二对里面的公钥。4.具体php代码实现https://www.wlphp.com/?post=263
程序在开发测试阶段开启日志有利于发现问题,并且解决问题,那么如果部署到生成环境还开起日志记录就会产生大量的垃圾文件占用大量的硬盘空间。所以就需要我们关闭日志记录功能。application/config.php 中知道log的配置如下: 'log' => [ // 日志记录方式,内置 file socket 支持扩展 'type' => 'test', //如果改为test则不记录日志,即可不写入任何日志。第二种方式是后面会提到的设置日志记录级别 File,test // 日志保存目录 'path' => LOG_PATH, // 日志记录级别 'level' => [], //log 常规日志,用于记录日志error 错误,一般会导致程序的终止notice 警告,程序可以运行但是还不够完美的错误info 信息,程序输出信息debug 调试,用于调试信息sql SQL语句,用于SQL记录,只在数据库的调试模式开启时有效 ],
//处理参数 function MakeSign_Params( $arr ) { ksort( $arr ); //按字典序排序参数 $buff = ''; foreach ( $arr as $k => $v ) { if ( $k != 'signValue' ) { $buff .= $k . '=' . $v . '&'; } } $buff = trim( $buff, '&' ); return $buff; } //生成 sha256WithRSA 签名 function getSign( $content ) { $filePath = 'test-pfx.pfx'; if ( !file_exists( $filePath ) ) { return false; } $pkcs12 = file_get_contents( $filePath ); if ( openssl_pkcs12_read( $pkcs12, $certs, '000000' ) ) { $pkey = $certs['pkey']; $pi_key = openssl_pkey_get_private($pkey);//这个函数可用来判断私钥是否是可用的,可用返回资源id Resource id,别名函数openssl_get_privatekey if(!$pi_key){ //var_dump($pi_key); return false; } //print_r($certs); 可以查看里面是啥 //根据实际情况键值可能不同 //最后一个参数签名算法还有其他值OPENSSL_ALGO_SHA1 openssl_sign( $content, $signature, $pkey, 'SHA256' ); openssl_free_key($pi_key); $sign = base64_encode( $signature ); return $sign; } } //验证 sha256WithRSA 签名 function verify( $content, $sign ) { $publicKey = file_get_contents( 'xdzf_cfca_prd.cer' ); //验证接口方传过来数据用使用这个公钥,使用这个。 $pu_key = openssl_pkey_get_public($publicKey);//这个函数可用来判断公钥是否是可用的,可用返回资源id Resource id if(!$pu_key){ //var_dump( $pu_key); return false; } $key = openssl_get_publickey( $publicKey ); //返回资源型Resource id #9,和上面函数openssl_pkey_get_public,效果一致 $ok = openssl_verify( $content, base64_decode($sign) , $key, 'SHA256' ); openssl_free_key( $key); return $ok; }
在订单系统中订单号不能重复,那么怎么才能高效的生成不会重复的订单号呢?通过程序生成的订单号为了保证不重复要么就是很长,要么就是一堆没有什么实际意义的长字符串,而且为了保证不重复还得在数据库表字段做唯一约束很不友好,所以通过sql数据库存储过程生成唯一订单号,而且还能有一定规则的方法就很优雅。第一步需要一个数据库表现导入进去:CREATE TABLE `generate_serialno` ( `id` int(11) NOT NULL, `ordervalue` varchar(255) CHARACTER SET utf8 COLLATE utf8_unicode_ci NULL DEFAULT NULL, `ordertime` datetime(0) NULL DEFAULT NULL, PRIMARY KEY (`id`) USING BTREE) ENGINE = MyISAM CHARACTER SET = utf8 COLLATE = utf8_unicode_ci ROW_FORMAT = Dynamic;第二部创建存储过程sql执行下:CREATE DEFINER=`root`@`localhost` PROCEDURE `generate_orderNo`( IN orderNamePre CHAR(4), IN num INT, OUT newOrderNo VARCHAR (25))BEGIN -- 订单变化的值 DECLARE orderNameValue INT ; -- 更新行数 DECLARE updateRow INT ; -- 当前日期,有可能包含时分秒 DECLARE currentTime DATETIME ; -- 订单号 DECLARE orderCode VARCHAR (64) ; DECLARE orderNum INT DEFAULT 5; -- 订单变化的值 -- 异常处理 DECLARE CONTINUE HANDLER FOR 1062 SET currentTime = NOW() ; -- 获得订单号 SELECT IFNULL(gs.ordervalue, 0) INTO orderNameValue FROM generate_serialno gs WHERE id = 1 ; SET currentTime = NOW() ; -- 打开自动提交 SET autocommit = 1 ; IF TRUE -- 如果true插入一条数据 THEN INSERT INTO generate_serialno (id, ordervalue, ordertime) VALUES (1, 1, currentTime) ; END IF ; -- 否则更改订单号 UPDATE generate_serialno obj SET obj.ordervalue = CASE -- 订单变化的值如果今天大于昨天从1开始,否则再原来的基础上加1 WHEN TO_DAYS(currentTime) > TO_DAYS(obj.ordertime) THEN 1 ELSE orderNameValue + 1 END, obj.ordertime = currentTime WHERE ( -- 订单变化的值 obj.id = 1 AND obj.ordervalue = orderNameValue ) ; SET updateRow = ROW_COUNT() ; WHILE ! updateRow = 1 DO -- 如果更改的行数不等于1 SELECT -- 获得当前的订单变化值 IFNULL(gs.ordervalue, 0) INTO orderNameValue FROM generate_serialno gs WHERE id = 1 ; UPDATE generate_serialno obj SET obj.ordervalue = CASE WHEN TO_DAYS(currentTime) > TO_DAYS(obj.ordertime) THEN 1 ELSE orderNameValue + 1 END, obj.ordertime = currentTime WHERE (-- 只有订单变化值和id都相等的情况下才能更改,更具行锁的机制 obj.id = 1 AND obj.ordervalue = orderNameValue -- 注意!!!! ) ; SET updateRow = ROW_COUNT() ; END WHILE ; IF num = 8 THEN -- 根据年月日生成订单编号,订单编号形式:前缀+年月日+流水号,如:SH2013011000002 SELECT CONCAT( orderNamePre, DATE_FORMAT(currentTime, '%Y%m%d'), LPAD(orderNameValue, orderNum, '0') ) INTO orderCode ; ELSEIF num = 14 THEN -- 根据年月日时分秒生成订单编号,订单编号形式:前缀+年月日时分秒+流水号,如:SH2013011010050700001,个人不推荐使用这种方法生成流水号 SELECT CONCAT( orderNamePre, DATE_FORMAT(currentTime, '%Y%m%d%H%i%s'), LPAD(orderNameValue, orderNum, '0') ) INTO orderCode ; ELSE -- 根据年月日时分生成订单编号,订单形式:前缀+年月日时分+流水号,如:SH20130110100900005 SELECT CONCAT( orderNamePre, DATE_FORMAT(currentTime, '%Y%m%d%H%i'), LPAD(orderNameValue, orderNum, '0') ) INTO orderCode ; END IF ; SELECT orderCode ;END第三部程序调用存储过程生成唯一订单号:(thinkphp5调用) $arr = Db:: query( "call generate_orderNo('D',14,@)"); return $arr[ '0'][ '0'][ 'orderCode'];最终效果的
第一步导入数据库表:CREATE TABLE `test_use` ( `id` int(11) NOT NULL, `name` varchar(255) CHARACTER SET utf8 COLLATE utf8_unicode_ci NULL DEFAULT NULL, `sex` varchar(255) CHARACTER SET utf8 COLLATE utf8_unicode_ci NULL DEFAULT NULL, `age` int(11) NULL DEFAULT NULL, PRIMARY KEY (`id`) USING BTREE, UNIQUE INDEX `name`(`name`) USING BTREE) ENGINE = MyISAM CHARACTER SET = utf8 COLLATE = utf8_unicode_ci ROW_FORMAT = Dynamic;第二步创建存储过程:DELIMITER $$CREATE PROCEDURE `test_user`.`test_insert`(IN args INT) BEGIN DECLARE i INT DEFAULT 1; START TRANSACTION; WHILE i <=args DO INSERT INTO test_use(id,NAME,sex,age) VALUE(i,CONCAT("小王-",i),'男',22); SET i=i+1; END WHILE; COMMIT; END$$DELIMITER ;第三步调用生成插入1000万条记录:最后执行:call test_insert(10000000); 即可需要执行一段时间具体看电脑的性能而定最终的查询生成的条数:
直接在cmd运行.bat里面的命令可以运行,但是双击.bat就是一闪而过,那么为什么会出现这样的问题呢,一般情况是.bat里面的命令,在双击运行的环境下出现了问题导致的。所以想办法看见错误信息就能根据具体的错误解决问题了。./frpc.exe -c ./frpc.inipause在.bat文件最后一行增加pause,表示暂停,就可以看到命令行中的报错信息了根据错误信息做相应的修改就能解决问题了。这个案例是win10下,不要增加./命令,会把.当成命令导致的错误,因此去掉./就行了。
1.首先把content目录迁移到阿里云oss的bucket里面,然后给bucket绑定一个自己的域名,得到一个bucket域名。例如我的是https://blog-static.wlphp.com/ 把content目录放到bucket的根目录即可,打个比方访问里面的某个css,https://blog-static.wlphp.com/content/templates/FLY/css/font-awesome.min.css2.找到init.php里面的TEMPLATE_URL,把他的值改成https://blog-static.wlphp.com/content/templates/FLY/3.找到模板文件夹inc里面的head.php,把pjax_theme的js变量值重置成本地的路径。4.找到模板文件夹下面的module里面的函数get_template_name 让他返回固定值即可。这样网站除了文章里面的图片,其他的静态资源全部迁移到阿里云的oss,网站加载速度有了明显的提升。阿里云oss在流量和存贮不大的情况下是基本不会产生费用的。
1.打开浏览器f12,console里面提示的是跨域请求错误。2.登录阿里云控制台,找到oss控制面板,如下图找到跨域设置。3.创建跨域规则。允许所有来源的所有请求的所有headers。
PHP5.5 以后官方自带了一个组件叫 Zend Opcache,具体可以参看官方文档(https://www.php.net/manual/zh/book.opcache.php)。开启此扩展后可以提高 PHP 的执行效率。Opcache是字节码缓存,PHP在被编译的时候,首先会把php代码转换为字节码,字节码然后被执行。php文件第二次执行时,同样还是会重新转换为字节码,但是很多时候,文件内容几乎是一样的,比如静态HTML文件,生成后内容许久都不会改变,用户访问请求直接由服务器读取响应给客户端浏览器。都不用经过PHP进行解析构建了。内存中的字节码数据,可以直接缓存进行二次编译。这样程序就会快一些,cpu的消耗也少了。(这里字节码 就是 opcode)在配置中有 2 个配置很重要,需要重点关注opcache.validate_timestamps=0;opcache.revalidate_freq=60;validate_timestamps 用于验证是否要重新生成缓存脚本, 如果设置为 0(性能最佳),需要手动在每次 PHP 代码更改后手动清除 OPcache。 如果此值为 0,那么 revalidate_freq 将失去作用。revalidate_freq 用于控制 opcache 多久生成一次缓存字节码,默认 60s。所以一般我们在开发环境中将上面两个值配置为opcache.validate_timestamps=1;opcache.revalidate_freq=1;或者干脆直接关闭 opcache。上面提到了,如果将 validate_timestamps 配置为 0 以后,我们每次部署 PHP 的时候默认是不会自动生成缓存。生产环境解决方案1服务器安装宝塔面板webhook,代码提交到码云,然后触发码云的钩子,码云钩子在触发宝塔webhook,宝塔webhook通过执行shell脚本,拉取仓库代码,然后在重启对应php版本的php-fpm#!/bin/shWEB=/www/wwwroot/youpinhui.guanhuaitong.com/ cd $WEB && /usr/bin/svn update --username 613154514@qq.com --password 123456 --no-auth-cachekill -USR2 `cat /www/server/php/73/var/run/php-fpm.pid`方案2利用php函数opcache_reset(),大概实现方案就是写个接口,然后在代码更新完毕之后,调用这个接口,接口去实现opcache_reset()这个方法即可。推荐一个opcache单页面面板,只有一个页面放到web站点根目录就可以访问了,生产环境建议自己增加访问权限。https://github.com/rlerdorf/opcache-status
php 5.3.3 以后的php-fpm 不再支持 php-fpm(start|stop|reload)等命令,所以不在介绍旧的方法,需要使用新的信号控制:INT, TERM 立刻终止QUIT 平滑终止USR1 重新打开日志文件USR2 平滑重载所有worker进程并重新载入配置和二进制模块现在看下如何重启:1.先查看php-fpm的master进程号[root@iZ2ze1o1f17iu7osdy7bukZ ~]# ps aux | grep php-fpm |grep masterroot 84659 0.0 0.2 129476 8216 ? Ss Feb19 0:12 php-fpm: master process (/www/server/php/56/etc/php-fpm.conf)root 84676 0.0 0.2 129476 8208 ? Ss Feb19 0:12 php-fpm: master process (/www/server/php/56/etc/php-fpm.conf)root 125290 0.0 0.2 126836 9528 ? Ss Feb20 0:11 php-fpm: master process (/www/server/php/73/etc/php-fpm.conf)root 526126 0.0 0.3 275668 12116 ? Ss 21:27 0:00 php-fpm: master process (/www/server/php/73/etc/php-fpm.conf)2.从上面找到php7.3的配置文件查看里面pid路径[root@iZ2ze1o1f17iu7osdy7bukZ ~]# cat /www/server/php/73/etc/php-fpm.conf[global]pid = /www/server/php/73/var/run/php-fpm.piderror_log = /www/server/php/73/var/log/php-fpm.loglog_level = notice[www]listen = /tmp/php-cgi-73.socklisten.backlog = 8192listen.allowed_clients = 127.0.0.1listen.owner = wwwlisten.group = wwwlisten.mode = 0666user = wwwgroup = wwwpm = dynamicpm.status_path = /phpfpm_73_statuspm.max_children = 80pm.start_servers = 5pm.min_spare_servers = 5pm.max_spare_servers = 20request_terminate_timeout = 100request_slowlog_timeout = 30slowlog = var/log/slow.log3.查看php7.3的php-fpm的pid[root@iZ2ze1o1f17iu7osdy7bukZ ~]# cat /www/server/php/73/var/run/php-fpm.pid5268994.重启7.3的php-fpmkill -USR2 5268995.整合到一起重启php7.3的php-fpm,注意下面不是单引号,是反引号重启kill -USR2 `cat /www/server/php/73/var/run/php-fpm.pid`停止kill -INT `cat /www/server/php/73/var/run/php-fpm.pid`
自从thinkphp5发布以来,可以说越来越优雅了,竟然支持.env配置文件啦!想必熟悉那个以优雅著称的某框架的同学一定不陌生。支持多种配置格式thinkphp5支持的配置类型包括.ini、.xml、.json 、.yaml和 .php。那么我们的.env就是采用ini方式的配置格式。用于在开发过程中模拟环境变量配置(该文件建议在服务器部署的时候忽略)。配置.env在项目根目录(不是application目录!!!)创建.env文件,配置格式例如:APP_DEBUG = trueAPP_TRACE = true[database]hostname = 127.0.0.1hostport = 3306username = rootpassword = 123456database = youpinhui[rediscache]prefix = fl_expire = 7200host = 127.0.0.1port = 6379password = 905507[redissession]prefix = zhensoftexpire = 3600*4host = 127.0.0.1port = 6379password = 905507这样我们就可以使用Env类来读取配置:获取环境变量 如果不存在则使用默认值rootEnv::get('database.username','root');当然你也可以使用助手函数:env('database.username', 'root')环境变量的获取不区分大小写环境变量中设置的APP_DEBUG和APP_TRACE参数会自动生效(优先于应用的配置文件),其它参数则必须通过Env::get方法才能读取。使用.env里的配置在config/database.php中:use \think\Env;return [ // …… // 服务器地址 'hostname' => env('database.hostname', '127.0.0.1'), // 数据库名 'database' => env('database.database', 'youpinhui'), // 用户名 'username' => env('database.username', 'root'), // 密码 'password' => env('database.password', '123456'), // 端口 'hostport' => env('database.hostport', '3306'),];其他配置,同样可以这样配置和使用。最后这样本地的开发环境获取.env的链接字符串,在部署线上生产环境的时候去掉.env,比如在使用svn提交到仓库的时候吧.env设置忽略,这样就能很优雅的发布代码到生产环境了。有了.env 文件,再也不用每次去改config里的配置啦!
驱动下载地址:http://windows.php.net/downloads/pecl/releases/igbinary (在序列化和反序列化的效率上高于其自带的)可以不添加http://windows.php.net/downloads/pecl/snaps/redis 可以看到有很多版本,这时需要根据自己的情况进行选择,如果选择不对版本基本不会成功。可以通过phpinfo()查看自己的php信息如下图:一定要注意下载驱动版本和phpinfo里面的cpu架构,vc版本,ts还是nts下载下来之后解压,然后将php_igbinary.dll,php_redis.dll拷贝至php的ext目录下修改php.ini,在该文件中加入:;php_redisextension=php_igbinary.dllextension=php_redis.dll注意:网上有人说 extension=php_igbinary.dll一定要放在extension=php_redis.dll的前面,否则此扩展不会生效 extension=php_igbinary.dll,这个扩展可以不用加,所以不一定在extension=php_redis.dll前面了。重启apache后再次查看phpinfo,看到有redis的信息表明扩展安装成功了
gitee又叫码云是和github类似的国内代码托管平台,之所有选择码云也是因为它在国内速度比较快,地址:https://gitee.com/宝塔是一款用python编写的服务器面板说白了就是先在服务器安装宝塔,然后通过宝塔管理服务器,同时给宝塔做个广告,宝塔是目前服务器面板里面做的最好的一款,没有之一,注册地址:宝塔服务器面板,一键全能部署及管理,送你3188元礼包,点我领取https://www.bt.cn/?invite_code=MV93dmxhb20=宝塔有一个免费的webhook的插件,大概意思就是对外提供一个http的接口,供外部调用从而在服务器执行shell脚本,我们恰巧利用的是码云的webhook,也就是本地代码提交到码云后,触发码云的webhook,码云的webhook又去触发宝塔的webhook从而可以在服务器执行脚本,通过脚本我们就能拉取仓库代码到我们的web站点了。1.首先在码云创建仓库,然后开启svn,我们就能找到svn的仓库地址了。 svn://gitee.com/zhensoft/zhensoft2.在本地开发环境首次先checkout仓库到本地完成本地初始化。会提示输入仓库地址,账号和密码,账号和密码是码云的账号和密码 。(本地需要安装svn客户端)3.在服务器web站点根目录执行svn checkout svn://gitee.com/zhensoft/zhensoft ./ ,完成站点内容初始化,需要安装svn客户端。4.在宝塔后台找到webhook插件,添加一个webhook,获取到一个http请求的地址,例如 http://123.56.147.134:9011/hook?access_key=5eIgW8ClChxrQ0oCvxbWou687A1HAXnoi9ryOUmCl0YMZHRq&param=aaa ,然后编辑脚本里面写如下shell代码,#!/bin/shWEB=/www/wwwroot/huodong.quanmindian.com/ cd $WEB && /usr/bin/svn update --username 613154514@qq.com --password 123456 --no-auth-cacheWEB是web站点的根目录,username是码云账号,password是码云的密码,这段代码意思就是一旦触发宝塔的这个webhook就会执行这段shell脚本,这段shell脚本意思就是进入web站点根目录,然后执行svn的update命令,把仓库的代码更新过来5.配置码云的webhook6.测试提交
实验场景:页面被客户访问发送邮件通知到我的邮箱,该场景只是为了测试,下单发送邮件或者短信的场景都是一样的,为了体现出来队列的优越性,我自己封装了个邮件发送的接口,接口内部实现增加了sleep(5),纯粹是为了给接口增加耗时,更好的达标实验效果。workerman的redis-queue地址:https://github.com/walkor/redis-queue自己封装的邮件发送接口地址:http://phpmailer.wlphp.com/sendmail_api.php?html=%E5%86%85%E5%AE%B9&recipient=613154514@qq.com&subject=%E7%89%B9%E5%88%AB%E6%8F%90%E7%A4%BA如果在页面里面直接调用发送邮件接口的话,页面需要等待5秒才能加载出来。代码实现如下 //直接调用, 在接口方故意模拟sleep( 5 ), 下面就会等5秒,上面就不用等直接扔给队列执行 $html = '被访问了下' . date ( 'Y-m-d H:i:s' ); $apiurl = 'http://phpmailer.wlphp.com/sendmail_api.php?html=' . urlencode ( $html ) . '&recipient=613154514@qq.com&subject=workerman的redis消息队列消息' ; echo file_get_contents ( $apiurl );消息队列实现:该页面在是thinkphp5的mvc框架里面首先在核心函数库放入如下函数://有时候一些项目运行在apache或者php-fpm环境,无法使用workerman/redis-queue项目,可以参考如下函数实现发送function redis_queue_send ( $redis , $queue , $data , $delay = 0 ) { $queue_waiting = 'redis-queue-waiting' ; $queue_delay = 'redis-queue-delayed' ; $now = time (); $package_str = json_encode ([ 'id' => rand (), 'time' => $now , 'delay' => 0 , 'attempts' => 0 , 'queue' => $queue , 'data' => $data ]); if ( $delay ) { return $redis -> zAdd ( $queue_delay , $now + $delay , $package_str ); } return $redis -> lPush ( $queue_waiting . $queue , $package_str );}在页面控制器对应方法里面调用这个函数把要发送邮件信息添加到消息队列里面实现代码如下: //基于redis的消息队列服务 $html = '被访问了下' . date ( 'Y-m-d H:i:s' ); $redis = new \ Redis ; $redis -> connect ( '127.0.0.1' , 6379 ); $queue = 'user-1' ; $data = [ date ( 'Y-m-d H:i:s' ), $html ]; redis_queue_send ( $redis , $queue , $data );mvc框架里面只需要把消息仍进队列,不管邮件是否发送成功,所以不会造成阻塞,页面加载速度基本不受影响。页面多刷新几次然后打开redis能够看见如下队列:启动基于wokerman的消费者端:注意生产者在mvc框架里面,随时把消息添加到队列。消费者端基于cli命令行方式启动。php index.php start启动之后如下:消息被依次消费发送邮件<?phprequire __DIR__ . '/vendor/autoload.php';use Workerman\Worker;use Workerman\Lib\Timer;use Workerman\RedisQueue\Client;$worker = new Worker();$worker->onWorkerStart = function () {undefined $client = new Client('redis://127.0.0.1:6379'); $client->subscribe('user-1', function($data){undefined echo "user-1\n"; var_export($data); //调用发送邮件接口 $html=$data[1]; $apiurl="http://phpmailer.wlphp.com/sendmail_api.php?html=".urlencode($html)."&recipient=613154514@qq.com&subject=workerman的redis消息队列消息"; echo file_get_contents($apiurl); });};Worker::runAll();
在写入完成后,不必急于拔出SD卡,现在在电脑中可以看到一个名字为boot的分区(此分区为FAT32格式,并且容量会很小,这是正常的,在SD卡插入树莓派后,这个boot分区就是系统的/boot文件夹)1.开启SSH现在的树莓派都是默认关闭SSH的,开启SSH只需要在boot下新建一个名为ssh的文件夹即可2.xshell连接树莓派树莓派启动后,就可以通过SSH连接登录系统。(查询树莓派的IP地址就可,端口默认22)树莓派默认ssh账户为pi,密码为raspberry我这里是给树莓派插上网线,然后通过小米路由器后台查询树莓派的ip地址。3.修改密码# 修改pi密码 sudo passwd pi # 修改root密码 sudo passwd root4.开启vnc远程链接 # 给板子装服务端 sudo apt-get install tightvncserver # 开启一个 VNC 流,代号为 1, 冒号前有一个空格 tightvncserver :1 # 输入命令后,即可开启服务 # 第一个运行时,会做初始化设置,按提示设置访问权限、连接密码,注意vnc的链接密码和树莓派账号密码不是一回事。下载VNC Viewer客户端 下载地址:https://www.realvnc.com/en/connect/download/viewer/开启VNC流后,在VNC Viewer中输入 [IP地址]:[代号] 如:192.168.31.101:1,输入密码,连接树莓派上的服务端。可以通过图形界面链接无线网络或者做一些其他的操作了就。停止vncserver # vncserver -kill [代号] vncserver -kill :1通过vnc给树莓派连接上无线网络后,我们通过小米路由器后台就能看见树莓派有两个ip地址,这时候我们就能拔掉树莓派的有线,通过无线网卡的ip地址链接树莓派。
mysql的表存储引擎必须是innodb,事务就是多条sql其中一个执行失败就回滚,都执行成功才一起提交。保证多条sql要么都执行成功,要么都不成功。但是事务并不能避免高并发带来的数据错乱问题。如何解决高并发带来的数据错乱问题会单独写一篇文章详细阐述。如下是一个下单事务案例:1.一定要注意update语句返回受影响的行记录,如果受影响行为0,一定要手动抛出异常,在catch里面统一处理。2.商品库存字段一定要设置成无符号,一单更新成负数sql也会自动抛出异常。3.update语句的where条件一定要增加上库存大于0,等类似这样的条件,这样做的好处就是如果之前更新过,那么这次更新返回受影响行就是0,在结合手动抛出异常,程序也回滚。 /** * 下单方法 */ public function add_order () { //sleep(20);die; //模拟超时 //获取平台客户和活动信息 $clientkeynum = $this -> clientkeynum ; $activity_info = $this -> activity_info ; $bianhao = $activity_info [ 'activity_code' ]; $start_time = $activity_info [ "start_time" ]; $end_time = $activity_info [ "end_time" ]; //如果小于开始时间内 if ( time ()< $start_time ){ $rst [ 'sta' ] = "0" ; $rst [ 'msg' ] = '活动还未到开始时间!请耐心等待!' ; echo json_encode ( $rst ); die ; } //如果大于结束时间 if ( time ()> $end_time ){ $rst [ 'sta' ] = "0" ; $rst [ 'msg' ] = '活动已经结束,期待您下次参与!' ; echo json_encode ( $rst ); die ; } //如果活动不可用 $status = $activity_info [ "status" ]; if ( $status != '1' ){ $rst [ 'sta' ] = "0" ; $rst [ 'msg' ] = '活动已被禁用!' ; echo json_encode ( $rst ); die ; } //活动是否归档 $is_over = $activity_info [ "is_over" ]; if ( $is_over == '1' ){ $rst [ 'sta' ] = "0" ; $rst [ 'msg' ] = '活动已归档!' ; echo json_encode ( $rst ); die ; } $request = Request :: instance (); $param = $request -> param (); $name = trim ( $param [ 'name' ] ); $phone = trim ( $param [ 'phone' ] ); $province = trim ( $param [ 'province' ] ); $city = trim ( $param [ 'city' ] ); $area = trim ( $param [ 'area' ] ); $address = trim ( $_REQUEST [ 'address' ] ); //常规业务逻辑 if ( $name == '' ) { $rt [ 'sta' ] = '0' ; $rt [ 'msg' ] = '对不起,收货人姓名不能为空!' ; echo json_encode ( $rt ); die ; } if ( $phone == '' ) { $rt [ 'sta' ] = '0' ; $rt [ 'msg' ] = '对不起,收货人手机号不能为空!' ; echo json_encode ( $rt ); die ; } //php手机号正则校验 if ( ! preg_match ( '/ ^ 0?(1|1|1|1|1)[0-9]{10} $ /' , $phone ) ) { $rt [ 'sta' ] = '0' ; $rt [ 'msg' ] = '对不起,您输入的手机号格式不正确!' ; echo json_encode ( $rt ); die ; } if ( $province == '请选择' || $city == '请选择' || $area == '请选择' ) { $rt [ 'sta' ] = '0' ; $rt [ 'msg' ] = '对不起,请选择收货人地址!' ; echo json_encode ( $rt ); die ; } if ( $address == '' ) { $rt [ 'sta' ] = '0' ; $rt [ 'msg' ] = '对不起,收货人详细地址不能为空!' ; echo json_encode ( $rt ); die ; } //档次手机号 和选择商品信息 $activity_grade_phone_info = session ( 'icbc_activity_grade_phone_info' ); $cart_good_info = session ( 'icbc_cart_good_info' ); if ( empty ( $activity_grade_phone_info ) ) { $rt [ 'sta' ] = '0' ; $rt [ 'msg' ] = '对不起,档次里面手机号信息丢失!' ; echo json_encode ( $rt ); die ; } if ( empty ( $cart_good_info ) ) { $rt [ 'sta' ] = '0' ; $rt [ 'msg' ] = '对不起,请去重新选择商品!' ; echo json_encode ( $rt ); die ; } $daoru_phone = $activity_grade_phone_info [ 'phone' ]; $table_name = 'client_activity_grade_phone_' . $bianhao ; $goodid = $cart_good_info [ 'id' ]; //校验是否可以兑换 //名单表 $activity_grade_phone_info = Db :: table ( $table_name )-> where ( 'phone' , $daoru_phone )-> where ( 'clientkeynum' , $clientkeynum )-> find (); if ( $activity_grade_phone_info [ 'is_order' ] != '0' ) { $rt [ 'sta' ] = '0' ; $rt [ 'msg' ] = '您已经兑换过礼品了!' ; echo json_encode ( $rt ); die ; } //兑换的产品必须在当前档次里面 $grade_id = $activity_grade_phone_info [ 'grade_id' ]; $grade_good_arr = Db :: table ( 'client_activity_grade_good' )-> where ( 'clientkeynum' , $clientkeynum )-> where ( 'grade_id' , $grade_id )-> column ( "good_id" ); if (! in_array ( $goodid , $grade_good_arr )){ $rt [ 'sta' ] = '0' ; $rt [ 'msg' ] = '对不起产品范围异常,请重新兑换!' ; echo json_encode ( $rt ); die ; } //同一个活动同一个人只能兑换一次 $orderinfo_count = Db :: table ( 'client_order_info' )-> where ( 'clientkeynum' , $clientkeynum )-> where ( 'daoru_phone' , $daoru_phone )-> where ( 'activity_id' , $activity_grade_phone_info [ 'activity_id' ] )-> count (); if ( $orderinfo_count > 1 ) { $rt [ 'sta' ] = '0' ; $rt [ 'msg' ] = '同一个手机号同一个活动只能兑换一次!' ; echo json_encode ( $rt ); die ; } //库存 $client_good_info = Db :: table ( 'client_good' )-> where ( 'id' , $goodid )-> where ( 'clientkeynum' , $clientkeynum )-> find (); if ( $client_good_info [ 'stock' ]< 1 ) { $rt [ 'sta' ] = '0' ; $rt [ 'msg' ] = '对不起该产品已经没有了库存!' ; echo json_encode ( $rt ); die ; } //获取活动详情 $activity_id = $activity_grade_phone_info [ 'activity_id' ]; $activity_info = Db :: table ( 'client_activity' )-> where ( 'id' , $activity_id )-> where ( 'clientkeynum' , $clientkeynum )-> find (); $activity_name = $activity_info [ 'activity_name' ]; $project_id = $activity_info [ 'project_id' ]; //订单唯一标识 $order_keynum = create_guid (); // 启动事务 $trans_result = true ; Db :: startTrans (); try { //订单表 $order_sn = 'D' . create_order_sn (); $order_info [ 'order_sn' ] = $order_sn ; $order_info [ 'name' ] = $name ; $order_info [ 'phone' ] = $phone ; $order_info [ 'province' ] = $province ; $order_info [ 'city' ] = $city ; $order_info [ 'area' ] = $area ; $order_info [ 'address' ] = $address ; $order_info [ 'add_time' ] = time (); $order_info [ 'order_status' ] = '0' ; $order_info [ 'add_time' ] = time (); $order_info [ 'goodid' ] = $cart_good_info [ 'id' ]; $order_info [ 'goodimg' ] = $cart_good_info [ 'goods_thumb' ]; $order_info [ 'goodsku' ] = $cart_good_info [ 'goodssku' ]; $order_info [ 'goodname' ] = $cart_good_info [ 'goodsname' ]; $order_info [ 'goodsintegral' ] = $cart_good_info [ 'goodsintegral' ]; $order_info [ 'market_integral' ] = $cart_good_info [ 'market_integral' ]; $order_info [ 'keynum' ] = $order_keynum ; $order_info [ 'clientkeynum' ] = $clientkeynum ; $order_info [ 'activity_id' ] = $activity_grade_phone_info [ 'activity_id' ]; $order_info [ 'activity_name' ] = $activity_name ; $order_info [ 'project_id' ] = $project_id ; $order_info [ 'grade_id' ] = $activity_grade_phone_info [ 'grade_id' ]; $order_info [ 'referer' ] = $_SERVER [ 'HTTP_USER_AGENT' ]; $order_info [ 'daoru_phone' ] = $daoru_phone ; //同一个活动同一个达标手机号只能有一个订单,数据库达标手机号daoru_phone和活动activity_id两个字段一起做unique索引,一旦重复了,错误也会自动到catch里面 $order_id = Db :: table ( 'client_order_info' )-> insertGetId ( $order_info ); //手动抛出异常,如果insert的sql出错也会把异常抛出到catch里面 if ( ! $order_id ) { throw new \Exception ( 'insert,client_order_info失败!' ); } //修改档次下面名单表, Affected rows: 0 也会成功, 所以手动抛出异常,在catch里面记录异常信息日志 $grade_phone [ 'order_sn' ] = $order_sn ; $grade_phone [ 'order_keynum' ] = $order_keynum ; $grade_phone [ 'order_id' ] = $order_id ; $grade_phone [ 'is_order' ] = "1" ; $grade_phone [ 'order_time' ] = time (); //update语句,where条件要千万注意,增加上is_order='0',这样如果这条记录更新过,那么update返回影响的记录行就是0,就能进入下面的手动抛出异常 $flag = Db :: table ( $table_name )-> where ( 'is_order' , '1' )-> where ( 'phone' , $daoru_phone )-> where ( 'clientkeynum' , $clientkeynum )-> update ( $grade_phone ); if ( ! $flag ) { logRes ( Db :: table ( $table_name )-> getLastSql (), 'order' ); throw new \Exception ( 'update' . $table_name . '失败!' ); } //订单日志 $order_log [ 'order_sn' ] = $order_sn ; $order_log [ 'action_user' ] = '前台客户' ; $order_log [ 'action_note' ] = '前台客户下单' ; $order_log [ 'add_time' ] = time (); $order_log [ 'clientkeynum' ] = $clientkeynum ; $log_id = Db :: table ( 'client_order_log' )-> insertGetId ( $order_log ); if ( ! $order_id ) { throw new \Exception ( 'insert,client_order_log失败!' ); } //减去产品库存 where条件要注意增加stock>0,其次数据库也要把库存字段stock类型改为无符号UNSIGNED,一旦更新成负数,也能在catch中自动捕获异常,从而回滚,保证库存别卖超 $sql = "update client_good set stock=stock-1 where stock>0 and clientkeynum=' $clientkeynum ' and id=' $goodid '" ; $stock_flag = Db :: execute ( $sql ); if ( ! $stock_flag ) { throw new \Exception ( 'update,client_good的库存失败!' ); } Db :: commit (); } catch ( \ Exception $e ) { // 回滚事务 Db :: rollback (); $trans_result = false ; $msg = $e -> getMessage (); logRes ( '订单提交失败!--》' . $msg , 'order' ); } //如果失败 if ( ! $trans_result ) { $rt [ 'sta' ] = '0' ; $rt [ 'msg' ] = '兑换失败!' . $msg ; echo json_encode ( $rt ); die ; } //清除session信息,保存订单信息 $order_info = Db :: table ( 'client_order_info' )-> where ( "order_id=' $order_id '" )-> find (); session ( 'icbc_order_info' , $order_info ); //存入session Session :: delete ( 'icbc_activity_grade_phone_info' ); Session :: delete ( 'icbc_cart_good_info' ); $rt [ 'sta' ] = '1' ; $rt [ 'msg' ] = '兑换成功' ; echo json_encode ( $rt ); die ; }
我的系统是ubuntu-20.10-desktop-amd64,启动后黑屏,然后按ctrl+alt+f2(或者f3、f4...)可以切换终端原因之一是gdm3与nvidia冲突,是的gdm3无法正常显示图形界面,出现黑屏的问题。切换到其他终端然后删除nvidia组件sudo apt-get remove --purge nvidia-*执行成功之后,执行reboot重启即可正常进入桌面。
树莓派烧录系统需要先下载树莓派系统镜像或镜像压缩包,再将SD卡插入电脑,用SDFormatter这个软件对SD卡的第一个盘(boot盘)进行格式化,再用Win32DiskImager这个软件选择img镜像文件进行系统的烧录。软件点击下载:SDFormatter(格式化工具)https://www.sdcard.org/downloads/formatter/; Win32DiskImager(系统烧录工具)https://sourceforge.net/projects/win32diskimager/files/latest/download;SD卡的格式化格式化是为了清除SD卡原本的数据,以烧录新的系统。但是SD卡的格式化是不能用系统自带的格式化工具进行格式化的。如果您之前SD卡已经烧录过了系统,SD卡插入电脑是显示两个盘的,一个是SD卡的boot分区的盘,一个无法识别内容的盘。如果用系统自带的格式化工具对这一个或两个盘进行格式化,是不能完全格式化SD卡的,但是使用SDFormatter就可以很方便的对SD卡进行整卡的格式化的。使用SDFormatter需要电脑读取到SD卡的盘,然后点击“更新”,选取SD卡插入后新增的第一个盘的盘符字母,然后按“格式化”,最后再按“确定”。系统的烧录系统的烧录需要您先有一个.img结尾的镜像文件,下载了镜像压缩包的需要先解压出.img文件后才能烧录。然后打开Win32 Disk Imager这个软件,选择boot盘的盘符字母,点击图中浏览打开选取您要烧录的.img镜像文件。最后再按“Write”键将镜像烧录进SD卡中,成功后会弹出图中绿框的写入成功窗口。成功后千万不要按照系统提示进行格式化SD卡的盘,以后插入SD卡也不要按照提示格式化SD卡的盘。如果要重新烧录镜像,再用SDFormatter进行格式化。
2023年07月
2023年03月
2023年02月
2022年11月
2022年09月
2022年08月
2022年07月
2022年06月
2022年04月
2022年03月
2022年02月
通过使用函数计算部署Stable Diffusion这个让我更深层次的体会到了函数计算的优势:
弹性伸缩:函数计算是按需自动伸缩的,无需手动管理服务器或虚拟机的规模。它可以根据请求的数量和负载情况自动扩展和缩减计算资源,以确保始终有足够的资源来处理请求。这样可以大大降低运维成本,并且可以应对突发的高并发请求。
无服务器架构:函数计算是一种无服务器架构,开发者无需关心底层的基础设施和服务器管理。开发者只需编写函数代码并上传到云端,云服务提供商会负责管理底层的服务器和资源。
按量计费:函数计算采用按量计费的方式,即按照实际执行的函数代码的运行时间和资源消耗进行计费。开发者只需为实际使用的计算资源付费,无需预付费或长期租用服务器。
高可用性:函数计算具有高可用性,云服务提供商会自动进行故障转移和容错处理,确保函数在任何时间都可用。即使在出现故障的情况下,云服务也会自动将函数迁移到其他可用的计算节点上。
多语言支持:函数计算支持多种编程语言,如JavaScript、Python、Java等,开发者可以根据自己的喜好和需求选择合适的语言进行开发。
集成其他云服务:函数计算可以与其他云服务进行集成,例如对象存储、消息队列、数据库等。开发者可以方便地使用这些服务来构建更复杂的应用程序。
外部硬盘备份:将文件复制到外部硬盘驱动器中,以防止计算机出现问题时数据丢失。
云备份:将数据上传到云服务提供商中的服务器上,以便在需要时可以随时恢复数据。
网络备份:通过网络将数据发送到备份服务器或其他设备上,以确保数据安全性和可用性。
光盘备份:将重要数据烧录到光盘中,作为另一种备份存储介质。
从开发者的角度来看,优秀的产品经理应该具备以下几个方面的品质:
具备技术背景:优秀的产品经理应该具备一定的技术背景,能够理解开发过程和技术架构,对开发人员的工作有一定的了解和尊重,同时也能够为解决技术上的问题提出建设性的意见。
细心和耐心:产品经理需要对产品的细节和用户体验有很高的敏感度,能够在开发过程中发现并解决问题。同时,要有耐心地听取开发人员的反馈,理解他们的观点并寻求解决方案。
具备管理能力:作为产品团队的领导者,优秀的产品经理需要具备一定的管理能力,能够合理规划和分配工作任务,展现出自己的领导风范,并帮助团队成员解决问题。
沟通能力强:产品经理需要与开发团队紧密协作,并与其他部门进行沟通协调,例如与设计师、市场营销团队和客户服务团队等。优秀的产品经理应该能够清晰地表达自己的想法和需求,同时能够理解和处理不同人群间的沟通误解,从而确保产品的成功。
一、充分认识自己 首先,开发者需要充分认识自己的能力和目标,具体而言,有以下几点:
1、认识自己的技术及知识水平。首先,要了解自己目前拥有的技术及知识,以及所掌握的技术及知识的开发水平,假如是旧技术及知识,要考虑否定新技术及知识;
2、弄明白自己最感兴趣的方向。不同的专业方向都有属于自己的特点,开发者要弄清楚自己偏好于哪方面,然后打算如何朝着某一方向发展;
二、制定适当的职业规划
1、结合具体行业发展趋势,制定发展规划。现在不仅要学习技术,而且要注重发展能力,即从技术理论上学习,也要从当下社会发展趋势去分析行业发展趋势,以弄清自己方向,做出深思熟虑的决策以便达到长远发展;
2、做好技术结构布局规划。除了结合行业发展趋势区制定适当的职业规划外,还要弄清功能结构规划,例如,要把握核心技术所需的专业模块,以及所需的技术支持及管理经验,使自己的结构及技术布局更合理,以便更好的满足用户对安全稳定性的要求;
3、找对职业规划的支持。因为职业规划是一件不容易的事情,要把握好发展趋势,为了确定靠谱的职业规划,建议将自己的计划提交给一般有经验的职业规划专家或公司,以得到有效的、靠谱的指导建议。
1、建立开源社区规范和准则:确立清晰的规范和准则,尽量准确化条条框框,约束开源社区发展方向和影响范围;
2、构建好社区运营模式:确立适合社区发展的运营模式,结合开源社区的特点,建立一套可行的运营机制;
3、学习和改进社区风气:把开源运动的口号和思想坚持到位,同时激发社区的进步精神;
4、加强社区管理力度:建立严格的审核制度,加快社区的发展进程,维护社区的整体形象;
5、分配责任和保障利益:给予社区参与者合理的待遇和利润,从而增强社区的凝聚力和参与欲望;
6、不断完善社区形象:强化社区宣传工作,增加外部社会对开源社区的关注,扩大社区影响力。
总之,构建“可控开源”体系,需要把握住规范与自由相结合,同时兼顾激励与发展,权衡包容与约束,形成一种宽容而有序的开源社区。
2023年社区讨论的话题可以包括:
新能源技术的进步:随着新能源技术的普及,人们如何充分发挥新能源的优势,全面推进新能源技术的发展;
电子商务新趋势与发展:未来电子商务行业如何利用新技术,更好地为消费者提供更加优质、便捷的服务;
智能化制造行业的发展:智能化制造技术如何带动工业制造业的进步以及产品提升质量的成效;
人工智能与机器学习:人工智能和机器学习如何推进信息化时代的进步,划定未来社会生活模式的新轨迹;
虚拟现实的应用:如何利用虚拟现实虚拟技术,打造更真实的世界体验,让用户沉浸在虚拟现实空间中;
医疗保健:怎样利用新科技,为患者提供更高标准的诊疗服务与护理服务,改善群众就医体验。
不一定是下一场IT革命。低代码开发的想法是利用友好的图形界面让用户快速创建、测试以及部署应用程序,而不需要花费大量时间在编写技术性代码上。这是一个很有意义的发展,但它不可能全面取代传统的编码方式。云计算和低代码开发都是使开发更快捷、更灵活的技术,但它们并不一定直接相关。
作为开发者,我喜欢有关技术,新技术进展,新的应用开发的话题,以及业界变革的新趋势。我也喜欢有关数据分析和推理,机器学习线上应用的内容。
serverless的本质,不是算力资源的简单堆砌,而是池化——它将大量的零散算力资源(廉价的算力资源)进行打包、汇聚,实现更高可靠性、更高性能、更低成本的算力。
具体来说,在云计算中,CPU、GPU、内存、硬盘等计算资源被集合起来,通过软件的方式,组成一个虚拟的可无限扩展的“算力资源池”。如果用户有算力需求,“算力资源池”就会动态地进行算力资源的分配,构建一个虚拟的“计算机”。用户按需使用、付费,即可。
相比于用户自购设备、自建机房、自己运维,云计算有明显的成本优势,可以节约大量资金和人力。
本文中的磁盘/dev/vdb为笔者测试服务器上的命名,在您的服务器中可能是/dev/xdb、/dev/sdb、/dev/xvdb等等
请根据实际情况进行修改
1、创建挂载目录(www为宝塔默认安装目录)
mkdir -p /www
2、确认是否没有分区的磁盘,如下图,没有分区的磁盘是/dev/vdb,在您的服务器中可能是/dev/sdb,请注意按照实际名称修改
fdisk -l
3、为磁盘分区,若已分区,可跳过
fdisk /dev/vdb
4、输入n开始创建分区
5、输入p创建主分区
6、选择分区号 输入1
7、输入分区开始位置,直接回车
8、输入分区结束位置,直接回车
9、输入wq 回车退出
10、检查是否分区成功(带有vdb1/sdb1/xvb1说明成功)
fdisk -l
11、格式化分区,这里输入看到的磁盘加分区号 如下图为/dev/vdb1 已格式化的可跳过
mkfs.ext4 /dev/vdb1
12、将分区挂载信息添加到配置文件/etc/fstab中,实现开机/重启自动挂载
echo "/dev/vdb1 /www ext4 defaults 0 0" >> /etc/fstab
13、重新挂载所有分区
mount -a
14、检查是否挂载成功
df -h
挂载后即可安装面板
安装宝塔参考:https://www.bt.cn/new/index.html 不在详细累赘叙述了。。
ossfs能让您在Linux系统中,将对象存储OSS的存储空间(Bucket)挂载到本地文件系统中,您能够像操作本地文件一样操作OSS的对象(Object),实现数据的共享。
1.下载安装包。
以下载CentOS 7.0 (x64)版本为例:
wget http://gosspublic.alicdn.com/ossfs/ossfs_1.80.6_centos7.0_x86_64.rpm
2.安装ossfs。
CentOS 6.5及以上系统版本的安装命令:
sudo yum localinstall ossfs_1.80.6_centos7.0_x86_64.rpm
3.配置账号访问信息。
将Bucket名称以及具有此Bucket访问权限的AccessKeyId/AccessKeySecret信息存放在/etc/passwd-ossfs文件中。注意这个文件的权限必须正确设置,建议设为640。
echo zhensoft-bak:LTAI*********ErRTa7:yxgVMMD3Eh******64AuCkkslbkok > /etc/passwd-ossfs chmod 640 /etc/passwd-ossfs4.将Bucket挂载到指定目录。
mkdir /oss
ossfs zhensoft-bak /oss -ourl=http://oss-cn-beijing-internal.aliyuncs.com -o allow_other
5.如果您不希望继续挂载此Bucket,您可以将其卸载。
fusermount -u /oss
如果出现
fusermount: failed to unmount /oss: Device or resource busy
使用如下命令
fusermount -zu /oss
如果还是不行就重启服务器
6.如果出现错误
ossfs: error while loading shared libraries: libcrypto.so.10: cannot open shared object file: No such file or directory需要执行:yum install compat-openssl10
ecs建站推荐使用centos stream和ubuntu系统,然后安装宝塔面板。通过这个可视化工具就超级简单了。https://www.bt.cn/new/index.html
需要再oss绑定自己的域名,通过自己的域名+图片路径才能才能域名,否则就是下载。
之前使用阿里云oss自带域名是支持预览的后来做了限制。目前再 img标签里面引用阿里云oss自带域名+图片路径也是可以的,唯独不能预览。
建议使用docker安装:下面示例说的是msyql8.0,服务器的安全组以及服务器防火墙要放行对应端口
拉取msyql镜像,通过镜像启动容器
拉取镜像
docker pull mysql:8.0
通过镜像启动容器
docker run --name zs-test-mysql80 -e MYSQL_ROOT_PASSWORD=111111 -p 39001:3306 -d mysql:8.0
说明:
zs-test-mysql80 容器名字
-e 指定msyql的密码
-p映射端口前面是宿主机服务器端口:后面是容器服务端口
-d 后台运行,并且返回容器id
mysql:8.0 镜像名字
下面说下步骤:
https://fcnext.console.aliyun.com/cn-beijing/services
1.根据页面的提示先创建一个服务。
2.创建函数,选择使用标准runtime从0创建,因为我只是一个接口服务,用最轻量的形式创建即可。运行环境选择php7.2,目前只有7.2,如果你是把已有的项目迁移进来,或者是php的其他版本,选择后面的自定义运行时。代码包通过我上面的gitee地址下载即可。
这里给官方一个建议直接可以拉去开源的gitee或者github地址的代码,那样更方便些。
3.如何访问。
点击下图里面的url,提示一个文件下载,打开下载的文件,里面是返回的转化后的地址的json,这里为啥是下载呢?使用的阿里云的域名会在头部增加强制下载,绑定自己的域名,通过自己的域名访问就好了。
4.绑定自己的域名
根据下图填写域名然后自己去cname,这里给官方提个小建议,本账号下面的域名添加的时候最好能支持自动cname那样就更方便了。
下面是路由配置 /* 指的是根目录下面所有 指向 这个ip2region这个服务下面的ip2region这个函数下面的最新版本。
5.可以通过自定义域名访问了就。
是不是很简单。
源代码地址以及说明:
https://gitee.com/wlphp/aliyun-fc-ip2regioin
redis开启了免密就不需要添加白名单了,同一个vpc就行。
如果没有开启免密,您需要添加白名单或是使用相同的安全组
白名单是公用的(内网链接和公网链接),白名单和安全组设置一个就可以,建议直接设置白名单
您ECS和rds在同一个安全组就可以不用添加白名单,就只有这个作用,所以建议直接设置白名单就行
看到身边很多朋友都去了云栖大会打卡☘️
所以我也来啦!
不得不说真的收获太多啦!
大家周末不知道去哪玩也可以考虑来这里打卡一下的呦✨
尤其要和大家说的就是这个B馆无影展区
亲自去体验了一下驾驶飞机穿梭云端的感觉
不得不说太炫酷啦!
而且完全就是沉浸式体验
真的特别真实✨
另外这里还有很多新品在等待着大家!
还有这个任生堂也是超级好逛的⭐️
来这里一次真的玩的很开心吖
大家有时间记得亲自去打卡啦
感受一下科技的魅力!
我是php软件开发,有用过流水线发布代码到ecs也使用过流水线发布vue-cli的纯前段项目到oss,总体感觉就是比我们自己搭建的jkens来说,不需要我们运维,同时免费的额度已经够我们使用了,相比之前更稳定。云效appstack的简单化的部署确实减轻好多工作量。更加可视化。自己先多使用熟悉熟悉,然后在公司内部先推荐其他同事使用起来。感觉非常棒!!