推荐一款开源一站式SQL审核查询平台!功能强大、安全可靠! - 狂师 - 博客园

mikel阅读(186)

来源: 推荐一款开源一站式SQL审核查询平台!功能强大、安全可靠! – 狂师 – 博客园

1、前言

在当今这个数据驱动的时代,数据库作为企业核心信息资产的载体,其重要性不言而喻。随着企业业务规模的不断扩大,数据库的数量和种类也日益增多,这对数据库的管理与运维工作提出了前所未有的挑战。在这样的背景下,一款高效、易用的数据库管理工具显得尤为重要。Archery,作为一款开源的数据库管理平台,以其独特的魅力成为了众多开发者和运维工程师的得力助手,如同古代战场上的利箭,精准而有力地击中了数据库管理的痛点。

2、Archery介绍

Archery是一个基于Python Flask开发的数据库管理平台,它支持多种数据库(如MySQL、PostgreSQL、MariaDB、Redis等),提供了包括数据库查询、管理、权限控制、SQL审核、数据备份与恢复等功能在内的全方位数据库管理解决方案。其设计初衷是为了简化数据库管理流程,提高运维效率,同时保证数据的安全性和可靠性。

项目地址

https://github.com/hhyo/Archery
https://gitee.com/rtttte/Archery

3、核心功能解析

  1. 数据库管理
    Archery通过直观的Web界面,允许用户轻松管理多个数据库实例。用户可以添加、删除、修改数据库连接信息,实时查看数据库状态,以及执行诸如数据库备份、恢复、优化等高级操作。这一功能极大地降低了数据库管理的门槛,使得即便是非专业的运维人员也能快速上手。
  2. SQL查询与审核
    Archery内置了强大的SQL编辑器,支持语法高亮、自动补全等功能,极大提升了SQL编写和调试的效率。同时,它还提供了SQL审核功能,通过预设的规则库对SQL语句进行自动化审查,帮助用户及时发现并纠正潜在的风险,保障数据库操作的安全性和合规性。
  3. 权限控制
    Archery支持细粒度的权限控制策略,可以针对不同的用户或用户组设置不同的操作权限。这一功能有效防止了数据泄露和误操作的风险,保障了数据的安全性和隐私性。
  4. 数据备份与恢复
    数据备份是数据库运维中不可或缺的一环。Archery提供了自动化的数据备份和恢复功能,用户可以自定义备份策略,实现定时备份、增量备份等需求。同时,当数据发生丢失或损坏时,用户可以迅速通过备份文件恢复数据,保障业务的连续性。
  5. 可视化监控
    Archery还集成了数据库性能监控功能,通过图表和报告的形式展示数据库的运行状态、性能指标等关键信息。这使得运维人员能够实时监控数据库的健康状况,及时发现并解决潜在的问题。

4、优势与应用场景

优势

  • 开源免费:Archery作为一款开源项目,其源代码完全公开,用户可以自由下载、使用并根据实际需求进行修改和扩展。
  • 易于部署:Archery支持Docker等容器化部署方式,简化了部署流程,降低了部署门槛。
  • 功能全面:涵盖了数据库管理的各个方面,满足了从日常运维到高级管理的各种需求。
  • 安全性高:通过权限控制、SQL审核等功能保障了数据的安全性和合规性。

应用场景

  • 中小型企业:对于资源有限的中小型企业而言,Archery提供了一种低成本、高效率的数据库管理解决方案。
  • 互联网企业:面对海量数据和复杂的数据库环境,Archery的自动化管理和监控功能能够显著提升运维效率。
  • 金融、医疗等敏感行业:这些行业对数据的安全性和合规性要求极高,Archery的权限控制和SQL审核功能能够有效保障数据的安全性。

5、安装与使用

Archery提供了容器化部署和非容器化部署两种模式,部署较为简单,在此不过多介绍,可参照如下部署文档即可

1、容器化部署:

https://github.com/hhyo/archery/wiki/docker

2、非容器化部署:

https://github.com/hhyo/archery/wiki/manual

3、在线体验地址 : https://demo.archerydms.com/
在这里插入图片描述
账号和密码为:archer/archer

6、小结

Archery作为一款开源的数据库管理平台,以其丰富的功能、易于部署的特点和强大的性能赢得了广泛的关注和好评。它如同一把精准的利箭,穿透了数据库管理的重重迷雾,为数据库的管理与运维工作带来了前所未有的便利和效率。未来,随着技术的不断进步和应用的不断深化,Archery有望成为更多企业和个人首选的数据库管理工具。

使用C#爬取快手作者主页,并下载视频/图集(附源码及软件下载链接) - 猫叔Vincent - 博客园

mikel阅读(266)

来源: 使用C#爬取快手作者主页,并下载视频/图集(附源码及软件下载链接) – 猫叔Vincent – 博客园

最近发现一些快手的作者,作品还不错,出于学习研究的目的,决定看一下怎么爬取数据。现在网上有一些爬虫工具,不过大部分都失效了,或者不开源。于是自己就写了一个小工具。先看一下成果:
image
image
软件只需要填写作者uid以及网页版的请求Cookie,即可实现自动下载,下载目录在程序根目录下的Download文件夹。
由于快手的风控比较厉害,软件也做了应对措施。不过需要用户点击软件中的提示文字,复制粘贴到浏览器,把请求的json保存到本地文件。使用软件提供的解析本地json按钮解析下载即可。如果返回的json文件很短或者没有数据,需要在快手的任意一个页面刷新一下,也就是告诉快手风控,现在是正常浏览,没有机器人的行为。

