UED Landing 頁 - 定時抓取掘金文章

袋鼠雲數棧UED發表於2022-11-22

我們是袋鼠雲數棧 UED 團隊,致力於打造優秀的一站式資料中臺產品。我們始終保持工匠精神,探索前端道路,為社群積累並傳播經驗價值。

本文作者:琉易 https://liuxianyu.cn

本次分享基於『袋鼠雲數棧UED團隊』新發布的 UED Landing 頁 實踐得來,UED Landing 頁集合了團隊目前所有的基礎建設以及精選文章,是團隊展現風采的一個地方。
專案基於 next.js、ts、pnpm、koa2、MongoDB 等技術方式實現,程式碼倉庫:https://github.com/DTStack/UED,歡迎 star。

file

需求介紹

Landing 頁有一個專欄頁面,需要展示團隊以往發在掘金社群的文章、對應的標籤以及其他社群入口。

設計思路

基於上述的需求分析後,進行以下設計:

1、透過 node-schedule設定一個定時任務,每天去請求掘金的介面查詢最新的文章資料,包括每篇文章的標題、釋出人、閱讀量、釋出日期、標籤等;

2、將上述方法拿到的資料處理後存入 MongoDB 資料庫,只保留需要的欄位;

3、提供一個 RESTful 風格的介面,分頁返回文章列表和標籤列表,供專欄頁面查詢使用;

4、頁面請求介面,查詢標籤、文章資料 ,渲染頁面。

實現步驟

以下實現步驟比較詳細,類似的需求也可以按以下步驟去實現。

Docker 安裝 MongoDB

1、下載映象

docker pull mongo

2、建立掛載資料夾

mkdir -p /opt/dtstack/docker/mongo
cd /opt/dtstack/docker/mongo

3、啟動容器

docker run -v /opt/dtstack/docker/mongo:/data/db --name mongodb -p 27019:27017 -e MONGO_INITDB_ROOT_USERNAME=root -e MONGO_INITDB_ROOT_PASSWORD='Admin123!@#' -d mongo --auth

命令解釋

  • -v 掛載本地資料夾,儲存資料
  • -name 給容器指定名稱
  • -p 表示埠對映,-p 宿主機port:容器port,這裡不使用相同埠是為了防止攻擊
  • -e 攜帶密碼等引數
  • -d 後臺執行容器
  • --auth MongoDB 進行許可權校驗

4、進入容器

docker exec -it mongodb mongo admin
注意:
rpc error: code = 2 desc = oci runtime error: exec failed: container_linux.go:235: starting container process caused “exec: “mongo”: executable file not found in $PATH”.
如果出現上述報錯,是下載的 mongodb 映象版本比較高,mongodb 5.0 以上的版本需要使用 mongosh命令來代替原來的 mongo 命令。
docker exec -it mongodb mongosh admin

5、驗證使用者名稱密碼登入
返回 1 代表登入成功。

db.auth('root', 'Admin123!@#')

file

6、使用資料庫

use landingDB

7、建立資料庫的管理員

db.createUser({ user: "landing-user", pwd: "landing-admin.1234", roles: [{ role: "readWrite", db: "landingDB" }] })

MongoDB 不允許同一視窗有多個使用者登入,退出再次進入終端:

db.auth('landing-user', 'landing-admin.1234')

8、建立表

db.createCollection('article')
db.createCollection('tag')

9、測試插入資料

db.article.insert({ id: 1, title: '測試文章標題' })

10、透過 MongoDB Compass 連線資料庫

mongodb://landing-user:landing-admin.1234@127.0.0.1:27019/landingDB

file
file

編寫 node 服務

1、藉助 koa2 啟動 node 服務

服務入口處新建定時任務,每天去掘金獲取文章資料

// 引入模組
const Koa = require('koa')
const schedule = require('node-schedule')

// 例項化
const app = new Koa()

const main = async () => {
    await initDB()

    // 儲存文章列表
    const articleList = await getJueJinArticleList()
    await insertArticles(articleList)

    // 儲存標籤列表
    const tagList = getTagList(articleList)
    await insertTags(tagList)
}

app.listen(envJson.appPort, () => {
    console.log(`app runs on port ${ envJson.appPort }`)
    schedule.scheduleJob(cron, main)
})

2、將查詢的資料存入資料庫,並處理歷史資料

const { MongoClient } = require('mongodb')

const url = `mongodb://${username}:${password}@${host}:${port}/${dbName}`
const client = new MongoClient(url)

// 初始化資料庫連結
const initDB = async () => {
    await client.connect()
    console.log('Connected successfully to mongodb')
}

// 新增查詢到的文章列表
const insertArticles = async (articleList) => {
    const db = client.db(dbName)
    const collection = db.collection('article')

    const updateResult = await collection.updateMany({ isDelete: 0 }, { $set: { isDelete: 1, updateTime: getDateStr() } })
    console.log('updateArticles documents =>', updateResult)

    const insertResult = await collection.insertMany(articleList)
    console.log('insertArticles documents =>', insertResult)
}

file

3、提供介面,從資料庫讀取資料
介面文件

const Router = require('koa-router')
const router = new Router()

router.get('/api/getTagList', async (ctx) => {
    try {
        const db = client.db(dbName)
        const collection = db.collection('tag')
        
        const data = await collection.find({ isDelete: 0 }).toArray()
        ctx.body = {
            code: 200,
            data,
            message: '成功',
        }
    } catch (error) {
        ctx.body = {
            code: 1,
            error
        }
    }
})

編寫頁面

1、頁面請求介面,拿到文章資料進行渲染,在標籤、分頁等引數變化時重新請求介面

useEffect(() => {
    const params = {
        page,
        pageSize,
        tag_id,
        sort_type,
    }
    fetch(`/api/getArticleList?${new URLSearchParams(params).toString()}`)
        .then(res => res.json())
        .then(res => {
            const { articleList, total } = res.data
            setArticleList(articleList || [])
            setTotal(total || [])
        })
}, [tag_id, sort_type, page])

部署方式

一臺 CentOS 伺服器,安裝 node 14+,pnpm,pm2,Docker(可選),MongoDB,nginx。

mkdir -p /opt/dtstack
git clone https://github.com/DTStack/UED.git
cd UED
pnpm i
pnpm deploy

因為後端服務的介面一般不對外暴露,此處透過 nginx 進行轉發:

# ued landing 的 nginx 配置

# http
server {
  listen          80;
  server_name     ued.dtstack.cn;

  location / {
    proxy_pass http://localhost:3004/;
  }

  location /api {
    proxy_pass http://localhost:3002/api;
  }
}

實現效果

http://ued.dtstack.cn/article

file

相關文章