Wails + Go 实现图形化桌面应用

news2025/1/11 23:43:53

效果展示

编写一个热点查看程序,包含百度热搜、微博热搜、头条、知乎等,废话不说上效果图:

  • 效果图1:
    image.png

  • 效果图2
    image.png

  • 打包大小

image.png

涉及技术点

Golang

使用golang 1.9 编写代码

Wails + vue3

使用Wails技术实现GUI渲染,页面组件使用ant-design-vue,vite进行前端资源打包。
Wails技术
https://wails.io/zh-Hans/docs/introduction

Wails 是一个可让您使用 Go 和 Web 技术编写桌面应用的项目。

将它看作为 Go 的快并且轻量的 Electron 替代品。 您可以使用 Go 的灵活性和强大功能,结合丰富的现代前端,轻松的构建应用程序。

  • 原生菜单、对话框、主题和半透明
  • Windows、macOS 和 linux 支持
  • 内置 Svelte、React 、Preact 、Vue、Lit 和 Vanilla JS 的模板
  • 从 JavaScript 轻松调用 Go 方法
  • 自动将 Go 结构体转换为 TypeScript 模块
  • Windows 上不需要 CGO 或外部 DLL
  • 使用 Vite 的实时开发模式
  • 可以轻松创建、构建和打包应用的强大命令行工具
  • 丰富的 运行时库
  • 使用 Wails 构建的应用程序兼容 Apple & Microsoft 商店

colly v2

colly v2 实现数据抓取:
Go colly爬虫框架精简高效【杠杠的】入门到精通 - 掘金 (juejin.cn)

应用程序打包

window环境为例:wails build -clean将资源文件和程序打包程成独立的exe文件。

环境准备

go环境

从 Go 下载页面 下载 Go,并配置好环境变量,还需要确保的 PATH 环境变量包含您的 ~/go/bin 目录路径

image.png

node环境

npm --version 检查环境

WebView2

在window环境下运行,需要保证WebView2,现在window10/11默认已经安装好了,微软强制内置的环境,可以忽略,如果后续环境检测不通过可以再额外进行安装。

Wails 环境

命令行运行 go install github.com/wailsapp/wails/v2/cmd/wails@latest 安装 Wails CLI

环境检测

命令行运行 wails doctor 命令,类似如下结果,说明完成环境配置了。

如果提示 wails 找不到命令,检查 …go/bin 是否配置path环境

PS C:\Users\14639> wails doctor
DEB | Using go webview2loader
Wails CLI v2.5.1

 SUCCESS  Done.

# System

OS           | Windows 10 Home China
Version      | 2009 (Build: 22000)
ID           | 21H2
Go Version   | go1.19.9
Platform     | windows
Architecture | amd64

# Wails

Version | v2.5.1

# Dependencies

Dependency | Package Name | Status    | Version
WebView2   | N/A          | Installed | 113.0.1774.57
Nodejs     | N/A          | Installed | 16.14.2
npm        | N/A          | Installed | 8.5.0
*upx       | N/A          | Available |
*nsis      | N/A          | Available |
* - Optional Dependency

# Diagnosis

Your system is ready for Wails development!
Optional package(s) installation details:
  - upx : Available at https://upx.github.io/
  - nsis : More info at https://wails.io/docs/guides/windows-installer/

 ♥   If Wails is useful to you or your company, please consider sponsoring the project:
https://github.com/sponsors/leaanthony

具体环境配置细节可以参考wails官网:安装 | Wails

项目开发

项目创建

直接使用wails脚手架创建,wails init -n wails-demo -t vue,使用vue进行开发,这里模式使用的vue3,打包使用的vite。相关技术不了解的同学可以自行学习。

项目结构

image.png

  • 新增或修改前端依赖相关,需要进入到frontend文件夹下
  • 项目dev模式运行和打包在项目根目录即可

项目命令

  • 开发模式启动: wails dev
  • 打包可执行文件:wails build -clean
  • 前端操作:npm install xxx

