【问题标题】:Google App Engine: Unit testing concurrent access to memcacheGoogle App Engine:对内存缓存的并发访问进行单元测试
【发布时间】:2010-04-25 02:56:40
【问题描述】:

你们能告诉我一种在 Google App Engine 上模拟对 memcache 的并发访问的方法吗?我正在尝试使用 LocalServiceTestHelpers 和线程,但没有任何运气。每次我尝试在一个线程中访问 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));
 }
}

【问题讨论】:

    标签: google-app-engine memcached


    【解决方案1】:

    您是在尝试测试您的应用还是 App Engine 的内存缓存实现? memcache 在并发读取和写入下的语义很好理解 - 您最好简单地模拟可能发生的条件,以验证您的应用程序处理它们是否正常。

    【讨论】:

    • 嗯,我认为你的观点很好。我真的不需要测试并发访问,因为它应该由谷歌保证。我需要测试的是我的业务逻辑在同步访问下是否能正常工作。
    【解决方案2】:

    问题在于线程是如何设置的,可以使用 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();
        }
    
    
    }
    

    【讨论】:

      【解决方案3】:

      GAE 不支持用户线程。我认为你试图过度复杂化。

      您可以对此进行测试的一种方法是创建一个外部线程客户端并生成您希望远程访问 GAE 的任意数量的线程。如果它在您的本地盒子上运行,只需指向那里。

      【讨论】:

      • +1。唯一有意义的测试方法是让多个外部客户端同时敲击 Web 服务。
      • 我正在处理一个场景,当我的应用程序在两个不同的服务器上运行并尝试修改相同的 memcache 条目(同时)并且线程是我发现可以用来模仿这个的东西场景。
      • 我真的不想创建外部线程客户端,因为它会要求我的单元测试依赖于 Web 服务器(这需要很少的启动开销,并且不能在单位方式)。
      • 嘿,我能想到的一种方法是将任何线程中对 Memcache 的所有调用转发到给定线程(借助模拟)。 (我认为的重新演员模式)。你觉得可行吗?
      • @Phuong Nguyen de ManCity fan 听起来您正在尝试测试并发在 App Engine 中是否有效?如果这些是单元测试,那么您需要做的就是测试您可以从 Memcache 写入和读取,单元测试是一项功能性活动。
      【解决方案4】:

      我猜的测试库 GAE SDK 试图模仿真实的 环境,从而设置 只有一个线程的环境( 运行测试的线程) 其他线程看不到。

      这个猜测大致正确:因为 GAE 本身从不在一个进程中运行多个线程,因此 SDK 也同样如此 - 所以我不清楚为什么要测试在 GAE 中不可能发生的多线程访问.

      【讨论】:

      • “因为 GAE 本身从不在一个进程中运行多个线程”。吹毛求疵,但是请求处理线程永远不会超过一个,而且你不能启动自己的线程,但是有些事情可以异步发生(就像所有 RPC 调用一样),所以我假设内部有多个线程。
      • @Thilo,并非如此:异步操作不一定是线程的同义词(请记住,线程在 Python 中非常有限)。一个正确的挑剔是我可以将我的断言限制在 GAE in Python —— Java 运行时是不同的(我不太熟悉它,但我相信它允许多线程使用)。
      • GAE 基本上是一个 servlet 引擎。在调用期间,每个专用线程(可以作为进程实现)有一个请求。
      • @Romain,在 Python GAE 的情况下,它肯定是进程,而不是线程——特别是,如果在查询服务期间您的代码设置了任何全局变量,则值您设置的设置将一直保留到查询服务结束(不允许其他查询的其他服务践踏它们甚至看到它们)。查询完成后,引擎可能会随心所欲地终止进程或将其重新用于下一个查询——但同时为该查询提供服务的其他查询肯定不会在同一进程中提供服务。
      • @Alex Martelli,我所知道的所有 Java Servlet 引擎都是基于线程的。从理论上讲,可以使用 Processes 实现一个。在 Servlet 中,如果您使用 Java API,则使用它,没有可能的全局变量,有一个应用程序上下文允许您在应用程序级别放入变量(可以有多个 Servlet),并且有一个Servlet 上下文,允许您将信息放入 Servlet 级别(还有会话上下文和请求上下文分别用于会话范围和请求范围)我怀疑 Py 和 Java 实现是不同的
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-09-04
      • 2017-06-23
      • 1970-01-01
      • 2018-02-24
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多