下面说一下构建整个App的思路。

1. 快手网页端准备

  1. 打开https://live.kuaishou.com/ ,在顶部搜索你要爬取的作者昵称,进入作者主页。也可以从App端分享作者的主页链接,粘贴进来。作者主页加载完成后,地址栏的地址一定要是类似:https://live.kuaishou.com/profile/xxxxxx。 后面的xxxxxx就是作者的user id。这个记住,复制出来,后面会用到。
  2. 按F12打开浏览器的开发者工具(我之前就说过开发者工具是好东西,研究爬虫必备,一定要好好学习)。
  3. 选择开发者工具顶部的“网络”,“全部”,如图所示。在请求列表中找到user id,点击它,右面就会出来请求的标头。里面有个Cookie,需要记住,复制出来。如果没有的话,记得刷新页面。
    image
  4. 在列表里面可以看到很多请求,我们需要从中找到网页端展示作品列表的那条请求,即public开头的,或者直接在左上角搜索public,即可过滤绝大部分无关请求。这个请求的响应数据里面有作者作品的完整json响应。
    image

你可以右击它,在新标签页面打开,打开后地址栏会显示完成的浏览器请求地址。这个网址需要记住,后续会用到。那个count默认是12或者20,我们用到时候,直接拉满,9999即可。
image
image

2. Postman拦截请求,模拟请求,并生成C#请求代码

  1. 安装postman interceptor拦截器,安装地址https://chromewebstore.google.com/detail/postman-interceptor/aicmkgpgakddgnaphhhpliifpcfhicfo 不得不说,这又是一个神器,搭配开发者工具,理论上可以搞定几乎所有的爬虫需求了。
  2. 打开Postman,点击右下角的Start Proxy,
    image
    开启拦截后,重新回到网页版作者主页,刷新一下页面,等页面加载完成后,点击停止拦截。否则列表会一直增多,因为他会拦截电脑的所有网络请求。这时Postman拦截器就会拦截到一大堆请求,同理,找到public请求,或者在左上角输入public,即可过滤出来我们需要的。
    image
    点击这个请求链接
    image
    这是Postman会打开一个新的窗口,包含了请求这个链接的所有参数以及标头信息。
    image
    点击Postman最右面的代码工具即可生成我们需要的代码。你可以选择C#、python、js、curl等等。
    image

3. 使用WPF写界面以及下载逻辑

  1. 新建WPF工程,为了界面好看,这次我用了开源的WPF UI,之前用过HandyControl、MicaWPF,这些都是不错的UI控件库。
    下载使用了开源的Downloader,请求使用了RestSharp,解析Json使用NewtonsoftJson,另外推荐一个免费的图标库FlatIcon。
    界面如下:
点击查看代码
  1. 后台逻辑没有使用MVVM,就是图方便。
点击查看代码
  1. 下载类,下载完文件后,将文件的日志修改为发表日志,方便排序以及数据分析。
点击查看代码
  1. 源码分享
    完整版代码已上传到Github https://github.com/hupo376787/KuaishouDownloader ,喜欢的点一下Star谢谢。

4. 下载使用

打开https://github.com/hupo376787/KuaishouDownloader/releases/tag/1.0,点击下载zip文件,解压缩后,就可以像开头那样使用了。
image
image

ThinkPHP5中find()和select()区别 - 范仁义 - 博客园

mikel阅读(133)

来源: ThinkPHP5中find()和select()区别 – 范仁义 – 博客园

读取数据是指读取数据表中的一行数据(或者关联数据),主要通过find方法完成,例如:

1
2
3
4
$User = M("User"); // 实例化User对象
// 查找status值为1name值为think的用户数据 
$data $User->where('status=1 AND name="thinkphp"')->find();
dump($data);

find方法查询数据的时候可以配合相关的连贯操作方法,其中最关键的则是where方法,如何使用where方法我们会在查询语言章节中详细描述。

如果查询出错,find方法返回false,如果查询结果为空返回NULL,查询成功则返回一个关联数组(键值是字段名或者别名)。 如果上面的查询成功的话,会输出:

1
2
3
4
array (size=3)
  'name' => string 'thinkphp' (length=8)
  'email' => string 'thinkphp@gmail.com' (length=18)
  'status'=> int 1

即使满足条件的数据不止一个,find方法也只会返回第一条记录(可以通过order方法排序后查询)。

还可以用data方法获取查询后的数据对象(查询成功后)

1
2
3
4
$User = M("User"); // 实例化User对象
// 查找status值为1name值为think的用户数据 
$User->where('status=1 AND name="thinkphp"')->find();
dump($User->data());

读取数据集

读取数据集其实就是获取数据表中的多行记录(以及关联数据),使用select方法,使用示例:

1
2
3
$User = M("User"); // 实例化User对象
// 查找status值为1的用户数据 以创建时间排序 返回10条数据
$list $User->where('status=1')->order('create_time')->limit(10)->select();

如果查询出错,select的返回值是false,如果查询结果为空,则返回NULL,否则返回二维数组。

复制代码
$about=M('document');
$abouts=$about->where('id=2')->select();
$abouts2=$about->where('id=2')->find();
var_dump($abouts);
var_dump($abouts2);
复制代码

输出结果:

复制代码
array (size=1)
  0 => 
    array (size=24)
      'id' => string '2' (length=1)
      'uid' => string '1' (length=1)
      'name' => string '' (length=0)
      'title' => string '公司简介' (length=12)
      'category_id' => string '39' (length=2)
      'group_id' => string '0' (length=1)
      'description' => string '公司简介公司简介公司简介公司简介公司简介公司简介公司简介公司简介公司简介公司简介公司简介公司简介公司简介' (length=156)
      'root' => string '0' (length=1)
      'pid' => string '0' (length=1)
array (size=24)
  'id' => string '2' (length=1)
  'uid' => string '1' (length=1)
  'name' => string '' (length=0)
  'title' => string '公司简介' (length=12)
  'category_id' => string '39' (length=2)
  'group_id' => string '0' (length=1)
  'description' => string '公司简介公司简介公司简介公司简介公司简介公司简介公司简介公司简介公司简介公司简介公司简介公司简介公司简介' (length=156)
  'root' => string '0' (length=1)
  'pid' => string '0' (length=1)
复制代码

从上面的代码可以看出,find()返回一个一维数组select()返回一个二维数组,所以在取值时有所不同,一维数组取值用data[id]data[“id”],二维数组取值用data[0][“id”],由于一开始没了解这个用法,调试一天也取不值,最后有var_dump()方法才看到两个方法的不同所在!

复制代码
        $about=M('document');
        $abouts=$about->where('id=2')->select();
        $abouts2=$about->where('id=2')->find();
//        var_dump($abouts);
//        var_dump($abouts2);
         if($abouts){
            $article = M('document_article');
            //$info = $article->find($abouts['id']);
            $info=$article->where('id='.$abouts2['id'])->find();
        }
        $this->assign('wzjj',$info);
复制代码

 

这是DDD建模最难的部分(其实很简单) - 老肖想当外语大佬 - 博客园

mikel阅读(158)

来源: 这是DDD建模最难的部分(其实很简单) – 老肖想当外语大佬 – 博客园

本文书接上回《为了落地DDD,我是这样“PUA”大家的》 ,欢迎关注公众号(老肖想当外语大佬),加群、获取最新文章更新和DDD框架源码,视频和直播在B站。
https://mp.weixin.qq.com/s/DjC0FSWY1bgJyLPIND5evA
 

什么是最重要的事

如果你认真读过前面的文章,那么一定知道我们的核心逻辑:领域驱动是一种价值观,这个价值观是:“领域(边界)”的明确是软件设计掌控复杂度最重要的事。
那么整个软件交付过程中,架构师的职责就是持续保持“需求”、“模型”、“代码”三者的边界范围明确且一致。再回过头看整个软件交付存在的意义,就是为了满足需求,因此本质上来说,“需求”的边界就决定了一切,那么结论就不言而喻了:
最重要的事,就是需求的边界(范围)。
 

什么叫边界明确

假如说我有两个需求A和B,那么我们建模的时候就有如下几种结果,大家感受一下,通常情况下,自己给出的结果是哪个选项?哪个选项又是最理想的?
 

我相信大部分人都会认同,其中B和D是符合边界明确、需求与模型边界一致的原则的,而现实的情况大部分结果是A和C,各种各样的“join”充斥着系统的各个角落,一个典型的例子,就是用户-角色系统的设计。
用户-角色系统,通常会有这样几个关键需求:
  1. 创建用户
  2. 创建角色
  3. 为用户设置角色
  4. 查看用户有哪些角色
  5. 查看一个角色包含多少个用户
我们用最传统的设计方法来做,模型大体是这样的:
 

这样的结果是不是对应到了前面选项A和C比较类似?因为“用户聚合”与“角色聚合”连线的存在,导致需求与模型的边界不一致。
而我们要做的,就是在满足所有需求的同时,消除打破边界的连线。

如何操作

首先我们分析上图,假如我们把下面几个需求先去掉:
  1. 为用户设置角色
  2. 查看用户有哪些角色
  3. 查看一个角色包含多少个用户
那么我们会得到一个符合边界明确原则的设计:
 

然后我们再思考,下面两个需求,应该哪个聚合负责:
  1. 为用户设置角色
  2. 查看用户有哪些角色
答案很显然是“用户聚合”,那么我们可以得到下面的设计:
 

这时你会疑问,如果没有“用户聚合”和“角色聚合”的连线,怎么设置用户有哪些角色呢?
问题的关键,就在这里,通常我们总是会把“关系表”在图中用一条线来表示,那如果我说,“用户聚合”有一个集合属性,叫做“用户角色”,你会认同吗?如果我们知道用户对象有一个集合属性叫“用户角色”,那么是不是上图就很合理?
如果顺着这个思路,我们再来看需求“查看一个角色包含多少个用户”,它应该由哪个模型来解决?我想你已经知道答案了,就是“用户聚合”,最终我们得到如下设计:
 

到此,所有的需求可以满足,需求被划分为两个范围,分别对应两个模型。

为什么说它很难

如果你一直跟着我的思路,完成了上面的过程,那么你会发现,需求的边界不是客观存在的,而是我们主观的划分,这个划分的目的是为了在一个确定的范围内,能够解决这个问题。因为它是主观的,就不可衡量和判断,每个人都可以有自己的划分思路。另外它又是简单的,因为你可以像上图一样,这样划分边界,给出对应的模型解决它,就像在给自己家的袜子分配收纳盒一样简单。
所以,我常常叹息,关于领域驱动设计:
说它难,难的是做出取舍。
说它简单,是因为能明确知道取什么舍什么。

