laravel 下正确添加扩展包

我们经常要往现有的项目中添加扩展包,有时候因为编码人员还不了解 Laravel,在一些不良开发文档的引导下,如下图来自 七牛云

又或者像这样:

前者,不经由 Laravel,使用原生 Composer, 舍弃了包的管理,显然有问题。后者,在现在的逻辑中,可能会对项目造成巨大的伤害——这个是一次性把所有扩展包更新到最新版本(避免这个问题可以针对单独一个包进行 update,或者在 composer.json 中指定版本号)。

所以今天好好看了下 composer 在 laravel 项目下的一些配置。

composer 命令

先简单解释下 composer 命令。

  • composer install - 如有 composer.lock 文件,直接安装,否则从 composer.json 安装最新扩展包和依赖;
  • composer update - 从 composer.json 安装最新扩展包和依赖;
  • composer update vendor/package - 从 composer.json 或者对应包的配置,并更新到最新;
  • composer require new/package - 添加安装 new/package, 可以指定版本,如: composer require new/package ~2.5.

更多详情可以查看 Composer官方网站,或对应的中文网站

流程

新项目:

  • composer install,安装扩展包并生成 composer.lock;

协作项目:

  • composer install

添加新扩展包:

  • composer require vendor/package 添加扩展包

需要加版本的话 composer require "foo/bar:1.0.0"

关于 composer.lock

composer.lock 文件里保存着对每一个代码依赖的版本记录,配合 composer install 使用,保证了团队所有协作者开发环境、线上生产环境中运行的代码版本的一致性。

关于 composer.json

相关文档在 Composer 的官方文档scripts里有详细讲解。

其实从命名里也能看出端倪了。

这是我们项目中的配置

{
    "name": "xxx/webapp",
    "type": "project",
    "repositories": [
        {
            "type": "vcs",
            "url": "https://git.oschina.net/xxx/xxx.git"
        }
    ],
    "require": {
        "php": ">=5.5.9",
        "laravel/framework": "5.1.*",
        "ramsey/uuid": "3.0.*",
        "rmccue/requests": "1.6.*",
        "doctrine/dbal": "2.5.*",
        "sinergi/browser-detector": "5.1.*",
        "intervention/image": "2.3.*",
        "apkj/webframework": "dev-dev",
        "ignited/laravel-omnipay": "2.*",
        "lokielse/omnipay-alipay": "dev-master",
        "predis/predis": "1.0.*",
        "qiniu/php-sdk": "v7.0.8"
    },
    "require-dev": {
        "fzaninotto/faker": "~1.4",
        "mockery/mockery": "0.9.*",
        "phpunit/phpunit": "~4.0",
        "phpspec/phpspec": "~2.1"
    },
    "autoload": {
        "classmap": [
            "database"
        ],
        "psr-4": {
            "App\\": "app/"
        }
    },
    "autoload-dev": {
        "classmap": [
            "tests/TestCase.php"
        ]
    },
    "scripts": {
        "post-install-cmd": [
            "php artisan clear-compiled",
            "php artisan optimize"
        ],
        "pre-update-cmd": [
            "php artisan clear-compiled"
        ],
        "post-update-cmd": [
            "php artisan clear-compiled",
            "php artisan optimize"
        ],
        "post-root-package-install": [
            "php -r \"copy('.env.example', '.env');\""
        ],
        "post-create-project-cmd": [
            "php artisan key:generate"
        ]
    },
    "config": {
        "preferred-install": "dist",
        "secure-http": false
    }
}

特别说明的一点是 "secure-http": false,因为某些包的 https 下载速度实在是太慢了。。。。。 特别说明的一点是 "secure-http": false,因为某些包的 https 下载速度实在是太慢了。。。。。

错误定位

今天 composer update 时发生了这样一个错误:

PHP Fatal error:  Class Illuminate\Cache\FileStore contains 2 abstract methods and must therefore 
be declared abstract or implement the remaining methods (Illuminate\Contracts\Cache\Store::many, 
Illuminate\Contracts\Cache\Store::putMany) in 

其实不是今天了,很久前就出现这个问题了。因为平时都是针对单个包update,所以并不会产生什么影响,也没去解决,一直拖到了现在。

