Google app engine GoogleAppEngine:单元测试对memcache的并发访问

Google app engine GoogleAppEngine:单元测试对memcache的并发访问,google-app-engine,memcached,Google App Engine,Memcached,你们能告诉我一种在Google App Engine上模拟并发访问memcache的方法吗?我正在尝试使用LocalServiceTestHelper和线程,但没有任何运气。每次尝试在线程内访问Memcache时,都会出现以下错误: ApiProxy$CallNotFoundException: The API package 'memcache' or call 'Increment()' was not found 我猜GAE SDK的测试库试图模拟真实环境,因此只为一个线程(运行测试的线

你们能告诉我一种在Google App Engine上模拟并发访问memcache的方法吗?我正在尝试使用LocalServiceTestHelper和线程,但没有任何运气。每次尝试在线程内访问Memcache时,都会出现以下错误:

ApiProxy$CallNotFoundException: The API package 'memcache' or call 'Increment()' was not found
我猜GAE SDK的测试库试图模拟真实环境,因此只为一个线程(运行测试的线程)设置环境,而其他线程看不到该环境

下面是一段可以重现问题的代码

package org.seamoo.cache.memcacheImpl;

import org.testng.Assert;
import org.testng.annotations.AfterMethod;
import org.testng.annotations.BeforeMethod;
import org.testng.annotations.Test;

import com.google.appengine.api.memcache.MemcacheService;
import com.google.appengine.api.memcache.MemcacheServiceFactory;
import com.google.appengine.tools.development.testing.LocalMemcacheServiceTestConfig;
import com.google.appengine.tools.development.testing.LocalServiceTestHelper;

public class MemcacheTest {
 LocalServiceTestHelper helper;

 public MemcacheTest() {
  LocalMemcacheServiceTestConfig memcacheConfig = new LocalMemcacheServiceTestConfig();
  helper = new LocalServiceTestHelper(memcacheConfig);
 }

 /**
  * 
  */
 @BeforeMethod
 public void setUp() {
  helper.setUp();
 }

 /**
  * @see LocalServiceTest#tearDown()
  */
 @AfterMethod
 public void tearDown() {
  helper.tearDown();
 }

 @Test
 public void memcacheConcurrentAccess() throws InterruptedException {
  final MemcacheService service = MemcacheServiceFactory.getMemcacheService();
  Runnable runner = new Runnable() {

   @Override
   public void run() {
    // TODO Auto-generated method stub
    service.increment("test-key", 1L, 1L);
    try {
     Thread.sleep(200L);
    } catch (InterruptedException e) {
     // TODO Auto-generated catch block
     e.printStackTrace();
    }
    service.increment("test-key", 1L, 1L);
   }
  };

  Thread t1 = new Thread(runner);
  Thread t2 = new Thread(runner);
  t1.start();
  t2.start();
  while (t1.isAlive()) {
   Thread.sleep(100L);
  }
  Assert.assertEquals((Long) (service.get("test-key")), new Long(4L));
 }
}
我猜,测试库 GAE SDK试图模仿真实世界 环境,从而设置 仅用于一个线程的环境( 运行测试的线程)哪个 无法被其他线程看到


这个猜测大致正确:因为GAE本身从来不会在一个进程中运行多个线程,因此SDK也不会这样做——所以我不清楚为什么要测试GAE中无法实现的多线程访问。

GAE不支持用户线程。我认为你想把事情复杂化


您可以测试这一点的一种方法是创建一个外部线程客户端,并生成您想要远程访问GAE的尽可能多的线程。如果它在您的本地设备上运行,请指向那里。

您是在测试您的应用程序还是应用程序引擎的memcache实现?memcache在并发读写操作下的语义已经很好地理解了——你最好只是模拟可能发生的情况,以验证你的应用程序是否能够很好地处理它们。

问题在于线程是如何设置的,可以使用SDK中提供的ThreadManager来解决:

private final LocalServiceTestHelper helper =
        new LocalServiceTestHelper(new LocalDatastoreServiceTestConfig());

@Before
public void setUp() throws Exception {
    helper.setUp();
}

@After
public void tearDown() throws Exception {
    helper.tearDown();
}

AtomicInteger threadsRemaining = new AtomicInteger(NUM_USERS);
AtomicInteger numFailingThreads = new AtomicInteger(0);

@Test
public void testManyUsers() throws Exception {


    ArrayList<TestUser> testUsers = new ArrayList<TestUser>();

    // Create something to test in parallel (in this case a "user")
    for (int i = 0; i < NUM_USERS; ++i) {
        testUsers.add(new TestUser());
    }

    // Fork off a thread for each user
    runInParallel(testUsers);

    // Wait for threads to complete.
    while (threadsRemaining.intValue() != 0) {
        Thread.sleep(10); // TODO: Clean up with callbacks or whatever. 
    }

    assertEquals(numFailingThreads.intValue(), 0);
}

private void runInParallel(final ArrayList<TestUser> testUsers) {


    for (final TestUser u : testUsers) {

        Runnable myTask = new Runnable() {

            @Override
            public void run() {
                try {

                    // The user tries to kill my server

                } catch (Exception e) {

                    // The user triggered badness in my server

                    e.printStackTrace();
                    numFailingThreads.incrementAndGet();
                }
                threadsRemaining.decrementAndGet();
            }
        };

        ThreadFactory threadFactory = ThreadManager.currentRequestThreadFactory();

        Thread t = threadFactory.newThread(myTask);

        t.start();
    }


}
私有最终LocalServiceTestHelper=
新的LocalServiceTestHelper(新的LocalDatastoreServiceTestConfig());
@以前
public void setUp()引发异常{
helper.setUp();
}
@之后
public void tearDown()引发异常{
helper.tearDown();
}
AtomicInteger线程维护=新的AtomicInteger(NUM_用户);
AtomicInteger numFailingThreads=新的AtomicInteger(0);
@试验
public void testManyUsers()引发异常{
ArrayList testUsers=新的ArrayList();
//创建要并行测试的内容(在本例中为“用户”)
对于(int i=0;i
“因为GAE本身从不在一个进程内运行多个线程”。吹毛求疵,但从来没有一个以上的请求处理线程,而且您不能启动自己的线程,但有些事情可以异步发生(像所有RPC调用),所以我假设内部有多个线程。@Thilo,不是真的:异步操作不一定是线程的同义词(请记住,线程在Python中受到了惊人的限制)。一个正确的吹毛求疵是,我本可以将我的断言限制在Python中的GAE——Java运行时是不同的(我对它不是很熟悉,但我确实相信它允许多线程使用)。GAE基本上是一个servlet引擎。它每个专用线程有一个请求(可以实现为进程)在调用期间。@Romain,在Python GAE的情况下,它肯定是进程,而不是线程——特别是,如果在查询服务期间您的代码设置了任何全局变量,那么您设置的值将保持不变,直到查询服务结束(不允许对其他查询的其他服务践踏它们,甚至不允许看到它们)。在一个查询完成后,一个进程可能会被终止,或者在引擎的突发奇想下重新用于下一个查询——但同时为这个查询提供服务的其他查询肯定不会在同一个进程中提供服务。@Alex Martelli,我所知的所有Java Servlet引擎都是基于线程的。理论上,使用在Servlet中,如果您使用Java API,那么您就可以使用它,没有全局变量,有一个应用程序上下文允许您在应用程序级别(可以有多个Servlet)放入变量,还有一个Servlet上下文允许您在Servlet级别放入信息(对于会话范围和请求范围,还分别存在会话上下文和请求上下文)我怀疑Py和Java impl是不同的+1。唯一有意义的测试方法是让多个外部客户端同时敲打web服务。我正在处理一个场景,即我的应用程序在两个不同的服务器上运行,并尝试修改相同的memcache条目(同时)线程是我发现可以用来模拟这个场景的东西。我真的不想创建外部线程客户端,因为它会要求我的单元测试依赖于web服务器(这要求很少的启动开销,并且不能以单元方式完成).嘿,我能想到的一种方法是,将任何线程中对Memcache的所有调用转发到给定的线程(借助模拟)