软件 – web百事通 https://www.askme-121.space web互联网之家 Tue, 26 Nov 2024 12:14:12 +0000 zh-Hans hourly 1 https://wordpress.org/?v=6.5.13 https://www.askme-121.space/wp-content/uploads/2023/12/cropped-05ee702f-4b38-40f3-915f-c8fc68b10a91-32x32.png 软件 – web百事通 https://www.askme-121.space 32 32 Rust语言入门第二篇-Cargo教程 https://www.askme-121.space/rust-cargo/ https://www.askme-121.space/rust-cargo/#respond Tue, 26 Nov 2024 12:13:23 +0000 https://www.askme-121.pw/?p=553 一.Cargo 是什么

Cargo 是 Rust 的官方构建系统和包管理器。它负责管理 Rust 项目的依赖项、构建项目以及管理项目的配置。

二.Cargo教程

要使用 Cargo 创建一个新的 Rust 项目,请使用cargo new

cargo new hello_cargo

这一行命令新建了名为 hello_cargo 的目录和项目。我们将项目命名为 hello_cargo,同时 Cargo 在一个同名目录中创建项目文件。

进入目录,将会看到 Cargo 生成了两个文件和一个目录:一个 Cargo.toml 文件,一个 src 目录,以及位于 src 目录中的 main.rs 文件。

Cargo.toml文件

这个 Cargo 文件描述了一个 Rust 项目的元数据和依赖项。下面是对文件内容的解析:

1.[package] 是一个片段(section)标题,表示接下来的内容是关于项目包的配置。

name = "hello_cargo":指定了项目的名称为 “hello_cargo”
version = "0.1.0":指定了项目的版本号为 “0.1.0”。
edition = "2021":指定了 Rust 编译器所使用的 Edition(版本),这里是 2021 Edition。

2.[dependencies] 部分:

这个部分通常用于列出项目所依赖的外部库。Cargo 文件还可以包含其他部分,比如 [workspace]、[profile] 等,用于配置工作空间和编译器参数等。这些部分不是必需的,取决于项目的需要。

src/main.rs 文件

fn main() {
    println!("Hello, world!");
}

这段 Rust 代码定义了一个程序的入口点(main 函数),并在控制台打印 “Hello, world!” 消息。

构建并运行Cargo项目

cargo build

具体而言,cargo build 命令的含义是:

1.编译项目代码:Cargo 会根据项目根目录下的 Cargo.toml 文件中的配置信息,编译整个 Rust 项目的源代码。

2.生成可执行文件或库:根据项目类型(可执行程序或库),Cargo 会生成对应的可执行文件(通常在 target/debug 目录下)或者库文件(通常在 target/debug 目录下),这取决于项目的配置和 Cargo.toml 文件中的指定。

3.处理依赖关系:如果项目依赖了其他的 Rust crate(包),Cargo 会自动下载并编译这些依赖,以确保项目的所有依赖都被正确处理和链接。

4.检查代码:Cargo 会在编译过程中执行各种检查,包括语法检查、类型检查、错误检查等,以确保代码的质量和正确性。

我们也可以使用编译然后运行它,所有这些都在一个步骤中完成:cargo run

]]>
https://www.askme-121.space/rust-cargo/feed/ 0
Rust语言入门第一篇-环境搭建 https://www.askme-121.space/rust-makeup/ https://www.askme-121.space/rust-makeup/#respond Fri, 22 Nov 2024 14:35:19 +0000 https://www.askme-121.pw/?p=552 1、C++开发环境配置

Rust 语言的底层是依赖于 C/C++ 编译器的。在安装 Rust 编译器时,通常会自动安装所需的 C/C++ 编译环境,以便 Rust 能够生成可执行文件或库。因此,在安装 Rust 之前,建议确保系统已经安装了 C/C++ 编译器。

下载地址:https://github.com/niXman/mingw-builds-binaries/releases

验证C++是否环境配置成功,按下 “Win + R” 键,打开 “运行” 对话框。 输入gcc -v

2.Rust 环境配置

如果您曾经安装过 rustup,可以执行 rustup update 来升级 Rust。

配置 PATH 环境变量

在 Rust 开发环境中,所有工具都安装在 ~/.cargo/bin 目录中,您可以在这里找到包括 rustc、cargo 和 rustup 在内的 Rust 工具链。Rust 开发者通常会将该目录加入 PATH环境变量中。安装后在终端尝试执行 rustc –version

3.Rust程序

启动的过程中报错了,按下 “Win + R” 键,打开 “运行” 对话框。 输入rustup default stable-x86_64-pc-windows-gnu

代码讲解

  1. fn main() {
    这行代码定义了一个名为main的函数。在Rust程序中,main函数是程序的入口点,也是程序开始执行的地方。Rust程序从main函数开始执行,因此这个函数是必须的,没有main函数的Rust程序将无法正常运行。
  2. println!(“Hello, world!”);
    这是main函数中的一行代码,它调用了一个宏(macro)println! 来向控制台输出一段文本。在Rust中,println! 是一个宏,用于在标准输出流(通常是控制台)打印一段格式化的文本。在这里,println!(“Hello, world!”); 的作用是将字符串”Hello, world!” 输出到控制台。

println! 是一个宏,而不是普通的函数。宏在Rust中使用!来调用,例如println!、panic!等。"Hello, world!" 是一个字符串字面量(string literal),它是一个固定的文本内容,会被直接输出到控制台。

执行过程:

当你运行这段Rust程序时,操作系统会首先调用main函数。在main函数中,println!("Hello, world!");会被执行,导致字符串"Hello, world!"被输出到控制台。这是一个简单但经典的示例,用于展示如何编写并执行最基本的Rust程序。

注意事项:

  • Rust的语法相对严格和安全,因此每个语句末尾需要分号;来结束。
  • 字符串字面量(如"Hello, world!")是不可变的,它们存储在程序的只读内存区域中。
  • Rust中的宏(macro)和普通函数有所区别,宏在编译时进行代码替换,因此可以实现更灵活的代码生成和转换。
]]>
https://www.askme-121.space/rust-makeup/feed/ 0
Rust语言 https://www.askme-121.space/rust/ https://www.askme-121.space/rust/#respond Sat, 16 Nov 2024 13:55:10 +0000 https://www.askme-121.pw/?p=548

一,Rust语言是什么

Rust 是一种系统级编程语言,由 Mozilla 开发。它的设计注重安全性、并发性和高性能。Rust 最初发布于 2010年,其目标是成为一种能够替代 C 和 C++ 的编程语言,同时提供更好的内存安全性和并发支持。

以下是 Rust 目标成为一种能够替代 C 和 C++ 的编程语言的一些优势:

