# Threads zum Verringern der Latenzzeiten bei HTTP GET Request



## Apraxus (15. Dez 2007)

Hi,

ich hab mich gefragt, ob man Threads nicht auch dazu nutzen kann die Latenzzeit bei Webanwendungen zu umgehen. 
Das heißt konkret, dass ich wenn eine Seite geladen wird ein anderer Thread die nächste Seite vorlädt und dann bereitstellt. 

Ich habe versucht das ganze Thread Prinzip in den letzten zwei Tagen soweit in meine Anwendung zu integrieren, dass diese damit umgehen und das ganze auch verarbeiten kann. Hier im Forum gab es ja auch schon ein paar Hinweise und Fragen zu diesem Thema. :?: 


Da meine Anwendung zuerst ein Login benötigt, finde ich es ein wenig komplizierter wie die ganzen restlichen Anwendungen die hier so herumgeistern. Ich bin auch dazu übergegangen nicht die einzelnen Seiten ansich in Threads  wie in einer Warteschleife zu laden, sondern das Abarbeiten einer ganzen Sequenz von Anweisungen.


```
package com.vit.crawler.launch;

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.net.SocketException;

import java.sql.Connection;
import java.sql.SQLException;

import java.util.Collection;
import java.util.EmptyStackException;
import java.util.Iterator;
import java.util.Properties;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

import org.apache.commons.httpclient.HttpClient;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import com.vit.crawler.CClient;
import com.vit.crawler.CLogin;

import com.vit.db.DB_Object;
import com.vit.db.DB_UIDS;
import com.vit.dto.DTO_UIDS;

import com.vit.util.CRead;
import com.vit.util.CTimer;


/**
 * CCrawler.class
 * 
 * The Crawler Class is the main starting JAVA class file for the Visualize.It
 * crawler. So do your settings and have fun!
 */
public class CCrawlerProfileThread
{
	private static final Log LOG = LogFactory.getLog(CCrawlerProfileThread.class);
	private static String MAIN_PROPERTIES = "src/properties/main.properties";
	private static String USER_PROPERTIES = "src/properties/user.properties";
	
	//Thread implements Runnable
	//shows the available processors installed at the System
	int processorsNum = Runtime.getRuntime().availableProcessors();
	//starts two threads, for each processor
	ExecutorService executor = Executors.newFixedThreadPool(processorsNum); 
	
	/**
	 * The Main Method
	 * @param args
	 */
	public static void main(String[] args) 
	{
		CTimer tt = new CTimer();
		long time_start = tt.getTime();
		
		Properties props_main = new Properties();
		
		try {
			props_main.load(new FileInputStream(MAIN_PROPERTIES));
			   
		} catch (FileNotFoundException e) {
			if(LOG.isErrorEnabled())
				LOG.error("com.vit.crawler.CClient\n --> propertiesfiles not found:\n --> " + e.toString());
		} catch (IOException e) {
			if(LOG.isErrorEnabled())
				LOG.error("com.vit.crawler.CClient\n --> proplems while loading the propertiesfiles:\n --> " + e.toString());
		}
		
		int reconnect = Integer.parseInt(props_main.getProperty("reconnect"));
		while(reconnect >= 0)
		{
			try {
				CCrawlerProfileThread cpt = new CCrawlerProfileThread();
				boolean t = false;
				t = cpt.start();
				System.out.println("\n\n ---\nDurchgang erfolgreich?: " + t + " \n---\n\n");
			
			}catch (SocketException e) {
				LOG.fatal("SOCKETEXCEPTION: " + e.toString());
				System.out.println("\n---\nSocketExceptionHandling\n---\n");
				main(null);
			}catch (NullPointerException e) {
				LOG.fatal("NULLPOINTEREXCEPTION: " + e.toString());
				System.out.println("\n---\nNullPointerExceptionHandling\n---\n");
				main(null);
			}catch (EmptyStackException e){
				long time_end = tt.getTime();
				tt.calculateTime(time_start, time_end);
				System.out.println("JOB CRASHED ");
				e.printStackTrace();
				System.exit(1);
			}catch (RuntimeException e){
				long time_end = tt.getTime();
				tt.calculateTime(time_start, time_end);
				System.out.println("JOB CRASHED ");
				e.printStackTrace();
				System.exit(1);
			}
			
			reconnect--;
			System.out.println("\n\n --- ---- --- \n Reconnects: " + reconnect + " \n --- --- --- \n\n");
		}
		
		long time_end = tt.getTime();
		tt.calculateTime(time_start, time_end);
		System.out.println("JOB COMPLETED");
		System.exit(0);
		
	}//main()

	/**
	 * Start the Profile-Crawler
	 */
	public boolean start() throws SocketException, NullPointerException
	{
		LOG.info("com.vit.crawler.launch.CCrawler");

		HttpClient httpClient = null;
		CLogin login = new CLogin();
		
		// get the needed information out of the properties file
		Properties props_main = new Properties();
		Properties props_user = new Properties();
		
		try {
			props_main.load(new FileInputStream(MAIN_PROPERTIES));
			props_user.load(new FileInputStream(USER_PROPERTIES));
			   
		} catch (FileNotFoundException e) {
			if(LOG.isErrorEnabled())
				LOG.error("com.vit.crawler.CClient\n --> propertiesfiles not found:\n --> " + e.toString());
		} catch (IOException e) {
			if(LOG.isErrorEnabled())
				LOG.error("com.vit.crawler.CClient\n --> proplems while loading the propertiesfiles:\n --> " + e.toString());
		}
		
		// prepare the variables
		int selectedUser = Integer.parseInt(props_user.getProperty("selecteduser"));
		String id = "";
		String nid = "";
		
		switch(selectedUser)
		{
			case 0:      id = props_user.getProperty("id");
					nid = props_user.getProperty("nid");
					System.out.println("User 0");
					break;
			case 1:      id = props_user.getProperty("id1");
					nid = props_user.getProperty("nid1");
					System.out.println("User 1");
					break;

			default: 
					id = props_user.getProperty("defaultid");
					nid = props_user.getProperty("defaultnid");
					System.out.println("User Default");
					break;
		}
		
		String loggedin_uid = id;
		
		// do the login at the community site
		String cookieValue = login.doLogin(selectedUser);
	    
		// get a normal HTTPVItClient
		httpClient = CClient.getHttpVItClient();
	    
	    // crawl profiles
		Connection cn = null;
		int crawls = Integer.parseInt(props_main.getProperty("crawls"));
		int schleife = 0;
		
		try {
			cn = new DB_Object().get_Connection();
			// get crawlable profiles from database
			DB_UIDS dbuid = new DB_UIDS();
			Collection col = null;
			
			// number of crawls
			for(int i = 0; i < crawls; i++)
		    {
				
				col = dbuid.getUserArrayToCrawlThread(cn, loggedin_uid);
				Iterator itr = col.iterator();

				// profiles to crawl.
				while(itr.hasNext())
				{
					DTO_UIDS dto = (DTO_UIDS) itr.next();
					
					// allocation
					id = String.valueOf(dto.getUser_UID());
					
					CCrawlerThreadFriends ctf = new CCrawlerThreadFriends(id, httpClient, cookieValue, cn, loggedin_uid, dbuid);
					this.executor.execute(ctf);
					
					schleife++;
					System.out.println("\n\n --- ---- --- \n Anzahl Schleife: " + schleife + " \n --- --- --- \n\n");
				}//while
				schleife = 0;
				System.out.println("\n\n --- ---- --- \n Anzahl: " + i + " \n --- --- --- \n\n");
		    }//for		
				
				
		    new DB_Object().close_Connection(cn);
		    
		} catch (SQLException e) {
			LOG.error("" + e.toString());
		}
		
		return true;
	
	}//start()
	
}//class

/**
 * Thread class for the ProfileCrawler of Visualize.it
 * The class implements Runnable and is controlled by the 
 * Java ExecutorService ...
 */
class CCrawlerThreadFriends implements Runnable
{
	private static final Log LOG = LogFactory.getLog(CCrawlerThreadFriends.class);
	private static boolean status = false;
	
	private String id;
	private HttpClient httpClient;
	private String cookieValue;
	private Connection cn;
	private String loggedin_uid;
	private DB_UIDS dbuid;
	
	public CCrawlerThreadFriends(String id, HttpClient httpClient, String cookieValue, 
			 Connection cn, String loggedin_uid, DB_UIDS dbuid)
	{
		this.id = id;
		this.httpClient = httpClient;
		this.cookieValue = cookieValue;
		this.cn = cn;
		this.loggedin_uid = loggedin_uid;
		this.dbuid = dbuid;
	}
	
	public void run()
	{
		try
		{
		    // prepare profile site
		    String nextProfile = "/profile.php?id=" + id;
		    
		    // prepare friends site
		    String friendsURL = "/friends.php?id=" + id + "&nk=0";
	
		    System.out.println("ID: " + id +" AND URLS: " + nextProfile + " " + friendsURL);
		    
		    // write result to database - id specifies the current user
		    StringBuffer sbprofile = CClient.httpGetSiteProfile(httpClient, nextProfile, cookieValue, id);
			
		    // parse profile personal information
		    CRead.parsePersonalInfoFromProfile(cn, sbprofile.toString(), id);
		    
		    // parse network information
		    CRead.parsePersonalNetwork(cn, sbprofile.toString(), id);
		    
		    // parse profile groups
		    CRead.parseGIDsFromProfile(cn, sbprofile.toString(), id);
		    
		    // parse education info
		    CRead.parseEducationInfoFromProfile(cn, sbprofile.toString(), id);
		    
		    // parse work info
		    CRead.parseWorkInfoFromProfile(cn, sbprofile.toString(), id);
		   
		    // parse friends from profiles and write result to database
		    // -> loggedin_uid specifies the current logged in user
		    // -> id specifies the user id of the profile that is crawled at the moment
		    StringBuffer sbfriends = CClient.httpGetSiteFriends(httpClient, friendsURL, cookieValue);
			CRead.parseUserFriendsFromProfile(cn, sbfriends.toString(), id, loggedin_uid, 1, httpClient, cookieValue);
		    
			// update crawled profile entries
			boolean b = dbuid.updateUserArrayToCrawl(cn, id, loggedin_uid);
			System.out.println("SET USER TO 1: " + b);
			
			if(LOG.isDebugEnabled())
				LOG.debug("STATUS PROFILE: " + status);
			
		} catch (Exception e){
	        System.err.println ("Exception Fehler");
	        e.printStackTrace();
	    }
	}
}//class
```