但是事实上在项目的早期是没有这个问题的,于是慢慢回溯查看。终于找到了罪魁祸首!

现在已经不记得当时为什么要如此更改了。总之目前将划线的内容改为了

    "post-install-cmd": [
        "php artisan clear-compiled",
        "php artisan optimize"
    ],
    "pre-update-cmd": [
        "php artisan clear-compiled"
    ],
    "post-update-cmd": [
        "php artisan clear-compiled",
        "php artisan optimize"
    ],

参考资料

参考资料


Docker 新手上路(一)

新学习了 docker。记录一下。这个系列一共分为5篇,是我初次接触 Docker 的总结。

这几篇是一个很粗略的新手入门。并不足以了解全貌,不过可以让你短时间内了解 Docker 为何物以及一些简单的操作,可以一看。

Docker 是个划时代的开源项目,它彻底释放了计算虚拟化的威力,极大提高了应用的运行效率,降低了云计算资源供应的成本! 使用 Docker,可以让应用的部署、测试和分发都变得前所未有的高效和轻松!

无论是应用开发者、运维人员、还是其他信息技术从业人员,都有必要认识和掌握 Docker,以在有限的时间内做更多有意义的事。

为什么要使用 Docker?

作为一种新兴的虚拟化方式,Docker 跟传统的虚拟化方式相比具有众多的优势。

更高效的利用系统资源

由于容器不需要进行硬件虚拟以及运行完整操作系统等额外开销,Docker 对系统资源的利用率更高。无论是应用执行速度、内存损耗或者文件存储速度,都要比传统虚拟机技术更高效。因此,相比虚拟机技术,一个相同配置的主机,往往可以运行更多数量的应用。

更快速的启动时间

传统的虚拟机技术启动应用服务往往需要数分钟,而 Docker 容器应用,由于直接运行于宿主内核,无需启动完整的操作系统,因此可以做到秒级、甚至毫秒级的启动时间。大大的节约了开发、测试、部署的时间。

一致的运行环境

开发过程中一个常见的问题是环境一致性问题。由于开发环境、测试环境、生产环境不一致,导致有些 bug 并未在开发过程中被发现。而 Docker 的镜像提供了除内核外完整的运行时环境,确保了应用运行环境一致性,从而不会再出现 「这段代码在我机器上没问题啊」 这类问题。

持续交付和部署

对开发和运维(DevOps)人员来说,最希望的就是一次创建或配置,可以在任意地方正常运行。

使用 Docker 可以通过定制应用镜像来实现持续集成、持续交付、部署。开发人员可以通过 Dockerfile 来进行镜像构建,并结合 持续集成 系统进行集成测试,而运维人员则可以直接在生产环境中快速部署该镜像,甚至结合 持续部署 系统进行自动部署。

而且使用 Dockerfile 使镜像构建透明化,不仅仅开发团队可以理解应用运行环境,也方便运维团队理解应用运行所需条件,帮助更好的生产环境中部署该镜像。

更轻松的迁移

由于 Docker 确保了执行环境的一致性,使得应用的迁移更加容易。Docker 可以在很多平台上运行,无论是物理机、虚拟机、公有云、私有云,甚至是笔记本,其运行结果是一致的。因此用户可以很轻易的将在一个平台上运行的应用,迁移到另一个平台上,而不用担心运行环境的变化导致应用无法正常运行的情况。

更轻松的维护和扩展

Docker 使用的分层存储以及镜像的技术,使得应用重复部分的复用更为容易,也使得应用的维护更新更加简单,基于基础镜像进一步扩展镜像也变得非常简单。此外,Docker 团队同各个开源项目团队一起维护了一大批高质量的官方镜像,既可以直接在生产环境使用,又可以作为基础进一步定制,大大的降低了应用服务的镜像制作成本。

安装

先查看服务器的版本。lsb_release -a

Distributor ID:	Debian
Description:	Debian GNU/Linux 8.7 (jessie)
Release:	8.7
Codename:	jessie

添加源

按照教程的说法,Debian 8 的内核默认为 3.16,仅仅满足基本的 Docker 运行条件。如果打算使用 overlay2 存储层驱动,或某些功能不够稳定希望升级到较新版本的内核,可以添加 backports 源,升级到新版本的内核。 执行下面的命令添加 backports 源:

$ echo "deb http://http.debian.net/debian jessie-backports main" | sudo tee /etc/apt/sources.list.d/backports.list

升级到 backports 内核:

$ sudo apt-get update
$ sudo apt-get -t jessie-backports install linux-image-amd64

配置存储驱动

升级到 backports 的内核之后,会因为 AUFS 内核模块不可用,而使用默认的 devicemapper 驱动,并且配置为 loop-lvm,这是不推荐的。因此,不要忘记安装 Docker 后,配置 overlay2 存储层驱动。(这一步我跳过了,待熟悉以后再看看存储驱动选择的问题).

安装 Docker

在一切准备就绪后,就可以安装最新版本的 Docker 了,软件包名称为 docker-engine。将当前用户加入 docker 组,然后启动引擎。

$ sudo apt-get install docker-engine
// 如果没有这个包的话,源码安装 $ curl -sSL https://get.docker.com/ | sh
$ sudo usermod -aG docker $USER
$ sudo systemctl enable docker
$ sudo systemctl start docker

查看 docker 信息

# 查看docker版本
$docker version
	Client:
	 Version:      17.10.0-ce-rc2
	 API version:  1.33
	 Go version:   go1.8.3
	 Git commit:   af94197
	 Built:        Thu Oct 12 00:47:13 2017
	 OS/Arch:      linux/amd64
	
	Server:
	 Version:      17.10.0-ce-rc2
	 API version:  1.33 (minimum version 1.12)
	 Go version:   go1.8.3
	 Git commit:   af94197
	 Built:        Thu Oct 12 00:48:46 2017
	 OS/Arch:      linux/amd64
	 Experimental: false

# 显示docker系统的信息
$docker info
	Containers: 20
	 Running: 0
	 Paused: 0
	 Stopped: 20
	Images: 18
	Server Version: 17.10.0-ce-rc2
	Storage Driver: overlay
	 Backing Filesystem: xfs
	 Supports d_type: true
	Logging Driver: json-file
	Cgroup Driver: cgroupfs
	Plugins:
	 Volume: local
	 Network: bridge host macvlan null overlay
	 Log: awslogs fluentd gcplogs gelf journald json-file logentries splunk syslog
	Swarm: inactive
	Runtimes: runc
	Default Runtime: runc
	Init Binary: docker-init
	containerd version: 06b9cb35161009dcb7123345749fef02f7cea8e0
	runc version: 0351df1c5a66838d0c392b4ac4cf9450de844e2d
	init version: 949e6fa
	Security Options:
	 seccomp
	  Profile: default
	Kernel Version: 3.10.0-693.2.2.el7.x86_64
	Operating System: CentOS Linux 7 (Core)
	OSType: linux
	Architecture: x86_64
	CPUs: 8
	Total Memory: 15.54GiB
	Name: adsl-172-10-1-100.dsl.sndg02.sbcglobal.net
	ID: L4KV:FTZP:42FI:DVUS:3RJD:4ATZ:UMJ7:6UHH:I5ZW:3WYV:6OMI:XNZ3
	Docker Root Dir: /var/lib/docker
	Debug Mode (client): false
	Debug Mode (server): false
	Registry: https://index.docker.io/v1/
	Experimental: false
	Insecure Registries:
	 127.0.0.0/8
	Live Restore Enabled: false

获取镜像

Docker Hub 上有大量的高质量的镜像可以用。有可以直接拿来使用的服务类的镜像,如 nginx、redis、mongo、mysql、httpd、php、tomcat 等; 也有一些方便开发、构建、运行各种语言应用的镜像,如 node、openjdk、python、ruby、golang 等。 可以在其中寻找一个最符合我们最终目标的镜像为基础镜像进行定制。 如果没有找到对应服务的镜像,官方镜像中还提供了一些更为基础的操作系统镜像,如 ubuntu、debian、centos、fedora、alpine 等,这些操作系统的软件库为我们提供了更广阔的扩展空间。

Docker Hub提供API和云服务来发布基于Docker的应用程序。

获取镜像命令:

docker pull [选项] [Docker Registry地址]<仓库名>:<标签>

# 检索image
$docker search image_name

# 下载image
$docker pull image_name

