RSS Can:将网站信息流转换为 RSS 订阅源(三)

news2024/11/24 13:37:28

第三篇内容里,我们来聊聊把结构化数据转换为可以订阅的 RSS 订阅数据源。

写在前面

通过前两篇文章《RSS Can:使用 Golang 实现更好的 RSS Hub 服务(一)》和《RSS Can:借助 V8 让 Golang 应用具备动态化能力(二)》,我们已经能够将网站上的资讯信息,通过动态配置的方式整理成结构化的数据。

本篇文章,我们来简单聊聊,如何将这些结构化的数据变成可订阅的 RSS 订阅源,让网站的数据能够和我们的 RSS 阅读器“连通”起来。

RSS 格式标准

在聊代码实现之前,不论是作为开发者、还是作为 RSS 产品用户,了解下 RSS 格式标准还是非常有必要的。

互联网上关于 “RSS” 的格式标准比较出名的有三种流派,分别是:Atom、 RSS、JSON Feed,第三种出现于 RSS 式微,应用和呼声都不大,因此主要网络应用支持的格式都在集中在前两者:RSS 和 Atom。

TLDR,简单来说,如果你是内容提供方,你希望你的内容能够被更多的人用各种各样的 RSS 客户端访问,选择一定被支持的 RSS 2.0 将保持非常好的兼容性。如果你是读者,考虑到持续追踪文章的更新,以及更好的阅读体验,当网站同时提供多种 RSS 订阅格式时,不妨优先选择 Atom 格式的 RSS 订阅源

