trpc/http-gateway
需求:对外提供HTTP的转发功能 特性:
- 支持自定义的Filter和Predicate
- 兼容spring cloud gateway的配置
网关设计
storage predicate {
storage PredicateFactory
storage PathPredicateFactory
PredicateFactory<--PathPredicateFactory
}
storage route {
storage id
storage uri
storage Predicate
storage FilterChain
storage Filter
id-->uri
id-->Predicate
id-->FilterChain
FilterChain-->Filter: filter
}
storage WebHandler{
card Routes
}
storage filter {
storage FilterFactory
storage RemoveFilterFactory
FilterFactory<--RemoveFilterFactory
}
control request
Filter<--FilterFactory:factory
Predicate<--PredicateFactory: factory
request ->WebHandler
WebHandler->route
执行:
- 请求送达到WebHandler中,WebHandler寻找能够处理该请求的Route
- WebHandler找到这个Route,执行过滤器链
名词解释:
- Route: 一个代理的抽象 包含断言和过滤器
- FilterChain: 过滤器执行器
- Filter: 过滤器,处理http请求
- Predicate: 断言器,给定一个请求,判断其是否可以处理
说明:
- Webhandler : 请求处理器,负责这个请求的执行
- FilterFactory: 为过滤器的工厂
- PredicateFactory:为断言器的工程
- route:一个代理的设置
- id: 一个代理的唯一ID
- uri: 最终的uri
- predicate:使用的Http断言器
- filters:设置的过滤器
类图
interface Filter{
PreHandler(request, response)bool
PostHandler(request, response)
}
class RequestFilter{
PreHandler(request, response)bool
PostHandler(request, response)
int order
}
class GlobalFilter{
PreHandler(request, response)bool
PostHandler(request, response)
int order
}
class FilterChain{
FilterChain(Filter filters[])
Filter(request, response)
}
class RequestFilterFactory<T>{
New(string config)RequestFilter
}
interface RequestPredicate{
Test(request)bool
}
class Route{
string id
string url
RequestPredicate predicate
RequestFilter []filters
}
class WebHandler{
Route route[]
WebHandler(string path)
ServerHttp(request, response)
Lookup(request)Route
}
class RequestPredicateFactory<T>{
New(T t)RequestPredicate
}
Filter <.. RequestFilter
Filter<..GlobalFilter
RequestPredicate <-- RequestPredicateFactory
WebHandler<--*Route
Filter<-- RequestFilterFactory
约定: 方法大写为public方法 属性名大写为Public变量
时序图
actor Request as R
control WebHandler as W
control Route as T
control Predicate as P
control FilterChain as C
control Filter as F
R -> W: request xxx.weling.com/test
W -> T: consult
T -> P: do test method
P -->T: return boolean
T -> C: do filter
C -> F: do PreHandler and PostHandler
F -->C: return response
C -->T: complete
T -->W: write response to socket
W --> R: render
使用说明
##程序启动 使用步骤:
- 配置trpc——go.yml 文件
- 配置路由配置
- 启动
启动类
package main
import (
"fmt"
"gateway/handler"
web "git.code.oa.com/mossli/trpcweb"
"git.code.oa.com/trpc-go/trpc-go"
"git.code.oa.com/trpc-go/trpc-go/filter"
"git.code.oa.com/trpc-go/trpc-go/log"
"git.code.oa.com/trpc-go/trpc-go/plugin"
"git.code.oa.com/trpc-go/trpc-go/server"
"net/http"
)
func main(){
// load log config
plugin.Register("custom", log.DefaultLogFactory)
// load trpc config
opts := []server.Option{
server.WithFilter(filter.NoopFilter),
}
server := trpc.NewServer(opts...)
s := web.Server(server)
// for gateway
svi := web.Service(s.Server, "web.mossli.gateway.TestSrv2")
h:=handler.New()
svi.Handle("/",h)
// start stub for test
svi2 := web.Service(s.Server, "web.mossli.gateway.TestSrv3")
svi2.HandleFunc("/",func(w http.ResponseWriter, r *http.Request){
w.WriteHeader(200)
fmt.Fprintf(w, "echo")
})
s.Serve()
}
路由配置
[{
"id": "auth",
"uri": "lb://localhost:8021",
"predicates": [
{
//为自定义断言器工厂的name
"name": "Path",
"args": "/test/*"
}
],
"filters": [
{
//为自定义过滤器工厂的name
"name": "RemoveHeader",
"args": "Auth"
}
]
}]
自定义断言器(Predicate)
断言器是多例的,但是其工厂是单例的 约定:
- 断言器命名以Predicate结尾
- 断言器工厂命名以PredicateFactory结尾
步骤:
- 自定义断言器 实现RequestPredicate接口
- 自定义断言器工厂 实现RequestPredicateFactory
- 推荐自定义一个Config结构体
- 注册断言器工厂
代码如下:
package predicate
import (
"net/http"
"strings"
)
func init(){
RegisterFactory("method", &MethodPredicateFactory{})
}
/**
请求方法Method断言器
*/
type MethodPredicateFactory struct {
}
type MethodRequestPredicate struct{
config MethodConfig
}
type MethodConfig struct {
methods []string
}
func (p *MethodRequestPredicate)Test(r *http.Request)bool{
for _,method:=range p.config.methods{
if method==r.Method{
return true
}
}
return false
}
// methods 以逗号进行分割
func (p *MethodPredicateFactory)New(methods string)RequestPredicate{
m:=strings.Split(methods,",")
// http 中method均是大写
for i,method:=range m{
m[i]=strings.ToUpper(method)
}
return &MethodRequestPredicate{MethodConfig{m}}
}
自定义过滤器工厂
过滤器是多例的,但是其工厂是单例
约定
- 过滤器命名以Filter结尾
- 过滤器工厂命名以FilterFactory结尾
使用步骤:
- 自定义过滤器 实现Filter接口
- 自定义过滤器工厂 实现FilterFactory
- 推荐自定义一个Config结构体
- 注册过滤器工厂
代码如下
package filter
import (
"gateway/utils"
"net/http"
"strings"
)
func init(){
Register("RemoveHeader",&RemoveHeaderFilterFactory{})
}
type RemoveHeaderFilterFactory struct{
}
type RemoveHeaderFilter struct{
config RemoveConfig
order int
}
type RemoveConfig struct {
headers []string
}
func (f *RemoveHeaderFilter) PreHandler(response http.ResponseWriter,request *http.Request)bool{
headers:=request.Header
for name,_:=range headers{
if utils.ContainerString(f.config.headers,name){
headers.Del(name)
}
}
return true
}
func (f *RemoveHeaderFilter) PostHandler(response http.ResponseWriter,request *http.Request){
}
func (f *RemoveHeaderFilter)getOrder()int{
return f.order
}
func (f *RemoveHeaderFilter)SetOrder(order int)Filter{
f.order=order
return f
}
func (f *RemoveHeaderFilterFactory)New(config string)Filter{
return &RemoveHeaderFilter{RemoveConfig{strings.Split(config,",")},0}
}
自定义全局过滤器
有时候需要使用全局过滤器,针对的是所有的代理,全局代理器是单例
约定:
- 全局过滤器以GlobalFilter结尾
使用步骤:
- 自定义过滤器 实现Filter接口
- 注册全局过滤器
package filter
import (
"fmt"
"net/http"
"net/http/httputil"
"net/url"
"trpc-go/log"
)
/**
进行注册
*/
func init(){
RegisterGlobalFilter("proxy",&ProxyFilter{order:1024})
}
type ProxyFilter struct{
order int
}
func (p *ProxyFilter) PreHandler(response http.ResponseWriter,request *http.Request)bool{
defer func(){
if err:=recover();err!=nil{
log.Error("proxy filter has error",err)
}
}()
route:=request.Context().Value("route")
if route!=nil{
fmt.Printf("uri is %s /n",route)
}else {
return false
}
r := route.(string)
uri,err:=url.Parse(r)
if err!=nil{
response.WriteHeader(http.StatusInternalServerError)
return false
}
// not support http and https
if uri.Scheme!="http"||uri.Scheme!="https"{
response.WriteHeader(http.StatusInternalServerError)
return false
}
proxy := httputil.NewSingleHostReverseProxy(uri)
proxy.ServeHTTP(response, request)
return true
}
func (p *ProxyFilter) PostHandler(response http.ResponseWriter,request *http.Request){
fmt.Println("proxy complete")
}
func (p *ProxyFilter) getOrder()int{
return p.order
}
func (p *ProxyFilter) SetOrder(order int)Filter{
p.order=order
return p
}