代码介绍

先给出源码仓库(码云):wails-demo: wails-demo (gitee.com) 感兴趣的可以下载一下本地运行。下载后直接运行wails dev即可

  • 核心代码介绍

启动类

main.go程序的运行启动入口

package main  
  
import (  
"embed"  
"github.com/wailsapp/wails/v2"  
"github.com/wailsapp/wails/v2/pkg/options"  
"github.com/wailsapp/wails/v2/pkg/options/assetserver"  
)  
// 下面代码不能删除,是为了go打包资源文件
//go:embed all:frontend/dist  
var assets embed.FS  
  
func main() {  
// Create an instance of the app structure  
app := NewApp()  
  
// NewMenu  窗口操作菜单
//newMenu := menu.NewMenu()  
//FileMenu := newMenu.AddSubmenu("菜单")  
//FileMenu.AddText("设置", keys.CmdOrCtrl("t"), func(data *menu.CallbackData) {  
// runtime.EventsEmit(app.ctx, "open-file", time.Now().Format("2006-01-02 15:04:05"))  
//})  
//FileMenu.AddSeparator()  
//FileMenu.AddText("退出", keys.CmdOrCtrl("q"), func(_ *menu.CallbackData) {  
// runtime.Quit(app.ctx)  
//})   
	// Create application with options
	err := wails.Run(&options.App{
		Title:         "实时热点",
		Width:         1024,
		Height:        768,
		DisableResize: true,
		//Menu:   newMenu,
		AssetServer: &assetserver.Options{
			Assets: assets,
		},
		BackgroundColour: &options.RGBA{R: 27, G: 38, B: 54, A: 1},
		OnStartup:        app.startup,
		Bind: []interface{}{
			app,
		},
	})

	if err != nil {
		println("Error:", err.Error())
	}
}

App.go 主要承担和前端js的通信和方法绑定Bind。

package main

import (
	"context"
)

// App struct
type App struct {
	ctx context.Context
	hsr *HotSearchRouter
}

// NewApp creates a new App application struct
func NewApp() *App {
	return &App{}
}

// startup is called when the app starts. The context is saved
// so we can call the runtime methods
func (a *App) startup(ctx context.Context) {
	a.ctx = ctx
	hsr := &HotSearchRouter{}
	hsr.Init()
	a.hsr = hsr
}

// Greet returns a greeting for the given name
func (a *App) Greet(index int) []HotSearchDto {
	if index == Last {
		return []HotSearchDto{}
	}
	return a.hsr.Route(index).Visit()
}

hot_search.go 爬取热搜数据

package main

import (
	"encoding/json"
	"fmt"
	"github.com/PuerkitoBio/goquery"
	"github.com/gocolly/colly/v2"
	"github.com/gocolly/colly/v2/extensions"
	"github.com/labstack/gommon/log"
	"net/http"
	"net/http/cookiejar"
	"net/url"
	"strconv"
	"strings"
)

const (
	BaiDu = iota
	WeiBo
	TouTiao
	ZhiHu
	Last
)

// HotSearchDto 搜索结果项
type HotSearchDto struct {
	Sort  int    `json:"sort"`
	Title string `json:"title"`
	Desc  string `json:"desc"`
	Url   string `json:"url"`
	Hot   string `json:"hot"`
}

// IHotSearch 热搜接口
type IHotSearch interface {
	BindHTMLSelector()
	Visit() []HotSearchDto
}

// BaseSearch 基础搜索服务 实现接口的三个方法
type BaseSearch struct {
	Url       string
	Collector *colly.Collector
	Data      []HotSearchDto
	Limit     int
}

//func (bs *BaseSearch) Ajax() {
//	fmt.Println("base ajax")
//	bs.Data = []HotSearchDto{}
//}

func (bs *BaseSearch) BindHTMLSelector() {
	fmt.Println("Nothing to do")
}

