Example: Query Remap Plugin

The sample remap plugin, query_remap.c, maps client requests to a number of servers based on a hash of the request’s URL query parameter. This can be useful for spreading load for a given type of request among backend servers, while still maintaining “stickiness” to a single server for similar requests. For example, a search engine may want to send repeated queries for the same keywords to a server that has likely cached the result from a prior query.

Configuration of query_remap

The query remap plugin will allow the query parameter name to be specified, along with the hostnames of the servers to hash across. Sample remap.config rules using query_remap will look like:

  1. map http://www.example.com/search http://srch1.example.com/search @plugin=query_remap.so @pparam=q @pparam=srch1.example.com @pparam=srch2.example.com @pparam=srch3.example.com
  2. map http://www.example.com/profiles http://prof1.example.com/profiles @plugin=query_remap.so @pparam=user_id @pparam=prof1.example.com @pparam=prof2.example.com

The first @pparam specifies the query param key for which the value will be hashed. The remaining parameters list the hostnames of the servers. A request for http://www.example.com/search?q=apache will match the first rule. The plugin will look for the ``q`` parameter and hash the value ‘apache’ to pick from among srch_[1-3]_.example.com to send the request.

If the request does not include a ``q`` query parameter and the plugin decides not to modify the request, the default toURL ‘http://srch1.example.com/search’ will be used by TS.

The parameters are passed to the plugin’s tsremap_new_instance function. In query_remap, tsremap_new_instance creates a plugin-defined query_remap_info struct to store its configuration parameters. The ihandle, an opaque pointer that can be used to pass per-instance data, is set to this struct pointer and will be passed to the tsremap_remap function when it is triggered for a request.

  1. typedef struct _query_remap_info {
  2. char *param_name;
  3. size_t param_len;
  4. char **hosts;
  5. int num_hosts;
  6. } query_remap_info;
  7. int tsremap_new_instance(int argc,char *argv[],ihandle *ih,char *errbuf,int errbuf_size)
  8. {
  9. int i;
  10. if (argc param_name = strdup(argv[2]);
  11. qri->param_len = strlen(qri->param_name);
  12. qri->num_hosts = argc - 3;
  13. qri->hosts = (char**) TSmalloc(qri->num_hosts*sizeof(char*));
  14. for (i=0; i num_hosts; ++i) {
  15. qri->hosts[i] = strdup(argv[i+3]);
  16. }
  17. *ih = (ihandle)qri;
  18. return 0;
  19. }

Another way remap plugins may want handle more complex configuration is to specify a configuration filename as a pparam and parse the specified file during instance initialization.

Performing the Remap

The plugin implements the tsremap_remap function, which is called when TS has read the client HTTP request headers and matched the request to a remap rule configured for the plugin. The TSRemapRequestInfo struct contains input and output members for the remap operation.

tsremap_remap uses the configuration information passed via the ihandle and checks the request_query for the configured query parameter. If the parameter is found, the plugin sets a new_host to modify the request host:

  1. int tsremap_remap(ihandle ih, rhandle rh, TSRemapRequestInfo *rri)
  2. {
  3. int hostidx = -1;
  4. query_remap_info *qri = (query_remap_info*)ih;
  5. if (!qri) {
  6. TSError("[remap] NULL ihandle");
  7. return 0;
  8. }
  9. if (rri && rri->request_query && rri->request_query_size > 0) {
  10. char *q, *s, *key;
  11. //make a copy of the query, as it is read only
  12. q = (char*) TSmalloc(rri->request_query_size+1);
  13. strncpy(q, rri->request_query, rri->request_query_size);
  14. q[rri->request_query_size] = '\0';
  15. s = q;
  16. //parse query parameters
  17. for (key = strsep(&s, "&"); key != NULL; key = strsep(&s, "&")) {
  18. char *val = strchr(key, '=');
  19. if (val && (size_t)(val-key) == qri->param_len &&
  20. !strncmp(key, qri->param_name, qri->param_len)) {
  21. ++val;
  22. //the param key matched the configured param_name
  23. //hash the param value to pick a host
  24. hostidx = hash_fnv32(val, strlen(val)) % (uint32_t)qri->num_hosts;
  25. break;
  26. }
  27. }
  28. TSfree(q);
  29. if (hostidx >= 0) {
  30. rri->new_host_size = strlen(qri->hosts[hostidx]);
  31. if (rri->new_host_size new_host, qri->hosts[hostidx], rri->new_host_size);
  32. return 1; //host has been modified
  33. }
  34. }
  35. }
  36. //the request was not modified, TS will use the toURL from the remap rule
  37. return 0;
  38. }