How to write an ad server connector - datacratic/rtbkit GitHub Wiki
In this document, we'll write a basic ad server connector that sits between the stream of wins and rest of the stack. This component can be extended to support other kind of events. Those events are normally associated with a bid on an impression that resulted in a win. For example:
- WIN
- IMPRESSION
- CLICK
This code is used in the demo stack to provide support to the mock exchange.
The annotated source code for the full ad server connector that we will present in this example is available in rtbkit's repository.
Ad server connector
In general, to build an ad server connector, you need to extend the AdServerConnector
class. The main use of this class is to provide a common interface to publish events to the post auction loop using:
publishWin
publishCampaignEvent
Note that there is also a publishLoss
for tests since loss are implicit when no win message was received for some time.
Here, we'll inherit from HttpAdServerConnector
instead of directly from AdServerConnector
to reuse the build-in HTTP interface that most ad server will require. In addition to that, we'll publish events on a generic endpoint so that the data logger can connect to it.
So, let's begin with the class definition:
struct MockAdServerConnector : public HttpAdServerConnector
{
/// Generic publishing endpoint to forward wins to anyone registered. Currently, there's only the
/// logger that connects to this.
Datacratic::ZmqNamedPublisher publisher;
Then, let's construct all those pretty things:
MockAdServerConnector(const std::string& serviceName,
std::shared_ptr<Datacratic::ServiceProxies> proxies)
: HttpAdServerConnector(serviceName, proxies),
publisher(getServices()->zmqContext) {
}
Now, most of the work is done in the setup. The HttpAdServerConnector
easily supports multiple incoming sources.
Here, we will only handle win events received directly from the mock exchange. And since the mock exchange really send serialized post auction events in JSON, we cheat a little and reuse the JSON parser that already exists for that.
So, we can simply bind the incoming port to the handleEvent
function like so:
void init(int port) {
auto services = getServices();
// Initialize our base class
HttpAdServerConnector::init(services->config);
// Prepare a simple JSON handler that already parsed the incoming HTTP payload so that it can
// create the requied post auction object.
auto handleEvent = [&](const Datacratic::HttpHeader & header,
const Json::Value & json,
const std::string & text) {
this->handleEvent(PostAuctionEvent(json));
};
registerEndpoint(port, handleEvent);
// Publish the endpoint now that it exists.
HttpAdServerConnector::bindTcp();
Then, to provide an endpoint that the logger can connect to, we'll simply publish our service. This will endup in ZooKeeper so that the usual discovery mechanism can kick in.
// And initialize the generic publisher on a predefined range of ports to try avoiding that
// collision between different kind of service occurs.
publisher.init(services->config, serviceName() + "/logger");
publisher.bindTcp(services->ports->getRange("adServer.logger"));
}
Lastly, we need the piece of code to actually handle the event. We simply publish the win to the post auction loop. Then, that message is forward to the publisher.
void handleEvent(PostAuctionEvent const & event) {
if(event.type == PAE_WIN) {
publishWin(event.auctionId,
event.adSpotId,
event.winPrice,
event.timestamp,
Json::Value(),
event.uids,
event.account,
Date::now());
Date now = Date::now();
publisher.publish("WIN", now.print(3), event.auctionId.toString(), event.winPrice.toString(), "0");
}
}
And you're done.