1. 内存安全性:Rust 通过所有权系统和借用检查器来保证内存安全。这意味着在编写 Rust 代码时,不会出现诸如空指针、野指针或者数据竞争等内存安全问题,这些问题在 C 和 C++ 中经常导致程序崩溃或安全漏洞。

2.并发性:Rust 内置了对并发编程的支持,包括线程、消息传递和共享状态。通过使用 Rust 的并发编程模型,开发者可以编写出更加安全和高效的并发代码,避免了在 C 和 C++ 中常见的并发问题,如死锁、数据竞争等。

3.性能:Rust 的设计注重性能,其编译器能够生成高效的机器码,并且在不牺牲安全性的前提下尽可能地进行优化。因此,Rust 程序通常具有与 C 和 C++ 程序相媲美的性能。

4.可移植性:Rust 支持跨平台开发,可以在多种操作系统和硬件架构上运行。这使得 Rust 成为一个适用于各种项目和环境的通用编程语言。

访问 Rust 官网给出的答案涵盖了选择 Rust 的三个主要原因:高性能、可靠性和生产力。让我对这些原因进行更详细的解释:

1.高性能:Rust 的设计旨在提供接近于 C 和 C++ 的执行效率。它通过一系列的编译器优化和内存安全机制,使得 Rust 代码能够在不损失性能的情况下实现内存安全。这意味着你可以编写高性能的应用程序,同时又能够避免常见的内存错误,如空指针、野指针、数据竞争等。

2.可靠性:Rust 引入了独特的所有权系统和借用规则,以确保在编译时捕获内存安全问题。这种静态检查机制能够在代码编译阶段就发现潜在的错误,而不是在运行时产生意外行为或崩溃。通过在编译时防止内存错误和并发问题,Rust 可以提供更可靠、更稳定的软件。

3.生产力:尽管 Rust 强调了性能和可靠性,但它也注重开发者的生产力。Rust 提供了现代化的语法和丰富的功能,使得编写和维护代码更加轻松。它支持泛型编程、模式匹配、闭包等高级特性,提供了强大的标准库和第三方库,使得开发者可以快速构建复杂的应用程序。此外,Rust 的文档和工具链也都非常完善,有助于开发者更快地上手和解决问题。

二,Rust语言能做什么?

Rust语言的设计使其适用于许多不同的领域,包括但不限于以下几个方面:

  1. 传统命令行程序:
    Rust提供了强大的系统编程功能,使其非常适合编写各种命令行工具和实用程序。
  2. Web 应用:
    借助诸如Actix、Rocket等现代Web框架,Rust可以用于构建高性能、安全的Web应用程序。
  3. 网络服务器:
    Rust的并发和异步编程特性使其成为构建高性能网络服务器的理想选择,例如使用Tokio等异步运行时。
  4. 嵌入式设备:
    Rust的低级别控制和内存安全性使其成为嵌入式系统开发的良好选择,可以用于编写嵌入式设备的驱动程序、控制程序等。
  5. 操作系统:
    Rust的内存安全性和并发性使其成为操作系统开发的有力工具。例如,Redox OS就是一个完全用Rust编写的操作系统。
  6. 游戏引擎:
    虽然Rust在游戏开发领域的应用还不如其他语言广泛,但由于其性能和安全性,一些游戏引擎开始考虑使用Rust编写关键组件。
  7. 数据库:
    Rust社区中有一些正在积极开发的数据库项目,例如RocksDB等,可以用于构建高性能的数据库系统。
  8. 并行计算:
    Rust提供了并发和并行编程的支持,使其适用于开发并行计算应用程序,例如科学计算、数据处理等。

Rust的优势:

1.卓越的性能和安全性:Rust以性能和安全性为设计目标,通过强大的编译时检查和所有权系统,可以避免常见的内存安全问题,同时保持着接近于原生编译语言的性能。
2.灵活的语法和类型系统: Rust具有灵活的语法和严格的类型系统,支持函数式编程和面向对象编程,使得开发者能够编写出简洁、高效的代码。
3.包管理和构建工具: Rust拥有Cargo作为其默认的包管理和构建工具,它提供了便捷的依赖管理和项目构建功能,帮助开发者更快速地进行开发和部署。
4.快速的编译和执行速度: Rust的编译器通常能够快速生成高效的机器码,使得开发者可以更快地进行迭代和测试。

Rust的劣势:

1.陡峭的学习曲线: Rust的所有权和生命周期等概念对于一些开发者来说可能较为陌生,需要花费一定的时间和精力来学习和掌握。
2.相对较小的开发生态和社区: 相比于Java等老牌语言,Rust的开发生态和社区规模相对较小,可能会缺乏一些成熟的解决方案和第三方库。
3.严格的安全检查可能带来的负担: 虽然Rust的严格安全检查可以避免很多常见的错误,但有时也可能会导致一些开发负担,需要开发者手动管理一些内存和生命周期相关的问题。

]]>
https://www.askme-121.space/rust/feed/ 0
PHP-FPM是如何工作的? https://www.askme-121.space/php-fpm/ https://www.askme-121.space/php-fpm/#respond Sat, 20 Apr 2024 05:43:41 +0000 https://www.askme-121.pw/?p=518 首先了解一下几个知识点。
CGI:是 Web Server 与 Web Application 之间数据交换的一种协议。
FastCGI:同 CGI,是一种通信协议,但比 CGI 在效率上做了一些优化。
PHP-CGI:是 PHP (Web Application)对 Web Server 提供的 CGI 协议的接口程序。
PHP-FPM:是 PHP(Web Application)对 Web Server 提供的 FastCGI 协议的接口程序,额外还提供了相对智能一些任务管理。

CGI工作流程

1.如果客户端请求的是 index.html,那么Web Server会去文件系统中找到这个文件,发送给浏览器,这里分发的是静态数据。

2.当Web Server收到 index.php 这个请求后,会启动对应的 CGI 程序,这里就是PHP的解析器。接下来PHP解析器会解析php.ini文件,初始化执行环境,然后处理请求,再以CGI规定的格式返回处理后的结果,退出进程,Web server再把结果返回给浏览器。

FastCGI工作流程

1.如果客户端请求的是 index.html,那么Web Server会去文件系统中找到这个文件,发送给浏览器,这里分发的是静态数据。

2.当Web Server收到 index.php 这个请求后,FastCGI程序(FastCGI在启动时就初始化执行执行环境,每个CGI进程池各个CGI进程共享执行环境)在CGI进程池中选择一个CGI进程处理请求,再以CGI规定的格式返回处理后的结果,继续等待下一个请求。

PHP-FPM基本实现

PHP-FPM的实现就是创建一个master进程,在master进程中创建worker pool并让其监听socket,然后fork出多个子进程(work)。

这些子进程各自accept请求,子进程的处理非常简单,它在启动后阻塞在accept上,有请求到达后开始读取请求数据,读取完成后开始处理然后再返回。

