Spring HttpInvoke implementation and efficiency improvement!

Links to the original text: https://my.oschina.net/mohaiyong/blog/221248

I'll also sort out Spring Http Invoke, which was once the most familiar thing.
Spring Http Invoke, a commonly used implementation of remote calls between servers based on Spring architecture, can be said to be a lightweight RMI.
Initially, we used Spring HttpInvoke to synchronously configure data and refresh caches on multiple servers. Of course, would it be better to use distributed caches?
With Spring Http Invoke, you can call remote interfaces, perform data interaction, business logic operations, and so on.
Don't talk nonsense, code it!
User Operating Interface:
/**
 * @author <a href="mailto:zlex.dongliang@gmail.com">Beams and pillars</a>
 * @since 1.0
 */
public interface UserService {

	/**
	 * Getting Users
	 * 
	 * @param username
	 *            User name
	 * @return
	 */
	User getUser(String username);
}

User class, pay attention to implementation Serializable interface, which is the first requirement for executing remote calls to transfer data objects - data objects must be implemented Serializable interface, because, to perform serialization / deserialization operations!
/**
 * @author <a href="mailto:zlex.dongliang@gmail.com">Beams and pillars</a>
 * @since 1.0
 */
public class User implements Serializable {

	private static final long serialVersionUID = 5590768569302443813L;
	private String username;
	private Date birthday;

	/**
	 * @param username
	 * @param birthday
	 */
	public User(String username, Date birthday) {
		this.username = username;
		this.birthday = birthday;
	}
       // ellipsis
	/*
	 * (non-Javadoc)
	 * 
	 * @see java.lang.Object#toString()
	 */
	@Override
	public String toString() {
		return String.format("%s\t%s\t", username, birthday);
	}
}

Override the toString() method and output user information!
Look again at the UserService Impl implementation:
/**
 * @author <a href="mailto:zlex.dongliang@gmail.com">Beams and pillars</a>
 * @since 1.0
 */
public class UserServiceImpl implements UserService {
	private Logger logger = Logger.getLogger(UserServiceImpl.class);

	/*
	 * (non-Javadoc)
	 * 
	 * @see
	 * org.zlex.spring.httpinvoke.service.UserService#getUser(java.lang.String)
	 */
	@Override
	public User getUser(String username) {
		if (logger.isDebugEnabled()) {
			logger.debug("username:[" + username + "]");
		}
		User user = new User(username, new Date());
		if (logger.isDebugEnabled()) {
			logger.debug("user:[" + user + "]");
		}
		return user;
	}

}

Just typing out the user information will show the effect of the call!
Look at applicationContext.xml
<bean
		id="userService"
 class="org.springframework.remoting.httpinvoker.HttpInvokerServiceExporter">
		<property
			name="service">
			<bean
 class="org.zlex.spring.httpinvoke.service.impl.UserServiceImpl" />
		</property>
		<property
			name="serviceInterface"
			value="org.zlex.spring.httpinvoke.service.UserService" />
	</bean>

We need to expose the userService so that the implementation of this interface can be invoked externally through the http interface.
Speaking of HttpInvokerService Exporter, this class is used to wrap exposed interfaces on the server side.
Familiar with service, define specific implementation class!
<property
			name="service">
			<bean
 class="org.zlex.spring.httpinvoke.service.impl.UserServiceImpl" />
		</property>

Familiar with service interface pointing to interfaces that need to be exposed, Note the use of value to annotate the interface name!
<property
			name="serviceInterface"
			value="org.zlex.spring.httpinvoke.service.UserService" />

Finally, look at the servlet.xml configuration
<bean class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">
		<property
			name="urlMap">
			<map>
				<entry
					key="*"
					value-ref="userService" />
			</map>
		</property>
	</bean>

Directly point the request to the userService just configured
Now let's visit http://localhost:8080/spring/service/

This means that the server-side configuration has been successful! If this exception is frequently found in the log, it is likely that the server has been maliciously accessed!
Look at the client implementation again:
/**
 * @author <a href="mailto:zlex.dongliang@gmail.com">Beams and pillars</a>
 * @since 1.0
 */
public class UserServiceTest {
	private Logger logger = Logger.getLogger(UserServiceTest.class);
	private ApplicationContext context;

	private UserService userService;

	@Before
	public void initialize() {
		context = new ClassPathXmlApplicationContext("applicationContext.xml");
		userService = (UserService) context.getBean("userService");
	}

	@Test
	public void getUser() {
		User user = userService.getUser("zlex");
		if (logger.isDebugEnabled()) {
			logger.debug("user[" + user + "]");
		}
	}
}

