【问题标题】:How to isolate Jetty HttpClient for multiple users?如何为多个用户隔离 Jetty HttpClient?
【发布时间】:2020-12-22 21:16:32
【问题描述】:

我正在使用 Eclipse Jetty HttpClient 向服务器发送 POST 请求,以进行负载测试。

TL;DR:有没有办法使用单个 HttpClient 实例和多个用户凭据集到单个目标 URL?

为此,我需要以单独的用户身份登录到被测服务器。 尽管 HttpClient 是线程安全的,但由于其共享身份验证存储,它似乎并不支持单个实例。

解决方案看起来很简单,只需为每个用户或每个线程使用一个 HttpClient。

这没问题,除了 HttpClient 为每个实例创建多个线程(似乎是 5 到 10 个),因此我的负载测试需要一个非常大的堆,否则它会在尝试创建新线程时开始抛出 OutOfMemory 异常.

例如,在这个非常基本的测试中,第一组凭据用于所有后续 POST:

public class Test 
{
    static class User
    {
        String username;
        String password;
        User(String username, String password)
        {
            this.username = username;
            this.password = password;
        }
    }

    public static void main(String[] args) throws Exception 
    {
        SslContextFactory sslContextFactory = new SslContextFactory.Client();
        HttpClient httpClient = new HttpClient(sslContextFactory);
        httpClient.start();
        
        List<User> users = new ArrayList<>();
        
        users.add(new User("fry", "1234"));
        users.add(new User("leela", "2345"));
        users.add(new User("zoidberg", "3456"));
        
        URI uri = new URI("http://localhost:8080/myapi");

        for (User user : users)
        {
            AuthenticationStore auth = httpClient.getAuthenticationStore();
            auth.addAuthentication(new DigestAuthentication(uri, DigestAuthentication.ANY_REALM, user.username, user.password));

            Request request = httpClient.newRequest(uri);
            request.method("POST");
            
            ContentResponse result = request.send();

            System.out.println(result.getStatus());
        }
    }
}

现在,我意识到在这个人为的测试中,我可以在循环之间调用 httpClient.getAuthenticationStore().clearAuthenticationResults()httpClient.getAuthenticationStore().clearAuthentications();,但这不适用于我的实际测试,因为我有多个线程同时发布。

我是否坚持为每个用户使用单独的 HttpClient 实例?

感谢您的任何想法!

【问题讨论】:

    标签: java jetty jetty-httpclient


    【解决方案1】:

    您可以通过“抢占”每个请求的身份验证标头来完成您需要的操作,例如 explained in the documentation

    你会这样做:

    // Single HttpClient instance.
    HttpClient httpClient = new HttpClient();
    
    // The server URI.
    URI uri = URI.create("http://example.com/secure");
    
    // The authentication credential for each user.
    Authentication.Result authn1 = new BasicAuthentication.BasicResult(uri, "user1", "password1");
    Authentication.Result authn2 = new BasicAuthentication.BasicResult(uri, "user2", "password2");
    
    // Create a request instance.
    Request request1 = httpClient.newRequest(uri);
    // Add the authorization headers for user1.
    authn1.apply(request1);
    request1.send();
    
    Request request2 = httpClient.newRequest(uri);
    // Add the authorization headers for user2.
    authn2.apply(request2);
    request2.send();
    

    发送请求不需要像上面的简单示例那样按顺序发送或使用阻塞 API。

    您可以从for 循环中执行此操作,例如随机选择一个用户(及其对应的授权),并使用异步 API 以获得更好的性能。

    【讨论】:

    • 酷!太好了。
    • 喷,看来这个方法只支持Basic认证。
    • 它支持所有类型的身份验证,特别是支持开箱即用的BasicDigest。如果您有其他类型的身份验证,则需要说明哪一种,但HttpClient 身份验证机制是可插拔的,因此您基本上可以自己插入。
    • 非常感谢您的回答。我希望你不介意另一个问题:你说支持摘要。但是,org.eclipse.jetty.client.util.DigestAuthentication 类没有任何类似于用于应用于请求的BasicAuthentication.BasicResult 类。查看DigestAuthentication 的javadoc 让我相信这将是一个两步过程,您将提交请求,收到401 错误,调用DigestAuthentication.authenticate 以获取Authentication.Result 对象,然后重新提交。听起来对吗?
    • 差不多吧。但是,对于您最初的问题,不要以这种方式使用身份验证,而是使用 cookie。在启动时验证 N 个用户,让服务器为不同的用户生成不同的 cookie(例如//host/path1//host/pathN,其中 1..N 是不同的用户)。然后,不同的路径意味着不同的用户,剩下的工作将由 HttpClient 中的内置 cookie 功能完成。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2019-05-23
    • 2021-09-16
    • 2015-07-23
    • 2013-08-05
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多