문제

나는 며칠 동안 이 작업을 해왔고 몇 가지 해결책을 찾았지만 그 중 어느 것도 믿을 수 없을 정도로 간단하거나 가볍지 않았습니다.문제는 기본적으로 이렇습니다.우리는 10개의 머신으로 구성된 클러스터를 보유하고 있으며, 각 머신은 멀티스레드 ESB 플랫폼에서 동일한 소프트웨어를 실행하고 있습니다.동일한 머신에 있는 스레드 간의 동시성 문제는 상당히 쉽게 처리할 수 있지만, 다른 머신의 동일한 데이터에 대한 동시성은 어떻습니까?

기본적으로 소프트웨어는 웹 서비스를 통해 한 비즈니스에서 다른 비즈니스로 고객의 데이터를 공급하라는 요청을 받습니다.그러나 고객이 다른 시스템에 아직 존재할 수도 있고 존재하지 않을 수도 있습니다.그렇지 않은 경우 웹 서비스 방법을 통해 생성합니다.따라서 일종의 테스트 및 설정이 필요하지만 다른 컴퓨터가 경쟁 조건을 일으키지 않도록 잠그려면 일종의 세마포어가 필요합니다.단일 로컬 고객에 대해 원격 고객이 두 번 생성되는 상황이 있었는데 이는 실제로 바람직하지 않습니다.

제가 개념적으로 생각해 본 솔루션은 다음과 같습니다.

  1. 내결함성 공유 파일 시스템을 사용하여 고객에 따라 각 시스템에서 확인할 "잠금" 파일을 생성합니다.

  2. 잠금 기록에 대한 "테스트 및 설정"을 수행하기 위해 데이터베이스의 특수 테이블을 사용하고 전체 테이블을 잠급니다.

  3. 확장을 지원하지만 허브 앤 스포크 모델을 사용하는 오픈 소스 서버 소프트웨어인 Terracotta를 사용합니다.

  4. 메모리 내 "잠금"의 동기 복제를 위해 EHCache를 사용합니다.

이런 문제를 겪은 사람이 나 혼자뿐이라고는 상상할 수 없습니다.어떻게 해결하셨나요?사내에서 요리를 했나요? 아니면 좋아하는 타사 제품이 있나요?

도움이 되었습니까?

해결책

당신은 사용을 고려하고 싶을 수도 있습니다 헤이즐캐스트 분산 잠금.매우 가볍고 쉽습니다.

java.util.concurrent.locks.Lock lock = Hazelcast.getLock ("mymonitor");
lock.lock ();
try {
// do your stuff
}finally {
   lock.unlock();
}

Hazelcast - 분산 대기열, 맵, 설정, 목록, 잠금

다른 팁

우리는 테라코타를 사용하므로 그것에 투표하고 싶습니다.

나는 Hazelcast를 팔로우하고 있으며 또 다른 유망한 기술처럼 보이지만 그것을 사용해본 적이 없기 때문에 투표할 수 없으며, 그것이 P2P 기반 시스템을 사용한다는 소식을 들었을 때 나는 그것을 크게 신뢰하지 않을 것입니다. 스케일링이 필요합니다.

하지만 야후에서 나와 하둡이라는 우산 아래 움직이고 있는 주키퍼(Zookeeper)에 대해서도 들어봤습니다.새로운 기술을 시도하는 모험심이 있다면 이것은 매우 간결하고 비열하며 단지 조정에만 초점을 맞추기 때문에 실제로 많은 가능성을 가지고 있습니다.나는 비전과 약속을 좋아하지만 아직은 너무 녹색일지도 모릅니다.

Terracotta는 "계층형" 모델에 더 가깝습니다. 모든 클라이언트 애플리케이션은 Terracotta 서버 어레이와 통신합니다(더 중요한 것은 규모 측면에서 서로 통신하지 않는다는 것입니다).Terracotta 서버 어레이는 규모와 가용성을 위해 클러스터링할 수 있습니다(가용성을 위해 미러링, 규모를 위해 스트라이프).

어떤 경우든 Terracotta는 POJO 동기화/대기/알림을 사용하거나 ReentrantReadWriteLock과 같은 java.util.concurrent 기본 요소를 사용하여 단일 JVM에서와 동일한 방식으로 클러스터 전체에서 동시성을 표현할 수 있는 기능을 제공합니다. , CyclicBarrier, AtomicLong, FutureTask 등등.