当然,本文中我们将借助开源软件库一并将前两篇文章中整理好的数据,一并输出为三种格式。(反正没什么成本

Atom 格式相比较 RSS 2.0 的主要优势

如果你不想针对 “RSS” 进行细致的开发,我们只了解使用即可,这个小节的内容可以跳过。

  1. 能够标记字段中的 HTML 内容是否经过转义或编码,方便开发者在渲染时使用数据。
  2. 不再需要将内容的“正文”和“摘要”都混在 description 字段中,提供了新的 summary 字段,可以区分“摘要”和“正文”,同时允许在正文中添加非文本内容。
  3. “RSS” 存在几个变体版本,Atom 更为稳定和一致。
  4. 提供了符合 XML 标准的命名空间、能够使用 XML 内置的标签来支持相对地址的描述、能够使用 XML 内置标签告诉订阅者内容语言、支持 XML Schema,这些 RSS 2.0 都不具备。
  5. 每一个信息条目具备唯一 ID,订阅者能够追踪具体的内容的更新。
  6. 有统一明确的时间表示规范,方便程序进行处理。
  7. 在 IANA 注册了 application/atom+xml 的 MIME 媒体类型,将其变成了标准规范,RSS 使用的 application/rss+xml 还没有纳入标准。

使用 Go 转换数据为 RSS Feed 格式

Go 生态中支持生成 RSS Feed 的软件包有很多,我选择的是有十年维护历史的 gorilla/feeds。虽然在这个月的 9 号,维护团队宣布开源组织内的仓库都将进入“休眠状态”(存档),不再进行维护。

但是,对于我们的需求来说,RSS 是一个“古老、稳定”的协议,gorilla/feeds 已经经过了长时间的验证,所以选择使用它还是比较合适的。加之,对于这类不活跃维护或者停止维护的项目,还可以通过 Go 的特殊的包管理方式,来帮助我们管理代码,做代码维护变更,这块我们后续的文章中会提到。

Gorilla Feeds 的一般使用

我们先来了解如何使用 Gorilla Feeds 来生成 RSS Feed 格式的订阅源,先引入软件包:

import (
	"time"
	"github.com/gorilla/feeds"
)

这里之所以同时引入了 time ,是因为我不想麻烦的手动造数据。因为不同的 RSS 格式,对于时间的要求并不相同,所以关于时间的处理,后续展开一篇内容来聊,或许更为合适。

我们先以之前发布过的文章为例,编写一段 Mock 数据,等会用来测试 RSS 订阅源的生成:

now := time.Now()
feed := &feeds.Feed{
	Title:       "苏洋博客",
	Link:        &feeds.Link{Href: "https://soulteary.com/"},
	Description: "醉里不知天在水,满船清梦压星河。",
	Author:      &feeds.Author{Name: "soulteary", Email: "soulteary@gmail.com"},
	Created:     now,
}

feed.Items = []*feeds.Item{
	{
		Title:       "RSS Can:借助 V8 让 Golang 应用具备动态化能力(二)",
		Link:        &feeds.Link{Href: "https://soulteary.com/2022/12/13/rsscan-make-golang-applications-with-v8-part-2.html"},
		Description: "继续聊聊之前做过的一个小东西的踩坑历程,如果你也想高效获取信息,或许这个系列的内容会对你有用。",
		Author:      &feeds.Author{Name: "soulteary", Email: "soulteary@qq.com"},
		Created:     now,
	},
	{
		Title:       "RSS Can:使用 Golang 实现更好的 RSS Hub 服务(一)",
		Link:        &feeds.Link{Href: "https://soulteary.com/2022/12/12/rsscan-better-rsshub-service-build-with-golang-part-1.html"},
		Description: "聊聊之前做过的一个小东西的踩坑历程,如果你也想高效获取信息,或许这个系列的内容会对你有用。这个事情涉及的东西比较多,所以我考虑拆成一个系列来聊,每篇的内容不要太长,整理负担和阅读负担都轻一些。本篇是系列第一篇内容。",
		Author:      &feeds.Author{Name: "soulteary", Email: "soulteary@gmail.com"},
		Created:     now,
	},
	{
		Title:       "在搭载 M1 及 M2 芯片 MacBook设备上玩 Stable Diffusion 模型",
		Link:        &feeds.Link{Href: "https://soulteary.com/2022/12/10/play-the-stable-diffusion-model-on-macbook-devices-with-m1-and-m2-chips.html"},
		Description: "本篇文章,我们聊了如何使用搭载了 Apple Silicon 芯片(M1 和 M2 CPU)的 MacBook 设备上运行 Stable Diffusion 模型。",
		Created:     now,
	},
	{
		Title:       "使用 Docker 来快速上手中文 Stable Diffusion 模型:太乙",
		Link:        &feeds.Link{Href: "https://soulteary.com/2022/12/09/use-docker-to-quickly-get-started-with-the-chinese-stable-diffusion-model-taiyi.html"},
		Description: "本篇文章,我们聊聊如何使用 Docker 快速运行中文 Stable Diffusion 模型:太乙。 ",
		Created:     now,
	},
}

接着,编写简单的调用语句,数据就可以“转换”成我们需要的结果啦:

atom, err := feed.ToAtom()
if err != nil {
	log.Fatal(err)
}

rss, err := feed.ToRss()
if err != nil {
	log.Fatal(err)
}

json, err := feed.ToJSON()
if err != nil {
	log.Fatal(err)
}

fmt.Println(atom, "\n", rss, "\n", json)

将上面的代码放到可以被调用的函数中进行测试(比如 main),程序执行后,我们将看到类似下面的结果:

<?xml version="1.0" encoding="UTF-8"?><feed xmlns="http://www.w3.org/2005/Atom">
  <title>苏洋博客</title>
  <id>https://soulteary.com/</id>
  <updated>2022-12-14T12:29:55+08:00</updated>
  <subtitle>醉里不知天在水,满船清梦压星河。</subtitle>
  <link href="https://soulteary.com/"></link>
  <author>
    <name>soulteary</name>
    <email>soulteary@gmail.com</email>
  </author>
  <entry>
    <title>RSS Can:借助 V8 让 Golang 应用具备动态化能力(二)</title>
    <updated>2022-12-14T12:29:55+08:00</updated>
    <id>tag:soulteary.com,2022-12-14:/2022/12/13/rsscan-make-golang-applications-with-v8-part-2.html</id>
    <link href="https://soulteary.com/2022/12/13/rsscan-make-golang-applications-with-v8-part-2.html" rel="alternate"></link>
    <summary type="html">继续聊聊之前做过的一个小东西的踩坑历程,如果你也想高效获取信息,或许这个系列的内容会对你有用。</summary>
    <author>
      <name>soulteary</name>
      <email>soulteary@qq.com</email>
    </author>
  </entry>
...
...
</feed> 

<?xml version="1.0" encoding="UTF-8"?><rss version="2.0" xmlns:content="http://purl.org/rss/1.0/modules/content/">
  <channel>
    <title>苏洋博客</title>
    <link>https://soulteary.com/</link>
    <description>醉里不知天在水,满船清梦压星河。</description>
    <managingEditor>soulteary@gmail.com (soulteary)</managingEditor>
    <pubDate>Wed, 14 Dec 2022 12:29:55 +0800</pubDate>
    <item>
      <title>RSS Can:借助 V8 让 Golang 应用具备动态化能力(二)</title>
      <link>https://soulteary.com/2022/12/13/rsscan-make-golang-applications-with-v8-part-2.html</link>
      <description>继续聊聊之前做过的一个小东西的踩坑历程,如果你也想高效获取信息,或许这个系列的内容会对你有用。</description>
      <author>soulteary</author>
      <pubDate>Wed, 14 Dec 2022 12:29:55 +0800</pubDate>
    </item>
    <item>
      <title>RSS Can:使用 Golang 实现更好的 RSS Hub 服务(一)</title>
      <link>https://soulteary.com/2022/12/12/rsscan-better-rsshub-service-build-with-golang-part-1.html</link>
      <description>聊聊之前做过的一个小东西的踩坑历程,如果你也想高效获取信息,或许这个系列的内容会对你有用。这个事情涉及的东西比较多,所以我考虑拆成一个系列来聊,每篇的内容不要太长,整理负担和阅读负担都轻一些。本篇是系列第一篇内容。</description>
      <author>soulteary</author>
      <pubDate>Wed, 14 Dec 2022 12:29:55 +0800</pubDate>
    </item>
...
...
  </channel>
</rss>

{
  "version": "https://jsonfeed.org/version/1",
  "title": "苏洋博客",
  "home_page_url": "https://soulteary.com/",
  "description": "醉里不知天在水,满船清梦压星河。",
  "author": {
    "name": "soulteary"
  },
  "items": [
    {
      "id": "",
      "url": "https://soulteary.com/2022/12/13/rsscan-make-golang-applications-with-v8-part-2.html",
      "title": "RSS Can:借助 V8 让 Golang 应用具备动态化能力(二)",
      "summary": "继续聊聊之前做过的一个小东西的踩坑历程,如果你也想高效获取信息,或许这个系列的内容会对你有用。",
      "date_published": "2022-12-14T12:29:55.50867+08:00",
      "author": {
        "name": "soulteary"
      }
    },
...
...
  ]
}

上面输出的日志结果中,就包含了前文中提到的三种格式,能够覆盖绝大多数的 RSS 客户端的订阅使用。

连接来自网站的信息

在之前的文章中,我们将前文中通过动态配置解析目标网站,并将网站中信息转换为了 Go 中的数据结构。在了解了 Gorilla Feeds 是如何输出 RSS 格式之后,我们只需要将两者“连接”到一起,就能够得到 RSS 格式的资讯订阅源啦。

首先,针对前文中提到的“根据配置解析网站信息”的函数做一些调整:

func getWebsiteDataWithConfig(config define.JavaScriptConfig) (result define.BodyParsed) {
	doc := network.GetRemoteDocument("https://36kr.com/", "utf-8")
	if doc.Body == "" {
		return result
	}

	return parser.ParsePageByGoQuery(doc, func(document *goquery.Document) []define.InfoItem {
		var items []define.InfoItem
		document.Find(config.ListContainer).Each(func(i int, s *goquery.Selection) {
			var item define.InfoItem

			title := strings.TrimSpace(s.Find(config.Title).Text())
			author := strings.TrimSpace(s.Find(config.Author).Text())
			time := strings.TrimSpace(s.Find(config.DateTime).Text())
			category := strings.TrimSpace(s.Find(config.Category).Text())
			description := strings.TrimSpace(s.Find(config.Description).Text())

			href, _ := s.Find(config.Link).Attr("href")
			link := strings.TrimSpace(href)

			item.Title = title
			item.Author = author
			item.Date = time
			item.Category = category
			item.Description = description
			item.Link = link
			items = append(items, item)
		})
		return items
	})
}

上面的函数正常运行的情况下,就可以得到一个包含了结构化数据的数组。

接下来,写一个简单的函数,调用 Gorilla Feeds 生成我们需要的 RSS 订阅源:

func generateFeeds(data define.BodyParsed) {
	now := time.Now()

	rssFeed := &feeds.Feed{
		Title:   "36Kr",
		Link:    &feeds.Link{Href: "https://36kr.com/"},
		Created: now,
	}

	for _, data := range data.Body {
		feedItem := feeds.Item{
			Title:       data.Title,
			Author:      &feeds.Author{Name: data.Author},
			Description: data.Description,
			Link:        &feeds.Link{Href: data.Link},
			// 时间处理这块比较麻烦,后续文章再展开
			Created: now,
		}
		rssFeed.Items = append(rssFeed.Items, &feedItem)
	}

	atom, err := rssFeed.ToAtom()
	if err != nil {
		log.Fatal(err)
	}

	rss, err := rssFeed.ToRss()
	if err != nil {
		log.Fatal(err)
	}

	json, err := rssFeed.ToJSON()
	if err != nil {
		log.Fatal(err)
	}

	fmt.Println(atom, "\n", rss, "\n", json)
}

最后,调整程序的调用函数,以便于我们进行测试,将 RSS 生成结果打印到终端日志里:

func main() {
	jsApp, _ := os.ReadFile("./config/config.js")
	inject := string(jsApp)

	jsConfig, err := javascript.RunCode(inject, "JSON.stringify(getConfig());")
	if err != nil {
		fmt.Println(err)
		return
	}

	config, err := parser.ParseConfigFromJSON(jsConfig)
	if err != nil {
		fmt.Println(err)
		return
	}
	data := getWebsiteDataWithConfig(config)
	generateFeeds(data)
}

使用 go run main.go 执行程序,我们将得到符合预期的结果:

<?xml version="1.0" encoding="UTF-8"?><feed xmlns="http://www.w3.org/2005/Atom">
  <title>36Kr</title>
  <id>https://36kr.com/</id>
  <updated>2022-12-14T13:41:37+08:00</updated>
  <link href="https://36kr.com/"></link>
  <entry>
    <title>iOS 16.2来了,这7个新功能值得关注</title>
    <updated>2022-12-14T13:41:37+08:00</updated>
    <id>tag:,2022-12-14:/p/2043412066405640</id>
    <link href="/p/2043412066405640" rel="alternate"></link>
    <summary type="html">Apple 画的饼终于来了。</summary>
    <author>
      <name>少数派</name>
    </author>
  <entry>
    <title>如何更好地思考:人只能获得自己认知内的成就</title>
    <updated>2022-12-14T13:41:37+08:00</updated>
    <id>tag:,2022-12-14:/p/2018320727015942</id>
    <link href="/p/2018320727015942" rel="alternate"></link>
    <summary type="html">5个原则,让你成为一个更好的思考者。</summary>
    <author>
      <name>神译局</name>
    </author>
  </entry>
...

搞定了 RSS 客户端可以使用的数据格式,我们来解决“RSS 可订阅”的最后一步,启动一个简单的 Web 服务,将上面的数据变成可访问的接口地址。

使用 Gin 搞定 RSS Web 服务

Gin 是一个优秀的 HTTP Web 框架,它不见得是 Go 生态所有框架中最快的框架,但要论社区活跃度和易用性,妥妥名列前茅。

使用 Gin 启动一个简单的 Web 服务

Gin 对 Golang 的 net/http 能力进行了封装,提供了简单的调用方式,让我们能够启动一个 Web 服务,比如下面这段不到 20 行的代码:

package main

import (
  "net/http"

  "github.com/gin-gonic/gin"
)

func main() {
  r := gin.Default()
  r.GET("/ping", func(c *gin.Context) {
    c.JSON(http.StatusOK, gin.H{
      "message": "pong",
    })
  })
  r.Run()
}

上面的代码在运行之后,会启动一个 Web 服务,默认提供服务的地址是 http://localhost:8080 。当我们在浏览器中访问 /ping,服务器将响应并返回 pong

制作 RSS 订阅数据接口

上文提到过,因为生成不同格式的 RSS 并没有什么成本,所以我们可以将其全部都支持起来,应对各种 RSS 客户端的请求。

实际提供服务的时候,我们需要根据客户端请求的 RSS 格式类型,来输出不同的数据。所以,需要先调整下上文中我们用来生成 RSS Feed 的函数,让它支持根据请求参数中的类型来生成内容:

func generateFeeds(data define.BodyParsed, rssType string) string {
	now := time.Now()

	rssFeed := &feeds.Feed{
		Title:   "36Kr",
		Link:    &feeds.Link{Href: "https://36kr.com/"},
		Created: now,
	}

	for _, data := range data.Body {
		feedItem := feeds.Item{
			Title:       data.Title,
			Author:      &feeds.Author{Name: data.Author},
			Description: data.Description,
			Link:        &feeds.Link{Href: data.Link},
			// 时间处理这块比较麻烦,后续文章再展开
			Created: now,
		}
		rssFeed.Items = append(rssFeed.Items, &feedItem)
	}

	var rss string
	var err error

	switch rssType {
	case "RSS":
		rss, err = rssFeed.ToRss()
	case "ATOM":
		rss, err = rssFeed.ToAtom()
	case "JSON":
		rss, err = rssFeed.ToJSON()
	default:
		rss = ""
	}

	if err != nil {
		fmt.Println(err)
		return ""
	}

	return rss
}

完成了生成函数的调整之后,我们来完成一个简单的功能实现,支持根据不同的 API 请求路径,调用上面的函数输出不同格式的 RSS 订阅源:

route := gin.Default()
route.GET("/:type/", func(c *gin.Context) {
	var rssType RSSType
	if err := c.ShouldBindUri(&rssType); err != nil {
		c.JSON(http.StatusNotFound, gin.H{"msg": err})
		return
	}

	var response string
	var mimetype string
	switch strings.ToUpper(rssType.Type) {
	case "RSS":
		mimetype = "application/rss+xml"
		response = generateFeeds(data, "RSS")
	case "ATOM":
		mimetype = "application/atom+xml"
		response = generateFeeds(data, "ATOM")
	case "JSON":
		mimetype = "application/feed+json"
		response = generateFeeds(data, "JSON")
	}
	c.Data(http.StatusOK, mimetype, []byte(response))
})


route.Run(":8080")

启动服务,我们访问 http://localhost:8080/rsshttp://localhost:8080/atomhttp://localhost:8080/json 中的任意一个地址,就能在浏览器中看到 RSS 订阅源的数据啦。

有不少 RSS 订阅工具支持根据网页中的标签,对 RSS 订阅源进行自动探测,比如 Reeder。

为了方便我们在 Reeder 中进行测试,我们可以将上面的 RSS 订阅源地址都写到一个 HTML 页面中,然后“绑定”到这个 Web 服务的 / 根目录:

const hello = `<!DOCTYPE html>
<html lang="en">
<head>
	<meta charset="UTF-8">
	<meta http-equiv="X-UA-Compatible" content="IE=edge">
	<meta name="viewport" content="width=device-width, initial-scale=1.0">
	<title>RSS Feed Discovery.</title>
	<link rel="alternate" type="application/rss+xml" title="RSS 2.0 Feed" href="http://localhost:8080/rss">
	<link rel="alternate" type="application/atom+xml" title="RSS Atom Feed" href="http://localhost:8080/atom">
	<link rel="alternate" type="application/rss+json" title="RSS JSON Feed" href="http://localhost:8080/json">
</head>
<body>
	RSS Feed Discovery.
</body>
</html>`

route.GET("/", func(c *gin.Context) {
	c.Data(http.StatusOK, "text/html", []byte(hello))
})

重新运行程序,当我们在 Reeder 等 RSS 订阅工具中输入 http://127.0.0.1:8080 的时候,Reeder 会告知我们发现了三个订阅源。因为三个订阅源的数据是一样的,所以这里随便选择哪一个都行(推荐 Atom)。

使用 Reeder 验证 RSS 订阅源有效性

点击“订阅”按钮,来自网站的信息就出现在了 Reeder 的信息列表中啦。

RSS 客户端获取的 RSS 信息列表

至此,我们就初步解决了第一篇文章中提到的,某些不能被 RSS 订阅工具订阅的信息源的订阅问题。至于前两篇文章中提到的“关键词筛选”,“NLP 内容摘要聚合”,我们将在后续的文章中继续展开。

其他:一个隐蔽的内存泄漏隐患

在上篇文章里,为了安全的运行可能出现“死循环”的外部 JavaScript 代码,我们使用了下面的代码来解决问题:

duration := time.Since(start)
select {
case val := <-vals:
	fmt.Fprintf(os.Stderr, "cost time: %v\n", duration)
	return val, nil
case err := <-errs:
	return nil, err
case <-time.After(JS_EXECUTE_TIMEOUT):
	vm := ctx.Isolate()
	vm.TerminateExecution()
	err := <-errs
	fmt.Fprintf(os.Stderr, "execution timeout: %v\n", duration)
	time.Sleep(JS_EXECUTE_THORTTLING)
	return nil, err
}

今天折腾群里的同学 @Etran 提醒,这里存在一处隐秘的内存泄漏问题,time.After() 可能晚于我们接收到 vals 数据执行,导致计时器没有被正确释放。

那么,要如何解决这个问题呢?修正代码很简单:

duration := time.Since(start)
timeout := time.NewTimer(define.JS_EXECUTE_TIMEOUT)

select {
case val := <-vals:
	if !timeout.Stop() {
		<-timeout.C
	}
	fmt.Fprintf(os.Stderr, "cost time: %v\n", duration)
	return val, nil
case err := <-errs:
	return nil, err
case <-timeout.C:
	timeout.Stop()
	vm := ctx.Isolate()
	vm.TerminateExecution()
	err := <-errs
	fmt.Fprintf(os.Stderr, "execution timeout: %v\n", duration)
	time.Sleep(define.JS_EXECUTE_THORTTLING)
	return nil, err
}

最后

写在这篇文章的时候,我再次回顾了 RSS 的发展史,以及核心灵魂人物 David Winter 的从业历史,尝试用我的视角来概要的描绘 RSS 历史长河里的精彩瞬间。

在文章即将发布的时候,我改变了想法,关于 RSS 的故事,或许应该在本系列文章结束的时候再发布更为合适。

–EOF


我们有一个小小的折腾群,里面聚集了一些喜欢折腾的小伙伴。

在不发广告的情况下,我们在里面会一起聊聊软硬件、HomeLab、编程上的一些问题,也会在群里不定期的分享一些技术沙龙的资料。

喜欢折腾的小伙伴,欢迎阅读下面的内容,扫码添加好友。

  • 关于“交友”的一些建议和看法
  • 添加好友,请备注实名和公司或学校、注明来源和目的,否则不会通过审核。
  • 关于折腾群入群的那些事

本文使用「署名 4.0 国际 (CC BY 4.0)」许可协议,欢迎转载、或重新修改使用,但需要注明来源。 署名 4.0 国际 (CC BY 4.0)

本文作者: 苏洋

创建时间: 2022年12月14日
统计字数: 11361字
阅读时间: 23分钟阅读
本文链接: https://soulteary.com/2022/12/14/rsscan-convert-website-information-stream-to-rss-feed-part-3.html

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

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

相关文章

【云计算与大数据技术】集群资源统一管理系统YARN、Mesos、Omega讲解(图文解释 超详细)

相比于一种计算框架一个集群的模式&#xff0c;共享集群的模式具有以下三个优点 1&#xff1a;硬件共享 资源利用率高 2&#xff1a;人员共享 运维成本低 3&#xff1a;数据共享 数据复制开销低 一、集群资源统一管理系统 集群资源统一管理系统需要支持多种计算框架,并需…

如何能让Linux系统能够更好的支持高并发环境?

Linux系统在默认的参数下对高并发支持不好&#xff0c;主要瓶颈在于单进程最大打开文件数限制、内核TCP参数方面和IO事件分配机制等。所以下面我们从这几方面进行调优&#xff0c;使Linux系统能够更好的支持高并发环境。 iptables相关 如果不是必须使用&#xff0c;建议关掉或…

对接云眸控件兼容性记录

之前采用的UIKit的形式进行视频播放&#xff0c;因为业务要求转换成了Web视频控件的形式。但是Web控件存在不少的兼容性问题 开发的直播系统(www.a.com)是通过Iframe嵌入到门户网站(www.b.com)中&#xff0c;并且部署的地址不同于门户地址: 由于系统页面和门户网站不同域&…

证明客户端发起HTTP请求后会进入TIME_WAIT状态并占用端口

证明客户端发起HTTP请求后会进入TIME_WAIT状态并占用端口 起因 线上出现服务器发起Http请求会报 connect: cannot assign requested address 错误的现象原因是HTTP请求四次挥手的发起方会进入TIME_WAIT状态并占用端口&#xff0c;大量的短链接导致端口耗尽 – 在这篇文章里很详…

代码的编译原理,以Linux系统为例

程序编译分为预编译、编译、汇编和链接四个阶段。在Windows操作系统中&#xff0c;编译工具用的是集成的开发环境&#xff0c;在Linux系统中没有很好的继承开发环境&#xff0c;用的是gcc编译器或者g&#xff0c;gcc用于C语言代码的编译&#xff0c;g用在C的编译过程中。在Linu…

使用mybatisplus 和vben实现低代码开发

前言 如今软件的开发&#xff0c;低代码开发可以3天就做一个CRM&#xff0c;感觉程序员都要失业了的节奏。我们这边用了mybatisplus&#xff0c;其官方也推荐了几个低代码平台&#xff0c;有兴趣的同学可以去看看。我们就直接用mp提供的FastAutoGenerator来进行代码生成。 接下…

凭借这份秘籍,华为老总“寒气”传播下,仍然拿下大厂offer大满贯

首先感谢下华为老总“把寒气传递每一个人”【手动狗头】 当前互联网大环境确实不太乐观&#xff0c;所以我相信不仅仅是华为的 20 万员工感受到了所谓的“寒气”&#xff0c;众多的网友和互联网it工作者&#xff0c;也感受到了“寒气”。最近有很多同学来找我说最近工作难找&a…

【C++】 封装/重载/友元

文章目录一、内存分区、引用、函数1 内存分区模型2 引用&#xff08;reference&#xff09;&#xff08;指针常量&#xff09;3 函数默认参数4 函数占位参数5 函数重载二、封装1 struct和class区别三、对象的构造和析构1 构造函数的分类及调用2 拷贝构造函数调用时机3 构造函数…

产品经理 - 产品设计方法论业务落地部分_包括流程产品文档方法论需求设计方法论

整体 - 产品设计方法论思维导图 个人整理&#xff0c;存在异议大家可以讨论下 业务落地方法论 在进行了需求收集以及需求分析后&#xff0c;针对收集到的需求以及对应的分析结论后&#xff0c;需针对当前的需求点进行开发落地&#xff0c;核心即为两点&#xff0c;需求设计…

大学生HTML期末作业网页:使用DIV+CSS技术制作一个简单的小说网站 (3个页面 登录+注册+首页 )

&#x1f389;精彩专栏推荐 &#x1f4ad;文末获取联系 ✍️ 作者简介: 一个热爱把逻辑思维转变为代码的技术博主 &#x1f482; 作者主页: 【主页——&#x1f680;获取更多优质源码】 &#x1f393; web前端期末大作业&#xff1a; 【&#x1f4da;毕设项目精品实战案例 (10…

C# DotNet基本类库

统一的编程API&#xff1a;NET Framework 一 任何事物都是对象(类型转换) 1 任何事物都是object类的子类 ① 一个函数如果需要object参数&#xff0c;则可以代入任何参数&#xff1b; ② 任何对象都有以下方法&#xff1a; ToString() Equals() GetType() MemberwiseClone…

git 命令使用,和gitlab新建分支

一、gitlab 新建分支&#xff0c;并上传代码 在公司&#xff0c;会有项目管理&#xff0c;创建好master创库&#xff0c;在gitlab&#xff0c;个人需要创建个人分支&#xff0c;用于提交代码&#xff0c;并申请&#xff0c;合并到分支上&#xff08;一般会有第一个原始项目&…

SpringCloud项目实例2--服务治理、nacos安装

服务注册和服务发现 服务注册 比如订单微服务的实例运行在IP地址为192.168.1.122的7010端口和IP地址为192.168.3.41的7020端口上&#xff0c;菜品微服务的实例运行在IP地址为192.168.1.102的9009端口、IP地址为192.168.1.102的9010端口和IP地址为192.168.3.101的9020端口上。…

Nacos架构与原理

Nacos 生态 Nacos 几乎支持所有主流语言&#xff0c; 其中 Java/Golang/Python 已经支持 Nacos 2.0 长链接协议&#xff0c; 能 最大限度发挥 Nacos 性能。 阿里微服务 DNS&#xff08;DubboNacosSpring-cloud-alibaba/Seata/ Sentinel&#xff09; 最佳实践&#xff0c; 是 Ja…

02. 数据库基础

02. 数据库基础 数据库基础&#xff08;上&#xff09; /01 数据库基本概念 数据库基本概念 数据库 数据库&#xff08;database&#xff09;就是一个由一批数据构成的有序集合&#xff0c;这个集合通常被保存为一个或多个彼此相关的文件。 用户可以对文件中的数据进行新…

为什么宝宝睡着后,妈妈离开后他也能知道?雷达定位都没这么准确

经常听到一些妈妈说&#xff0c;照顾孩子真的太难了&#xff0c;完全失去了“人身自由”宝宝好像24小时都要挂在身上&#xff0c;即使睡着了&#xff0c;只要妈妈走开就像报警&#xff0c;宝宝马上就能知道并很快醒来&#xff0c;只要妈妈靠近&#xff0c;马上就会平静下来&…

Qt-Web混合开发-QtWebChannel实现Qt与Web通信交互(4)

Qt-Web混合开发-QtWebChannel实现Qt与Web通信交互&#x1f34f; 文章目录Qt-Web混合开发-QtWebChannel实现Qt与Web通信交互&#x1f34f;1、概述&#x1f353;2、实现效果&#x1f345;3、实现功能&#x1f95d;4、关键代码&#x1f33d;5、源代码&#x1f346;更多精彩内容&am…

JavaScript基础(一)

1、初始JavaScript 1.1、JavaScript 是什么 JavaScript 是世界上最流行的语言之一&#xff0c;是一种运行在客户端的脚本语言 &#xff08;Script 是脚本的意思&#xff09; 脚本语言&#xff1a;不需要编译&#xff0c;运行过程中由 js 解释器( js 引擎&#xff09;逐行来进…

牛客网开源Redis+MySQL核心架构手册,无意掀起Github浪潮

这次小编带来了两套笔记分别是&#xff1a; “Redis 深度历险&#xff1a;核心原理与应用实践”“MySQL DBA工作笔记&#xff1a;“数据库管理、架构优化与运维开发” 先从Redis开始&#xff0c;咱们跟着文章来看下吧~ Redis可以用来做什么? Redis 是互联网技术领域使用最为…

fiddler工具使用大全(全网最详细)

目录 Fiddler基础知识 HTTP协议 Fiddler的使用 总结&#xff1a; 重点&#xff1a;配套学习资料和视频教学 Fiddler基础知识 Fiddler是强大的抓包工具&#xff0c;它的原理是以web代理服务器的形式进行工作的&#xff0c;使用的代理地址是&#xff1a;127.0.0.1&#xff0…