編寫目的
FCM官方文檔中描述比較零散,我自己在調研接入方式時也耗費了一些時間,是以我想編寫一個文檔,對接入FCM前後端的改造點做一個完整的描述,也算是對FCM調研過程做一個總結,希望能給有相關需求的人一些參考,文檔會分為FCM服務申請,WEB端SDK接入,伺服器端消息發送三個部分來描述,所有的項目代碼都在github:https://github.com/caixunshi/go-fcm-example
歡迎大家star
參考資料
FCM官方文檔
一、FCM服務申請
谷歌推送以前叫GCM,在2019年4月11日遷移到了firebase cloud message(FCM),我們要使用FCM,需要在firebase上建立一個項目,是以需要有一個gmail郵箱
1.1 添加項目
通路https://console.firebase.google.com/,登陸後可以看到以下界面:
然後點選Add project,建立一個firebase項目
1.2 擷取應用憑證
建立号項目之後,點選項目→進入控制台 → 點選設定
然後在正常資訊欄中,檢視到web應用憑證,這裡也提供了npm和cdn的內建方式示例
應用憑證是用來給到前端內建SDK與FCM建立長連結,這裡我們這裡的應用憑證是:
{
apiKey: "AIzaSyAUVTaipaExXUTGGc7e-A3gUiA3Q8i7O8Y",
authDomain: "shipment-portal-1f1b3.firebaseapp.com",
projectId: "shipment-portal-1f1b3",
storageBucket: "shipment-portal-1f1b3.appspot.com",
messagingSenderId: "333922912996",
appId: "1:333922912996:web:18467f5642e6fba00efaf1",
measurementId: "G-5HNY007WZW"
}
然後在回到雲消息傳遞資訊欄,檢視到伺服器密鑰
伺服器密鑰用來向FCM推送消息,然後由FCM通過長連結推送給web用戶端
伺服器密鑰用來可以添加多個,這裡我們使用的密鑰是:
AAAAjaT6Kls:APA91bFki15CULQgVFGKn1N3cR-PlKhcU5wMEClTl5NtP9YQylHSBsJctlZzQ0sdp6TnOsp5jyzzqW4tzSVDneKCmP8XQ8ZgA8w6wk6AW28aAdZn6eyz0U-OA7TuJ2WlkBbS10KcxQHe
二、前端項目內建firebase sdk
前端可以通過npm或cdn的方式将fcm內建進自己的web項目,這裡我用cdn的方式做示例,npm的操作方式可以參考:https://firebase.google.com/docs/web/setup
2.1 建立index.html
在界面上我們添加了一個accountId的輸入框,點選登入之後,會将accountId和目前web用戶端的token發送給伺服器,用來模拟使用者登陸的場景,界面如下:
在onload中我們初始化firebase,并通過requestPermission方法請求使用者允許發送通知,然後注冊了一個messaging.onMessage監聽,當FCM通過長連結推送消息到web用戶端時,并且web應用位于前台時,會執行onMessage方法,請求使用者允許通知界面如下:
index.html的所有代碼如下:
<html xmlns="http://www.w3.org/1999/html">
<body>
<script src="https://www.gstatic.com/firebasejs/8.10.0/firebase-app.js"></script>
<script src="https://www.gstatic.com/firebasejs/8.10.0/firebase-messaging.js"></script>
<h1>FCM</h1>
account: <input id="accountId"/>
<input type="button" value="login" onclick="login()"/>
<div id="info"></div>
<div id="msg" style="font-size:64px;color:red">0</div>
</body>
<script>
var accessToken = "123123"
// 定義配置檔案
var firebaseConfig = {
apiKey: "AIzaSyBqBrEAaRBXhqtOaHaknfNV4_27Go2P3zE",
authDomain: "weqwe-d71c5.firebaseapp.com",
projectId: "weqwe-d71c5",
storageBucket: "weqwe-d71c5.appspot.com",
messagingSenderId: "608358247003",
appId: "1:608358247003:web:433e10a50f5f51a5db2d7e",
measurementId: "G-4QGGNLHSWW"
};
function login() {
var accountId = document.getElementById("accountId").value
// 注冊token到背景
document.getElementById("info").innerHTML = "token: " + accessToken
resp = fetch("http://localhost:8090/accessToken", {
method: "POST",
mode:"cors",
headers: {
"Content-type": 'application/json;charset=utf-8'
},
body: JSON.stringify({"accountId": accountId, "token": accessToken}),
})
console.log(resp);
}
window.onload = function () {
// 生成token
firebase.initializeApp(firebaseConfig);
var messaging = firebase.messaging();
messaging.requestPermission()
.then(function () {
return messaging.getToken();
})
.then(function (token) {
accessToken = token
})
.catch(function (err) {
console.log('Unable to get permission to notify.', err);
});
// 注冊監聽事件
messaging.onMessage(function (payload) {
// 更新清單消息數量
console.log('Message received. ', payload);
var msg = document.getElementById("msg").innerHTML
document.getElementById("msg").innerHTML = Number(msg) + 1
});
}
</script>
</html>
2.2 建立firebase-messaging-sw.js
當使用者界面處于背景時,是無法觸發onMessage方法,firebasebase基于service work提供了onBackgroundMessage方法,我們在index.html同目錄下建立一個firebase-messaging-sw.js,然後注冊一個onBackgroundMessage方法
代碼如下:
importScripts('https://www.gstatic.com/firebasejs/8.10.0/firebase-app.js');
importScripts('https://www.gstatic.com/firebasejs/8.10.0/firebase-messaging.js');
// Initialize the Firebase app in the service worker by passing in the
// messagingSenderId.
firebase.initializeApp({
apiKey: "AIzaSyAUVTaipaExXUTGGc7e-A3gUiA3Q8i7O8Y",
authDomain: "shipment-portal-1f1b3.firebaseapp.com",
projectId: "shipment-portal-1f1b3",
storageBucket: "shipment-portal-1f1b3.appspot.com",
messagingSenderId: "333922912996",
appId: "1:333922912996:web:18467f5642e6fba00efaf1",
measurementId: "G-5HNY007WZW"
});
// Retrieve an instance of Firebase Messaging so that it can handle background
// messages.
const messaging = firebase.messaging();
messaging.onBackgroundMessage((payload) => {
console.log('Received background message ', payload);
// console.log(self)
var title = payload.data.title
var options = {
body: payload.data.body,
icon: payload.data.url
}
self.registration.showNotification(title,
options);
});
我們在onBackgroundMessage中通過showNotification彈出一個彈框,當web應用處于背景時(使用者未停留在web界面),有FCM傳遞過來的新消息,會彈出一個彈框,效果如下:
三、後端項目發送通知消息
3.1 互動流程圖
3.2 建立Admin項目
建立一個Admin項目模拟我們的背景,主要是提供了查詢線上使用者清單,登陸,發送消息三個基本功能,用來模拟和web端的互動,admin的界面如下:
- 查詢線上使用者清單:使用者初始化SDK成功之後,我們需要将使用者的accountId和目前的token儲存起來;
- 登陸:在使用者web界面,點選login之後會将accountId和token傳遞給背景進行儲存;
- 發送消息:輸入需要發送的accountId和title body,url之後,點選send,消息内容達到背景之後,會通過POST請求發給FCM;
四、總結
4.1 項目代碼:
4.2 後端啟動
後端是基于gin建構,直接啟動main函數即可啟動
4.3 前端啟動
靜态資源的部署有很多種方式,一般我們可以利用nginx去部署,這裡我們利用http-server這種更加輕量的方式啟動一個http-server伺服器,http-server是一個輕量級的基于nodejs的http伺服器,可以使任意一個目錄成為伺服器的目錄,完全抛開背景的沉重工程,直接運作想要的js代碼。
4.2.1 安裝node
直接下載下傳安裝即可,官網位址: https://nodejs.org
安裝成功之後在指令行輸入指令$ node -v以及$ npm -v檢查版本,确認是否安裝成功。
4.2.2 安裝http-server
打開終端輸入:
npm install http-server -g
安裝成功之後進入目标檔案夾,這裡就是我們項目的 front/src
然後輸入:
http-server
這樣就會在本地8080端口啟動一個http伺服器(如果8080被占用則會在8081端口啟動,以此類推),啟動界面如下:
使用者web界面為:http://localhost:8080/index.html
後端管理界面為:http://localhost:8080/admin.html
這樣我們就可以在兩個浏覽器分别測試消息發送啦
PS: 使用者web界面谷歌浏覽器service work不支援非https,是以不能測試使用者離開web界面的彈框提醒場景,隻能測試使用者停留在web界面接受消息的場景,需要看效果的可以用firefox
4.3 思考
- 使用者初始化SDK并與FCM建立長連結是沒有傳入業務參數的,生成出來的token是跟我們的業務資料(如使用者)無關的,是以我們需要用戶端主動上報token 和 username,并在我們自己的背景維護這個關系,但這也衍生出來幾個問題,需要根據具體的業務場景去制定解決方案。
-
使用者在多個端(比如多個浏覽器)登陸時,一個使用者會有多個token,如果想要所有端都通知的話,就需要保留所有的token,否則就隻有最後登陸的端能接收到消,使用者量很大的情況下,儲存這個關系需要成本;
使用者下線之後,伺服器的token和user的關聯關系如何删除?(直接關掉web界面或直接kill 掉 app);