func (bs *BaseSearch) Visit() []HotSearchDto {
	bs.Data = []HotSearchDto{}
	err := bs.Collector.Visit(bs.Url)
	if err != nil {
		fmt.Printf("%v\n", err)
		return []HotSearchDto{}
	}

	return bs.Data
}

// HotSearchRouter 路由选择器
type HotSearchRouter struct {
	Router map[int]IHotSearch
}

func (r *HotSearchRouter) newCollector() *colly.Collector {
	return colly.NewCollector(
		colly.IgnoreRobotsTxt(),
		colly.AllowURLRevisit(),
		func(collector *colly.Collector) {
			// 设置随机ua
			extensions.RandomUserAgent(collector)
			// 设置cookiejar
			cjar, err := cookiejar.New(nil)
			if err == nil {
				collector.SetCookieJar(cjar)
			}
		})
}

func (r *HotSearchRouter) Init() {
	r.Router = make(map[int]IHotSearch)
	r.Router[BaiDu] = &BaiDuHotSearch{BaseSearch{
		Url:       "https://top.baidu.com/board?tab=realtime",
		Collector: r.newCollector(),
	}}
	r.Router[BaiDu].BindHTMLSelector()

	r.Router[WeiBo] = &WeiBoHotSearch{BaseSearch{
		Url:       "https://weibo.com/ajax/side/hotSearch",
		Collector: r.newCollector(),
	}}
	r.Router[WeiBo].BindHTMLSelector()

	r.Router[TouTiao] = &TouTiaoHotSearch{BaseSearch{
		Url:       "https://www.toutiao.com/hot-event/hot-board/?origin=toutiao_pc",
		Collector: r.newCollector(),
	}}
	r.Router[TouTiao].BindHTMLSelector()

	r.Router[ZhiHu] = &ZhiHuHotSearch{BaseSearch{
		Url:       "https://tophub.today/n/mproPpoq6O",
		Collector: r.newCollector(),
	}}
	r.Router[ZhiHu].BindHTMLSelector()
}

func (r *HotSearchRouter) Route(key int) IHotSearch {
	return r.Router[key]
}

// BaiDuHotSearch 百度
type BaiDuHotSearch struct {
	BaseSearch
}

func (hs *BaiDuHotSearch) BindHTMLSelector() {
	hs.Collector.OnHTML(".container-bg_lQ801", func(element *colly.HTMLElement) {
		element.DOM.Find(".category-wrap_iQLoo").Each(func(index int, itemSelection *goquery.Selection) {
			contentSelection := itemSelection.ChildrenFiltered(".content_1YWBm")
			title := contentSelection.Find(".c-single-text-ellipsis").Text()
			href, _ := contentSelection.ChildrenFiltered("a").Attr("href")
			desc := contentSelection.Find(".hot-desc_1m_jR").First().Text()
			if len(desc) > 0 {
				desc = strings.ReplaceAll(desc, "查看更多>", "")
			}
			hot := itemSelection.Find(".trend_2RttY .hot-index_1Bl1a").Text()
			hs.Data = append(hs.Data, HotSearchDto{
				Sort:  index,
				Title: title,
				Url:   href,
				Desc:  desc,
				Hot:   hot,
			})
		})
	})
}

// WeiBoHotSearch 微博
type WeiBoHotSearch struct {
	BaseSearch
}

// BindHTMLSelector 微博 重写父类ajax获取数据
func (hs *WeiBoHotSearch) BindHTMLSelector() {
	hs.Collector.OnResponse(func(response *colly.Response) {
		if response.StatusCode == http.StatusOK {
			var tempMap = make(map[string]interface{})
			err := json.Unmarshal(response.Body, &tempMap)
			if err != nil {
				log.Errorf("json反序列化失败:%v", err)
			}
			realtimeArr := tempMap["data"].(map[string]interface{})["realtime"].([]interface{})
			for i, v := range realtimeArr {
				word := v.(map[string]interface{})["word"].(string)
				wordScheme := word
				wsi := v.(map[string]interface{})["word_scheme"]
				if wsi != nil {
					wordScheme = wsi.(string)
				}
				ci := v.(map[string]interface{})["category"]
				category := "分类"
				if ci != nil {
					category = ci.(string)
				}

				hot := v.(map[string]interface{})["num"].(float64)
				hs.Data = append(hs.Data, HotSearchDto{
					Sort:  i,
					Title: word,
					Url:   "https://s.weibo.com/weibo?q=" + url.QueryEscape(wordScheme),
					Desc:  fmt.Sprintf("%s: %s", category, word),
					Hot:   strconv.Itoa(int(hot)),
				})
			}
			return
		}
		log.Errorf("读取微博ajax接口失败:%s", string(response.Body))
	})
}

