文档

TPP方案插件

本文介绍了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
  • 内容

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,如果是,请查看下是否是账号密码不对,或者没有设置白名单。

  • 本页导读 (0)
文档反馈