期待ing, Mac Book Pro 16寸原型机曝光

相对当前代的mac book pro, 苹果做了这些改变:

  • ARM构架的引入,这是大家所期待的,随着配套软件的成熟,arm带来的速度和功耗优势非常的棒。
  • Touch bar取消了!!这是一个鸡肋的设计,耗电不说,很多软件都不支持,要你何用?快哉!
  • HDMI和SD卡槽又回来了,这才亲民嘛,能放下身段迎合用户,此子可教也,特别是hdmi,方便了很多,SD卡槽对我们做软件开发的用户来讲可能不大,但对于摄影的来讲,很方便。
  • 最后就是屏蔽的圆角变成了硬件层面的了,之前的总感觉怪怪的。

下面就等上市啦,

更重要的是看价格,我要努力存钱了。。。。

Laravel全局分布式锁的使用

获取锁的代码:vendor/laravel/framework/src/Illuminate/Cache/RedisLock.php

/**
 * Attempt to acquire the lock.
 *
 * @return bool
 */
public function acquire()
{
    if ($this->seconds > 0) {
        return $this->redis->set($this->name, $this->owner, 'EX', $this->seconds, 'NX') == true;
    } else {
        return $this->redis->setnx($this->name, $this->owner) === 1;
    }
}

Redis的set有些特殊:vendor/laravel/framework/src/Illuminate/Redis/Connections/PhpRedisConnection.php

/**
* Set the string value in argument as value of the key.
*
* @param string $key
* @param mixed $value
* @param string|null $expireResolution
* @param int|null $expireTTL
* @param string|null $flag
* @return bool
*/
public function set($key, $value, $expireResolution = null, $expireTTL = null, $flag = null)
{
    return $this->command('set', [
    $key,
    $value,
    $expireResolution ? [$flag, $expireResolution => $expireTTL] : null, ]);
}

如果在获取锁的时候传了时间,调用redis的set的时候,调用的其实是:
$redis->set($key, $value, [‘NX’, ‘EX’ => $expireTTL])
也就是说不存在的时候才设置该key,过期时间为秒。

Redis的Set操作的参数参考:

  • EX second :设置键的过期时间为 second 秒。 SET key value EX second 效果等同于 SETEX key second value 。
  • PX millisecond :设置键的过期时间为 millisecond 毫秒。 SET key value PX millisecond 效果等同于 PSETEX key millisecond value 。
  • NX :只在键不存在时,才对键进行设置操作。 SET key value NX 效果等同于 SETNX key value 。
  • XX :只在键已经存在时,才对键进行设置操作。

锁的常规使用:

// 设置foo锁的超时时间为5,超过这个时间自动释放
$lock = Cache::lock("foo", 5);
if (!$lock->get()) {
    return false;
}

// 业务处理
$lock->release();

return true;

在指定的时间内获取到锁,如果获取不到会抛出异常:

// 设置foo锁的超时时间为5,超过这个时间自动释放
$lock = Cache::lock("foo", 5);
// 需要在2秒内获取到锁,否则会抛出异常
$lock->block(2);
// 业务处理
$lock->release();

 

Laravel实践笔记

Laravel程序的分层

1、Service层:业务的入口,如UserService

2、Repository层:通常和Service层对应,防止Service文件过大,对一些需要跨Model处理的业务,需要在Repository层来实现。

3、Model层:基础层,一些和数据表相关的简单的功能可以放在这里供调用。

对业务操作的归一化处理

每一个业务项(如用户、文章等,不是每一个表),都需要有一个createXXX、updateXXX、deleteXXX三个方法,他们需要写在Repository类中。

对所有业务层的对象的操作,最终都需要通过这3个方法来实现,这样的目的是做到数据修改的统一处理,方便在这里做日志、缓存等处理。

之所以不通过Laravel检测Model的事件来实现,是因为该种方式是针对单表的,不是针对业务的,会显示太麻烦和不合适。

