Write the Code. Change the World.

分类目录
12月 16

mysql 计算距离并按距离远近升序排序

$latitude = 22.531162063479226;
$longitude = 113.954436585508;

$query = Model::query()->select(['latitude', 'longitude', 'address',]);

$query->where('latitude', '>', 0);

$query->addSelect(DB::raw("acos(cos(" . $latitude . "*pi()/180)*cos(latitude*pi()/180)*cos(" . $longitude . "*pi()/180-longitude*pi()/180)+sin(" . $latitude . "*pi()/180)*sin(latitude * pi()/180)) * 6367000 AS distance"));

$res = $query->orderBy('distance', 'asc')->get();

参考:

SELECT
    id,
    latitude,
    longitude,
    ROUND(
        6378.138 * 2 * ASIN(
            SQRT(
                POW(
                    SIN(
                        (
                            23.163292 * PI() / 180 - latitude * PI() / 180
                        ) / 2
                    ),
                    2
                ) + COS(23.163292 * PI() / 180) * COS(latitude * PI() / 180) * POW(
                    SIN(
                        (
                            113.3114676 * PI() / 180 - longitude * PI() / 180
                        ) / 2
                    ),
                    2
                )
            )
        ) * 1000
    ) AS distance
FROM
    cw_party
ORDER BY
    distance asc
11月 26

针对视频文件,腾讯云点播做了一些功能处理,比如转码,自适应流,加密等。这些用 cos 是没法达到的。那么针对 laravel,针对 php,怎么使用腾讯云点播呢。这里就简单的总结下云点播的上传、删除、和防盗链。

腾讯云点播相关

云点播文档

云点播SDK

服务端上传-1

客户端上传-1

11月 25

Laravel Octane 通过使用功能强大的应用程序服务器(包括 SwooleRoadRunner )为应用程序提供服务,从而增强了应用程序的性能。Octane引导您的应用程序一次,将其保存在内存中,然后以超音速提供请求。

热点

安装条件

  • PHP8+
  • laravel v.8.37+

安装

可以通过Composer软件包管理器安装Octane:

composer require laravel/octane

安装Octane之后,您可以执行 octane:install Artisan 命令,该命令会将Octane的配置文件安装到您的应用程序中:

php artisan octane:install

RoadRunner

RoadRunner 由使用 Go 构建的 RoadRunner 二进制文件提供动力。首次启动基于 RoadRunner 的 Octane 服务器时,Octane 将为您下载并安装 RoadRunner 二进制文件。

RoadRunner Via Laravel Sail

RoadRunner 具体使用可以查看原文。

启动你的服务

可以通过 octane:startArtisan 命令启动 Octane 服务器。默认情况下,此命令将利用 server 应用程序 octane 配置文件的配置选项指定的服务器:

php artisan octane:start
# 设置应用HTTPS.配置
OCTANE_HTTPS=false

# 设置启动服务的容器
OCTANE_SERVER=swoole

# 应用代码热加载
php artisan octane:start --watch

注意 使用该命令之前,应该确保本地安装了Node 服务.您应该在您的project:library中安装Chokidar 文件监视库:

npm install --save-dev chokidar

您可以使用 watch 应用程序的 config/octane.php 配置文件中的配置选项来配置应监视的目录和文件。

指定worker进程数

默认情况下,Octane 将为您的计算机提供的每个 CPU 内核启动一个应用程序请求工作程序。然后,这些工作程序将在进入您的应用程序时用于处理传入的HTTP请求。您可以--workers在调用 octane:star t命令时手动指定要使用该选项的工人数量:

php artisan octane:start --workers=4

如果使用的是 Swoole 应用程序服务器,则还可以指定要启动的“任务工作者”数量:

php artisan octane:start --workers=4 --task-workers=6

指定最大的请求数

php artisan octane:start --max-requests=250

服务重启

您可以使用以下 octane:reload 命令正常重启 Octane 服务器的应用程序工作程序。通常,应在部署后执行此操作,以便将新部署的代码加载到内存中并用于处理后续请求:

php artisan octane:reload

服务停止

您可以使用 octane:stopArtisan 命令停止 Octane 服务器:

php artisan octane:stop

查看服务状态

php artisan octane:status

Octane 依赖注入

由于 Octane 会一次引导您的应用程序并在处理请求时将其保存在内存中,因此在构建应用程序时应注意一些注意事项。例如,您的应用程序服务提供商的registerand boot 方法仅在请求启动程序最初启动时执行一次。在后续请求中,相同的应用程序实例将被重用。
有鉴于此,在将应用程序服务容器或请求注入到任何对象的构造函数中时,应格外小心。这样,该对象可能具有旧版本的容器或后续请求中的请求。
Octane 将自动处理重置请求之间的任何第一方框架状态。但是,Octane 并不总是知道如何重置应用程序创建的全局状态。因此,您应该了解如何以Octane 友好的方式构建应用程序。下面,我们将讨论使用辛烷值时可能引起问题的最常见情况。

