Create & Init Project...

This commit is contained in:
2019-04-22 18:49:16 +08:00
commit fc4fa37393
25440 changed files with 4054998 additions and 0 deletions

View File

@@ -0,0 +1,2 @@
### v1.0.0
1. 上线功能xxx

View File

@@ -0,0 +1,6 @@
# Owner
weixuan
# Author
# Reviewer

View File

@@ -0,0 +1,10 @@
# See the OWNERS docs at https://go.k8s.io/owners
approvers:
- weixuan
labels:
- live
- service
- service/live/xroom-feed
options:
no_parent_owners: true

View File

@@ -0,0 +1,12 @@
# xroom-feed-service
## 项目简介
1.
## 编译环境
## 依赖包
## 编译执行

View File

@@ -0,0 +1,63 @@
load(
"@io_bazel_rules_go//proto:def.bzl",
"go_proto_library",
)
package(default_visibility = ["//visibility:public"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_library",
)
proto_library(
name = "api_proto",
srcs = ["api.proto"],
tags = ["automanaged"],
deps = ["@gogo_special_proto//github.com/gogo/protobuf/gogoproto"],
)
go_proto_library(
name = "api_go_proto",
compilers = ["@io_bazel_rules_go//proto:gogofast_grpc"],
importpath = "go-common/app/service/live/xroom-feed/api",
proto = ":api_proto",
tags = ["automanaged"],
deps = ["@com_github_gogo_protobuf//gogoproto:go_default_library"],
)
go_library(
name = "go_default_library",
srcs = [
"api.bm.go",
"client.go",
"generate.go",
],
embed = [":api_go_proto"],
importpath = "go-common/app/service/live/xroom-feed/api",
tags = ["automanaged"],
visibility = ["//visibility:public"],
deps = [
"//library/net/http/blademaster:go_default_library",
"//library/net/http/blademaster/binding:go_default_library",
"//library/net/rpc/warden:go_default_library",
"@com_github_gogo_protobuf//gogoproto:go_default_library",
"@com_github_gogo_protobuf//proto:go_default_library",
"@org_golang_google_grpc//:go_default_library",
"@org_golang_x_net//context:go_default_library",
],
)
filegroup(
name = "package-srcs",
srcs = glob(["**"]),
tags = ["automanaged"],
visibility = ["//visibility:private"],
)
filegroup(
name = "all-srcs",
srcs = [":package-srcs"],
tags = ["automanaged"],
visibility = ["//visibility:public"],
)

View File

@@ -0,0 +1,51 @@
// Code generated by protoc-gen-bm v0.1, DO NOT EDIT.
// source: api.proto
/*
Package api is a generated blademaster stub package.
This code was generated with go-common/app/tool/bmgen/protoc-gen-bm v0.1.
It is generated from these files:
api.proto
*/
package api
import (
"context"
bm "go-common/library/net/http/blademaster"
"go-common/library/net/http/blademaster/binding"
)
// to suppressed 'imported but not used warning'
var _ *bm.Context
var _ context.Context
var _ binding.StructValidator
var PathRecPoolGetList = "/live.xroomfeed.v1.RecPool/GetList"
// =================
// RecPool Interface
// =================
type RecPoolBMServer interface {
// 根据模块位置获取投放列表 position=>RoomItem
GetList(ctx context.Context, req *RecPoolReq) (resp *RecPoolResp, err error)
}
var RecPoolSvc RecPoolBMServer
func recPoolGetList(c *bm.Context) {
p := new(RecPoolReq)
if err := c.BindWith(p, binding.Default(c.Request.Method, c.Request.Header.Get("Content-Type"))); err != nil {
return
}
resp, err := RecPoolSvc.GetList(c, p)
c.JSON(resp, err)
}
// RegisterRecPoolBMServer Register the blademaster route
func RegisterRecPoolBMServer(e *bm.Engine, server RecPoolBMServer) {
RecPoolSvc = server
e.GET("/live.xroomfeed.v1.RecPool/GetList", recPoolGetList)
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,59 @@
syntax = "proto3";
import "github.com/gogo/protobuf/gogoproto/gogo.proto";
package live.xroomfeed.v1;
option go_package = "api";
option (gogoproto.goproto_getters_all) = false;
service RecPool {
// 根据模块位置获取投放列表 position=>RoomItem
rpc GetList (RecPoolReq) returns (RecPoolResp);
}
message RecPoolReq {
// 投放模块
int64 module_type = 1 [(gogoproto.moretags)='form:"module_type" validate:"required"'];
// 投放模块位置数
int64 position_num = 2 [(gogoproto.moretags)='form:"position_num" validate:"required"'];
// 投放模块页数 不传或传0、1都按一页算(暂时没用)
int64 page_num = 3 [(gogoproto.moretags)='form:"page_num"'];
// 当前模块已存在的位置房间逗号分隔、有序1~position*N内部去重,保证同一个房间优先出现在好位置)
string module_exist_rooms = 4 [(gogoproto.moretags)='form:"module_exist_rooms"'];
// 其它模块已存在的位置房间逗号分隔、有序1~position*N内部去重,保证同一个房间优先出现在好位置)
string other_exist_rooms = 5 [(gogoproto.moretags)='form:"other_exist_rooms"'];
// 请求来源
string from = 6 [(gogoproto.moretags)='form:"from"'];
}
message RecPoolResp {
// 主播position => 房间信息(依赖计算的)
map<int64, RoomItem> list = 1 [(gogoproto.jsontag) = "list"];
}
message RoomItem {
//房间id
int64 room_id = 1 [(gogoproto.jsontag) = 'room_id'];
//主播uid
int64 uid = 2 [(gogoproto.jsontag) = 'uid'];
//房间标题
string title = 3 [(gogoproto.jsontag) = 'title'];
//人气
int64 popularity_count = 4 [(gogoproto.jsontag) = 'popularity_count'];
//关键帧
string keyframe = 5 [(gogoproto.jsontag) = 'keyframe'];
//封面
string cover = 6 [(gogoproto.jsontag) = 'cover'];
//二级分区id
int64 area_id = 7 [(gogoproto.jsontag) = 'area_id'];
//一级分区id
int64 parent_area_id = 8 [(gogoproto.jsontag) = 'parent_area_id'];
//二级分区名称
string area_name = 9 [(gogoproto.jsontag) = 'area_name'];
//一级分区名称
string parent_area_name = 10 [(gogoproto.jsontag) = 'parent_area_name'];
//推荐规则 10000+rule_id
int64 rec_type = 11 [(gogoproto.jsontag) = 'rec_type'];
}

View File

@@ -0,0 +1,57 @@
<!-- package=live.xroomfeed.v1 -->
- [/live.xroomfeed.v1.RecPool/GetList](#live.xroomfeed.v1.RecPoolGetList) 根据模块位置获取投放列表 position=>RoomItem
## /live.xroomfeed.v1.RecPool/GetList
### 根据模块位置获取投放列表 position=>RoomItem
#### 方法GET
#### 请求参数
|参数名|必选|类型|描述|
|:---|:---|:---|:---|
|module_type|是|integer| 投放模块|
|position_num|是|integer| 投放模块位置数|
|page_num|否|integer| 投放模块页数 不传或传0、1都按一页算(暂时没用)|
|module_exist_rooms|否|string| 当前模块已存在的位置房间逗号分隔、有序1~position*N内部去重,保证同一个房间优先出现在好位置)|
|other_exist_rooms|否|string| 其它模块已存在的位置房间逗号分隔、有序1~position*N内部去重,保证同一个房间优先出现在好位置)|
|from|否|string| 请求来源|
#### 响应
```javascript
{
"code": 0,
"message": "ok",
"data": {
// 主播position => 房间信息(依赖计算的)
"list": {
"1": {
// 房间id
"room_id": 0,
// 主播uid
"uid": 0,
// 房间标题
"title": "",
// 人气
"popularity_count": 0,
// 关键帧
"keyframe": "",
// 封面
"cover": "",
// 二级分区id
"area_id": 0,
// 一级分区id
"parent_area_id": 0,
// 二级分区名称
"area_name": "",
// 一级分区名称
"parent_area_name": "",
// 推荐规则 10000+rule_id
"rec_type": 0
}
}
}
}
```

View File

@@ -0,0 +1,27 @@
package api
import (
"context"
"google.golang.org/grpc"
"go-common/library/net/rpc/warden"
)
const AppID = "live.xroomfeed"
type Client struct {
RecPoolClient RecPoolClient
}
// NewClient xroom-feed grpc client
func NewClient(cfg *warden.ClientConfig, opts ...grpc.DialOption) (*Client, error) {
client := warden.NewClient(cfg, opts...)
conn, err := client.Dial(context.Background(), "discovery://default/"+AppID)
if err != nil {
return nil, err
}
cli := &Client{}
cli.RecPoolClient = NewRecPoolClient(conn)
return cli, nil
}

View File

@@ -0,0 +1,4 @@
package api
// 生成 gRPC 代码
//go:generate $GOPATH/src/go-common/app/tool/warden/protoc.sh

View File

@@ -0,0 +1,43 @@
package(default_visibility = ["//visibility:public"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_binary",
"go_library",
)
go_binary(
name = "cmd",
embed = [":go_default_library"],
tags = ["automanaged"],
)
go_library(
name = "go_default_library",
srcs = ["main.go"],
importpath = "go-common/app/service/live/xroom-feed/cmd",
tags = ["automanaged"],
visibility = ["//visibility:public"],
deps = [
"//app/service/live/xroom-feed/internal/server/grpc:go_default_library",
"//app/service/live/xroom-feed/internal/server/http:go_default_library",
"//app/service/live/xroom-feed/internal/service:go_default_library",
"//library/conf/paladin:go_default_library",
"//library/ecode/tip:go_default_library",
"//library/log:go_default_library",
],
)
filegroup(
name = "package-srcs",
srcs = glob(["**"]),
tags = ["automanaged"],
visibility = ["//visibility:private"],
)
filegroup(
name = "all-srcs",
srcs = [":package-srcs"],
tags = ["automanaged"],
visibility = ["//visibility:public"],
)

View File

@@ -0,0 +1,50 @@
package main
import (
"context"
"flag"
"os"
"os/signal"
"syscall"
"time"
"go-common/app/service/live/xroom-feed/internal/server/grpc"
"go-common/app/service/live/xroom-feed/internal/server/http"
"go-common/app/service/live/xroom-feed/internal/service"
"go-common/library/conf/paladin"
ecode "go-common/library/ecode/tip"
"go-common/library/log"
)
func main() {
flag.Parse()
if err := paladin.Init(); err != nil {
panic(err)
}
log.Init(nil) // debug flag: log.dir={path}
defer log.Close()
log.Info("xroom-feed-service start")
ecode.Init(nil)
svc := service.New()
grpcSrv := grpc.New(svc)
httpSrv := http.New(svc)
c := make(chan os.Signal, 1)
signal.Notify(c, syscall.SIGHUP, syscall.SIGQUIT, syscall.SIGTERM, syscall.SIGINT)
for {
s := <-c
log.Info("get a signal %s", s.String())
switch s {
case syscall.SIGQUIT, syscall.SIGTERM, syscall.SIGINT:
ctx, _ := context.WithTimeout(context.Background(), 35*time.Second)
grpcSrv.Shutdown(ctx)
httpSrv.Shutdown(ctx)
log.Info("xroom-feed-service exit")
svc.Close()
time.Sleep(time.Second)
return
case syscall.SIGHUP:
default:
return
}
}
}

View File

@@ -0,0 +1,2 @@
# This is a TOML document. Boom

View File

@@ -0,0 +1,4 @@
[server]
addr = "0.0.0.0:9000"
timeout = "1s"

View File

@@ -0,0 +1,4 @@
[server]
addr = "0.0.0.0:8000"
timeout = "1s"

View File

@@ -0,0 +1,13 @@
demoExpire = "24h"
[demo]
name = "xroom-feed"
proto = "tcp"
addr = "127.0.0.1:11211"
active = 50
idle = 10
dialTimeout = "100ms"
readTimeout = "200ms"
writeTimeout = "300ms"
idleTimeout = "80s"

View File

@@ -0,0 +1,11 @@
[demo]
addr = "127.0.0.1:3306"
dsn = "{user}:{password}@tcp(127.0.0.1:3306)/{database}?timeout=1s&readTimeout=1s&writeTimeout=1s&parseTime=true&loc=Local&charset=utf8mb4,utf8"
readDSN = ["{user}:{password}@tcp(127.0.0.2:3306)/{database}?timeout=1s&readTimeout=1s&writeTimeout=1s&parseTime=true&loc=Local&charset=utf8mb4,utf8","{user}:{password}@tcp(127.0.0.3:3306)/{database}?timeout=1s&readTimeout=1s&writeTimeout=1s&parseTime=true&loc=Local&charset=utf8,utf8mb4"]
active = 20
idle = 10
idleTimeout ="4h"
queryTimeout = "200ms"
execTimeout = "300ms"
tranTimeout = "400ms"

View File

@@ -0,0 +1,13 @@
demoExpire = "24h"
[rec]
Name = "xroom-feed"
Proto = "tcp"
Addr = "redis-host:10010"
Idle = 10
Active = 10
DialTimeout = "1s"
ReadTimeout = "1s"
WriteTimeout = "1s"
IdleTimeout = "10s"

View File

@@ -0,0 +1,40 @@
package(default_visibility = ["//visibility:public"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_library",
)
go_library(
name = "go_default_library",
srcs = [
"dao.go",
"recdata.go",
],
importpath = "go-common/app/service/live/xroom-feed/internal/dao",
tags = ["automanaged"],
visibility = ["//visibility:public"],
deps = [
"//app/service/live/xroom-feed/internal/model:go_default_library",
"//library/cache/memcache:go_default_library",
"//library/cache/redis:go_default_library",
"//library/conf/paladin:go_default_library",
"//library/database/sql:go_default_library",
"//library/log:go_default_library",
"//library/time:go_default_library",
],
)
filegroup(
name = "package-srcs",
srcs = glob(["**"]),
tags = ["automanaged"],
visibility = ["//visibility:private"],
)
filegroup(
name = "all-srcs",
srcs = [":package-srcs"],
tags = ["automanaged"],
visibility = ["//visibility:public"],
)

View File

@@ -0,0 +1,96 @@
package dao
import (
"context"
"time"
"go-common/library/cache/memcache"
"go-common/library/cache/redis"
"go-common/library/conf/paladin"
"go-common/library/database/sql"
"go-common/library/log"
xtime "go-common/library/time"
)
// Dao dao.
type Dao struct {
db *sql.DB
redis *redis.Pool
redisExpire int32
mc *memcache.Pool
mcExpire int32
}
func checkErr(err error) {
if err != nil {
panic(err)
}
}
// New new a dao and return.
func New() (dao *Dao) {
var (
// dc struct {
// Demo *sql.Config
// }
rc struct {
Rec *redis.Config
RecExpire xtime.Duration
}
// mc struct {
// Demo *memcache.Config
// DemoExpire xtime.Duration
// }
)
// checkErr(paladin.Get("mysql.toml").UnmarshalTOML(&dc))
checkErr(paladin.Get("redis.toml").UnmarshalTOML(&rc))
// checkErr(paladin.Get("memcache.toml").UnmarshalTOML(&mc))
dao = &Dao{
// mysql
// db: sql.NewMySQL(dc.Demo),
// redis
redis: redis.NewPool(rc.Rec),
redisExpire: int32(time.Duration(rc.RecExpire) / time.Second),
// memcache
// mc: memcache.NewPool(mc.Demo),
// mcExpire: int32(time.Duration(mc.DemoExpire) / time.Second),
}
return
}
// Close close the resource.
func (d *Dao) Close() {
// d.mc.Close()
d.redis.Close()
// d.db.Close()
}
// Ping ping the resource.
func (d *Dao) Ping(ctx context.Context) (err error) {
// if err = d.pingMC(ctx); err != nil {
// return
// }
if err = d.pingRedis(ctx); err != nil {
return err
}
return nil
// return d.db.Ping(ctx)
}
// func (d *Dao) pingMC(ctx context.Context) (err error) {
// conn := d.mc.Get(ctx)
// defer conn.Close()
// if err = conn.Set(&memcache.Item{Key: "ping", Value: []byte("pong"), Expiration: 0}); err != nil {
// log.Error("conn.Set(PING) error(%v)", err)
// }
// return
// }
func (d *Dao) pingRedis(ctx context.Context) (err error) {
conn := d.redis.Get(ctx)
defer conn.Close()
if _, err = conn.Do("SET", "ping", "pong"); err != nil {
log.Error("conn.Set(PING) error(%v)", err)
}
return
}

View File

@@ -0,0 +1,120 @@
package dao
import (
"context"
"fmt"
"strconv"
"strings"
"go-common/app/service/live/xroom-feed/internal/model"
"go-common/library/cache/redis"
"go-common/library/log"
)
var (
recConfKey = "rec_conf"
recPoolKey = "rec_pool_%d"
recRoomInfo = "rec_info_%d"
)
//GetCacheData 获取缓存数据
func (d *Dao) GetCacheData() (data []byte) {
conn := d.redis.Get(context.Background())
defer conn.Close()
var err error
if data, err = redis.Bytes(conn.Do("GET", recConfKey)); err != nil {
log.Error("[recdata cache] GetCacheData err:%+v", err)
return make([]byte, 0)
}
return data
}
//GetRecInfoByRoomid 批量通过Roomid获取rec_info
func (d *Dao) GetRecInfoByRoomid(ctx context.Context, roomids []int64) map[int64]*model.RecRoomInfo {
conn := d.redis.Get(ctx)
defer conn.Close()
redisSuccess := make([]int64, 0, len(roomids))
for _, roomid := range roomids {
key := fmt.Sprintf(recRoomInfo, roomid)
if err := conn.Send("HGETALL", key); err != nil {
log.Error("[recdata] GetRecInfoByRoomid redis send roomid:%d err:%v", roomid, err)
continue
}
redisSuccess = append(redisSuccess, roomid)
}
conn.Flush()
result := make(map[int64]*model.RecRoomInfo)
for _, roomid := range redisSuccess {
var roominfo map[string]string
var err error
if roominfo, err = redis.StringMap(conn.Receive()); err != nil {
log.Error("[recdata] GetRecInfoByRoomid redis receive err:%d err:%+v", roomid, err)
continue
}
if len(roominfo) <= 0 {
log.Error("[recdata] GetRecInfoByRoomid redis receive empty:%d err:+%v", roomid, roominfo)
continue
}
result[roomid] = model.NewRecRoomInfo()
result[roomid].Title = roominfo["title"]
result[roomid].Uid, _ = strconv.ParseInt(roominfo["uid"], 10, 64)
result[roomid].PopularityCount, _ = strconv.ParseInt(roominfo["popularity_count"], 10, 64)
result[roomid].KeyFrame = roominfo["Keyframe"]
result[roomid].Cover = roominfo["cover"]
result[roomid].ParentAreaID, _ = strconv.ParseInt(roominfo["parent_area_id"], 10, 64)
result[roomid].ParentAreaName = roominfo["parent_area_name"]
result[roomid].AreaID, _ = strconv.ParseInt(roominfo["area_id"], 10, 64)
result[roomid].AreaName = roominfo["area_name"]
}
return result
}
//GetRecPoolByID 通过id获取推荐池
func (d *Dao) GetRecPoolByID(ctx context.Context, rids []int64) map[int64][]int64 {
conn := d.redis.Get(ctx)
defer conn.Close()
var (
room string
err error
)
redisSuccess := make([]int64, 0, len(rids))
for _, rid := range rids {
key := fmt.Sprintf(recPoolKey, rid)
if err := conn.Send("GET", key); err != nil {
log.Error("[recdata] GetRecPoolByID send id %d err:%+v", rid, err)
continue
}
redisSuccess = append(redisSuccess, rid)
}
conn.Flush()
result := make(map[int64][]int64)
for _, rid := range redisSuccess {
if room, err = redis.String(conn.Receive()); err != nil {
log.Error("[recdata] GetRecPoolByID receive id %d err:%+v", rid, err)
continue
}
if room == "" {
log.Info("[recdata] GetRecPoolByID receive empty room list rid: %d", rid)
continue
}
rooms := strings.Split(room, ",")
roomIDs := make([]int64, len(rooms), len(rooms))
for i, roomid := range rooms {
roomIDs[i], _ = strconv.ParseInt(roomid, 10, 64)
}
result[rid] = roomIDs
}
return result
}

View File

@@ -0,0 +1,28 @@
package(default_visibility = ["//visibility:public"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_library",
)
go_library(
name = "go_default_library",
srcs = ["model.go"],
importpath = "go-common/app/service/live/xroom-feed/internal/model",
tags = ["automanaged"],
visibility = ["//visibility:public"],
)
filegroup(
name = "package-srcs",
srcs = glob(["**"]),
tags = ["automanaged"],
visibility = ["//visibility:private"],
)
filegroup(
name = "all-srcs",
srcs = [":package-srcs"],
tags = ["automanaged"],
visibility = ["//visibility:public"],
)

View File

@@ -0,0 +1,55 @@
package model
//RecPoolConf 投放配置
type RecPoolConf struct {
ID int64 `json:"id"`
Name string `json:"name"`
Type int64 `json:"type"`
Rule string `json:"rules"`
Priority int64 `json:"priority"`
Percent float64 `json:"percent"`
TruePercent float64 `json:"true_percent"`
ModuleType int64 `json:"module_type"`
Position int64 `json:"position"`
}
//RecRoomInfo 房间信息
type RecRoomInfo struct {
Uid int64 `json:"uid"`
Title string `json:"title"`
PopularityCount int64 `json:"popularity_count"`
KeyFrame string `josn:"Keyframe"`
Cover string `josn:"cover"`
ParentAreaID int64 `json:"parent_area_id"`
ParentAreaName string `json:"parent_area_name"`
AreaID int64 `json:"area_id"`
AreaName string `josn:"area_name"`
}
//NewRecPoolConf 创建
func NewRecPoolConf() *RecPoolConf {
return &RecPoolConf{}
}
//NewRecRoomInfo 创建
func NewRecRoomInfo() *RecRoomInfo {
return &RecRoomInfo{}
}
//RecPoolSlice 配置
type RecPoolSlice []*RecPoolConf
//Len 返回长度
func (R RecPoolSlice) Len() int {
return len(R)
}
//Less 根据优先级降序排序
func (R RecPoolSlice) Less(i, j int) bool {
return R[i].Priority > R[j].Priority
}
//Swap 交换数据
func (R RecPoolSlice) Swap(i, j int) {
R[i], R[j] = R[j], R[i]
}

View File

@@ -0,0 +1,34 @@
package(default_visibility = ["//visibility:public"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_library",
)
go_library(
name = "go_default_library",
srcs = ["server.go"],
importpath = "go-common/app/service/live/xroom-feed/internal/server/grpc",
tags = ["automanaged"],
visibility = ["//visibility:public"],
deps = [
"//app/service/live/xroom-feed/api:go_default_library",
"//app/service/live/xroom-feed/internal/service:go_default_library",
"//library/conf/paladin:go_default_library",
"//library/net/rpc/warden:go_default_library",
],
)
filegroup(
name = "package-srcs",
srcs = glob(["**"]),
tags = ["automanaged"],
visibility = ["//visibility:private"],
)
filegroup(
name = "all-srcs",
srcs = [":package-srcs"],
tags = ["automanaged"],
visibility = ["//visibility:public"],
)

View File

@@ -0,0 +1,27 @@
package grpc
import (
pb "go-common/app/service/live/xroom-feed/api"
"go-common/app/service/live/xroom-feed/internal/service"
"go-common/library/conf/paladin"
"go-common/library/net/rpc/warden"
)
// New new a grpc server.
func New(svc *service.Service) *warden.Server {
var rc struct {
Server *warden.ServerConfig
}
if err := paladin.Get("grpc.toml").UnmarshalTOML(&rc); err != nil {
if err != paladin.ErrNotExist {
panic(err)
}
}
ws := warden.NewServer(rc.Server)
pb.RegisterRecPoolServer(ws.Server(), svc)
ws, err := ws.Start()
if err != nil {
panic(err)
}
return ws
}

View File

@@ -0,0 +1,35 @@
package(default_visibility = ["//visibility:public"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_library",
)
go_library(
name = "go_default_library",
srcs = ["http.go"],
importpath = "go-common/app/service/live/xroom-feed/internal/server/http",
tags = ["automanaged"],
visibility = ["//visibility:public"],
deps = [
"//app/service/live/xroom-feed/internal/service:go_default_library",
"//library/conf/paladin:go_default_library",
"//library/log:go_default_library",
"//library/net/http/blademaster:go_default_library",
"//library/net/http/blademaster/middleware/verify:go_default_library",
],
)
filegroup(
name = "package-srcs",
srcs = glob(["**"]),
tags = ["automanaged"],
visibility = ["//visibility:private"],
)
filegroup(
name = "all-srcs",
srcs = [":package-srcs"],
tags = ["automanaged"],
visibility = ["//visibility:public"],
)

View File

@@ -0,0 +1,61 @@
package http
import (
"net/http"
"go-common/app/service/live/xroom-feed/internal/service"
"go-common/library/conf/paladin"
"go-common/library/log"
bm "go-common/library/net/http/blademaster"
"go-common/library/net/http/blademaster/middleware/verify"
)
var (
svc *service.Service
)
// New new a bm server.
func New(s *service.Service) (engine *bm.Engine) {
var (
hc struct {
Server *bm.ServerConfig
}
)
if err := paladin.Get("http.toml").UnmarshalTOML(&hc); err != nil {
if err != paladin.ErrNotExist {
panic(err)
}
}
svc = s
engine = bm.DefaultServer(hc.Server)
initRouter(engine, verify.New(nil))
if err := engine.Start(); err != nil {
panic(err)
}
return
}
func initRouter(e *bm.Engine, v *verify.Verify) {
e.Ping(ping)
e.Register(register)
g := e.Group("/x/xroom-feed")
{
g.GET("/start", v.Verify, howToStart)
}
}
func ping(ctx *bm.Context) {
if err := svc.Ping(ctx); err != nil {
log.Error("ping error(%v)", err)
ctx.AbortWithStatus(http.StatusServiceUnavailable)
}
}
func register(c *bm.Context) {
c.JSON(map[string]interface{}{}, nil)
}
// example for http request handler.
func howToStart(c *bm.Context) {
c.String(0, "Golang 大法好 !!!")
}

View File

@@ -0,0 +1,40 @@
package(default_visibility = ["//visibility:public"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_library",
)
go_library(
name = "go_default_library",
srcs = [
"pool_conf.go",
"rec_pool.go",
"service.go",
],
importpath = "go-common/app/service/live/xroom-feed/internal/service",
tags = ["automanaged"],
visibility = ["//visibility:public"],
deps = [
"//app/service/live/xroom-feed/api:go_default_library",
"//app/service/live/xroom-feed/internal/dao:go_default_library",
"//app/service/live/xroom-feed/internal/model:go_default_library",
"//library/conf/paladin:go_default_library",
"//library/log:go_default_library",
"//library/xstr:go_default_library",
],
)
filegroup(
name = "package-srcs",
srcs = glob(["**"]),
tags = ["automanaged"],
visibility = ["//visibility:private"],
)
filegroup(
name = "all-srcs",
srcs = [":package-srcs"],
tags = ["automanaged"],
visibility = ["//visibility:public"],
)

View File

@@ -0,0 +1,117 @@
package service
import (
"context"
"encoding/json"
"sort"
"strconv"
"time"
"go-common/app/service/live/xroom-feed/internal/model"
"go-common/library/log"
)
// 保底数据源标识
const _defaultSourceName = "__default_source__"
func (s *Service) getPoolConf() (tidyRs map[string][]*model.RecPoolConf) {
data := s.dao.GetCacheData()
tidyRs = make(map[string][]*model.RecPoolConf)
rs := make([]*model.RecPoolConf, 0)
if err := json.Unmarshal(data, &rs); err != nil {
log.Error("[getPoolConf] loadDataTOcache json unmarshal err: %+v; rs: %+v;", err, rs)
return
}
if rs == nil {
log.Error("[getPoolConf] rs err: %+v; ", rs)
return
}
for _, recConf := range rs {
recPoolDataMap := s.dao.GetRecPoolByID(context.Background(), []int64{recConf.ID})
if _, ok := recPoolDataMap[recConf.ID]; !ok {
continue
}
if len(recPoolDataMap[recConf.ID]) == 0 {
continue
}
key := strconv.FormatInt(recConf.ModuleType, 10) + "_" +
strconv.FormatInt(recConf.Position, 10)
tidyRs[key] = append(tidyRs[key], recConf)
}
for k, recConfSlice := range tidyRs {
sort.Sort(model.RecPoolSlice(recConfSlice))
//加入保底数据源
tidyRs[k] = append(tidyRs[k], &model.RecPoolConf{
ID: 0,
Name: _defaultSourceName,
Type: 0,
Rule: "",
Priority: 1,
Percent: 0,
TruePercent: 0,
ModuleType: 0,
Position: 0,
})
var rest float64
rest = 100
for i, recConf := range tidyRs[k] {
if rest < 0 {
log.Error("[getPoolConf] true percent cal err: %+v", tidyRs[k])
recConf.TruePercent = recConf.Percent
continue
}
if i == 0 {
recConf.TruePercent = recConf.Percent
rest = rest - recConf.Percent
continue
}
if recConf.Name == _defaultSourceName {
//保底逻辑
recConf.TruePercent = rest
break
}
truePercent := rest * recConf.Percent / 100
recConf.TruePercent = truePercent
rest = rest - truePercent
}
}
return
}
// GetPoolConfFromMem get pool conf from mem...
func (s *Service) GetPoolConfFromMem(key string) (res []*model.RecPoolConf) {
rcl := s.recCache.Load()
rc, ok := rcl.(map[string][]*model.RecPoolConf)
var raw map[string][]*model.RecPoolConf
if !ok {
raw = s.getPoolConf()
log.Error("[GetPoolConfFromMem] mem cache miss, data assert err, rcl:%+v, raw: %+v", rcl, raw)
} else {
raw = rc
}
res, ok = raw[key]
if !ok {
log.Error("[GetPoolConfFromMem] key not exist, key: %+v", key)
return
}
return
}
func (s *Service) poolConfProc() {
for {
time.Sleep(time.Second * 5)
s.loadPoolConf()
}
}
func (s *Service) loadPoolConf() {
recPoolCache := s.getPoolConf()
if len(recPoolCache) == 0 {
log.Info("[loadPoolConf] getPoolConf empty")
}
s.recCache.Store(recPoolCache)
}

View File

@@ -0,0 +1,180 @@
package service
import (
"context"
"fmt"
"math/rand"
"time"
"go-common/library/log"
"go-common/library/xstr"
pb "go-common/app/service/live/xroom-feed/api"
)
// GetList implementation
// 根据模块位置获取投放列表 position=>RoomItem
func (s *Service) GetList(ctx context.Context, req *pb.RecPoolReq) (resp *pb.RecPoolResp, err error) {
resp = &pb.RecPoolResp{}
//1.得到module positions_num
module := req.ModuleType
//6..
positionNum := req.PositionNum
//24..
moduleExistRooms, err := xstr.SplitInts(req.ModuleExistRooms)
moduleExistRoomsMap := make(map[int64]int64)
if err != nil {
log.Error("[recPool.GetList]moduleExistRooms SplitInts err:%+v", err)
} else if len(moduleExistRooms) > 0 {
for index, iRoomID := range moduleExistRooms {
moduleExistRoomsMap[iRoomID] = int64(index) + 1
}
}
otherExistRooms, err := xstr.SplitInts(req.OtherExistRooms)
otherExistRoomsMap := make(map[int64]int64)
if err != nil {
log.Error("[recPool.GetList]otherExistRooms SplitInts err:%+v", err)
} else if len(moduleExistRooms) > 0 {
for index, iRoomID := range otherExistRooms {
otherExistRoomsMap[iRoomID] = int64(index) + 1
}
}
tidyRoomIDs := make([]int64, 0)
tidyRoomIDsMap := make(map[int64]int64)
tidyPoolIDsMap := make(map[int64]int64)
existPosRoomIdMap := make(map[int64]bool)
existCount := int64(0)
var pos int64
for pos = 1; pos <= positionNum; pos++ {
modulePosition := fmt.Sprintf("%d_%d", module, pos)
awardPoolIdsMap := make(map[int64]float64)
//2.module_position map[string][] from 内存
recPoolConfs := s.GetPoolConfFromMem(modulePosition)
if len(recPoolConfs) == 0 {
continue
}
//3.抽奖候选集数据非空校验、true_percent在routine
for _, recPoolConf := range recPoolConfs {
if recPoolConf == nil {
log.Warn("[recPool.GetList]recPoolConf empty, pos:%+v", modulePosition)
continue
}
if recPoolConf.TruePercent <= 0 {
log.Warn("[recPool.GetList]recPoolConf TruePercent abnormal, recPoolConf:%+v", recPoolConf)
continue
}
awardPoolIdsMap[recPoolConf.ID] = recPoolConf.TruePercent
}
if len(awardPoolIdsMap) <= 0 {
continue
}
//4.开始抽奖
log.Info("[recPool.GetList]awardPoolIdsMap result, module:%d; res:%+v; from:%+v", module, awardPoolIdsMap, req.From)
resPoolId := getAwardSource(awardPoolIdsMap)
if resPoolId <= 0 {
continue
}
tidyPoolIDsMap[pos] = resPoolId
//5.去重
resPoolDataMap := s.dao.GetRecPoolByID(ctx, []int64{resPoolId})
resPoolData, ok := resPoolDataMap[resPoolId]
if !ok {
continue
}
tidyPoolRoomIDs := make([]int64, 0)
for _, iRoomID := range resPoolData {
if ePos, ok := moduleExistRoomsMap[iRoomID]; ok && (ePos+existCount < pos) {
//原位置已存在&&位置比投放的位置好
continue
}
if _, ok := existPosRoomIdMap[iRoomID]; ok {
//同模块前面的位置已经吐过了
continue
}
if _, ok := otherExistRoomsMap[iRoomID]; ok {
//其它模块已经吐过了
continue
}
tidyPoolRoomIDs = append(tidyPoolRoomIDs, iRoomID)
}
//6.随机取一个
tidyRoomIDsLen := len(tidyPoolRoomIDs)
if tidyRoomIDsLen <= 0 {
continue
}
rand.Seed(time.Now().UnixNano())
tidyRoomID := tidyPoolRoomIDs[rand.Intn(tidyRoomIDsLen)]
tidyRoomIDs = append(tidyRoomIDs, tidyRoomID)
tidyRoomIDsMap[pos] = tidyRoomID
existPosRoomIdMap[tidyRoomID] = true
existCount++
}
//7.批量获取rec信息并返回
if len(tidyRoomIDs) <= 0 {
return
}
log.Info("[recPool.GetList]tidyRoomIDsMap result, module:%d; res:%+v; from:%+v", module, tidyRoomIDsMap, req.From)
resp.List = make(map[int64]*pb.RoomItem)
tidyRoomInfoMap := s.dao.GetRecInfoByRoomid(ctx, tidyRoomIDs)
for pos, iRoomID := range tidyRoomIDsMap {
rInfo, ok := tidyRoomInfoMap[iRoomID]
if !ok {
log.Warn("[recPool.GetList]recInfo empty:%+v from:%+v", tidyRoomInfoMap, req.From)
continue
}
recType := int64(10000)
if poolConfId, ok := tidyPoolIDsMap[pos]; ok {
recType += poolConfId
}
resp.List[pos] = &pb.RoomItem{
RoomId: iRoomID,
Uid: rInfo.Uid,
Title: rInfo.Title,
PopularityCount: rInfo.PopularityCount,
Keyframe: rInfo.KeyFrame,
Cover: rInfo.Cover,
AreaId: rInfo.AreaID,
AreaName: rInfo.AreaName,
ParentAreaId: rInfo.ParentAreaID,
ParentAreaName: rInfo.ParentAreaName,
RecType: recType,
}
}
return
}
// awardMap id=>百分比/权重
func getAwardSource(awardMap map[int64]float64) (tidyID int64) {
type awarSource struct {
index int64
offset float64
weight float64
}
awardSli := make([]*awarSource, 0, len(awardMap))
var sumCount float64
for n, c := range awardMap {
//精度 支持小数点后两位
c *= 100
a := awarSource{
index: n,
offset: sumCount,
weight: c,
}
awardSli = append(awardSli, &a)
sumCount += c
}
awardIndex := rand.Int63n(int64(sumCount))
for _, u := range awardSli {
if u.offset+u.weight > float64(awardIndex) {
tidyID = u.index
return
}
}
return
}

View File

@@ -0,0 +1,41 @@
package service
import (
"context"
"sync/atomic"
"go-common/app/service/live/xroom-feed/internal/dao"
"go-common/library/conf/paladin"
)
// Service service.
type Service struct {
ac *paladin.Map
dao *dao.Dao
recCache atomic.Value
}
// New new a service and return.
func New() (s *Service) {
var ac = new(paladin.TOML)
if err := paladin.Watch("application.toml", ac); err != nil {
panic(err)
}
s = &Service{
ac: ac,
dao: dao.New(),
}
s.loadPoolConf()
go s.poolConfProc()
return s
}
// Ping ping the resource.
func (s *Service) Ping(ctx context.Context) (err error) {
return s.dao.Ping(ctx)
}
// Close close the resource.
func (s *Service) Close() {
s.dao.Close()
}