在这期间是不会接收其它请求的,也就是说PHP-FPM的子进程同时只能响应一个请求,只有把这个请求处理完成后才会accept下一个请求

PHP-FPM的master进程与worker进程之间不会直接进行通信,master通过共享内存获取worker进程的信息。

比如worker进程当前状态、已处理请求数等,当master进程要杀掉一个worker进程时则通过发送信号的方式通知worker进程。

PHP-FPM可以同时监听多个端口,每个端口对应一个worker pool,而每个pool下对应多个worker进程

Worker工作流程

1.等待请求:worker进程阻塞在fcgi_accept_request()等待请求;

2.解析请求:fastcgi请求到达后被worker接收,然后开始接收并解析请求数据,直到request数据完全到达;

3.请求初始化:执行php_request_startup(),此阶段会调用每个扩展的:PHP_RINIT_FUNCTION();

4.编译、执行:由php_execute_script()完成PHP脚本的编译、执行;

5.关闭请求:请求完成后执行php_request_shutdown(),此阶段会调用每个扩展的:PHP_RSHUTDOWN_FUNCTION(),然后进入步骤(1)等待下一个请求。

Master进程管理

1.static: 这种方式比较简单,在启动时master按照pm.max_children配置fork出相应数量的worker进程,即worker进程数是固定不变的

2.dynamic: 动态进程管理,首先在fpm启动时按照pm.start_servers初始化一定数量的worker。

运行期间如果master发现空闲worker数低于pm.min_spare_servers配置数(表示请求比较多,worker处理不过来了)则会fork worker进程,但总的worker数不能超过pm.max_children。

如果master发现空闲worker数超过了pm.max_spare_servers(表示闲着的worker太多了)则会杀掉一些worker,避免占用过多资源,master通过这4个值来控制worker数

3.ondemand: 这种方式一般很少用,在启动时不分配worker进程,等到有请求了后再通知master进程fork worker进程。

总的worker数不超过pm.max_children,处理完成后worker进程不会立即退出,当空闲时间超过pm.process_idle_timeout后再退出

PHP-FPM事件管理器

1.sp管道可读事件:这个事件是master用于处理信号的

2.fpm_pctl_perform_idle_server_maintenance_heartbeat():这是进程管理实现的主要事件。

master启动了一个定时器,每隔1s触发一次,主要用于dynamic、ondemand模式下的worker管理,master会定时检查各worker pool的worker进程数,通过此定时器实现worker数量的控制

3.fpm_pctl_heartbeat():这个事件是用于限制worker处理单个请求最大耗时的,php-fpm.conf中有一个request_terminate_timeout的配置项,如果worker处理一个请求的总时长超过了这个值那么master将会向此worker进程发送kill -TERM信号杀掉worker进程,此配置单位为秒,默认值为0表示关闭此机制

4.fpm_pctl_on_socket_accept():ondemand模式下master监听的新请求到达的事件,因为ondemand模式下fpm启动时是不会预创建worker的,有请求时才会生成子进程,所以请求到达时需要通知master进程

]]>
https://www.askme-121.space/php-fpm/feed/ 0
PHP开发api签名验证 https://www.askme-121.space/php-api-sign/ https://www.askme-121.space/php-api-sign/#respond Sat, 20 Apr 2024 04:17:40 +0000 https://www.askme-121.pw/?p=517 开发过程中,我们经常会与接口打交道,有的时候是调取别人网站的接口,有的时候是为他人提供自己网站的接口,但是在这调取的过程中都离不开签名验证。

我们在设计签名验证的时候,请注意要满足以下几点:

  • 可变性:每次的签名必须是不一样的。
  • 时效性:每次请求的时效,过期作废等。
  • 唯一性:每次的签名是唯一的。
  • 完整性:能够对传入数据进行验证,防止篡改。

这里介绍一种方式,是目前国内互联网公司常用的一种方式,其中淘宝的支付宝支付接口、淘宝开放平台接口、腾讯开放平台等应用的一种方式。

一、签名参数sign生成的方法

第1步: 将所有参数(注意是所有参数),除去sign本身,以及值是空的参数,按参数名字母升序排序。

第2步: 然后把排序后的参数按参数1值1参数2值2…参数n值n(这里的参数和值必须是传输参数的原始值,不能是经过处理的,如不能将”转成”后再拼接)的方式拼接成一个字符串。

第3步: 把分配给接入方的验证密钥key拼接在第2步得到的字符串前面。

第2步: 在上一步得到的字符串前面加上验证密钥key(这里的密钥key是接口提供方分配给接口接入方的),然后计算md5值,得到32位字符串,然后转成大写.

第4步: 计算第3步字符串的md5值(32位),然后转成大写,得到的字符串作为sign的值。

举例:

假设传输的数据是/interface.php?sign=sign_value&p2=v2& p1=v1&method=cancel&p3=&pn=vn(实际情况最好是通过post方式发送),其中sign参数对应的sign_value就是签名的值。

第一步,拼接字符串,首先去除sign参数本身,然后去除值是空的参数p3,剩下p2=v2&p1=v1&method=cancel& amp;pn=vn,然后按参数名字符升序排序,method=cancel&p1=v1&p2=v2&pn=vn.

第二步,然后做参数名和值的拼接,最后得到methodcancelp1v1p2v2pnvn
第三步,在上面拼接得到的字符串前加上验证密钥key,我们假设是abc,得到新的字符串abcmethodcancelp1v1p2v2pnvn

第四步,然后将这个字符串进行md5计算,假设得到的是abcdef,然后转为大写,得到ABCDEF这个值即为sign签名值。

注意,计算md5之前请确保接口与接入方的字符串编码一致,如统一使用utf-8编码或者GBK编码,如果编码方式不一致则计算出来的签名会校验失败。

二、签名验证方法

根据前面描述的签名参数sign生成的方法规则,计算得到参数的签名值,和参数中通知过来的sign对应的参数值进行对比,如果是一致的,那么就校验通过,如果不一致,说明参数被修改过。

三、下面直接看代码

<?php

// 设置一个公钥(key)和私钥(secret),公钥用于区分用户,私钥加密数据,不能公开
$key = "c4ca4238a0b923820dcc509a6f75849b";
$secret = "28c8edde3d61a0411511d3b1866f0636";

// 待发送的数据包
$data = array(
    'username' => 'abc@qq.com',
    'sex' => '1',
    'age' => '16',
    'addr' => 'guangzhou',
    'key' => $key,
    'timestamp' => time(),
);

// 获取sign
function getSign($secret, $data) {
    // 对数组的值按key排序
    ksort($data);
    // 生成url的形式
    $params = http_build_query($data);
    // 生成sign
    $sign = md5($params . $secret);
    return $sign;
}

// 发送的数据加上sign
$data['sign'] = getSign($secret, $data);

