使用信号量修复并发问题
Use a Semaphore to fix a Concurrency Issue
我最近问了一个问题:link 关于在我的 autodesk-forge Web 应用程序中存储刷新令牌的最佳方式。我目前将刷新令牌存储在只有一行和一列的 SQL 数据库中,其中包含刷新令牌。有关令牌的步骤如下:
- 当用户登录时,调用 GET 方法从数据库中检索最新的令牌。 Returndata.php 简单地连接到 SQL 数据库并从 table 检索行。获取方法代码:
function getRefreshToken() {
$.get("returndata.php",
function(response) {
var res = JSON.parse(response);
console.log(response);
console.log(res);
refreshToken = res[0].Value;
// console.log(refreshToken);
useRefresh();
// console.log(response.value);
//var times = response.times;
},
);
}
- 返回令牌并用于为用户获取访问令牌。
- 检索访问令牌时,它会附带一个刷新令牌,该令牌随后将保存在 SQL 数据库中与前一个刷新令牌相同的行中。 Savesettings.php 只需连接到数据库并使用新的刷新令牌更新单行。 POST 存储刷新令牌的方法代码:
function saveRefreshToken() {
$.post("savesettings.php",
{
Value: refreshToken,
},
function(data, status){
console.log(data);
});
}
在大多数情况下,此方法工作正常,但也有几次出现错误。我相信这是由于并发问题。如果两个用户在非常相似的时间访问数据库,数据可能会损坏或提供错误的数据。要解决这个问题,我知道我需要使用信号量,以便其他请求等到第一个请求完成后再执行。我如何使用信号量来实现这一目标?任何帮助将不胜感激,因为我非常困在这个问题上。谢谢。干杯!!
编辑
function getToken(){
$.ajax({
method: 'POST',
url: 'https://developer.api.autodesk.com/authentication/v1/gettoken',
headers: {
'Content-Type':'application/x-www-form-urlencoded'
},
data:'client_id=xxxxxxxxxxxxxxxxxxxx&client_secret=xxxxxxxxxxxxxxx&grant_type=authorization_code&code=' + code + '&redirect_uri=xxxxxxxxxxxxxxxxxxxxxx',
success:function(response){
// console.log(response);
access_token = response.access_token;
console.log(access_token);
console.log(response);
refreshToken = response.refresh_token;
saveRefreshToken()
}
})
}
function useRefresh(){
$.ajax({
method:'POST',
url: 'https://developer.api.autodesk.com/authentication/v1/refreshtoken',
headers: {
'Content-Type':'application/x-www-form-urlencoded'
},
data:'client_id=xxxxxxxxxxxxxxxxxxxxxxxxx&client_secret=xxxxxxxxxxxxxxxxx&grant_type=refresh_token&refresh_token='+refreshToken+'&scope=data:read',
success:function(response){
console.log(response);
refreshToken = response.refresh_token;
//console.log(refreshToken);
access_token = response.access_token;
saveRefreshToken();
}
})
}
如果您 运行 遇到 PHP 和 MySQL 的并发问题,信号量不是解决此问题的正确级别。例如,如果您想要 运行 第二台服务器来处理 PHP 请求怎么办。
处理此问题的正确方法是在您的数据库中。如果损坏发生在单个请求期间,可能是您的查询有误,或者您需要使用事务或锁。
Evert 仅在与 MySQL 赛车问题相关时是正确的。在您的情况下,您还调用了另一台服务器来获取访问令牌;这意味着我们需要在刷新时为 read/write 锁定 table。
我写了一个小例子here,它做了最少的事情来解决这个问题。但是,正如 Evert 所说,如果您选择信号量方法,它只能在单个服务器上处理 HTTP 请求。如果你在做负载均衡,我们就需要使用 mysql/redis 锁的方法。我说 Redis,因为在该主题上它比 mysql 更好。
该示例还将向您展示在 Forge 应用程序中获取令牌的正确方法。在上面的代码中,我注意到您正在使用 $.ajax() 代码,这意味着您正在从 HTML 页面(在客户端?)的 OAuth 服务器上进行 HTTP 调用 - 这样做会使您暴露于高安全性问题,您正在泄漏访问令牌和 clientID/secret 密钥。任何人都可以窃取您的 clientID/secret 并在您不知情的情况下开始使用您的帐户。
您应该颁发 2 个访问令牌,一个 public 具有最低权限(这个仅供查看者使用)。具有额外权限的第二个令牌将保留在服务器上,永远不会暴露给任何客户端应用程序。
该示例执行 2 个简单操作:
- 使用 public 标记在查看器中显示模型
- 使用内部令牌列出存储桶或文件夹中的模型
该示例还显示了 2 条腿的流程和 3 条腿的代码流程。虽然理论上两者的工作方式应该相同,但有一个重要的区别需要了解。
您可以随时生成任意数量的 2 条腿令牌。这意味着您将始终获得有效令牌,即使您同时多次使用令牌也是如此。实际上,您不刷新令牌,每次都创建一个新令牌,这样做不会使先前生成的令牌无效。该示例有点懒惰,只要需要就立即获取令牌,而不会打扰服务器处理的 HTTP 请求。
虽然您也可以生成任意多的三足令牌(代码流),但每次创建新令牌时都需要登录这一事实会限制您的使用。但是,当您创建访问令牌时,您会获得一个刷新令牌,它可以让您在每次需要时更新 access_token。但是,刷新令牌有 3 个注意事项:
- 刷新令牌的有效期只有 14 天,因此您至少需要每 14 天刷新一次令牌。
- 当您第一次创建令牌时,您需要提供您可能需要的所有范围。刷新令牌时,您可以降级范围,但永远无法添加新范围。
- 每当您使用刷新令牌时,这个都会丢失,但您会得到一个新的。最后一个令牌是您接下来需要使用的令牌。
要使用 2legged 版本,只需进入服务器的根目录,例如 http://localhost/
要使用 3legged 版本,请转到 http:///localhost/login 创建一对新的令牌,否则请转到 http:///localhost/www/bim360.html
还有一个小测试示例,可以在您的服务器上同时触发 5 个 HTTP 请求。我限制我的 Apache 实例一次处理 2 个请求,这是为了验证在更新令牌时,所有请求不会相互竞争,这会使您的 token/refresh 令牌不同步。
readme 应该有足够的详细信息来部署示例和浏览代码。
我最近问了一个问题:link
- 当用户登录时,调用 GET 方法从数据库中检索最新的令牌。 Returndata.php 简单地连接到 SQL 数据库并从 table 检索行。获取方法代码:
function getRefreshToken() {
$.get("returndata.php",
function(response) {
var res = JSON.parse(response);
console.log(response);
console.log(res);
refreshToken = res[0].Value;
// console.log(refreshToken);
useRefresh();
// console.log(response.value);
//var times = response.times;
},
);
}
- 返回令牌并用于为用户获取访问令牌。
- 检索访问令牌时,它会附带一个刷新令牌,该令牌随后将保存在 SQL 数据库中与前一个刷新令牌相同的行中。 Savesettings.php 只需连接到数据库并使用新的刷新令牌更新单行。 POST 存储刷新令牌的方法代码:
function saveRefreshToken() {
$.post("savesettings.php",
{
Value: refreshToken,
},
function(data, status){
console.log(data);
});
}
在大多数情况下,此方法工作正常,但也有几次出现错误。我相信这是由于并发问题。如果两个用户在非常相似的时间访问数据库,数据可能会损坏或提供错误的数据。要解决这个问题,我知道我需要使用信号量,以便其他请求等到第一个请求完成后再执行。我如何使用信号量来实现这一目标?任何帮助将不胜感激,因为我非常困在这个问题上。谢谢。干杯!!
编辑
function getToken(){
$.ajax({
method: 'POST',
url: 'https://developer.api.autodesk.com/authentication/v1/gettoken',
headers: {
'Content-Type':'application/x-www-form-urlencoded'
},
data:'client_id=xxxxxxxxxxxxxxxxxxxx&client_secret=xxxxxxxxxxxxxxx&grant_type=authorization_code&code=' + code + '&redirect_uri=xxxxxxxxxxxxxxxxxxxxxx',
success:function(response){
// console.log(response);
access_token = response.access_token;
console.log(access_token);
console.log(response);
refreshToken = response.refresh_token;
saveRefreshToken()
}
})
}
function useRefresh(){
$.ajax({
method:'POST',
url: 'https://developer.api.autodesk.com/authentication/v1/refreshtoken',
headers: {
'Content-Type':'application/x-www-form-urlencoded'
},
data:'client_id=xxxxxxxxxxxxxxxxxxxxxxxxx&client_secret=xxxxxxxxxxxxxxxxx&grant_type=refresh_token&refresh_token='+refreshToken+'&scope=data:read',
success:function(response){
console.log(response);
refreshToken = response.refresh_token;
//console.log(refreshToken);
access_token = response.access_token;
saveRefreshToken();
}
})
}
如果您 运行 遇到 PHP 和 MySQL 的并发问题,信号量不是解决此问题的正确级别。例如,如果您想要 运行 第二台服务器来处理 PHP 请求怎么办。
处理此问题的正确方法是在您的数据库中。如果损坏发生在单个请求期间,可能是您的查询有误,或者您需要使用事务或锁。
Evert 仅在与 MySQL 赛车问题相关时是正确的。在您的情况下,您还调用了另一台服务器来获取访问令牌;这意味着我们需要在刷新时为 read/write 锁定 table。
我写了一个小例子here,它做了最少的事情来解决这个问题。但是,正如 Evert 所说,如果您选择信号量方法,它只能在单个服务器上处理 HTTP 请求。如果你在做负载均衡,我们就需要使用 mysql/redis 锁的方法。我说 Redis,因为在该主题上它比 mysql 更好。
该示例还将向您展示在 Forge 应用程序中获取令牌的正确方法。在上面的代码中,我注意到您正在使用 $.ajax() 代码,这意味着您正在从 HTML 页面(在客户端?)的 OAuth 服务器上进行 HTTP 调用 - 这样做会使您暴露于高安全性问题,您正在泄漏访问令牌和 clientID/secret 密钥。任何人都可以窃取您的 clientID/secret 并在您不知情的情况下开始使用您的帐户。
您应该颁发 2 个访问令牌,一个 public 具有最低权限(这个仅供查看者使用)。具有额外权限的第二个令牌将保留在服务器上,永远不会暴露给任何客户端应用程序。
该示例执行 2 个简单操作:
- 使用 public 标记在查看器中显示模型
- 使用内部令牌列出存储桶或文件夹中的模型
该示例还显示了 2 条腿的流程和 3 条腿的代码流程。虽然理论上两者的工作方式应该相同,但有一个重要的区别需要了解。
您可以随时生成任意数量的 2 条腿令牌。这意味着您将始终获得有效令牌,即使您同时多次使用令牌也是如此。实际上,您不刷新令牌,每次都创建一个新令牌,这样做不会使先前生成的令牌无效。该示例有点懒惰,只要需要就立即获取令牌,而不会打扰服务器处理的 HTTP 请求。
虽然您也可以生成任意多的三足令牌(代码流),但每次创建新令牌时都需要登录这一事实会限制您的使用。但是,当您创建访问令牌时,您会获得一个刷新令牌,它可以让您在每次需要时更新 access_token。但是,刷新令牌有 3 个注意事项:
- 刷新令牌的有效期只有 14 天,因此您至少需要每 14 天刷新一次令牌。
- 当您第一次创建令牌时,您需要提供您可能需要的所有范围。刷新令牌时,您可以降级范围,但永远无法添加新范围。
- 每当您使用刷新令牌时,这个都会丢失,但您会得到一个新的。最后一个令牌是您接下来需要使用的令牌。
要使用 2legged 版本,只需进入服务器的根目录,例如 http://localhost/
要使用 3legged 版本,请转到 http:///localhost/login 创建一对新的令牌,否则请转到 http:///localhost/www/bim360.html
还有一个小测试示例,可以在您的服务器上同时触发 5 个 HTTP 请求。我限制我的 Apache 实例一次处理 2 个请求,这是为了验证在更新令牌时,所有请求不会相互竞争,这会使您的 token/refresh 令牌不同步。
readme 应该有足够的详细信息来部署示例和浏览代码。