最新AI生成视频工具!效果不输快手可灵,CogVideoX下载介绍 - 老艾的AI世界 - 博客园

mikel阅读(261)

来源: 最新AI生成视频工具!效果不输快手可灵,CogVideoX下载介绍 – 老艾的AI世界 – 博客园

要说AI生成视频最火的项目,当属国产的快手可灵了,甚至比OpenAI的Sora还要火,前者还是个ppt,可灵已经在落地公测了,博主在前段时间申请试用通道的时候,竟然排到几十万人开外的位置,好在最后还是拿到了使用资格,还没用上的外国友人只能干着急,在社交媒体发「求求了!」

 

但就在最近,可灵开始收费了,而且年费要大几千,着实有点儿压力~

就在几天前,对标快手可灵的CogVideoX横空出世了,还可以部署到本地电脑使用,从下图可以看到其效果甚至不输快手可灵

 

CogVideoX最新中文版:

百度网盘:https://pan.baidu.com/s/1b2dS7Wj6-yta7xo7NrjihQ?pwd=7hmo

 

CogVideoX的提示词上限为226个token,视频长度为6秒,帧率为8帧/秒,视频分辨率为720*480。用户只需输入一段文字,就能快速生成充满想象力的视频,还可以自由选择生成的视频风格,包括卡通、3D、黑白、油画、电影等,并配上软件自带的音乐

CogVideoX通过融合文本、时间和空间三个维度,实现了高效、连贯的视频生成,并采用DiT架构和优化算法,显著提升了推理速度和生成质量

工作流程

· 数据预处理:将输入的图像或视频数据转换为模型可以处理的格式,将图像切分成固定大小的patches(小块),然后将这些patches转换为特征向量

· 噪声引入:在数据预处理后的特征向量上逐步引入噪声,形成一个噪声增加的扩散过程,这个过程可以视为从原始数据到噪声数据的转换

· 模型训练:使用引入了噪声的特征向量作为输入,训练Transformer模型,模型的目标是学习如何逆转噪声增加的过程,即从噪声数据恢复出原始数据

· 视频生成:在模型训练完成后,通过输入随机生成的噪声和原始数据的映射关系到模型中,经过模型的处理后生成新的图像或视频

使用方法

1.点击软件界面右下角的load按钮,选择项目自带的workflow.json文件并加载

2.在新打开的界面中,输入待生成视频的提示词

3.设置参数(一般保持默认)

4.点击Queue Prompt按钮

稍微等待一下,视频就生成好了,甄嬛传中皇上啃鸡腿这段我怎么没见过?

 

注意事项

①项目安装路径不要包含中文

②推荐使用GTX1070以上显卡运行此项目

③使用过程中若不慎关闭软件后台,请重新打开,并刷新网页

使li标签横向排列的三种方式_li横向排列-CSDN博客

mikel阅读(144)

来源: 使li标签横向排列的三种方式_li横向排列-CSDN博客

一、display:inline

通过把li转换为行内元素实现,行内元素不独占一行,根据内容撑开大小

 

 

 

 

二、float:left

通过让li浮动实现横向排列 ,浮动会使li脱离文本流,且不占位置

 

 

三。通过flex布局实现

首先给父级定义flex,li{ flex:1}的意思为独占父级宽度的n分之1,这里的n取决于li的个数,也就是4分之1

 

 

————————————————

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。

原文链接:https://blog.csdn.net/m0_61685348/article/details/120694044

深入理解 PHP 高性能框架 Workerman 守护进程原理 - Yxh_blogs - 博客园

mikel阅读(141)

来源: 深入理解 PHP 高性能框架 Workerman 守护进程原理 – Yxh_blogs – 博客园

大家好,我是码农先森。

守护进程顾名思义就是能够在后台一直运行的进程,不会霸占用户的会话终端,脱离了终端的控制。相信朋友们对这东西都不陌生了吧?如果连这个概念都还不能理解的话,建议回炉重造多看看 Linux 进程管理相关的基础知识。在我们日常的编程中常见有类似 php think ...php artisan ...php yii ... 等命令启动需要一直执行的任务,都会通过 nohup 挂载到后台保持长期运行的状态。同样在 Workerman 中也是使用类似 php index.php start 的命令来启动进程,但不同的是它不需要利用 nohup 便可以挂载到后台运行。那有些朋友就会好奇它是怎么实现的呢?为了解决朋友们的疑惑,我们今天就重点深入分析一下 Workerman 守护进程的实现原理。

我们先了解一些进程相关的知识:

  • 父进程:父进程是生成其他进程的进程。当一个进程创建了另一个进程时,创建者被称为父进程,而被创建的进程则成为子进程。父进程可以通过进程标识符(PID)来识别它所创建的子进程。
  • 子进程:子进程是由父进程创建的新进程。子进程继承了父进程的一些属性,例如环境变量、文件描述符等。子进程独立于父进程运行,它可以执行自己的代码,并且具有自己的资源和内存空间。
  • 进程组:进程组是一组相关联的进程的集合。每个进程组都有一个唯一的进程组ID(PGID),用于标识该进程组。进程组通常由一个父进程创建,并且包含了与父进程具有相同会话ID(SID)的所有子进程。
  • 会话:会话是一组关联进程的集合,通常由用户登录到系统开始,直至用户注销或关闭终端会话结束,一个会话中的进程共享相同的控制终端。每个会话都有一个唯一的会话ID(SID),用于标识该会话。会话通常包含一个或多个进程组,其中第一个进程组成为会话的主进程组。