/**
 * 后台验证sign是否合法
 * @param  [type] $secret [description]
 * @param  [type] $data   [description]
 * @return [type]         [description]
 */
function verifySign($secret, $data) {
    // 验证参数中是否有签名
    if (!isset($data['sign']) || !$data['sign']) {
        echo '发送的数据签名不存在';
        die();
    }
    if (!isset($data['timestamp']) || !$data['timestamp']) {
        echo '发送的数据参数不合法';
        die();
    }
    // 验证请求, 10分钟失效
    if (time() - $data['timestamp'] > 600) {
        echo '验证失效, 请重新发送请求';
        die();
    }
    $sign = $data['sign'];
    unset($data['sign']);
    ksort($data);
    $params = http_build_query($data);
    // $secret是通过key在api的数据库中查询得到
    $sign2 = md5($params . $secret);
    if ($sign == $sign2) {
        die('验证通过');
    } else {
        die('请求不合法');
    }
}
]]>
https://www.askme-121.space/php-api-sign/feed/ 0
用PHP实现SSO单点登录 https://www.askme-121.space/php-sso/ https://www.askme-121.space/php-sso/#respond Sat, 20 Apr 2024 02:33:31 +0000 https://www.askme-121.pw/?p=516 SSO英文全称Single Sign On,单点登录。SSO是在多个应用系统中,用户只需要登录一次就可以访问所有相互信任的应用系统。

它包括可以将这次主要的登录映射到其他应用中用于同一个用户的登录的机制。它是目前比较流行的企业业务整合的解决方案之一,下面我们来看看吧。

简单讲一下 SSO 单点登录系统的接入的原理,前提是系统本身有完善的用户认证功能,即基本的用户登录功能,那实现起来就很方便了。

SSO 登录请求接口往往是接口加上一个回调地址,访问这个地址会跳转到回调地址并带上一个 ticket 参数,拿着这个 ticket 参数再请求接口可以获取到用户信息,如果存在用户则自动登录,不存在就新增用户并登录。

要使用 PHP 实现单点登录(Single Sign-On,SSO),可以借助一些标准和协议,如OAuth 2.0、OpenID Connect 或 SAML(Security Assertion Markup Language)。

下面是一个使用 OAuth 2.0 实现 SSO 的简单示例:

创建认证服务器(Authorization Server):该服务器负责处理用户认证和发放访问令牌。

// authorization_server.php

// 用户登录验证逻辑,验证用户名和密码是否正确
function validateUser($username, $password) {
    // 进行用户验证逻辑,返回验证结果
    // ...
}

// 发放访问令牌给授权成功的用户
function issueAccessToken($username) {
    // 生成访问令牌并返回
    // ...
}

// 处理用户登录请求
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
    $username = $_POST['username'];
    $password = $_POST['password'];

    // 验证用户名和密码
    if (validateUser($username, $password)) {
        // 发放访问令牌
        $accessToken = issueAccessToken($username);

        // 将访问令牌返回给客户端
        echo json_encode(['access_token' => $accessToken]);
        exit;
    } else {
        // 用户认证失败
        http_response_code(401);
        exit;
    }
}

创建客户端应用(Client Application):该应用将使用认证服务器发放的访问令牌来验证用户身份。

// client_application.php

// 验证访问令牌是否有效
function validateAccessToken($accessToken) {
    // 进行访问令牌验证逻辑,返回验证结果
    // ...
}

// 处理用户登录请求
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
    $accessToken = $_POST['access_token'];

    // 验证访问令牌
    if (validateAccessToken($accessToken)) {
        // 用户已验证通过,执行单点登录逻辑
        // ...
        echo 'Login successful';
        exit;
    } else {
        // 访问令牌验证失败
        http_response_code(401);
        exit;
    }
}

用户登录和单点登录流程:

  • 用户访问 Client Application,检查用户是否已登录。
  • 如果用户未登录,则跳转到认证服务器的登录页面。
  • 用户在认证服务器上提供用户名和密码进行登录认证。
  • 认证服务器验证用户名和密码,并发放访问令牌。
  • 用户被重定向回 Client Application,并将访问令牌发送到 Client Application。
  • Client Application 验证访问令牌的有效性,确认用户已成功登录,执行相应的单点登录逻辑。

请注意,上述示例只是一个简单的演示,实际的 SSO 实现可能更复杂,涉及到会话管理、安全性等方面的考虑。您可以根据具体的需求和使用的协议,进一步完善和扩展这个基本示例。

]]>
https://www.askme-121.space/php-sso/feed/ 0
app开发框架有哪些 https://www.askme-121.space/app/ https://www.askme-121.space/app/#respond Mon, 18 Mar 2024 06:59:01 +0000 https://www.askme-121.pw/?p=514 APP开发框架是指在移动应用程序开发中,为了提高开发效率、降低开发成本和提高代码质量而使用的软件架构体系。下面将介绍一些常用的APP开发框架。

1. React Native

React Native是由Facebook开发的一种基于React的开源框架。它允许开发者使用JavaScript和React来构建原生移动应用程序。React Native可以让开发者快速构建高性能的移动应用程序,并且可以在不同平台上使用相同的代码。React Native通过使用原生组件来实现高性能,从而提高了应用程序的性能和响应能力。

2. Flutter

Flutter是Google开发的一种开源框架,它可以用于构建高性能的移动应用程序。Flutter使用Dart语言来编写代码,并且可以在iOS和Android平台上使用相同的代码。Flutter具有快速的开发周期、高质量的用户界面和出色的性能。Flutter使用自己的渲染引擎来实现高性能,从而提高了应用程序的性能和响应能力。

3. Ionic

Ionic是一个基于HTML5和CSS3的开源框架,可以用于构建跨平台的移动应用程序。Ionic使用AngularJS来构建应用程序,并且可以在不同平台上使用相同的代码。Ionic提供了大量的UI组件和工具来帮助开发者更快地构建应用程序。Ionic使用自己的渲染引擎来实现高性能,从而提高了应用程序的性能和响应能力。

4. Xamarin

Xamarin是由Microsoft开发的一种跨平台移动应用程序开发框架。它允许开发者使用C#和.NET框架来构建原生移动应用程序。Xamarin可以在iOS、Android和Windows Phone平台上使用相同的代码。Xamarin使用自己的渲染引擎来实现高性能,从而提高了应用程序的性能和响应能力。

5. PhoneGap

PhoneGap是一个基于HTML、CSS和JavaScript的开源框架,可以用于构建跨平台的移动应用程序。PhoneGap使用Web技术来构建应用程序,并且可以在不同平台上使用相同的代码。PhoneGap使用自己的渲染引擎来实现高性能,从而提高了应用程序的性能和响应能力。