// 头条
type TouTiaoHotSearch struct {
	BaseSearch
}

func (hs *TouTiaoHotSearch) BindHTMLSelector() {
	hs.Collector.OnResponse(func(response *colly.Response) {
		if response.StatusCode == http.StatusOK {
			var tempMap = make(map[string]interface{})
			err := json.Unmarshal(response.Body, &tempMap)
			if err != nil {
				log.Errorf("json反序列化失败:%v", err)
			}
			dataArr := tempMap["data"].([]interface{})
			for i, v := range dataArr {
				title := v.(map[string]interface{})["Title"].(string)
				link := v.(map[string]interface{})["Url"].(string)
				hot := v.(map[string]interface{})["HotValue"].(string)
				labelInter := v.(map[string]interface{})["LabelDesc"]
				desc := title
				if labelInter != nil {
					desc = labelInter.(string) + ":" + desc
				}
				hs.Data = append(hs.Data, HotSearchDto{
					Sort:  i,
					Title: title,
					Url:   link,
					Desc:  desc,
					Hot:   hot,
				})
			}
			return
		}
		log.Errorf("读取头条ajax接口失败:%s", string(response.Body))
	})
}

// 知乎
type ZhiHuHotSearch struct {
	BaseSearch
}

func (hs *ZhiHuHotSearch) BindHTMLSelector() {
	hs.Collector.OnHTML(".Zd-p-Sc", func(element *colly.HTMLElement) {
		element.DOM.Find(".cc-dc-c tbody").First().Find("tr").Each(func(i int, selection *goquery.Selection) {
			title := selection.Find(".al a").Text()
			href, _ := selection.Find(".al a").Attr("href")
			hot := selection.Find("td:nth-child(3)").Text()
			hs.Data = append(hs.Data, HotSearchDto{
				Sort:  i,
				Title: title,
				Url:   element.Request.AbsoluteURL(href),
				Desc:  title,
				Hot:   hot,
			})
		})
	})
}

前端核心代码 App.vue

<script setup>
import {reactive} from 'vue'
// import HelloWorld from './components/HelloWorld.vue'
import txImg from './assets/images/tx.gif'
import {Greet} from '../wailsjs/go/main/App'
import { onMounted } from 'vue'
import {
    PieChartOutlined,
    BarChartOutlined,
    DotChartOutlined,
    LineChartOutlined} from '@ant-design/icons-vue';

onMounted(() => {
    tabClick(0)
})
const data = reactive({
    activeKey: 0,
    image: [
        "https://zos.alipayobjects.com/rmsportal/jkjgkEfvpUPVyRjUImniVslZfWPnJuuZ.png",
        txImg,
        "https://zos.alipayobjects.com/rmsportal/jkjgkEfvpUPVyRjUImniVslZfWPnJuuZ.png",
        txImg,
        "https://zos.alipayobjects.com/rmsportal/jkjgkEfvpUPVyRjUImniVslZfWPnJuuZ.png",
    ],
    hotData: {
        0: [],
        1: [],
        2: [],
        3: []
    },
    loading: false
})

function handleMouse(e) {
    // e.preventDefault();
}

function tabClick(index) {
    data.loading = true
    Greet(index).then(result => {
        console.log(result)
        data.loading = false
        data.hotData[index] = result
    })
}