这些概念俗称八股文,向来都不怎么好理解,那我们来看个例子。执行了命令 php index.php 便产生了进程 61052「该进程的父进程是 Bash 进程 8243,这里不用管它」,然后通过 Fork 创建了子进程 61053 且其父进程就是 61052,这两个进程拥有共同的进程组 61052 和会话 8243。调用 posix_setsid 函数,将会为子进程 61053 开启新的进程组 61053 和新的会话 61053,这里的会话可以理解为一个新的命令窗口终端。最后子进程 61053 通过 Fork 创建了子进程 61054,进程 61053 升级成了父进程,这里再次 Fork 的原因是要避免被终端控制进程所关联,这个进程 61052 是在终端的模式下创建的,自此进程 61054 就形成了守护进程。

Copy
[manongsen@root phpwork]$ php index.php
[parent] 进程ID: 61052, 父进程ID: 8243, 进程组ID: 61052, 会话ID: 8243 
[parent1] 进程ID: 61052, 父进程ID: 8243, 进程组ID: 61052, 会话ID: 8243 退出了该进程
[child1] 进程ID: 61053, 父进程ID: 61052, 进程组ID: 61052, 会话ID: 8243 
[child1] 进程ID: 61053, 父进程ID: 61052, 进程组ID: 61053, 会话ID: 61053 
[parent2] 进程ID: 61053, 父进程ID: 61052, 进程组ID: 61053, 会话ID: 61053 退出了该进程
[child2] 进程ID: 61054, 父进程ID: 61053, 进程组ID: 61053, 会话ID: 61053 保留了该进程

[manongsen@root phpwork]$ ps aux | grep index.php
root             66064   0.0  0.0 408105040   1472 s080  S+   10:00下午   0:00.00 grep index.php
root             61054   0.0  0.0 438073488    280   ??  S    10:00下午   0:00.00 php index.php

上面举例的进程信息,正是这段代码运行所产生的。如果看了这段代码且细心的朋友,会发现为什么 posix_setsid 这个函数不放在第一次 Fork 前调用,而在第二次 Fork 前调用呢,这样的话就不用 Fork 两次了?原因是组长进程是不能创建会话的,进程组ID 61052 和进程ID 61052 相同「即当前进程则为组长进程」,所以需要子进程来创建新的会话,这一点需要特别注意一下。

Copy
<?php

function echoMsg($prefix, $suffix="") {
    // 进程ID
    $pid = getmypid(); 
    // 进程组ID
    $pgid = posix_getpgid($pid);
    // 会话ID
    $sid = posix_getsid($pid); 
    // 父进程ID
    $ppid = posix_getppid();

    echo "[{$prefix}] 进程ID: {$pid}, 父进程ID: {$ppid}, 进程组ID: {$pgid}, 会话ID: {$sid} {$suffix}" . PHP_EOL;
}

// [parent] 进程ID: 61052, 父进程ID: 8243, 进程组ID: 61052, 会话ID: 8243
echoMsg("parent");

// 第一次 Fork 进程  
$pid = pcntl_fork();
if ( $pid < 0 ) {
    exit('fork error');
} else if( $pid > 0 ) {
    // [parent1] 进程ID: 61052, 父进程ID: 8243, 进程组ID: 61052, 会话ID: 8243 退出了该进程
    echoMsg("parent1", "退出了该进程");
    exit;
}

// 创建的 子进程ID 为 61053 但 进程组、会话 还是和父进程是同一个
// [child1] 进程ID: 61053, 父进程ID: 61052, 进程组ID: 61052, 会话ID: 8243 
echoMsg("child1");

// 调用 posix_setsid 函数,会创建一个新的会话和进程组,并设置 进程组ID 和 会话ID 为该 进程ID
if (-1 === \posix_setsid()) {
    throw new Exception("Setsid fail");
}

// 现在会发现 进程组ID 和 会话ID 都变成了 61053 在这里相当于启动了一个类似 Linux 终端下的会话窗口
// [child1] 进程ID: 61053, 父进程ID: 61052, 进程组ID: 61053, 会话ID: 61053 
echoMsg("child1");

// 第二次 Fork 进程
// 这里需要二次 Fork 进程的原因是避免被终端控制进程所关联,这个进程 61052 是在终端的模式下创建的
// 需要脱离这个进程 61052 以确保守护进程的稳定
$pid = pcntl_fork();
if ( $pid  < 0 ){
    exit('fork error');
} else if( $pid > 0 ) {
    // [parent2] 进程ID: 61053, 父进程ID: 61052, 进程组ID: 61053, 会话ID: 61053 退出了该进程
    echoMsg("parent2", "退出了该进程");
    exit;
}

// 到这里该进程已经脱离了终端进程的控制,形成了守护进程
// [child2] 进程ID: 61054, 父进程ID: 61053, 进程组ID: 61053, 会话ID: 61053 保留了该进程
echoMsg("child2", "保留了该进程");

sleep(100);

有时间的朋友最好自行执行代码并分析一遍,会有不一样的收获。这里假装你已经实践过了,这下我们来看 Workerman 的 Worker.php 文件中 554 行的 runAll 方法中的 static::daemonize() 这个函数,实现的流程逻辑和上面的例子几乎一样。不过这里还使用了 umask 这个函数,其主要的作用是为该进程所创建的文件或目录赋予相应的权限,保证有权限操作文件或目录。