以上是一些常用的APP开发框架,每个框架都有自己的特点和适用范围。选择一个适合自己的框架可以提高开发效率和代码质量,从而更快地构建高性能的移动应用程序。

]]>
https://www.askme-121.space/app/feed/ 0
静态化API是什么?用Swoole如何去实现呢? https://www.askme-121.space/static-api/ https://www.askme-121.space/static-api/#respond Mon, 29 Jan 2024 06:25:32 +0000 https://www.askme-121.pw/?p=503 什么是静态化API?
静态化API可以理解成把一些接口的数据存储在服务器本地。常用的是存成json文件,也可以是放在swoole的table中,总之是用户不从数据库直接读取数据,而是从本地加载的方式来大幅提高性能,因为很多系统的性能瓶颈是在数据库的位置。

解决方案
方案1 easySwoole + crontab
方案2 easySwoole定时器
方案3 Swoole table
方案4 Redis

实现
这里做的分页的场景,不包含分页的源码,只从拿到了分页的数据看定时生成json和获取json的部分

原始的方法 – 读取mysql

这是原始的方法,每个用户访问都会去数据库里面读取一次,每一次分页也会访问数据库,会造成大量的资源开销。

public function lists0(){
    $condition = [];
    if(!empty($this->params['cat_id'])){
        $condition['cat_id'] = intval($this->params['cat_id']);
    }

    try {
        $videoModel = new VideoModel();
        $data = $videoModel->getVideoData($condition, $this->params['page'], $this->params['size']);
    } catch (\Exception $e) {
        //$e->getMessage();
        return $this->writeJson(Status::CODE_BAD_REQUEST,"服务异常");
    }

    if(!empty($data['lists'])){
        foreach ($data['lists'] as &$list){
            $list['create_time'] = date("Ymd H:i:s",$list['create_time']);
            $list['video_duration'] = gmstrftime("%H:%M:%S",$list["video_duration"]);
        }
    }

    return $this->writeJson(Status::CODE_OK,'OK',$data);
}

知识点:

1 获取以秒为单位的时间长度使用gmstrftime(“%H:%M:%S”,$list[“video_duration”])
2 在做模型的时候写一个基类,把连接数据库的工作放在这个基类的构造方法当中,这样每次实例化的时候就自动连接了,提高代码的复用性

<?php


namespace App\Model;

use EasySwoole\Core\Component\Di;

class Base
{
    public $db = "";

    public function __construct()
    {
        if(empty($this->tableName)){
            throw new \Exception("table error");
        }
        $db = Di::getInstance()->get("MYSQL");
        if($db instanceof \MysqliDb){
            $this->db = $db;
        }else{
            throw new \Exception("db error");
        }
    }

    public function add($data){
        if(empty($data) || !is_array($data)){
            return false;
        }
        return $this->db->insert($this->tableName,$data);
    }

}

方案一、方案二 使用easySwoole定时器以及contab的生成静态化API

这是直接读取静态化API的方法,其实就是读文件然后返回给前端

/**
 * 第二套方法 - 直接读取静态化json数据
 * @return bool
 */
public function lists(){
    $catId = !empty($this->params['cat_id']) ? intval($this->params['cat_id']) : 0;
    $videoFile = EASYSWOOLE_ROOT."/webroot/video/json/".$catId.".json";
    $videoData = is_file($videoFile) ? file_get_contents($videoFile) : [];
    var_dump($videoData);
    $videoData = !empty($videoData) ? json_decode($videoData,true) : [];

    $count = count($videoData);
    // var_dump($videoData);
    return $this->writeJson(Status::CODE_OK,'OK',$this->getPagingData($count,$videoData));
}

这里的getPagingData使用了array_slice来切割数组来做分页。

/**
 * 获取分页信息
 * @param $count
 * @param $data
 * @return array
 */
public function getPagingData($count, $data){
    $totalPage = ceil($count / $this->params['size']);
    $data = $data ?? [];
    $data = array_slice($data, $this->params['from'], $this->params['size']);
    return [
        'total_page' => $totalPage,
        'page_size' => $this->params['page'],
        'count' => intval($count),
        'list' => $data
    ];

}

定时生成Json文件代码 – 核心部分 全局事件easySwooleEvent->mainServiceCreate中执行,这样easySwoole启动了之后就会执行,这里使用的原生的crontab,只能够精确到分

$cacheVideoObj = new VideoCache();
// 使用cronTab处理定时任务
// 这里是闭包 要use $cacheVideoObj之后才能获取,实例化不放在function中是为了防止每次都实例化浪费资源
CronTab::getInstance()
    ->addRule("test_bing_crontab", '*/1 * * * *', function () use($cacheVideoObj) {
        $cacheVideoObj->setIndexVideo();
    });

也可以使用easySwoole的定时器来实现也是放在mainServiceCreate中执行,这里的代码一定要注意要注册一个onWorkerStart。
然后指定一个进程去执行这个定时任务注意一下这里闭包里面又有一个闭包,外面的变量要use两次。
一定要注意easwoole定时器的使用,这里的坑比较多一定要注意,优势是swoole的定时器可以到毫秒级而contab只能到分级

// easySwoole自带定时器
$register->add(EventRegister::onWorkerStart,function (\swoole_server $server,$workerId)use($cacheVideoObj){
    //让第一个进程去执行,否则每个进程都会执行
    if($workerId == 0){
        Timer::loop(1000*2,function ()use ($cacheVideoObj){
            $cacheVideoObj->setIndexVideo();
        });
    }
});

方案三 Swoole table的解决方案

swoole_table一个基于共享内存和锁实现的超高性能,并发数据结构。用于解决多进程/多线程数据共享和同步加锁问题。性能十分强悍。使用起来有一点像是缓存。这里不再是生成一个json文件去读取了,读取table中数据给前端的方法,注意要use cache这个类,这里直接使用get就可以根据key获取到table中的数据,注意这个是一个单例模式,因此需要getInstance

public function lists(){
    $catId = !empty($this->params['cat_id']) ? intval($this->params['cat_id']) : 0;
    $videoFile = EASYSWOOLE_ROOT."/webroot/video/json/".$catId.".json";

    //  第三套方案 table
    $videoData = Cache::getInstance()->get("index_video_data_cat_id".$catId);
    $videoData = !empty($videoData) ? $videoData : [];

    $count = count($videoData);;
    return $this->writeJson(Status::CODE_OK,'OK',$this->getPagingData($count,$videoData));
}

设置直接set(key,data)就可以了,注意这里的代码不是很严谨,比如要判断一下是不是设置成功了,没有设置成功发短信之类的,也要处理一下空值的场景,这里只是做一个演示。

<?php

namespace App\Lib\Cache;

use App\Model\Video as VideoModel;
use EasySwoole\Config;
use EasySwoole\Core\Component\Cache\Cache;