function urlClick(url) {
    window.runtime.BrowserOpenURL(url)
    return false
}

</script>

<template>
  <div style="width: 100%; height: 100%;overflow: hidden;padding-bottom: 200px" @contextmenu="handleMouse">
      <div style="text-align: center">
          <a-image :width="200"
                   :src="data.image[data.activeKey]"/>
      </div>

      <div style="padding: 10px;overflow: auto;height: 100%;">
          <a-tabs v-model:activeKey="data.activeKey" type="card" @tabClick="tabClick">
              <a-tab-pane :key="0">
                  <template  #tab>
                <span>
                  <pie-chart-outlined />
                  百度
                </span>
                  </template>
                  <a-list item-layout="horizontal" :data-source="data.hotData[0]" rowKey="sort" :loading="data.loading">
                      <template #renderItem="{ item }">
                          <a-list-item>
                              <a-list-item-meta :description="item.desc">
                                  <template #title>
                                      <a href="javascript:" @click="urlClick(item.url)">{{ item.title }}</a>
                                  </template>
                                  <template #avatar>
                                      <a-avatar v-if="item.sort < 3" style="background-color: red">{{ item.sort + 1 }}</a-avatar>
                                      <a-avatar v-else-if="item.sort >=3 && item.sort < 6" style="background-color: #f56a00">{{ item.sort + 1 }}</a-avatar>
                                      <a-avatar v-else-if="item.sort >=6 && item.sort < 9" style="background-color: #c8b50eff">{{ item.sort + 1 }}</a-avatar>
                                      <a-avatar v-else>{{ item.sort + 1 }}</a-avatar>
                                  </template>
                              </a-list-item-meta>
                              <div>热度:
                                  <span v-if="item.sort < 3" style="color: red">{{ item.hot }}</span>
                                  <span v-else-if="item.sort >=3 && item.sort < 6" style="color: #f56a00">{{ item.hot }}</span>
                                  <span v-else-if="item.sort >=6 && item.sort < 9" style="color: #c8b50eff">{{ item.hot }}</span>
                                  <span v-else>{{ item.hot }}</span>
                              </div>
                          </a-list-item>
                      </template>
                  </a-list>
              </a-tab-pane>
              <a-tab-pane :key="1">
                  <template  #tab>
                <span>
                  <bar-chart-outlined />
                  微博
                </span>
                  </template>
                  <a-list item-layout="horizontal" :data-source="data.hotData[1]" rowKey="sort" :loading="data.loading">
                      <template #renderItem="{ item }">
                          <a-list-item>
                              <a-list-item-meta :description="item.desc">
                                  <template #title>
                                      <a href="javascript:" @click="urlClick(item.url)">{{ item.title }}</a>
                                  </template>
                                  <template #avatar>
                                      <a-avatar v-if="item.sort < 3" style="background-color: red">{{ item.sort + 1 }}</a-avatar>
                                      <a-avatar v-else-if="item.sort >=3 && item.sort < 6" style="background-color: #f56a00">{{ item.sort + 1 }}</a-avatar>
                                      <a-avatar v-else-if="item.sort >=6 && item.sort < 9" style="background-color: #c8b50eff">{{ item.sort + 1 }}</a-avatar>
                                      <a-avatar v-else>{{ item.sort + 1 }}</a-avatar>
                                  </template>
                              </a-list-item-meta>
                              <div>热度:
                                  <span v-if="item.sort < 3" style="color: red">{{ item.hot }}</span>
                                  <span v-else-if="item.sort >=3 && item.sort < 6" style="color: #f56a00">{{ item.hot }}</span>
                                  <span v-else-if="item.sort >=6 && item.sort < 9" style="color: #c8b50eff">{{ item.hot }}</span>
                                  <span v-else>{{ item.hot }}</span>
                              </div>
                          </a-list-item>
                      </template>
                  </a-list>
              </a-tab-pane>
              <a-tab-pane :key="2">
                  <template  #tab>
                <span>
                  <dot-chart-outlined />
                  头条
                </span>
                  </template>
                  <a-list item-layout="horizontal" :data-source="data.hotData[2]" rowKey="sort" :loading="data.loading">
                      <template #renderItem="{ item }">
                          <a-list-item>
                              <a-list-item-meta :description="item.desc">
                                  <template #title>
                                      <a href="javascript:" @click="urlClick(item.url)">{{ item.title }}</a>
                                  </template>
                                  <template #avatar>
                                      <a-avatar v-if="item.sort < 3" style="background-color: red">{{ item.sort + 1 }}</a-avatar>
                                      <a-avatar v-else-if="item.sort >=3 && item.sort < 6" style="background-color: #f56a00">{{ item.sort + 1 }}</a-avatar>
                                      <a-avatar v-else-if="item.sort >=6 && item.sort < 9" style="background-color: #c8b50eff">{{ item.sort + 1 }}</a-avatar>
                                      <a-avatar v-else>{{ item.sort + 1 }}</a-avatar>
                                  </template>
                              </a-list-item-meta>
                              <div>热度:
                                  <span v-if="item.sort < 3" style="color: red">{{ item.hot }}</span>
                                  <span v-else-if="item.sort >=3 && item.sort < 6" style="color: #f56a00">{{ item.hot }}</span>
                                  <span v-else-if="item.sort >=6 && item.sort < 9" style="color: #c8b50eff">{{ item.hot }}</span>
                                  <span v-else>{{ item.hot }}</span>
                              </div>
                          </a-list-item>
                      </template>
                  </a-list>
              </a-tab-pane>
              <a-tab-pane :key="3">
                  <template  #tab>
                <span>
                  <line-chart-outlined />
                  知乎
                </span>
                  </template>
                  <a-list item-layout="horizontal" :data-source="data.hotData[3]" rowKey="sort" :loading="data.loading">
                      <template #renderItem="{ item }">
                          <a-list-item>
                              <a-list-item-meta :description="item.desc">
                                  <template #title>
                                      <a href="javascript:" @click="urlClick(item.url)">{{ item.title }}</a>
                                  </template>
                                  <template #avatar>
                                      <a-avatar v-if="item.sort < 3" style="background-color: red">{{ item.sort + 1 }}</a-avatar>
                                      <a-avatar v-else-if="item.sort >=3 && item.sort < 6" style="background-color: #f56a00">{{ item.sort + 1 }}</a-avatar>
                                      <a-avatar v-else-if="item.sort >=6 && item.sort < 9" style="background-color: #c8b50eff">{{ item.sort + 1 }}</a-avatar>
                                      <a-avatar v-else>{{ item.sort + 1 }}</a-avatar>
                                  </template>
                              </a-list-item-meta>
                              <div>热度:
                                  <span v-if="item.sort < 3" style="color: red">{{ item.hot }}</span>
                                  <span v-else-if="item.sort >=3 && item.sort < 6" style="color: #f56a00">{{ item.hot }}</span>
                                  <span v-else-if="item.sort >=6 && item.sort < 9" style="color: #c8b50eff">{{ item.hot }}</span>
                                  <span v-else>{{ item.hot }}</span>
                              </div>
                          </a-list-item>
                      </template>
                  </a-list>
              </a-tab-pane>
          </a-tabs>
      </div>

  </div>