Copy
// workerman/Worker.php:554
/**
 * Run all worker instances.
 * 运行进程
 * @return void
 */
public static function runAll()
{
    static::checkSapiEnv();
    static::init();
    static::parseCommand();
    static::lock();
    // 创建进程并形成守护进程
    static::daemonize();
    static::initWorkers();
    static::installSignal();
    static::saveMasterPid();
    static::lock(\LOCK_UN);
    static::displayUI();
    static::forkWorkers();
    static::resetStd();
    static::monitorWorkers();
}

// workerman/Worker.php:1262
/**
 * Run as daemon mode.
 * 使用守护进程模式运行
 * @throws Exception
 */
protected static function daemonize()
{
	// 判断是否已经是守护状态、以及当前系统是否是 Linux 环境
    if (!static::$daemonize || static::$_OS !== \OS_TYPE_LINUX) {
        return;
    }
    
    // 设置 umask 为 0 则当前进程创建的文件权限都为 777 拥有最高权限
    \umask(0);
    
    // 第一次创建进程
    $pid = \pcntl_fork();
    if (-1 === $pid) {
    	// 创建进程失败
        throw new Exception('Fork fail');
    } elseif ($pid > 0) {
    	// 主进程退出
        exit(0);
    }

	// 子进程继续执行...
    // 调用 posix_setsid 函数,可以让进程脱离父进程,转变为守护进程
    if (-1 === \posix_setsid()) {
        throw new Exception("Setsid fail");
    }

	// 第二次创建进程,在基于 System V 的系统中,通过再次 Fork 父进程退出
	// 保证形成的守护进程,不会成为会话首进程,不会拥有控制终端
    $pid = \pcntl_fork();
    if (-1 === $pid) {
    	// 创建进程失败
        throw new Exception("Fork fail");
    } elseif (0 !== $pid) {
    	// 主进程退出
        exit(0);
    }

    // 子进程继续执行...
}

守护进程也是 Workerman 中重要的一部分,它保障了 Workerman 进程的稳定性。不像我们通过 nohup 启动的命令,挂起到后台之后,有时还神不知鬼不觉的就挂了,朋友们或许都有这样的经历吧。当然在市面上也有一些开源的守护进程管理软件,比如 supervisor 等,其次还有人利用会话终端 screen、tmux 等工具来实现。其实守护进程的实现方式有多种多样,我们这里只是为了分析 Workerman 中守护进程的实现原理,而引出了在 PHP 中实现守护进程模式的例子,希望本次的内容能对你有所帮助。

感谢大家阅读,个人观点仅供参考,欢迎在评论区发表不同观点。

【干货】ThinkPHP6对接微信扫码登录_thinkphp6 微信扫码登陆-CSDN博客

mikel阅读(141)

来源: 【干货】ThinkPHP6对接微信扫码登录_thinkphp6 微信扫码登陆-CSDN博客

在近几年的互联网网站中,使用微信登录的场景可是越来越多。据统计2020年,全球微信高达11亿,也确实如此,微信这个好用的社交工具,可以说小到一个小学生大到你的七大姑八大姨,很多人可能没有QQ,但他一定有微信。所以微信登录是程序员必备的一项工作技能。

微信扫码登录对接ThinkPHP6,话不多说,直接上车。

一、准备资料:

1、访问 https://open.weixin.qq.com/,注册账户。

2、开发者认证:企业。

3、创建一个网站应用:网站域名必须备案(可使用二级域名),获得相应的AppID和AppSecret,申请微信登录且通过审核。

二、接入微信登录步骤:

首先看下微信官网给出的步骤说明:

https://developers.weixin.qq.com/doc/oplatform/Website_App/WeChat_Login/Wechat_Login.html

第三方发起微信授权登录请求,微信用户允许授权第三方应用后,微信会拉起应用或重定向到第三方网站,并且带上授权临时票据code参数;

通过code参数加上AppID和AppSecret等,通过API换取access_token;

通过access_token进行接口调用,获取用户基本数据资源或帮助用户实现基本操作。

三、接入微信登录实操环节:

1、放好微信登录图标,并添加链接。
比如链接到www.a,com/index/user/weixindenglu。下面我们看下weixindenglu方法代码。