class Video
{
    public function setIndexVideo()
    {
        $catIds = array_keys(Config::getInstance()->getConf("category"));
        array_unshift($catIds, 0);

        $modelObj = new VideoModel();

        foreach ($catIds as $catId) {
            $condition = [];
            if (!empty($catId)) {
                $condition['cat_id'] = $catId;
            }

            try {
                $data = $modelObj->getVideoCacheData($condition);
            } catch (\Exception $e) {
                // 短信报警
                $data = [];
            }
            if (empty($data)) {

            }
            foreach ($data as &$list) {
                $list['create_time'] = date("Ymd H:i:s", $list["create_time"]);
                $list["video_duration"] = gmstrftime("%H:%M:%s", $list["video_duration"]);
            }
            // 由于table存在内存所以重启服务器时数据会丢失,要将配置中的PERSISTENT_TIME设置为1进行落盘操作
            Cache::getInstance()->set("index_video_data_cat_id".$catId, $data);
        }
    }
}

注意,一定要去config里面开启

'PERSISTENT_TIME'=>1//如果需要定时数据落地,请设置对应的时间周期,单位为秒

否则会在重启服务的时候没有数据,因为每次启动的时候swoole table会清空,要等到定时去set table的时候才会有数据,因此要开启数据落盘,这样会生成两个文件:

每次启动的时候由于还没有执行定时任务,就会先读取这两个落盘的文件中的数据,防止服务启动时等待table生成造成业务中断。

方案四 Redis

用redis来做数据缓存,每次从缓存里面度读先重写一下set方法,更加严谨一点

/**
 * 重写set方法 处理一下失效时间以及数组转json
 * @param $key
 * @param $value
 * @param $time
 * @return bool|string
 */
public function set($key, $value, $time){
    if(empty($key)){
        return '';
    }
    if(is_array($value)){
        $value = json_encode($value);
    }
    if(!$time){
        return $this->redis->set($key,$value);
    }
    return $this->redis->setex($key, $time, $value);
}

使用起来很简单啦,在之前的代码中

Di::getInstance()->get("REDIS")->set("index_video_data_cat_id".$catId, $data);

然后取出的数据的部分

public function lists(){
    $catId = !empty($this->params['cat_id']) ? intval($this->params['cat_id']) : 0;
    //redis
    $videoData = Di::getInstance()->get("REDIS")->get("index_video_data_cat_id".$catId);
    $videoData = !empty($videoData) ? json_decode($videoData,true) : [];
    $count = count($videoData);
    return $this->writeJson(Status::CODE_OK,'OK',$this->getPagingData($count,$videoData));
}

高度封装

只需要在配置文件中进行配置即可选择相应方法。设置静态化API和获取静态化API的方法

<?php


namespace App\Lib\Cache;

use App\Model\Video as VideoModel;
use EasySwoole\Config;
use EasySwoole\Core\Component\Cache\Cache;
use EasySwoole\Core\Component\Di;
use EasySwoole\Core\Http\Message\Status;

class Video
{
    /**
     * 设置静态API的方法
     * @throws \Exception
     */
    public function setIndexVideo()
    {
        $catIds = array_keys(Config::getInstance()->getConf("category"));
        array_unshift($catIds, 0);

        // 获取配置
        try {
            $cacheType = Config::getInstance()->getConf("base.indexCacheType");
        } catch (\Exception $e) {
            return $this->writeJson(Status::CODE_BAD_REQUEST,"请求失败");
        }

        $modelObj = new VideoModel();

        foreach ($catIds as $catId) {
            $condition = [];
            if (!empty($catId)) {
                $condition['cat_id'] = $catId;
            }

            try {
                $data = $modelObj->getVideoCacheData($condition);
            } catch (\Exception $e) {
                // 短信报警
                $data = [];
            }
            if (empty($data)) {

            }

            foreach ($data as &$list) {
                $list['create_time'] = date("Ymd H:i:s", $list["create_time"]);
                $list["video_duration"] = gmstrftime("%H:%M:%s", $list["video_duration"]);
            }

            switch ($cacheType) {
                case 'file':
                    $res = file_put_contents($this->getVideoCatIdFile($catId), json_encode($data));
                    break;
                case 'table':
                    $res = Cache::getInstance()->set($this->getCatKey($catId), $data);
                    break;
                case 'redis':
                    $res = Di::getInstance()->get("REDIS")->set($this->getCatKey($catId), $data);
                    break;
                default:
                    throw new \Exception("请求不合法");
                    break;
            }

            if(empty($res)){
            //    记录日志
            //    报警

            }

        }

    }

    /**
     * 获取数据的方法
     * @param $catId
     * @return array|bool|mixed|null|string
     * @throws \Exception
     */
    public function getCache($catId){
        $cacheType = Config::getInstance()->getConf("base.indexCacheType");
        switch ($cacheType){
            case 'file':
                $videoFile = $this->getVideoCatIdFile($catId);
                $videoData = is_file($videoFile) ? file_get_contents($videoFile) : [];
                $videoData = !empty($videoData) ? json_decode($videoData,true) : [];
                break;
            case 'table':
                $videoData = Cache::getInstance()->get($this->getCatKey($catId));
                $videoData = !empty($videoData) ? $videoData : [];
                break;
            case 'redis':
                $videoData = Di::getInstance()->get("REDIS")->get($this->getCatKey($catId));
                $videoData = !empty($videoData) ? json_decode($videoData,true) : [];
                break;
            default:
                throw new \Exception("请求不合法");
                break;
        }
        return $videoData;
    }

    public function getVideoCatIdFile($catId = 0){
        return EASYSWOOLE_ROOT . "/webroot/video/json/" . $catId . ".json";
    }


    public function getCatKey($catId = 0){
        return "index_video_data_cat_id".$catId;
    }
}

只需修改配置文件

<?php

return [
    "indexCacheType" => "redis" // redis file table
];

控制器获取数据给前端

public function lists(){
    $catId = !empty($this->params['cat_id']) ? intval($this->params['cat_id']) : 0;
    $videoData = (new VideoCache())->getCache($catId);
    $count = count($videoData);
    return $this->writeJson(Status::CODE_OK,'OK',$this->getPagingData($count,$videoData));
}
]]>
https://www.askme-121.space/static-api/feed/ 0
异步定时多任务消息推送,用php怎么做 ? https://www.askme-121.space/message-push/ https://www.askme-121.space/message-push/#respond Sun, 21 Jan 2024 07:52:01 +0000 https://www.askme-121.pw/?p=501 要实现异步定时多任务消息推送,可以使用多种技术来实现,如Redis、RabbitMQ、Beanstalkd等消息队列服务。