容器注入

通常,应避免将应用程序服务容器或 HTTP 请求实例注入其他对象的构造函数中。例如,以下绑定将整个应用程序服务容器注入到作为单例绑定的对象中:

use App\Service;

/**
 * Register any application services.
 *
 * @return void
 */
public function register()
{
    $this->app->singleton(Service::class, function ($app) {
        return new Service($app);
    });
}

在此示例中,如果 Service 在应用程序启动过程中解析了该实例,则该容器将被注入到服务中,并且该 Service 实例将在后续请求中保留该容器。对于您的特定应用程序,这可能不是问题;但是,这可能导致容器意外丢失在引导周期后期或后续请求中添加的绑定。

解决方法是,您可以停止将绑定注册为单例,也可以将容器解析器闭包注入始终解析当前容器实例的服务中:

use App\Service;
use Illuminate\Container\Container;

$this->app->bind(Service::class, function ($app) {
    return new Service($app);
});

$this->app->singleton(Service::class, function () {
    return new Service(fn () => Container::getInstance());
});

全局 app 帮助器和 Container::getInstance() 方法将始终返回应用程序容器的最新版本。

请求注入

通常,应避免将应用程序服务容器或HTTP请求实例注入其他对象的构造函数中。例如,以下绑定将整个请求实例注入到作为单例绑定的对象中:

use App\Service;

/**
 * Register any application services.
 *
 * @return void
 */
public function register()
{
    $this->app->singleton(Service::class, function ($app) {
        return new Service($app['request']);
    });
}

在此示例中,如果Service在应用程序启动过程中解析了实例,则HTTP请求将被注入到服务中,并且该Service实例将在后续请求中保留相同的请求。因此,所有标头,输入和查询字符串数据以及所有其他请求数据都将不正确。

解决方法是,您可以停止将绑定注册为单例,或者可以将请求解析程序闭包注入始终解析当前请求实例的服务中。或者,最推荐的方法只是在运行时将对象需要的特定请求信息传递给对象的方法之一:

use App\Service;

$this->app->bind(Service::class, function ($app) {
    return new Service($app['request']);
});

$this->app->singleton(Service::class, function ($app) {
    return new Service(fn () => $app['request']);
});

// Or...

$service->method($request->input('name'));

全局 request 帮助程序将始终返回应用程序当前正在处理的请求,因此可以在您的应用程序中安全使用。

配置文件注入

通常,应避免将配置实例注入其他对象的构造函数中。例如,以下绑定将配置注入到作为单例绑定的对象中:

use App\Service;

/**
 * Register any application services.
 *
 * @return void
 */
public function register()
{
    $this->app->singleton(Service::class, function ($app) {
        return new Service($app->make('config'));
    });
}

在此示例中,如果配置值在两次请求之间更改,则该服务将无法访问新值,因为它取决于原始存储库实例。
解决方法是,您可以停止将绑定注册为单例,或者可以将配置存储库解析器闭包注入到该类中:

use App\Service;
use Illuminate\Container\Container;

$this->app->bind(Service::class, function ($app) {
    return new Service($app->make('config'));
});

$this->app->singleton(Service::class, function () {
    return new Service(fn () => Container::getInstance()->make('config'));
});

全局 config 将始终返回配置的最新版本,因此可以在您的应用程序中安全使用。

管理内存泄漏

请记住,Octane 在两次请求之间将您的应用程序保留在内存中;因此,将数据添加到静态维护的阵列将导致内存泄漏。例如,由于对应用程>序的每个请求将继续向静态$data数组添加数据,因此以下控制器发生内存泄漏:

use App\Service;
use Illuminate\Http\Request;
use Illuminate\Support\Str;

/**
 * Handle an incoming request.
 *
 * @param  \Illuminate\Http\Request  $request
 * @return void
 */
public function index(Request $request)
{
    Service::$data[] = Str::random(10);

    // ...
}

并发任务

提示该功能需要使用 Swoole

使用 Swoole 时,您可以通过轻量级后台任务同时执行操作。您可以使用 Octane:: tick concurrently 方法完成此操作。您可以将此方法与PHP数组解>构相结合,以检索每个操作的结果:

use App\User;
use App\Server;
use Laravel\Octane\Facades\Octane;

[$users, $servers] = Octane::concurrently([
    fn () => User::all(),
    fn () => Server::all(),
]);

由 Octane 处理的并发任务利用 Swoole 的 “task workers”,并在与传入请求完全不同的过程中执行。可用于处理并发任务的工作者数量由 --task-workers命令上的 octane:start 指令确定:

php artisan octane:start --workers=4 --task-workers=6

