• 某次小红书帮助下的Edu SRC挖掘

      1► 记一次edu挖掘   平时不怎么爱挖src,因此除了项目需要之外(基本全部都是企业的网站),业余几乎不会抽时间去挖挖各大众测平台的src,还没有挖过类似于学校edu这一类的src,于是就想着玩一下,看看有什么区别。   2► 目标定夺   首先是目标的问题,一般对于网站的一些首页,内容太多,所以打起来会比较墨迹,因此这次目标定在了某个学校的智慧校园后台,一般这一类的后台管理系统都会存在类似于弱口令、登录绕过等各种问题,而且功能相对来说也比较简单。 3► 操作手册泄露   在对网站目录进行扫描爆破的途中,发现了一个/doc路径,访问下载出来了一个系统的操作手册,按照以往的经验,这一类的操作手册一般都会存在很多可利用的地方,比如说一些默认的账号密码会写明,或者说后台的一些用户名账号密码可能会被泄露。果不其然,这里虽然后面大部分图片被打上了马赛克,但是依旧有少部分图片是泄露了学生的姓名,账号,管理员的账号admin_g等。 拿到了账号之后,尝试了一轮的暴力破解,因为个人觉得这一类密码应该都是有规律的,可能就是学号或者姓名简写等,很遗憾的并没有,开发人员对登录错误次数限制为了5次,因此并没有能够成功登录到任何一个账号。   4► JS泄露   这里的JS文件有些许奇怪,它以前应该是测试的时候在内网有一个路径Env,从这个Env获取类似于配置的东西,这里应当某个子域名的外网。 尝试访问配置文件的路径/Env,得到了很多的api接口的URL,根据各类接口的命名,可以看到它是通过前后端分离,通过不同的接口域名处理不同角色的逻辑,教师的、学生的,等等。 这是一个Java编写的应用程序,根据经验,类似的api路径接口可能会存在springboot信息泄露或swagger接口文档能够被访问的问题,进行了尝试之后,发现每一个接口的URL路径都存在了swagger接口文档泄露的问题。   5► Swagger接口泄露   swagger接口文档未删除,能够被访问并不意味着一定存在漏洞,当开发人员对网站的各种权限措施等做的足够好时,接口文档也仅是一个接口文档了,只不过能够让攻击者快速摸清楚网站的各类接口和它的作用。在前面几个类似于登录Swagger接口文档并没有找到可利用的点,基本上都返回401 Unauthorized,因为接口很多,逐渐被消磨着耐心,它这里的一些data数据接口甚至进行了IP封锁,直接返回了403。   直至来到处理Student与Teacher接口,通过一个个寻找发现了存在可用的未授权接口,这两个接口能够获取到所有学生、教师的姓名、班级、身份证号等信息。 通过学生的ID号还能够得到学生、老师的详细信息个人信息。 6► 小红书的作用   这时候想起操作文档中泄露的账号,是否可以继续通过这些信息猜测账号密码呢,因为通过文档的说法,是存在着一个默认密码的,但是我这里一直都没有试出来(当然这也与我几乎没有接触过学校类的网站相关,经验实在是太少了),所以我开始尝试其它方式,比如小红书?我在小红书发布了一个帖子,并艾特了这个学校,以求在评论区中得到默认的账号密码。 果不其然小红书起到了它该有的作用,下面有人回复我的贴子,得到了我想到的内容。 剩下最后一个问题就是说,密码是身份证后六位,我该如何得到操作手册中对应用户的身份证呢?于是我尝试了不用操作手册中的账号,我直接通过了身份证号作为账号,后六位作为密码进行了一次登录,登录成功了,尝试了其它的账号,也都成功了,也就意味着这个学校大量的教师和学生账号泄露出来了。 在后台里面能够访问到该学校的其它管理系统,新闻中心,甚至能够看到协同办公的OA等等   当然还存在了其它的接口也存在未授权的问题,比如说成绩的导出等等,泄露了大量学生的成绩信息、身份证号等等。   7► 总结   之所以写下这篇文章,并不是说这次的src有多难,而是觉得这次的src是有意义的B站,小红书,抖音等社交软件也可以成为有效的攻击社工利用手段之一。而对于学校这一类的系统,因为用户量大,学生安全意识并不强的原因,大部分的账号都是默认密码,学生教师等都并不会更改密码。 原文作者:Aiwin文章来源:https://xz.aliyun.com/t/14232      ...

    2024-04-21 技术教程 89
  • 手机端免费版全自动剪辑去重助手

    功能简介: 比剪映好用1w倍的手机剪辑软件 完全免费 无广告 亲测好用 效果拉满 一键全自动处理 直播录制+下载 批量MD5值修改 自带100+剪辑功能 百分百过原创 谁用谁说好! ...

    2024-04-17 技术教程 168
  • 电脑steam怎么下载?(详细下载安装教程)

    对于喜欢玩游戏的小伙伴来说,steam是一个再熟悉不过的平台了,对于不怎么熟悉的小伙伴来说可能只知道这个是一个游戏平台。很多人下载下来的都是假的steam,或者下载下来过后会出现各种错误。那么怎么才能下载并且正常的使用steam呢?下面我们就来和大家分享一下。 准备工作 熟悉steam的小伙伴都知道steam是一个国外游戏平台,在国内并没有专门的服务器。我们想要在国内正常的使用steam必须要用到游戏加速器。游戏加速器的种类很多,比较好用的就有UU加速、小黑盒、瓦特工具箱等,大家可以根据需求进行下载使用。之前小编已经为大家分享过UU加速器的下载方法,感兴趣的朋友可以去看看。 电脑steam怎么下载? 1、当我们下载好加速器,并连接好网络过后。我们打开百度,输入“steam下载”,然后点击搜索。我们会看到搜索出来的第一个结果就是stream的官网了,直接点击进去。 2、打开steam官网过后,我们点击这里的“安装steam”。 3、然后,点击这里的“安装”就可以了。 4、弹出这个安装向导这里直接点击“下一步”。 5、语言的话,我们一般都是选择“简体中文”,然后再点击下一步。 6、安装的时候只有一个需要注意的地方,就是要选择一个正确的安装路径,这里不要选择安装在C盘。因为游戏的话一般都比较大,如果什么都安装在C盘,很快就可以让你的C盘爆了。选择好安装路径过后,点击安装就OK了。 7、现在我们就已经安装好steam了,我们就可以开始注册使用了。 大家在搜索steam官网的时候,在这里一定要看清楚是不是steam官网,否则你下载下来的就可能不是真的steam客户端。如果不确定的话,可以直接复制下面小编为大家提供的steam官网链接到浏览器网址栏打开即可。 Steam官网链接:https://store.steampowered.com/ 今天为大家分享的是如果在电脑端下载steam客户端。如果你很喜欢玩游戏,一定不能错过steam这个游戏平台哦。因为它会给你带来更好的游戏体验。 ...

    2024-04-14 技术教程 190
  • 记一次对天翼安全网关的渗透

    0x00 起因 听大师傅说天翼安全网关可以telnet登录,回去尝试了下发现并没有开放telnet端口,把目标转向如何开启,经过一系列搜索,最后拿下网关。 0x01 信息泄露 我家这个网关型号是 HG261GS,经过一番搜索,发现访问 http://192.168.1.1/cgi-bin/baseinfoSet.cgi可以拿到密码 然后搜索密码的加密方式,得到工程账号的密码,加密算法为加密算法:字母转换为其ASCII码+4,数字直接就是ASCII码 之后登录 0x02 命令执行 在后台转了一圈没发现开启telnet的地方,经过一番搜索,找到这个版本存在命令执行的问题,访问http://192.168.1.1/cgi-bin/telnet.cgi通过InputCmd参数可以直接执行系统命令,然后访问http://192.168.1.1/cgi-bin/telnet_output.log可以看到执行结果,通过js下的telnet.js也可以访问http://192.168.1.1/cgi-bin/submit.cgi得到执行结果   测试一下效果   结果   这样就找到了个rce点,开始的想法是反弹shell,后来发现大部分命令都没有,是个BusyBox,打算用awk弹个shell,又碰见编码问题,试了很多种方法写不进去,这里就想着直接老老实实开启telnet登录,搜索了一波怎么进入系统 硬件牛盖子一拆就能长驱直入了,我也想,但是实力不允许首先先导出原配置文件,在管理-设备管理,插个U盘,备份配置即可 这里导出配置感觉不太合适,万一弄坏了其他人都上不了网了,拆盖子又不会,那么只能继续摸索,转换下思路,既然有配置文件,那么肯定有读取配置文件去开启的功能,直接find搜索所有cgi文件InputCmd=find%20/%20-name%20"*cgi*",找到一个名叫telnetenable.cgi的文件,这不就是开启telnet的东西嘛   直接读取内容,找到开启方法   发现还是不能连接,经过排查,网关开启了防火墙过滤,阻止了23端口的连接,这里找到对应的iptables链和规则行号,直接删除,比如要删除INPUT链的第三条规则,可以执行iptables -D INPUT 3,然后添加23端口允许通过,效果如下   0x03 添加用户 成功进入登录页面,尝试使用读到的工程账密码和光猫背面默认密码登录,然而都登不上去   这里直添加一个Linux用户test/password@123 echo "test:advwtv/9yU5yQ:0:0:User_like_root:/root:/bin/bash" >>/etc/passwd 登录成功~   最后记得关掉telnet,光猫作为出口,会有个公网地址,开启后公网也可以远程登录进来关闭命令killall telnetd >/dev/null 2>&1 文章来源:先知社区(7tem7)原文地址:https://xz.aliyun.com/t/9046      ...

    2024-04-14 技术教程 247
  • 记一次Edu挖掘记录

    平时不怎么爱挖src,因此除了项目需要之外(基本全部都是企业的网站),业余几乎不会抽时间去挖挖各大众测平台的src,还没有挖过类似于学校edu这一类的src,于是就想着玩一下,看看有什么区别。 目标定夺 首先是目标的问题,一般对于网站的一些首页,内容太多,所以打起来会比较墨迹,因此这次目标定在了某个学校的智慧校园后台,一般这一类的后台管理系统都会存在类似于弱口令、登录绕过等各种问题,而且功能相对来说也比较简单。 操作手册泄露 在对网站目录进行扫描爆破的途中,发现了一个/doc路径,访问下载出来了一个系统的操作手册,按照以往的经验,这一类的操作手册一般都会存在很多可利用的地方,比如说一些默认的账号密码会写明,或者说后台的一些用户名账号密码可能会被泄露。果不其然,这里虽然后面大部分图片被打上了马赛克,但是依旧有少部分图片是泄露了学生的姓名,账号,管理员的账号admin_g等。 拿到了账号之后,尝试了一轮的暴力破解,因为个人觉得这一类密码应该都是有规律的,可能就是学号或者姓名简写等,很遗憾的并没有,开发人员对登录错误次数限制为了5次,因此并没有能够成功登录到任何一个账号。 JS泄露 这里的JS文件有些许奇怪,它以前应该是测试的时候在内网有一个路径Env,从这个Env获取类似于配置的东西,这里应当某个子域名的外网。 尝试访问配置文件的路径/Env,得到了很多的api接口的URL,根据各类接口的命名,可以看到它是通过前后端分离,通过不同的接口域名处理不同角色的逻辑,教师的、学生的,等等。 这是一个Java编写的应用程序,根据经验,类似的api路径接口可能会存在springboot信息泄露或swagger接口文档能够被访问的问题,进行了尝试之后,发现每一个接口的URL路径都存在了swagger接口文档泄露的问题。 Swagger文档泄露 swagger接口文档未删除,能够被访问并不意味着一定存在漏洞,当开发人员对网站的各种权限措施等做的足够好时,接口文档也仅是一个接口文档了,只不过能够让攻击者快速摸清楚网站的各类接口和它的作用。在前面几个类似于登录Swagger接口文档并没有找到可利用的点,基本上都返回401 Unauthorized,因为接口很多,逐渐被消磨着耐心,它这里的一些data数据接口甚至进行了IP封锁,直接返回了403。 直至来到处理Student与Teacher接口,通过一个个寻找发现了存在可用的未授权接口,这两个接口能够获取到所有学生、教师的姓名、班级、身份证号等信息。 通过学生的ID号还能够得到学生、老师的详细信息个人信息。 小红的作用 这时候想起操作文档中泄露的账号,是否可以继续通过这些信息猜测账号密码呢,因为通过文档的说法,是存在着一个默认密码的,但是我这里一直都没有试出来(当然这也与我几乎没有接触过学校类的网站相关,经验实在是太少了),所以我开始尝试其它方式,比如小红书?我在小红书发布了一个帖子,并艾特了这个学校,以求在评论区中得到默认的账号密码。 果不其然小红书起到了它该有的作用,下面有人回复我的贴子,得到了我想到的内容。 剩下最后一个问题就是说,密码是身份证后六位,我该如何得到操作手册中对应用户的身份证呢?于是我尝试了不用操作手册中的账号,我直接通过了身份证号作为账号,后六位作为密码进行了一次登录,登录成功了,尝试了其它的账号,也都成功了,也就意味着这个学校大量的教师和学生账号泄露出来了。   在后台里面能够访问到该学校的其它管理系统,新闻中心,甚至能够看到协同办公的OA等等 当然还存在了其它的接口也存在未授权的问题,比如说成绩的导出等等,泄露了大量学生的成绩信息、身份证号等等。   总结 之所以写下这篇文章,并不是说这次的src有多难,而是觉得这次的src是有意义的B站,小红书,抖音等社交软件也可以成为有效的攻击社工利用手段之一。而对于学校这一类的系统,因为用户量大,学生安全意识并不强的原因,大部分的账号都是默认密码,学生教师等都并不会更改密码。   黑白之道发布、转载的文章中所涉及的技术、思路和工具仅供以安全为目的的学习交流使用,任何人不得将其用于非法用途及盈利等目的,否则后果自行承担! 如侵权请私聊我们删文  ...

    2024-04-11 技术教程 221
  • vscode执行pnpm : 无法加载文件 pnpm.ps1,因为在此系统上禁止运行脚本解决办法

    文章目录   问题原因 解决办法 在vscode上运行pnpm install时提示:pnpm : 无法加载文件 D:\install\nvm\nodejs\pnpm.ps1,因为在此系统上禁止运行脚本,这个问题该如何解决呢? 问题原因 产生该问题的原因就是因为Power Shell的执行策略限制了脚本的执行。我们可以通过如下指令查看当前Power Shell的执行策略: Get-ExecutionPolicy 我们发现是Restricted默认策略,那这些策略什么意思呢? Restricted:默认策略,禁止运行任何脚本,除非明确允许。 AllSigned:只允许运行经由数字签名并由受信任的发布者签名的脚本。 RemoteSigned:允许运行未经签名的脚本,但是必须由受信任的发布者在本地计算机上签名。 Unrestricted:允许运行所有脚本。 解决办法 解决办法很简单,如果你是Restricted默认策略,那执行以下指令改为Unrestricted策略即可: Set-ExecutionPolicy -ExecutionPolicy Unrestricted -Scope Process 如果你是 AllSigned 或 RemoteSigned策略,执行以下指令改为Unrestricted策略: Set-ExecutionPolicy -ExecutionPolicy Unrestricted -Scope Process -Force 最后再去执行pnpm install发现就可以正常执行指令了,就是这么简单。 ...

    2024-04-08 技术教程 135
  • uniapp Vue 使用 sip.js进行语音通话视频通话

    下载或者安装 sip.js 到 uniapp 项目,APP 端在 menifest.json 中配置麦克风权限 menifest.json 中 app 权限配置选中: android.permission.RECORD_AUDIO android.permission.MODIFY_AUDIO_SETTINGS sip.js 低版本 如 V0.13.0 版本的写法 注册 取消注册 呼叫 {{isInOut?inComingNumber:outGoingNumber}} 通话中... 呼入... 呼出... import * as sip from "@/common/js/sip-0.13.0.min.js" let ua; export default { name: "VoiceIntercom", props: {}, data() { return { configuration: {}, outGoingNumber: '02700002', baseUrl: 'web.domain.com', // sipurl格式: "sip:02700001@web.domain.com:7065", port: '7065', user: { number: '02700001', name: 'test', password: '123456' }, server: 'wss://web.domain.com:7067', currentSession: null, inComingNumber: null, isRegistered: false, isConnected: false, // 是否接通 isInOut: true, // true 被呼, false 呼出 } }, onLoad() { this.handleRegister() }, beforeDestroy() { this.handleUnRegister() }, methods: { // 登录 handleRegister() { this.configuration = { uri: `sip:${this.user.number}@${this.baseUrl}:${this.port}`, displayName: this.user.name, password: this.user.password, transportOptions: { wsServers: [this.server], traceSip: true }, } ua = new sip.UA(this.configuration) ua.on('registered', (resp) => { this.showTishi('【' + this.user.number + '】语音登录成功') this.isRegistered = true }) ua.on('registrationFailed', (resp) => { if (resp.statusCode == 503) { this.showTishi('【' + this.user.number + '】服务不可用') } else { this.showTishi('【' + this.user.number + '】语音登录失败:' + resp.reasonPhrase) } this.isRegistered = false console.log(resp, '语音登录失败') }) ua.on('unregistered', (response, cause) => { this.showTishi('【' + this.user.number + '】取消语音登录成功') console.log(response, cause, '取消语音登录') }) ua.on('invite', (session) => { this.currentSession = session this.inComingNumber = session.remoteIdentity.uri.user this.isInOut = true this.$refs.popup.open() this.$nextTick(() => { this.$refs.bell.$refs.audio.play() this.$refs.bell.$refs.audio.currentTime = 0 }) this.sessionEvent(session) }) ua.on('message', (message)=>{ console.log(message,'ua-message') }) ua.start() }, // 退出登录 handleUnRegister() { if (ua) { ua.unregister() this.isRegistered = false } }, // session 处理 sessionEvent(session) { session.on('rejected', () => { console.log('inComing挂断') }) session.on('cancel', () => { console.log('outgoing挂断') }) session.on('terminated', (message, cause) => { console.log(message, cause, 'session-terminated') if (cause == 'Rejected') { if (message.reasonPhrase == 'Decline') { this.showTishi('您的拨号暂时无人接听!') } else { this.showTishi('对方拒接了!') } } else if (cause == 'BYE') { this.showTishi('对方已挂机!') } else if (cause == 'Canceled') { this.showTishi('对方已取消!') } this.$refs.bell.$refs.audio.pause() this.$refs.popup.close() }) session.on('accepted', (resp) => { this.$refs.bell.$refs.audio.pause() this.isConnected = true console.log(resp, '接受了') }) session.on('trackAdded', () => { const pc = session.sessionDescriptionHandler.peerConnection const remoteStream = new MediaStream() pc.getReceivers().forEach((receiver) => { if (receiver.track) { remoteStream.addTrack(receiver.track) this.$refs.remoteAudio.$refs.audio.srcObject = remoteStream this.$refs.remoteAudio.$refs.audio.play() } }) }) session.on('bye', (resp, cause) => { console.log(resp, cause, 'session-bye') if ((resp && resp.method == 'BYE') || cause == 'BYE') { this.isConnected = false this.$refs.popup.close() this.$refs.remoteAudio.$refs.audio.pause() this.showTishi('【' + this.user.number + '】通话已结束!') } }) session.on('failed', () => { console.log('session-failed') }) }, // 接听 handleAccept() { const option = { sessionDescriptionHandlerOptions: { constraints: { audio: true, video: false } } } this.currentSession.accept(option) }, // 拒接 handleTerminate() { this.currentSession.terminate() this.$refs.popup.close() this.isConnected = false }, // 挂断 handleCacel() { if (this.isInOut) { this.currentSession.reject() } else { this.currentSession.terminate() } this.$refs.popup.close() this.isConnected = false }, // 拨打 handleCall(number) { number = this.outGoingNumber if (this.isRegistered) { this.isInOut = false const sipUrl = `sip:${number}@${this.baseUrl}:${this.port}` this.currentSession = ua.invite(sipUrl, { sessionDescriptionHandlerOptions: { constraints: { audio: true, video: false } } }) this.$refs.popup.open() this.sessionEvent(this.currentSession) } else { this.showTishi('请先登录语音用户') } }, showTishi(title){ uni.showToast({ title: title, icon: 'none' }) }, } } .container { font-size: 30rpx; } .top-box { padding: 30rpx; display: flex; } .popup-box { background: #ffffff; width: 80vw; padding: 40rpx; border-radius: 20rpx; line-height: 80rpx; } .flex-box { display: flex; justify-content: space-around; .uni-tag { font-size: 30rpx; padding: 16rpx 28rpx; } } .fs-36{ font-size: 36rpx; font-weight: bold; } sip.js 高版本如 V0.21.2 用法(参数同上,只列出 methods 里的部分) import { UserAgentOptions, UserAgent, Registerer, Invitation, Inviter, Session, SessionState, InvitationAcceptOptions, InviterOptions, Messager, URI, RegistererState, } from '@/common/js/sip-0.21.2.min.js' let userAgent, registerer; const target = UserAgent.makeURI(`sip:${this.outGoingNumber}@${this.baseUrl}:${this.port}`); methods: { handleRegister() { const uri = UserAgent.makeURI(`sip:${this.user.number}@${this.baseUrl}:${this.port}`) if(!uri){ console.log('创建URI失败') } const transportOptions = { server: this.server } const userAgentOptions = { authorizationUsername: this.user.number, authorizationPassword: this.user.password, displayName: this.user.name, transportOptions, uri, delegate: { onInvite } } userAgent = new UserAgent(userAgentOptions) registerer = new Registerer(userAgent) userAgent.start().then(()=>{ registerer.register({ requestDelegate: { onReject: (resp)=>{ console.log(resp, 'onReject') }, onAccept: (resp)=>{ console.log(resp, 'onAccept') }, onProgress: (resp) => { console.log(resp, 'onProgress') }, onRedirect: (resp) => { console.log(resp, 'onRedirect') }, onTrying: (resp) => { console.log(resp, 'onTrying') }, } }).catch((e: Error) => { console.log(e, "Register failed") }) }).catch((err:any)=>{ console.log(err,'start-err') }) registerer.stateChange.addListener((newState)=>{ switch (newState) { case RegistererState.Unregistered: console.log('退出登录') break; case RegistererState.Registered : break; case RegistererState.Initial : console.log('语音用户登录Initial') break; case RegistererState.Terminated : console.log('语音用户登录Terminated') break; } }) function onInvite(invitation){ this.currentSession = invitation this.inComingNumber = invitation.remoteIdentity.uri.user this.$refs.popup.open() invitation.stateChange.addListener((state)=>{ this.sessionStateEvent(state, invitation) }) } } handleAccept(){ let constrainsDefault = { audio: true, video: false, } const options = { sessionDescriptionHandlerOptions: { constraints: constrainsDefault } } this.currentSession.accept(options) this.isConnected = true } handleReject(){ this.currentSession.reject() this.$refs.popup.close() } sessionStateEvent(state, session){ switch(state){ case SessionState.Initial: console.log('SessionState.Initial') case SessionState.Establishing: console.log('SessionState.Establishing') break; case SessionState.Established: console.log('SessionState.Established') this.isConnected = true this.setupRemoteMedia(session) break; case SessionState.Terminating: console.log('SessionState.Terminating') break; case SessionState.Terminated: console.log('SessionState.Terminated') this.clearupMedia(session) break; } } setupRemoteMedia(session){ const remoteStream = new MediaStream() // console.log(session.sessionDescriptionHandler, 'sessionDescriptionHandler') session.sessionDescriptionHandler.peerConnection.getReceiver().forEach((receiver)=>{ if(receiver.track){ remoteStream.addTrack(receiver.track) } }) this.$refs.remoteAudio.$refs.audio.srcObject = remoteStream this.$refs.remoteAudio.$refs.audio.play() } clearupMedia(session){ if(this.isCallIn){ if(session.isCanceled){ console.log('对方已挂机') } }else{ if(!session.isCanceled){ // console.log('对方已挂机') } } this.$refs.remoteAudio.$refs.audio.srcObject = null this.$refs.remoteAudio.$refs.audio.pause() this.endCall() } handleCall(){ this.isCallIn = false this.$refs.popup.open() const inviterOptions = { sessionDescriptionHandlerOptions: { constraints: { audio: true, video: false } } } if(target){ const inviter = new Inviter(userAgent, target, inviterOptions) this.currentSession = inviter inviter.invite({ requestDelegate: { onReject: (resp)=>{ console.log(resp, 'inviter-onReject') if(resp.statusCode == 500){ console.log('对方不在线') }else{ console.log('对方拒接了') } }, onAccept: (resp)=>{ console.log(resp, 'inviter-onAccept') }, onProgress: (resp) => { console.log(resp, 'inviter-onProgress') }, onRedirect: (resp) => { console.log(resp, 'inviter-onRedirect') }, onTrying: (resp) => { console.log(resp, 'inviter-onTrying') }, } }) inviter.stateChange.addListener((state)=>{ this.sessionStateEvent(state, inviter) }) } } handleURegister(){ if(userAgent){ this.isRegistered = false registerer.unregister() } } sendMessage(){ if(target && this.isRegistered){ const messager = new Messager(userAgent, target, '你好') messager.message() } } endCall(){ this.isConnected = false this.$refs.popup.close() switch(this.currentSession.state){ case SessionState.Initial: case SessionState.Establishing: if(this.currentSession instanceof Inviter){ // incoming session this.currentSession.cancel() }else{ // outgoing session this.currentSession.reject() } break; case SessionState.Established: this.currentSession.bye() break; case SessionState.Terminating: break; case SessionState.Terminated: console.log(SessionState,'Terminated-endCall') break; } } } APP模式下检测麦克风权限,permission.js可从插件市场直接下载,使用页面import引入。 getPermission(){ let env = uni.getSystemInfoSync().platform if (env === 'android') {permission.requestAndroidPermission('android.permission.RECORD_AUDIO').then((e) => { if (e === -1) { uni.showToast({ title: '您已经拒绝录音权限,请在应用设置中手动打开', icon: 'none', }) } else if (e === 0) { uni.showToast({ title: '您拒绝了录音授权', icon: 'none', }) } else if (e === 1) { console.log('已授权') } else { uni.showToast({ title: '授权返回值错误', icon: 'none', }) } }).catch((err) => { uni.showToast({ title: '拉起录音授权失败', icon: 'none', }) }) } else if (env === 'ios') { if (permission.judgeIosPermission("record")){ console.log('已授权') }else{ uni.showToast({ title: '您拒绝了录音授权,请在应用设置中手动打开', icon: 'none', }) } } } ...

    2024-04-05 技术教程 161
  • 【MySQL】数据库排查慢查询、死锁进程排查、预防以及解决方法

    MySQL数据库排查慢查询、死锁进程及解决方法 一、排查慢查询 1.1检查慢查询日志是否开启 1.1.1使用命令检查是否开启慢查询日志: SHOW VARIABLES LIKE 'slow_query_log';   如果是 Value 为 off 则并未开启 1.1.2开启并且查看慢查询日志: MySQL提供了慢查询日志功能,可以记录所有执行时间超过long_query_time秒的查询语句,通过分析这些慢查询可以找到数据库性能瓶颈。 启用慢查询日志,在my.cnf中添加配置: slow_query_log=1 long_query_time=1 #修改记录慢查询的阈值,默认为10s log-slow-queries=/路径/slowquery.log #slowquery.log日志文件的路径 使用mysqldumpslow分析慢查询日志,找出真正的慢查询: mysqldumpslow -s t /路径/slowquery.log 根据慢查询结果分析数据库索引使用情况、SQL调优空间等。 1.2 使用EXPLAIN来分析SQL语句的执行计划 主要步骤如下: 选择需要分析的SQL语句。 在SQL语句前添加EXPLAIN关键字,例如: EXPLAIN SELECT * FROM table WHERE condition; 执行查询。EXPLAIN不会真正执行语句,只是展示其执行计划。 查看结果集。EXPLAIN查询结果将是一张表格,包含以下重要列:   id: SELECT的步骤序列,从上到下执行。id值从上到下为访问顺序,上层依赖下层。 select_type: SELECT类型,如SIMPLE或PRIMARY。 简单为PRIMARY或SIMPLE查询较好,JOIN等复杂嵌套查询效率较低。 table:读取的数据源。 type:连接类型或访问类型,如ALL或RANGE。ALL类型效率最低,需要全表扫描;使用索引类型为CONST、eq_ref、ref、range效率较高。 possible_keys:可能使用的索引。若为空,说明SQL没有利用索引,需添加索引。 key:实际使用的索引。实际使用的索引名。若使用不到索引,表明优化空间大。 key_len:使用的索引最大长度。 索引长度短效率高,避免过长索引。 rows:预估需要读取的行数。数值越小性能越好,多表JOIN联合查询数值过高需优化提升性能。 filtered:过滤条件生效率。比率越高过滤效率越好,能有效减少数据读取量。 根据结果分析SQL访问数据的方式及性能瓶颈点,如是否利用索引、表连接方式等。从而优化SQL语句。多次查询对比验证是否效果明显。 二、检测死锁 使用show processlist命令查看当前请求,锁等待(锁类型)大于0的为死锁进程   找到对应的阻塞操作的进程id 使用kill强制结束死锁进程 KILL ; 分析死锁原因,修改sql或导致死锁的业务代码 三、优化建议 添加合理的索引提高查询效率 使用explain分析查询执行计划,查询是否走索引 控制事务块大小,减小锁定范围 可以将一大批次的操作拆分为多个较小的事务,从而减小每个事务持有锁的范围,降低其他事务被锁定的可能性。 调整事务 isolation level,减少锁冲突 可以将事务级别调整为较宽松的读已提交(READ COMMITED)或REPEATABLE READ等级。这些级别对锁的要求不如串行化等级严格,可以减少冲突。 优化sql,减少回表次数等 如减少排序、执行多个小查询代替一个大查询、利用索引更好地检索数据等,可以降低数据库磁盘的IO操作,同时锁定的时间也会缩短。 增加硬件资源(CPU、内存等)对冲突几率更低 总体上,通过控制事务粒度、隔离级别和SQL优化,可以有效降低数据库锁冲突的可能性,提高并发处理能力。这些措施都值得尝试,看是否可以带来可观的优化空间 ...

    2024-04-05 技术教程 162
  • 前端开发:Vue3提示警告Failed to resolve component:XXX If this is a native custom element… 的解决方法

    前言 在前端开发中使用Vue的开发者都知道,Vue目前已经以Vue3.0为基础版本了,也就是说Vue3.0已经成为主流版本了。还在只用Vue2.0开发没有使用Vue3.0的开发者要注意了,要抓紧时间熟悉和了解Vue3以上的相关语法和知识点了,迫在眉急。本篇博文来分享一下,关于在使用Vue3的时候遇到的一个提示警告错误,虽然不复杂,但是这是一个比较常见且共性的问题,分享出来记录一下,方便以后查阅使用。 警告提示 虽然现在Vue3为基础版本,但是对于熟悉使用Vue2的开发者来说,有些写法还没有调整过来,比如本文要分享的警告提示在Vue2不会有警告,但是在Vue3就会有警告,下面分享一下笔者在开发过程中遇到的相似问题提示的汇总,虽然有三个不同的组件警告提示,其实是属于同一类的问题,具体警告信息合集如下所示:   1、 [Vue warn]: Failed to resolve component: van-loading If this is a native custom element, make sure to exclude it from component resolution via compilerOptions.isCustomElement.   at  ...

    2024-04-05 技术教程 150
  • Excel如何将FILTER函数筛选结果多余的错误值去掉?

    Excel如何将FILTER函数筛选结果多余的错误值去掉? 很多同学常用WPS处理表格数据,再用WPS表格提取数据的时候有一个很好用的函数Filter函数,它可以自动提取符合条件的数据,操作起来方便快捷。 但是如果我们所选择的区域太大,可能筛选结果会出现错误值,那么如何将FILTER函数筛选结果多余的错误值去掉? 推荐方法如下: =IFERROR(INDEX(FILTER(表!$A:$E,表!$E:$E="Kirin博客"),ROW(A1),COLUMN(A1)),"") 先用index函数和row、column函数把处理结果转换一下,然后加iferror函数将错误值隐藏。 大家可以尝试一下,有问题欢迎提问。有更好的方法可以留言补充,谢谢!   ...

    2024-04-04 技术教程 124
  • Excel中利用Filter函数进行多条件筛选数据的2种方法

    Excel中如何利用Filter函数进行多条件筛选数据? 很多同学常用WPS处理表格数据,再用WPS表格提取数据的时候有一个很好用的函数Filter函数,它可以自动提取符合条件的数据,操作起来方便快捷。但是在Excel的Filter函数介绍中只介绍了单条件筛选数据的方法,那我们在处理数据过程中,如果遇到想要通过多条件去筛选数据,那么改如何操作? 下面就给大家讲一下Excel中利用Filter函数进行多条件筛选数据的2种方法: 1、FILTER函数,多个条件筛选(且条件) 通用公式: =FILTER(结果值,(查找列1=查找值1)*(查找列2=查找值2)) 如果还有3个条件,就再次的相乘即可! 2、FILTER函数,多个条件筛选(或条件)   通用公式: =FILTER(结果值,(查找列1=查找值1)+(查找列2=查找值2)) 如果还有3个条件,就再次的相加即可! 大家可以尝试一下,有问题欢迎提问。有更好的方法可以留言补充,谢谢!...

    2024-04-04 技术教程 111
  • 油管YouTube最全教程:从注册到收款

    第一步、注册账号 对于纯新手而言,注册账号包括2个流程:一个是注册谷歌账号,一个是创建YouTube频道。 1.注册Gmail邮箱 几年前只要能科学上网,注册Gmail是没什么难度的,但近几年注册很容易失败,最常见的问题就是中国大陆的手机号无法接收验证码。 常见的的破解方法有:清理浏览器cookie、更换浏览器语言、使用第三方平台接码、更换电脑ip或浏览器等等。但根据个人实际测试,很多方法都已经行不通,也可能是自己的电脑ip注册太多账号的缘故。比较推荐的方法是用手机注册,而不是电脑注册。实在不想花时间折腾,也可以在某宝购买,价格也不高。 第一步 打开https://www.**屏蔽敏感词**/ 修改显示语言 登陆界面 注册好gmail之后,接下来就可以创建YouTube频道了。 2.创建YouTube频道 一般情况注册好gmail后会自带一个频道,但不推荐直接使用这个频道,因为该频道的资料是跟谷歌信息关联的,不方便后续的资料更改。 创建YouTube频道的方法: 点击头像,在头像下方有“创建频道”字样(初次创建才有),点击即可开始创建频道; . 频道创建完成只是入坑油管的第一步,接下来后台设置和频道包装非常关键。 频道设置和包装做好之后就可以发布视频了。在这里可以直接上传视频 点击上传视频 第二步、注册AdSense YouTube频道必须达到获利门槛(4000小时+1000订阅)之后才可以注册AdSense。如下图,频道达到获利门槛后,后台显示三步流程:查看合作伙伴计划条款、注册AdSense、接受审核,点击开始就可以开始注册AdSense。 注册AdSense相比创建频道而言,更为复杂,也更容易出错。 地区选择 香港 然后保存 注意这里的地区是指youtube频道地区,改为香港才能有获利功能。但是我们的adsense账户后台仍然是中国的,不影响正常收款结算 1、填写网址 注册 AdSense时要求提供网站的网址,YouTube频道就直接填网址(例如 **屏蔽敏感词**/channel/weitube123); 填写邮件地址,一般用创建频道的gmail关联就行。 2、填写所在国家或地区 填写所在的国家或地区时,一定不能乱填。如果填写中国,PIN码也会发到中国大陆且只能绑定中国大陆银行;如果是中国香港或者海外国家,PIN码则寄到对应地区或国家。国家或地区一旦确认,将无法更改! 3、创建付款资料 这部分主要填写个人地址等信息,这部分可以填写代收人信息,不一定填写自己的资料。账户类型有「个人」或「企业」,二者唯一的差别仅在「个人帐户」是用帐户持有人的收款人姓名收款,「企业帐户」则以公司名义收款。 AdSense帐户金额达验证门槛时(10美金),谷歌会寄送个人识别码(PIN码),因此收件地址一定要填写可以收到信件的,建议填写中国沿海大城市,交通和信息比较便利的地址(可委托朋友代收)。 新版AdSense会要求填写税务信息,将来的注册流程可能会有所变化。 adsense注册完成后,系统会自动进入获利审核状态,如果频道获利审核通过,YouTube的收益会每个月放进AdSense账号。 备注:官方不允许一个人拥有2个AdSense,因此建议一个浏览器不要同时存在2个AdSense,否则容易引起不必要的麻烦。 AdSense如何填写银行付款信息? 目前国内最常用的方法是招商银行的电汇,在付款设置里面增加电汇,具体填写方法如下图: 温馨提示: 1、接收不同的币种,需提供相应的中间行信息给汇款人作参考。 2、部分网点没有取得“金融标识码”,则该网点开户的一卡通无法接收境外汇入汇款,柜台可能会做退汇处理,详情可联系开户行确认。 3、有任何不明白的问题,打一卡通开户行的网点电话咨询清楚。 招行一卡通接收谷歌广告联盟境外汇款,英文姓名怎么填写? 收款人的英文姓名,按照银行卡证件姓名的汉语拼音拼写,全部大写,姓在前,名在后。比如:两个字的姓名,张三:ZHANG SAN;三个字的姓名,张小三:ZHANG XIAOSAN。 如果不放心,可以在招行网上银行、手机银行APP内查看,里面有自己姓名的英文写法。可直接在招行官网查询 汇款线路指南 -- 招商银行官网,或者下载官方路劲指南。填写完成后,YouTube产生的收益会每月底发到AdSense,如果收益超过100美金,则转入你所绑定的招行卡账户里。 收益转入银行卡之后怎么结汇取出来? 其实很简单,只要安装招行app找到相应的入口,并在招行工作时间即可结汇成人民币。 Google AdSense付款日期是哪一天? 谷歌广告联盟的付款日期是每月21日-26日,会在付款后的5个工作日内到账,通常在第二天也就是22日就会到招商银行账户。通过谷歌广告赚钱,对于大部分朋友来说,从申请到收款,障碍太多了,希望大家都能坚持到最后。 本文还没来及发布,来了个小惊喜。12日上午11点多在Google AdSense中添加电汇支付方式,当天下午3点,收到了付款邮件通知。 国内接收境外汇款,几天到账? 通常在支付日算起的5个工作日内到账,不过一般情况下,会在1-2天内到账,会因收款银行不同而有区别。 作者:峰少 公众号:峰少微课...

    2024-04-03 技术教程 97
  • 宝塔Linux面板一键迁移网站-Windows面板快速迁移网站

    宝塔Linux/Windows面板如何一键迁移网站?相信做网站的站长朋友都会面临着网站搬家的时候,可能原因是网站的服务器不稳定,或者网站发展起来后需要更好的服务器,所以就需要进行网站的搬家了。如果两台服务器都安装有宝塔面板的话,可以在旧服务器的面板上安装“宝塔一键迁移 API 版本”即可实现快速迁移网站数据到新服务器上(只需要在旧服务器面板上安装该插件即可),本文档介绍如何使用宝塔 Linux/Windows 面板快速迁移网站到其他云服务器中。 迁移准备 1、迁出云服务器:指需要进行数据迁移的其他的云服务器,在服务器中安装宝塔 Linux/Windows 面板软件,需要升级至最新版本,本示例以宝塔Linux面板为例。 2、迁入云服务器:指用于接收数据的云服务器,在服务器安装宝塔 Linux/Windows 面板软件,需要升级至最新版本,本示例以宝塔Linux面板为例。 3、确保迁出服务器和迁入服务器之间网络可达,可通过公网互通,或则私网互通。 迁入服务器操作 1、登录迁入云服务器的宝塔 Linux 面板。 2、选择左侧菜单栏中的【面板设置】,并在设置页面中开启 API 接口。如下图所示: 3、在弹出的“配置面板API” 窗口中,记录好接口密钥,将需迁出服务器的公网 IP /内网IP地址加入 IP 白名单中,单击【保存】。如下图所示: 迁出服务器操作 1、登录迁出服务器的宝塔 Linux 面板,选择左侧菜单栏中的【软件商店】。 2、在“软件商店”页面中,找到“宝塔一键迁移API版本”插件,并单击右侧的【安装】。如下图所示:注意:仅需在迁出服务器中安装一键迁移插件即可。 3、安装成功后,单击插件所在行右侧的【设置】。 4、在弹出的“宝塔一键迁移API版本”窗口中,参考以下信息填写接受数据的迁入服务器信息。如下图所示: 面板地址:填写迁入服务器的面板地址。例如:http://公网 IP:8888。 API密钥:填写在迁入服务器操作的 中已获取的 API 接口密钥。 5、选择【下一步】,在弹出提示窗口中单击【已添加,继续操作】进入检测环境步骤。 6、在检测环境步骤中,宝塔面板将对远程接收数据的迁入服务器进行环境检测,查看是否已安装相关软件及运行环境。其中包含网站服务、MySQL 数据库、FTP、PHP、可用磁盘容量。 若检测失败,并提示远程迁入服务器中未安装某个软件(例如 PHP、MySQL 等)则需登录迁入服务器面板,前往【软件商店】安装对应版本软件,直至检测全部通过后,单击【下一步】。如下图所示: 7、在选择数据步骤中,按需选择需迁移的数据,并单击【一键迁移】。如下图所示: 8、等待数据迁移完成,若提示迁移失败,则建议您查看迁移日志,并参考提示信息处理对应问题。问题解决后,再重新执行迁移步骤。迁移成功如下图所示: 网站迁移完成后 1、修改网站的域名解析,降域名解析记录修改为新的服务器公网ip地址。 2、域名解析生效需要点时间,等待解析生效完成后,在浏览器访问域名,就可以访问迁移后的网站了。  ...

    2024-04-02 技术教程 76
  • Telegram 完美解决接收不了验证码和其他问题~

    之前出了一期 Telegram 的软件分享,虽然只发了一点点的教程,因为我在注册的时候挺轻松的,所以就没有发太详细的教程,但是收到大家的反馈都在验证码获取出现了许多问题。今天就为大家做一期完整的教程,和解决可能会出现的各种问题。 在一切开始之前需要做的准备: 1.请确认手机号码输入正确,区号是否是 +86 2.然后检查手机网络是否稳定。 3.检查Telegram是否被加入了应用的信任列表。 4.接验证码的时候需要科学上网/全部节点都可尝试 5.保证手机号码可接收短信的正常功能 6. 联系手机运营商确认手机号码状态 7.确保手机短信存储空间充足检查手机信号是否正常 8.如果之前有在其他设备登陆过,那该账号需要原设备验证登陆 正文开始 Telegram纸飞机收不到86短信验证码,很大一部分原因是因为运营商的限制。要绕开这个限制,可以尝试一下以下的方法,不保证绝对成功,但是成功率会提高很多。 要想使用上电报,以下几个步骤是必须要进行的: 首先是IOS用户: ①第一步是安装问题,大家可以参考往期文章,这个是如何用外区的apple id 下载telegram的教程,当然下载之后还用不了,还需要网络优化工具,就是常说的【梯子】,因为这是违规的在这里分享不了,大家可以加入粉丝群来讨论。 ②第一步做完了,确保梯子能用的前提下,打开电报软件,进行手机号码注册登录,根据这几天大家的反馈,有些IOS用户也遇到了接收不到验证吗的情况,但是反复尝试又可以登录了。我总结为以下几点: Ⅰ:Telegram的短信验证业务是外包出去的,不定期会更换。如果发短信的号码没有被限制,那么是可以正常接受到验证码的。这个就就需要我们多去尝试。一般来说,建议大家选择晚上注册。会有意想不到的惊喜哦! Ⅱ:如果你有朋友在国外,可以让其帮你购买一个国外的号码。反之,去万能的某宝,除了违禁品和女朋友,几乎啥都能搜到。不过需要搜索技巧哦! Ⅲ:如果注册的时候,一直在转圈圈就是梯子的问题,要想使用上电报,梯子是必不可少的环节!!! ③第三步就是登录之后的汉化:在前面的步骤做完之后,你已经进入了电报里面的界面了,但是全是英文的,大部分人英语不好,这里玲玲给大家分享一个汉化包:https://t.me/setlanguage/zh-hans-raw    复制链接打开浏览器自动跳转电报,根据提示进行操作就可以啦。 接下来是安卓用户: ①首先将电报和电报X两个软件安装好,安装包在往期文章,或者加入交流群获取软件 ②然后确保梯子能用,没有梯子的小伙伴,可以进入交流群来讨论噢~ ③在梯子打开的前提下,打开电报X,用手机号码去注册,先查看手机的短信有没有拦截,发送验证码,如果电报X也是接收不了验证吗,可以换一下线路或者更换设备,或者换一个时间点注册,建议是在晚上十一点左右。 ④电报X登录之后,在里面搜索telegram官方账号,如下图,然后打开电报进行手机号码登录,然后在电报X里面就会收到验证码,然后电报就可以登录啦。 ⑤还是接收不到验证码的话:在tao宝上可以解决 最后再发一下汉化的网址:https://t.me/setlanguage/zh-hans-raw 各种群聊大家可以在youtube上搜索电报群组,最好是那些大博主 最后再给大家温馨提示: 1.定不要相信电报里面的任何人,也不要轻易地去下载里面的软件,诈骗分子几乎就在这里面,大家一定要分辨清楚!!! 2.载软件也要是那种大群每天都只有一个固定的用户在分享软件,其他用户分享的软件大家千万别下载,可能一不小心就被锁机或者盗取你的信息了! 3.不要去用自己的信息去注册任何网站!!! 4.最后在欢迎大家加入粉丝群来讨论~~~ ...

    2024-03-29 技术教程 114
  • 24年强开花呗教程

    吧闲的无聊逛某鱼 发现竟然有卖支付B强开花呗的教程 (因为自己也没开所以下款看看) 但看完教程觉着太麻烦  想着就这点破教程还卖个五六块 就来这里给大家分享一下  第一步: 支付宝开通网商银行存200金余利宝  等收到3天活期利息,不要动  提醒:周六周日不要打开余利宝  如果有钱可以多存,额度会更高;期间配合多走流水效果更佳。  第二步: 用当前支付宝账号开通一个个人淘宝店铺  不需要营业执照,不需要保证金  开通后随便上架一两个产品 卖衣服的也行,卖裤子的也行。   联系三个好友在同城或者是隔壁城市的下单  (下单人一定要常用收货地址。)  一共需要下满六笔,金额随意。 [交易额更好在1000元以上] 然后寄给朋友。 同城邮递费便宜 和朋友沟通好,随便寄个东西就行。  [-定要有物流信息], 然后手机卸载当前支付宝  重新下载支付宝登录账号 在搜索栏上面搜索花呗,借呗  点进去就可以看到可以开通的渠道 是真麻烦啊  麻烦到家了已经,怎么还算强开呢  不过还有个方法二,图就是教程(图片放到文件夹里了 ) ...

    2024-02-17 技术教程 180
  • WP Fastest Cache手动实现缓存预加载

    文章目录   步骤 其他办法 使用WP Fastest Cache缓存插件开启了缓存预加载后,发现还是需要点击文章才能自动生成制定数量的缓存页面,导致网站访问量较小时,网页缓存不全,蜘蛛爬取耗时较高,为了解决该问题,可以使用手动操作实现缓存预加载。 步骤 打开浏览器,输入如下地址访问: 你的网站地址/?action=wpfastestcache&type=preload 注意替换你的网站地址。 然后我们会发现页面显示哪些页面生成了缓存: 我这里设置的是每次生成4个页面缓存,手工一次次访问太慢,直接借助在线定时自动刷新网页工具:https://hao.panziye.com/sites/5096.html 输入你的请求地址和刷新频率,点击开始刷新就行了,这样就省了很多事,非常简单方便。 其他办法 实现WP Fastest Cache缓存预加载,还有其他办法,如果你的是基于宝塔面板的,直接新建个计划任务去定时请求该地址,如果不是,那么有技术的朋友也可以自己写个WP_CORN定时任务去定时请求,这里就不再介绍了,因为毕竟不会频繁删除所有缓存,因此手动结合定时工具一次性生成所有缓存已经够用了。 ...

    2024-01-05 技术教程 209
  • 一致性hash算法作用是什么

    一致性哈希(Consistent Hashing)算法,乍一听大家可能觉得这是高大上的技术名词,但其实它在分布式系统中无疑是个解决大难题的土方法,就像是中国的传统医术在现代仍能医治各种疑难杂症一样。 这个算法自从 1997 年由麻省理工学院的博士生提出后,就在分布式系统中扮演着至关重要的角色。一致性哈希算法在分布式系统中的地位可比咱们生活中的在线记账软件,解决了数据存放位置的大问题。 传统的哈希算法在节点增减时面临着数据重新分配的巨大代价,就像如果你用纸质的账本,每次账目中间有变动(比如,中间有几天忘了记账)时都得整本重写一遍,想想都头疼。而一致性哈希通过精妙地圆环结构使得节点变动只影响邻近的一小部分数据,大大降低了系统维护的复杂度。 说到一致性哈希算法的基本概念,想象我们有一张圆桌,桌面上标着从 0 到 2^32(假设用的是 32 位的哈希函数)的数字,形成一个闭环: 每当有个新服务器来了,我们就给它一个或多个哈希值,让它在这张圆桌的某个地方坐下 每次我们有数据要存储时,就按照数据的哈希值找到在此值之后的第一个服务器,把数据放在那儿 如果这个服务器忙碌了,它会找一个最近的邻居节点来帮助存储数据 这样,每当服务器来来去去时,我们只需要重新调整它们附近的数据即可 这个算法的魅力在于,不管你的网络多么巨大,每次添加或删除一个节点,都只涉及到节点旁边的一小部分数据,而不是整个网络。 这就像在一个巨大的停车场里找车位,即便是一个区域的停车位满了,你也不用担心其他地区的车位会被迁移。 当然,这个算法也有它的缺点。有时候,所有人似乎都想停在同一个车位上,这就造成了负载不均,即哈希环倾斜的情况。 这时,你可能需要一些“虚拟车位”,也即是虚拟节点,让这个停车场的车辆更加均匀地分布。 这种情况我们可以这么理解:项目中某个区域的缓存快满了怎么办? 那就是加新节点! 为了让缓存数据均匀分布,我们通常会采用哈希后取模的方式来确定数据归属的节点。 而在加减节点的过程中,一致性哈希算法可以保证大多数 key 照旧停留在原有的车位上,而不需要把整个车场的车全部重新停一遍。 ...

    2024-01-05 技术教程 209
  • Linux grep指令的10个使用技巧

    文章目录   一、在文件中搜索字符串或正则表达式 二、忽略大小写进行搜索 三、反向搜索 四、显示行号 五、递归搜索 六、只显示文件名 七、只显示匹配的行数 八、打印匹配前后的行 九、匹配多个字符串或正则表达式 十、使用特殊字符进行搜索 本文主要讲解关于Linux grep指令的10个使用技巧相关内容,让我们来一起学习下吧! grep (global regular expression print) 是 Linux 或其他类 Unix 操作系统中的一个功能强大的命令行搜索工具。它可以从文件或标准输入中搜索文本或字符串,并显示匹配的行或统计信息。grep 命令有大量的选项和用法,本文将介绍一些常见的示例。 一、在文件中搜索字符串或正则表达式 grep 命令可用于在一个或多个文件中搜索字符串或正则表达式。以下是如何使用它的基本语法: grep 'pattern' file1 file2 ... 例如,如果我们想在 file.txt 中搜索字符串 “hello”,我们可以使用以下命令: grep 'hello' file.txt 这个命令会打印出 file.txt 中包含 hello 的所有行。我们还可以使用通配符来在多个文件中搜索。例如,如果我们想在当前目录下所有以 .txt 结尾的文件中搜索 “hello”,我们可以使用以下命令: grep 'hello' *.txt 如果要使用正则表达式进行搜索,我们可以使用 -E 选项或使用 egrep 命令。例如,如果我们想在 file.txt 中搜索以 h 开头,后面跟一个或多个字母的单词,我们可以使用以下命令: grep -E 'h[a-zA-Z]+' file.txt 或者 egrep 'h[a-zA-Z]+' file.txt 二、忽略大小写进行搜索 默认情况下,grep 命令是区分大小写的。如果我们要忽略大小写进行搜索,我们可以使用 -i 选项。例如,如果我们想在 file.txt 中搜索 “hello”,不管它是大写还是小写,我们可以使用以下命令: grep -i 'hello' file.txt 这个命令会打印出 file.txt 中包含 hello , Hello , HELLO 等的所有行。 三、反向搜索 有时我们可能想要找出不匹配某个字符串或正则表达式的行,而不是匹配的行。这时我们可以使用 -v 选项进行反向搜索。例如,如果我们想在 file.txt 中找出不包含 “hello” 的所有行,我们可以使用以下命令: grep -v 'hello' file.txt 这个命令会打印出 file.txt 中不包含 hello 的所有行。 四、显示行号 如果我们想在搜索结果中显示每一行的行号,我们可以使用 -n 选项。例如,如果我们想在 file.txt 中搜索 “hello”,并显示匹配行的行号,我们可以使用以下命令: grep -n 'hello' file.txt 这个命令会在每一行的前面显示一个数字,表示该行在文件中的行号。 五、递归搜索 如果我们想在一个目录及其子目录中的所有文件中搜索一个字符串或正则表达式,我们可以使用 -r 选项进行递归搜索。例如,如果我们想在 dir 目录及其子目录中的所有文件中搜索 “hello”,我们可以使用以下命令: grep -r 'hello' dir/ 这个命令会在每一行的前面显示匹配的文件名和行号。 六、只显示文件名 如果我们只想知道哪些文件包含了匹配的字符串或正则表达式,而不想看到具体的内容,我们可以使用 -l 选项来只显示文件名。例如,如果我们想在当前目录下所有以 .txt 结尾的文件中搜索 “hello”,并只显示包含 “hello” 的文件名,我们可以使用以下命令: grep -l 'hello' *.txt 这个命令会只打印出包含 hello 的文件名,而不显示具体的行。 七、只显示匹配的行数 如果我们只想知道有多少行匹配了字符串或正则表达式,而不想看到具体的内容,我们可以使用 -c 选项来只显示匹配的行数。例如,如果我们想在 file.txt 中搜索 “hello”,并只显示匹配 “hello” 的行数,我们可以使用以下命令: grep -c 'hello' file.txt 这个命令会只打印出一个数字,表示 file.txt 中有多少行包含 hello 。 八、打印匹配前后的行 有时候,我们可能想要看到匹配的行的前后的一些行,以便更好地理解上下文。这时候,我们可以使用 -A 选项来打印匹配后的 N 行,使用 -B 选项来打印匹配前的 N 行,或者使用 -C 选项来打印匹配前后的 N 行。例如,如果我们想在 file.txt 中搜索 hello ,并打印匹配后的 2 行,我们可以使用以下命令: grep -A 2 'hello' file.txt 这个命令会打印出 file.txt 中包含 hello 的所有行,以及每一行后面的 2 行。类似地,如果我们想打印匹配前的 2 行,我们可以使用以下命令: grep -B 2 'hello' file.txt 这个命令会打印出 file.txt 中包含 hello 的所有行,以及每一行前面的 2 行。如果我们想打印匹配前后的 2 行,我们可以使用以下命令: grep -C 2 'hello' file.txt 这个命令会打印出 file.txt 中包含 hello 的所有行,以及每一行前后的 2 行。 九、匹配多个字符串或正则表达式 如果我们想在一个或多个文件中搜索多个字符串或正则表达式,我们可以使用 -e 选项来指定多个模式,或者使用 -f 选项来从一个文件中读取多个模式。例如,如果我们想在 file.txt 中搜索 hello 或 world ,我们可以使用以下命令: grep -e 'hello' -e world file.txt 这个命令会打印出 file.txt 中包含 hello 或 world 的所有行。或者,我们可以把 hello 和 world 写在一个文件中,比如 pattern.txt ,然后使用以下命令: grep -f pattern.txt file.txt 这个命令会打印出 file.txt 中包含 pattern.txt 中的任何一个字符串的所有行。 十、使用特殊字符进行搜索 grep 命令支持一些特殊字符,用于表示行的开头或结尾,或者表示任意字符或重复次数等。这些特殊字符可以组合成复杂的正则表达式,用于进行精确的搜索。以下是一些常用的特殊字符: ^ :表示行的开头。例如, ^hello 表示以 hello 开头的行。 $ :表示行的结尾。例如, hello$ 表示以 hello 结尾的行。 . :表示任意一个字符。例如, h.llo 表示 h 和 llo 之间有任意一个字符的行。 * :表示前面的字符可以重复0次或多次。例如, hel*o 会匹配 “hello” 和 “helo”。 [] :表示括号中的任意一个字符。例如, h[ae]llo 表示 h 和 llo 之间是 a 或 e 的行。 [^] :表示括号中的任意一个字符以外的字符。例如,在表达式 h[^ae]llo 中,[^ae] 表示除了 a 和 e 之外的任意字符。 其他的正则字符: {} :表示前面的字符可以重复指定次数。例如, h{2,4}llo 会匹配 “hhello”, “hhelllo”, 和 “hhelllo”。 | :表示或。例如, h|l|o 会匹配 “h”, “l”, 或 “o”。 \d :匹配一个数字字符。 \D :匹配一个非数字字符。 \w :匹配一个字母、数字或下划线。 \W :匹配一个非字母、非数字或非下划线的字符。 \s :匹配一个空白字符,例如空格、制表符、换页符等。 \S :匹配一个非空白字符。 \b :匹配一个单词边界。 \B :匹配一个非单词边界。 [[:<:]] 和 [[:>:]]:分别匹配行的开头和结尾(与 ^ 和 $ 功能相同,但更通用)。 [[:alnum:]]、[[:alpha:]]、[[:digit:]] 等:匹配指定的字符类。 这些特殊字符和其他一些高级功能使得 grep 命令成为处理文本数据的强大工具。 以上就是关于Linux grep指令的10个使用技巧相关的全部内容,希望对你有帮助。欢迎继续关注潘子夜个人博客,学习愉快哦! ...

    2023-12-25 技术教程 207
  • linux启动流程步骤详解

    Linux是怎么启动的? 几乎每个软件工程师都用过 Linux,但并不是每个人都知道它的启动过程,让我们深入了解一下。 下图给我们展示了具体步骤。 第1步 当我们打开电源时,BIOS(基本输入/输出系统,Basic Input/Output System)或 UEFI(统一可扩展固件接口,Unified Extensible Firmware Interface)固件会从非易失性内存中加载,并执行 POST(开机自检,Power On Self Test)。 第2步 BIOS/UEFI 检测连接到系统的设备,包括 CPU、内存和存储设备。 第3步 选择一个启动设备来启动操作系统。 可以是硬盘、网络服务器或 CD ROM。 第4步 BIOS/UEFI 运行引导加载器 (GRUB),它提供了一个选择操作系统或内核功能的菜单。 第5步 内核准备就绪后,我们现在切换到用户空间。 内核启动 systemd 作为第一个用户空间进程,负责管理进程和服务、探测所有剩余硬件、挂载文件系统并运行桌面环境。 第6步 系统启动时,systemd 默认激活 default.target 单元。 同时还会执行其他分析单元。 第7步 系统运行一组启动脚本并配置环境。 第8步 用户将看到一个登录窗口。 系统现已准备就绪。 ...

    2023-12-22 204
  • 什么是Tomcat,安装及配置教程

    1、Tomcat 介绍 什么是 Tomcat Tomcat 是 Apache 软件基金会一个核心项目,是一个开源免费的轻量级 web 服务器,支持 Servlet / jsp 少量JavaEE规范,Tomcat 也被称为 Web 容器、Servlet 容器。 官网:https://tomcat.apache.org/ 什么是 JavaEE JavaEE:Java Enterprise Edition,Java 企业版。指 Java 企业级开发的技术规范总和。 包含 13 项技术规范:JDBC、JNDI、EJB、RMI、JSP、Servlet、XML、JMS、Java IDL、JTS、JTA、JavaMail、JAF 2、Tomcat 使用配置 2.1、Tomcat 下载启动 Tomcat 下载安装 下载:https://tomcat.apache.org/ 安装:Tomcat 是绿色版,直接解压就可以了,建议:不要有中文的目录,目录层次不要太深 打开apache-tomcat目录就能看到如下目录结构,每个目录中包含的内容需要认识下 Tomcat 启动关闭 启动:双击:bin\startup.bat 关闭 直接 x 掉运行窗口:强制关闭 bin\shutdown.bat:正常关闭 ctrl+c:正常关闭 Tomcat 访问 访问方式:浏览器输入localhost:8080,Tomcat 默认端口是 8080 2.2、Tomcat 启动乱码 问题 控制台有中文乱码,需要修改 conf/logging.prooperties 进入 Tomcat 的 conf 目录 找到 logging.properties 文件 将里面所有的 UTF-8 替换成 GBK 注意:建议使用 Vscode 打开或者其他工具,直接查找替换,避免发生错误 2.3、Tomcat 端口号修改 进入 Tomcat 的 conf 目录下 找到 server.xml 文件,打开 找到下列代码位置,大概在 69 行,修改 port=你想要的位置 <Connector port="8080" protocol="HTTP/1.1" connectionTimeout="20000" redirectPort="8443" /> 端口号冲突 3、Tomcat 项目部署 项目部署的两种方法 直接将项目复制到 webapps 目录下 采用压缩文件.war 的方式 将整个项目使用压缩工具打包成 zip 文件 改 zip 的扩展名为 war 复制到 webapps 目录下,tomcat 会自动解压成一个同名的目录 注意:里面的文件不能有中文名 部署演示:直接将项目复制到 webapps 目录下 项目名为:hello,将其文件复制到 webapps 目录下 启动 Tomcat 后,访问 hello 部署演示:采用压缩文件.war 的方式 将项目压缩成 zip 文件 改 zip 的扩展名为 war 复制到 webapps 目录下,tomcat 会自动解压成一个同名的目录 访问测试 4、IDEA 中使用 Tomcat 方式 通过 Maven 的 package 命令可以将项目打包成 war 包,将 war 文件拷贝到 Tomcat 的 webapps 目录下,启动 Tomcat 就可以将项目部署成功,然后通过浏览器进行访问即可。 然而我们在开发的过程中,项目中的内容会经常发生变化,如果按照上面这种方式来部署测试,是非常不方便的 如何在 IDEA 中能快速使用 Tomcat 呢? 将本地 Tomcat 集成到 IDEA 中 打开添加本地 Tomcat 的面板 指定本地 Tomcat 配置本地 Tomcat 将项目部署到集成 Tomcat 里面 扩展内容:xxx.war 和 xxx.war exploded 这两种部署项目模式的区别? war 模式是将 WEB 工程打成 war 包,把 war 包发布到 Tomcat 服务器上 war exploded 模式是将 WEB 工程以当前文件夹的位置关系发布到 Tomcat 服务器上 war 模式部署成功后,Tomcat 的 webapps 目录下会有部署的项目内容 war exploded 模式部署成功后,Tomcat 的 webapps 目录下没有,而使用的是项目的 target 目录下的内容进行部署 建议大家都选 war 模式进行部署,更符合项目部署的实际情况 ...

    2023-12-18 321

联系我们

在线咨询:点击这里给我发消息

QQ交流群:KirinBlog

工作日:8:00-23:00,节假日休息

扫码关注