</template>

<style>
.ant-layout-header {
    background-color: #7cb305;
}
</style>

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/580306.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

网络——网络协议总结

个人简介&#xff1a;云计算网络运维专业人员&#xff0c;了解运维知识&#xff0c;掌握TCP/IP协议&#xff0c;每天分享网络运维知识与技能。座右铭&#xff1a;海不辞水&#xff0c;故能成其大&#xff1b;山不辞石&#xff0c;故能成其高。 个人主页&#xff1a;小李会科技的…

笔记本安装CentOS

目标: 1.利用闲置笔记本 2.省电/提高利用率/不安装图形桌面/最小化安装/附加选项:开发工具 step1&#xff1a;镜像下载 CentOS-7.9 163镜像 阿里云镜像 清华大学镜像 随便选一个 step2: 下载U盘系统盘制作工具Rufus U盘写入镜像/安装 step3: 安装完毕进入系统 …

2023年上半年软考学习总结(超详细)

目录 前言 一、背景1.1上次考试感受&#xff1a;1.2这次考试感受&#xff1a;1.3方法&#xff1a; 二、 过程2.1计算机网络概论计算机组成数据表示相关知识校验码相关知识计算机体系结构网络体系结构OSI/RM和TCP/IP计算机安全性可靠性性能评价 2.2 程序设计语言基础知识编译和解…