What have we done? Nothing! It's the same as calling implementations in general Spring containers!
Look at applicationContext.xml again:
<bean
		id="userService"
 class="org.springframework.remoting.httpinvoker.HttpInvokerProxyFactoryBean">
		<property
			name="serviceUrl"
			value="http://localhost:8080/spring/service" />
		<property
			name="serviceInterface"
			value="org.zlex.spring.httpinvoke.service.UserService" />
	</bean>

Here we can call userService through the Spring container, but in fact, he is a HttpInvoker ProxyFactoryBean, in this configuration, defines the access address serviceUrl and the access interface serviceInterface.
Execute the test!

If we write this, we call it by default. SimpleHttp Invoker Request Executor is implemented. I'm afraid this implementation can only be used as a demonstration!
This is also the problem of efficiency!!!
To improve efficiency, we should use Commons-HttpClient!
What do we need to do? Import this jar and change to xml!
<bean
		id="userService"
		class="org.springframework.remoting.httpinvoker.HttpInvokerProxyFactoryBean">
		<property
			name="serviceUrl"
			value="http://localhost:8080/spring/service" />
		<property
			name="serviceInterface"
			value="org.zlex.spring.httpinvoke.service.UserService" />
		<property
			name="httpInvokerRequestExecutor">
			<ref
				bean="httpInvokerRequestExecutor" />
		</property>
	</bean>
 <bean
		id="httpInvokerRequestExecutor" class="org.springframework.remoting.httpinvoker.CommonsHttpInvokerRequestExecutor">
		<property
			name="httpClient">
			<bean
				class="org.apache.commons.httpclient.HttpClient">
				<property
					name="connectionTimeout"
					value="2000" />
				<property
					name="timeout"
					value="5000" />
			</bean>
		</property>
	</bean>

With HttpClient, we can configure timeout and connection timeout Timeout attributes, so that when the server performs operations, if the timeout can be forced to release the connection, so poor tomcat will not be exhausted because the HttpInvoke connection is not released!
Looking back at my N years ago code, long live, I did achieve that! Fortunately, no low-level mistakes have been made!!!
Execute the operation!

At this point, it is implemented as org. spring framework. remoting. httpinvoker. CommonsHttpInvokerRequestExecutor!
But colleagues think that this efficiency is not high enough!!!
Change again, what? Or xml!
<bean
		id="httpInvokerRequestExecutor"
		class="org.springframework.remoting.httpinvoker.CommonsHttpInvokerRequestExecutor">
		<property
			name="httpClient">
			<bean
				class="org.apache.commons.httpclient.HttpClient">
				<property
					name="connectionTimeout"
					value="2000" />
				<property
					name="timeout"
					value="5000" />
				<property
					name="httpConnectionManager">
					<ref
						bean="multiThreadedHttpConnectionManager" />
				</property>
			</bean>
		</property>
	</bean>
	<bean
		id="multiThreadedHttpConnectionManager"
		class="org.apache.commons.httpclient.MultiThreadedHttpConnectionManager">
		<property
			name="params">
			<bean
				class="org.apache.commons.httpclient.params.HttpConnectionManagerParams">
				<property
					name="maxTotalConnections"
					value="600" />
				<property
					name="defaultMaxConnectionsPerHost"
					value="512" />
			</bean>
		</property>
	</bean>

change to the use of sth. MultiThreaded Http Connection Manager, Multithread!!!
Testing goes without saying. Practice has proved that:
By default, the server can respond to a request for an average of about 10 seconds.
Multi-threaded implementation, the average server response to a request about 20ms.
This is not an order of magnitude!!!

Note: In version 3.1 of HttpClient, the following configuration is not supported, and the corresponding method has been abandoned!
<property  
                     name="connectionTimeout"  
                     value="2000" />  
                 <property  
                     name="timeout"  
                     value="5000" />


If you look at the document carefully,
Quote
HttpClient that uses a default MultiThreadedHttpConnectionManager.

How can the implementation of commons series ignore multithreading? The default implementation is multi-threaded! How worried my colleagues are!
Of course, my colleague added that we need to control the number of connections!
No wonder, it's set up here.
<bean
				class="org.apache.commons.httpclient.params.HttpConnectionManagerParams">
				<property
					name="maxTotalConnections"
					value="600" />
				<property
					name="defaultMaxConnectionsPerHost"
					value="512" />
			</bean>


What is the default?
Quote
Max Connections PerHost Maximum number of parallel links per host, default 2
public static final int DEFAULT_MAX_HOST_CONNECTIONS = 2;
Maximum number of total parallel links on maxTotal Connections client, default 20
public static final int DEFAULT_MAX_TOTAL_CONNECTIONS = 20;


Skills are one of the best!
Write it down for your reference!
See annex for details!
I haven't written a blog for a long time. Today I finally stretched my blog!

Reproduced in: https://my.oschina.net/mohaiyong/blog/221248

Keywords: Spring xml Apache Java

Added by Sgarissta on Mon, 09 Sep 2019 08:56:27 +0300