Die Threads sind hierbei nur irgendwie so schnell, dass das durchführen der gesamten Anweisungen aus der run() Methode nicht mehr durchgeführt wird. Die Ausgabe des System.out.println() wird noch vollbracht 

Das hier sind die Fehler die ich erhalte:


```
8345 [pool-1-thread-1] INFO  com.vit.crawler.CClient  - com.vit.crawler.CClient.httpGetSiteProfile()
8346 [pool-1-thread-2] INFO  com.vit.crawler.CClient  - com.vit.crawler.CClient.httpGetSiteProfile()
8349 [pool-1-thread-2] WARN  org.apache.commons.httpclient.SimpleHttpConnectionManager  - SimpleHttpConnectionManager being used incorrectly.  Be sure that HttpMethod.releaseConnection() is always called and that only one thread and/or method is using this connection manager at a time.
8938 [pool-1-thread-1] FATAL com.vit.crawler.CClient  - IOEXCEPTION: org.apache.commons.httpclient.ProtocolException: Unable to parse header: hre=t-8
8939 [pool-1-thread-2] FATAL com.vit.crawler.CClient  - IOEXCEPTION: java.net.SocketException: socket closed
```

... und danach wird das laden der Seite schon mit einigen Exceptions abgebrochen. ...

Weiterhin gibt es dann noch ein paar NullPointerExceptions, sowie IllegalState und ProtocolExceptions. Diese bauen meiner Meinung nach aber auf den oben geposteten Fehlern auf... 
Na ja und wenn ich das ganze mit noch mehr als 2 Threads laufen lassen würde gibt es noch mehr und noch schneller Fehler.