定时器 & 时间间隔

  • 提示该功能需要使用Swoole

使用 Swoole 时,您可以注册将在每指定的秒数内执行的 “tick” 操作。您可以通过 tick 方法注册 “ tick” 回调。提供给该tick方法的第一个参数应该是代表股票行情指示器名称的字符串。第二个参数应该是可调用的,它将在指定的时间间隔被调用。
在此示例中,我们将注册一个闭包,每10秒调用一次。通常,tick应在boot应用程序的服务提供者之一的方法内调用该方法:

Octane::tick('simple-ticker', fn () => ray('Ticking...'))
       ->seconds(10);

使用该 immediate 方法,您可以指示 Octane 在 Octane 服务器最初启动时立即调用 tick 回调,此后每隔 N 秒:

Octane::tick('simple-ticker', fn () => ray('Ticking...'))
        ->seconds(10)
        ->immediate();

Octane 缓存

  • 这功能依赖于Swoole

使用 Swoole 时,您可以利用 Octane 缓存驱动程序,该驱动程序提供每秒高达 200 万次操作的读写速度。因此,对于需要从其缓存层实现极高读/写速度的应用程序而言,此缓存驱动程序是一个绝佳选择。
该缓存驱动程序由 Swoole 表提供动力。缓存中存储的所有数据对服务器上的所有工作人员均可用。但是,重新启动服务器后,将刷新缓存的数据:

Cache::store('octane')->put('framework', 'Laravel', 30);

缓存间隔

除了 Laravel 缓存系统提供的典型方法外,Octane 缓存驱动程序还具有基于间隔的缓存。这些缓存将按指定的时间间隔自动刷新,并应使用 boot 您的应用程序的服务提供商之一的方法进行注册。例如,以下缓存将每五秒钟刷新一次:

use Illuminate\Support\Str;

Cache::store('octane')->interval('random', function () {
    return Str::random(10);
}, seconds: 5);

进程间共享内存 (table)

高性能共享内存

使用 Swoole 时,您可以定义自己的任意 Swoole 表并与之交互。Swoole 表提供了极高的性能吞吐量,并且服务器上的所有工作人员都可以访问这些表中的数据。但是,重新启动服务器后,其中的数据将丢失。

表应在tables应用程序的octane配置文件的配置数组中定义。已经为您配置了一个示例表,该表最多允许1000行。可以通过在列类型之后指定列大小来配置字符串列的最大大小,如下所示:

'tables' => [
    'example:1000' => [
        'name' => 'string:1000',
        'votes' => 'int',
    ],
],

要访问表,可以使用以下 Octane::table 方法:

use Laravel\Octane\Facades\Octane;

Octane::table('example')->set('uuid', [
    'name' => 'Nuno Maduro',
    'votes' => 1000,
]);

return Octane::table('example')->get('uuid');

通过Swoole table 支持的列类型:string,int,和 float。

参考

文章来源
译文
laravel 9 发布
JWT
还要为性能选择 Lumen 吗
Laravel 清单

11月 10

运营商如果不是一成不变。那么就要面对更换所要面临的问题。先换 oss 到 cos

换吧

迁移工具: https://cloud.tencent.com/document/product/436/15392

liunx 下,下载好工具,配置好 config。安装必须的环境(JAVA),执行 sh start_migrate.sh 开始迁移。等到结束就 ok。

换到 cos,laravel 的工具包也跟着换了 https://github.com/overtrue/laravel-filesystem-cos

11月 09

从零开始,来吧

php 下载地址集:https://www.php.net/downloads

先下载解压 php 包

# 先把文件下到这个目录下来
cd /usr/local/src/php

wget https://www.php.net/distributions/php-7.4.25.tar.gz 

# 提示说 错误: “www.php.net” 的证书不可信 错误: “www.php.net” 的证书使用不安全的算法签名。
# wget 搞不定,我们用 curl 来弄
curl -o php-7.4.25.tar.gz https://www.php.net/distributions/php-7.4.25.tar.gz

# 再解压
tar -xzvf php-7.4.25.tar.gz

安装依赖包

能用 yum 装的就用 yum 装,不行的再源码编译安装

yum -y install gcc gcc-c++

yum -y install sqlite-devel

yum install libxslt-devel

yum install cmake