이러한 기본 요소의 사용을 보여주는 간단한 요리법이 많이 있습니다. 테라코타 요리책.

예를 들어, ReentrantReadWriteLock 예제를 게시하겠습니다("테라코타" 버전의 잠금이 없습니다. 일반 Java ReentrantReadWriteLock을 사용하면 됩니다).

import java.util.concurrent.locks.*;

public class Main
{
    public static final Main instance = new Main();
    private int counter = 0;
    private ReentrantReadWriteLock rwl = new ReentrantReadWriteLock(true);

    public void read()
    {
        while (true) {
            rwl.readLock().lock();
                try {
                System.out.println("Counter is " + counter);
            } finally {
                rwl.readLock().unlock();
            }
            try { Thread.currentThread().sleep(1000); } catch (InterruptedException ie) {  }
        }
    }

    public void write()
    {
        while (true) {
            rwl.writeLock().lock();
            try {
               counter++;
               System.out.println("Incrementing counter.  Counter is " + counter);
            } finally {
                 rwl.writeLock().unlock();
            }
            try { Thread.currentThread().sleep(3000); } catch (InterruptedException ie) {  }
        }
    }

    public static void main(String[] args)
    {
        if (args.length > 0)  {
            // args --> Writer
            instance.write();
        } else {
            // no args --> Reader
            instance.read();
        }
    }
}

나는 사용하는 것이 좋습니다 레디슨.다음을 포함하여 30개 이상의 분산 데이터 구조 및 서비스를 구현합니다. java.util.Lock.사용 예:

Config config = new Config();
config.addAddress("some.server.com:8291");
Redisson redisson = Redisson.create(config);

Lock lock = redisson.getLock("anyLock");
lock.lock();
try {
    ...
} finally {
   lock.unlock();
}

redisson.shutdown();

나는 로그를 보관하기 위한 매우 빠른 분산 RAM 저장소로 memcached를 사용하는 방법에 대해 조언하려고 했습니다.하지만 EHCache는 비슷한 프로젝트이지만 더 자바 중심적인 것 같습니다.

원자성 업데이트를 사용하는 한 둘 중 하나가 좋습니다(memcached는 이를 지원하지만 EHCache에 대해서는 모릅니다).이는 단연 가장 확장성이 뛰어난 솔루션입니다.

관련 데이터 포인트로 Google은 BigTable을 비롯한 여러 시스템의 루트로 빠른 RAM 기반 분산 잠금 저장소인 'Chubby'를 사용합니다.

저는 Coherence를 사용하여 많은 작업을 수행해 왔으며 이를 통해 분산 잠금 구현에 대한 여러 가지 접근 방식이 가능해졌습니다.순진한 접근 방식은 모든 참여 노드에서 동일한 논리 개체를 잠그도록 요청하는 것이었습니다.일관성 측면에서 이는 복제된 캐시의 키를 잠그는 것입니다.노드를 추가하면 네트워크 트래픽이 선형적으로 증가하므로 이 접근 방식은 확장이 잘 되지 않습니다.더 현명한 방법은 클러스터의 각 노드가 자연스럽게 키 공간의 일부를 담당하는 분산 캐시를 사용하는 것이었습니다. 따라서 이러한 캐시에 키를 잠그려면 항상 최대 하나의 노드와의 통신이 필요합니다.이 아이디어를 기반으로 자신만의 접근 방식을 적용하거나 더 나은 방법으로는 일관성을 얻을 수 있습니다.이것은 정말로 여러분이 꿈꾸던 확장성 툴킷입니다.

나는 네트워크 장애가 발생할 경우 올바르게 작동하려면 절반 수준의 다중 노드 네트워크 기반 잠금 메커니즘이 합리적으로 정교해야 한다고 덧붙이고 싶습니다.

전체 맥락을 이해하는지 잘 모르겠지만 이를 뒷받침하는 단일 데이터베이스가 1개 있는 것 같나요?데이터베이스 잠금을 활용해 보는 것은 어떨까요?고객 생성이 단일 INSERT인 경우 데이터베이스가 제약 조건 중 하나를 위반하는 두 번째 INSERT를 거부하므로 이 문만 잠금 역할을 할 수 있습니다.예를 들어 고객 이름이 고유하다는 사실).

"고객 삽입" 작업이 원자적이지 않고 일련의 명령문인 경우 고객을 식별하는 간단한 기본 레코드(필요한 UNIQUEness 제약 조건 포함)를 생성하는 초기 INSERT를 도입(또는 사용)한 다음 모든 작업을 수행합니다. 동일한 트랜잭션의 다른 삽입/업데이트.이번에도 데이터베이스는 일관성을 관리하며 동시 수정으로 인해 그 중 하나가 실패하게 됩니다.

저는 두 가지 방법으로 간단한 RMI 서비스를 만들었습니다.잠금 및 해제.두 방법 모두 키를 사용합니다(내 데이터 모델은 UUID를 pk로 사용했기 때문에 잠금 키이기도 했습니다).

RMI는 중앙 집중식이므로 이에 대한 좋은 솔루션입니다.EJB로는 이 작업을 수행할 수 없습니다(특히 호출이 어느 시스템에 연결될지 모르기 때문에 클러스터에서는 더욱 그렇습니다).게다가 쉽습니다.

그것은 나를 위해 일했습니다.

단일 고객에 대한 요청이 항상 동일한 서버에 매핑되도록 로드 밸런싱을 설정할 수 있다면 로컬 동기화를 통해 이를 처리할 수 있습니다.예를 들어 고객 ID mod 10을 사용하여 10개 노드 중 사용할 노드를 찾습니다.

일반적인 경우에 이를 원하지 않더라도 노드는 이러한 특정 유형의 요청에 대해 서로 프록시할 수 있습니다.

사용자가 충분히 균일하다고 가정합니다(예:노드가 너무 많다면) 하나의 노드가 과부하되는 핫스팟이 나타날 것으로 예상하지 않는 경우에도 여전히 꽤 잘 확장될 것입니다.

당신은 또한 고려할 수 있습니다 카천닉스 분산 잠금용.여기에 언급된 다른 것과 달리 Cacheonix는 필요할 때 읽기에서 쓰기로 잠금 에스컬레이션을 통해 ReadWrite 잠금을 지원합니다.

ReadWriteLock rwLock = Cacheonix.getInstance().getCluster().getReadWriteLock();
Lock lock = rwLock.getWriteLock();
try {
  ...
} finally {
  lock.unlock();
}

전체 공개:저는 카천닉스 개발자입니다.

이미 데이터베이스에 연결하고 있으므로 다른 인프라 부분을 추가하기 전에 다음을 살펴보십시오. Jdbc세마포, 사용이 간단합니다.

JdbcSemaphore semaphore = new JdbcSemaphore(ds, semName, maxReservations);
boolean acq = semaphore.acquire(acquire, 1, TimeUnit.MINUTES);
if (acq) {
 // do stuff
 semaphore.release();
} else {
  throw new TimeoutException();
}

그것은의 일부입니다 spf4j 도서관.

과거에는 이를 처리하기 위해 네트워크의 특정 "잠금 서버"를 사용했습니다.블레.

데이터베이스 서버에는 이러한 종류의 작업을 수행하기 위한 리소스가 있을 수 있습니다.MS-SQL Server에는 다음을 통해 사용할 수 있는 애플리케이션 잠금이 있습니다. sp_getapplock/sp_releaseapplock 절차.

우리는 오픈 소스 분산 동기화 프레임워크를 개발해 왔으며 현재 DistributedReentrantLock 및 DistributedReentrantReadWrite 잠금이 구현되었지만 여전히 테스트 및 리팩토링 단계에 있습니다.우리 아키텍처에서 잠금 키는 버킷으로 나누어지고 각 노드는 특정 수의 버킷을 담당할 수 있습니다.성공적인 잠금 요청에 대해서는 효과적으로 네트워크 요청이 하나만 있습니다.또한 AbstractQueuedSynchronizer 클래스를 로컬 잠금 상태로 사용하므로 실패한 모든 잠금 요청은 로컬로 처리되므로 네트워크 트래픽이 크게 줄어듭니다.우리는 JGroups(http://jgroups.org)은 그룹 통신용이고 Hessian은 직렬화용입니다.

자세한 내용은 확인해 주세요. http://code.google.com/p/vitrit/.

여러분의 소중한 의견을 보내주세요.

캄란

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top