如何在华为OD机试中获得满分?Java实现【求最小公倍数】一文详解!

✅创作者&#xff1a;陈书予 &#x1f389;个人主页&#xff1a;陈书予的个人主页 &#x1f341;陈书予的个人社区&#xff0c;欢迎你的加入: 陈书予的社区 &#x1f31f;专栏地址: Java华为OD机试真题&#xff08;2022&2023) 文章目录 1. 题目描述2. 输入描述3. 输出描述…

【Unity3D】广告牌特效

1 前言 广告牌特效是指&#xff1a;空间中的一个 2D 对象始终&#xff08;或尽可能&#xff09;面向相机&#xff0c;使得用户能够尽可能看清楚该 2D 物体。广告牌特效一共有以下 3 种&#xff1a; 正视广告牌&#xff1a;广告牌始终以正视图姿态面向相机&#xff0c;即广告牌…

异常检测进阶梳理1:Tabular AD视角

接触异常检测领域也有一年多的时间了&#xff0c;过程中遇到不少坑&#xff0c;知识体系也在不断更新完善&#xff0c;这里以专题的形式进行知识体系的梳理~ 异常检测&#xff08;Anomaly Detection, AD&#xff09;领域内的划分体系较多&#xff0c;这里基于异常检测最常用到的…

【P39】JMeter 随机顺序控制器(Random Order Controller)

文章目录 一、随机顺序控制器&#xff08;Random Order Controller&#xff09;参数说明二、测试计划设计2.1、测试计划一2.2、测试计划二 一、随机顺序控制器&#xff08;Random Order Controller&#xff09;参数说明 可以让控制器内部的组件按随机顺序执行&#xff08;内部…

Alibaba Arthas学习与使用

Alibaba Arthas学习与使用 目录 下载安装卸载退出快捷键重点部分: 命令 dashboardthreadjvmsyspropsysenvvmoptiongetstaticognlscsmjadmcredefinedumpclassloadermonitorwatchtracestackttoptionsprofiler 下载安装 # 下载 curl -O https://alibaba.github.io/arthas/art…

Apache Kafka - 如何实现可靠的数据传递

文章目录 可靠的数据传递导图 可靠的数据传递 Kafka 通过以下几个方面实现可靠的数据传递: 分区副本 - Kafka 的分区有多个副本,如果某个副本失效,其他副本可以继续服务。生产者重试 - 生产者在发送消息失败时会自动重试,一直到成功发送或者达到最大重试次数。批量确认 - 生产…

云服务器和专用服务器之间的区别

在当今数字化时代&#xff0c;服务器是构建和支持各种应用和服务的基础设施之一。随着技术的发展和需求的增加&#xff0c;出现了不同类型的服务器&#xff0c;其中最常见的是云服务器和专用服务器。本文将详细介绍云服务器和专用服务器之间的区别&#xff0c;以帮助您更好地了…

SpringSecurity从入门到实战

SpringSecurity从入门到实战 0. 简介 ​ Spring Security 是 Spring 家族中的一个安全管理框架。相比与另外一个安全框架Shiro&#xff0c;它提供了更丰富的功能&#xff0c;社区资源也比Shiro丰富。 ​ 一般来说中大型的项目都是使用SpringSecurity 来做安全框架。小项目有…

【LAMP架构】