Wenn ich das ganze ohne Threads ausführe komme ich wunderbar zu dem gewünschten Ergebnis.
Kann mir von euch vielleicht jemand Helfen.

Schon mal danke im Vorraus.

Ah... vielleicht sollte ich noch dazusagen, dass das DTO Objekt im Moment ca. 40 ids an die while Schleife liefert, welche letztendlich den Thread bzw. das Runnable aufruft.


----------



## maki (15. Dez 2007)

> SimpleHttpConnectionManager being used incorrectly.  Be sure that HttpMethod.releaseConnection() is always called and that only one thread and/or method is using this connection manager at a time.


Tja, sieht so aus als ob die Connection nicht von mehreren Threads benutzt werden darf.


----------



## Apraxus (15. Dez 2007)

tja und was kann ich da machen.
Die release Method wird auf jedenfall noch aufgerufen. 

Wie benutze ich den SimpleHttpConnectionManager denn richtig?
bzw. ich war mir gar nicht bewusst, dass ich den SimpleHttpConnectionManager überhaupt verwende. Wird der von einer Unterklasse aufgerufen?


----------



## maki (15. Dez 2007)

Was passiert hier?

```
httpClient = CClient.getHttpVItClient();
```


----------



## Apraxus (15. Dez 2007)

Na es erzeugt ein httpClient Object mit den Configurations