# 还是要手动安装 oniguruma
请参考:[https://blog.vini123.com/379](https://blog.vini123.com/379)

yum -y install libxml2-devel openssl-devel curl-devel libjpeg-devel libpng-devel libicu-devel freetype-devel openldap-devel openldap openldap-devel

安装 gd库,freetype2,libzip ,请参考:https://blog.vini123.com/378

安装

如果之前没建立其用户组,先建立一个用户组

groupadd nginx
useradd -g nginx -M nginx

参考

https://www.cnblogs.com/alliancehacker/p/12255445.html

https://blog.vini123.com/378

https://blog.vini123.com/303

8月 26

oss 上传 api 中,一个是上传 file ,有个是上传 content 。如何处理各种文件以及数据格式呢。常见的一种操作是上传图片。这里就弄弄看。

操作一波

如果上传的图片是 base64 ,先要转换。我们这里定义一个函数。

function base64ToImage($file)
{
    if (preg_match('/^(data:\s*image\/(\w+);base64,)/', $file, $result)) {
        $image = base64_decode(str_replace($result[1], '', $file));
        return $image;
    }
    return null;
}

我们还知道怎么拼接完成的 url。

# 这样就可以获取公网 url,当然你也可以获取内网 url
$url = App\Services\Oss::getPublicObjectURL($ossKey);

在这里,我们是先将文件传到服务器,再从服务器将文件传到 oss 上。这时,就用内网上传即可。流量要钱的,这样蛮好的。为什么不直接传 oss 上呢。直接传服务端逻辑不方便操作,直接传权限控制不好处理。反正就这种操作吧。

上边的还没说完,转换后,需要内网上传。

$file = base64ToImage($request->file);
$ossKey = ''upload/images/meets/1.jpg;
if($file) {
    $uploaded  = Oss::privateUploadContent($ossKey, $file);
}

想要删也可以的

Oss::privateDeleteObject($ossKey);

图片处理

https://help.aliyun.com/document_detail/47735.html

https://help.aliyun.com/product/31815.html

8月 26

package and github

https://packagist.org/packages/johnlui/aliyun-oss

https://github.com/johnlui/AliyunOSS

适应 laravel7 版本的请看
https://github.com/johnlui/AliyunOSS-Laravel7

操作一波

composer require johnlui/aliyun-oss

新建配置文件 config/alioss.php,填充下边的 code 。

<?php
return [
    'AccessKeyId' => env('ALIOSS_KEYID', null),                     // key
    'AccessKeySecret' => env('ALIOSS_KEYSECRET', null),             // secret
    'BucketName' => env('ALIOSS_BUCKETNAME', null)                  // bucket
];

并且在 .env.example 中加入下边的配置(养成好的习惯)。

# OSS 配置
ALIOSS_KEYID=
ALIOSS_KEYSECRET=
ALIOSS_BUCKETNAME=

继续阅读

8月 26

阿里的 rds 外网怎么连呢。需要一台能连接外网的 ecs 就可以。

操作一波

打开 navicat,新建一个连接。切换到 ssh 页签,输入 ssh 服务器的地址,账号和密码。如下图所示:

然后切换到 General(常规) 页签,填入 rds 的地址、用户名、密码,点击 test connection 即可。如下图所示:

好吧,就这样了。

8月 25

基于定位获取周边的数据的场景还是蛮多的。这不就用到了,就记录下怎么整吧。

准备

因为用到的是 laravel 框架, ORM 这些会使用到。会用到 scope。假如这里有一个模型 Meet,对用的表名是 meets, 表里有这些字段:title、content、region_code、latitude、longitude、address、created_at、updated_at

模型如下:

<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Factories\HasFactory;

class Idc extends Model
{
    use HasFactory;

    protected $fillable = ['title', 'content', 'region_code', 'latitude', 'longitude', 'address'];
}

开始

我们准备一个 distanc 的 scope

use Illuminate\Support\Facades\DB;

……

public function scopeDistance($query, $lat, $long, $distance)
{
    return $query->having('distance', '<', $distance)
    ->select(
        DB::raw("*,
        (3959 * ACOS(COS(RADIANS($lat))
        * COS(RADIANS(latitude))
        * COS(RADIANS($long) - RADIANS(longitude))
        + SIN(RADIANS($lat))
        * SIN(RADIANS(latitude)))) AS distance")
        )->orderBy('distance', 'asc');
}

3959 计算出来的是英里,换成 6378.138 计算出来的是公里。也就是地球的半径。

然后使用

$latitude = 31.0811;
$longitude = 121.26035;
$meets = Meet::distance($latitude,$longitude, 5)->get(['id','title', 'content', 'latitude', 'longitude', 'address']);

这样就计算出距离该坐标 5 km 内的数据了。

参考来源

https://www.it1352.com/1542694.html

https://www.box3.cn/tools/lbs.html

8月 18

params 的字符串和 array 的转换

$array = ['gender' => 1, 'target' => 2, 'region' => 310100];

$paramsStr = $str = http_build_query($array);

var_dump($paramsStr);

parse_str($paramsStr, $paramsArr);

var_dump($paramsArr);

在线测试:https://www.json.cn/runcode/run_php/

输出结果

string(31) "gender=1&target=2®ion=310100"
array(3) {
  ["gender"]=>
  string(1) "1"
  ["target"]=>
  string(1) "2"
  ["region"]=>
  string(6) "310100"
}