比如增加一个用户createUser,里面涉及了操作多个表(如用户扩展表),这是一个原子操作,不用对扩展表进行事件的监听处理。

Laravel数据库笔记

在Model里调用self::where等操作时,其实是先到了Model类的 __callStatic 方法,再到了 __call 方法,根据情况,再到了Builder文件,也就是:
vendor/laravel/framework/src/Illuminate/Database/Eloquent/Builder.php

大多数的操作都可以去这里面去找。

别的一些常用操作记录

$flight = App\Models\Flight::create(['name' => 'Flight 10']);
$flight->fill(['name' => 'Flight 22']);


Model::find(1, [字段])
ProductModel::where('id', '1')->value('id')
ProductModel::where(['id' => '1'])->value('id')

ProductModel::where(['id' => '1'])->get(['id','name'])->toArray()
ProductModel::where(['id' => '1'])->first(['id','name'])->toarray()

# find in set
whereRaw("FIND_IN_SET(?, accept_type)", [$param['accept_type']])

// 直接取条一条记录
ProductModel::firstWhere("id", 1)
或
ProductModel::firstWhere("id", "=", 1)


// Insert并获取主键
ProductModel::create(['name'=>'sdfsdfds'])->id

ProductSkuPropertyDataModel::updateOrCreate(
    ['sku_id' => $skuId, 'property_id' => $property['id']],
    [
        'product_id' => $productId,
        'sku_id' => $skuId,
        'property_id' => $property['id'],
        'property_value' => $pValue
    ]
);

ProductStyleImageModel::whereIn('origin_hash', [2,3,4])

// insert
$user = User::create([
    'first_name' => 'Taylor',
    'last_name' => 'Otwell',
    'title' => 'Developer',
]);

// var_dump(ProductModel::where(['id' => '1'])->get(['id','name'])->toarray());
// var_dump(ProductModel::create(['name'=>'sdfsdfds'])->id);

// 插入或更新,其实是先select判断再执行
$spiderStatModel = SpiderStatModel::subSite($site['id']);
$spiderStatModel->updateOrCreate($conditions, $data);
// 或者直接:
Model::updateOrCreate($conditions, $array)

//批量更新
App\Models\Flight::where('active', 1)
          ->where('destination', 'San Diego')
          ->update(['delayed' => 1]);

//单条更新
$flight = App\Models\Flight::find(1);
$flight->name = 'New Flight Name';
$flight->save();

$styles = DB::table("t_product_style_0 as product_style")
                    ->where([['product_style.product_id', '=', $product->id]])
                    ->limit(1)
                    ->get();


$model = self::from('huddle as huddle')
        ->leftJoin("store as store", "huddle.store_id", "=", "store.id")
        ->join("store_data as store_data", "huddle.store_id", "=", "store_data.store_id")
        ->join("store_site as store_site", "huddle.store_id", "=", "store_site.store_id")
        ->where([
            'huddle.topic_id' => $actId,
            'huddle.status' => self::STATUS,
            'store_data.status' => self::STATUS,
            'store_site.status' => self::STATUS,
            'store_site.site_id' => $siteId,
            'store_data.lang_id' => $langId,
        ])
        ->where('huddle.status', '>', time())
        ->offset($page * $pageSize)
        ->limit($pageSize);

    $ret = $model->get([
            'huddle.id',
            'huddle.store_id',
            'store_data.name',
            'huddle.rebate',
            'huddle.places',
            'huddle.start_time',
            'huddle.end_time',
            'store.is_upto'
        ])
        ->toArray();


// firstOrNew 需要手动调用 save,才会保存到数据库。适合同时需要修改其他属性的场景。
// firstOrCreate 会自动保存到数据库。适合不需要额外修改其他属性的场景。

use Illuminate\Database\Query\Builder as QueryBuilder;

 

Laravel测试用例编写

测试全部
php artisan test –stop-on-failure

只测试Feature
php artisan test –group=feature –stop-on-failure

只测试某一个文件
php74 vendor/bin/phpunit tests/Feature/ServiceProductTest.php