# 列出镜像列表; -a, --all=false Show all images; --no-trunc=false Don't truncate output; -q, --quiet=false Only show numeric IDs
$docker images

# 显示一个镜像的历史; --no-trunc=false Don't truncate output; -q, --quiet=false Only show numeric IDs
$docker history image_name

例如docker pull debian,安装完成后显示镜像列表docker images,如下

REPOSITORY          TAG                 IMAGE ID            CREATED             SIZE
debian              latest              19134a8202e7        4 weeks ago         123 MB

运行镜像

有了镜像后,我们就可以以这个镜像为基础启动一个容器来运行。

$ docker run -it --rm debian bash
$ docker run --name webserver -d -p 1644:80 nginx

-it:这是两个参数,一个是 -i:交互式操作,一个是 -t 终端。我们这里打算进入 bash 执行一些命令并查看返回结果,因此我们需要交互式终端。
--rm:这个参数是说容器退出后随之将其删除。默认情况下,为了排障需求,退出的容器并不会立即删除,除非手动 docker rm。
debian:这是指用 debian 镜像为基础来启动容器。
bash:放在镜像名后的是命令,这里我们希望有个交互式 Shell,因此用的是 bash。 
最后 exit 退出容器。

新建/启动/唤醒/进入/终止/删除

docker run ubuntu:14.04 /bin/echo 'Hello world' -d # 后台运行
docker run -t -i ubuntu:14.04 /bin/bash  # -t分配一个伪终端, -i 标准输入保持打开。
docker start ubuntu:14.04
docker stop pid
docker restart pid
docker ps         # 命令来查看运行中的容器信息
docker ps -a      # 包括终止的容器
docker ps -l       # 最近一次启动的container
docker attach pid    # 交互命令下 exit 命令退出 或 Ctrl+d进入后台运行
docker rm xxx/pid
docker rm $(docker ps -a -q) 清除所有终止状态的容器

也可以使用 nsenter 命令更方便地 attach docker 的界面。命令在debian 8中自带。下载 .bashrc_docker,并放到 .bashrc 或者 .zshrc 中。

wget -P ~ https://github.com/yeasy/docker_practice/raw/master/_local/.bashrc_docker;
cd ~
cat .bashrc_docker >> .bashrc
source .bashrc

docker ps
docker-enter pid

保存和加载

当需要把一台机器上的镜像迁移到另一台机器的时候,需要保存镜像与加载镜像。

docker save image_name -o file_path # 保存镜像到一个tar包; -o, --output="" Write to an file
docker load -i file_path # 加载一个tar包格式的镜像; -i, --input="" Read from a tar archive file

$docker save image_name > /home/save.tar # 机器a

# 使用scp将save.tar拷到机器b上,然后:
$docker load < /home/save.tar


docker login # 登陆registry server; -e, --email="" Email; -p, --password="" Password; -u, --username="" Username

docker push new_image_name # 发布docker镜像

Docker 命令行

功能划分 命令 用法
环境信息相关    
  info 本地的配置信息
  version 显示Docker,API,Git commit,Docker,Go的版本号。
系统运维相关    
  attach 挂载正在后台运行的容器
  build 从源码构建新Image的命令
  commit 把有修改的container提交成新的Image,官方不建议使用
  cp 把容器內的文件复制到Host主机上
  diff 列出3种容器内文件状态变化(A - Add, D - Delete, C - Change )的列表清单。
  export 把容器系统文件打包并导出来,方便分发给其他场景使用
  images  
  import / save / load  
  inspect  
  kill  
  port  
  pause / unpause  
  ps  
  rm  
  rmi  
  run  
  start / stop / restart  
  tag  
  top  
  wait  
日志信息相关    
  events  
  history  
  logs  
Docker Hub服务相关    
  login  
  pull / push  
  search  
     

这篇就到这里。下一篇再写其他方面的。

参考资料:


linux查看运行级别

Linux运行级别从0~6,共7个。

0:关机。不能将系统缺省运行级别设置为0,否则无法启动。
1:单用户模式,只允许root用户对系统进行维护。
2:多用户模式,但不能使用NFS(相当于Windows下的网上邻居)
3:字符界面的多用户模式。
4:未定义。
5:图形界面的多用户模式。
6:重启。不能将系统缺省运行级别设置为0,否则会一直重启。

  查看运行级别命令:

