背景1枚
Dewu ではエクスペリエンスをビジネスのキーワードの 1 つとしており、フロントエンド開発においてはユーザー エクスペリエンスの向上は最も重要な課題の 1 つです。
Dewuのフロントエンドプラットフォームには現在、オンラインページの安定稼働を確保するための検査システムや監視プラットフォームなどが導入されているが、「監視の盲点」には依然としていくつかの問題があり、検査や監視は警報後の方法である。ページがオンラインであることを確認するために、事前に一定のユーザー エクスペリエンスの保証を得ることができるため、会社の戦略目標と合わせて、オンラインになりそうなページを事前に検出するために使用される H5 ページ検出サービスを開発することにしました。を起動し、ページの潜在的な問題を事前に公開して、対応する開発/運用にフィードバックするこのサービスを「エクスペリエンス チェックポイント」と呼びます。
この記事は、「体験銃剣」サービスの開発実践から始まり、Dewu検査システムの構造と設計を紹介し、安定性構築に携わる開発パートナーに何らかの学習と参考価値を提供することを願っています。
2 ユーザーエクスペリエンスの定量化基準
ユーザー エクスペリエンスに影響を与える問題を定量化しようとするときは、次の 2 つの主な質問について考える必要があります。
ユーザーエクスペリエンスに何が影響するのでしょうか?
私たちは豊富なデータサポートと実践経験を通じて、ユーザーエクスペリエンスに影響を与える要因を深く理解しています。過去のオンライン問題のフィードバック収集と開発経験に基づいて、体験問題を大きく 2 つのレベルに分けます。
P0 レベル: これらの問題は、ページの読み込み速度に重大な影響を与えるか、セキュリティ リスクを伴います。たとえば、ページに大きな画像やメディア リソースが含まれたり、ページに個人のプライバシー情報が含まれたりします。
P1 レベル: これらの問題は、ユーザー エクスペリエンスに潜在的な影響を与える可能性があります。たとえば、ページ上の応答時間が 300 ミリ秒を超えるインターフェイス リクエストがあります。
問題を検出して定量化するにはどうすればよいでしょうか?
エクスペリエンスの問題を定義して等級付けしたら、それらを検出するための適切なメカニズムを確立する必要があります。チェックポイント サービスの場合、次の手順を実行して問題を定量化し、それを実行可能な検出コードに変換し、呼び出し元が使用できるチェックポイント サービスを通じて対応する検出レポートを生成します。
-
指標と指標を特定する: まず、エクスペリエンスの問題を定量化するための指標と指標を特定する必要があります。例えば、インターフェースのリクエスト速度の問題については、インターフェースの応答時間を指標として使用することができ、また、特定の時間閾値を超えると問題とするなど、一定の基準を同時に設定することも可能です。
-
自動化スクリプトの作成: インジケーターと標準に基づいて、ページの読み込み、ボタンのクリック、リクエストの送信など、ヘッドレス ブラウザーで関連する操作を実行するユーザーをシミュレートする自動化スクリプトを作成できます。これらのスクリプトは、設定されたメトリックに基づいてパフォーマンス測定と問題検出を実行します。
-
ヘッドレス ブラウザを使用してテストを実行する: ヘッドレス ブラウザで自動スクリプトを実行して、ユーザーの動作をシミュレートし、対応するパフォーマンス データを収集できます。
-
結果分析とレポート生成: 収集したパフォーマンス データを通じて結果分析を実施し、問題と関連データをテスト レポートに変換します。レポートには、問題の詳細な説明、問題レベル、関連するパフォーマンス指標およびデータが含まれる場合があります。
呼び出し元に提供する: 最後に、チェックポイント サービスを通じて、生成された検出レポートを呼び出し元に提供できます。呼び出し元は、レポートの問題とデータに基づいて対応する最適化と改善を行い、ユーザー エクスペリエンスを向上させることができます。
このようなメカニズムは、エクスペリエンスの問題を自動的に検出して定量化し、実行可能な検出コードと関連レポートを提供するのに役立ちます。このようにして、より効果的に問題を特定して解決し、開発チームが最適化するための正確なデータを提供できます。
以下は、実装する必要があると分類された検出ケースです。
ユーザーエクスペリエンスに影響を与える具体的なケースを収集した後、具体的な開発計画を決定する必要がありますが、バヨネットサービスとDewuフロントエンドプラットフォーム検査システムには多くの技術的重複があるため、既存の検査アーキテクチャを使用して統合することにしました。 「エクスペリエンスチェックポイント」は既存の検査システムに統合されており、開発時間を大幅に節約できます。
3 検査システムインフラ
検査システムのプログラムの目的は、一文に要約されます。データ ソースから検出対象のページ アドレスのリストを定期的に取得し、バッチ検出を実行してレポートを生成します。
さまざまなシナリオでパーソナライズされたニーズを満たすために、検査システムはインスペクターの 3 つの基本クラスを抽象化し、各シーンはその基本クラスを継承してカスタマイズ要件を実現します。
3.1 パトロール基本クラス
1. DataProviderBase (データプロバイダー基本クラス):
dataSlim():冗長データを簡素化します。
fetchData():リモート データを取得し、検出されるページの URL リストを処理して返します。
isSkipTime():条件を設定し、特定の条件下でタイミング タスクをスキップするために使用されます。
スケジュール():スケジュールされたタスクの実行間隔を設定します。
2. PageInspectorBase (ページインスペクター基本クラス):
check():指定された検出ページを開き、さまざまなリソースの監視を初期化するために使用されるチェッカー エントリ。
injectRequestHeaders():ページ インターフェイス リクエストで必要な Cookie、トークンなどを挿入します。
urlCheck(): URL アドレスのチェック;
onRequest():ページリクエストを監視します。
onResponse():ページの応答を監視します。
onPageError():ページ エラーを監視します。
3. DataReporterBase (データ レポート基本クラス):
buildReporter():収集されたエラー情報に基づいて検出レポートを生成します。
feishuNotify():生成されたレポートを、Feishu を通じて指定された通知グループに送信します。
getHTMLReporterUrl(): ejs テンプレートに従ってレポートの静的 HTML ファイルを生成してアップロードし、オンライン レポートのアドレスを返します。
これら 3 つの基本クラスを、レストラン内の異なる分業を持つ 3 つの部門と視覚的に比較することで、理解を容易にすることができます。
ホテルのフロントはお客様からの注文を受け、キッチンは注文に応じて料理を調理し、ウェイターは出来上がった料理をお客様に提供する役割を担っています。
DataProviderBase (データ プロバイダーの基本クラス):外部から提供される検出対象のページのリストを受信するための定期的なポーリングを担当します。このコンポーネントは、顧客から注文を受けるレストランのフロントデスクに似ています。これは、検出するページのリストを外部から取得し、これらのページを検出のために検出器に渡す役割を果たします。
PageInspectorBase (ページ インスペクター基本クラス):ページ リスト内の各 URL を 1 つずつ検出し、ページ内の潜在的な問題を検出します。このコンポーネントは、注文に従って食材を切り、調理し、料理を積み込むプロセスと同様に、検出対象ページのリストにある URL を 1 つずつ検出し、各ページの問題を検出します。一連の検出方法とルールを使用して、ページに潜在的な問題があるかどうかを判断できます。
DataReporterBase(データレポート基本クラス):検出により収集された問題点をさらに整理した後、レポートを送信します。顧客に調理済みの食事を提供するウェイターと同様に、このコンポーネントは、検出された問題を分類して要約し、対応するレポートを生成する責任を負います。レポートには、問題の説明、重大度、関連ページの URL などの情報を含めることができます。その後、レポートを開発や運用などの関連する利害関係者に送信できます。
3.2 パトロール
上記 3 つの基本クラスをベースとして、異なる検査シナリオに応じて異なるインスペクタ (インスペクタ) が開発されます。 各インスペクタには、上記 3 つの基本クラスを継承する 3 つのサブクラスと、基本クラスのインスペクタを継承するサブクラスが含まれます。 インスペクタは、独自の要件を実現します基本クラスのメソッドをオーバーライド/拡張することにより、以下は最小限のインスペクターの例です。
// data-provider.ts
export class DataProvider extends DataProviderBase {
// 实现特定的页面列表获取逻辑
async fetchData(args) {
return await axios.get('https://xxx.xxx').then(res => res.data.urlList)
}
// 每隔15分钟获取一次待检测列表
async schedule() {
return [{cron: '*/15 * * * *',args: {}}]
}
}
// page-inspector.ts
export class PageInspector extends PageInspectorBase {
async onPageOpen(page, reporter: PageReporter, data) {
const pageTitle = await page.evaluate('window.document.title')
console.log('这里可以获取到页面title', pageTitle)
}
}
// data-reporter.ts
export class DataReporter extends DataReporterBase {
async beforeFeishuNotify(data: InspectorReportBase) {
console.log('在飞书通知前做点什么', data)
return data
}
}
3.3 検査メインプログラム
検査システムでは、各ページの検出タスクは独立した非同期タスクであり、各検出レポートの分類と送信も独立した非同期タスクです。これらの非同期タスクの管理とメンテナンス、およびタスク メッセージの保存と配信を容易にするために、検査システムでは、検査システムの非同期タスク管理ツールとして Redis と Bull を組み合わせて使用します。
Redis は、高性能のデータ ストレージとアクセス機能を提供するインメモリ データベースです。
Bull は、タスクのスケジューリング、実行、メッセージ配信の機能を提供する Redis ベースのタスク キュー ライブラリです。
インスペクターと非同期タスク管理機能を使用した、メイン プログラムの主な作業は次のとおりです。
-
タスクを定義します。Bull を使用して 2 つのタスク キューを作成します。page_queue は「ページ検出タスク」を格納するために使用され、reporter_queue は「レポート生成タスク」を格納するために使用されます。
-
プロダクションタスク: 検査システムでは、ページ検出タスクとレポート生成タスクのプロデューサー (メインプログラム) が、タスクを対応するキューに追加する責任を負います。インスペクターがページ検出を実行する必要がある場合、プロデューサーはページ検出タスクを page_queue に追加し、レポートを生成する必要がある場合、プロデューサーはレポート生成タスクをreporter_queue に追加します。
-
消費タスク: 検査システムのタスク消費者 (メイン プログラム) は、タスク キューからタスクを取得して実行する責任があります。検出タスクには 1 以上のページ検出タスクがあり、上で紹介したページ インスペクター PageInspector によって実行されます。チェックして、検出レポートを Redis に保存します。検出タスクのすべてのページが検出されると、reporter_queue タスクが作成され、インスペクターの DataReporter によって消費されます。
4 銃剣サービス
検査システムを導入した後、銃剣サービスを自己検査システムに統合する方法を見てみましょう。
チェックポイント サービスの主な機能は、一文で要約できます。検査システムの既存のアーキテクチャにアクセスし、リモート インターフェイスを外部に公開し、インターフェイスの呼び出し元にページをアクティブに検出する機能を提供し、その後、検出レポートを発信者に返します。
既存の検査システムとバヨネットサービスの違いを比較してください。
上記の検査システム アーキテクチャの紹介と上記の表の分析から、バヨネット サービスの開発は、検査システムの検査デバイス アーキテクチャに基づいて検査デバイスをカスタマイズして実装することであることがわかります。
4.1 銃剣サービスの実行シーケンス
銃剣サービスのチェッカーの開発を始める前に、銃剣サービス全体の実行シーケンスを整理しましょう。
その中で、銃剣サービスの主な開発タスクは、ステップ 2、3、4、および 7 です。
4.2 タスクインターフェイスの作成
巡回検査は事後検出方式であると上で述べましたが、巡回検査システムのDataProviderBase(データプロバイダ基本クラス)の主な機能は「外部から提供される検査対象ページのリストを定期的にポーリングして受信する」ことです。
バヨネット サービスの場合、検出タスクは検出パーティによって能動的に作成されるため、DataProviderBase の実装にはあまり注意を払う必要はありませんが、検出タスクの作成を担当する API サービスを開始します。以下のとおりであります:
app.post('/xxx.xxx', async (req, res) => {
const urls = req.body?.urls // 待检测url列表
const callBack = req.body?.callBack // 调用方接收报告的回调接口地址
const transData = req.body?.transData // 调用方需要在回调中拿到的透传数据
// 巡检系统检测任务创建函数
newApp.createJob(urls.map(url => ({ url,
// 在redis任务队列中传递的信息
pos: { callBack, transData },
})),
jobId => { // 返回任务id给调用方
res.json({ taskId: jobId })
}
)
})
4.3 ページ検出
PageInspectorBase (ページ インスペクターの基本クラス) は銃剣サービスの変換の焦点です。この基本クラスのサブクラス実装に関しては、実装するために上記の特定の検出ケースを実行する必要があります。主に 2 つのタイプがあります。検出ケースの割合:
1. リソース タイプ検出のケースをリクエストします。サブクラスの onResponse メソッドをオーバーライドし、リソース タイプごとに異なる検出ロジックを実行します。
2. ランタイム検出の場合: サブクラスの onPageOpen メソッドをオーバーライドし、基本クラスから渡された Page オブジェクトを通じて js スクリプトを挿入し、ページのランタイム検出を実行します。
//
页面检测类
class PageInspector extends PageInspectorBase {
// ...
// 针对不同资源类型检测方法配置Map
checkResponseMethodsMap = new Map([['image', this.checkImageResponse]])
// 请求资源型检测入口 针对请求资源进行检测
async onResponse(response: Response, reporter: PageReporter, data: IJobItem) {
const resourceType = response.request().resourceType()
const checkMethod = this.checkResponseMethodsMap.get(resourceType)
await checkMethod(response, reporter, data)
}
// 检测图片资源
async checkImageResponse(response: Response, reporter: PageReporter, data: IJobItem) {
// ...
if (imageCdnList.includes(url)) {reporter.add({ errorType: "图片类型错误.非cdn资源" })}
// ...
}
// 运行时检测入口 在页面打开时执行注入的js脚本进行运行时检测
async onPageOpen(page, reporter: PageReporter, data) {
// ...
const htmlText = await page.evaluate('window.document.documentElement.innerHTML')
const phoneRegex = /\b((?:\+?86)?1(?:3\d{3}|5[^4\D]\d{2}|8\d{3}|7(?:[35678]\d{2}|4(?:0\d|1[0-2]|9\d))|9[189]\d{2}|66\d{2})\d{6})\b/g;
let phoneMatch: RegExpExecArray
let collectMessage = []
while ((phoneMatch = phoneRegex.exec(html)) !== null) {
const phone = phoneMatch[1];collectMessage.push(`手机号码:${phone}`);
}
collectMessage.forEach(val => {reporter.add({ errorMessage: `敏感信息:${val}`})})
// ...
}
// ...
}
RegExp.prototype.exec()
JavaScript RegExp オブジェクトは、グローバル フラグまたはスティッキー フラグが設定されている場合 (/foo/g または /foo/y など) ステートフルになります。最後に成功した一致以降の位置を lastIndex プロパティに記録します。この機能を使用すると、exec() を使用して単一文字列内の複数の一致 (キャプチャされた一致を含む) を反復処理できます。一方、String.prototype.match() は一致した結果のみを返します。
4.4 レポートとコールバック
検出タスクの実行後、新しい「レポート生成タスク」がreporter_queueに作成され、メインプログラムはDataReporterBaseを継承するサブクラスを呼び出して次の操作を実行します。
-
検出された項目を 1 つずつ分類し、収集されたエラーを分類し、レポートのソース データを整理します。
-
レポートのソース データと ejs テンプレートを組み合わせて静的 HTML を生成し、それをアップロードしてオンライン検出レポートのアドレスを取得します。
-
呼び出し元へのコールバック計測レポート。
4.4.1 ChatGPTを使用したレポートテンプレートの生成
ほとんどの研究開発の UI デザイン レベルは誰の目にも明らかです。レポート テンプレートのスタイルやインタラクションを壊すのではなく、AI に任せたほうがよいでしょう。事前定義されたデータ構造と要件を入力すると、ChatGPT は迅速にテンプレートを与えれば、あとは少しだけ調整するだけで、使用可能な状態になります。
Q: 我需要你写一个ejs模板,这个模板用来展示一个统计报告,为了样式和交互方便,模板会通过内置bootstrap的css样式和Jquery,你可以用到他们来实现html元素的组合和设置交互,具体的数据结构如下:
interface CheckResult {
name: string;
status: 0 | 1;
message: string;
severityLevel: 0 | 1;
}
interface DetectionResult {
url: string;
status: 0 | 1;
checkResults: CheckResult[];
}
interface InspectorReport {
taskId: string;
reporterUrl?: string;
detectionResults: DetectionResult[];
startTimeStr: string;
endTimeStr: string;
duration: number;
pageTotal: number;
taskStat: {
completed: number,
waiting: number,
active: number,
delayed: number,
paused: number,
failed: number,
}
}
export interface InspectorReportBase {
manifest: IManifest,
data: InspectorReport;
}
export interface IManifest {
name: string
desc?: string
notifyTo?: string[]
}
A: 好的,以下是基于提供的数据结构设计的ejs模板:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<link href="https://xxx.xxx.css" rel="stylesheet">
<script src="https://xxx.xxx.js"></script>
<title>
<%=data.manifest.desc%>
</title>
</head>
<body>
<div class="container">
<h2 class="text-center">
<%= data.manifest.desc %> 统计报告
</h2>
<div class="row">
<div class="col-md-4">
<div class="card">
<div class="card-body">
<h5 class="card-title">任务信息</h5>
<p class="card-text">任务ID: <%= data.data.taskId %>
</p>
<p class="card-text">检测开始时间: <%= data.data.startTimeStr %>
</p>
<p class="card-text">检测结束时间: <%= data.data.endTimeStr %>
</p>
<p class="card-text">检测时长: <%= data.data.duration %> 秒</p>
<p class="card-text">检测页面数: <%= data.data.pageTotal %>
</p>
<!--以下省略 -->
</body>
</html>
AI が生成したオンライン レポート テンプレート:
4.4.2 発信者からの切り離し
サービスプロバイダーとして、銃剣サービスは発信者が誰であるかにあまり注意を払いたくないので、発信者からの切り離しをどのように達成するのでしょうか?
——上記の「タスク インターフェイスの作成」リンクをもう一度見てみると、このインターフェイス設計の入力パラメータには callBack フィールドと transData フィールドが含まれています。
-
callBack: 検出タスクが終了すると、銃剣サービスはこのアドレスを呼び出して検出レポートを返します。
-
transData: 検出タスクの終了時に、コールバックに透過的に渡す必要があるデータ。
ページ検出タスクが完了すると、コールバック テスト レポート リンクで、バヨネット サービスは Redis キュー タスクのキャッシュからこれら 2 つの値をフェッチし、POST リクエストを使用してレポートと transData をコールバックに送信します。
卡口服务回调示例代码
axios.post(callBack, {
data: { msg: "本次检测检测报告如下:xxxxx", transData: `透传的数据如下:${transData}` }
})
フォローアップ計画では、バヨネット サービスをより多くのシナリオのさまざまなニーズに適応させるために、バックエンド マイクロサービス登録センターの概念を参照して、登録センターの単純な抽象モデルを実現できます。バヨネット サービスとその呼び出し元をさらに分離します。それらの間のロジックにより、カスタム検出項目、カスタム レポート テンプレートなど、より多くの機能を同時に拡張できます。
5 まとめ
銃剣サービスでは、検査のソースコードを学習して読むことが重要な事前作業です。検査システムの実装の詳細と基礎となるアーキテクチャ設計を深く理解することで、検査システムがどのように動作するかをよりよく理解できるようになり、より適切にカスタマイズおよび拡張できるようになります。これらの経験は、コーディングおよび設計能力の向上にも役立ちます。後続の技術プロジェクトに適用および実践されます。この記事を読んだ開発学生がこの実践的なまとめから何かを得ることができれば幸いです~
引用/参考リンク
GitHub - OptimalBits/雄牛
RegExp.prototype.exec() - JavaScript | MDN
文:ハン・フェイ
この記事は Dewu technology のオリジナルに属し、出典: Dewu technology 公式ウェブサイト
Dewu Technology の許可なく転載することは固く禁じられています。そうでない場合は、法律に従って法的責任を調査されます。