<?php

namespace Tests\Feature;

use App\Common\ServiceRequestParam;
use App\Services\ProductService;
use App\Models\TagLangModel;
use Illuminate\Foundation\Testing\DatabaseTransactions;
use Tests\TestCase;

class SearchTest extends TestCase
{
    // 警告!!!!!!
    // 不要用这个啊,用了数据会被清空
    // use RefreshDatabase; 

    // 使用下面这个,完成后会回滚数据
    // use DatabaseTransactions;


    public function testSearch()
    {
        // TagLangModel::create(['tag_id'=>1, 'name'=>'sdfssfsdf', 'type'=>'NORMAL', 'lang'=>'en-US']);
        $tagService = resolve('App\Services\SearchService');
        $req = ServiceRequestParam::make([
            'word' => 'Farfetch',
            'limit' => 5
        ]);
        $ret = $tagService->searchProduct($req);
        $this->assertTrue($ret->isSuccess());
    }


}

 

注意事项

  • 不要用use RefreshDatabase,用了数据会被清空。
  • 可以用use DatabaseTransactions,用了数据会回滚。
  • Feature相当于集成测试,一些关联比较多的功能需要写在这里面,不然调用不了

Laravel连接数据库失败的诡异问题

晚上被运营叫起来了,发现一些本该上架的信息没有上架,观察发现是计划任务出现了问题,报数据连接超时:SQLSTATE[HY000] [2002] Connection time out。

找了一半天没有发现问题,config(“database”)看配置也是对的。

最后在框架的Connection类里打出pdo调试信息,发现数据库地址居然是测试环境的,全局搜索测试环境地址:

find ./ -name "*.php" | xargs grep  "1.1.1.1"

发现在一个Console/Command的Test测试文件的全局部分出现了,如下图:

由于Laravel在命令行运行的时候,会全局的加载所有的Command文件,分析出命令字等信息,所以造成了这段代码被执行,所以才引起了这个错误。

Redash笔记

安装

参考:https://github.com/getredash/setup
Mac上由于运行setup.sh有问题,需要手动的执行,包括以下几步
一、参考setup.sh建议env文件

PYTHONUNBUFFERED=0
REDASH_LOG_LEVEL=INFO
REDASH_REDIS_URL=redis://redis:6379/0
POSTGRES_PASSWORD=upGYF9nPSdUMErOynCeKP6VCkMPzwnCh
REDASH_COOKIE_SECRET=NiTTuwP6XcvWxjO1RQc3lKfQFAG9C0dL
REDASH_SECRET_KEY=0cYKX4YAcY2ERYBJTt4gK8nS1op5yB8K
REDASH_DATABASE_URL=postgresql://postgres:upGYF9nPSdUMErOynCeKP6VCkMPzwnCh@postgres/postgres

二、把docker-compose.xml下载下来,并修改env对应的目录。
三、初始化
“`
docker-compose run –rm server create_db
“`

四、运行
“`
docker-compose up -d
“`

文档地址:https://redash.io/help/

把一个查询当成一个数据源,需要建立一个Query DataSource,再按下面一个查询写:
“`
select * from query_1;
// 上面的以query_开始后面的数据,是查询的id
“`

在Redash里更新数据,需要手动的commit

select @max_order_id:=`value` from redash_var where `var_name`='max_order_id';
select @max_order_id_new:=max(id) from `order`;

replace into redash_var (`var_name`, `value`) values ('max_order_id', @max_order_id_new);
commit;
select * from `order` where id>@max_order_id;

用Api获取结果
http://111.111.111.111:88/api/queries/3/results?api_key=xxx
post传json

{
"parameters": {
"BeginDate": "2021-01-01",
"EndDate": "2021-01-05"
},
"max_age": 1800
}

 

 

Mac操作系统PHP、MySQL等开发环境相关笔记

Mac使用

brew使用命令
https://www.cnblogs.com/miker-lcy/archive/2004/01/13/13622391.html

加入–verbose可以显示详情
brew update –verbose

