本文介绍了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
![image](https://help-static-aliyun-doc.aliyuncs.com/assets/img/zh-CN/9506770861/p622945.png)
内容
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,如果是,请查看下是否是账号密码不对,或者没有设置白名单。
该文章对您有帮助吗?