runlevel

  先后显示系统上一次和当前运行级别。如果不存在上一次运行级别,则用N表示。


加快你的 Windows 运行速度

公司的网络速度被不少同事们诟病。当然大部分人都不是搞技术的,一般人发现电脑卡顿,上网速度卡顿,一股脑全部总结为一句话,网络不好或者电脑不行。

当然实话说,网络的质量确实不算好,尤其是相比于公司刚搬来时候的网络。那时刚办的宽带,网络公司应该也是给了比较好的网络,加上当时候人少,自然上网顺畅。不过这个网络不好的锅,是给网络公司背,还是我们的WiFi路由器背,有待我未来几天进一步验证。先写了这篇非常基础的小文,为小白们从山顶洞人进化为现代人做些微小的贡献(手动+1s),技术人们请走开。

文章会按照电脑清理、电脑使用和网络监控几个模块进行说明。

电脑清理

安装360

技术人员看到这一条大概会直接把我枪毙了Orz 不管怎么说,360对于小白来说非常合适,对付流氓软件确实很有一套。

卸载各种无用的软件

包括不限于各种杀毒软件。


Laravel 的一些小坑总结

ps: 今天有个小新闻,央行终于对比特币出手了。话说去年今年的新闻真是各种好看各种兴奋。黑天鹅一大波,各种新东西出来。

Laravel 是 Taylor Otwell 开发的一款基于 PHP 语言的 Web 开源框架。由于 Laravel 具备 Rails 敏捷开发等优秀特质,并结合了 PHP 强大的扩展包(Composer)生态与 PHP 开发者广大的受众群,让 Laravel 在发布之后的短短几年时间得到了极其迅猛的发展。

通过 Google Trends 提供的趋势图可以看出,Laravel 框架在过去十年,其增长速度在各类 PHP 框架中都是有史以来最快的,这也从正面直接反映出了 Laravel 的强大,以及其未来非常可观的发展前景。

图1

使用Laravel也有1年多的时间了。偶尔会出现点小问题,也总算是解决了。今后会慢慢总结一些。现在先说说昨天晚上遇到的坑。如果你看到这篇文章并且很巧的知道下文的一些坑的原理的话,欢迎留言或者邮件告诉我。

新建 model 的小坑

图2

Laravel 的 Eloquent ORM 提供了漂亮、简洁的 ActiveRecord 实现来和数据库进行交互。每个数据库表都有一个对应的「模型」可用来跟数据表进行交互。

然而当 new 一个新对象,并save到数据库时(第1第2步),如果不重新去数据库中 find 出来(第3步),则 Eloquent 中很多默认赋值的属性将不存在!比如说 created_at 等默认属性。

同事跟我说这是没有用好boot方法的原因,源码看少了。表示现在还不了解,接下来好好看了。

DB::table 导致的std::function()不存在的小坑

起因是使用了DB::table方法,期望是获取Session的实例,在第二个标记处获得它的id。如图

图3

然而运行的时候,会报如下错误:

exception 'Symfony\Component\Debug\Exception\FatalErrorException' with message 'Call to undefined method stdClass::getId()' in xxx.php:38
Stack trace:
#0 {main}

古狗大法终于出来了这个原因: Call to undefined method stdClass,原因就是 DB::table 会返回 StdClass objects 的集合,而不是 Session 这个实例的集合。std 显然没有 getId() 这个方法。修正的办法就是将 getId() 改为 id 即可,直接获取属性值。


Slack/Hubot

想拥有一个自己的hubot也有一段时间了。

虽然很早就开始用slack,都是用来配合ifttt耍的,还不知道这么一个大杀器。Slack 是聊天群组 + 大规模工具集成 + 文件整合 + 统一搜索。截至2014年底,Slack 已经整合了电子邮件、短信、Google Drives、Twitter、Trello、Asana、GitHub 等 65 种工具和服务,把可以把各种碎片化的企业沟通和协作集中到一起。

后来订阅了湾区日报。看了一些文章后才发现的,确实是个好东西。工作忙里偷闲,按照slack API里 Slack Developer Kit for Hubot 的内容一步一步走来。目前对里面的一些产品插件都还不了解,先记录下来,以后再慢慢补充了。

