掃二維碼與項目經(jīng)理溝通
我們在微信上24小時期待你的聲音
解答本文疑問/技術(shù)咨詢/運(yùn)營咨詢/技術(shù)建議/互聯(lián)網(wǎng)交流
作者在阿里做過和學(xué)習(xí)過不少服務(wù)實現(xiàn),如下,給阿里服務(wù)體系中常見服務(wù)大致分一個類,每個類別有些是應(yīng)用層,有些是中間層,這里不作贅述,這里我們重點討論內(nèi)容型服務(wù)

比如對于我們淘系業(yè)務(wù),搭建一套服務(wù),我們需要先想清楚下面的幾個點
構(gòu)建一套pipeline,每個處理節(jié)點關(guān)注一個問題解決
是不是看起來有點類似Java8中stream的API,但這套pipeline具體還是偏內(nèi)容下發(fā)業(yè)務(wù),比Java原生API要更豐富,以淘寶的AR淘業(yè)務(wù)為例,pipeline如下:
下面代碼實現(xiàn)一套基于運(yùn)營配置數(shù)據(jù)源的pipeline
1.首先,自定義Pipeline,沒有使用Lambada,對java7及以下也適用
public class PipeLine{
private List> functionList = new ArrayList<>();
public PipeLineadd(PipeLineFunction pipeLineFunction) {
functionList.add(pipeLineFunction);
return this;
}
public D execute(D data, C context) throws Exception {
for (PipeLineFunction function : functionList) {
data = (D) function.execute(data, context);
}
return data;
}
}
2.其次管線,對于處理次序和節(jié)點進(jìn)行指定:包括從配置讀取數(shù)據(jù)--->通過arType過濾--->隨機(jī)打亂數(shù)據(jù)--->置頂主題類數(shù)據(jù)--->翻頁--->增加sku和item信息--->增加AR模型信息--->完整性校驗
public void initSkuResultHotRecommendPipeLine() {
PipeLine skuResultHotRecommendPipeLine = new PipeLine();
skuResultHotRecommendPipeLine
.add((data, context) -> skuResultDataSource.fromConfig(context))
.add((data, context) -> skuResultSorter.shuffle(data))
.add((data, context) -> skuResultSorter.topTheme(data, context))
.add((data, context) -> skuResultSorter.page(data, context))
.add((data, context) -> skuResultCompleter.addSkuInfo(data))
.add((data, context) -> skuResultCompleter.addAREffect(data, context))
.add((data, context) -> skuResultValidator.check(data));
}
3.最后,搭建pipeline,接口收到請求后,通過管線處理,下發(fā)對應(yīng)內(nèi)容
public ResultVOgetSkuList(SkuQuery skuQuery) {
try {
SkuResultVO skuResultVO = skuResultHotRecommendPipeLine
.execute(new SkuResultVO(), skuQuery);
} catch (Exception e) {
log.error("", e.fillInStackTrace());
return ResultVO.failOf(e.getMessage());
}
return ResultVO.failOf(CameraArCause.No_Valid_Ar_Type
.toMessage(skuQuery.toString()));
}
4.我們再討論一下對于有固定的遍歷邏輯的情況,遍歷方式也可以抽象成一個iterator,在不同的filter作為參數(shù)傳入下,完成遍歷功能,下圖就是對商品的一種遍歷,這個特性用到FunctionalInterface標(biāo)注,需要java8及以上
(1) 定義遍歷器
@FunctionalInterface
public interface FilterFunction{
boolean execute(T t) throws Exception;
}
@FunctionalInterface
public interface IterateFunction{
T execute(T t, FilterFunction filterFunction);
}
private IterateFunctionskuVOFilterIterator = (skuResultVO, filter) -> {
ListskuFeedUnitVOList = skuResultVO.getSkuFeedUnitVOList()
.stream().filter(skuFeedUnitVO -> {
ListfilterSkuVOList = skuFeedUnitVO.getSkuVOList()
.stream().filter(skuVO -> {
try {
return filter.execute(skuVO);
} catch (Exception e) {
log.warn("", e);
return false;
}
}).collect(toList());
if (filterSkuVOList.size() == 0) {
return false;
}
skuFeedUnitVO.setSkuVOList(filterSkuVOList);
return true;
}).collect(toList());
if (skuFeedUnitVOList.size() == 0) {
log.warn(CameraArCause.No_Valid_Sku_Feed_Unit_List
.toMessage(skuResultVO.toString()));
}
skuResultVO.setSkuFeedUnitVOList(skuFeedUnitVOList);
return skuResultVO;
};
(2) 在Filter模塊中使用遍歷器,如果把skuResultVO換成一個返回SkuResultVO的Supplier,是不是有點柯里化的味道啦?
public SkuResultVO byArType(SkuResultVO skuResultVO) {
return skuResultIterator.getSkuVOFilterIterator()
.execute(skuResultVO, (FilterFunction) skuVO ->
!CameraArSwitch.Black_List_Config.getArType()
.contains(skuVO.getArType()));
}
整體架構(gòu)
對于補(bǔ)齊數(shù)據(jù)源的選擇,要詳細(xì)了解上游各個補(bǔ)齊數(shù)據(jù)源的業(yè)務(wù)定位和業(yè)務(wù)邊界,選擇合適的補(bǔ)齊數(shù)據(jù)源。比如淘系商品補(bǔ)全數(shù)據(jù)源常見服務(wù)有以下幾個,要根據(jù)業(yè)務(wù)自身需求
|
數(shù)據(jù)補(bǔ)齊二方服務(wù) |
優(yōu)勢 |
劣勢 |
|
商品中心(IC) |
最底層的數(shù)據(jù)源,有item和sku維度的信息 |
維度不夠多,比如一些商品的運(yùn)營信息 |
|
阿拉丁 |
維度比較多,比如商品白底圖 |
只有item維度信息,但是有些數(shù)據(jù)源不穩(wěn)定,比如品牌信息有些商品會缺失 |
|
搜索的Summary |
跟主搜的信息保持一致,信息比較實時,比如優(yōu)惠、銷量信息 |
只有item維度信息,維度不夠多 |
內(nèi)容下發(fā)除了做好個性化,如果是一個公域產(chǎn)品,對內(nèi)容的篩選能力決定用戶是否能主動找到自己想要的商品,我們需要設(shè)計一個易擴(kuò)展的篩選器接口,常見的垂直頻道類產(chǎn)品,一級和二級tab頁就滿足業(yè)務(wù)訴求,但對應(yīng)比較大的公域,比如搜索,需要支持多維度篩選+多篩選能力,都需要實現(xiàn)兩個接口,這時, 我們需要設(shè)計一個通用的接口格式,做好兩件事。
只需要下發(fā)一級和二級的tab樹狀接口即可,用戶通過先后選擇一級和二級tab來過濾內(nèi)容,這里不作過多的討論。
需要下發(fā)多維度篩選器,如果有一級Tab,多維度篩選器放在每個Tab內(nèi)部,例子如下:
1.篩選器下發(fā)接口,格式如下,其中Name結(jié)尾的字段為前端展示,Id結(jié)尾的字段作為篩選上傳的接口字段
{
"tabList": [
{
"tabName": "tab1",
"tabId": "xxx",
"filterList": [
{
"filterName": "xxx",
"filterId": "xxx",
"filterItemList": [
{
"filterItemName": "fitler1",
"filterItemId": "xxx"
},
{
"filterItemName": "fitler2",
"filterItemId": "xxx"
}
]
}
]
},
{
"tabName": "tab2",
"tabId": "xxx",
"filterList": [
{
"filterName": "xxx",
"filterId": "xxx",
"filterItemList": [
{
"filterItemName": "fitler1",
"filterItemId": "xxx"
},
{
"filterItemName": "fitler2",
"filterItemId": "xxx"
}
]
}
]
}
]
}
2.上傳用戶選擇的篩選項,接口格式如下,tabId可以作為單獨的字段傳,filterList是另外一個字段
{
"tabId": "xxx",
"filterList": [
{
"filterId": "xxx",
"filterItemList": [
{
"filterItemId": "xxx"
},
{
"filterItemId": "xxx"
}
]
},
{
"filterId": "xxx",
"filterItemList": [
{
"filterItemId": "xxx"
},
{
"filterItemId": "xxx"
}
]
}
]
}
本文通過大淘寶業(yè)務(wù)的例子,梳理出一套 內(nèi)容服務(wù)下發(fā)內(nèi)容時面臨的問題和挑戰(zhàn)、設(shè)計一套內(nèi)容處理鏈路,用模塊化的設(shè)計來控制系統(tǒng)的復(fù)雜度,并且對接口上線后,如何靈活運(yùn)維進(jìn)行了討論,重點研究了保證易擴(kuò)展、易運(yùn)維的思路,希望對各位有所啟發(fā)。
內(nèi)容下發(fā)服務(wù)已經(jīng)有些團(tuán)隊推出了前后端一體的方案,客戶端不用請求應(yīng)用服務(wù)來拿商品數(shù)據(jù),通過統(tǒng)一的平臺接口,把客戶端布局和內(nèi)容填充一起請求來返回,這么做的好處是效率比較高,迭代起來快,帶來的不便是客戶端和平臺耦合比較緊,靈活性變差,比如客戶端有一塊內(nèi)容源不走平臺,就會比較麻煩

我們在微信上24小時期待你的聲音
解答本文疑問/技術(shù)咨詢/運(yùn)營咨詢/技術(shù)建議/互聯(lián)網(wǎng)交流