我的Google Apps脚本部署为网络应用,任何用户都可以访问。它的功能是打开和更改该文档中的文本。
我发送脚本document ID
作为查询参数,如下所示:
https://script.google.com/a/macros/s/AKfycbzCP...TnwHUbXxzDM/exec?docId=1_cMN0nuJadBw6QVjKtdEA6eXhE8ubIoxIJai1ticxnE`
Web应用程序打开文档并更改文档中的文本。
function doGet(e){
var params=e.parameters;
var doc = DocumentApp.openById(params['docId']);
...
/* change text of the document */
}
问题
现在,当有多个用户试图同时在同一文档上运行应用程序脚本时,Web应用程序无法处理并发和功能中断。
我查看了Lock Service但锁定服务的document lock仅适用于容器绑定脚本,而不适用于网络应用。
然后我尝试使用cache service var cache = CacheService.getDocumentCache();
和property service var documentProperties = PropertiesService.getDocumentProperties();
为文档设置属性,但文档属性和文档缓存在Web应用中返回null
仅限于容器绑定脚本,如documentation:
如果在包含的上下文之外调用此方法 文档(例如来自独立脚本或Web应用程序),此方法 返回null。
当Google Apps脚本部署为Web应用程序时,是否有任何方法可以处理文档中脚本执行的并发性。 (不是容器绑定)
答案 0 :(得分:2)
正如@azawaza指出的那样,你应该使用具有适当范围的Lock,并且Script Lock更适合你的场景。这在Does this code lock the onFormSubmit(e) method correctly?
中讨论如果代码的关键部分足够快,那么在对文档1的另一次更新继续进行时,没有真正关心使用户更新文档2等待;他们不会等很久。类似的东西:
function doGet1(e){
// Perform any "pre" operations on private
// or non-critical shared resources.
var params=e.parameters;
// Get a script lock, because we're about to modify a shared resource.
var lock = LockService.getScriptLock();
// Wait for up to 10 seconds for other processes to finish.
lock.waitLock(10000);
////// Critical section begins vvvvv
var doc = DocumentApp.openById(params['docId']);
// change text of the document here
doc.saveAndClose();
////// Critical section ends ^^^^^
lock.releaseLock();
// Continue with operations on private
// or non-critical shared resources.
return ContentService.createTextOutput("Document updated.")
}
Google Apps脚本锁服务开箱即用,旨在保护代码的关键部分。如果我们想要控制对特定资源(可能是长时间)的访问(例如Google文档),我们可以通过更改我们的内容来修改它"锁定"。
在此示例中,Lock服务保护检查和更新“脚本属性”的关键部分。这些属性有"键"符合我们的docId
参数;价值并不重要,因为我们可以使用简单的密钥存在作为我们的测试。
注意:目前,此脚本可能会永久阻止用户" (如果脚本超时),如果另一个脚本无法删除保护其共享文档使用的属性。您希望在生产代码中更加小心。
function doGet2(e){
// Perform any "pre" operations on private
// or non-critical shared resources.
var params=e.parameters;
// Wait for exclusive access to docId
var ready = false;
// Get a script lock, because we're about to modify a shared resource.
var lock = LockService.getScriptLock();
while (!ready) {
// Wait for up to 1 second for other processes to finish.
if (lock.tryLock(1000)) {
////// Critical section begins vvvvv
var properties = PropertiesService.getScriptProperties();
// If nobody has "locked" this document, lock it; we're ready.
if (properties.getProperty(docId) == null) {
// Set a property with key=docId.
properties.setProperty(docId,"Locked");
}
////// Critical section ends ^^^^^
lock.releaseLock();
}
}
// We have exclusive access to docId now.
var doc = DocumentApp.openById(params['docId']);
// change text of the document here
doc.saveAndClose();
// Delete the "key" for this document, so others can access it.
properties.deleteProperty(docId);
return ContentService.createTextOutput("Document updated.")
}
我们在前一个示例中使用的逻辑可以封装到Object中,以提供更优雅的界面。事实上,布鲁斯麦克弗森用cNamedLock Library完成了他的Desktop Liberation site。使用该库,您可以实现特定于文档的锁定:
function doGet3(e){
// Perform any "pre" operations on private
// or non-critical shared resources.
var params=e.parameters;
// Get a named lock.
var namedLock = new NamedLock().setKey(docId);
namedLock.lock();
////// Critical section begins vvvvv
// We have exclusive access to docId now.
var doc = DocumentApp.openById(params['docId']);
// change text of the document here
doc.saveAndClose();
////// Critical section ends ^^^^^
namedLock.unlock();
return ContentService.createTextOutput("Document updated.")
}