Hubot是由Github开发的开源聊天机器人,基于Node.js采用CoffeeScript编写。

可以借助Hubot开发Chatbot来自动化的完成想要一切自动化任务,比如:

  • 运维自动化(编译部署代码、重启机器,监控服务器运行情况,自动修复Bug等)
  • 外部服务交互(管理Redmine、集成Jenkins、监视Zabbix等)
  • 定时获取天气预报
  • 随机订餐
  • 聊天机器人等等。

1.安装node和npm环境

可以访问我的gist下载。

chmod a+x NodejsInstall.sh

然后就要经过漫长的等待(我的机器性能一般,大概是1个小时)。 安装完成后,node就安装好了。在安装node的同时,npm也安装到了你的服务器上。通过下面的命令查看node和npm的版本号信息。

node -v
npm -v

由于npm更新的速度会比node更快一些,所以运行下面的命令可以保证你的npm保持最新。

npm install npm@latest -g

2.安装slack/Hubot kit

使用下面的命令快速安装Yeomanhubot 。Yeoman可以辅助我们快速安装hubot。

npm install -g yo generator-hubot

安装好环境后,在我们感兴趣的目录下,可以开始新建我们的hubot项目了。

如果你是以root用户安装的话,记得要给相关的文件夹赋权限。 要不然就会像下面这样。

EACCES: permission denied, open '/root/.config/configstore/insight-yo.json'
You don't have access to this file.

赋予配置文件权限,新建项目:

chmod g+rwx /root /root/.config /root/.config/configstore /root/.npm

mkdir my-awesome-hubot
chmod 777 my-awesome-hubot 
cd my-awesome-hubot
yo hubot --adapter=slack

同时你还需要去hubot页面生成你的机器人API Token。在获得api token之后,你可以运行以下命令跑起来了。

HUBOT_SLACK_TOKEN=xoxb-YOUR-TOKEN-HERE ./bin/hubot --adapter slack

3. 让 hubot 执行 shell 脚本

npm install hubot-script-shellcmd
cp -R node_modules/hubot-script-shellcmd/bash ./

修改一下external-scripts.json,添加上以下模块:hubot-script-shellcmd。如果到此为止,你操作的步骤和我基本一样的话,你的external-scripts.json应该长的像这个样子:

[
  "hubot-diagnostics",
  "hubot-help",
  "hubot-google-images",
  "hubot-google-translate",
  "hubot-pugme",
  "hubot-maps",
  "hubot-rules",
  "hubot-shipit",
  "hubot-script-shellcmd"
]

接下来:

cd bash/handlers

这里面的 helloworld 就是个例子,可以改成自己的脚本。运行的话,如果在群组内,需要@xxx(xxx为机器人的名字,例如hubot)

hubot shellcmd helloworld

如果是私人会话,可以直接回复

shellcmd helloworld

我们可以完成任意想要的脚本,例如下面的脚本将计算CPU的使用率。

#!/bin/bash
top -b -n2 -p 1 | fgrep "Cpu(s)" | tail -1 | awk -F'id,' -v prefix="$prefix" '{ split($1, vs, ","); v=vs[length(vs)]; sub("%", "", v); printf "%s%.1f%%\n", prefix, 100 - v }'

exit 0

将文件命名成cpu,只要运行shellcmd cpu,就可以了。

4. 高级配置

5. 其他开发

参考 hubot 的两个文档 scripting 和 patterns,写的非常详细。下面是个简单的例子,将收到的信息转发到网站。

module.exports = (robot) ->
    robot.listen(
        (message) ->
            message.user.name is "你的Slack用户名" #这里限制只对我的回复做响应
            robot.brain.set 'message', message.rawText
        (response) ->
            req = "data=" + JSON.stringify({
                message: robot.brain.get('message'),
            })  
            robot.http("http://test.com/api") # 改为你自己的接口地址
                .header('Content-Type: application/x-www-form-urlencoded;charset=utf-8')
                .post(req) (err, res, body) ->
                    if err 
                        response.reply "请求接口失败" 
                        robot.emit 'error', err, res 
                        return
                    if res.statusCode isnt 200 
                        response.reply "接口返回非200"
                        return
                    response.send body
    )    

参考资料

参考资料