目录 一、LAMP架构1、组件作用 二、编译安装Apache httpd服务2、安装环境依赖包3、配置软件模块4、编译及安装5、优化配置文件路径&#xff0c;并把httpd服务的可执行程序文件放入路径环境变量的目录中便于系统识别6.添加httpd系统服务7.修改httpd 服务配置文件8.浏览器访问验证…

【云原生|探索 Kubernetes 系列 5】简化 Kubernetes 的部署,深入解析其工作流程

前言 大家好&#xff0c;我是秋意零。 在前面 4 个章节中&#xff0c;我们充分了解了容器技术和 Kubernes 原生时代引擎的架构和设计思想&#xff0c;今天分享的主要内容是&#xff0c;探索 Kubernetes 部署&#xff0c;深入解析其工作流程 &#x1f47f; 简介 &#x1f3e0…

[元带你学: eMMC协议详解 11] Data transfer mode 数据传输模式

依JEDEC eMMC 5.1及经验辛苦整理&#xff0c;付费内容&#xff0c;禁止转载。 所在专栏 《元带你学: eMMC协议详解》 全文2300 字&#xff0c; 主要介绍数据传输模式&#xff0c;本节数据传输模式图非常重要。数据传输模式图可以说是我查对过最频繁的图之一了。eMMC 限定了这么…

数据库基础——6.排序与分页

这篇文章来讲一下数据库的排序与分页 目录 1.排序数据 1.1排序规则 1.2 单列排序 1.3 多列排序 2.分页 2.1 背景 2.2 实现规则 2.3 拓展 1.排序数据 1.1排序规则 使用 ORDER BY 子句排序 ASC&#xff08;ascend&#xff09;&#xff1a;升序 &#xff1b; DESC&a…

怎么一键保存浏览器中打开的所有标签页?

2023年5月28日&#xff0c;周日晚上&#xff1a; 被这个问题困扰很久了&#xff0c;之前一直不知道怎么全部保存浏览器中打开的所有标签页&#xff0c;浪费了不少的时间&#xff0c;今天下午偶然发现了解决办法。 很简单&#xff0c;直接在浏览器里使用快捷键“CtrlShiftD” …

本周大新闻|传Meta与Magic Leap谈专利授权;PS VR2前6周出货60万台

本周XR大新闻&#xff0c;AR方面&#xff0c;苹果XR项目核心高管曝光&#xff1b;传Meta与Magic Leap洽谈专利授权合作&#xff1b;歌尔光学公布新一代AR显示模组&#xff1b;Lumus公布二代波导Z-Lens最新细节&#xff1b;JBD X-cube发布全彩Micro LED光机Hummingbird&#xff…

我用GPT写了一个关于GPT的文章,大家看看写的如何

声明&#xff1a;以下内容来自GPT-3.5大模型&#xff08;图片除外&#xff09; 目录 I. 引言 1.1 研究背景和意义 1.2 现有研究综述 II. ChatGPT技术介绍 2.1 ChatGPT技术原理 2.2 ChatGPT技术优势 III. ChatGPT技术在智能客服中的应用和挑战 3.1 ChatGPT技术在智能客…

【TOP生物信息】使用Seurat包自带的方法进行单细胞类型注释

扫码关注下方公粽号&#xff0c;回复推文合集&#xff0c;获取400页单细胞学习资源&#xff01; 本文共计1318字&#xff0c;阅读大约需要4分钟&#xff0c;目录如下&#xff1a; 方法简介演示数据来源代码演示小结代码参考往期单细胞系统教程 单细胞自动注释细胞类型的软件和…

华为OD机试真题B卷 Java 实现【查字典】,附详细解题思路

一、题目描述 输入一个单词前缀和一个字典&#xff0c;输出包含该前缀的单词。 二、输入描述 单词前缀字典长度字典。 字典是一个有序单词数组。 输入输出都是小写。 三、输出描述 所有包含该前缀的单词&#xff0c;多个单词换行输出。 若没有则返回-1。 四、解题思路…