基於原始碼分析apppium服務端啟動過程

進擊的程式茗發表於2018-01-19

寫在前面

本文件主要是通過斷點跟蹤對於appium原始碼,從而記錄的appium服務端的啟動過程,如有錯誤或者理解不當之處,歡迎評論提出。 appium版本:1.7.2 客戶端 appium-python-client 2018年1月 可以直接看結論,根據結論中的關鍵js檔案即可斷點跟蹤出全過程。

appium文件

github 官方網站

主體結構

官方圖片
首先是官方這張圖片,這張圖片簡直涵蓋appium所有知識點!而且對於這張圖片還有中文的readme! 中文readme直通車
圖上分類很清晰 基本上以appium為字首的都被封裝成了類庫,通過npm載入,在node_modules中如下圖所示,其中appium-base-driver為整個服務的基礎類庫。上圖所示的 jsonwp-proxymobile-json-wire-protocal等都在裡面。
image.png

node的express

appium的服務端採用的是express框架,express中文文件,如果之前用express建立過專案的話,會對express中的路由比較熟悉,很多時候路由的處理大概像Appium原始碼分析(3)-路由器模組這樣列出來的樣子,即通過rest.get('/wd/hub/status', controller.getStatus);該種方式可以檢視到所有路由的處理,但是到今年18年appium的程式碼它的路由配置經過了層層呼叫。

而且作為node服務之前比較習慣入口的app.js即作為服務開啟,但是在appium中它將該入口作為express模組直接放在了appium-base-driver中,也就是說appium原始碼的main入口並不是服務入口,那麼他們之間的關係是一個怎樣的繼承及呼叫呢?

原始碼目錄分析

假設我們想實現一個與客戶端通訊的服務,那麼主要包括服務開啟、客戶端http請求,響應函式,那麼在appium中我們將其細化一下,大概是

開啟服務->客戶端傳來請求->開啟當前測試用例會話->根據配置確定ios、android等->分別處理ios或安卓請求->ios或安卓返回後->響應客戶端吧->關閉會話。 首先看入口結構:

WX20180118-194031.png

  • build工程打包後出現的檔案 程式執行來源於build中的main.js ,基本是對lib資料夾下通過babel對於程式碼進行的轉換,所以除錯的時候建議根據main.js除錯
  • config.js 配置檔案
  • logger.js 日誌處理
  • parser.js 終端命令的處理
  • utils.js 基礎函式 main.js 中除了啟動的一些檢查,重點程式碼在於
import { server as baseServer } from 'appium-base-driver';
let router = getAppiumRouter(args);//該函式來源於appium.js
  //此處router返回的是個函式  用於裝填路由路徑 該函式執行路徑位於/mjsonwp/mjsonwp.js 中routeConfiguringFunction 的返回函式
  //baseServer執行之後服務開始啟動
let server = await baseServer(router, args.port, args.address);
複製程式碼

這兩行程式碼可nb厲害了……就這兩行基本就把基礎類庫溜了一圈!程式碼都是幾行幾行的,然後一調就轉一大圈 在appium.js中我們找到了以下函式

import { BaseDriver, routeConfiguringFunction, errors,
         isSessionCommand, processCapabilities } from 'appium-base-driver';

function getAppiumRouter (args) {
  let appium = new AppiumDriver(args);//例項化的這個類繼承了appium-base-driver!
  return routeConfiguringFunction(appium);//這個函式來自於appium-base-driver 
}
複製程式碼

由此成功引入基礎類庫appium-base-driver,然後就去那裡翻吧

基礎類庫appium-base-driver

沒截全的地方都是配置
畫出的幾個紅框基本就是程式碼功能分類,其中jsonwp-proxymjsonwp是和協議相關,由於我不是很瞭解,所以不做介紹,只說明其中程式碼位置。 上邊提到程式碼轉向routeConfiguringFunction

routeConfiguringFunction

這出自mjsonwp/mjsonwp.js請牢牢的記住這個返回函式!!!

function routeConfiguringFunction (driver) {
//...省略一些
  // return a function which will add all the routes to the driver
  return function (app) {
    for (let [path, methods] of _.toPairs(METHOD_MAP)) {
      for (let [method, spec] of _.toPairs(methods)) {
        // set up the express route handler
        buildHandler(app, method, path, spec, driver, isSessionCommand(spec.command));
      }
    }
  };
}
複製程式碼

它返回了一個函式並且裡面還傳參了app,之前斷點打到這裡時滿腦子都是我是誰,我在哪裡,我要幹什麼 我們把程式碼往上看一下routeConfiguringFunction的返回值返回給getAppiumRouter再返回給let router 然後再傳給baseServer(怪我不好好學js……看個程式碼艱辛非常……) 那麼baseServer來自哪裡呢?看import,它來自appium-base-server! main.js中呼叫了一圈再次進入appium-base-server

express中的server.js

之前說到要去找baseServer來自哪裡,終於在express/server.js中找到了 就是appium的http服務啟動的地方!所以說服務開啟的地方不在外部!在基礎類庫裡啊!

async function server (configureRoutes, port, hostname = null) {
//......
//裡面有這樣的程式碼
configureServer (app, configureRoutes) {
//然後再該函式中呼叫了
  configureRoutes(app);
}
複製程式碼

所以程式碼走到這裡執行的就是那個返回函式,app是express的例項!那麼執行這一步是為了做什麼呢?這個METHOD_MAP是最大的全路由配置!來自於mjsonwp/routes.js,其中500行都是路由配置,經過該配置有效避免批量寫app.get()//balabala


function (app) {
  for (let [path, methods] of _.toPairs(METHOD_MAP)) {
  //balabalbala
 }
複製程式碼

至此全路由配置裝填結束!

服務啟動

這個就沒有什麼波折了,既然server.js都找到了,就再這個檔案中,看到這裡終於看到了熟悉的node啟動~

let app = express();
let httpServer = http.createServer(app);
複製程式碼

當斷點跟到這裡時終端就可以跳出如下輸出,服務啟動啦~

[Appium] Appium REST http interface listener started on 0.0.0.0:4723
複製程式碼

總結

啟動服務執行過程 lib/main.js 執行來自於lib/appium.js中的getAppiumRouter函式 該函式中例項化AppiumDriver類,同時讀取路由配置檔案,該類繼承於Appium-base-driver庫中暴露的基類,路由配置檔案來源於Appium-base-driver庫中/mjsonwp/routes.js的配置 配置檔案讀取之後在/mjsonwp/mjsonwp.js中以函式的形式返回main.js 執行baseServer函式 該函式來自於Appium-base-driver/express/server.js

也就是說整個過程從

main.js-[呼叫]-appium.js-[呼叫]-appium-base-driver/mjsonwp-[返回]-main.js-[呼叫]-appium-base-driver/express

其實是通過層層呼叫將路由已配置的方式進行裝填並在基礎類庫appium-base-server中啟動

公眾號:進擊的程式茗

相關文章