问题
In my Python3 program that uses the Gmail API, I want to re-instantiate the Gmail "service" instance every few hours for the purpose of enhanced reliability. I am not certain if this is required to better reliability, but I thought it would be useful. So, when I start my program, I run the below function, which as you can see, builds the Gmail "service" instance. Then, after a few hours, I run the same code again. However, this results in socker.timeout errors. I know this because the socket.timeout errors occur every 6 hours in my log file, which is how often I re-initialize.
Why does this occur? Do I somehow need to disconnect my existing service, before reconnecting again? I did not find anything in the Gmail API Documentation or in other forum posts.
My other question is that do I even need to re-instantiate the service? Will the service instance be valid forever? (My program is intended to be continuous and run forever without touching it)
def gmailAPIInitialize(self):
try:
self.waitForInternet()
logging.info("Initializing the Gmail API Service")
creds = None
# The file token.pickle stores the user's access and refresh tokens, and is
# created automatically when the authorization flow completes for the first
# time.
if os.path.exists('token.pickle'):
with open('token.pickle', 'rb') as token:
creds = pickle.load(token)
# If there are no (valid) credentials available, let the user log in.
if not creds or not creds.valid:
if creds and creds.expired and creds.refresh_token:
creds.refresh(Request())
with open('token.pickle', 'wb') as token:
pickle.dump(creds, token)
else:
logging.error("Unable to find token.pickle file to initialize Gmail Service. Please provide 'token.pickle' file in current directory")
sys.exit(-1)
#flow = InstalledAppFlow.from_client_secrets_file(
#'credentials.json', SCOPES)
#creds = flow.run_local_server(port=0)
# Save the credentials for the next run
service = build('gmail', 'v1', credentials=creds, cache_discovery=False)
self.gmailAPIService = service
logging.info("Successfully initialized the Gmail API Service")
return True
except:
logging.error("An error was encountered while attempting to initialize the Gmail API")
tb = traceback.format_exc()
logging.exception(tb)
return False
Re-initializing code:
currentTime = datetime.datetime.now()
if currentTime > nextRefresh:
logging.info("Refreshing Gmail Connection")
nextRefresh = currentTime + timeDeltaResult
reinitalized = self.gmailAPIInitialize()
if reinitalized is True:
logging.error("Successfully Refreshed Gmail connection")
else:
logging.error("Failed to Refresh Gmail connection")
logging.info("Next Refresh time is at (" + nextRefresh.strftime("%d %b %Y %H:%M:%S") +")")
#RUN MY CALLS AS USUAL TO GMAIL SERVICE
回答1:
Answers:
socket.timeout
errors occur when the timeout window is exceeded when waiting on individual packets of a response.- In order to keep a connection open, you need to use a service account.
More Information:
The socket timeout error occurs when the time before the receipt of an expected new packet is exceeded before a packet is actually received. As this occurs only for open/continuous connections, if you aren't making requests frequently enough to the Gmail API then it is likely that this is the origin of the error. I'm not sure how often this is, but given your refresh is every 6 hours I would expect this is longer than the token's lifetime.
The connection to the server that you establish will provide you an authorisation token, which has a limited lifespan before it needs to be renewed. The refresh token you receive however, could expire under certain circumstances, for example if you have multiple instances of the application running in parallel.
According to the documentation:
Access tokens periodically expire and become invalid credentials for a related API request. You can refresh an access token without prompting the user for permission (including when the user is not present) if you requested offline access to the scopes associated with the token.
and:
Requesting offline access is a requirement for any application that needs to access a Google API when the user is not present. For example, an app that performs backup services or executes actions at predetermined times needs to be able to refresh its access token when the user is not present
So in order to keep your refresh token valid, you need to set the access_type
keyword argument to offline
when calling the authorisation flow.
In python:
authorization_url, state = flow.authorization_url(
# Enable offline access so that you can refresh an access token without
# re-prompting the user for permission. Recommended for web server apps.
access_type='offline',
# Enable incremental authorization. Recommended as a best practice.
include_granted_scopes='true')
More information about refreshing access tokens without re-prompting users for permissions can be read about here
Service Accounts:
Service Accounts, however, do not need to re-obtain tokens to maintain access. You can either give the service account the scopes needed to run your application, or it can impersonate a user and run the required application services this way. Google has pretty thorough documentation for service accounts, the links which I think will be of help to you can be found below.
I hope this is helpful to you!
References:
- Understanding service accounts | Cloud IAM Documentation
- Service accounts | Cloud IAM Documentation
- Using OAuth2 for Web Server Applications
- Refreshing an access token (offline access)
Related Questions:
- ConnectionTimeout vs SocketTimeout
Google API: How to increase access token expiration date?
How to Auth to Google Cloud using Service Account in Python?
- Can't authenticate Google service account to Gmail
来源:https://stackoverflow.com/questions/60778266/how-to-disconnect-gmail-api-service-instance