mirror of
https://github.com/zhengkai/orca.git
synced 2026-02-04 14:42:26 +08:00
up
This commit is contained in:
@@ -2,8 +2,9 @@ package config
|
||||
|
||||
// config
|
||||
var (
|
||||
Prod bool
|
||||
Dir string
|
||||
Prod bool
|
||||
Dir string
|
||||
LogDir string
|
||||
|
||||
StaticDir = `/www/orca/static`
|
||||
|
||||
|
||||
@@ -8,11 +8,13 @@ import (
|
||||
func init() {
|
||||
|
||||
Dir, _ = filepath.Abs(filepath.Dir(os.Args[0]))
|
||||
LogDir = Dir + `/log`
|
||||
|
||||
list := map[string]*string{
|
||||
`OPENAI_API_KEY`: &OpenAIKey,
|
||||
`STATIC_DIR`: &StaticDir,
|
||||
`WEB_ADDR`: &WebAddr,
|
||||
`ORCA_WEB`: &WebAddr,
|
||||
`ORCA_LOG`: &LogDir,
|
||||
}
|
||||
for k, v := range list {
|
||||
s := os.Getenv(k)
|
||||
|
||||
25
server/src/core/cache.go
Normal file
25
server/src/core/cache.go
Normal file
@@ -0,0 +1,25 @@
|
||||
package core
|
||||
|
||||
import (
|
||||
"project/pb"
|
||||
"project/util"
|
||||
)
|
||||
|
||||
func tryCache(p *pb.Req) ([]byte, bool) {
|
||||
|
||||
file := rspCacheFile(p)
|
||||
if !util.FileExists(file) {
|
||||
return nil, false
|
||||
}
|
||||
|
||||
ab, err := util.ReadFile(file)
|
||||
if err != nil {
|
||||
return nil, false
|
||||
}
|
||||
|
||||
return ab, true
|
||||
}
|
||||
|
||||
func rspCacheFile(r *pb.Req) string {
|
||||
return util.CacheName(r.Hash()) + `-rsp.json`
|
||||
}
|
||||
@@ -36,7 +36,11 @@ func fetchRemote(r *pb.Req) (ab []byte, err error) {
|
||||
return
|
||||
}
|
||||
|
||||
defer rsp.Body.Close()
|
||||
for k, v := range rsp.Header {
|
||||
zj.J(k, v)
|
||||
}
|
||||
|
||||
return io.ReadAll(rsp.Body)
|
||||
ab, err = io.ReadAll(rsp.Body)
|
||||
rsp.Body.Close()
|
||||
return
|
||||
}
|
||||
32
server/src/core/metrics.go
Normal file
32
server/src/core/metrics.go
Normal file
@@ -0,0 +1,32 @@
|
||||
package core
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
"project/metrics"
|
||||
"project/pb"
|
||||
"project/util"
|
||||
)
|
||||
|
||||
func doMetrics(ab []byte, cached bool, r *http.Request) {
|
||||
|
||||
metrics.RspBytes(len(ab))
|
||||
|
||||
o := &pb.Rsp{}
|
||||
json.Unmarshal(ab, o)
|
||||
|
||||
u := o.GetUsage()
|
||||
if u == nil {
|
||||
metrics.RspJSONFail()
|
||||
return
|
||||
}
|
||||
|
||||
metrics.RspToken(u.PromptTokens, u.TotalTokens, cached)
|
||||
|
||||
ip, err := util.GetIP(r)
|
||||
sip := ip.String()
|
||||
if err != nil {
|
||||
sip = `unknown`
|
||||
}
|
||||
metrics.RspTokenByIP(sip, u.TotalTokens)
|
||||
}
|
||||
@@ -8,12 +8,38 @@ import (
|
||||
"project/zj"
|
||||
)
|
||||
|
||||
func (c *Core) getAB(p *pb.Req, r *http.Request) (ab []byte, cached bool, err error) {
|
||||
ab, ok := tryCache(p)
|
||||
if ok {
|
||||
cached = true
|
||||
return
|
||||
}
|
||||
|
||||
pr := c.add(p, r)
|
||||
|
||||
go func() {
|
||||
reqFile := util.CacheName(p.Hash()) + `-req.json`
|
||||
if !util.FileExists(reqFile) {
|
||||
util.WriteFile(reqFile, p.Body)
|
||||
}
|
||||
}()
|
||||
|
||||
pr.wait()
|
||||
|
||||
ab = pr.rsp
|
||||
err = pr.err
|
||||
return
|
||||
}
|
||||
|
||||
func req(w http.ResponseWriter, r *http.Request) (p *pb.Req, err error) {
|
||||
|
||||
path := r.URL.Path
|
||||
method := r.Method
|
||||
|
||||
zj.J(method, path)
|
||||
if path == `/favicon.ico` {
|
||||
err = errSkip
|
||||
return
|
||||
}
|
||||
|
||||
ab, err := io.ReadAll(http.MaxBytesReader(w, r.Body, 1024*1024))
|
||||
if err != nil {
|
||||
|
||||
@@ -29,6 +29,7 @@ func (pr *row) run() {
|
||||
zj.J(`new`, s)
|
||||
|
||||
pr.rsp, pr.err = fetchRemote(pr.req)
|
||||
|
||||
go pr.saveFile()
|
||||
go pr.metrics()
|
||||
|
||||
@@ -44,11 +45,8 @@ func (pr *row) wait() {
|
||||
}
|
||||
|
||||
func (pr *row) saveFile() {
|
||||
rspFile := util.CacheName(pr.req.Hash()) + `-rsp.json`
|
||||
if !util.FileExists(rspFile) {
|
||||
util.WriteFile(rspFile, pr.rsp)
|
||||
zj.J(rspFile)
|
||||
}
|
||||
rspFile := rspCacheFile(pr.req)
|
||||
util.WriteFile(rspFile, pr.rsp)
|
||||
}
|
||||
|
||||
func (pr *row) metrics() {
|
||||
|
||||
@@ -1,36 +1,37 @@
|
||||
package core
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"net/http"
|
||||
"project/metrics"
|
||||
"project/util"
|
||||
"project/zj"
|
||||
)
|
||||
|
||||
var errSkip = errors.New(`skip`)
|
||||
|
||||
// WebHandle ...
|
||||
func (c *Core) WebHandle(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
p, err := req(w, r)
|
||||
if err != nil {
|
||||
metrics.ReqFailCount()
|
||||
return
|
||||
}
|
||||
metrics.ReqBytes(len(p.Body))
|
||||
|
||||
pr := c.add(p, r)
|
||||
|
||||
go func() {
|
||||
reqFile := util.CacheName(p.Hash()) + `-req.json`
|
||||
if !util.FileExists(reqFile) {
|
||||
util.WriteFile(reqFile, p.Body)
|
||||
if err != errSkip {
|
||||
metrics.ReqFailCount()
|
||||
}
|
||||
}()
|
||||
|
||||
pr.wait()
|
||||
|
||||
if pr.err != nil {
|
||||
err500(w)
|
||||
return
|
||||
}
|
||||
|
||||
w.Write(pr.rsp)
|
||||
metrics.ReqBytes(len(p.Body))
|
||||
|
||||
ab, cached, err := c.getAB(p, r)
|
||||
if err != nil {
|
||||
err500(w)
|
||||
return
|
||||
}
|
||||
zj.J(`cached`, cached)
|
||||
|
||||
w.Header().Add(`Content-Type`, `application/json`)
|
||||
w.Write(ab)
|
||||
|
||||
go doMetrics(ab, cached, r)
|
||||
}
|
||||
|
||||
@@ -7,4 +7,11 @@ func init() {
|
||||
prometheus.MustRegister(reqFailCount)
|
||||
prometheus.MustRegister(reqBytes)
|
||||
prometheus.MustRegister(errorCount)
|
||||
|
||||
prometheus.MustRegister(rspBytes)
|
||||
prometheus.MustRegister(rspPromptTokenCount)
|
||||
prometheus.MustRegister(rspTokenCount)
|
||||
prometheus.MustRegister(rspTokenCachedCount)
|
||||
prometheus.MustRegister(rspJSONFailCount)
|
||||
prometheus.MustRegister(rspTokenByIP)
|
||||
}
|
||||
|
||||
45
server/src/metrics/rsp.go
Normal file
45
server/src/metrics/rsp.go
Normal file
@@ -0,0 +1,45 @@
|
||||
package metrics
|
||||
|
||||
import (
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
)
|
||||
|
||||
var (
|
||||
rspBytes = newCounter(`orca_rsp_bytes`, `rsp bytes`)
|
||||
rspPromptTokenCount = newCounter(`orca_rsp_prompt_token`, `prompt token`)
|
||||
rspTokenCount = newCounter(`orca_rsp_token`, `token`)
|
||||
rspTokenCachedCount = newCounter(`orca_rsp_token_cached`, `token cached`)
|
||||
rspJSONFailCount = newCounter(`orca_rsp_json_fail`, `json fail`)
|
||||
rspTokenByIP = prometheus.NewCounterVec(
|
||||
prometheus.CounterOpts{
|
||||
Name: `orca_token_by_ip`,
|
||||
Help: `API 返回报错`,
|
||||
},
|
||||
[]string{`ip`},
|
||||
)
|
||||
)
|
||||
|
||||
// RspToken ...
|
||||
func RspToken(prompt, total uint32, cached bool) {
|
||||
if cached {
|
||||
rspTokenCachedCount.Add(float64(total))
|
||||
return
|
||||
}
|
||||
rspPromptTokenCount.Add(float64(prompt))
|
||||
rspTokenCount.Add(float64(total))
|
||||
}
|
||||
|
||||
// RspBytes ...
|
||||
func RspBytes(n int) {
|
||||
rspBytes.Add(float64(n))
|
||||
}
|
||||
|
||||
// RspJSONFail ...
|
||||
func RspJSONFail() {
|
||||
rspJSONFailCount.Inc()
|
||||
}
|
||||
|
||||
// RspTokenByIP ...
|
||||
func RspTokenByIP(ip string, token uint32) {
|
||||
rspTokenByIP.WithLabelValues(ip).Add(float64(token))
|
||||
}
|
||||
@@ -28,18 +28,20 @@ func GetIP(r *http.Request) (net.IP, error) {
|
||||
return nil, errors.New(`Invalid IP address`)
|
||||
}
|
||||
|
||||
// 检查是否是IPv4
|
||||
parsedIPv4 := parsedIP.To4()
|
||||
if parsedIPv4 == nil {
|
||||
return nil, errors.New(`IP address not IPv4`)
|
||||
}
|
||||
/*
|
||||
// 检查是否是IPv4
|
||||
parsedIPv4 := parsedIP.To4()
|
||||
if parsedIPv4 == nil {
|
||||
return nil, errors.New(`IP address not IPv4`)
|
||||
}
|
||||
|
||||
// 检查是否为局域网IP
|
||||
if !parsedIP.IsPrivate() {
|
||||
return nil, errors.New(`Public IP address not allowed`)
|
||||
}
|
||||
// 检查是否为局域网IP
|
||||
if !parsedIP.IsPrivate() {
|
||||
return nil, errors.New(`Public IP address not allowed`)
|
||||
}
|
||||
*/
|
||||
|
||||
return parsedIPv4, nil
|
||||
return parsedIP, nil
|
||||
}
|
||||
|
||||
// IPString ...
|
||||
|
||||
@@ -6,6 +6,8 @@ import (
|
||||
"project/core"
|
||||
"project/zj"
|
||||
"time"
|
||||
|
||||
"github.com/prometheus/client_golang/prometheus/promhttp"
|
||||
)
|
||||
|
||||
// Server ...
|
||||
@@ -13,6 +15,7 @@ func Server() {
|
||||
|
||||
mux := http.NewServeMux()
|
||||
|
||||
mux.Handle(`/_metrics`, promhttp.Handler())
|
||||
mux.HandleFunc(`/`, core.NewCore().WebHandle)
|
||||
|
||||
s := &http.Server{
|
||||
|
||||
@@ -34,15 +34,13 @@ func Init() {
|
||||
|
||||
baseLog.SetDirPrefix(filepath.Dir(zog.GetSourceFileDir()))
|
||||
|
||||
// 生产环境走 docker,不写本地文件
|
||||
if !config.Prod {
|
||||
dir := config.LogDir
|
||||
|
||||
mainFile, _ := zog.NewFile(config.Dir+`/log/default.txt`, false)
|
||||
infoFile, _ := zog.NewFile(config.Dir+`/log/io.txt`, false)
|
||||
errFile, _ := zog.NewFile(config.Dir+`/log/err.txt`, true)
|
||||
mainFile, _ := zog.NewFile(dir+`/default.txt`, false)
|
||||
infoFile, _ := zog.NewFile(dir+`/io.txt`, false)
|
||||
errFile, _ := zog.NewFile(dir+`/err.txt`, true)
|
||||
|
||||
mainCfg.Output = append(mainCfg.Output, mainFile)
|
||||
infoCfg.Output = append(infoCfg.Output, infoFile)
|
||||
errCfg.Output = append(errCfg.Output, mainFile, errFile)
|
||||
}
|
||||
mainCfg.Output = append(mainCfg.Output, mainFile)
|
||||
infoCfg.Output = append(infoCfg.Output, infoFile)
|
||||
errCfg.Output = append(errCfg.Output, mainFile, errFile)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user