使用Apache+Tomcat构建应用服务器集群
1.安装Apache HTTP server:http://httpd.apache.org/
2.安装2-3个(或更多)Tomcathttp://tomcat.apache.org/,安装zip版,因为安装多个Windows Service Installer版会出现错误(可以安装一个Installer版,其他两个用zip版)。
3.配置3个Tomcat,使得3个Tomcat在一台机器上可以同时运行:
修改三个Tomcat安装目录下的bin/startup.bat,将其中的CATALINA_HOME环境变量修改成互不相同的名字,比如CATALINA_HOME1,CATALINA_HOME2,CATALINA_HOME3。
4.配置集群
1) 修改Apache安装目录下的conf/httpd.conf文件,在文件末尾加入如下几行ProxyRequests Off <proxy balancer://cluster> BalancerMember ajp://127.0.0.1:8009 loadfactor=1 route=jvm1 BalancerMember ajp://127.0.0.1:9009 loadfactor=1 route=jvm2 BalancerMember ajp://127.0.0.1:9099 loadfactor=1 route=jvm3 </proxy>8009,9009,9099分别是三个Apache用来于Tomcat连接的端口oadfactor是指每一个tomcat的负载系数。
2) 修改Tomcat下的conf/server.xml
a.将所有3个Tomcat的server.xml中的
<Engine name="Catalina" defaultHost="localhost">
修改为<Engine name="Catalina" defaultHost="localhost" jvmRoute="jvm1">,3个tomcat的jvmRoute分别为jvm1,jvm2,jvm3
b.修改所有3个server.xml中的<Connector port="8009" protocol="AJP/1.3" redirectPort="8443" />,将8009端口改为与上面Apache中配置的三个端口一致(这点很重要!),与上面配置的jvm1,jvm2也要一致。也就是说,配置为jvm1的,这里端口改为8009,配置为jvm2的tomcat,这里端口给为9009,jvm3的为9099。
c.修改server.xml中的所有其他端口,使得3个tomcat没有重复的端口,包括:<Server port="8005",<Connector port="8080",redirectPort="8443" 这三个,三个tomcat的这三个端口都要不一样,其中每个tomcat中有两个redirectPort,同一个tomcat中的redirectPort一样,不同tomcat中的redirectPort不一样。
4.现在,集群就应该已经正确的构建完毕,访问Apache(localhost,端口为80)时,Apache就会将请求转发给这3个tomcat中的一个,可以自己写一个JSP或者servlet试试,注意,三个tomcat中的webapp下面都需要有一份JSP/servlet。
5.写一个实验程序(servlet),发布到3个tomcat上,写一个客户端程序,多线程并发访问Apache,观察三个tomcat的负载均衡情况(可以调节一下loadfactor再观察),并绘制在线程越来越多的情况下,响应时间曲线。
由于规模较小的计算,响应时间非常快,可以忽略。所以选择一个计算量较大的运算:
判断一个数是否为素数,Sevlet代码如下:package com.hw;
import java.io.IOException;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.math.MathContext;
import java.math.RoundingMode;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
* Servlet implementation class JudgePrimeServlet
*/
public class JudgePrimeServlet extends HttpServlet {
private static final long serialVersionUID = 1L;
private static final String TRUE_JSON = "{success:true}";
private static final String FALSE_JSON = "{success:false}";
public JudgePrimeServlet() {
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
String numStr = request.getParameter("number");
boolean ans;
if(numStr.length() < 10)
ans = isPrime(Long.parseLong(numStr));
else
ans = isPrime(new BigInteger(numStr));
System.out.println(numStr + " : " + ans);
response.getWriter().write(ans ? TRUE_JSON : FALSE_JSON);
response.getWriter().flush();
response.getWriter().close();
}
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doGet(request, response);
}
public static boolean isPrime(long n)
{
if(n % 2 == 0)
{
if(n == 2)
return true;
return false;
}
long up = (long)Math.sqrt((double)n);
for(int i = 3; i <= up; i += 2)
if(n % i == 0)
return false;
return true;
}
public static boolean isPrime(BigInteger n)
{
if(n.mod(BigInteger.valueOf(2)).equals(BigInteger.ZERO))
{
if(n.equals(BigInteger.valueOf(2)))
return true;
return false;
}
MathContext mc = new MathContext(n.toString().length(), RoundingMode.HALF_DOWN);
BigInteger up = new BigDecimal(Math.sqrt(n.doubleValue()) ,mc).toBigInteger();
for(BigInteger i = BigInteger.valueOf(3); i.compareTo(up) <= 0; i = i.add(BigInteger.valueOf(2)))
if(n.mod(i).equals(BigInteger.ZERO))
return false;
return true;
}
},客户端的线程代码如下:public class ClientTestThread extends Thread
{
public static final long[] primeArr = new long[] { 2055437, 2057317, 5298991, 5300467, 5352869, 5377679, 6220229,
7155887, 8491247, 8885837, 9999929, 9999931, 9999943, 9999971, 9999973, 9999991, 11547391, 12531809,
13192643, 15766453, 16854379, 26459779, 26456789, 26453803, 29999791, 1000003871, 1015490783, 1800000011,
1800000019, 1800000047, 1800000049, 1800000053, 1800000061, 1800000109, 1800000113, 1800001523, 1978012607,
1978012609, 1978012633, 1978012669, 1978012703, 1978012709, 1978012739, 1978012741, 1978012759, 1978012763,
2008693679, 2008693681, 2008693727, 2008693741, 2008693759, 2095462079, 2095462099, 2095462151, 2095462163,
2095462207, 2099999999 };
private static Long timer = 0L;
@Override
public void run()
{
URL url;
HttpURLConnection conn;
long millisec = GregorianCalendar.getInstance().getTimeInMillis();
try
{
url = new URL("http://localhost/ClusterExp/JudgePrimeServlet?number=" + 2099999999);
conn = (HttpURLConnection) url.openConnection();
Scanner scan = new Scanner(conn.getInputStream());
if(scan.hasNext())
scan.nextLine();
scan.close();
ClientTestThread.addToTimer((GregorianCalendar.getInstance().getTimeInMillis() - millisec));
}
catch (MalformedURLException e)
{
e.printStackTrace();
}
catch (IOException e)
{
e.printStackTrace();
}
}
public static void addToTimer(long time)
{
synchronized (timer)
{
timer += time;
}
}
public static void clearTimer()
{
synchronized (timer)
{
timer = 0L;
}
}
public static Long getTimer()
{
return timer;
}
},客户端界面以及生成多线程、绘制响应时间曲线代码如下(使用SWT,如果要运行,需要使用eclipse.org的SWT包):package com.hw.client;
import org.eclipse.swt.SWT;
public class TestClient
{
protected Shell shell;
private Text lowField;
private Text highField;
private Group lineGroup;
private static final int unit = 10;
private static final int off = 10;
private static final int width = 800;
private static final int height = 600;
/**
* Launch the application.
*
* @param args
*/
public static void main(String[] args)
{
try
{
TestClient window = new TestClient();
window.open();
}
catch (Exception e)
{
e.printStackTrace();
}
}
/**
* Open the window.
*/
public void open()
{
Display display = Display.getDefault();
createContents();
shell.open();
shell.layout();
while (!shell.isDisposed())
{
if (!display.readAndDispatch())
{
display.sleep();
}
}
}
/**
* Create contents of the window.
*/
protected void createContents()
{
shell = new Shell();
shell.setSize(1024, 768);
shell.setText("/u8D1F/u8F7D/u5E73/u8861/u6D4B/u8BD5/u5BA2/u6237/u7AEF");
shell.setLayout(null);
Composite composite = new Composite(shell, SWT.NONE);
composite.setBounds(10, 10, 655, 97);
Group group = new Group(composite, SWT.NONE);
group.setBounds(10, 23, 422, 64);
group.setText("/u6D4B/u8BD5/u7684/u7EBF/u7A0B/u6570/u91CF/u533A/u95F4");
group.setLayout(null);
Label label = new Label(group, SWT.NONE);
label.setBounds(10, 27, 60, 12);
label.setText("/u4E0B/u9650");
lowField = new Text(group, SWT.BORDER);
lowField.setBounds(76, 24, 106, 18);
Label label_1 = new Label(group, SWT.NONE);
label_1.setBounds(198, 27, 60, 12);
label_1.setText("/u4E0A/u9650");
highField = new Text(group, SWT.BORDER);
highField.setBounds(265, 24, 106, 18);
Button button = new Button(composite, SWT.NONE);
button.addSelectionListener(new SelectionAdapter() {
@Override
public void widgetSelected(SelectionEvent arg0) {
GC gc = new GC(lineGroup);
gc.setForeground(new Color(Display.getCurrent(), 255, 0, 0));
gc.drawLine(off, height - off, width + off, height - off);
gc.drawLine(off, height - off, off, off);
for(int i = 1; i < width / unit; ++ i)
{
if( i % 5 == 0)
gc.drawString(String.valueOf(i), i * unit + off - 2, height - off + 3);
}
gc.drawString("线程数", width + off, height - off - 10);
gc.setBackground(new Color(Display.getCurrent(), 255, 0, 0));
for(int i = 1; i < width / unit; ++ i)
{
gc.fillRectangle(i * unit + off, height - off - 3, 2, 3);
}
for(int i = 1; i < height / unit; ++ i)
{
gc.fillRectangle(off, height - off - i * unit, 3, 2);
}
int low = Integer.parseInt(lowField.getText());
int high = Integer.parseInt(highField.getText());
int lastX = off, lastY = height - off;
int x, y;
for (int i = low; i <= high; ++i)
{
Thread[] multiThread = new ClientTestThread[i];
for (Thread t : multiThread)
{
t = new ClientTestThread();
t.start();
}
boolean allFinished = false;
while (!allFinished)
{
allFinished = true;
for (Thread t : multiThread)
{
if (t != null && t.isAlive())
{
allFinished = false;
Thread.yield();
break;
}
}
}
x = i * unit + off;
y = height - off - ClientTestThread.getTimer().intValue() / i / 3;
if(y < 0)
y = 0;
gc.drawLine(lastX, lastY, x, y);
System.out.println(x + "," + y);
lastX = x;
lastY =y;
//gc.fillOval(i, 600 - ClientTestThread.getTimer().intValue(), 3,3);
// gc.drawPoint(i, ClientTestThread.getTimer().intValue());
ClientTestThread.clearTimer();
}
}
});
button.addMouseListener(new MouseAdapter()
{
@Override
public void mouseDown(MouseEvent arg0)
{
}
});
button.setBounds(476, 53, 72, 22);
button.setText("/u6D4B/u8BD5");
lineGroup = new Group(shell, SWT.NONE);
lineGroup.setText("/u8D1F/u8F7D/u66F2/u7EBF");
lineGroup.setBounds(10, 113, 996, 618);
}
}
,运行后,三个tomcat的控制台都会输出结果,说明请求被分发给三个tomcat,运行1-75个线程的响应时间曲线如下: