gcs新的发展趋势
它发生在我们所有人身上。 我们开发了无状态应用程序,可以不费力地水平扩展。但是,有时会出现需要进行某种协调的情况。
您可以在此方面真正地前进。 例如,您可以使用Akka之类的框架及其集群功能。 或者,您也可以像简单地自己滚动机制一样简单,只要它可以为您提供所需的结果即可。 另外,根据您需要他们进行的工作,您可以拥有不同的节点组。 选项和解决方案可以根据问题进行更改。
如果您的问题可以通过一个简单的选项解决,那么在您使用Google Cloud Storage的情况下,一种解决方法是使用其锁定功能。例如,假设有一个4个节点的场景,它们确实会动态扩展,但是每当一个新节点注册时,您想要通过获取一个唯一的配置来更改其操作,该配置不会与另一个节点可能收到的配置发生冲突。
该策略可以是使用Google Cloud Storage上的文件进行锁定,以及充当集中配置注册表的文件。
锁定文件不过是应在云存储上创建和删除的文件。 赋予我们锁定能力的是GCS上仅当文件不存在时才创建文件的选项。因此,来自一个节点的进程将尝试创建“锁”文件,此操作等同于获取锁。一旦该过程完成,将删除文件,此操作等同于释放锁定。同时,其他进程将尝试创建文件(获取锁)并失败(文件已存在),因为其他进程已创建文件。同时,成功创建文件(获取锁)的过程将更改集中式配置注册表,完成后将删除文件(释放锁)。
因此,让我们从锁定对象开始。
package com.gkatzioura.gcs.lock;
import java.util.Optional;
import com.google.cloud.storage.Blob;
import com.google.cloud.storage.BlobInfo;
import com.google.cloud.storage.Storage;
import com.google.cloud.storage.StorageException;
public class GCSLock {
public static final String LOCK_STRING = "_lock" ;
private final Storage storage;
private final String bucket;
private final String keyName;
private Optional<Blob> acquired = Optional.empty();
GCSLock(Storage storage, String bucket, String keyName) {
this .storage = storage;
this .bucket = bucket;
this .keyName = keyName;
}
public boolean acquire() {
try {
var blobInfo = BlobInfo.newBuilder(bucket, keyName).build();
var blob = storage.create(blobInfo, LOCK_STRING.getBytes(), Storage.BlobTargetOption.doesNotExist());
acquired = Optional.of(blob);
return true ;
} catch (StorageException storageException) {
return false ;
}
}
public void release() {
if (!acquired.isPresent()) {
throw new IllegalStateException( "Lock was never acquired" );
}
storage.delete(acquired.get().getBlobId());
}
}
如您所见,write指定仅当对象不存在时才写入对象。 幕后该操作使用x-goog-如果代匹配头,其用于对并发。因此,一个节点将能够获取锁并更改配置文件。之后,它可以删除锁。 如果引发异常,则操作可能会失败并且该锁已被获取。
为了使示例更完整,让我们制作配置文件。 配置文件将是用于键映射操作的简单json文件。
package com.gkatzioura.gcs.lock;
import java.util.HashMap;
import java.util.Map;
import com.google.cloud.storage.BlobId;
import com.google.cloud.storage.BlobInfo;
import com.google.cloud.storage.Storage;
import org.json.JSONObject;
public class GCSConfiguration {
private final Storage storage;
private final String bucket;
private final String keyName;
GCSConfiguration(Storage storage, String bucket, String keyName) {
this .storage = storage;
this .bucket = bucket;
this .keyName = keyName;
}
public void addProperty(String key, String value) {
var blobId = BlobId.of(bucket, keyName);
var blob = storage.get(blobId);
final JSONObject configJson;
if (blob== null ) {
configJson = new JSONObject();
} else {
configJson = new JSONObject( new String(blob.getContent()));
}
configJson.put(key, value);
var blobInfo = BlobInfo.newBuilder(blobId).build();
storage.create(blobInfo, configJson.toString().getBytes());
}
public Map<String,String> properties() {
var blobId = BlobId.of(bucket, keyName);
var blob = storage.get(blobId);
var map = new HashMap<String,String>();
if (blob!= null ) {
var jsonObject = new JSONObject( new String(blob.getContent()));
for (var key: jsonObject.keySet()) {
map.put(key, jsonObject.getString(key));
}
}
return map;
}
}
这是GCS支持的简单配置工具。 最终,可以对其进行更改,并将锁操作放入addProperty操作中,这取决于用户和代码。 就本博客而言,我们仅需获取锁即可更改配置并释放锁。我们的主要班级将像这样。
package com.gkatzioura.gcs.lock;
import com.google.cloud.storage.StorageOptions;
public class Application {
public static void main(String[] args) {
var storage = StorageOptions.getDefaultInstance().getService();
final String bucketName = "bucketName" ;
final String lockFileName = "lockFileName" ;
final String configFileName = "configFileName" ;
var lock = new GCSLock(storage, bucketName, lockFileName);
var gcsConfig = new GCSConfiguration(storage, bucketName, configFileName);
var lockAcquired = lock.acquire();
if (lockAcquired) {
gcsConfig.addProperty( "testProperty" , "testValue" );
lock.release();
}
var config = gcsConfig.properties();
for (var key: config.keySet()) {
System.out.println( "Key " +key+ " value " +config.get(key));
}
}
}
现在让我们进行一些多线程处理。 十个线程将尝试放置值,预计它们会有一些故障。
package com.gkatzioura.gcs.lock;
import java.util.ArrayList;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import com.google.cloud.storage.Storage;
import com.google.cloud.storage.StorageOptions;
public class ApplicationConcurrent {
private static final String bucketName = "bucketName" ;
private static final String lockFileName = "lockFileName" ;
private static final String configFileName = "configFileName" ;
public static void main(String[] args) throws ExecutionException, InterruptedException {
var storage = StorageOptions.getDefaultInstance().getService();
final int threads = 10 ;
var service = Executors.newFixedThreadPool(threads);
var futures = new ArrayList<Future>(threads);
for (var i = 0 ; i < threads; i++) {
futures.add(service.submit(update(storage, "property-" +i, "value-" +i)));
}
for (var f : futures) {
f.get();
}
service.shutdown();
var gcsConfig = new GCSConfiguration(storage, bucketName, configFileName);
var properties = gcsConfig.properties();
for (var i= 0 ; i < threads; i++) { System.out.println(properties.get( "property-" +i)); } } +i)); } } private static Runnable update( final Storage storage, String property, String value) { return () -> {
var lock = new GCSLock(storage, bucketName, lockFileName);
var gcsConfig = new GCSConfiguration(storage, bucketName, configFileName);
boolean lockAcquired = false ;
while (!lockAcquired) {
lockAcquired = lock.acquire();
System.out.println( "Could not acquire lock" );
}
gcsConfig.addProperty(property, value);
lock.release();
};
}
}
显然,有10个线程可以显示这些功能。 在线程初始化和执行期间,一些线程将尝试同时获取锁,而一个锁将失败,而其他线程将延迟并失败,并等待直到锁可用。
最后,期望它们都将其值添加到配置中。而已。 如果您的问题性质简单,则此方法可以解决问题。 显然,您可以使用http api代替sdk。 您可以在github上找到代码。
翻译自: https://www.javacodegeeks.com/2020/10/locking-for-multiple-nodes-the-easy-way-gcs.html
gcs新的发展趋势