安装和使用

brew install [email protected]
启动php-fpm
brew services start [email protected]

安装扩展

到指定的版本目录下,如7.4,则到:/usr/local/opt/[email protected]/bin 目录下
./pecl install redis
如果是php5.6,则要指定版本:
./pecl install https://pecl.php.net/get/redis-4.3.0.tgz
配置文件路径:
/usr/local/etc/php/8.0

如果要自己编译安装如memcache,可以从pecl上下载下来,
tar zxvf xxx.tgz后,
/usr/local/opt/[email protected]/bin/phpize
要注意下面的命令,是指定了php-config版本的
./configure –with-zlib-dir=/usr/local/Cellar/zlib/1.2.11 –with-php-config=/usr/local/opt/[email protected]/bin/php-config
make
make install
vi /usr/local/etc/php/5.6/php.ini ,加入:
extension=”memcache.so”

VSCode安装断点调试

安装xdebug扩展,在配置文件里加入:

[XDebug]
zend_extension="xdebug.so"
xdebug.mode=debug
xdebug.log_level=0
xdebug.start_with_request=yes
xdebug.client_host="127.0.0.1"
xdebug.client_port="9180"

上面的:zend_extension=”xdebug.so” 可能在安装扩展的时候已经被加上了,这里我是放在一起方便管理。
xdebug.log_level=0,如果不加这一行的话,在命令行下cli运行时,会报下面的错误:
Xdebug: [Step Debug] Could not connect to debugging client. Tried: 127.0.0.1:9180 (through xdebug.client_host/xdebug.client_port) 🙁

安装多个版本

如果要同时安装多个版本,可以先安装旧版本,再设置:
export HOMEBREW_NO_INSTALL_CLEANUP=TRUE
后,安装新版本(用install xxx.rb),这样旧版本就不会被移除掉,我们就可以用switch来切换了。

查看版本
“`
brew list –version icu4c
“`

### 退回旧版本
https://stackoverflow.com/questions/55826221/install-icu4c-version-63-with-homebrew
锁住某个包不更新
“`
brew pin 包名
“`

### 多个php版本
https://www.cnblogs.com/jingxiaoniu/p/11132807.html

### 多个版本间的切换
“`
brew switch icu4c 64.2
“`
安装PHP多版本和使用

 

开放已经放弃的包
执行以下命令,不然找不到php5
brew tap exolnet/homebrew-deprecated
上面的功能已经失效,可以删除改用下面的 2022年
brew untap exolnet/homebrew-deprecated
brew tap shivammathur/php
brew reinstall [email protected]

/usr/local/Cellar/php/7.4.12/bin/php

brew –prefix php

ln -s /usr/local/opt/[email protected]/lib/libcrypto.1.0.0.dylib /usr/local/opt/openssl/lib/libcrypto.1.0.0.dylib
ln -s /usr/local/opt/[email protected]/lib/libssl.1.0.0.dylib /usr/local/opt/openssl/lib/libssl.1.0.0.dylib

 

手动安装brew各版本
https://stackoverflow.com/questions/55826221/install-icu4c-version-63-with-homebrew/55828190#55828190

在/usr/local/opt下面有一堆的软连接,指向到/usr/local/Cellar不同的版本

If you need to have [email protected] first in your PATH run:
echo ‘export PATH=”/usr/local/opt/[email protected]/bin:$PATH”‘ >> ~/.zshrc
echo ‘export PATH=”/usr/local/opt/[email protected]/sbin:$PATH”‘ >> ~/.zshrc

For compilers to find [email protected] you may need to set:
export LDFLAGS=”-L/usr/local/opt/[email protected]/lib”
export CPPFLAGS=”-I/usr/local/opt/[email protected]/include”

安装openssl1.0
php5.6需要openssl1.0,但现在已经升级了,参照下面来安装。
https://stackoverflow.com/questions/59337838/openssl-1-0-2m-on-macos