在设计数据表时,可以考虑创建一个 tasks 表,用于存储所有需要推送的任务信息。该表可以包含以下字段:
id:任务ID,自增长整数类型;
name:任务名称,用于描述该任务的作用;
type:任务类型,用于区分不同类型的任务;
data:任务数据,可以存储该任务需要用到的数据信息,可以是序列化后的字符串或JSON格式的数据;
status:任务状态,用于标识该任务的状态,如待处理、已处理等;
created_at:任务创建时间,用于记录该任务的创建时间;
updated_at:任务更新时间,用于记录该任务的最后更新时间;

在添加任务时,可以向 tasks 表中插入一条记录,设置好任务的名称、类型、数据等信息。然后,在需要推送任务时,可以通过查询 tasks 表,找到需要推送的任务信息,并将其加入到消息队列中,等待消息队列服务处理。

在处理任务时,可以使用类似下面的代码:

$queue = new RedisQueue('my_queue', $redis);

// 处理队列中的任务
while ($job = $queue->pop()) {
    $task = unserialize($job->payload);

    // 执行任务处理操作
    handleTask($task);

    // 标记任务为已处理
    $task->status = Task::STATUS_COMPLETED;
    $task->save();
}

其中,RedisQueue 是一个自定义的 Redis 队列类,用于将任务加入到 Redis 队列中。handleTask 函数则是用于处理任务的函数,根据实际需求进行编写即可。

可以使用 cron 或类似的定时任务工具来定时检查 tasks 表,并将需要推送的任务加入到消息队列中。具体的实现方式可以根据实际情况选择不同的技术方案和工具。

需要注意的是,在异步任务处理过程中,需要考虑异常情况的处理,例如任务执行失败、队列服务异常等情况。因此,在编写异步任务时,需要进行充分的异常处理和错误日志记录。

下面介绍几个具体的例子:

1、 使用 Laravel 框架提供的队列和计划任务功能实现消息推送:

// 定义消息推送任务类
class PushMessage implements ShouldQueue
{
    use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;

    protected $message;

    public function __construct($message)
    {
        $this->message = $message;
    }

    public function handle()
    {
        // 处理推送消息的逻辑
    }
}

// 在计划任务中添加消息推送任务
$schedule->call(function () {
    PushMessage::dispatch('hello')->delay(now()->addMinutes(10));
})->everyMinute();

在上面的示例代码中,我们使用 Laravel 框架提供的队列和计划任务功能,定义了一个 PushMessage 消息推送任务类,并在计划任务中添加了一个延迟 10 分钟执行的消息推送任务。

2、 使用第三方的消息队列服务实现消息推送:

// 使用 beanstalkd 消息队列
use Pheanstalk\Pheanstalk;

// 连接到 beanstalkd 服务
$pheanstalk = new Pheanstalk('127.0.0.1');

// 向队列添加消息
$pheanstalk->useTube('push_message')->put('hello', Pheanstalk::DEFAULT_PRIORITY, 60);

// 处理消息队列中的消息
while (true) {
    $job = $pheanstalk->watch('push_message')->ignore('default')->reserve();
    $message = $job->getData();
    // 处理推送消息的逻辑
    $pheanstalk->delete($job);
}

在上面的示例代码中,我们使用了第三方的消息队列服务 beanstalkd,并实现了向队列添加消息和处理消息队列中的消息的逻辑。

3、 使用 Swoole 扩展实现异步定时任务和消息推送:

// 创建 Swoole 定时器和 HTTP 服务器
$timer = swoole_timer_tick(1000, function () {
    // 处理定时任务的逻辑
});
$http = new swoole_http_server('127.0.0.1', 9501);
$http->on('request', function ($request, $response) use ($timer) {
    // 处理 HTTP 请求的逻辑
    $response->end('hello');

    // 添加异步任务
    swoole_timer_after(10000, function () use ($response) {
        $response->end('world');
    });

    // 关闭定时器
    swoole_timer_clear($timer);
});
$http->start();

在上面的示例代码中,我们使用 Swoole 扩展创建了一个定时器和 HTTP 服务器,并在定时器回调函数中处理定时任务的逻辑,在 HTTP 请求回调函数中处理 HTTP 请求的逻辑,并添加了一个 10 秒后执行的异步任务。

]]>
https://www.askme-121.space/message-push/feed/ 0
php操作ElasticSearch示例 https://www.askme-121.space/php-elasticsearch/ https://www.askme-121.space/php-elasticsearch/#respond Sat, 20 Jan 2024 07:08:11 +0000 https://www.askme-121.pw/?p=500 一、安装

通过composer安装

composer require 'elasticsearch/elasticsearch'

二、使用

创建ES类

<?php

require 'vendor/autoload.php';

//如果未设置密码
$es = \Elasticsearch\ClientBuilder::create()->setHosts(['xxx.xxx.xxx.xxx'])->build();

//如果es设置了密码
$es = \Elasticsearch\ClientBuilder::create()->setHosts(['http://username:password@xxx.xxx.xxx.xxx:9200'])->build();

三、新建ES数据库

index 对应关系型数据(以下简称MySQL)里面的数据库,而不是对应MySQL里面的索引

<?php
$params = [
    'index' => 'autofelix_db', #index的名字不能是大写和下划线开头
    'body' => [
        'settings' => [
            'number_of_shards' => 5,
            'number_of_replicas' => 0
        ]
    ]
];
$es->indices()->create($params);

四、创建表

  • 在MySQL里面,光有了数据库还不行,还需要建立表,ES也是一样的
  • ES中的type对应MySQL里面的表
  • ES6以前,一个index有多个type,就像MySQL中一个数据库有多个表一样
  • 但是ES6以后,每个index只允许一个type
  • 在定义字段的时候,可以看出每个字段可以定义单独的类型
  • 在first_name中还自定义了 分词器 ik,这是个插件,是需要单独安装的

<?php
$params = [
    'index' => 'autofelix_db',
    'type' => 'autofelix_table',
    'body' => [
        'mytype' => [
            '_source' => [
                'enabled' => true
            ],
            'properties' => [
                'id' => [
                    'type' => 'integer'
                ],
                'first_name' => [
                    'type' => 'text',
                    'analyzer' => 'ik_max_word'
                ],
                'last_name' => [
                    'type' => 'text',
                    'analyzer' => 'ik_max_word'
                ],
                'age' => [
                    'type' => 'integer'
                ]
            ]
        ]
    ]
];
$es->indices()->putMapping($params);

五、插入数据

  • 现在数据库和表都有了,可以往里面插入数据了
  • 在ES里面的数据叫文档
  • 可以多插入一些数据,等会可以模拟搜索功能

<?php
$params = [
    'index' => 'autofelix_db',
    'type' => 'autofelix_table',
    //'id' => 1, #可以手动指定id,也可以不指定随机生成
    'body' => [
        'first_name' => '飞',
        'last_name' => '兔',
        'age' => 26
    ]
];
$es->index($params);

六、 查询所有数据

<?php
$data = $es->search();
 
var_dump($data);

