本文介紹了TPP方案插件的使用方法。
概況
TPP方案SDK中只給出了開發必須的最小集合,如果你還需要用其它的類,就需要用到插件。比如你可能還用到:
redis
be
abfs
pai-eas
httpClient
這些jar包都在下面的tpp-aliyun-airec-plugin插件中提供。
插件安裝
<!--
plugin
-->
<dependency>
<groupId>com.aliyun.kondyle</groupId>
<artifactId>tpp-aliyun-airec-plugin</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
位置
com/alibaba/tpp/solution/${yourBizPackage}/runtime.properties
內容
plugin.dependencies=tpp-aliyun-airec-plugin:1.0-SNAPSHOT
plugin.load-from-local=true
使用樣例
以下給出一些簡單的使用例子,請大家按照實際情況填寫參數。
public class ItemDetails implements Step<RecommendContext, List<Item>> {
private CloseableHttpClient httpClient;
@Override
public void init() {
HttpClientBuilder builder = HttpClientBuilder.create();
PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager();
connectionManager.setDefaultMaxPerRoute(1000);
connectionManager.setMaxTotal(3000);
RequestConfig requestConfig = RequestConfig.custom()
.setAuthenticationEnabled(false)
.setMaxRedirects(5)
.setConnectTimeout(3000)
.setConnectionRequestTimeout(3000)
.setSocketTimeout(6000)
.setStaleConnectionCheckEnabled(true).build();
builder.setConnectionManager(connectionManager);
builder.setDefaultRequestConfig(requestConfig);
httpClient = builder.build();
}
@Override
public void destroy() {
if (httpClient!=null){
HttpClientUtils.closeQuietly(httpClient);
}
}
/**
* 查詢item詳情
*/
public List<Item> queryDetails(List<String> itemIds){
String host ="example.com";
String path = "/items";
HttpGet httpGet = new HttpGet(host + path);
httpGet.addHeader("Content-Type", ContentType.APPLICATION_JSON.getMimeType());
try {
CloseableHttpResponse response = httpClient.execute(httpGet);
int code = response.getStatusLine().getStatusCode();
String entity = EntityUtils.toString(response.getEntity(), "UTF-8");
if (code == 200 && entity != null) {
JSONObject entityObject = JSON.parseObject(entity, JSONObject.class);
if (entityObject.getBoolean("success")) {
String detailString = entityObject.getString("result");
return JSON.parseArray(detailString,Item.class);
}
} else {
//todo your logic
}
} catch (Exception e) {
//todo your logic
}
return null;
}
@Override
public List<Item> invoke(RecommendContext context) throws Exception {
List<Item> detailList = queryDetails(context.getItemIds());
context.setItemList(detailList);
return detailList;
}
}
public abstract class RedisMatch<V> implements Match<RecommendContext,V> {
@Getter
private RedisClient redisClient;
@PostConstruct
public void init() {
RedisConfig redisConfig = new RedisConfig();
redisConfig.setHost("r-uf***.redis.rds.aliyuncs.com");
redisConfig.setPassword("redisAccount:redisPassword");
redisConfig.setPort(6379);
redisConfig.setTimeout(3000);
redisConfig.setMaxIdleConnectionCount(16);
redisConfig.setMaxTotalConnectionCount(64);
this.redisClient = new RedisClient(redisConfig);
redisClient.init();
}
@PreDestroy
public void destroy() {
redisClient.destroy();
}
//召回
public final List<V> match(RecommendContext context) throws Exception{
List<V> results = new LinkedList<>();
List<String> keys = buildKey(context);
for (String key : keys) {
Jedis jedis = redisClient.getResource();
try {
List<String> value = jedis.lrange(key, 0, -1)
.stream().filter(line -> line != null && line.length() > 0)
.collect(Collectors.toList());
if (value == null || value.isEmpty()) {
throw new RuntimeException("value is empty");
}
results.addAll(parseValue(context, value));
} catch (Exception e) {
//某幾個key失敗,記空結果日志, 寫 empty reason 進 trace log
context.getEmptyTraceLog().put("match "+key+" empty",JSON.toJSONString(e.getStackTrace()));
//記數據日志,用于排查監控
context.getDataTraceLog().put(this.getClass().getName(), key);
} finally {
if (jedis!=null) {
jedis.close();
}
}
}
return results;
}
//構造key
protected abstract List<String> buildKey(RecommendContext context) throws Exception;
//解析value
//空結果應該落日志排查, 不允許拋異常
protected abstract List<V> parseValue(RecommendContext context, List<String> value);
}
public abstract class EasRank<REQUEST extends MessageLite, RESULT> implements Rank<RecommendContext,RESULT> {
@Getter
private PredictClient client;
@PostConstruct
public void init() {
EasConfig easConfig = new EasConfig()
easConfig.setHost("1***7.cn-shanghai.pai-eas.aliyuncs.com");
easConfig.setToken("easyrec_***_tower");
easConfig.setModel("Y***=");
HttpConfig httpConfig = new HttpConfig();
httpConfig.setReadTimeout(easConfig.getReadTimeout());
httpConfig.setConnectTimeout(easConfig.getConnectTimeout());
httpConfig.setRequestTimeout(easConfig.getRequestTimeout());
client = new PredictClient(httpConfig);
client.setEndpoint(easConfig.getHost());
client.setModelName(easConfig.getModel());
client.setToken(easConfig.getToken());
}
@PreDestroy
public void destroy() {
if (client != null) {
client.shutdown();
}
}
@Override
public boolean skip(RecommendContext context) {
return !context.isEasRankSwitch();
}
@Override
public List<RESULT> rank(RecommendContext context) throws Exception {
checkParams(context);
REQUEST request = buildRequest(context);
byte[] rawResponse = client.predict(request.toByteArray());
if (rawResponse == null || rawResponse.length<=0){
//記數據日志,用于排查監控
context.getDataTraceLog().put(this.getClass().getName(), JSON.toJSONString(request));
}
return parseResponse(context, rawResponse);
}
protected void checkParams(RecommendContext context) {
if (context.getItemIds() == null || context.getItemIds().isEmpty()) {
throw new IllegalArgumentException("itemIds is empty");
}
}
//構造request
protected abstract REQUEST buildRequest(RecommendContext context);
//解析response
//空結果應該落日志排查,不允許拋異常
protected abstract List<RESULT> parseResponse(RecommendContext context, byte[] response) throws Exception;
}
public abstract class BeMatch<RESPONSE> implements Match<RecommendContext, RESPONSE> {
@Getter
private BeClient beClient;
public BeMatch() {
BeConfig beConfig = new BeConfig();
beConfig.setPassword("password");
beConfig.setUsername("username");
beConfig.setDomain("aime-cn-******.aime.aliyuncs.com");
beConfig.setBizName("x2i_match");
beClient = new BeClient(beConfig.getDomain(), beConfig.getPort(), beConfig.getUsername(), beConfig.getPassword());
}
public BeMatch(BeConfig beConfig) {
beClient = new BeClient(beConfig.getDomain(), beConfig.getPort(), beConfig.getUsername(), beConfig.getPassword());
}
@Override
public List<RESPONSE> match(RecommendContext context) throws Exception {
checkParams(context);
List<String> triggers = buildKey(context);
BeReadRequest x2iRequest = BeReadRequest.builder()
.bizName(beConfig.getBizName())
.bizType(BeBizType.X2I)
.returnCount(context.getMatchMaxNum())
.items(triggers)
.filter(new FilterClause(new SingleFilter(
beConfig.getBeMatchFilterField(),
FilterOperator.GT,
"'" + beConfig.getBeMatchFilterValue() + "'"))
)
.build();
BeResponse<BeResult> x2iResponse = beClient.query(x2iRequest);
return parseResponse(context,x2iResponse);
}
protected void checkParams(RecommendContext context) {
if (context.getUserId() == null || context.getUserId().isEmpty()) {
throw new IllegalArgumentException("userId is empty");
}
}
//構造key
protected abstract List<String> buildKey(RecommendContext context) throws Exception;
/**
* 解析response
*/
protected abstract List<RESPONSE> parseResponse(RecommendContext context,BeResponse<BeResult> response);
}
public class FeatureSearcher implements Step<RecommendContext, Map<String, String>> {
private ABFSClient abfsClient;
public FeatureSearcher(ABFSConfig abfsConfig) {
this.abfsClient = ABFSClientBuilder.create().build(
abfsConfig.getSrc(),
abfsConfig.getEndpoint(),
abfsConfig.getUserName(),
abfsConfig.getUserPasswd());
}
@Override
public void destroy() {
abfsClient.close();
}
@Override
public Map<String, String> invoke(RecommendContext context) throws Exception {
Map<String, String> userFeatures = userFeatures(context);
return userFeatures;
}
// query user feature
private Map<String, String> userFeatures(RecommendContext context) {
String userId = context.getUserId();
if (userId!=null) {
KeyList keyList = new KeyList(userId, context.getBizId());
AtomicQuery atomicQuery = AtomicQuery.builder()
.table("tpp_rec_demo_user")
.keyLists(Arrays.asList(keyList))
.fields(Lists.newArrayList("gender", "age_level", "pay_level", "user_tag", "city")) // 設置返回字段子句,只有設置的這些字段會序列化回到客戶端,默認全返回
.filter("is_cap=1") // 設置過濾子句,默認無過濾
.orderby("+age_level;-pay_level") // 設置orderby子句,默認無排序
.build();
try {
QueryResult queryResult = abfsClient.searchSync(new PgSessionCtx(), atomicQuery);
if (queryResult != null) {
List<SingleQueryResult> singleQueryResultList = queryResult.getAllQueryResult();
if (!singleQueryResultList.isEmpty()) {
Map<String, String> userFeatures = new HashMap<>(singleQueryResultList.size() * 16);
singleQueryResultList.forEach(singleQueryResult -> {
int genderIndex = singleQueryResult.getFieldIndex("gender");
int ageLevelIndex = singleQueryResult.getFieldIndex("age_level");
int payLevelIndex = singleQueryResult.getFieldIndex("pay_level");
int userTagIndex = singleQueryResult.getFieldIndex("user_tag");
int cityIndex = singleQueryResult.getFieldIndex("city");
singleQueryResult.getMatchRecords().forEach(matchRecord -> {
userFeatures.put("gender", matchRecord.getString(genderIndex));
userFeatures.put("age_level", matchRecord.getString(ageLevelIndex));
userFeatures.put("pay_level", matchRecord.getString(payLevelIndex));
userFeatures.put("user_tag", matchRecord.getString(userTagIndex));
userFeatures.put("city", matchRecord.getString(cityIndex));
});
});
return userFeatures;
}
}
} catch (Exception e) {
context.getLogContext().error(this.getClass().getName(), e);
}
}
return Collections.EMPTY_MAP;
}
}
網絡問題
本地無法連上redis/pai-eas/be/abfs/其它HTTP服務
解決方法:找相關的技術支持答疑
在tpp上線后,無法連上redis/pai-eas/be/abfs/其它HTTP服務
解決方法:確認和tpp是否在同一個vpc,如果是,請查看下是否是賬號密碼不對,或者沒有設置白名單。
文檔內容是否對您有幫助?