但在安装的时候,可能会出现:
NoMethodError: undefined method `prefer_64_bit?’ for OS::Mac:Module

做法比较粗野,把openssl.rb文件里那一行删除掉就行了,直接用64的那一行。

openssl1.0和1.0安装的时候,/usr/local/opt下的符号会指向相同的目录,需要修改。

根据前缀删除redis key
redis-cli -h 1.1.1.1 -n 5 keys “ss1:en-US:cp*” | xargs redis-cli -h db-gocashback-redis.gocashback.com -n 5 del

Linux操作记录

Linux隧道端口映射和转发

1.本地ssh可以登录远程服务器,(密码或者秘钥)
2.远程数据库需要赋权限给隧道端的ip
命令:

ssh -NCPfg [email protected] -L 3388:192.168.1.1:3306

参数:
-C 使用压缩功能,是可选的,加快速度。
-P 用一个非特权端口进行出去的连接。
-f 一旦SSH完成认证并建立port forwarding,则转入后台运行。
-N 不执行远程命令。该参数在只打开转发端口时很有用(V2版本SSH支持)
-g 表示远程机器可以连接到这个端口
这里的[email protected]是连接远程服务器的用户名和IP
-L 3388:192.168.1.1:3306 这里表示建立的隧道,3388是本机端口,通过3388和远程服务器建立隧道,192.168.1.1是内网ip,可以用127.0.0.1,当然也可以用外网ip,这里要注意数据库要给这个ip赋权,因为隧道连接实际上就是数据库连接的这个ip ,后面的是远程数据库的端口
这个命令在本地运行
运行这个命令会在本地机器上开启3388端口
ss和nestat 和lsof可以看到,开启了127.0.0.1:3388
连接:
本地装好mysql客户端
mysql -u用户名 -P 3388 -h127.0.0.1 -p密码
恭喜你登陆了

ssh -NCPfg -itmp [email protected] -L 3388:127.0.0.1:3306

防火墙
https://blog.csdn.net/s_p_j/article/details/80979450

CentOS Rinetd安装

这东西在大负载使用的时候,会存在问题,最好是用nginx来做代理
下载rpm包
https://centos.pkgs.org/7/nux-misc-x86_64/rinetd-0.62-9.el7.nux.x86_64.rpm.html

rpm -ivh rinetd-0.62-9.el7.nux.x86_64.rpm

0.0.0.0 6379 db-gocashback-redis.gocashback.com 6379

systemctl start rinetd

防火墙操作

https://blog.csdn.net/s_p_j/article/details/80979450

服务操作

查看服务列表:systemctl list-unit-files
设置开机启动:systemctl enable postfix.service
取消开机启动:systemctl disable postfix.service
systemctl status/start/stop/restart postfix.service

 

安装SQUID代理

yum install squid

vim /etc/squid/squid.conf

设置指定的ip可用
acl localnet src x.x.x.x/32

设置ip隐藏
forwarded_for delete
via off
follow_x_forwarded_for deny all
request_header_access X-Forwarded-For deny all

systemctl enable squid.service
systemctl start squid.service

SSH通过跳板机登录/执行命令

ssh 
root@目标主机IP 
-i 登录目标主机的Key 
-o ProxyCommand='ssh -A ops@跳板机IP -i 登录跳板机Key -W %h:%p -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no' 
-o UserKnownHostsFile=/dev/null 
-o StrictHostKeyChecking=no 
"需要在目标主机上执行的命令"

UserKnownHostsFile 忽略ssh目录下的known_hosts文件
StrictHostKeyChecking 不要询问是否接受key的提示
ProxyCommand 通过该参数里的命令建立通道
上面2处的-i指定的private key都是当前主机目录下的key

PS命令

查看进程树
pstree -ap 进程id

查看进程的运行时间
ps -eo pid,lstart,etime,cmd | grep nginx

 

看phpfpm在干什么

strace -p `curl -s http://127.0.0.1/fpm_status?full | grep -C 2 "Finishing" | grep "pid" | awk '{print $2}' | awk 'END {print}'`