七、查询单条数据

  • 如果你在插入数据的时候指定了id,就可以查询的时候加上id
  • 如果你在插入的时候未指定id,系统将会自动生成id,你可以通过查询所有数据后查看其id
<?php
$params = [
    'index' => 'autofelix_db',
    'type' => 'autofelix_table',
    'id' =>  //你插入数据时候的id
];
$data = $es->get($params);

八、搜索

ES精髓的地方就在于搜索

<?php
$params = [
    'index' => 'autofelix_db',
    'type' => 'autofelix_table',
    'body' => [
        'query' => [
            'constant_score' => [ //非评分模式执行
                'filter' => [ //过滤器,不会计算相关度,速度快
                    'term' => [ //精确查找,不支持多个条件
                        'first_name' => '飞'
                    ]
                ]
            ]
        ]
    ]
];
 
$data = $es->search($params);
var_dump($data);

九、测试代码

基于Laravel环境,包含删除数据库,删除文档等操作

<?php
use Elasticsearch\ClientBuilder;
use Faker\Generator as Faker;
 
/**
 * ES 的 php 实测代码
 */
class EsDemo
{
    private $EsClient = null;
    private $faker = null;
 
    /**
     * 为了简化测试,本测试默认只操作一个Index,一个Type
     */
    private $index = 'autofelix_db';
    private $type = 'autofelix_table';
 
    public function __construct(Faker $faker)
{
        /**
         * 实例化 ES 客户端
         */
        $this->EsClient = ClientBuilder::create()->setHosts(['xxx.xxx.xxx.xxx'])->build();
        /**
         * 这是一个数据生成库
         */
        $this->faker = $faker;
    }
 
    /**
     * 批量生成文档
     * @param $num
     */
    public function generateDoc($num = 100) {
        foreach (range(1,$num) as $item) {
            $this->putDoc([
                'first_name' => $this->faker->name,
                'last_name' => $this->faker->name,
                'age' => $this->faker->numberBetween(20,80)
            ]);
        }
    }
 
    /**
     * 删除一个文档
     * @param $id
     * @return array
     */
    public function delDoc($id) {
        $params = [
            'index' => $this->index,
            'type' => $this->type,
            'id' =>$id
        ];
        return $this->EsClient->delete($params);
    }
 
    /**
     * 搜索文档,query是查询条件
     * @param array $query
     * @param int $from
     * @param int $size
     * @return array
     */
    public function search($query = [], $from = 0, $size = 5) {
//        $query = [
//            'query' => [
//                'bool' => [
//                    'must' => [
//                        'match' => [
//                            'first_name' => 'Cronin',
//                        ]
//                    ],
//                    'filter' => [
//                        'range' => [
//                            'age' => ['gt' => 76]
//                        ]
//                    ]
//                ]
//
//            ]
//        ];
        $params = [
            'index' => $this->index,
//            'index' => 'm*', #index 和 type 是可以模糊匹配的,甚至这两个参数都是可选的
            'type' => $this->type,
            '_source' => ['first_name','age'], // 请求指定的字段
            'body' => array_merge([
                'from' => $from,
                'size' => $size
            ],$query)
        ];
        return $this->EsClient->search($params);
    }
 
    /**
     * 一次获取多个文档
     * @param $ids
     * @return array
     */
    public function getDocs($ids) {
        $params = [
            'index' => $this->index,
            'type' => $this->type,
            'body' => ['ids' => $ids]
        ];
        return $this->EsClient->mget($params);
    }
 
    /**
     * 获取单个文档
     * @param $id
     * @return array
     */
    public function getDoc($id) {
        $params = [
            'index' => $this->index,
            'type' => $this->type,
            'id' =>$id
        ];
        return $this->EsClient->get($params);
    }
 
    /**
     * 更新一个文档
     * @param $id
     * @return array
     */
    public function updateDoc($id) {
        $params = [
            'index' => $this->index,
            'type' => $this->type,
            'id' =>$id,
            'body' => [
                'doc' => [
                    'first_name' => '张',
                    'last_name' => '三',
                    'age' => 99
                ]
            ]
        ];
        return $this->EsClient->update($params);
    }
 
    /**
     * 添加一个文档到 Index 的Type中
     * @param array $body
     * @return void
     */
    public function putDoc($body = []) {
        $params = [
            'index' => $this->index,
            'type' => $this->type,
            // 'id' => 1, #可以手动指定id,也可以不指定随机生成
            'body' => $body
        ];
        $this->EsClient->index($params);
    }
 
    /**
     * 删除所有的 Index
     */
    public function delAllIndex() {
        $indexList = $this->esStatus()['indices'];
        foreach ($indexList as $item => $index) {
            $this->delIndex();
        }
    }
 
    /**
     * 获取 ES 的状态信息,包括index 列表
     * @return array
     */
    public function esStatus() {
        return $this->EsClient->indices()->stats();
    }
 
    /**
     * 创建一个索引 Index (非关系型数据库里面那个索引,而是关系型数据里面的数据库的意思)
     * @return void
     */
    public function createIndex() {
        $this->delIndex();
        $params = [
            'index' => $this->index,
            'body' => [
                'settings' => [
                    'number_of_shards' => 2,
                    'number_of_replicas' => 0
                ]
            ]
        ];
        $this->EsClient->indices()->create($params);
    }
 
    /**
     * 检查Index 是否存在
     * @return bool
     */
    public function checkIndexExists() {
        $params = [
            'index' => $this->index
        ];
        return $this->EsClient->indices()->exists($params);
    }
 
    /**
     * 删除一个Index
     * @return void
     */
    public function delIndex() {
        $params = [
            'index' => $this->index
        ];
        if ($this->checkIndexExists()) {
            $this->EsClient->indices()->delete($params);
        }
    }
 
    /**
     * 获取Index的文档模板信息
     * @return array
     */
    public function getMapping() {
        $params = [
            'index' => $this->index
        ];
        return $this->EsClient->indices()->getMapping($params);
    }
 
    /**
     * 创建文档模板
     * @return void
     */
    public function createMapping() {
        $this->createIndex();
        $params = [
            'index' => $this->index,
            'type' => $this->type,
            'body' => [
                $this->type => [
                    '_source' => [
                        'enabled' => true
                    ],
                    'properties' => [
                        'id' => [
                            'type' => 'integer'
                        ],
                        'first_name' => [
                            'type' => 'text',
                            'analyzer' => 'ik_max_word'
                        ],
                        'last_name' => [
                            'type' => 'text',
                            'analyzer' => 'ik_max_word'
                        ],
                        'age' => [
                            'type' => 'integer'
                        ]
                    ]
                ]
            ]
        ];
        $this->EsClient->indices()->putMapping($params);
        $this->generateDoc();
    }
}
]]>
https://www.askme-121.space/php-elasticsearch/feed/ 0