严格来说,广告请求的流转路径可以有两种模式:
目前,大多数 RTB 竞价采用 SSP → Ad Exchange → DSP 这种 中介交易市场 模式。
✅ Ad Exchange 负责向多个 DSP 发送广告请求,并从 DSP 收集出价,最终决定哪个广告展示。
✅ SSP 负责连接媒体方,并将广告请求发给 Ad Exchange,有时也会直接对接 DSP。
✅ 目前主流 RTB 竞价采用 SSP → Ad Exchange → DSP 模式。
✅ 一般情况下:
✅ 特殊情况:
步骤 | 组件 | 作用 |
---|---|---|
1 | 用户 | 访问媒体方(Publisher) |
2 | Publisher | 发送广告请求给 SSP |
3 | SSP(供应方平台) | 处理流量,决定是否进入竞价 |
4 | Ad Exchange(广告交易平台) | 发送广告请求到多个 DSP |
5 | DSP(需求方平台) | 计算出价并返回广告素材 |
6 | Ad Exchange | 选择最高出价的 DSP |
7 | SSP | 最终决定是否展示广告 |
8 | Publisher | 展示广告给用户 |
SSP 不会主动向 Ad Exchange 发送请求,必须依赖用户访问媒体方(Publisher)来触发。
Header Bidding 和 RTB 的关系可以理解为:Header Bidding 负责并行多个平台的竞价,而 RTB 是在每个并行平台内部进行的竞价流程。
可以用下面的图示来理解:
┌──────────────┐
│ Publisher │ → 向多个 SSP/DSP 并行发送 Header Bidding 请求
└──────────────┘
↓
┌──────────────────────────────────────────┐
│ Header Bidding(多个 SSP 并行竞价) │
│ ┌─────────┐ ┌─────────┐ ┌─────────┐ │
│ │ SSP A │ │ SSP B │ │ SSP C │ ... │
│ └─────────┘ └─────────┘ └─────────┘ │
└──────────────────────────────────────────┘
↓
每个 SSP 内部进行 RTB 竞价:
↓
┌───────────┐ ┌───────────┐ ┌───────────┐
│ DSP A1 │ │ DSP B1 │ │ DSP C1 │
│ DSP A2 │ │ DSP B2 │ │ DSP C2 │
│ DSP A3 │ │ DSP B3 │ │ DSP C3 │
└───────────┘ └───────────┘ └───────────┘
↓
每个 SSP 选择一个最高出价后返回给 Header Bidding,再由广告服务器决定最终展示
对比项 | Header Bidding | RTB(每个 SSP 内部的流程) |
---|---|---|
触发方 | Publisher(媒体方) | SSP(供应方平台) |
作用范围 | 多个 SSP 并行竞价 | 单个 SSP 内部的 RTB 竞价 |
竞价流程 | Publisher 直接向多个 SSP/DSP 并行发送竞价请求 | SSP 内部向多个 DSP 发起 RTB 竞价 |
竞价方式 | 所有 SSP 公开竞争 | 每个 SSP 选择最高 DSP 出价 |
优点 | 并行竞价,最大化收益 | DSP 竞价更精准 |
✅ Header Bidding 负责让多个 SSP 竞争,RTB 负责每个 SSP 内部的 DSP 竞价。
✅ Header Bidding 让所有 SSP 并行竞价,而 RTB 是 SSP 内部的流程。
✅ 最终广告展示取决于 Header Bidding 最高出价 和 广告服务器内部的 RTB 竞价结果的比较。
在大型互联网平台的广告营销业务中,Publisher(媒体) 向 SSP(Supply-Side Platform,供应方平台) 发送广告请求的方式主要有以下几种:
流程:
Publisher 的服务器直接向 SSP 发送 HTTP 请求(通常是 POST
请求),请求返回合适的广告素材。
请求方式:
请求入参:
site_id
、app_id
)ad_unit_id
,广告尺寸 width/height
,支持的广告类型 banner/video/native
)user_id
,IP ip
,Cookie/Device ID)device_type
,操作系统 os
,浏览器 browser
,屏幕分辨率 screen_size
)lat/lon
或 IP 解析地址)流程:
用户访问 Publisher 网站或 APP,浏览器/APP 直接向 SSP 发送广告请求。
请求方式:
请求入参:
ad_slot_id
,广告尺寸,格式)流程:
Publisher 通过 JavaScript 代码(如 Prebid.js)或 APP SDK 并行向多个 SSP 发送竞价请求,并选出最高价广告填充。
请求方式:
POST
/ GET
请求(通常使用 JSON 格式)请求入参:
请求方式 | 发送者 | 请求方式 | 适用场景 |
---|---|---|---|
S2S 直连 | 服务器 | HTTP POST |
大型 Publisher,流量大,减少延迟 |
Client-Side | 浏览器/APP | JS、SDK | 适用于小型 Publisher,易于部署 |
Header Bidding | 浏览器/APP | JS(Prebid.js)、SDK | 允许多个 SSP 竞价,提高收益 |
Publisher(网站、APP、视频平台等)和 SSP 之间的通信通常通过 HTTP(S) API、SDK 或 JavaScript 代码 实现,主要方式如下:
适用于: 网站、APP、小程序等 无 SDK 集成 的场景
工作方式: Publisher 在页面或 APP 端发起 HTTP(S) 请求,向 SSP 请求广告
示例流程:
示例 HTTP 广告请求(OpenRTB JSON 格式):
{
"id": "123456789",
"site": {
"id": "example.com",
"domain": "example.com"
},
"device": {
"ip": "192.168.1.1",
"ua": "Mozilla/5.0"
},
"user": {
"id": "abc123"
},
"imp": [
{
"id": "1",
"banner": {
"w": 300,
"h": 250
}
}
]
}
说明:
id
:请求 IDsite.domain
:媒体域名device
:用户设备信息(IP、User-Agent)user.id
:用户唯一标识(可能是 Cookie ID 或 IDFA/GAID)imp
:广告位信息(如 banner 广告的尺寸 300x250)SSP 返回广告 JSON(OpenRTB Response 格式):
{
"id": "123456789",
"seatbid": [
{
"bid": [
{
"id": "bid123",
"impid": "1",
"price": 1.2,
"adm": "
"
}
]
}
]
}
说明:
seatbid.bid.price
:广告出价(1.2 美元)adm
:广告 HTML 代码(可以是图片、视频或 JS 代码) 适用于: 网站、PC 端 Web(如新闻网站、博客)
工作方式: Publisher 在网页中插入 SSP 提供的 JS 代码,由前端异步请求 SSP 获取广告
示例:Publisher 在 HTML 中插入 JS 代码
<script async src="https://ssp.example.com/ad.js?site_id=12345">script>
JS 代码运行后,会向 SSP 发起广告请求(类似 HTTP API 请求,但是在前端进行)。
SSP 返回广告后,JS 代码负责渲染广告位(展示 Banner、视频或原生广告)。
✅ 优点:
❌ 缺点:
适用于: 移动 APP(iOS、Android)
工作方式: Publisher 在 APP 中集成 SSP 提供的 SDK,SDK 负责发起广告请求
APP 代码示例(Android Java)
AdView adView = new AdView(this);
adView.setAdUnitId("ca-app-pub-3940256099942544/6300978111");
adView.setAdSize(AdSize.BANNER);
AdRequest adRequest = new AdRequest.Builder().build();
adView.loadAd(adRequest);
SDK 负责完成广告请求、竞价、填充广告,Publisher 只需要调用 loadAd()
。
✅ 优点:
❌ 缺点:
适用于: 视频网站、短视频 APP、直播平台
工作方式: Publisher 通过 VAST(Video Ad Serving Template)或 VPAID(Video Player Ad Interface Definition)标准,从 SSP 获取视频广告 URL,并在播放器中播放
示例:Publisher 请求视频广告 VAST XML
<VAST version="3.0">
<Ad id="123456">
<InLine>
<AdSystem>SSPAdSystem>
<Creatives>
<Creative>
<Linear>
<MediaFiles>
<MediaFile type="video/mp4">
https://cdn.example.com/video_ad.mp4
MediaFile>
MediaFiles>
Linear>
Creative>
Creatives>
InLine>
Ad>
VAST>
播放器解析 VAST XML 并播放视频广告。
✅ 优点:
❌ 缺点:
通信方式 | 适用场景 | 优点 | 缺点 |
---|---|---|---|
HTTP API(OpenRTB) | 网站、APP、小程序 | 直接对接 SSP,适用于 RTB 竞价 | 需要后端开发,延迟较高 |
JavaScript(前端异步加载) | 网站 | 适合网页,易集成 | 受浏览器隐私限制,可能被 AdBlock 拦截 |
SDK(移动端) | iOS、Android APP | 易于集成,封装完整 | 需要更新 SDK,占用 APP 体积 |
VAST/VPAID(视频广告) | 视频网站、直播 | 标准化,兼容性强 | 需要播放器支持 |
Java 开发,通常需要实现 HTTP API 方式,处理 OpenRTB 请求和响应。
✅ SSP(供应方平台)通常采用微服务架构,但它并不是单个微服务,而是一套分布式微服务系统。
SSP 的核心功能:
微服务拆分方式:
✅ Traffic Manager Service
(流量管理服务):接收广告请求,进行基本过滤。
✅ Bid Request Service
(竞价请求服务):将请求转发给 Ad Exchange 或 DSP。
✅ Anti-Fraud Service
(反作弊服务):分析广告流量是否正常。
✅ Analytics Service
(数据分析服务):记录广告投放数据,优化收益。
SSP 需要处理 高并发流量请求,并与 多个 Ad Exchange、DSP 交互,需要 低延迟、可扩展、稳定性强,所以通常会拆分为多个微服务。
SSP 主要负责 接收媒体方(Publisher)请求,优化流量,发送给 Ad Exchange 或直接对接 DSP 竞价。
可以拆分成如下 微服务组件
微服务 | 功能 | 技术栈(Java 相关) |
---|---|---|
流量接入服务 | 处理 Publisher 的广告请求(HTTP/gRPC) | Spring Boot + Netty |
流量过滤与优化 | 过滤无效流量(IVT)、反作弊 | Java + Redis + Elasticsearch |
广告竞价分发服务 | 把广告请求转发给 Ad Exchange / DSP | Kafka + Netty |
PMP 直销服务 | 处理 Private Marketplace(私有市场)交易 | Spring Boot + MySQL |
预算 & 库存管理服务 | 计算广告位库存、控制广告填充率 | ClickHouse + Redis |
日志 & 监控服务 | 记录竞价日志,提供 BI 分析 | ELK(Elasticsearch + Logstash + Kibana) |
数据分析服务 | 分析 eCPM、填充率,优化流量分发 | Flink / Spark + Druid |
✅ 高性能架构
✅ 流量优化
✅ 分布式架构
✅ SSP 不是单个微服务,而是由多个微服务组成的分布式系统。
✅ 它的核心职责是优化广告流量,并向 Ad Exchange / DSP 发送竞价请求。
✅ SSP 采用高并发、低延迟的微服务架构,如 Netty + Kafka + Redis + ClickHouse。
✅ Ad Exchange 不是单个微服务,而是一个分布式微服务系统。
Ad Exchange(广告交易平台)负责 接收 SSP 发送的广告请求,并将其转发给多个 DSP 进行竞价,最终选出最高出价的广告展示给用户。
由于 高并发、低延迟、实时竞价 的需求,Ad Exchange 通常采用微服务架构,拆分成多个独立的子系统,以保证高效稳定运行。
Ad Exchange 的核心功能:
微服务拆分方式:
✅ Bid Manager Service
(竞价管理服务):负责向 DSP 发送竞价请求,并计算出价。
✅ Auction Engine Service
(竞价引擎服务):处理 RTB 竞价逻辑,选出最高出价者。
✅ Ad Delivery Service
(广告投放服务):处理广告素材返回,确保广告正确展示。
✅ Reporting & Analytics Service
(报表与分析服务):记录竞价数据,优化 DSP 竞价策略。
结论:Ad Exchange 也是一个由多个微服务组成的系统,核心是竞价引擎,但其他功能也需要不同微服务支持。
Ad Exchange 可以拆分为如下 微服务组件
微服务 | 功能 | 技术栈(Java 相关) |
---|---|---|
竞价请求处理服务 | 解析 SSP 请求,并转发给 DSP | Spring Boot + Netty |
实时竞价引擎 | 计算 DSP 竞价结果,选择最高出价 | Java + Redis + ClickHouse |
广告填充服务 | 处理未竞价成功的流量(Fallback 广告) | Spring Boot + Redis |
反作弊 & 质量控制 | 过滤垃圾流量(IVT)、无效点击 | Elasticsearch + Kafka |
日志 & 监控服务 | 记录竞价日志,提供 BI 分析 | ELK(Elasticsearch + Logstash + Kibana) |
数据分析服务 | 计算 eCPM、优化 DSP 竞价策略 | Flink / Spark + Druid |
流量管理 & 频次控制 | 处理广告展示频次、用户曝光控制 | Redis + MySQL |
✅ 高性能架构
✅ 实时竞价 & 机器学习
✅ 分布式 & 容器化
✅ Ad Exchange 不是单个微服务,而是由多个微服务组成的分布式系统。
✅ 它的核心职责是连接 SSP 和 DSP,执行实时竞价,并选择最高出价的广告展示。
✅ 采用高并发、低延迟架构,如 Netty + Kafka + Redis + ClickHouse。
✅ DSP(需求方平台)通常是一个微服务架构的系统,但它并不一定是单个微服务,而是由多个微服务组成的分布式系统。
DSP 主要负责广告竞价、投放策略、数据分析等任务,高并发、低延迟,适合拆分为多个微服务。
常见的 DSP 微服务架构 可能包括以下几个核心模块:
微服务 | 功能 | 技术栈(Java 相关) |
---|---|---|
广告请求处理服务 | 负责接收 Ad Exchange 的广告竞价请求 | Spring Boot + Netty |
竞价引擎(Bid Engine) | 计算出价,决定是否参与竞价 | Java + Redis + ClickHouse |
用户画像服务(DMP) | 结合 DMP(数据管理平台)优化广告投放 | Spark/Flink + MongoDB |
策略决策服务(Rule Engine) | 处理黑名单、品牌安全、预算控制 | Drools / 自定义规则引擎 |
广告素材管理服务 | 处理广告创意(图片、视频、HTML) | MinIO + Redis 缓存 |
日志与监控服务 | 记录竞价日志,分析 CTR/CVR | ELK(Elasticsearch + Logstash + Kibana) |
报表与BI分析 | 统计广告投放效果 | ClickHouse + BI 平台 |
如果你想开发 DSP 竞价系统的核心微服务,可以关注以下技术:
✅ 高性能处理:
✅ 竞价算法优化:
✅ 数据分析:
✅ 分布式架构:
在广告营销业务中,SSP、DSP 和 Ad Exchange 这三个核心组件都会用到 DMP(Data Management Platform,数据管理平台),但它们使用 DMP 的方式和目的有所不同。
DMP 主要通过以下几种方式获取数据:
在实际应用中:
总的来说,DMP 是广告营销生态中的重要组成部分,SSP、DSP、Ad Exchange 都会使用 DMP,但具体的使用方式和数据需求各不相同。
Ad Exchange、SSP、DSP 这三者可以 共用一个 DMP,但在实际操作中,也可以根据需求和架构设计选择不同的方案。
数据管理平台(DMP) 主要用于收集、整理和分析用户数据,帮助提高广告投放的精准度和效果。在广告生态中,SSP、DSP 和 Ad Exchange 都需要通过数据来优化广告竞价、流量管理和广告展示,因此它们完全可以共用一个 DMP。
在某些情况下,SSP、DSP 和 Ad Exchange 会选择 各自拥有独立的 DMP,这通常与 独立运营 或 不同的业务需求 有关。
在现实环境中,SSP、DSP 和 Ad Exchange 通常会选择共用一个 DMP,而不是每个系统独立拥有自己的 DMP。这种方式更为常见,并且具有以下几个实际优势:
尽管共用一个 DMP 是更常见的选择,但某些特定的广告平台或业务场景中,也可能会选择 各自独立的 DMP。例如:
现实中,绝大多数广告平台(SSP、DSP、Ad Exchange)选择共用一个 DMP。
这不仅能提高数据一致性和广告效果,还能减少冗余的工作和资源浪费,优化数据的使用效率。尤其是在 广告竞价(RTB) 和 精准投放 的需求下,统一的 DMP 能更好地支撑多方数据共享和广告优化。
DMP(数据管理平台)的代码结构一般围绕数据采集、存储、处理、分析、API 服务等核心功能展开。其架构通常分为多个模块,每个模块都可以是一个独立的微服务,并结合大数据生态进行高效的数据管理和分析。
DMP/
│── data-ingestion/ # 数据采集层
│ ├── log-collector/ # 日志采集(Kafka、Flume、Logstash)
│ ├── event-tracker/ # 用户行为跟踪(埋点SDK,如JS/Python/Java)
│ ├── batch-ingestion/ # 批量数据导入(ETL、Hive、Flink)
│── data-storage/ # 数据存储层
│ ├── hdfs/ # HDFS 存储用户数据
│ ├── clickhouse/ # ClickHouse 进行实时数据分析
│ ├── mongodb/ # MongoDB 存储非结构化数据
│ ├── redis/ # Redis 作为缓存加速查询
│── data-processing/ # 数据处理层
│ ├── spark-processing/ # Spark 进行离线数据处理
│ ├── flink-streaming/ # Flink 进行实时数据计算
│ ├── feature-engineering/ # 特征工程,构建广告投放画像
│── data-analysis/ # 数据分析层
│ ├── user-profiling/ # 用户画像分析
│ ├── audience-segmentation/ # 受众分群
│ ├── predictive-modeling/ # 预测建模(机器学习)
│── api-services/ # API 服务层
│ ├── rest-api/ # 提供对外接口(Spring Boot、FastAPI)
│ ├── grpc-service/ # gRPC 实现高性能数据传输
│── ui-dashboard/ # 前端可视化
│ ├── react/ # 基于 React 或 Vue.js 构建的管理面板
│── deployment/ # 部署运维
│ ├── docker/ # Docker 部署
│ ├── kubernetes/ # K8s 集群管理
│ ├── monitoring/ # Prometheus + Grafana 监控
主要负责从各种渠道收集数据,例如:
技术栈:
用于存储和管理收集到的海量数据,不同类型的数据使用不同的存储:
DMP 需要进行数据清洗、特征工程、用户分群等处理:
示例:
from pyspark.sql import SparkSession
spark = SparkSession.builder.appName("DMPUserProfile").getOrCreate()
# 读取用户行为数据
df = spark.read.json("hdfs://data/user_behavior.json")
# 进行特征提取
df = df.groupBy("user_id").agg({"clicks": "sum", "impressions": "sum"})
df.show()
DMP 需要提供用户画像分析、受众分群、预测建模:
示例:用 Python 进行广告点击率(CTR)预测:
from sklearn.ensemble import RandomForestClassifier
from sklearn.model_selection import train_test_split
# 假设有CTR数据集
X_train, X_test, y_train, y_test = train_test_split(features, labels, test_size=0.2)
model = RandomForestClassifier(n_estimators=100)
model.fit(X_train, y_train)
# 预测点击率
predictions = model.predict(X_test)
DMP 需要对外提供 API,让 SSP、DSP、Ad Exchange 获取数据:
示例:一个简单的用户画像 API:
from fastapi import FastAPI
import redis
app = FastAPI()
redis_client = redis.Redis(host="localhost", port=6379)
@app.get("/user_profile/{user_id}")
def get_user_profile(user_id: str):
data = redis_client.get(f"user_profile:{user_id}")
return {"user_id": user_id, "profile": data}
提供 Web 界面,让运营人员查看数据:
示例:用 ECharts 显示用户画像数据:
var myChart = echarts.init(document.getElementById('main'));
myChart.setOption({
title: { text: '用户兴趣分布' },
series: [{ type: 'pie', data: [{ value: 40, name: '游戏' }, { value: 60, name: '电商' }] }]
});
组件 | 技术栈 |
---|---|
数据采集 | Kafka, Flume, Logstash, JavaScript SDK |
数据存储 | HDFS, ClickHouse, MongoDB, Redis, ElasticSearch |
数据处理 | Apache Spark, Flink, Airflow, Python |
模型训练 | Scikit-Learn, TensorFlow, PyTorch |
API 服务 | Spring Boot, FastAPI, gRPC |
前端可视化 | React, Vue.js, ECharts, Grafana |
部署运维 | Docker, Kubernetes, Prometheus |
DMP 不是一个单一的微服务,而是一个包含多个微服务和大数据组件的大数据平台:
DMP 结合了 大数据处理 + 机器学习 + 微服务,支持 SSP、DSP、Ad Exchange 进行精准营销。
在 DMP 里,数据同步非常重要,保证各个存储系统(MySQL、Kafka、Hive)之间的数据一致性:
示例:使用 Flink CDC 监听 MySQL 变更并同步到 Kafka:
StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment();
FlinkKafkaProducer<String> kafkaProducer = new FlinkKafkaProducer<>(
"kafka-topic",
new SimpleStringSchema(),
kafkaProps
);
env.addSource(new MySQLSource()).addSink(kafkaProducer);
env.execute();
数据安全在 DMP 里至关重要,需要:
示例:Spring Security 进行 API 认证:
@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/api/**").authenticated()
.and().oauth2Login();
}
}
示例:Prometheus 监控 Java API:
@RestController
public class MetricsController {
private final Counter requestCounter = Counter.build()
.name("api_requests_total")
.help("Total API Requests")
.register();
@GetMapping("/metrics")
public String getMetrics() {
requestCounter.inc();
return "OK";
}
}
SSP(供应方平台)主要用于管理媒体方的广告资源,并将广告请求转发给 Ad Exchange 或 DSP 进行实时竞价(RTB)。它的代码结构通常涉及 广告请求处理、流量过滤、竞价管理、广告填充、日志与监控等模块。
SSP/
│── ad-request-handler/ # 广告请求处理模块
│ ├── http-server/ # 处理媒体方的广告请求(Spring Boot、Netty)
│ ├── grpc-server/ # 处理 gRPC 请求,提高性能
│ ├── validation/ # 请求参数校验(IP、设备信息)
│── traffic-filter/ # 流量过滤模块(防作弊、无效流量)
│ ├── bot-detection/ # 识别爬虫和自动化流量
│ ├── fraud-detection/ # 过滤点击欺诈流量
│ ├── geo-filtering/ # 地域过滤(基于 IP 库)
│── ad-auction/ # 竞价管理模块
│ ├── bid-request/ # 生成 RTB 竞价请求,转发给 Ad Exchange/DSP
│ ├── bid-response/ # 解析 DSP 返回的竞价结果
│ ├── auction-engine/ # 竞价逻辑(第二高价竞价、优先广告策略)
│── ad-selection/ # 广告填充模块
│ ├── direct-ads/ # 直接投放广告(非竞价广告)
│ ├── fallback-ads/ # 兜底广告(竞价失败时填充)
│── data-sync/ # 数据同步模块
│ ├── dmp-integration/ # 与 DMP 对接,获取用户画像数据
│ ├── kafka-events/ # 通过 Kafka 进行数据同步
│── reporting/ # 数据上报模块
│ ├── win-notification/ # 向 DSP 发送竞价成功通知
│ ├── impression-tracking/ # 记录广告展示(曝光)日志
│ ├── click-tracking/ # 记录广告点击日志
│── api-services/ # API 服务层
│ ├── rest-api/ # 提供对外 API(Spring Boot、FastAPI)
│ ├── grpc-service/ # 高性能 API(gRPC)
│── ui-dashboard/ # 管理控制台
│ ├── react/ # React 或 Vue.js 前端界面
│ ├── reporting-ui/ # 广告报表可视化
│── security/ # 安全模块
│ ├── auth/ # 用户认证(JWT、OAuth2)
│ ├── rate-limiting/ # API 限流(Sentinel、Guava RateLimiter)
│── deployment/ # 部署运维
│ ├── docker/ # Docker 容器化部署
│ ├── kubernetes/ # Kubernetes 自动扩展
│ ├── monitoring/ # 监控系统(Prometheus + Grafana)
媒体方(Publisher)会向 SSP 发送广告请求,SSP 需要解析请求,并进行流量过滤:
{
"user_id": "12345",
"device": "mobile",
"location": "New York, USA",
"ad_slot": "banner_top",
"page_url": "https://example.com/article"
}
示例:Spring Boot 处理广告请求:
@RestController
@RequestMapping("/ad")
public class AdRequestController {
@PostMapping("/request")
public ResponseEntity<AdResponse> handleAdRequest(@RequestBody AdRequest request) {
if (!TrafficFilter.isValidRequest(request)) {
return ResponseEntity.status(HttpStatus.FORBIDDEN).build();
}
AdResponse response = AdAuction.processRequest(request);
return ResponseEntity.ok(response);
}
}
SSP 需要将广告请求转发给 Ad Exchange 或多个 DSP 进行实时竞价(RTB),然后选择 最高竞价的广告进行展示。
{
"bid_request_id": "abc123",
"imp": [
{
"id": "1",
"banner": {
"w": 300,
"h": 250
}
}
],
"site": {
"domain": "example.com"
},
"user": {
"id": "user_123"
}
}
示例:发送 HTTP 请求到 Ad Exchange 进行竞价:
public BidResponse sendBidRequest(BidRequest bidRequest) {
RestTemplate restTemplate = new RestTemplate();
return restTemplate.postForObject("https://adexchange.com/bid", bidRequest, BidResponse.class);
}
SSP 需要防止广告欺诈(如虚假流量、自动点击),可以通过 IP 黑名单、设备指纹、爬虫检测 来进行过滤:
public static boolean isValidRequest(AdRequest request) {
return !FraudDetection.isBlacklistedIP(request.getIp()) &&
!BotDetection.isAutomatedTraffic(request.getUserAgent());
}
如果没有 DSP 竞价成功,SSP 需要使用直接投放广告或兜底广告:
public AdResponse getFallbackAd(AdRequest request) {
return new AdResponse("fallback_ad.jpg", "https://sponsor.com");
}
SSP 需要与 DMP 交互,获取用户画像,提高广告匹配度:
public UserProfile getUserProfile(String userId) {
return restTemplate.getForObject("https://dmp.com/user/" + userId, UserProfile.class);
}
广告展示后,SSP 需要回传曝光(impression)和点击(click)数据:
public void trackImpression(String adId, String userId) {
kafkaTemplate.send("impressions", new ImpressionLog(adId, userId, System.currentTimeMillis()));
}
SSP 需要 Prometheus + Grafana 进行监控:
@RestController
public class MetricsController {
private final Counter requestCounter = Counter.build()
.name("ad_requests_total")
.help("Total Ad Requests")
.register();
@GetMapping("/metrics")
public String getMetrics() {
requestCounter.inc();
return "OK";
}
}
这个架构可以支撑 高并发、低延迟 的 SSP 系统
Ad Exchange(广告交易平台)是连接 SSP(供应方平台)和 DSP(需求方平台) 的核心组件,负责接收 SSP 传来的广告竞价请求,并将其转发给多个 DSP 进行 RTB(实时竞价),然后选择最高竞价广告进行返回。
AdExchange/
│── request-handler/ # 广告竞价请求处理模块
│ ├── http-server/ # 处理 HTTP 广告竞价请求
│ ├── grpc-server/ # 处理 gRPC 广告竞价请求
│── bid-manager/ # 竞价管理模块
│ ├── dsp-connector/ # 向多个 DSP 发送竞价请求
│ ├── auction-engine/ # 竞价引擎(Vickrey Auction、First Price Auction)
│── impression-tracking/ # 监测广告曝光与点击
│ ├── win-notification/ # 竞价胜出通知(通知 DSP)
│ ├── event-logger/ # 记录曝光、点击日志
│── dmp-integration/ # DMP 数据对接
│ ├── user-profiling/ # 获取用户画像数据
│ ├── targeting-rules/ # 进行人群定向匹配
│── analytics/ # 竞价数据分析
│ ├── real-time-metrics/ # 监控竞价速率、胜出率
│ ├── fraud-detection/ # 竞价欺诈检测
│── api-services/ # API 层
│ ├── rest-api/ # 提供 REST API
│ ├── grpc-service/ # 提供 gRPC API
│── ui-dashboard/ # 管理控制台
│ ├── react/ # 前端 UI
│ ├── bid-analytics/ # 竞价分析界面
│── deployment/ # 部署与监控
│ ├── docker/ # Docker 容器化
│ ├── kubernetes/ # Kubernetes 自动扩展
│ ├── monitoring/ # Prometheus + Grafana 监控
Ad Exchange 需要接收 SSP 传来的竞价请求,并解析请求数据:
{
"id": "req_12345",
"imp": [
{
"id": "1",
"banner": {
"w": 300,
"h": 250
}
}
],
"site": {
"domain": "example.com"
},
"user": {
"id": "user_6789"
}
}
Spring Boot 处理 HTTP 请求:
@RestController
@RequestMapping("/adexchange")
public class BidRequestController {
@PostMapping("/bid")
public ResponseEntity<BidResponse> handleBidRequest(@RequestBody BidRequest request) {
AuctionResult result = AuctionEngine.runAuction(request);
return ResponseEntity.ok(result.getWinningBid());
}
}
Ad Exchange 需要向多个 DSP 发送竞价请求,并收集竞价数据:
public class DspConnector {
private final RestTemplate restTemplate = new RestTemplate();
public BidResponse sendBidRequest(BidRequest bidRequest, String dspUrl) {
return restTemplate.postForObject(dspUrl, bidRequest, BidResponse.class);
}
}
示例:DSP 竞价返回的响应数据:
{
"id": "bid_7890",
"price": 1.5,
"ad": {
"url": "https://ads.com/ad123.jpg"
}
}
Ad Exchange 需要对多个 DSP 返回的竞价结果进行比对,选出 最高出价广告:
public class AuctionEngine {
public static BidResponse runAuction(BidRequest request, List<BidResponse> dspBids) {
return dspBids.stream()
.max(Comparator.comparing(BidResponse::getPrice))
.orElse(null);
}
}
常见的竞价算法:
Ad Exchange 需要记录广告展示和点击事件,并通知 DSP:
public void trackImpression(String adId, String userId) {
kafkaTemplate.send("impressions", new ImpressionLog(adId, userId, System.currentTimeMillis()));
}
竞价胜出后,通知 DSP:
public void notifyWinningBid(BidResponse bid) {
restTemplate.postForObject("https://dsp.com/win", bid, Void.class);
}
Ad Exchange 需要与 DMP(数据管理平台) 对接,获取用户画像数据:
public UserProfile getUserProfile(String userId) {
return restTemplate.getForObject("https://dmp.com/user/" + userId, UserProfile.class);
}
通过 DMP 定向投放广告:
public boolean isUserTargeted(UserProfile userProfile, AdRequest request) {
return userProfile.getInterests().contains(request.getTargetCategory());
}
Ad Exchange 需要分析竞价数据,包括 竞价速率、胜出率、欺诈检测:
@RestController
public class MetricsController {
private final Counter bidCounter = Counter.build()
.name("bids_total")
.help("Total Bids Processed")
.register();
@GetMapping("/metrics")
public String getMetrics() {
bidCounter.inc();
return "OK";
}
}
Ad Exchange 需要使用 Prometheus + Grafana 进行监控:
apiVersion: monitoring.coreos.com/v1
kind: ServiceMonitor
metadata:
name: adexchange-monitor
spec:
endpoints:
- port: metrics
interval: 30s
selector:
matchLabels:
app: adexchange
✅ Ad Exchange 需要实现的核心功能
这个架构可以支撑 高并发、低延迟 的 Ad Exchange 系统
DSP(Demand-Side Platform)主要负责 接收 Ad Exchange 的竞价请求,执行 广告选择与定价策略,并返回 竞价响应。DSP 需要结合 DMP(数据管理平台) 进行用户定向投放,同时管理 广告主账户、预算控制、广告创意等。
DSP/
│── request-handler/ # 竞价请求处理
│ ├── http-server/ # 处理 HTTP 请求
│ ├── grpc-server/ # 处理 gRPC 请求
│── bidding-engine/ # 竞价引擎
│ ├── ad-selection/ # 选择最合适的广告
│ ├── price-strategy/ # 竞价策略(第一价格拍卖、第二价格拍卖)
│── dmp-integration/ # DMP 数据对接
│ ├── user-profiling/ # 用户画像分析
│ ├── targeting-engine/ # 目标受众匹配
│── ad-management/ # 广告管理模块
│ ├── ad-creative/ # 广告创意管理
│ ├── campaign-management/ # 广告投放管理(预算、日限额)
│── reporting/ # 数据分析与报表
│ ├── win-rate-analysis/ # 竞价胜率分析
│ ├── budget-monitoring/ # 预算消耗监控
│── api-services/ # API 服务
│ ├── rest-api/ # 提供 REST API
│ ├── grpc-service/ # 提供 gRPC API
│── deployment/ # 部署与监控
│ ├── docker/ # Docker 容器化
│ ├── kubernetes/ # Kubernetes 自动扩展
│ ├── monitoring/ # Prometheus + Grafana 监控
DSP 需要接收 Ad Exchange 发送的竞价请求,并解析请求数据:
{
"id": "req_12345",
"imp": [
{
"id": "1",
"banner": {
"w": 300,
"h": 250
}
}
],
"site": {
"domain": "example.com"
},
"user": {
"id": "user_6789"
}
}
Spring Boot 处理 HTTP 竞价请求:
@RestController
@RequestMapping("/dsp")
public class BidRequestController {
@PostMapping("/bid")
public ResponseEntity<BidResponse> handleBidRequest(@RequestBody BidRequest request) {
BidResponse bidResponse = BiddingEngine.processBid(request);
return ResponseEntity.ok(bidResponse);
}
}
DSP 需要根据 广告匹配度、用户定向、广告预算 计算最佳竞价:
public class BiddingEngine {
public static BidResponse processBid(BidRequest request) {
// Step 1: 选择适合的广告
Ad ad = AdSelection.selectBestAd(request);
// Step 2: 计算竞价价格
double bidPrice = PricingStrategy.calculateBid(ad, request);
return new BidResponse(ad.getId(), bidPrice, ad.getAdUrl());
}
}
广告选择逻辑:
public class AdSelection {
public static Ad selectBestAd(BidRequest request) {
List<Ad> eligibleAds = AdRepository.getAdsByTargeting(request.getUser());
return eligibleAds.stream()
.max(Comparator.comparing(Ad::getClickThroughRate)) // 选择点击率最高的广告
.orElse(null);
}
}
竞价策略(第一价格拍卖 or 第二价格拍卖):
public class PricingStrategy {
public static double calculateBid(Ad ad, BidRequest request) {
double baseBid = ad.getBasePrice();
return baseBid * (1 + Math.random() * 0.2); // 增加浮动因子
}
}
DSP 需要与 DMP 交互,获取 用户画像,优化广告投放:
public class DmpClient {
private static final String DMP_URL = "https://dmp.com/user/";
public static UserProfile getUserProfile(String userId) {
return restTemplate.getForObject(DMP_URL + userId, UserProfile.class);
}
}
然后,DSP 需要根据 DMP 数据进行 精准投放:
public class TargetingEngine {
public static boolean isUserTargeted(UserProfile userProfile, Ad ad) {
return userProfile.getInterests().contains(ad.getCategory());
}
}
DSP 需要管理 广告创意、广告主账户、预算控制:
public class AdCampaign {
private String advertiserId;
private String campaignId;
private double dailyBudget;
private double spentToday;
public boolean canBid(double bidAmount) {
return (spentToday + bidAmount) <= dailyBudget;
}
}
如果预算已达上限,DSP 不会继续竞价:
public class BudgetManager {
public static boolean checkBudget(AdCampaign campaign, double bidAmount) {
return campaign.canBid(bidAmount);
}
}
DSP 需要监控 竞价胜率、广告转化率、预算消耗:
@RestController
@RequestMapping("/metrics")
public class MetricsController {
private final Counter bidCounter = Counter.build()
.name("dsp_bids_total")
.help("Total DSP Bids")
.register();
@GetMapping("/bids/count")
public String getBidCount() {
bidCounter.inc();
return "Bids counted.";
}
}
DSP 需要使用 Prometheus + Grafana 进行监控:
apiVersion: monitoring.coreos.com/v1
kind: ServiceMonitor
metadata:
name: dsp-monitor
spec:
endpoints:
- port: metrics
interval: 30s
selector:
matchLabels:
app: dsp
✅ DSP 需要实现的核心功能
这个架构可以支撑 高并发、精准投放 的 DSP 平台
在 广告营销业务 的 DSP(需求方平台) 中,竞价计算 是 DSP 决定广告出价 的核心环节。第一价格拍卖(First-Price Auction) 是其中的一种竞价方式。
第一价格拍卖 是指 广告主(DSP)提交的最高出价即为实际支付价格,即 最终赢家支付自己的出价金额。
假设 3 个 DSP 参与竞价,它们的出价如下:
DSP | 出价(Bid) |
---|---|
DSP A | $3.50 |
DSP B | $4.00 |
DSP C | $3.80 |
在第一价格拍卖规则下:
优势 | 劣势 |
---|---|
✅ 透明 —— 竞价者支付的价格与出价完全一致 | ❌ 出价过高风险 —— 由于不知道其他 DSP 出价,可能导致广告主支付过高费用 |
✅ 快速决策 —— 不需要复杂的二次计算 | ❌ 价格波动大 —— 由于 DSP 可能会过度竞价或减少出价,导致市场价格不稳定 |
✅ 防止拍卖操控 —— 无法通过策略性出价操控价格 | ❌ 广告主成本较高 —— 竞价者为了赢得广告位,往往会出更高价格 |
在 DSP 的竞价引擎 中,第一价格拍卖的逻辑很简单,就是返回出价最高的 DSP,并让其支付自身出价:
public class FirstPriceAuction {
public static BidResponse determineWinner(List<BidRequest> bids) {
// 按出价金额降序排列
bids.sort((b1, b2) -> Double.compare(b2.getBidPrice(), b1.getBidPrice()));
// 最高出价者赢得广告位
BidRequest winningBid = bids.get(0);
return new BidResponse(winningBid.getDspId(), winningBid.getBidPrice(), winningBid.getAdUrl());
}
}
在 Ad Exchange 端,当收到 DSP 竞价后,执行第一价格拍卖:
public class AdExchange {
public static void main(String[] args) {
List<BidRequest> bidRequests = List.of(
new BidRequest("DSP_A", 3.50, "https://ad-a.com"),
new BidRequest("DSP_B", 4.00, "https://ad-b.com"),
new BidRequest("DSP_C", 3.80, "https://ad-c.com")
);
// 执行第一价格拍卖
BidResponse winner = FirstPriceAuction.determineWinner(bidRequests);
System.out.println("赢家: " + winner.getDspId() + " | 付款金额: $" + winner.getBidPrice());
}
}
✅ 执行后输出:
赢家: DSP_B | 付款金额: $4.00
竞价方式 | 支付金额 | 市场影响 |
---|---|---|
第一价格拍卖 | 最高竞价者支付自己的出价 | 出价会偏保守,广告主可能出更低的价格 |
第二价格拍卖 | 最高竞价者支付「第二高出价 + 一定增量(如$0.01)」 | DSP 会大胆出价,因为不会真正支付自己出价的最高金额 |
在 实际广告投放市场中,第二价格拍卖更常用,因为它可以 减少 DSP 过度出价的风险,但近年来一些平台(如 Header Bidding)开始更多采用 第一价格拍卖 来提升广告收入。
在 DSP 竞价策略中,第一价格拍卖简单直观,但广告主通常需要结合数据分析(如 DMP)调整出价,以避免支付过高的成本!