狐友作為搜狐的一款社交產品,在流量傳播上有著旺盛的需求點。而在流量傳播所需的眾多載體之中,海報圖片以其簡單的分享形式、可定制的視覺體驗、自帶二維碼識別導流等特點,成為了社交產品高頻必備的流量載體。
作為狐友的前端開發,生成海報圖片就成為了我們工作中持續不斷的一個重要需求點。以下是狐友目前的產品前端服務矩陣和海報圖片的產品形式。
圖 1 狐友產品前端服務矩陣和海報圖片的產品形式
從上圖1可以看到,生成海報圖片對于狐友產品矩陣來說是一個高頻強需求。海報圖片作為分享載體,對于各平臺的分享流程對接也非常暢通和直觀,例如不同于小程序卡片分享只能拘泥于微信平臺,網頁分享的鏈接形式不夠直觀。
而在海報圖片這個重要環節,長期的主要技術手段一直是通過各客戶端開發在本地設備上進行繪制,但這種方案存在如下的劣勢困擾著我們:
為了解決以上問題,我們開始著手調研并實踐落地了一套全新的海報圖片統一服務,命名為hy-ssr-img。
圖 2 狐友大前端海報生成各端開發狀態
海報圖片統一服務是一套基于Puppeteer的Node.js后端服務端(SSR)渲染頁面并截圖生成海報圖片的服務,這一服務解決了原有海報圖片生成的三大問題:
圖 3 海報圖片統一服務各端復用
那么,海報圖片統一服務是如何建立起來的呢?下面是從項目立項開始的具體設計方案。
首先,我們調研了業界現有的圖片渲染方案,如下表。通過下表的調研總結可以看出各個方案各有優劣勢。由于復用提效是重要需求點,我們把方案鎖定了服務端渲染方向上,即入選方案為:
在對比兩種方案,以及參考相關業務實踐后,我們最終選用了最常見的【Node.js截圖方案】。因為服務端圖層渲染選用的圖形庫在排版表達力上遠不及HTML+CSS在瀏覽器上的表達力,而且系統預期的使用對象是前端開發人員,并不是產品和運營,所以服務端圖層渲染方案的拖拽方式并不是必選項,反而由于拖拽渲染表達力的限制,對于實現一些排版復雜的圖片非常吃力。而Node.js截圖方案則沒有這方面的問題,前端開發只需要常規開發頁面即可,然后交給Puppeteer渲染后進行截圖形成最終用戶看到的海報圖片。
表 1 項目選型方案對比
在確定了【Node.js截圖方案】之后,我們對項目整體架構流程進行了設計,首先由于我們的應用的后端Java接口服務已經非常成熟,針對參數的合法性校驗及加簽解簽等防護措施都已建立,所以我們在開發Node.js服務時就沒有必要再造輪子,那么怎么把這些基礎接口功能和Node.js服務聯系起來呢?我們決定把Node.js服務放到內網,通過只允許后端服務直接訪問的方式來達到這一目的,這樣后端接口層就像一個盾牌,擋在用戶和Node.js服務之間,而我們就可以專注實現Node.js的截圖功能了。整體方案流程如下圖4:
圖 4 Node.js截圖方案整體流程方案
整體架構流程方案確定后,我們就需要細化截圖相關的詳細流程方案。在經過調研之后,我們發現常見的截圖開源方案在我們的使用場景中還有很多需要優化的點和不滿足需求的部分,因此我們決定自研開發【僅首屏SSR渲染】方案。針對項目特點,每個海報圖片只有一張,所以不存在首屏渲染過后還要渲染第二頁的場景,因此該方案只包含首屏字符串渲染即可,不需要帶有常見 SSR 的客戶端激活渲染的打包構建及執行流程。 去掉常見SSR方案中的非首屏渲染邏輯后,頁面就只包含了首屏必要的渲染代碼(HTML + CSS),去除一切不需要的環節(JS執行激活),保證頁面給到 Puppeteer渲染時是最簡化的狀態,盡可能減少網絡I/O和本地磁盤I/O,只包含單純的渲染過程,以加速渲染速度。以下是常見方案和自研方案的對比。
最常見的截圖方案是通過請求網頁地址,渲染后截圖,如圖5。
圖 5 Node.js截圖詳細流程常見方案
這種方案不管是CSR(客戶端渲染)方式,還是SSR(服務端渲染)方式,都還是有優化空間。我們可以發現,Puppeteer請求網頁的網絡消耗是可以被節省下來的,如果我們直接使用在本地生成好的HTML和CSS,則請求網頁并下載所用的時間就可以節省下來,獲得更快的截圖速度。
另外頁面的動態渲染通過SSR來進行,這樣完全不需要客戶端JS的存在,直接還可以省去加載客戶端JS的時間,獲得更優的渲染速度,這種優化后的Node.js截圖詳細流程方案如圖6所示。
圖 6 優化后的Node.js截圖詳細流程方案
該方案還有技術細節需要設計,即如何完成SSR渲染首屏的工作,由于我們的技術棧是Vue,所以選用vue-server-renderer為基礎庫來完成這一過程,如圖7。
圖 7 僅首屏SSR渲染方案技術流程
整體架構流程方案確定之后,我們就要進行更細化的業務技術流程設計,這里有很多細節需要考慮:
為了解決這些問題,我們設計了如下的流程,如圖8。
圖 8 Node.js截圖服務業務技術流程
可以看到整個流程設計由接口同步請求和截圖異步任務兩大塊組成。我們把Node.js截圖服務當做一個純渲染圖片的服務,用戶發起請求給后端服務時會攜帶渲染頁面所需要的動態參數,后端服務則負責參數校驗等工作,并且下發給用戶一個異步任務ID。然后后端服務會請求截圖服務,截圖服務收到請求后并不會立刻截圖,而是直接返回后端服務,并開啟異步任務去進行耗時的截圖服務,這樣就可以防止長鏈接堆積造成服務不可用的情況發生。之前下發給用戶側的異步任務ID就是異步截圖任務完成后通知后端服務的憑證,這樣當截圖服務完成后就可以通知后端服務截圖完成狀態(截圖成功或者失敗),而用戶側則可通過輪詢后端服務接口得知截圖是否完成并使用海報圖片了,當然可以設置一個超時時間來完成整個截圖服務的交互閉環。
對于海報圖片的緩存層也是要考慮的,因為很多場景下用戶請求的海報圖片是一模一樣的,比如我們的熱榜海報會在固定時間生成一次,那么緩存層可以有效緩解截圖耗時操作,并且為預生成海報圖片提供了基礎。比如我們會在特定時間通過代碼自動預渲染一批海報,當第一個用戶來訪問時就不需要等待耗時的截圖服務,直接返回渲染好的海報圖片即可。那么命中緩存的條件是什么呢?對于長的一模一樣的海報圖片當然希望只生成一次后都走緩存層。我們的設計中決定“圖片長相”的因素有兩個:
1.海報地址:對應不同的海報樣式
2.海報參數:對應同一個海報樣式中的不同數據
所以,我們通過hash(“海報地址”+“海報參數”)的方式得到緩存命中的key,以此來控制命中緩存。另外截圖服務生成海報圖片后會直接上傳到CDN進行存儲,用戶側加載海報圖片的速度和穩定性也得到了相應的保障。
在業務技術流程方案確定后,就需要為此搭建一套工程化開發環境,來支持項目業務的具體開發。我們基于公司現有基礎設施以及技術棧,確定了以下主要技術選型:
如圖9,展示了通過以上技術棧及基礎設施組建的整個工程化方案。
圖 9 Node.js截圖服務工程化方案
這里需要說明是,一個海報圖片對應一個頁面,一個頁面會有兩個入口文件:一個CSR(客戶端渲染)用于開發頁面時使用,一個SSR(服務端渲染)用于截圖時Puppeteer渲染頁面使用。這么設計的原因是,CSR渲染使用webpack-dev-server是現成的開源方案,對于開發時熱更新等支持不需要自定義開發,開箱即用,如果開發時也使用SSR進行就需要進行針對性的改造,由于開發體驗上并沒有區別,我們就選用了更高效地搭建方式。而截圖時頁面渲染方式就是上文提到的【僅首屏 SSR 渲染】。
從用戶發起請求到海報圖片返回,這整個過程的耗時需要進行“壓榨”,以獲得更好的用戶體驗。以下是我們在開發過程中實踐和驗證的相關重點優化和部分效果收益。從第一版基礎開發到最后優化完成的版本,截圖服務總用時從1300ms+降低到了600ms+(注:測試數據均取相同開發機10次執行結果的平均值,渲染相同的海報圖片,圖片為超長圖且內容豐富,長寬為:4967×750)。表2為各關鍵優化點明細。
表 2 項目關鍵優化實踐
為了方便使用海報圖片的開發人員,我們還配套開發了海報生成系統在線文檔。開發人員可以通過該系統查看現有的海報圖片以及相關參數字段,并可以通過右方的編輯器更改字段的值并實時得到新的渲染圖進行預覽和下載。如圖10,開發人員可以在這個playground里進行所見即所得的預覽及操作。
圖 10 海報在線文檔預覽系統
海報圖片服務作為一個后端服務,日志采集分析和監控是必不可少的,我們可以通過日志得到以下信息:
所以,我們封裝了3種log日志用于海報圖片服務:
日志處理完成后,我們接入了公司現有日志基建服務來完成后續的日志采集、存儲和分析等功能,如圖11所示,通過這一套日志流程,我們就可以更加放心地上線并時刻關注我們服務的運行情況,輕松做到快速排查和分析問題。
圖 11 海報系統日志服務
海報生成是一個耗時任務,其繪制速度依賴于服務器的CPU和內存。經過線上數據評估,截圖服務qps最大支持60即可,經使用JMeter并發測試:
所以為了保證圖片的生成速度和穩定性,使并發量少的時候圖片生成速度盡可能快,并發量大的時候圖片生成速度在可接受時間內,采用了多實例加多進程的部署方式,如圖12,項目部署于DomeOS平臺,部署了5臺 (CPU4核 + 內存 6G) 實例,每個實例通過pm2啟動4個進程。通過負載均衡可以使請求平均打到每個實例的每個進程上,讓并發少的時候最快的生成圖片,并發大的時候充分利用所有實例,加快整體生成圖片時間。生成5000像素的圖片,當并發數小于5時,圖片可以在1s左右返回,當并發數為20,圖片可以在3s左右返回,當并發數為60時,圖片可以在8s之內返回。
圖 12 海報系統部署方案
截止到2023年初,海報圖片服務已上線海報10+個。每個海報只需開發1遍就可供給H5、小程序、Android和iOS共4端進行使用。每天平均生成海報圖片6000+,每張海報圖片平均生成時間400ms左右,支持超長圖可達10000+像素。
隨著項目迭代,海報服務未來可能有更大的需求訴求,下面列出海報服務未來進化的一些展望:
1、賦能外部開發人員
目前項目是以普通項目的開發模式進行研發,如果提供給非內部研發人員使用則有很多流程和規范上的問題難以解決。未來可以支持渲染遠程組件,通過遠程組件的方式下發給外部研發進行開發,以此隔絕海報服務核心邏輯和業務方邏輯,使得業務方只需關心業務,也防止業務方無意間可能影響到海報核心邏輯。此外,在整個過程中還可以增加審核遠程組件等項目管理能力。
2、賦能非研發人員
為了海報圖片渲染極具靈活性,我們把開發人員作為首要滿足對象。未來除了開發人員可以開發海報頁面,同時可以支持非研發人員通過拖拽編輯等低代碼方式完成海報圖片的生產,這樣針對簡單海報圖片的場景,運營、產品等非研發人員也可以進行海報圖片的制作,進一步提高生產效能。
作者:狐友FE
來源:微信公眾號:搜狐技術產品
出處:https://mp.weixin.qq.com/s/D1N9qxHYGRBUlCGePvqj3w
之前做的 WordPress 插件,更多涉及的是管理功能,比如用戶管理,分類管理這些插件等等,這些主要這些功能比較純粹,然后 WPJAM Basic 已經實現了 WordPress 后臺的各種底層類庫和交互組件,只要功能需求就能快速做好。
但是最近在更新 Autumn Pro 5.0 的功能的時候,也開始有所涉獵 WordPress 的前端開發。 我就把一些常用的前端功能整理成統一的插件,一是方便日后更新,也是方便其他主題的集成。
今天就來介紹這批插件中的第一個,一個全能的 WordPress 彈窗插件,也可以叫模態框插件,目前已經實現了海報 / 打賞 / 分享 / 搜索彈窗,接下來還會實現登錄 / 公告等彈窗,也提供彈窗接口,只要簡單幾行代碼也可以實現你自己的彈窗。
插件激活之后,在后臺「WPJAM」菜單下就又會有「頁面彈窗」的子菜單,當然如果已經使用 Autumn Pro 主題,這個在菜單在「Autumn」菜單下,總之不在「WPJAM」菜單,就是在主題設置菜單下,點擊進去就進入彈窗的設置頁面了:
如上圖所示,文章海報彈窗,需要你設置 logo 和標語,然后選擇自動插入(Autumn Pro 主題已經通過代碼插入,上面的自動插入選項也會沒有),就可以在頁面上顯示「生成海報」按鈕:
當然其他主題你也可以自己通過 wpjam_get_poster_button($args=[]) 來手工插入,這里 $args 的默認值是:['title'=>'生成海報', 'class'=>'poster-button', 'icon'=>'ri-image-line'],你也可以自定義修改它:
總之最后在頁面上出現「生成海報」按鈕,點擊它之后就會生成海報彈窗:
樣式還是可以的,當然你也自己改一下 CSS,去調整海報的樣式,這里再多說兩句,海報是通過 HTML2Canvas 生成圖片的,這樣做好處,就是無需在服務端去生成海報圖片了,能夠顯著降低服務端的消耗,同時海報上的二維碼也是使用 jQuery.qrcode 前端 jQuery 插件實現,也不是服務端生成的圖片,更進一步降低服務器的消耗,本來這些功能就應該在前端實現的嘛,現在的瀏覽器能力也是越來越強了。
我們繼續看會最上面的設置圖,文章打賞彈窗需要設置「打賞標題 / 標語和收款碼」這三個選項,之后就會出現「打賞作者」的按鈕:
同樣它的自定義調用函數是wpjam_get_donate_button($args=[]),$args的默認值是:['title'=>'打賞作者', 'class'=>'donate-button', 'icon'=>'ri-money-cny-circle-line']。
點擊之后的彈窗如下:
文章分享彈窗沒有設置選項,它的自定義調用函數是 wpjam_get_share_button($args=[]),$args 的默認值是:['title'=>'分享', 'class'=>'share-button', 'icon'=>'ri-share-forward-2-fill']。
點擊生成的分享按鈕之后彈窗如下:
最后一個是文章搜索彈窗,通過它可以在后臺設置常用搜索詞:
搜索按鈕不能自動插入,它只能通過 wpjam_get_search_button($args=[]) 函數手動插入,$args 的默認值是:['title'=>'', 'class'=>'search-button', 'icon'=>'ri-search-line']。
比如 Autumn 的搜索按鈕就如下:
點擊它之后就可以進行文章搜索:
除了顯示搜索框和后臺設置的熱門搜索之外,還顯示當前用戶的搜索歷史,是不是有種大站才有搜索的感覺,哈哈。
日常的工作中(比如微信小程序),我們經常有這樣的需求,就是需要使用程序生成推廣海報,然后海報里要包含指定的二維碼,這樣用戶分享出去別人掃碼之后就可以確定用戶推薦關系。
單獨生成海報背景或者單獨生成二維碼通常還算比較簡單,但如果要將兩者結合到一起那還是需要花一點心思的。
那現在怎么解決呢?如何使用PHP在服務器生成推廣海報呢?對的,使用PHP中的GD庫。具體過程請往下看。
1、海報背景圖。背景圖一般存服務器,程序本地讀取;
2、推廣二維碼。可以是二維碼圖片鏈接,也可以是字符串圖像流。如果自己生成二維碼,詳見phpqrcode官網,地址:https://sourceforge.net/projects/phpqrcode。
3、開啟PHP的GD擴展
首先熟悉一下幾個關鍵的PHP函數:
1、getimagesize:取得圖像大小。getimagesize() 函數將測定任何 GIF,JPG,PNG,SWF,SWC,PSD,TIFF,BMP,IFF,JP2,JPX,JB2,JPC,XBM或 WBMP 圖像文件的大小并返回圖像的尺寸以及文件類型和一個可以用于普通 HTML 文件中 IMG 標記中的 height/width 文本字符串。如果不能訪問 filename 指定的圖像或者其不是有效的圖像,getimagesize() 將返回 FALSE 并產生一條 E_WARNING級的錯誤。
2、image_type_to_extension:取得圖像類型的文件后綴。
3、imagesx:取得圖像寬度。
4、imagesy:取得圖像高度。
5、imagecreatetruecolor:新建一個真彩色圖像。imagecreatetruecolor() 返回一個圖像標識符,代表了一幅大小為 x_size 和 y_size 的黑色圖像。是否定義了本函數取決于 PHP 和 GD 的版本。從 PHP 4.0.6 到 4.1.x 只要加載了 GD 模塊本函數一直存在,但是在沒有安裝 GD2 的時候調用,PHP 將發出致命錯誤并退出。在 PHP 4.2.x 中此行為改為發出警告而不是錯誤。其它版本只在安裝了正確的 GD 版本時定義了本函數。
6、imagecolordeallocate:取消圖像顏色的分配。
7、imagefill:區域填充。imagefill() 在 image 圖像的坐標 x,y(圖像左上角為 0, 0)處用 color 顏色執行區域填充(即與 x, y 點顏色相同且相鄰的點都會被填充)。
8、imagecopyresampled:重采樣拷貝部分圖像并調整大小。imagecopyresampled() 將一幅圖像中的一塊正方形區域拷貝到另一個圖像中,平滑地插入像素值,因此,尤其是,減小了圖像的大小而仍然保持了極大的清晰度。
9、getimagesizefromstring:從字符串中獲取圖像尺寸信息。同 getimagesize() 函數。 區別是 getimagesizefromstring() 第一個參數是圖像數據的字符串表達,而不是文件名。
10、imagecopymerge:拷貝并合并圖像的一部分。
11、imagejpeg:輸出圖象到瀏覽器或文件。
12、imagedestroy:銷毀一圖像。
熟悉完了這十幾個核心方法后,我們就可以編碼了。核心代碼如下:
以上代碼是利用PHP生成推廣海報的主要部分,有了它我們就可以很容易的生成推廣海報了。
參考案例:
1、生成帶有二維碼的海報。
2、生成帶有圖像,昵稱和二維碼的海報。
從以上示例可以看出,只要我們定義好核心代碼之后,后面生成不同的推廣海報,只需要調整config配置參數即可。
*請認真填寫需求信息,我們會在24小時內與您取得聯系。