【Traefik二次开发】服务 Service 开发
Service 定义
https://doc.traefik.io/traefik/routing/services/
The Services are responsible for configuring how to reach the actual services that will eventually handle the incoming requests.
Service 特点
- Service是Traefik流程中最后处理请求的位置(中间件在此之前处理请求)
- Service可以控制请求直接到达源站(中间件做不到)
- Service可以对请求进行修改(中间件也可以)
Traefik 官方自带的 Service
官方自带了三个 Service:
- 流量镜像
- 负载均衡(同时实现了权重)
流量镜像的代码位于:/pkg/server/service/loadbalancer/mirror/mirror.go
负载均衡代码位于:/pkg/server/service/loadbalancer/wrr/wrr.go
上述代码可以作为我们开发 Service 的参考。
Service 开发要点
定义 Service 配置
Traefik 的配置解析,是直接映射(或者说反序列化)struct实现的,所有 Service 的配置都 属于 `dynamic.Service` 这个 struct 。这个 struct 位于:/pkg/config/dynamic/http_config.go
其定义如下:
type Service struct {
LoadBalancer *ServersLoadBalancer `json:"loadBalancer,omitempty" toml:"loadBalancer,omitempty" yaml:"loadBalancer,omitempty" export:"true"`
Weighted *WeightedRoundRobin `json:"weighted,omitempty" toml:"weighted,omitempty" yaml:"weighted,omitempty" label:"-" export:"true"`
Mirroring *Mirroring `json:"mirroring,omitempty" toml:"mirroring,omitempty" yaml:"mirroring,omitempty" label:"-" export:"true"`
}
按照 Traefik 已有的 Service 配置来看,我们自定义的 Service 所使用的配置也应该在 http_config.go 文件中
需要注意的是:
- 自己新定义的配置,需要在 `dynamic.Service` 中新增一条属性。否则 配置不会被加载。
- 定义的配置需要其他模块读取,应注意首字母大写,以保证可访问性
- 需要正确填写tag,包括 json、yaml和toml三种序列化格式的名称,否则可能无法正确加载
- 可以省略的参数,其定义应设置为指针类型,否则即是配置的必选项
举例,定义 白名单Service:
在 http_config.go 中新增:
type WhiteList struct {
IPList []string `json:"ipList,omitempty" toml:"ipList,omitempty" yaml:"ipList,omitempty"`
Service string `json:"service,omitempty" toml:"service,omitempty" yaml:"service,omitempty" export:"true"`
MaxBodySize *int64 `json:"maxBodySize,omitempty" toml:"maxBodySize,omitempty" yaml:"maxBodySize,omitempty" export:"true"`
}
在 http_config.go 的 `dynamic.Service` 这个 struct 中添加 WhiteList:
type Service struct {
LoadBalancer *ServersLoadBalancer `json:"loadBalancer,omitempty" toml:"loadBalancer,omitempty" yaml:"loadBalancer,omitempty" export:"true"`
Weighted *WeightedRoundRobin `json:"weighted,omitempty" toml:"weighted,omitempty" yaml:"weighted,omitempty" label:"-" export:"true"`
Mirroring *Mirroring `json:"mirroring,omitempty" toml:"mirroring,omitempty" yaml:"mirroring,omitempty" label:"-" export:"true"`
WhiteList *WhiteList `json:"whiteList,omitempty" toml:"whiteList,omitempty" yaml:"whiteList,omitempty" label:"-" export:"true"`
}
这个新增的 WhiteList 对应的配置(其中的maxBodySize可以不填):
http:
services:
my-whitelist:
whiteList:
iplist:
- "127.0.0.1"
- "192.168.0.0/24"
maxBodySize: 2000
service: example
# Define how to reach an existing service on our infrastructure
example:
loadBalancer:
servers:
- url: "http://xxx.xxx.xxx.xxx:8888/"
定义 Service 的 Handler
接下来,需要定义 Service 的功能代码。按照 Traefik 已有的 Service 来看,其 Service 应定义在 /pkg/server/service/ 中。每个 Service 单独作为一个包存在。
欲新增 Service 则需要在 /pkg/server/service/ 下新建一个文件夹,并在其中新建文件。还是以 白名单 为例,结构如下(省略的其他无关部分):
├── pkg
│ └── server
│ └── service
│ └── whitelist
│ └── whitelist.go
创建好文件后,在其中添加代码,至少需要:
- 规范包名
- 包内有一个 New 函数,作为 Handler 的初始化函数。(当然可以使用其他名字,但是我们应该按照 Traefik 的规范来)
- New 函数返回一个实现了 http.Handler 接口的对象。
大致如下(省略了所有功能,只保留代码结构):
package whitelist
import (
"net"
"net/http"
"github.com/traefik/traefik/v2/pkg/config/dynamic"
)
// WhiteList is an http.Handler 用于实现白名单功能.
type WhiteList struct {
......
}
// New returns a new instance of *WhiteList.
func New(config *dynamic.WhiteList) *WhiteList {
return &WhiteList{}
}
func (w *WhiteList) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
w.handler.ServeHTTP(rw, req)
}
注:实现 http.Handler 接口,只需实现 func ServeHTTP(http.ResponseWriter, *http.Request) 即可
编写 Service 初始化代码
有了配置和代码实现,接下来就是在 Service 的初始化代码中,添加我们新增的 Service了。
核心代码位于:/pkg/server/service/service.go
需要关注 func (m *Manager) BuildHTTP() 这个方法,它由 Router 的初始化代码进行调用,用于初始化 Router 定义的 Service 。
我们需要在 func (m *Manager) BuildHTTP() 这个方法中实现对我们自定义 Service 的初始化。
这个方法首先提取了 Service 的配置,然后通过其中的 switch 语句,对配置的存在性进行判断。通过后,开始构建 Service 实例。核心的代码如下:
switch {
case conf.LoadBalancer != nil:
var err error
lb, err = m.getLoadBalancerServiceHandler(ctx, serviceName, conf.LoadBalancer)
if err != nil {
conf.AddError(err, true)
return nil, err
}
case conf.Weighted != nil:
var err error
lb, err = m.getWRRServiceHandler(ctx, serviceName, conf.Weighted)
if err != nil {
conf.AddError(err, true)
return nil, err
}
case conf.Mirroring != nil:
var err error
lb, err = m.getMirrorServiceHandler(ctx, conf.Mirroring)
if err != nil {
conf.AddError(err, true)
return nil, err
}
default:
sErr := fmt.Errorf("the service %q does not have any type defined", serviceName)
conf.AddError(sErr, true)
return nil, sErr
}
可以看见,三种默认 Service,均定义了 getxxxxxServiceHandler 函数,用于初始化 Service 实例。我们也应该定义类似的方法,以保证上述代码简洁可读。
我们定义的函数如下:
func (m *Manager) getIPWhiteListServiceHandler(ctx context.Context, config *dynamic.WhiteList) (http.Handler, error) {
serviceHandler, err := m.BuildHTTP(config.Service)
if err != nil {
return nil, err
}
handler := whitelist.New(serviceHandler, config)
return handler, nil
}
其中 `m.BuildHTTP(config.Service)` 这里是调用 BuildHTTP 方法,通过配置中传入的其他 Service 名称,创建其 Handler,以供我们的Service 调用。
定义好 `getIPWhiteListServiceHandler` 后,需要在 BuildHTTP 方法中增加配置的判断和调用就行:
// BuildHTTP Creates a http.Handler for a service configuration.
func (m *Manager) BuildHTTP(rootCtx context.Context, serviceName string) (http.Handler, error) {
......(省略了其他代码)
var lb http.Handler
switch {
......(省略了其他配置)
case conf.WhiteList != nil:
var err error
lb, err = m.getIPWhiteListServiceHandler(ctx, conf.WhiteList)
if err != nil {
conf.AddError(err, true)
return nil, err
}
default:
sErr := fmt.Errorf("the service %q does not have any type defined", serviceName)
conf.AddError(sErr, true)
return nil, sErr
}
return lb, nil
}
到此我们的自定义 Service 已经开发完成。可以根据需求,对代码进行测试和修改
【Traefik二次开发】服务 Service 开发的更多相关文章
- 仅用移动开发服务:开发native应用
不花一分钱,就可以做native应用开发,这在以前是根本不敢想象的事儿.然而在今天,移动开发工具和服务已经五花八门,聪明的开发者只要随心所欲的抓取几个顺手的,就能完成native开发.今天给大家介绍的 ...
- Delphi 三层框架开发 服务端开发
采用Delphi7+SQL2008 一.创建数据库和表 CREATE TABLE [dbo].[tb_Department]( [FKey] [uniqueidentifier] NOT NULL, ...
- Web Service学习-CXF开发Web Service实例demo(一)
Web Service是什么? Web Service不是框架.更甚至不是一种技术. 而是一种跨平台,跨语言的规范 Web Service解决什么问题: 为了解决不同平台,不同语言所编写的应用之间怎样 ...
- Android系统编程入门系列之加载服务Service
之前几篇文章简单梳理了在Android系统的四大组件之一,最主要的界面Activity中,使应用程序与用户进行交互响应的相关知识点,那对于应用程序中不需要与用户交互的逻辑,又要用到哪些内容呢?本文开始 ...
- 【工业串口和网络软件通讯平台(SuperIO)教程】七.二次开发服务驱动
SuperIO相关资料下载:http://pan.baidu.com/s/1pJ7lZWf 1.1 服务接口的作用 围绕着设备驱动模块采集的数据,根据需求提供多种应用服务,例如:数据上传服务.数 ...
- 免费提供UG、ProE二次开发、定制化开发服务
免费提供UG.ProE二次开发,定制开发服务. 拥有六年UG.ProE二次开发经验,相关项目经验. 从事过智能设计.计算机图形学相关研究. 联系方式: QQ:1787326383 微信号:begtos ...
- ubuntu下安装 gSOAP 用于C/C++开发web service服务端与客户端
昨天在ubuntu下进行安装gSOAP,费了很多时间,没成功,今天又来找了大量教程资料,终于一次成功,这里写下自己的安装步骤和方法,供大家参考. 首先下载gsoap,我下载的是gsoap-2.8.1. ...
- 北京智和信通IT运维管理系统二次开发服务提供商
随着云计算.大数据.物联网.移动互联网.人工智能.5G等高新技术的快速发展,数据中心及网络基础设施呈现出井喷式的增长模式,对设备商来说,多.快.好.省的实现定制化网络管理开发,可极大的扩充设备适用范围 ...
- 使用CXF开发Web Service服务
1.使用CXF开发Web Service服务端 1.1 开发一个Web Service业务接口,该接口要用@WebService修饰 (1)创建一个Java项目MyServer (2)在MyServe ...
随机推荐
- SpringBoot的浅浅配置和小整合
SpringBoot的浅浅配置和小整合 本文如题,就是浅浅记录一下学习的过程中一些过程,比较简单,并没有多少深度.谢谢! SpringBoot创建 从IDEA中新建项目或者模块.注意jdk版本,一般不 ...
- SpringBoot 集成缓存性能之王 Caffeine
使用缓存的目的就是提高性能,今天码哥带大家实践运用 spring-boot-starter-cache 抽象的缓存组件去集成本地缓存性能之王 Caffeine. 大家需要注意的是:in-memeory ...
- ShardingSphere-proxy-5.0.0容量范围分片的实现(五)
一.修改配置文件config-sharding.yaml,并重启服务 # # Licensed to the Apache Software Foundation (ASF) under one or ...
- bat-使用bat安装jdk和配置环境变量
文件路径 @echo off Setlocal enabledelayedexpansion @REM vscode中自动开启延迟环境变量扩展, %~d0 cd %~dp0 @REM dir echo ...
- VScode运行总是显示running状态
一.每次点击运行都显示code is already running,而且键盘也没有办法输入 二.解决办法 注意:记得重新启动VScode
- C语言输出九九乘法表
C语言学了有一阵子了,趁着假期没事练练手,没想到挺简单 基本思路是这样的 先写一个主函数,然后定义两个变量i1和i2;使用for语句循环嵌套,外层循环负责写循环9次,内循环里面写从1开始递增去和外层循 ...
- do-while循环和三种循环的区别
循环语句3--do...while do...while循环格式 初始化表达式① do{ 循环体③ 步进表达式④ }while(布尔表达式②); 执行流程 执行顺序:①③④>②③④>②③④ ...
- .NET GC工作流程
前言 在上文[如何获取GC的STW时间]一文中,我们聊到了如何通过监听GC发出的诊断事件来计算STW时间.里面只简单的介绍了几种GC事件和它的流程. 群里就有小伙伴在问,那么GC事件是什么时候产生的? ...
- 攻防世界MISC进阶区---41-45
41.Get-the-key.txt 得到无类型文件,扔进kali中,strings一下,得到了一堆像flag的内容 扔进010 Editor中,搜索关键字,发现一堆文件,改后缀为zip 打开,直接得 ...
- loguru备忘
loguru是个非常好用的三方日志管理包,简单易用,奈何老是记不住,在这记录一下吧 #coding:utf-8 ''' @version: python3.8 @author: 'eric' @lic ...