Chapter1 在云上架构你的应用
实验条件:
虚拟机管理器 VirtualBox
两个虚拟机:都是XP,一个命名为"XP_JPetStore",一个命名为"XP_JPetStore_Client",其实"XP_JPetStore_Client"是由"XP_JPetStore"完全拷贝得到。
两具虚拟机里面都下载装了Tomcat 8应用服务器相应于xp上的运行程序,且也下载了宠物商店(JPetStore)应用程序mybatis-jpetstore-6.0.0-bundle.zip,当然也下载了
Java JDK(因为Tomcat要运行需要有Java环境),且Tomcat 8要运行在JDK7版本及以上,所以Java JDK为版本7,且虚拟机里的操作系统已配置好了java环境;
VirtualBox对两个虚拟机的网络模式设置都为Host Only模式(总之,目的是:使主机操作系统能ping通两个虚拟机,然后我发现这个模式下达到了要求),VirtualBox在主机上的
HostOnly的ip设为192.168.56.1,两个虚拟机的ip分别为:192.168.56.102和192..168.56.103,网关都为192.168.56.1,网络掩码当然都是255.255.255.0
主机:Windows XP系统,安装了JMeter测试工具(Windows下JMeter启动\bin\jmeter.bat)
实验步骤:
一、部署网页应用程序(网站)
1、安装Tomcat 8
将下载的Tomcat 8的zip文件解压到相应目录,为了能进入Tomcat 8的manager管理网页并部署宠物商店应用程序mybatis-jpetstore-6.0.0.war,需要先编辑Tomcat 8conf目录下的tomcat-users.xml,加入以下有效代码
<tomcat-users>
<role rolename="manager-gui" />
<user username="用户名" password="密码" roles="用户名,manager-gui" />
</tomcat-users>
2、如果上面实验条件顺利,并且Tomcat 8安装正确的话,接下来,部署宠物商店应用程序mybatis-jpetstore-6.0.0.war,浏览器中(无论是主机上还是虚拟机里的OS上)打开http://192.168.56.102(客户机的IP):8080(8080中Tomcat默认的端口)/,可以进入Tomcat管理页面,单击Manager,输入上面配置的用户名、密码,可进入管理页面,然后点击Deploy(部署)Tab,进入后点击Browse按钮,找到我们解压mybatis-jpetstore-6.0.0-bundle.zip得到的mybatis-jpetstore-6.0.0.war,然后部署;
如果安装存在问题,查看Tomcat 8的logs\catalina.***..log
3、现在在浏览器中输入http://192.168.56.102:8080/mybatis-jpetstore-6.0.0/actions/Action.htm即可看到宠物商店的网页
二、对网站进行压力测试
假设开始时,虚拟机的内存设为256M,运行jmeter目录\bin\jmeter.bat得到测试效果(注意过程是:右击测试计划->添加Threads(Users)->线程组……,另外,在刚才的线程组上右击添加了Sampler->Http请求后,如下)
运行jmeter目录\bin\jmeter.bat得到测试效果
现虚拟机的内存设为512M,运行jmeter目录\bin\jmeter.bat得到测试效果
三、云计算的理念来提高网站的响应速度
原理是:将网站部部署在两台主机(这里是两台虚拟机)上,当网站的访问量太大——表现为Tomcat的负载过重(也即内存、CPU、网络带宽消耗很重时),使两台机子上的应用服务器一起工作,然后,在网站应用程序的逻辑功能前加一个“导购”似的逻辑程序来分流访问——负载均衡。
说的更具体点:因为Tomcat本身是使用Java语言实现,所以,通过监控Java虚拟机,可以达到监控Tomcat的目的。JMX(Java Management Extensions,即Java管理扩展)是一个为应用程序、设备、系统等植入管理功能的框架。Java虚拟机本身就内建了一些JMX管理模块,可以监控内存以及CPU的使用情况(在其java目录下\bin\jconsole.exe即是提供了图形化的监控界面)。我们根据Tomcat使用的内存来作相应策略——例如,当Tomcat使用的内存超过50%时,启动第二台虚拟机XP_JPetStore_Client并启动同样的Tomcat来分流负载
为使主机能监控虚拟机里运行的Tomcat 相应的负载,需要编辑Tomcat安装目录中的catalina.bat文件(这是一个Windows脚本,用来启动Tomcat应用服务器),具体在下图中有相应标注说明:
即在catalina.bat文件中一大堆rem 注释后、各函数之前加入set JMX_REMOTE_CONFIG=-Dcom.sun.management.jmxremote ……等内容,然后重新启动虚拟机里的Tomcat,在配置好Java环境变量的主机上的命令终端输入jconsole,然后选择远程连接,输入虚拟机IP:端口(这里我的端口设置为9004),即可得到上述监控界面
现在,我们根据VirtualBox的终端操作命令和JMX接口,根据Tomcat运行时的负载(内存消耗)来择机启动第二台虚拟机,这里,监控程序(远程监控Tomcat运行消耗内存)如下:
/**
* @author xiaoyao3857
*
*/
package com.qihua.cloud;
import java.io.IOException;
import java.net.MalformedURLException;
import javax.management.MBeanServerConnection;
import javax.management.ObjectName;
import javax.management.openmbean.CompositeDataSupport;
import javax.management.remote.JMXConnectorFactory;
import javax.management.remote.JMXServiceURL;
public class JMXClient {
public JMXClient(){
super();
}
public static void main(String[] args) throws Exception{
String urlForJMX ="service:jmx:rmi:///jndi/rmi://192.168.56.102:9004/jmxrmi";
MBeanServerConnection jmxServerConnection = JMXConnectorFactory.connect(new JMXServiceURL(urlForJMX),null).getMBeanServerConnection();
try{
String[] domains = jmxServerConnection.getDomains();
for(String domain:domains){
System.out.println(domain);
}
ObjectName memory = new ObjectName("java.lang:type=Memory");
CompositeDataSupport comp = (CompositeDataSupport)jmxServerConnection.getAttribute(memory, "HeapMemoryUsage");
double max = (Long)comp.get("max");
double used = (Long)comp.get("used");
System.out.println(used/max);
}catch(Exception ex){
ex.printStackTrace();
}
}
public static double getMemoryUsage(){
String urlForJMX="service:jmx:rmi:///jndi/rmi://192.168.56.102:9004/jmxrmi";
MBeanServerConnection jmxServerConnection = null;
double memoryUsage = 0;
try{
jmxServerConnection = JMXConnectorFactory
.connect(new JMXServiceURL(urlForJMX),null)
.getMBeanServerConnection();
}catch(MalformedURLException e){
e.printStackTrace();
}catch(IOException e){
e.printStackTrace();
}
try{
String[] domains = jmxServerConnection.getDomains();
for(String domain:domains){
System.out.println(domain);
}
ObjectName memory = new ObjectName("java.lang:type=Memory");
CompositeDataSupport comData = (CompositeDataSupport)jmxServerConnection.getAttribute(memory, "HeapMemoryUsage");
double max = (Long)comData.get("max");
double usage = (Long)comData.get("used");
memoryUsage = usage/max;
}catch(Exception e){
e.printStackTrace();
}
return memoryUsage;
}
}
上面程序得到堆内存的使用率
下面程序是通过Java语言实现启动与关闭虚拟机的操作
/*
* Author xiaoyao3857
* 用JAVA根据VirtualBox来操作虚拟机 "XP_JPetStore_Client"(它是在VirtualBox中已建好的,
* 只是初始状态,只是初始状态是关闭的)
*
* 根据VirtualBox的帮助文档中关于VBoxMange的章节和查阅资料,加上先实际命令行操作试验:
* 它是先用VBoxMange startvm vmname|vm_guid 来开启虚拟机
* 接下来再使用
* VBoxManage --nologo guestcontrol "My VM" execute --image "/bin/ls"
--username foo --passwordfile bar.txt --wait-exit --wait-stdout -- -l /usr
VBoxManage --nologo guestcontrol "My VM" execute --image "c:\\windows\\system32\\ipconfig.exe"
--username foo --passwordfile bar.txt --wait-exit --wait-stdout
* 目前OperateVM先如代码中那样写着:具体没有用到用户名、密码等,因为没有
* 找到VirtualBox中对于使用用户名、密码来完全使Guest Operating System运行的
*/
package com.qihua.cloud.util;
public class OperateVM {
//public void startVM(String userName,String passwd,String url,String dataStore,String path,String VMName){
//}
public static void startVM(String VMName) {
//start Virtual Machine
ExecuteCommand.execute("C:\\Program Files\\Oracle\\VirtualBox\\VBoxManage startvm " + VMName);
//run the Virtual Machine's Tomcat
ExecuteCommand.execute("C:\\Program Files\\Oracle\\VirtualBox\\VBoxManage --nologo guestcontrol " + VMName
+ " execute --image \"E:\\apache-tomcat-8.0.0-RC5-windows-x86\\apache-tomcat-8.0.0-RC5\\bin\\startup.bat\" --username HuaHero --password 123 --wait-exit --wait-stdout" );
}
public static void stopVM(String VMName){
ExecuteCommand.execute("C:\\Program Files\\Oracle\\VirtualBox\\VBoxManage controlvm " + VMName + " poweroff");
}
}
/*
* Author xiaoyao3857
* execute command
*/
package com.qihua.cloud.util;
import java.io.BufferedReader;
import java.io.InputStreamReader;
public class ExecuteCommand {
public static void execute(String cmd){
try{
Runtime rt = Runtime.getRuntime();
Process pr = rt.exec(cmd);
BufferedReader input = new BufferedReader(new InputStreamReader(pr.getInputStream()));
String line = null;
while((line = input.readLine())!=null){
System.out.println(line);
}
int exitVal = pr.waitFor();
System.out.println("Exited with error code" + exitVal);
}catch(Exception e){
System.out.println(e.toString());
e.printStackTrace();
}
}
}
那么,集成监控与虚拟机操作代码,当Tomcat使用内存超过50%时我们启动第二台虚拟机及其相应服务。程序如下:
/*
* Author xiaoyao3857
* 虚拟机调度器
*/
package com.qihua.cloud;
import com.qihua.cloud.util.OperateVM;
public class VMscheduler {
/**
* @param args
*/
public static void main(String[] args) {
// TODO Auto-generated method stub
while(true){
double memoryUsage = 0;
try{
memoryUsage = JMXClient.getMemoryUsage();
//每隔1分钟查看一次内存使用情况
Thread.sleep(1*60*1000);
}catch(InterruptedException e){
e.printStackTrace();
}
if(memoryUsage > 0.5){
OperateVM.startVM("XP_JPetStore_Client");
}else{
OperateVM.stopVM("XP_JPetStore_Client");
}
}
}
}
整个程序架构为:
实验结果为:
当然,上面虚拟机启动出现问题是由于我的主机内存不足,关掉一些程序后,点击虚拟机里“确定”按钮就正常了。
实验中,为了早些观察结果:我的程序是每一分钟查看一次内存使用情况,并且,为了验证,可以将>0.5条件改为0.4条件,并且,可以尝试关掉Tomcat,看我们的虚拟机调度程序是否关闭我们打开的虚拟机——总之,这样可以来大体上验证我们的程序是否按我们想走的逻辑正常进行。
有了上面的工作,接下来的后续工作是:实现负载均衡——即当访问网站的流量太大时,我们新开了主机(或虚拟机)同样的Tomcat服务器,然后在流量进入服务器前加一个负载均衡器(起生活中“导购”的作用),实现途径是:1、可水平扩展;2、可垂直扩展
本博客只记概要流程,在实验中某一过程如有问题,可以利用搜索引擎找到解决办法,当然,也欢迎留言探讨