Niklas Therning (JIRA) a écrit :
Hi Niklas,
I'm switching to the ML because JIRA may have a problem with too many
threads used ;)
[ http://issues.apache.org/jira/browse/DIRMINA-197?page=comments#action_12373020 ]
Niklas Therning commented on DIRMINA-197:
-----------------------------------------
You say that you only call bind/unbind?
Yep
There are no clients connecting at all?
I have wrote an injector that simulates clients. Right now, I just do N
times Bind/Unbind.
If there are no clients I can't understand why a ThreadPoolFilter would be created in the first place.
Each Bind is issued by what we can call a Client. So it makes sense that
a thread handle the request.
Maybe you could post your test code to clarify things?
yep, of course. Attached.
If there are clients connecting I think the problem could be that a new ThreadPoolFilter is created for each new session.
That's it.
You see, when a new session is created the ThreadModel, used when binding the
Acceptor to the port, will be asked to modify the new session's filter chain.
I've had a look at the code and it seems to me that the default ThreadModel
implementation (PooledThreadModel) creates a new ThreadPoolFilter for each new
session which it adds to the start of the session's chain. I don't think this
is what was intended. It could be bug.
I don't know. However, I was expecting the thread to be destroyed when I
unbind, as I'm closing the socket. Another solution is to create a
threads pool to deal with all those sessions. I suppose that the
PooledThreadModel is the perfect class for that, but ...
To verify that this is the problem you could implement your own ThreadModel and in its buildFilterChain() method you always add the same ThreadPoolFilter instance to the chain instead of creating a new every time like PooledThreadModel does.
Yeah, maybe. However, the PooledThreadModel, which is a subclass of
ThreadModel is supposed to be a pool, but it only pools worker threads.
I don't why if it's a bug in the design, or if it's done on purpose.
Trustin might know !
You can set a non-default ThreadModel by specifying an IoServiceConfig when
calling bind() on your acceptor.
All of this is only my guess though and I may have completely misunderstood how
the new ThreadModel stuff work. :)
Me neither :) Mina is still a mistery to me. I never had time to deepen
my knowledge of MINA.
Thanks a lot for the help !
package org.apache.directory.tester;
import java.io.BufferedOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.net.SocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SocketChannel;
import java.nio.channels.spi.SelectorProvider;
import java.util.Hashtable;
import javax.naming.Context;
import javax.naming.Name;
import javax.naming.NamingEnumeration;
import javax.naming.NamingException;
import javax.naming.directory.DirContext;
import javax.naming.directory.InitialDirContext;
import javax.naming.directory.SearchControls;
import org.apache.directory.shared.asn1.ber.Asn1Decoder;
import org.apache.directory.shared.asn1.ber.IAsn1Container;
import org.apache.directory.shared.asn1.ber.tlv.TLVStateEnum;
import org.apache.directory.shared.asn1.codec.DecoderException;
import org.apache.directory.shared.asn1.codec.EncoderException;
import org.apache.directory.shared.ldap.codec.LdapDecoder;
import org.apache.directory.shared.ldap.codec.LdapMessage;
import org.apache.directory.shared.ldap.codec.LdapMessageContainer;
import org.apache.directory.shared.ldap.codec.bind.BindRequest;
import org.apache.directory.shared.ldap.codec.bind.BindResponse;
import org.apache.directory.shared.ldap.codec.bind.LdapAuthentication;
import org.apache.directory.shared.ldap.codec.bind.SimpleAuthentication;
import org.apache.directory.shared.ldap.codec.extended.ExtendedResponse;
import org.apache.directory.shared.ldap.codec.unbind.UnBindRequest;
import org.apache.directory.shared.ldap.util.StringTools;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class Agent
{
private static final Logger log = LoggerFactory.getLogger( Agent.class );
private SocketChannel channel;
private SocketAddress serverAddress;
private OutputStream serverOut;
private InputStream serverIn;
private int messageId = 0;
private IAsn1Container ldapMessageContainer = new LdapMessageContainer();
private Asn1Decoder ldapDecoder = new LdapDecoder();
public void bind( String host, int port, String rootdn, Name name,
String password )
throws Exception
{
LdapMessage message = new LdapMessage();
message.setMessageId( messageId++ );
LdapMessage bind = new BindRequest();
bind.setMessageId( messageId );
((BindRequest)bind).setVersion( 3 );
SimpleAuthentication auth = new SimpleAuthentication();
auth.setSimple( StringTools.getBytesUtf8( password ) );
((BindRequest)bind).setAuthentication( auth );
((BindRequest)bind).setName( name );
message.setProtocolOP( bind );
ByteBuffer bb = message.encode( null );
bb.flip();
serverAddress = new InetSocketAddress( host, port );
channel = SocketChannel.open( serverAddress );
channel.configureBlocking( true );
channel.write( bb );
bb.clear();
while (true)
{
int nbRead = channel.read( bb );
if (nbRead == -1)
{
break;
}
else
{
bb.flip();
// Decode the PDU
ldapDecoder.decode( bb, ldapMessageContainer );
if ( ldapMessageContainer.getState() ==
TLVStateEnum.PDU_DECODED )
{
LdapMessage messageResp =
((LdapMessageContainer)ldapMessageContainer).getLdapMessage();
if ( messageResp instanceof BindResponse )
{
BindResponse resp =
((LdapMessageContainer)ldapMessageContainer).getLdapMessage().getBindResponse();
if
(resp.getLdapResult().getResultCode() != 0 )
{
System.out.println( "Error : "
+ resp.getLdapResult().getErrorMessage() );
}
}
else if (messageResp instanceof
ExtendedResponse )
{
ExtendedResponse resp =
((LdapMessageContainer)ldapMessageContainer).getLdapMessage().getExtendedResponse();
if
(resp.getLdapResult().getResultCode() != 0 )
{
System.out.println( "Error : "
+ resp.getLdapResult().getErrorMessage() );
}
}
//System.out.println(
((LdapMessageContainer)ldapMessageContainer).getLdapMessage() );
((LdapMessageContainer)ldapMessageContainer).clean();
break;
}
else
{
bb.flip();
}
}
}
}
public void unbind() throws Exception
{
LdapMessage message = new LdapMessage();
message.setMessageId( messageId++ );
LdapMessage unbind = new UnBindRequest();
unbind.setMessageId( messageId );
message.setProtocolOP( unbind );
ByteBuffer bb = message.encode( null );
bb.flip();
channel.write( bb );
bb.clear();
channel.close();
}
/**
* connect to server
*
* @param host
* Description of Parameter
* @param username
* Description of Parameter
* @param password
* Description of Parameter
* @exception NamingException
* Description of Exception
*/
public DirContext bind( String host, String port, String rootdn, String
username, String password )
throws NamingException {
DirContext dirContext;
Hashtable env = new Hashtable();
env.put(Context.INITIAL_CONTEXT_FACTORY,
"com.sun.jndi.ldap.LdapCtxFactory");
env.put(Context.PROVIDER_URL, "ldap://" + host + ":" + port +
"/" + rootdn);
env.put(Context.SECURITY_CREDENTIALS, password);
env.put(Context.SECURITY_PRINCIPAL, username);
log.debug ( "Binding ..." );
dirContext = new InitialDirContext(env);
log.debug ( "Bound!" );
return dirContext;
}
private String decode( ByteBuffer buffer ) throws DecoderException
{
Asn1Decoder ldapDecoder = new LdapDecoder();
// Allocate a LdapMessageContainer Container
IAsn1Container ldapMessageContainer = new LdapMessageContainer();
// Decode the PDU
ldapDecoder.decode( buffer, ldapMessageContainer );
// Check that everything is OK
LdapMessage ldapMessage = ( (LdapMessageContainer) ldapMessageContainer
).getLdapMessage();
return ldapMessage.toString();
}
/**
* disconnect from the server
*/
public void unbind( DirContext dirContext ) {
if (dirContext == null) {
log.info("Cannot disconnect null context");
return;
}
try {
log.debug ( "Unbinding ..." );
dirContext.close();
log.debug ( "Unbound !" );
} catch (NamingException e) {
log.warn("Ldap client disconnect - ", e);
}
}
/***************************************************************************
* Filter the data in the ldap directory for the given search base
*
* @param search
* base where the search should start
* @param search
* filter filter this value from the base
**************************************************************************/
public NamingEnumeration searchTest(DirContext dirContext,
String searchBase,
String searchFilter,
int scope,
long countlim,
int timelim,
String[] attrs,
boolean retobj,
boolean deref) throws NamingException {
SearchControls searchcontrols = null;
searchcontrols = new SearchControls();
searchcontrols.setSearchScope(scope);
NamingEnumeration answer = dirContext.search(searchBase,
searchFilter, searchcontrols);
return answer;
}
}
package org.apache.directory.tester;
import javax.naming.InvalidNameException;
import javax.naming.Name;
import javax.naming.NamingEnumeration;
import javax.naming.directory.DirContext;
import javax.naming.directory.SearchControls;
import javax.naming.directory.SearchResult;
import org.apache.directory.shared.ldap.codec.bind.LdapAuthentication;
import org.apache.directory.shared.ldap.codec.bind.SimpleAuthentication;
import org.apache.directory.shared.ldap.name.LdapDN;
import org.apache.directory.shared.ldap.util.StringTools;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class Injector extends Thread {
private static final Logger log = LoggerFactory.getLogger( Injector.class );
private static int nbSearches;
static Object lock = new Object();
static int nbTh = 0;
static
{
try
{
final Name name = new LdapDN( "uid=admin,ou=system" );
}
catch (Exception e)
{
}
}
public void run()
{
Agent ldapAgent = new Agent();
LdapAuthentication simple = new SimpleAuthentication();
( (SimpleAuthentication)simple ).setSimple(
StringTools.getBytesUtf8( "secret" ) );
long t0 = System.currentTimeMillis();
System.out.println( this.currentThread().getId() + " starts " );
try
{
for (int j= 0; j < nbSearches; j++ )
{
if ( j%100 == 0 )
{
System.out.println(j);
}
log.debug( "(" + j + ")Searching..." );
ldapAgent.bind( "localhost", 10389, "", new
LdapDN( "uid=admin,ou=system" ), "secret" );
/*
DirContext context = null;
for (int j= 0; j < nbSearches; j++ )
{
if ( j%100 == 0 )
{
System.out.println(j);
}
log.debug( "(" + j + ")Searching..." );
NamingEnumeration res =
ldapAgent.searchTest( context, "ou=users,ou=system", "(cn=ele*)",
SearchControls.SUBTREE_SCOPE, 0L, 0, new String[]{}, false, false );
log.debug( "Found !" );
if ( log.isDebugEnabled() )
{
if ( ( res == null ) || (
res.hasMore() == false ) )
{
log.debug( "(" + j +
")Not found..." + ( res == null ? "Null" : "no elem" ) );
}
else
{
while ( ( res != null )
&& ( res.hasMore() ) )
{
SearchResult sr
= (SearchResult)res.next();
log.debug( "("
+ j + ")Found name = " + sr.getName() );
}
}
}
}
*/
ldapAgent.unbind();
}
}
catch ( Exception e)
{
e.printStackTrace();
}
System.out.println( this.currentThread().getId() + " ends : " +
( System.currentTimeMillis() - t0 ) );
}
/**
* @param args
*/
public static void main(String[] args) throws Exception
{
// TODO Auto-generated method stub
Agent ldapAgent = new Agent();
LdapAuthentication simple = new SimpleAuthentication();
( (SimpleAuthentication)simple ).setSimple(
StringTools.getBytesUtf8( "secret" ) );
Name name = new LdapDN( "uid=admin,ou=system" );
int nbThreads = 1;
long t0 = System.currentTimeMillis();
nbSearches = 10000;
System.out.println( "Test starts ");
for (int i = 0; i < nbThreads; i++ )
{
Injector in = new Injector();
in.start();
}
System.out.println( "Test ends : " + (
System.currentTimeMillis() - t0 ) );
}
}