前言
本文追述LBS的前世今生,透視LBS擁有的優秀基因和繼承的缺陷,從技術架構的視角對流行的LBS+SNS系統進行剖析。 – 作者:李祎
LBS基于位置的服務(Location Based Services)一直是移動開發的熱門領域,各種基于LBS的移動應用層出不窮。作為應用開發者如何不霧裡看花,找到最好的切入點進行技術選型和設計應用? 本文追訴LBS的前世今生,透視LBS擁有的優秀基因和繼承的缺陷,從技術架構的視角對流行的LBS與SNS結合的應用進行剖析, 為打算在LBS領域創業和創新的開發者提供一些技術參考。
前世 – 源自GIS
就像認識一個人首先要了解他的家庭背景,我們先介紹一下LBS的父親GIS 地理資訊系統(Geographic Information System) 。 GIS發展于60年代,它的技術架構和傳統的Client/Server資訊系統沒有太多差別。PC端應用程式通過網絡連接配接背景資料庫,查詢和處理地理參照資料(例如大氣層和地表等空間要素的位置和屬性)。從技術角度來說建構一個地理資訊系統,需要解決三個問題。資料如何保持,如何分析,以及怎麼展現給使用者。 GIS有多種展現形式,例如PC用戶端,web網站(見圖1),本文就不深入讨論,我們重點來研究資料如何儲存,如何分析這兩個問題。
圖(1)
GIS将通過全球定位系統GPS(Global Positioning System)得到的資料,儲存到空間資料庫。空間資料庫有别于傳統的關系資料庫,它補充了大量幾何空間相關的功能。 主要解決存儲空間中的對象資料(例如點,線和多邊型);并對空間資料的特殊需求進行空間索引, 用最優的CPU和IO效率進行空間資料對象相關的查詢。
空間分析是空間資料庫的基礎功能,它從空間物體的空間位置、聯系等方面去對空間中的對象資料進行計算和空間檢索。例如某個視圖範圍内的矩形檢索,周邊檢索,同城搜尋等空間分析功能。目前常見的商業關系資料庫都有基于GIS的擴充,例如ORACLE 的spacial database ,MS SQL的空間擴充,PostgreSQL針對空間資料庫的擴充 PostGIS 。常見的開源資料庫 MongoDB和Mysql也支援空間資料類型的存儲,但是對空間資料對象相關的檢索和查詢支援得非常少。
今生 – LBS+SNS
現在如火如荼的社會化網絡SNS大潮,改寫着網際網路的格局,也沖擊着LBS應用領域。特别是智能手機應用的噴井式爆發,吸引更多的掘金者把目光聚焦到LBS和SNS結合這個領域來。例如在 2012年12月,高德與新浪微網誌宣布戰略合作,使用者在使用高德地圖時就可以看到自己微網誌足迹的文字和圖檔資訊。
那麼要如何選擇技術架構搭建LBS與SNS結合的應用,來解決位置資訊的存儲和分析這個兩個核心問題?比較現實的選擇是如圖2A,用PostGIS作為POI興趣點(Point of Interest)資料的存儲和查詢解決方案,用Mysql + Memcache支援SNS使用者資料的存儲和查詢,通過使用者ID和POI ID把LBS和SNS這兩個看似獨立的系統進行資料關聯起來。例如世紀佳緣,陌陌(圖2B),圖釘等的使用者定位,還有瓜子社群根據使用者位置展現周邊社群消息,都适合這種架構。 通過搜尋查詢空間資料庫來找到附近的POI資訊,然後從POI中關聯的使用者ID資訊到SNS系統中去查詢使用者資訊, 通過接口調用交給手機端顯示。
圖(2)A 圖(2) B
下面用示例來展現如何實作“查詢手機使用者目前1公裡範圍内的陌生人”這個典型LBS與SNS結合的功能。 在PostGIS資料庫中 建立表
sns_lbs_tire :
CREATE TABLE sns_lbs_tire (
gid serial NOT NULL,
poi_id character varying(),
user_id integer,
geom geometry(Point,),
CONSTRAINT gid_pt_pkey PRIMARY KEY (gid)
)
WITH ( OIDS=FALSE );
表中user_id字段表示使用者的唯一ID, poi_id表示POI的唯一ID, geom字段為空間資料庫中的POINT類型存儲POI的經緯度資訊。 使用PostGIS空間資料庫的分析函數,查詢目前位置(-117.147605 32.715802)) 1公裡範圍内的所有陌生人。SQL語句 如下:
"SELECT poi_id, user_id FROM sns_lbs_tire WHERE + ST_Distance(ST_Transform(ST_GeomFromText('POINT(-117.147605 32.715802)',),), ST_Transform(geom,)) <= ;";
其中 4326 和 26986 是OGC标準中空間參照系統中常用的SRID
使用Java代碼來實作調用postGIS資料庫, 執行SQL語句查詢附近陌生人使用者ID清單。
import java.sql.*;
import java.util.*;
import java.lang.*;
public class LBS2SNS {
public static void main(String[] args) {
Connection conn;
String dbname = "template_postgis_20";
String dbuser = "postgres";
String dbpass = "admin";
String dbhost = "localhost";
String dbport = "5432";
String nearbySQL = "SELECT poi_id, user_id FROM sns_lbs_tire " +
"WHERE " +
"ST_Distance(ST_Transform(ST_GeomFromText('POINT(-117.147605 32.715802)',4326),26986)," +
"ST_Transform(geom,26986)) <= 1000;";
try {
Class.forName("org.postgresql.Driver");
String url = "jdbc:postgresql://" + dbhost + ":" + dbport + "/" + dbname;
conn = DriverManager.getConnection(url, dbuser, dbpass);
Statement s = conn.createStatement();
System.out.println("Querying table...");
ResultSet r = s.executeQuery(nearbySQL);
while( r.next() ) {
String poi_id = r.getString();
int user_id = r.getInt();
System.out.println("poi_id= " + poi_id + ",user_id=" + user_ id);
}
s.close();
conn.close();
} catch( Exception e ) {
e.printStackTrace();
}
}
}
得到使用者ID清單後,可通過圖2A中使用者資料接口查詢使用者資訊, 輕松完成圖2B的前端展示。
圖(2)的技術架構, 适用于千萬級以下使用者的LBS結合SNS的應用。當遇到需要将SNS和LBS深度結合的需求,面對海量的使用者資料帶來強烈的沖擊,它就顯得難于應付了。 例如新浪的位置微網誌(圖3),使用者使用手機釋出的微網誌都帶有經緯度坐标, 每個新的經緯度坐标都會建立一個POI。 依據新浪微網誌官方資料,每天産生 1億條微網誌,如果50%以上的使用者使用移動終端來釋出,每天就有近5千萬條位置微網誌産生。如此海量的POI資料,而且還在持續高速增長,如果使用傳統GIS的空間資料庫來存儲,無論是PostGIS還是Oracle Spatial database, 都是無法滿足存儲要求和實作高效查詢的。那麼要如何來解決上千萬使用者級别的LBS+SNS應用所需要的資料存儲和分析呢?
圖(3)
案例分析
筆者參考了網際網路上公開的資料,同時結合自己的架構經驗,對新浪位置微網誌的系統架構進行分析。筆者認為新浪位置微網誌采用了和新浪微網誌首頁的微網誌牆類似的系統架構。我們先來看一下新浪微網誌的系統架構。
新浪微網誌為了實作使用者快速通路微網誌牆,将粉絲關系和使用者的微網誌牆都放到緩存Memcached。 使用者釋出的微網誌會被推送到異步消息隊列。另外一個程序從異步消息隊列中取出微網誌,根據釋出者的使用者ID從緩存中取出釋出者的粉絲清單,将微網誌ID逐一添加到每個粉絲的微網誌牆上, 同時更新釋出者的微網誌牆。我們用明星成龍和姚晨釋出微網誌來舉例, 看看他們的微網誌是如何被粉絲甲看到的。
圖(4)
- 成龍釋出了一條微網誌001,姚晨釋出了一條微網誌002。 兩條微網誌通過http請求被送到微網誌釋出APIserver。
- 微網誌釋出APIServe将兩條微網誌消息寫到MQ消息隊列。
- 微網誌消息分發子產品從MQ隊列取出微網誌消息, 将微網誌内容寫入微網誌内容Memcached。 将微網誌001的ID 001添加到成龍的個人微網誌牆Memcached, 将微網誌002的ID 002添加到姚晨的個人微網誌牆Memcached。
- 微網誌消息分發子產品根據成龍的ID通路使用者粉絲關系Memcached找到他的粉絲ID清單。啟動多線程,根據粉絲數分批次的将微網誌001的ID 001寫到粉絲的微網誌牆上。根據姚晨的ID通路使用者粉絲關系Memcached找到他的粉絲ID清單。将微網誌001的ID 002寫到粉絲的微網誌牆上。
- 粉絲甲浏覽自己的微網誌牆時, 微網誌浏覽APIserver 根據粉絲甲的微網誌牆ID清單, 調用微網誌内容Memcached,取得微網誌牆的資料, 交給Apache Web Server進行最終的html頁面渲染, apache Web server 将最終頁面傳回給粉絲甲。
新浪位置微網誌采用了和新浪微網誌非常相似的架構,差別在于位置微網誌把緩存(Memcached)中的使用者 ID換成POI ID, 當使用者釋出微網誌時,位置微網誌會根據釋出微網誌的經緯度資訊生成POI ID。 當有使用者要查詢某個POI附近的微網誌時,通過附近查找算法, 找到附近的POI ID清單(類似于微網誌的粉絲ID清單)。由微網誌消息分發子產品将微網誌ID 寫入POI點的微網誌牆。
圖(5)
- 新浪微網誌通過微網誌消息分發子產品,将MQ消息隊列中的微網誌消息微網誌001,微網誌002分發到位置微網誌的MQ隊列。
- 位置微網誌消息分發子產品從MQ隊列取出微網誌消息,根據微網誌中包含的經緯度資訊計算生成POI ID, 将POI 資訊儲存到空間資料索引中。
- 微網誌消息分發子產品取出微網誌001,将微網誌ID 001寫入 西長安街POI的微網誌牆Memcached中。取出微網誌002将ID 002寫入東交民巷的微網誌牆Memcached。
- 位置微網誌消息分發子產品調用附近POI查詢子產品, 獲得POI ID附近的POI ID清單,把微網誌ID 001 002寫到周圍的POI 的微網誌牆Memcached中。
- 當使用者檢視戶天安門附近的微網誌時, 位置微網誌浏覽APIserver根據天安門POI ID調用POI的微網誌牆Memcached取出微網誌ID清單, 然後調用微網誌内容Memcached 取出微網誌内容。交給Apache Web Server進行最終的html頁面渲染, apache Web server 将最終頁面傳回給粉絲甲。
如上所述, 新浪微網誌根據自身業務的需要, 量身打造了一個完整的位置服務LBS+SNS的解決方案。要說明一下,新浪位置微網誌開放的位置服務API提供了:地點寫入,地點讀取,地點附近查詢,使用者LBS資訊查詢,位置微網誌動态查詢等6大類接口(參見: http://open.weibo.com/wiki/位置服務), 基本滿足了SNS網站功能開發的需要。對于獨立應用開發者,如果并不打算把産品完全綁定在新浪微網誌上,這套體系并不一定适用。
百度的LBS雲服務(參見: http://api.map.baidu.com/lbsapi/cloud/lbs-cloud.htm), 提供了一種完全透明的LBS+SNS解決方案。 它一方面解決伺服器端日益增長的海量位置資料的存儲維護壓力,另一方面徹底解決基于位置資料的高并發檢索瓶頸。 完全可以替代傳統的空間資料庫,解決基于位置資料的高并發檢索瓶頸(圖6)。這兩個問題的解決,無異于解除了應用開發者頭上的緊箍咒。唯一不足的是在實時性方面,由于存儲索引和查詢索引不是同步更新,百度LBS雲在查詢附近POI時,很可能出現不能實時查到的情況。
圖(6)
總結
綜上所述,随着LBS技術領域的不斷發展,如何解決空間資料存儲和檢索查詢這兩個問題的技術在不斷進步,湧現出了很多更好更高效的解決方案。 本文分析了在現有市場上可尋找到的技術, 對于架構一個LBS與SNS結合的應用給出了可行性的架構方案。 小型應用使用PostGIS,中型應用使用oracle spacial database 或百度LBS雲, 大型應用則要根據LBS+SNS的具體需求,自己建構合适的空間索引和業務架構。