Joe Gregorio | 05375c0 | 2011-03-29 16:29:37 -0400 | [diff] [blame] | 1 | # Copyright (C) 2010 Google Inc. |
| 2 | # |
| 3 | # Licensed under the Apache License, Version 2.0 (the "License"); |
| 4 | # you may not use this file except in compliance with the License. |
| 5 | # You may obtain a copy of the License at |
| 6 | # |
| 7 | # http://www.apache.org/licenses/LICENSE-2.0 |
| 8 | # |
| 9 | # Unless required by applicable law or agreed to in writing, software |
| 10 | # distributed under the License is distributed on an "AS IS" BASIS, |
| 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| 12 | # See the License for the specific language governing permissions and |
| 13 | # limitations under the License. |
| 14 | """Sample for threading and queues. |
| 15 | |
| 16 | A simple sample that processes many requests by constructing a threadpool and |
| 17 | passing client requests by a thread queue to be processed. |
| 18 | """ |
Joe Gregorio | fffa7d7 | 2011-02-18 17:20:39 -0500 | [diff] [blame] | 19 | from apiclient.discovery import build |
| 20 | from apiclient.errors import HttpError |
Joe Gregorio | 05375c0 | 2011-03-29 16:29:37 -0400 | [diff] [blame] | 21 | from oauth2client.file import Storage |
| 22 | from oauth2client.client import OAuth2WebServerFlow |
| 23 | from oauth2client.tools import run |
Joe Gregorio | 0c73a67 | 2010-10-14 08:27:59 -0400 | [diff] [blame] | 24 | |
| 25 | import Queue |
Joe Gregorio | 05375c0 | 2011-03-29 16:29:37 -0400 | [diff] [blame] | 26 | import gflags |
Joe Gregorio | 0c73a67 | 2010-10-14 08:27:59 -0400 | [diff] [blame] | 27 | import httplib2 |
Joe Gregorio | 05375c0 | 2011-03-29 16:29:37 -0400 | [diff] [blame] | 28 | import logging |
| 29 | import sys |
Joe Gregorio | 0c73a67 | 2010-10-14 08:27:59 -0400 | [diff] [blame] | 30 | import threading |
| 31 | import time |
| 32 | |
Joe Gregorio | 05375c0 | 2011-03-29 16:29:37 -0400 | [diff] [blame] | 33 | # How many threads to start. |
| 34 | NUM_THREADS = 3 |
Joe Gregorio | 0c73a67 | 2010-10-14 08:27:59 -0400 | [diff] [blame] | 35 | |
Joe Gregorio | 05375c0 | 2011-03-29 16:29:37 -0400 | [diff] [blame] | 36 | # A list of URLs to shorten. |
| 37 | BULK = [ |
| 38 | "https://code.google.com/apis/buzz/", |
| 39 | "https://code.google.com/apis/moderator/", |
| 40 | "https://code.google.com/apis/latitude/", |
| 41 | "https://code.google.com/apis/urlshortener/", |
| 42 | "https://code.google.com/apis/customsearch/", |
| 43 | "https://code.google.com/apis/shopping/search/", |
| 44 | "https://code.google.com/apis/predict", |
| 45 | "https://code.google.com/more", |
| 46 | ] |
Joe Gregorio | 0c73a67 | 2010-10-14 08:27:59 -0400 | [diff] [blame] | 47 | |
Joe Gregorio | 05375c0 | 2011-03-29 16:29:37 -0400 | [diff] [blame] | 48 | FLAGS = gflags.FLAGS |
| 49 | FLOW = OAuth2WebServerFlow( |
| 50 | client_id='433807057907.apps.googleusercontent.com', |
| 51 | client_secret='jigtZpMApkRxncxikFpR+SFg', |
| 52 | scope='https://www.googleapis.com/auth/urlshortener', |
| 53 | user_agent='urlshortener-cmdline-sample/1.0') |
| 54 | |
| 55 | gflags.DEFINE_enum('logging_level', 'ERROR', |
| 56 | ['DEBUG', 'INFO', 'WARNING', 'ERROR', 'CRITICAL'], |
| 57 | 'Set the level of logging detail.') |
Joe Gregorio | 0c73a67 | 2010-10-14 08:27:59 -0400 | [diff] [blame] | 58 | |
| 59 | queue = Queue.Queue() |
| 60 | |
| 61 | |
| 62 | class Backoff: |
| 63 | """Exponential Backoff |
| 64 | |
| 65 | Implements an exponential backoff algorithm. |
Joe Gregorio | 05375c0 | 2011-03-29 16:29:37 -0400 | [diff] [blame] | 66 | Instantiate and call loop() each time through |
| 67 | the loop, and each time a request fails call |
| 68 | fail() which will delay an appropriate amount |
| 69 | of time. |
Joe Gregorio | 0c73a67 | 2010-10-14 08:27:59 -0400 | [diff] [blame] | 70 | """ |
Joe Gregorio | af276d2 | 2010-12-09 14:26:58 -0500 | [diff] [blame] | 71 | |
Joe Gregorio | 0c73a67 | 2010-10-14 08:27:59 -0400 | [diff] [blame] | 72 | def __init__(self, maxretries=8): |
| 73 | self.retry = 0 |
| 74 | self.maxretries = maxretries |
| 75 | self.first = True |
| 76 | |
| 77 | def loop(self): |
| 78 | if self.first: |
| 79 | self.first = False |
| 80 | return True |
| 81 | else: |
| 82 | return self.retry < self.maxretries |
| 83 | |
| 84 | def fail(self): |
| 85 | self.retry += 1 |
Joe Gregorio | af276d2 | 2010-12-09 14:26:58 -0500 | [diff] [blame] | 86 | delay = 2 ** self.retry |
Joe Gregorio | 0c73a67 | 2010-10-14 08:27:59 -0400 | [diff] [blame] | 87 | time.sleep(delay) |
| 88 | |
| 89 | |
| 90 | def start_threads(credentials): |
Joe Gregorio | 05375c0 | 2011-03-29 16:29:37 -0400 | [diff] [blame] | 91 | """Create the thread pool to process the requests.""" |
Joe Gregorio | 0c73a67 | 2010-10-14 08:27:59 -0400 | [diff] [blame] | 92 | |
Joe Gregorio | 05375c0 | 2011-03-29 16:29:37 -0400 | [diff] [blame] | 93 | def process_requests(n): |
Joe Gregorio | 0c73a67 | 2010-10-14 08:27:59 -0400 | [diff] [blame] | 94 | http = httplib2.Http() |
| 95 | http = credentials.authorize(http) |
Joe Gregorio | 05375c0 | 2011-03-29 16:29:37 -0400 | [diff] [blame] | 96 | loop = True |
Joe Gregorio | 0c73a67 | 2010-10-14 08:27:59 -0400 | [diff] [blame] | 97 | |
Joe Gregorio | 05375c0 | 2011-03-29 16:29:37 -0400 | [diff] [blame] | 98 | |
| 99 | while loop: |
Joe Gregorio | 0c73a67 | 2010-10-14 08:27:59 -0400 | [diff] [blame] | 100 | request = queue.get() |
| 101 | backoff = Backoff() |
| 102 | while backoff.loop(): |
| 103 | try: |
Joe Gregorio | 05375c0 | 2011-03-29 16:29:37 -0400 | [diff] [blame] | 104 | response = request.execute(http) |
| 105 | print "Processed: %s in thread %d" % (response['id'], n) |
Joe Gregorio | 0c73a67 | 2010-10-14 08:27:59 -0400 | [diff] [blame] | 106 | break |
| 107 | except HttpError, e: |
| 108 | if e.resp.status in [402, 403, 408, 503, 504]: |
| 109 | print "Increasing backoff, got status code: %d" % e.resp.status |
| 110 | backoff.fail() |
Joe Gregorio | 05375c0 | 2011-03-29 16:29:37 -0400 | [diff] [blame] | 111 | except Exception, e: |
| 112 | print "Unexpected error. Exiting." + str(e) |
| 113 | loop = False |
Joe Gregorio | fffa7d7 | 2011-02-18 17:20:39 -0500 | [diff] [blame] | 114 | break |
Joe Gregorio | 0c73a67 | 2010-10-14 08:27:59 -0400 | [diff] [blame] | 115 | |
| 116 | print "Completed request" |
| 117 | queue.task_done() |
| 118 | |
Joe Gregorio | fffa7d7 | 2011-02-18 17:20:39 -0500 | [diff] [blame] | 119 | |
Joe Gregorio | 0c73a67 | 2010-10-14 08:27:59 -0400 | [diff] [blame] | 120 | for i in range(NUM_THREADS): |
Joe Gregorio | 05375c0 | 2011-03-29 16:29:37 -0400 | [diff] [blame] | 121 | t = threading.Thread(target=process_requests, args=[i]) |
Joe Gregorio | 0c73a67 | 2010-10-14 08:27:59 -0400 | [diff] [blame] | 122 | t.daemon = True |
| 123 | t.start() |
| 124 | |
Joe Gregorio | 0c73a67 | 2010-10-14 08:27:59 -0400 | [diff] [blame] | 125 | |
Joe Gregorio | 05375c0 | 2011-03-29 16:29:37 -0400 | [diff] [blame] | 126 | def main(argv): |
| 127 | try: |
| 128 | argv = FLAGS(argv) |
| 129 | except gflags.FlagsError, e: |
| 130 | print '%s\\nUsage: %s ARGS\\n%s' % (e, argv[0], FLAGS) |
| 131 | sys.exit(1) |
| 132 | |
| 133 | logging.getLogger().setLevel(getattr(logging, FLAGS.logging_level)) |
| 134 | |
| 135 | storage = Storage('threadqueue.dat') |
Joe Gregorio | fffa7d7 | 2011-02-18 17:20:39 -0500 | [diff] [blame] | 136 | credentials = storage.get() |
| 137 | if credentials is None or credentials.invalid == True: |
Joe Gregorio | 05375c0 | 2011-03-29 16:29:37 -0400 | [diff] [blame] | 138 | credentials = run(FLOW, storage) |
Joe Gregorio | 0c73a67 | 2010-10-14 08:27:59 -0400 | [diff] [blame] | 139 | |
| 140 | start_threads(credentials) |
| 141 | |
| 142 | http = httplib2.Http() |
| 143 | http = credentials.authorize(http) |
| 144 | |
Joe Gregorio | 05375c0 | 2011-03-29 16:29:37 -0400 | [diff] [blame] | 145 | service = build("urlshortener", "v1", http=http, |
| 146 | developerKey="AIzaSyDRRpR3GS1F1_jKNNM9HCNd2wJQyPG3oN0") |
| 147 | shortener = service.url() |
Joe Gregorio | 0c73a67 | 2010-10-14 08:27:59 -0400 | [diff] [blame] | 148 | |
Joe Gregorio | 05375c0 | 2011-03-29 16:29:37 -0400 | [diff] [blame] | 149 | for url in BULK: |
| 150 | body = {"longUrl": url } |
| 151 | shorten_request = shortener.insert(body=body) |
| 152 | print "Adding request to queue" |
| 153 | queue.put(shorten_request) |
Joe Gregorio | 0c73a67 | 2010-10-14 08:27:59 -0400 | [diff] [blame] | 154 | |
Joe Gregorio | 05375c0 | 2011-03-29 16:29:37 -0400 | [diff] [blame] | 155 | # Wait for all the requests to finish |
Joe Gregorio | 0c73a67 | 2010-10-14 08:27:59 -0400 | [diff] [blame] | 156 | queue.join() |
| 157 | |
| 158 | |
| 159 | if __name__ == "__main__": |
Joe Gregorio | 05375c0 | 2011-03-29 16:29:37 -0400 | [diff] [blame] | 160 | main(sys.argv) |