public function weixindenglu(){

$appid=’wx868f988d79a4f2bb’;

$redirect_uri=urldecode(‘http://www.dongpaiweb.cn/index/index/weixin.html’);

$url=’https://open.weixin.qq.com/connect/qrconnect?appid=’.$appid.’&redirect_uri=’.$redirect_uri.’&response_type=code&scope=snsapi_login&state=STATE#wechat_redirect’;

header(“location:”.$url);

}

这个时候我们点击微信小图标就会出现扫码的界面。拿出手机赶紧微信扫码。

(注意:$redirect_uri是我们的回调地址,意思是用户微信扫码后的处理地址)

2、获得用户的code。
微信扫码后就跳转到了上面定义的回调地址weixin方法。我们看下weixin方法代码:

public function weixin(){
$code=input(‘get.code’);
}

code获取很简单,我们看下打印效果:

3、获得Access Token 和openid,我们在weixin()方法中继续添加代码:

public function weixin(){
$code=input(‘get.code’);
$appid=’wx868f988d79a4f25b’;
$appsecret=’82b426f2882b6a1398b8312cc1de037b’;
$url=’https://api.weixin.qq.com/sns/oauth2/access_token?appid=’.$appid.’&secret=’.$appsecret.’&code=’.$code.’&grant_type=authorization_code’;

//json对象变成数组
$res=json_decode(file_get_contents($url),true);
$access_token=$res[‘access_token’];
$openid=$res[‘openid’];
}

这样我们就获取到了access_token和openid,我们看下打印效果:

5、获取用户全部信息,我们在weixin()方法中继续添加代码:

public function weixin(){

$code=input(‘get.code’);
$appid=’wx868f988d79a4f25b’;
$appsecret=’82b426f2882b6a1398b8312cc1de037b’;
$url=’https://api.weixin.qq.com/sns/oauth2/access_token?appid=’.$appid.’&secret=’.$appsecret.’&code=’.$code.’&grant_type=authorization_code’;

//json对象变成数组

$res=json_decode(file_get_contents($url),true);
$access_token=$res[‘access_token’];
$openid=$res[‘openid’];

$urlyonghu=’https://api.weixin.qq.com/sns/userinfo?access_token=’.$access_token.’&openid=’.$openid;
$user=json_decode(file_get_contents($urlyonghu),true);
print_r($user);

}

这样我们就获取了用户的昵称、地址、头像等信息,看下打印效果:
在我们获取用户微信信息后,我们就可以整理数据入库了。

如果是用户第一次登录,我们可以设置绑定手机号的界面,绑定手机号后即算是注册成功。如果我们检测到已经绑定手机号,即代表登录成功跳转到成功界面。

以上就是ThinkPHP6对接微信扫码登录的操作步骤。加薪升职,赶紧get此技能!
————————————————

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。

原文链接:https://blog.csdn.net/wang_xiaoshen/article/details/106709083

C#/.NET/.NET Core优秀项目和框架2024年7月简报 - 追逐时光者 - 博客园

mikel阅读(163)

来源: C#/.NET/.NET Core优秀项目和框架2024年7月简报 – 追逐时光者 – 博客园

前言

公众号每月定期推广和分享的C#/.NET/.NET Core优秀项目和框架(每周至少会推荐两个优秀的项目和框架当然节假日除外),公众号推文中有项目和框架的介绍、功能特点、使用方式以及部分功能截图等(打不开或者打开GitHub很慢的同学可以优先查看公众号推文,文末一定会附带项目和框架源码地址)。注意:排名不分先后,都是十分优秀的开源项目和框架,每周定期更新分享。

简报GitHub开源地址:https://github.com/YSGStudyHards/DotNetGuide/blob/main/docs/DotNet/DotNetProjectMonthly.md

优秀项目和框架精选:https://github.com/YSGStudyHards/DotNetGuide/blob/main/docs/DotNet/DotNetProjectPicks.md

QrCodeGenerator

WPF UI

IoTClient

N_m3u8DL-RE

SuperShortLink

Dommel

Flurl

  • 项目简介: Flurl是一个集现代性、流畅性、异步性、可测试性、可移植性于一身的URL构建器与HTTP客户端库。它提供了简洁的API,使得HTTP请求的发送与URL的构建变得极为简单与直观。无论是构建复杂的URL路径,还是设置查询参数、请求头或认证信息,Flurl都能以几乎零学习成本的方式实现。
  • 项目源码地址: https://github.com/tmenier/Flurl
  • 公众号详细介绍: https://mp.weixin.qq.com/s/C8dgCdgd5nwLeZvirSqNDw

DBCHM

ML.NET

  • 项目简介: 一个.NET开源、免费、跨平台(支持Windows、Linux、macOS多个操作系统)的机器学习框架,ML.NET 允许开发人员在其 .NET 应用程序中轻松构建、训练、部署和使用自定义模型,而无需具备开发机器学习模型的专业知识或使用 Python 或 R 等其他编程语言的经验。该框架提供从文件和数据加载的数据。数据库,支持数据转换,并包含许多机器学习算法。
  • 项目源码地址: https://github.com/dotnet/machinelearning
  • 公众号详细介绍: https://mp.weixin.qq.com/s/TMC6Dzk-k_Xs13THNNhbbA

Ant Design Blazor

ShardingCore

  • 项目简介: ShardingCore是一款开源、简单易用、高性能、普适性,针对EF Core生态下的分表分库的扩展解决方案,支持EF Core2+的所有版本,支持EF Core2+的所有数据库、支持自定义路由、动态路由、高性能分页、读写分离的一款EF Core拓展程序包,一款零依赖第三方组件的扩展。
  • 项目源码地址: https://github.com/dotnetcore/sharding-core
  • 公众号详细介绍: https://mp.weixin.qq.com/s/skI0O03jvhc7hdCwkdZbjw

Terminal.Gui

BootstrapAdmin

DotNetGuide技术社区交流群

  • DotNetGuide技术社区是一个面向.NET开发者的开源技术社区,旨在为开发者们提供全面的C#/.NET/.NET Core相关学习资料、技术分享和咨询、项目框架推荐、求职和招聘资讯、以及解决问题的平台。
  • 在DotNetGuide技术社区中,开发者们可以分享自己的技术文章、项目经验、学习心得、遇到的疑难技术问题以及解决方案,并且还有机会结识志同道合的开发者。
  • 我们致力于构建一个积极向上、和谐友善的.NET技术交流平台。无论您是初学者还是有丰富经验的开发者,我们都希望能为您提供更多的价值和成长机会。

.NET 与 LayUI 实现高效敏捷开发框架 - 小码编匠 - 博客园

mikel阅读(216)

来源: .NET 与 LayUI 实现高效敏捷开发框架 – 小码编匠 – 博客园

前言

WaterCloud 是一个集成了 LayUI 的高效敏捷开发框架,专为 .NET 开发者设计。

它不仅支持多种 .NET 版本(.NET 4.5、.NET Core 3.1、.NET 5、.NET 6),还内置了丰富的功能,如权限管理、流程表单设计以及多数据库支持下的多租户架构。使用了 ORM(SQLSugar 和 Chloe ) 能够轻松应对复杂的数据处理需求。

WaterCloud 基于ASP.NET 6.0 MVC + API + SQLSugar + LayUI的框架,帮我们解决.NET 开发中的重复工作,提升开发效率。

该框架采用了主流的架构模式,易于学习与使用,有效降低了学习成本,欢迎有需求的小伙伴们来试用!

框架介绍

.NET 和 LayUI 集成的最佳实践;

敏捷开发优选框架,自带权限包含字段、数据权限,自带流程表单设计,基于多数据库的多租户等;

项目版本包含.NET 4.5、.NET Core 3.1、.NET 5、.NET 6;

ORM 包含SQLSugar 和 Chloe;

框架特点

  • 完全开源: 源代码开放,便于二次开发和定制。
  • 主流技术栈: 支持 ASP.NET 6.0、LayUI、SqlSugar 等技术。
  • 多数据库兼容: 支持 SQL Server、MySQL 等多种数据库。
  • 模块化设计: 层次结构清晰,便于维护和扩展。
  • 内置功能: 包括权限管理、数据权限控制、代码生成器等企业级功能。
  • 权限管理: 基于 RBAC 的精细权限控制,覆盖从菜单到字段级别的访问控制。
  • 数据权限: 精细化的数据权限管理,确保数据安全。
  • 表单设计器: 提供直观的拖拽式表单设计工具。
  • 流程设计器: 动态设计工作流程,灵活调整业务逻辑。
  • 内容管理: 集成 wangEditor 编辑器,方便内容编辑。
  • 文件管理: 支持文件上传与下载功能。
  • 响应式布局: 支持多种设备访问,包括 PC、平板、手机等。
  • 实用工具: 封装了日志记录、缓存管理、数据验证等功能。
  • 多租户支持: 基于 Database 的多租户功能。
  • 定时任务: 支持基于 Quartz 的定时任务,具备集群能力。
  • 广泛的适用性: 可用于开发OA、ERP、BPM、CRM、WMS、TMS、MIS、BI、电商、物流、快递、教务管理系统等各类管理软件。

框架技术栈

前端技术

1、JavaScript 框架

  • JQuery 3.4.1
  • LayUI
  • LayUI mini (开源)

2、图标

  • Font Awesome 4.7.0
  • LayUI 自带图标

3、控件

  • 客户端验证: LayUI verify
  • 富文本编辑器: wangEditor (开源), LayUI editor
  • 文件上传: LayUI upload
  • 动态页签: LayUI mini miniTab
  • 数据表格: LayUI table, soul-table (已实现后端筛选)
  • 下拉选择框: LayUI select, xmselect
  • 树结构控件: LayUI dtree
  • 树状表格: treetable-lay (兼容 soul-table 组件,修复了固定列等 BUG)
  • 穿梭框: LayUI transfer
  • 日期控件: LayUI laydate
  • 图标选择: LayUI IconPicker
  • 省市区选择: LayUI layarea

4、页面布局

  • LayUI
  • LayUI mini

5、图表插件

  • echarts

后端技术

  • 核心框架: ASP.NET 6.0, WEB API
  • 定时任务: QuartZ (支持 web 控制)
  • 持久层框架SqlSugar (支持多种数据库, 复杂查询, 多租户, 分库分表等);Chloe (支持多种数据库, 复杂查询, 稳定)
  • 安全支持: 过滤器, Sql 注入防护, 防止请求伪造
  • 服务端验证: 实体模型验证
  • 缓存框架: Redis/Memory (支持单点登录控制)
  • 消息队列: RabbitMQ
  • 事件总线: Jaina
  • 日志管理: 日志记录, 登录日志, 操作日志
  • 工具类: MiniExcel, Newtonsoft.Json, 验证码生成, 通用公共类

环境要求

1、VS 2022 及以上版本;

2、ASP.NE 6.0;

3、Mysql 或者 SQL Server 2005及以上版本,database文件夹下有SQL文件可执行;

4、请使用 VS 2022 及以上版本打开解决方案。

5、Redis 和 RabbitMq 在项目文件夹里有

框架演示

1、演示地址http://47.116.127.212:5000/ (登录: admin / 0000; 数据库每两小时恢复一次)

2、在线文档https://gitee.com/qian_wei_hong/WaterCloud/wikis/pages

3、在线项目https://replit.com/@MonsterUncle/WaterCloud

效果页面

1、登录页面

2、首页展示

3、系统管理

4、流程中心

系统还包含了丰富的功能模块,如文件中心、信息中心、内容管理和订单管理等。可以下载源码,并运行体验这些功能。

项目地址

Gitee:https://gitee.com/qian_wei_hong/WaterCloud

最后

如果你觉得这篇文章对你有帮助,不妨点个赞支持一下!你的支持是我继续分享知识的动力。如果有任何疑问或需要进一步的帮助,欢迎随时留言。

也可以加入微信公众号 [DotNet技术匠] 社区,与其他热爱技术的同行一起交流心得,共同成长!