```
public static HttpClient getHttpVItClient() 
	{
		LOG.info("com.vit.crawler.CClient.getHttpVItClient()");
		
		// get the needed information out of the properties file
		Properties props_main = new Properties();
		
		try {
			props_main.load(new FileInputStream(MAIN_PROPERTIES));
			   
		} catch (FileNotFoundException e) {
			if(LOG.isErrorEnabled())
				LOG.error("com.vit.crawler.CClient\n --> propertiesfiles not found: \n --> " + e.toString());
		} catch (IOException e) {
			if(LOG.isErrorEnabled())
				LOG.error("com.vit.crawler.CClient\n --> proplems while loading the propertiesfiles:\n --> " + e.toString());
		}
		   
		// set host, port and protocol
		String host 	= props_main.getProperty("host");
		int iport 		= Integer.parseInt(props_main.getProperty("port"));
		String protocol = props_main.getProperty("protocol");
		   
		HttpClient httpClient = null;
		HostConfiguration conf = null;  
		 
		try {
			httpClient = new HttpClient();
			conf = httpClient.getHostConfiguration();
			 
			// set the host configuration data
			conf.setHost(host, iport, protocol);
			httpClient.setHostConfiguration(conf);
			 
			// set the host timeout parameter and the cookie policy
			httpClient.getHttpConnectionManager().getParams().setConnectionTimeout(30000);
			httpClient.getParams().setCookiePolicy(CookiePolicy.BROWSER_COMPATIBILITY);
		
		} catch(Exception e) {
			if(LOG.isErrorEnabled())
				LOG.error("com.vit.crawler.CClient\n --> problems while setting the host configuration data:\n --> " + e.toString());
		}
		 
		return httpClient;
	 
	 }//getHttpVItClient()
```


----------



## Apraxus (15. Dez 2007)

glaub ich hab fast eine lösung. du hast mich auf eine idee gebracht.
wenn es funktioniert poste ich es hier.

bin aber dennnoch für weitere anregungen dankbar.


----------



## maki (15. Dez 2007)

An deiner Stelle würde ich pro Thread einen HttpClient erzeugen.
Der SimpleHttpConnectionManager ist er Standard ConnectionManager wenn kein anderer angeben wurde, siehe API Doc.


----------



## Apraxus (15. Dez 2007)

Das mit dem Threading hab ich jetzt glaub ich soweit hinbekommen.
Hab jetzt ein anderes Problem ... nach zwei Seitenaufrufen werde ich wieder ausgeloggt !


```
public static HttpClient getHttpVItClientThreaded() 
	{
		LOG.info("com.vit.crawler.CClient.getHttpVItClient()");
		
		// get the needed information out of the properties file
		Properties props_main = new Properties();
		
		try {
			props_main.load(new FileInputStream(MAIN_PROPERTIES));
			   
		} catch (FileNotFoundException e) {
			if(LOG.isErrorEnabled())
				LOG.error("com.vit.crawler.CClient\n --> propertiesfiles not found: \n --> " + e.toString());
		} catch (IOException e) {
			if(LOG.isErrorEnabled())
				LOG.error("com.vit.crawler.CClient\n --> proplems while loading the propertiesfiles:\n --> " + e.toString());
		}
		   
		// set host, port and protocol
		String host 	= props_main.getProperty("host");
		int iport 		= Integer.parseInt(props_main.getProperty("port"));
		String protocol = props_main.getProperty("protocol");
		   
		HttpClient httpClient = null;
		HostConfiguration conf = null;  
		 
		try {
			MultiThreadedHttpConnectionManager manager = new MultiThreadedHttpConnectionManager();
			HostConfiguration hostConfig = new HostConfiguration();
			
			hostConfig.setHost(host, iport, protocol);
			
			manager.getConnectionWithTimeout(hostConfig, 30000);
			manager.getConnectionWithTimeout(hostConfig, 30000);
			
			//get connection pool
			int num = manager.getConnectionsInPool();
			System.out.println("# of Connections in the pool: " + num);

			
			httpClient = new HttpClient(manager);
			conf = httpClient.getHostConfiguration();
			 
			// set the host configuration data
			conf.setHost(host, iport, protocol);
			httpClient.setHostConfiguration(conf);
			 
			// set the host timeout parameter and the cookie policy
			httpClient.getHttpConnectionManager().getParams().setConnectionTimeout(30000);
			httpClient.getParams().setCookiePolicy(CookiePolicy.BROWSER_COMPATIBILITY);
		
		} catch(Exception e) {
			if(LOG.isErrorEnabled())
				LOG.error("com.vit.crawler.CClient\n --> problems while setting the host configuration data:\n --> " + e.toString());
		}
		 
		return httpClient;
	 
	 }//getHttpVItClient()
```


Hab ich das mit dem ConnectionPool richtig gemacht? Verstanden? Muss ich die Verbindungen selbst auchmachen oder kann er das auch bei bedarf automatisch. Weil dann würde es ja ausreichen, wenn ich manager.getConnectionWithTimeout(...) einmal aufrufe.


----------



## DocRandom (16. Dez 2007)

Moin!

..guck Dir mal HTTPUnit an, da hast Du ein WebConversations-Object (Simuliert den Browser) und von diesem ableitend, WebResponse-Objekte (jede Site ein WebResponse).

lg
DocRandom


----------

