2015-09-14 11:32:33 biantaosky 阅读数 4181

本来要用亚马逊的beanstalk进行托管应用,但是由于其EIP的个数超过限制,没法获得IP,所以我就试着直接在AWS上自己搭一个tomcat的环境,由于没有在linux服务器进行开发的经验,所以我也是查阅好多资料才搭建好,此文章从最基本开始介绍

1.基本

用AWS 的服务首先需要有账号,这不用多说,具体申请账号什么的不在此多说,亚马逊有实验练习帮助掌握最基础的一些操作,点我进实验练习。这里我们进行下一步的前提是:启动了一个EC2实例,并且已经使用putty远程上了AWS EC2实例。

2.进阶

进行命令行操作的时候推荐进入root模式,能省下一些不必要的麻烦,如果进入root模式,下面的sudo可省去

最新的补丁和升级更新实例, 特别是如果有安全更新可用 :

sudo yum update
java环境EC2已经默认装好了,接下来可以配置环境变量,我不知道EC2配置了没,所以最好配置一下(查询资料得出已经配置),配置完成后可以输入java -version查看版本信息。

下面安装tomcat7
sudo yum install tomcat7

启动tomcat7
sudo service tomcat6 start

如果您现在尝试连接到您的实例在端口8080上, 你应该会得到错误400。没关系,这是因为标准Tomcat样本, 文档和管理web应用程序不包括在tomcat7包和他的的依赖性。 你现在可以安装:
重要
sudo yum install tomcat6-webapps tomcat6-docs-webapp tomcat6-admin-webapps

tomcat连接不上往往就是因为这个原因

这个时候就可以访问tomcat了,在本地浏览器上输入  http://YOUR_PUBLIC_DNS:8080 ,显示的页面就是tomcat
如果不能访问,可能EC2安全组的入占权限没有设置好,把8080公开,任何IP可访问(当然也可以限制为你某个IP)。

接下来就可以在本地操作tomcat,上传java web项目时会要求登录,具体怎么修改配置文件以及设置账号什么的就不详细说明



2016-05-25 16:08:52 huoqi12 阅读数 0

前段时间,本人参与了某项目的从“零”开始的开发与运维。真的是从零开始啊……从项目设计到开发,再到发布、运维,说多了都是泪……还好现在有好多现成的工具可以使用,省了很多时间和精力。

此项目使用AWS,Web 端架构采用 ELB + AutoScalling group,数据库使用RDS,文件存储使用了S3。使用这个架构可以节省很多的运维时间和精力,可以拿更多的时间关注项目的开发。但是这个架构并不包括代码部署的方面,本文主要介绍在代码部署方面自动化运维道路上的各种进化。

项目主要软件环境: Java EE, Spring 4 MVC, maven, tomcat8, gitlab

项目分测试环境和生产环境,生产环境采用ELB+AutoScalling,测试环境只有一台服务器跑tomcat,虽然不是很严谨,但是在前期还是能省(qian)则省了大笑……

在代码部署方面大体经历了以下几个阶段。

石器时代

最开始时在本地开发测试,然后idea 打包上传到服务器上,然后ssh 登陆服务器手动部署代码。每次代码部署都要执行n多操作和命令。有段时间网络不是很好,光上传war 包就耗费十几分钟,对耐心是一场很大的考验。实在受不了这种繁琐的操作时候开始了一步步简化操作。

服务器上部署war 时需要先停止tomcat,然后删除tomcat webapps 目录下ROOT.war 文件和ROOT 目录,然后移动新的ROOT.war 到webapps 下,最后启动tomcat 服务。首先对这个步骤写了个shell 脚本:

“石头锤子” deployWar.sh

#! /bin/bash

if [[ $# -eq 0 ]]; then
        warFile="/home/ec2-user/target/hs-0.1-SNAPSHOT.war"
elif [[ $# -eq 1 ]]; then
        warFile="$1"
else 
        echo "Parameter Error!"
        exit 1
fi

if [[ -f "$warFile" ]]; then
        service tomcat8 stop
        rm -f "/usr/share/tomcat8/webapps/ROOT.war"
        rm -rf "/usr/share/tomcat8/webapps/ROOT"
        cp "$warFile" "/usr/share/tomcat8/webapps/ROOT.war"
        service tomcat8 start
        mv "$warFile" "${warFile%'.war'}_`date +'%Y-%m-%d_%H:%M:%S'`.war"
        echo
        echo Done!
else
    echo "No file: $warFile!"
fi

此“石头锤子”能实现上述war包的部署步骤,并对当前部署的war包进行备份。

然后又出现一个问题,如果改动只有一个或几个文件,完整部署太麻烦,这时可以只上传改动的文件,然后部署就可以了。

“石头镰刀1” updateClasses.sh

#! /bin/bash

service tomcat8 stop
cp -R /home/ec2-user/target/classes/ /home/ec2-user/webapps/ROOT/WEB-INF/
service tomcat8 start
echo Done!

tomcat 的class 文件更新后需要重启tomcat 才能生效,而静态文件如js、css 文件等直接覆盖即可。所以针对静态文件有:

“石头镰刀2” updateStatic.sh
#! /bin/bash

cp -R /home/ec2-user/src/main/webapp/WEB-INF/ /home/ec2-user/webapps/ROOT/
echo Done!

“铁器时代”

当测试情况良好需要部署到生产坏境时,就涉及到从原来的单点部署到集群部署了。原来的脚本和架构也不太适合了,幸好我们有还有铸就“金刚”之身的原料--S3。首先我们将需要部署的war包上传的到S3 的指定目录,登陆需要部署的服务器,下载该war 包并部署。流程很简单,但是需要执行的命令也是繁杂和重复。
“小铁铲”  cpWarToS3.sh
#! /bin/bash

if [[ $# -eq 0 ]]; then
        warFile="/home/ec2-user/target/hs-0.1-SNAPSHOT.war"
elif [[ $# -eq 1 ]]; then
        warFile="$1"
else 
        echo "Parameter Error!"
        exit 1
fi

if [[ -f "$warFile" ]]; then
        echo Copy $warFile to S3...
        aws s3 cp "$warFile" "s3://config.ziyoufang.cn/war/ROOT.war"
        echo Done!
else
    echo "No file: $warFile!"
fi
“大铁锤” deployFromS3.sh
#! /bin/bash
if [[ $# -eq 0 ]]; then
    WAR=ROOT.war
elif [[ $# -eq 1 ]]; then
    WAR=$1
else 
    echo "Parameter Error!"
    exit 1
fi

WAR=`echo $WAR | awk -F '.' '{print $1}'`

service tomcat8 stop
rm -f "/usr/share/tomcat8/webapps/ROOT.war"
rm -rf "/usr/share/tomcat8/webapps/ROOT"
aws s3 cp s3://config.ziyoufang.cn/war/"$WAR".war "/usr/share/tomcat8/webapps/ROOT.war"
service tomcat8 start
log="`date +'%Y-%m-%d %H:%M:%S'` Deploy war from s3. done!"
echo $log > deploy.log
echo
echo Done!

从文件名上就可以看出这两个脚本一个是用来将war 上传到S3,一个是从S3 下载war包并部署的。

“工业时代”

上面还面临着一个重要的问题,就是每次部署都要打包上传完整的war 包到S3,这也是一个比较耗时耗力的过程,对于一个能坐就不站,能躺就不坐的”懒货“来说是一种巨大的折磨。
“烈火” gitlab +“鼓风” maven

gitlab 作为一款优秀的git server 系统,maven 作为一款最常用的包管理软件之一,各位前辈已经提供的了丰富的工具我们就得充分利用。在开发时使用git 做版本控制,gitlab 部署在AWS 上,开发只需要和gitlab 进行sync 即可。然后在服务器上使用mvn clean install 进行打包,并上传到S3上。

“小卡车” updateWarToS3.sh

#! /bin/bash

if [[ $# -eq 0 ]]; then
    BRANCH=master
elif [[ $# -eq 1 ]]; then
    BRANCH=$1
    TARGET=ROOT.war
elif [[ $# -eq 2 ]]; then
    BRANCH=$1
    TARGET=$2
else 
    echo "Parameter Error!"
    exit 1
fi
TARGET=`echo $TARGET | awk -F '.' '{print $1}'`

cd /home/ec2-user/Web_server
git checkout $BRANCH
git pull
mvn clean install

S3_Prefix="s3://config.ziyoufang.cn/war/"
S3_War=$S3_Prefix"$TARGET".war
S3_WarBack=$S3_Prefix"$TARGET""_`date +'%Y-%m-%d_%H:%M:%S'`.war"
warFile="/home/ec2-user/Web_server/target/hs-0.1-SNAPSHOT.war"

echo Backup "$TARGET".war on S3...
aws s3 mv $S3_War $S3_WarBack

echo upload new "$TARGET".war...
aws s3 cp $warFile $S3_War

echo "upload done."


此时部署时只需先执行 updateWarToS3.sh,然后登陆需要部署的服务器执行 deployFromS3.sh 即可。这下干感觉就是从原始社会走出来了~~爽啊~~~


可是……(哎……就怕有可是……)在部署生产环境时,每次都需要执行多个流程:

从ELB 中移除一台EC2 -> 等待connection draining -> 登陆该EC2 -> 执行deployFromS3.sh -> 等待tomcat启动起来  -> 添加该EC2 回ELB -> 等待监控状态检查到InService -> 下一台EC2……

在压力小的情况下执行该操作ELB 后端实例较少,部署几次之后我烦了,交给了另外一个人去部署,(嗯,以邻为壑的感觉挺爽~~)结果他部署了几次之后他也烦了,威胁说撂挑子不干了……无奈只好继续利用我大shell 铸造大杀器了……

“巨型铲车” deployIntoELBBackendInstance.sh

#/bin/bash
# deployIntoELBBackendInstance.sh

succeed_instances=""
failed_instances=""
ELB_NAME=""
DEPLOY_COMMAND="sudo -s /home/ec2-user/target/deployFromS3.sh"

function usage()
{
	echo "Usage: $0 [Option] <parameter>"
	echo "Options:"
	echo "  -b	ELB name"
	echo "  -c	Deployment command. Default: \"$DEPLOY_COMMAND\""
}

function addSuccessInstance()
{
	if [[ $# -ne 1 ]]; then
		return 1
	fi

	#add this intance to "succeed instances list" if get private ip
	if [[ -z "$succeed_instances" ]]; then
		succeed_instances=$instance
	else
		succeed_instances="$succeed_instances",\ "$instance"
	fi
}

function addFailedInstance()
{
	if [[ $# -ne 1 ]]; then
		return 1
	fi
	
	#add this intance to "fialed instance list" if can't get private ip
	if [[ -z "$failed_instances" ]]; then
		failed_instances=$instance
	else
		failed_instances="$failed_instances",\ "$instance"
	fi
}

function deploy()
{
	if [[ $# -ne 1 ]]; then
		return 1
	fi

	privateIp=$1
	# ssh in and deploy on this instance
	echo Deploying...
	ssh $privateIp "$DEPLOY_COMMAND"
	# ssh $privateIp echo deploying...
	for (( i = 0; i < 6; i++ )); do
		echo -n Please wait for retarting tomcat 
		for (( j = 0; j < 10; j++ )); do
			echo -n .
			sleep 1s
		done
		echo .

		testLineNum=`curl -s $privateIp:8080 | grep html | wc -l`
		if [[ $testLineNum -gt 0 ]]; then
			echo tomcat retarts successfully.
			break
		fi
	done
}

#main
while getopts "b:c:" arg
do
	case $arg in
		b )
			ELB_NAME=$OPTARG
			;;
		c ) 
			DEPLOY_COMMAND=$OPTARG
			;;
		? )	
			echo "Unknown argument."
			usage
			exit 1
			;;
	esac
done

if [[ -z "$ELB_NAME" ]]; then
	echo "$0: missing elb name"
	usage
	exit 1
fi
# verify if exist this ELB
verifyELB=`aws elb describe-load-balancers --load-balancer-name $ELB_NAME | grep LoadBalancerName | awk -F '"' '{print $4}'`
if [[ "$verifyELB"x != "$ELB_NAME"x ]]; then
	echo Cannot find Load Balancer $ELB_NAME
	exit 1
fi
# go on if exist this elb
for instance in `aws elb describe-instance-health --load-balancer-name $ELB_NAME | grep InstanceId | awk -F '"' '{print $4}'` ; do
	echo $instance is in progress...

	#loop getting until get private ip
	privateIp=""
	for ip in `aws ec2 describe-instances --instance-ids $instance | grep PrivateIpAddress | awk -F '"' '{print $4}'` ; do
		if [[ -n "$ip" ]]; then
			privateIp=$ip
			break
		fi
	done

	if [[ -z "$privateIp" ]]; then
		addFailedInstance $instance
	else
		#deregister this instance from elb
		aws elb deregister-instances-from-load-balancer --load-balancer-name $ELB_NAME --instances $instance >/dev/null 2>&1
		echo -n Please waitting for deregister $instance from elb $ELB_NAME
		for (( i = 0; i < 20; i++ )); do
			sleep 5s
			echo -n .
			outservice=`aws elb describe-instance-health --load-balancer-name $ELB_NAME --instances $instance | grep OutOfService | wc -l`
			if [[ $outservice -eq 1 ]]; then
				echo
				echo $instance has been deregistered from elb $ELB_NAME
				deploy $privateIp
				#register this instance with elb
				aws elb register-instances-with-load-balancer --load-balancer-name $ELB_NAME --instances $instance >/dev/null 2>&1
				echo -n Please wait for register $instance with elb $ELB_NAME
				for (( j = 0; j < 20; j++ )); do
					sleep 6s
					echo -n .
					inservice=`aws elb describe-instance-health --load-balancer-name $ELB_NAME --instances $instance | grep InService | wc -l`
					if [[ $inservice -eq 1 ]]; then
						echo
						echo $instance has been registered with elb $ELB_NAME
						addSuccessInstance $instance
						break
					fi
					if [[ $j -ge 19 ]]; then
						addFailedInstance $instance
					fi
				done
				echo
				break
			elif [[ $i -ge 19 ]]; then
				echo 
				echo Deregister $instance time out. Process the next
				addFailedInstance $instance
				echo
				continue
			fi
		done
	fi
done

echo 
echo succeed instances: $succeed_instances
echo failed instances: $failed_instances

该脚本能实现自动将指定ELB 下的后端健康实例进行部署,最后会提示部署成功和部署失败的实例。

至此,整个部署流程在updateWarToS3.sh 之后只需要执行deployIntoELBBackendInstance.sh 就可以了。

手执“大铁锤”,开着“巨型铲车”,慵懒的日子~舒坦~~~


2018-04-26 19:01:29 weixin_41470722 阅读数 1540

AWS下搭建Hadoop集群

原帖地址:http://xiaomixin11.iteye.com/blog/2089917

1. 搭建流程
2. 注册AWS账户
3. 创建Ubuntu Instances

登陆到AWS Console之后,在services列表中选中EC2(EC2用于配置云中的基础设备)点击INSTANCES下的instances

进入到instances显示列表,点击Launch Instance

 
   
 
 

 3.1 Choose AMI

 此处选择: Ubuntu Server 14.04 LTS (HVM) - ami-987724ca 

(注:因为此为Free tier,故选用它做测试及讲解,正式在安装的时候,需要根据自已的AMI需求,选择合适的AMI)

 

 

 3.2 Choose Instance Type

此处选择: t2.micro

 (注:因为此为Free tier,故选用它做测试及讲解用,正式安装的时候,需要根据自己的硬件需求选择合适的instance Type )

 3.3 Configure Instance Details
 此处采用默认配置,此处是配置instance网络,访问控制,监控等信息,在正式安装时,可根据需求配置
 3.4 Add Storage
 此处采用默认配置,可以在Instance运行期进行Storage的横向扩展
 3.5 Tag Instance
 此处为instance新增Tag,如有需要可自主新增,这里添加name为masterTest
 3.6 Configure Security Group
 此处配置instance的安全访问列表,相当于防火墙的功能,可控制那个范围的IP地址,可以经过什么端口,通过什么网络协议访问Instance. 可以为一个VPC设置同一个Security Group也可以为多个Instance设置同一个Security Group. 这里由于测试用,故设置了所有的机器都可以通过TCP或ICMP访问Instance的所有端口
 3.7 Review
 最后检查以上配置是否有问题,如都正确,则点击launch,创建并启动Instance
 3.8 Create keypair
 在点击launch时,需要创建keypair以安全的连接Instance,这里会生成一对密钥,并且由instance保存      公钥,由本地保存私钥,当需要连接到Instance时,必须提供私钥。故一定要下载私钥并保存好,一旦      丢失,则无法连接到instance。

此处下拉选择Create a new key pair, 并输入名称。下载后的keypair为pem文件

点击Launch完成Instance的所有配置,最后以同样的方式创建Slave1与Slave2两个Instance

4. 配置Ubuntu SSH

4.1   连接 Instance

点击INSTANCES下的instances, 选中刚刚新增的Instance,点击Connect,会发现连接的方式有2种,一种是通过SSH Client,另一种是通过Java SSH Client. 此处选择第一种,通过PuTTY工具连接Instance

注:由于AWS生成的私钥格式为pem,而PuTTY在连接Instance时需要使用ppk格式的私钥,故需要使用PuTTYGen将pem格式的私钥转换为ppk格式
4.2   配置SSH

 

 

  4.2.1 概述

从图中可以看出,外部的Windows机器在访问Instance时需要传递Private Key. 然后Instance 的SSH Server获取到Private Key,同时根据用户名称,在/home/{username}/.ssh下获得Public Key,并用Public Key对Private Key进行解密验证,如果成功,则连接成功。故在连接时有三个要点需要注意:


  • 必须输入用户登陆名(在PuTTY Configuration的Connection下的Data配置中,需要填写Auto-login username字段,由于是Ubuntu系统,故username必须为ubuntu)
  • 必须传入私钥(在PuTTY Configuration下的Connection->SSH->Auth中,需要配置Private key file for authentication为私钥文件,格式为ppk)
  • 传入的私钥格式是Instance能解析的格式(格式必须是PPK)

 

  4.2.2 获得ppk私钥文件

  下载puttygen工具,将3.8获得的私钥文件test.pem转换为test.ppk

  首先点击load,加载pem格式的私钥(在3.8环节得到),然后点击save private key保存为ppk格式

 

  4.2.3 获得id_rsa私钥文件      
  使用puttygen工具将私钥导出为OpenSSH key(先是load私钥,再点击Conversions->Export OpenSSH key),生成名称为id_rsa的无后缀私钥文件
  4.2.4 上传id_rsa私钥文件
在windows下打开DOS窗口,执行
Java代码  收藏代码
  1. Pscp -i PuttyPrivateKey OpenSSHPrivateKey ubuntu@PublicDNS:/home/ubuntu/.ssh<span>    
  2.   </span><span> </span>  

 

上传至Ubuntu master的.ssh目录下

注:PuttyPrivateKey为Windows连接Ubuntu时需要提供的私钥,需要注明全路径

OpenSSHPrivateKey为生成的用于Ubuntu之间互联的私钥,需要注明全路径

       PublicDNS为Instance的对外DNS
  4.2.5 修改id_rsa访问权限

上传之后还需要修改id_rsa的访问权限,否则会抛出“id_rsa are too open”的异常,这里使用命令

Java代码  收藏代码
  1. chmod 600 id_rsa  

修改访问权限

 4.3 配置hosts

 使用命令 sudo vi /etc/hosts

 删除

 

Java代码  收藏代码
  1. 127.0.0.1 localhost  
 

 

 新增

 

Java代码  收藏代码
  1. 172.31.27.31   masterTest  
  2. 172.31.22.145  slave1Test  
  3. 172.31.22.39   slave2Test  
 

 

注: 到这里就可以使用ssh slave1Test 无密码连接slave1Test机器了

 

 

 5. 安装JDK
 
5.1 安装JDK

 l  首先从http://download.oracle.com/下载JDK,要求是64位的Linux版的gz格式

 l  使用Pscp命令将JDK上传至Ubuntu中

 l  使用tar命令解压JDK至/usr/java/目录中

5.2 配置JDK
   在/etc/profile中配置JDK的环境变量

   

Java代码  收藏代码
  1. export JAVA_HOME=/usr/java/jdk1.7.0_55  
  2. export PATH=$PATH:$JAVA_HOME/bin  
  3. export CLASSPATH=.:$JAVA_HOME/lib/tools.jar  

 

 

5.3 验证JDK
  输入java –version,如果出现版本信息,则安装成功,以同样的方式在其它两台Slave机器上安装JDK
 6.安装Hadoop
 
6.1 安装Hadoop

l  首先从http://hadoop.apache.org/releases.html下载hadoop-2.2.0版本的安装包

l  使用pscp命令将Hadoop安装包上传至Ubuntu中

l  使用tar命令解压Hadoop至/home/Ubuntu/hadoop目录下

l  使用64位的native替换32位的native(由于AWS上的Instance都是64位的,而Hadoop2.2版只有32位的,故有二种方法,       一是直接下载一个64位的native,二是下载hadoop-2.2.0的源码,然后自已编译成64位的hadoop)

6.2 配置Hadoop

6.2.1  配置Hadoop环境变量

在/etc/profile中配置Hadoop的环境变量

Java代码  
  1. export HADOOP_HOME=/home/ubuntu/hadoop/hadoop-2.2.0  
  2. export PATH=$PATH:$HADOOP_HOME/bin  
  3. export JAVA_LIBRARY_PATH=$HADOOP_HOME/lib/native  
  4. export HADOOP_COMMON_LIB_NATIVE_DIR=$HADOOP_HOME/lib/native  
  5. export HADOOP_OPTS="-Djava.library.path=$HADOOP_HOME/lib"  

配置完后,执行source /etc/profile命令使配置生效

6.2.2   配置hadoop-env.sh

进入$HADOOP_HOME/etc/hadoop目录下,修改hadoop-env.sh中的

 

Java代码  
  1. export JAVA_HOME=$JAVA_HOME  

 

$JAVA_HOME为JDK的安装路径
6.2.3  配置core-site.xml
Xml代码  
  1. <configuration>  
  2.    <property>  
  3.      <name>fs.defaultFS</name>  
  4.      <value>hdfs://master:9000</value>  
  5.    </property>  
  6.    <property>  
  7.      <name>io.file.buffer.size</name>  
  8.      <value>131072</value>  
  9.    </property>  
  10.    <property>  
  11.      <name>hadoop.tmp.dir</name>  
  12.      <value>file:/home/ubuntu/hadoop/tmp</value>  
  13.    </property>  
  14.   <property>  
  15.     <name>hadoop.proxyuser.hduser.hosts</name>  
  16.     <value>*</value>  
  17.   </property>  
  18.   <property>  
  19.     <name>hadoop.proxyuser.hduser.groups</name>  
  20.     <value>*</value>  
  21.   </property>  
  22. </configuration>  
 
6.2.4 配置 mapred-site.xml
Xml代码  
  1. <configuration>  
  2.      <property>  
  3.       <name>mapreduce.framework.name</name>  
  4.       <value>yarn</value>  
  5.      </property>  
  6.      <property>  
  7.       <name>mapreduce.jobhistory.address</name>  
  8.       <value>master:10020</value>  
  9.      </property>  
  10.      <property>  
  11.         <name>mapreduce.jobhistory.webapp.address</name>  
  12.         <value>master:19888</value>  
  13.      </property>  
  14. </configuration>  
 
6.2.5  配置 hdfs-site.xml
Xml代码  
  1. <configuration>  
  2. <property>  
  3.    <name>dfs.namenode.secondary.http-address</name>  
  4.    <value>master:9001</value>  
  5. </property>  
  6. <property>  
  7.    <name>dfs.namenode.name.dir</name>  
  8.    <value>/home/ubuntu/hadoop/local/hdfs/name</value>  
  9. </property>  
  10. <property>  
  11.    <name>dfs.datanode.data.dir</name>  
  12.    <value>/home/ubuntu/hadoop/local/hdfs/data</value>  
  13. </property>  
  14. <property>  
  15.       <name>dfs.replication</name>  
  16.       <value>1</value>  
  17.   </property>  
  18. <property>  
  19.   <name>dfs.webhdfs.enabled</name>  
  20.   <value>true</value>  
  21. </property>  
  22. </configuration>  
 
6.2.6   配置 yarn-site.xml
Xml代码  
  1. <configuration>  
  2.   <property>  
  3.    <name>yarn.nodemanager.aux-services</name>  
  4.    <value>mapreduce_shuffle</value>  
  5.   </property>  
  6.   <property>  
  7.    <name>yarn.nodemanager.aux-services.mapreduce.shuffle.class</name>  
  8.    <value>org.apache.hadoop.mapred.ShuffleHandler</value>  
  9.   </property>  
  10.   <property>  
  11.     <name>yarn.resourcemanager.address</name>  
  12.     <value>master:8032</value>  
  13.   </property>  
  14.   <property>  
  15.     <name>yarn.resourcemanager.scheduler.address</name>  
  16.     <value>master:8030</value>  
  17.   </property>  
  18.   <property>  
  19.     <name>yarn.resourcemanager.resource-tracker.address</name>  
  20.     <value>master:8031</value>  
  21.   </property>  
  22.   <property>  
  23.     <name>yarn.resourcemanager.admin.address</name>  
  24.     <value>master:8033</value>  
  25.   </property>  
  26.   <property>  
  27.     <name>yarn.resourcemanager.webapp.address</name>  
  28.     <value>master:8088</value>  
  29.   </property>  
  30. </configuration>  
 
6.2.7  配置masters,slaves

进入到$HADOOP_HOME/etc/hadoop下

  • 配置masters

 

Java代码  
  1. master  

 

  • 配置slaves

 

Java代码  
  1. slave1Test  
  2. slave2Test  

 

 7. 运行并检测Hadoop
 
7.1 拷贝hadoop-2.2.0

 使用

 

Java代码  
  1. scp -r hadoop-2.2.0 Ubuntu@slave1Test:/home/ubuntu/hadoop  
  2. scp -r hadoop-2.2.0 Ubuntu@slave2Test:/home/ubuntu/hadoop  
 

 

 将hadoop-2.2.0拷贝至slave1Test、slave2Test

 

7.2 修改slave1Test、slave2Test的hosts

在slave1Test、slave2Test的/etc/hosts文件中添加行

 

Java代码  
  1. 172.31.27.31   masterTest  
 以便能跟masterTest进行通信

 

7.3 启动Hadoop
  •   格式化namenode,使用命令

 

Java代码  
  1. ./$HADOOP_HOME/bin/hadoop namenode -format  

 

  •    启动hadoop,使用命令

 

Java代码  
  1. ./$HADOOP_HOME/sbin/start-all.sh  
 

 

7.4 检查Hadoop运行状态

    7.4.1 使用JPS命令

  • 在master中使用jps出现SecondaryNameNode、NameNode、ResourceManager
  • 在slave中使用jps出现DataNode、NodeManager
    7.4.2  使用浏览器


 



 

 8. 运行WordCount
 

 8.1 创建文件

  • 在~下创建本地文件夹file,使用命令mkdir file
  • 进入~/file,使用echo “hallo world!” > file1.txt创建file1.txt文件
  • 进入~/file,使用echo “hallo hadoop!” > file2.txt创建file2.txt文件
  • 使用hadoop fs –mkdir /input创建input文件夹
  • 将file1.txt 与file2.txt上传至/input文件夹下,用hadoop fs –put file*.txt /input

完成之后的结果通过

Java代码  
  1. hadoop fs -ls /input  

 查看结果



 

 8.2 运行WordCount小程序
  • 进入到$HADOOP_HOME/share/hadoop/mapreduce/下
  • 使用hadoop jar hadoop-mapreduce-examples-2.2.0.jar wordcount /input /output命令运行WordCount程序
 8.3 运行结果
 
2019-04-03 15:48:00 weixin_30721899 阅读数 140

 

  由于新工作是在AWS PaaS平台上进行开发,为不耽误工作,先整理一下AWS MVS的使用规范,快速上手。对AWS PaaS平台的相关介绍留到以后再来补充。本文几乎是对官方学习文档的整理,有遗漏的后补。

1.简介

  AWS MVC(Model-View-Controller),轻量级、高性能,是开发AWS App的标准框架,同时AWS PaaS自身也是基于它开发。与传统开源的Spring MVC、Strusts2相比:

  • 清晰、干净、请求驱动(请求-响应)的轻量级Web编程框架
  • 学习难度远小于Spring MVCStrusts2
  • No ServletNo JSPJust J2SE,更容易的开发调试和问题追踪
  • 与AWS平台架构兼容的会话安全、DAO、I18N国际化、日志、审计和SLA告警机制
  • 没有值栈、OGNL表达式、标签库等增加代码复杂度和性能下降的架构缺陷
  • 优于Strusts2的注解编程和方法拦截,能直接根据注解绑定Request参数,更简洁
  • 对处理结果是HTML、JSON、XML数据结构和错误码的统一处理,避免业务架构缺陷
  • 提供了一套基于JQueryJQuery Mobile的AWS UI库,针对企业级需求进行了增强封装
  • 内核采用了NIO通信和多线程池化管理,能够充分对计算资源的扩容做出适应,实现纵向扩展(Scale Up)
  • 为多个集群节点提供自我发现和自我纠正能力,简化部署和运维,轻松实现弹性的横向扩展(Scale Out)

特点:基于请求驱动,使用了前端控制器模式设计,再根据请求映射规则分发给相应的后端逻辑控制器(动作/处理器)进行处理

上图,前端控制器(AWS Web Server / Front Controller)和后端处理控制(AWS App Server / Controller)是AWS MVC的核心通信框架,黄色区域(视图组装、模型、模版)写业务逻辑,元素描述:

AWS Web Server 安装有AWS Portal的标准Servlet容器,例如:Tomcat、WebLogic
AWS App Server 安装有AWS Server的应用服务器,所有的业务逻辑在这里处理
Servlet 前端控制器 接收所有来自用户的请求,封装后转发给AWS App服务器
后端处理控制器 通过注解拦截到方法,绑定逻辑处理程序
View视图组装 实现业务逻辑,返回处理的结果
Model模型 资源处理对象,如处理逻辑、对象、国际化、Dao处理、缓存
Template模版 基于静态模版的渲染,处理成动态页面结果

AWS MVC编程框架的主要组成部分:

  • 提交请求(submit)
  • 前端参数解析
  • 后端控制器(controller)
  • 业务对象封装(ModelBean)
  • DAO封装
  • Cache缓存
  • View视图(返回结果)
  • 页面模版渲染(Template)

其它(略):提高生产环境下的SLA服务可用性;应用的分发、安装和卸载;对计算资源的扩容做出适应,实现纵向扩展(Scale Up)、获得线性的横向扩展(Scale Out)

2. 框架详解

2.1 提交请求

AWS MVC基于Command命令请求驱动,约定5类常见url请求地址,都可以通过GET/POST提交

url类型说明
./w text/html (web)-请求结果一定是普通Web页面数据时
./jd application/json (json data)-请求结果一定是ResponseObject Json
./xd application/xml (xml data)-请求结果一定是ResponseObject XML
./df application/octet-stream(成功)
application/json(失败)
(download file)-文件下载流,如下载失败或权限校验不通过,返回Json处理结果
./uf application/json (upload file)-文件上传流,返回Json处理结果

测试:启动完本地AWS服务,浏览器输入请求url:http://localhost:8088/portal/r/w?cmd=API_BENCHMARK_TEST&p=Hello AWS

说明

  1.8088是安装AWS开发服务的默认Web端口号

  2./portal/是默认的Web App根名

  3.返回结果:“Hello AWS”,如下:

·Web可靠性编程习惯

  任何请求在极端环境或服务故障时都会发生错误。如果在浏览器背后(如一个Ajax请求)发送的HTTP返回一个特定数据结构或操作状态值,那么AWS MVC会要求你在Java代码使用ResponseObject对象进行封装,并在前端对result进行检查,只有当resultok时从data中读取结果,否则应该检查errorCodemsg项,并处理该异常(如提醒操作者或其他操作)。模拟一个停机故障:关闭AWS服务,但保持Web服务的正常运行,在浏览器中输入以下URL请求:http://localhost:8088/portal/r/jd?cmd=API_BENCHMARK_TEST&p=Hello AWS

  说明

  1.使用./jd请求一个json数据结果,模拟加载ajax数据

  2.获得了非预期的“Hello AWS”

  {
  result: "error",
  msg: "AWS Instance Server连接失败!(590)"
  }

result值类型

  • ok(操作成功)
  • error(服务端发生错误,错误描述在msg项)
  • warning(服务端发生警告,警告描述在msg项)
 3.查看结果:↓

  

默认编码字符集:utf-8

常规请求必须参数

  • sid(用户会话)
  • cmd(请求指令。命名规范:前缀为该应用的Id,后面是动作名,中间以下划线分割,区分大小写

  

Web层配置文件

  每个AWS App的Web资源被独立的定义在webapp根目录apps/%AppId%下。如果该应用需要cmd处理,至少应定义一个接参配置文件,并建议命名为action.xml如果使用AWS自带的Developer开发工具,该配置文件可忽略,AWS Developer会自动根据Controller类同步构建该配置)。配置文件存为 UTF-8 NO BOM,格式为一个或多个符合Schema规范、以action(不区分大小写)开头、后缀为xml的文件。action.xml位置及内容示例:

  

<?xml version="1.0" encoding="utf-8"?>
<aws-actions>
    <cmd-bean name="%AppId%_xxx1">
        <param name="p1" />
    </cmd-bean>
    <cmd-bean name="%AppId%_xxx2">
        <param name="p1" />
    </cmd-bean>
    <cmd-bean name="%AppId%_xxx3">
        <param name="p1" type="body"/>
    </cmd-bean>
</aws-actions>

实际项目中配置

 cmd-bean的type属性:

  • type不配置时,该参数的值来自请求中查询参数或者application/x-www-form-urlencoded类型内容中请求参数
  • type=body时,该参数的值来自请求内容application/xml、application/json等类型的值,Controller将接收到请求内容的字符串值
  • type=cookie时,该参数的值来自请求内容的cookie,Controller将接收到对应namecookie
  • type=header时,该参数的值来自请求内容的header,Controller将接收到对应nameheader

2.1.1 Form Submit提交

  普通表单提交处理方式,包括HTML的form提交和js提交。如下模拟一个数值运算场景,由浏览器提交两个数值和运算类型,经过前端控制器传参和后端控制器的接参,最终抵达逻辑区

a) HTML提交:

<!doctype html>
<html>
<head>
    <meta charset="UTF-8">
    <title>数据计算</title>
    <!-- JQuery -->
    <script type="text/javascript" src="../commons/js/jquery/scripts/jquery.js"></script>
    <!-- AWS UI -->
    <link rel="stylesheet" type="text/css" href="../commons/css/awsui.css">
    <script type="text/javascript" src="../commons/js/awsui.js"></script>
</head>
<body>
    <form id="editForm" method="post" action="./jd?sid=<#sid>&cmd=%AppId%_calculation">
        <table class="awsui-ux">
            <tr>
                <td class="awsui-ux-title">计算</td>
                <td class="required">
                    <input id="number1" name="number1" class="awsui-textbox" />
                    <select id="sign" name="sign" class="awsui-select">
                        <option value="+">+</option>
                        <option value="-">-</option>
                        <option value="*">*</option>
                        <option value="/">/</option>
                    </select>
                    <input id="number2" name="number2" class="awsui-textbox" />
                    <span> = </span>
                    <input id="results" name="results" class="awsui-textbox" />
                </td>
            </tr>
            <tr>
                <td class="awsui-ux-title" colspan="2" style="text-align: center; ">
                    <span id="submitBtn" class="button blue">提交</span>
                </td>
            </tr>
        </table>
    </form>
</body>
</html>
注:上述源码中,<#sid>在实际运行中由实际的用户会话Id替代

b) JavaScript提交:

$('#submitBtn').click(function(e) {
    // 提交表单
    $('#editForm').submit();
});

c) 前端传参配置(Web层action.xml配置文件)

<?xml version="1.0" encoding="utf-8"?>
<aws-actions>
    <cmd-bean name="%AppId%_calculation">
        <param name="number1" />
        <param name="number2" />
        <param name="sign" />
    </cmd-bean>
</aws-actions>
  • 声明%AppId%_calculation命令,%AppId%是你实际的应用Id
  • 该命令接收number1,number2,sign三个参数

d) 后端接参处理

式1(推荐)

@Mapping("%AppId%_calculation")
public String calculation(UserContext me, int number1, int number2, @Param(defaultValue = "+")String sign) {
    TestWeb web = new TestWeb(me);
    return web.calculation(number1, number2, sign);
}
  • 显示定义与变量名匹配的方法参数
  • UserContext是AWS MVC内置对象,获得当前操作者上下文信息
方式2
@Mapping("%AppId%_calculation")
public String calculation(UserContext me, RequestParams params) {
    // 参数接收
    int number1 = params.getInt("number1");        // 获取整数类型
    int number2 = params.getInt("number2");        // 获取整数类型
    String sign = params.get("sign", "+");         // 获取字符串类型,第二个参数为默认值,以下方式相同
//    Boolean bool = params.getBoolean('paramsName');   // 获取Boolean类型
//    Double number = params.getDouble('paramsName');   // 获取浮点型参数
    TestWeb web = new TestWeb(me);
    return web.calculation(number1, number2, sign);
}
  • 不定义方法参数,动态从RequestParams对象中读取变量
  • 这种方式可以让你的代码更灵活,但不利于重构

e) View层逻辑处理

public class TestWeb extends ActionWeb {
    public TestWeb() {
    }
    public TestWeb(UserContext ctx) {
        super(ctx);
    }
    public String calculation(int number1, int number2, String sign) {
        ResponseObject ro = ResponseObject.newOkResponse();
        // 处理逻辑
        // ro.put("newNum",newNum);
        return ro.toString();
    }
}

2.1.2 Ajax请求

1)AWS在JQuery ajax基础上对常用异步请求封装,提供4类请求(request、load、post、get),并对底层进行了统一的处理,如异常拦截。方法说明:

  1.awsui.ajax.request

    a) 通过 HTTP 请求加载远程数据,参考jQuery.ajax()。

    b) 参数:url,[settings]。分别是String,Object类型
    url:一个用来包含发送请求的URL字符串。
    settings:AJAX 请求设置。所有选项都是可选的。

  2.awsui.ajax.load

    a) 载入远程 HTML 文件代码并插入至 DOM 中。默认使用 GET 方式 - 传递附加参数时自动转换为 POST 方式

    b) 参数:url,[data,[callback]]。类型:String,Map/String,Callback
    url:待装入 HTML 网页网址。
    data:发送至服务器的 key/value 数据。
    callback:载入成功时回调函数

  3.awsui.ajax.post

    a) 简单POST 请求,成功可调用回调函数

    b) 参数:url,[data],[callback],[type]。类型:String,Map,Function,String
    url:发送请求地址。
    data:待发送 Key/value 参数。
    callback:发送成功时回调函数。
    type:返回内容格式,xml, html, script, json, text, _default

  4.awsui.ajax.get

    a) 简单GET 请求。请求成功时可调用回调函数

    b) url,[data],[callback],[type]。类型:String,Map,Function,String
    url:发送请求地址。
    data:待发送 Key/value 参数。
    callback:发送成功时回调函数。
    type:返回内容格式,xml, html, script, json, text, _default。

  5,awsui.ajax.ok

    a) 验证是否为返回状态是否为成功

    b) data:ajax请求后的返回对象

  6.awsui.ajax.responseObject

    a)验证是否为ResponseObject对象

    b)data:字符串或对象。

  7.awsui.ajax.alert

    a)弹出请求提示消息

    b)data, model, callback 。类型:Object, boolean,Function
    data: 异步请求返回对象。
    model: 消息提示框的展示模式,是否为模态。
    callback:弹出消息提示后回调函数

示例代码

awsui.ajax.request 示例

awsui.ajax.request({
    type: "POST",
    url: "./jd?sid="+sid+"&cmd=%AppId%_calculation",
    data: "number1=15&number2=1032&sign=+",
    ok : function(r) {
        //请求处理成功
    },
    err : function(r){
        //请求处理错误
    }
});

awsui.ajax.load 示例

awsui.ajax.load("./jd?sid="+sid+"&cmd=%AppId%_calculation", { number1: 15, number2: 1032, sign: '+' }, function(data){
    awsui.ajax.alert(data, true, function(){ alert('callback'); });
});

awsui.ajax.post 示例

awsui.ajax.post("./jd?sid="+sid+"&cmd=%AppId%_calculation", { number1: 15, number2: 1032, sign: '+' }, function(data) {
    awsui.ajax.alert(data, true, function(){ alert('callback'); });
}, 'json');

awsui.ajax.get 示例

awsui.ajax.get("./jd?sid="+sid+"&cmd=%AppId%_calculation", { number1: 15, number2: 1032, sign: '+' }, function(data) {
    if(awsui.ajax.responseObject(data))
        awsui.ajax.alert(data, true, function(){ alert('callback'); });
}, 'json');

awsui.ajax.ok 示例

if(awsui.ajax.ok(data))
    alert(data['msg']);

awsui.ajax.responseObject 示例

if(awsui.ajax.responseObject(data))
    awsui.ajax.alert(data, true, function(){ alert('callback'); });

awsui.ajax.alert 示例

awsui.ajax.alert(data, true, function(){ alert('callback'); });

2)后端接参

@Mapping("%AppId%_calulation")
public String calculation(UserContext me, int number1, int number2, String sign) {
    TestWeb web = new TestWeb(me);
    return web.calculation(number1, number2, sign);
}

3)View层逻辑处理

public class TestWeb extends ActionWeb {
    public TestWeb() {
    }
    public TestWeb(UserContext ctx) {
        super(ctx);
    }
    public String calculation(int number1, int number2, String sign) {
        ResponseObject ro = ResponseObject.newOkResponse();
        // 处理逻辑
        // ro.put("newNum",newNum);
        return ro.toString();
    }
}

2.1.3 上传文件

AWS容器对各资源提供沙箱管理,对文件读写提供了一套完善的DC(Doc Center)机制,文件必须通过DC处理器进行处理。AWS对文件上传封装了一个公用的组件,能够满足常用浏览器(IE8+, Firefox,Google Chrome,Safari,平板UC等)的文件上传功能。

a)服务端插件注册和声明

注册DCPluginProfile要声明repositoryName(存放文件的根目录名)和文件处理器(继承com.actionsoft.bpms.server.fs.AbstFileProcessor)。

  • 有关DC开发详细内容,参见《AWS 插件扩展开发参考指南》
  • 有关DC API操作(如读、写文件),参见aws-api-doc提供的DCAPI

b)客户端调用

upfile是AWS UI封装的通用文件上传组件,采用双核技术(IE8/IE9提供Flash模式,对支持HTML5的浏览器采用无插件模式)实现批量文件上传、文件类型过滤、文件大小控制和上传进度控制,对各类浏览器提供了较好的体验支持。

  i)上传组件的资源引用

<link rel="stylesheet" type="text/css" href="../commons/css/awsui.css"/>
<script type="text/javascript" src="../commons/js/jquery/scripts/jquery.js"></script>
<script type="text/javascript" src="../commons/js/awsui.js"></script>

  ii)用JavaScript打开上传对话框

<script type="text/javascript">
$(function(){
    //myUpfile对象绑定upfile组件
    $("#myUpfile").upfile({
        sid: "<#sid>", // 会话ID
        appId: "com.actionsoft.apps.poc.plugin", // 应用ID
        groupValue: "dir1", // DC大类,建议变量规则
        fileValue: "dir2", // DC小类,建议变量规则
        numLimit: "2", //最多一次允许上传几个,0(无限制)
        filesToFilter : [["Images (*.jpg; *.jpeg; *.gif; *.png; *.bmp)","*.jpg; *.jpeg; *.gif; *.png; *.bmp"]],
        repositoryName: "myfile", // 该应用申请的DC名
        done: function(e, data){
            //事件回调函数
            //上传完成后,开始调用导入代码
            if (awsui.ajax.ok(data.result.data)) {//判断请求是否执行成功
                //可以调用公共方法处理提示信息
                awsui.ajax.alert(data.result.data);
                //或者自行处理提示信息
                //$.simpleAlert('文件上传成功!');
                // downloadURL
                var url = data.result.data.data.attrs.url;
                //如果定义了其他返回的属性,也需要使用data.result.data.data.attrs调用
            } else {
                // 上传失败,提示出错误信息
                awsui.ajax.alert(data.result.data);
            }
        }
    });

});
</script>

  iii)上传按钮的定义

<span id="myUpfile" class="button green" onclick="return false;">上传</span>

上述客户端调用示例,也可从AWS企业应用商店安装扩展插件概念验证应用,所有源码在src目录下

c)参数说明

  • 属性
NameTypeDescriptionDefault
sid(*-必须) String 会话ID  
appId(*-必须) String 应用ID  
repositoryName(*-必须) String DC插件定义  
groupValue(*-必须) String 文件大类  
fileValue(*-必须) String 文件小类  
filesToFilter String var filter = [["Images (.jpg; .jpeg; .gif; .png; .bmp)",".jpg; .jpeg; .gif; .png; .bmp"]]; 不过滤,可上传所有文件
sizeLimit number 文件大小限制 25*1024*1024(25M)
numLimit int 上传个数限制 0(无限制)
  • 事件

 

NameParamDescription
add e 事件;data(返回的数据) 文件被添加到上传列表时触发,当返回false时,可阻止上传。
例如: //data.files 为上传文件的数组
add:function(e, data){
     //data.files 为上传文件的数组
     $.each(data.files, function(index, file) {
        // file.name 文件名称 file.size 文件大小 file.type 文件类型
    }) if(size==0){ //空文件不允许上传 return false;
} }
progress e 事件;data(返回的数据) 文件上传中触发
done e 事件;data(返回的数据) 单个文件上传完毕后触发
error e 事件;data(返回的数据) 单个文件上传失败后触发。
可能是web服务器也可能是app服务器造成的失败
complete   文件成功上传结束后触发

2.1.4 下载文件

下载DC仓库内文件的url必须由DCContextgetDownloadURL()获取。

  • 以下是获取文件下载链接的Java示例

    // 构造下载附件的url链接
    UserContext me = getContext();
    String appId = "xxxxxx";                //该App Id名
    String repositoryName = "orderFile";    //该App的DC仓库根目录
    String groupValue = yearMonth;          //一级目录为年月,201402
    String fileValue = orderId;             //订单Id
    String fileName = "order.jpg";          //文件名
    DCContext context = new DCContext(me, SDK.getDCAPI().getDCProfile(appId, repositoryName), appId, groupValue, fileValue, fileName);
    result.put("url", context.getDownloadURL());
    
  • JavaScript示例

    window.open(url);
    // or
    window.location.href = url;

 

2.2 后端处理控制器

就这么简单,提交的请求已经被AWS MVC传输至AWS应用层。

AWS MVC的后端控制器以@Controller注解到普通的Java类,并为该方法增加@Mapping("%Command%")注解即可。控制器会负责将前端cmd参数名与该Java方法的参数名进行匹配和赋值,完成拦截、绑定和执行过程。

下面是一个Controller示例

@Controller
public class ABCController {
    // Test1
    @Mapping("%AppId%_xxx1")
    public String apiTestHome(UserContext me, String p1) {
        return "Hi,p1="+p1;
    }
    //Test2
    @Mapping("%AppId%_xxx2")
    public String apiInfo(UserContext me, String p2) {
        ABCWeb abc= new ABCWeb(me);
        return abc.getMainPage(p2);
    }
}

注解

控制器只负责拦截、绑定和执行cmd方法,对于该方法的具体实现应交给View层处理,不建议直接在Controller中完成逻辑处理。

注解说明
@Controller 类注解。声明该类是一个后端处理控制器
@Mapping 方法注解。声明该方法将响应一个前端Web请求,参数值是该cmd值

默认每个请求必须含有sid的会话信息,如开发者要求在无session场景下执行服务端请求,可参考如下语法

@Mapping(value = "%AppId%_xxx3",
session = false,
noSessionEvaluate = "无安全隐患",
noSessionReason = "用于MVC框架稳定性测试")
  • value,cmd的名称
  • session,是否拦截sessionId进行合法性校验
  • noSessionEvaluate,对安全隐患做出评估说明
  • noSessionReason,设计该cmd的原因或功效

被标记为无session验证的请求是非常不安全的,原因是AWS无法识别请求者身份。开发者应审慎评估该请求背后执行的逻辑规则,不会被用于恶意处理

参数映射

以下参数可以出现在方法参数中

参数说明
UserContext对象类型 【可选】获取AWS用户会话对象
RequestParams对象类型 【可选】获取请求参数,Key为变量名,Value为值
clientIp变量名 【可选】String类型。客户端ip地址
responseType变量名 【可选】String类型。请求结果类型(W/JD/XD...)见Message常量
%变量名% 【可选】cmd的参数名,支持String、Integer、Boolean、Long、Double类型。变量的命名来自具体请求中提供的参数

package包结构命名建议

  • /model/ 存放modelBean类
  • /dao/ 存放DAO类
  • /cache/ 存放cache类
  • /web/ 存放view类
  • /util/ 存放util或service逻辑处理类

注意事项

第一次创建Controller类并进行调试时,需要编译jar文件至该app的lib目录下,否则可能会提示找不到cmd异常。这是一个设计缺陷,我们计划在时间充分的时候修复

下图为实例:

 

2.3 View视图

不建议直接在处理控制器中完成业务处理过程。

在AWS MVC框架中,View层负责实现具体的业务逻辑,组织处理结果。View提供客户端用户会话、身份及设备等信息,通过继承ActionWeb,完成View的开发。

开发示例

public class ABCWeb extends ActionWeb {
    public ABCWeb (UserContext uc) {
        super(uc);
    }
    public String getMainPage(String p2) {
        return “Hi,p2=”+p2;
    }
}

异常处理

当操作发生错误时,框架将抛出uncheck异常(如AWSDataAccessException),如果你的逻辑没有方案或需求去处理这个异常可以继续向外抛出。

当操作发生参数非法、执行非法等常见View层处理逻辑场景时,建议抛出如下异常(详细请参见异常处理章节)

  • AWSIllegalArgumentException 非法参数异常造成错误的访问请求,对应400错误
  • AWSObjectNotFindException 资源未找到异常,对应404错误
  • AWSForbiddenException 访问被拒绝异常,对应403错误

框架范围之外的util或service层

如果业务处理逻辑相对复杂,建议将逻辑操作封装成util类或service类

识别访问者设备类型

通常你在为PC端浏览器的界面交互编程。如果需要你的程序能够更好的服务于其他移动设备,可以调用UserContext.getDeviceType()方法获取到当前用户的设备类型。

  • LoginConst.DEVICE_PC PC桌面电脑
  • LoginConst.DEVICE_TABLET 平板电脑
  • LoginConst.DEVICE_MOBILE 智能手机

与View相关的常见开发

  • Server端开发
    • 模版渲染(HTML静态文件+标签)
    • ModelBean封装
    • DAO封装
    • Cache封装
  • Web端开发
    • JavaScript
    • CSS
    • 熟悉portal/commons下的各种组件资源,如AWS UI、JQuery Mobile

2.3.1 HTML Document

  AWS MVC提供了页面模版处理框架,通过定义模版变量和程序对变量的处理生成最终页面内容。

  提交请求的响应结果通常是一个完整的HTML Document。在一些前端动态拼装场景,局部DOM结构也可以被独立的定义成模版文件。但有时局部DOM无需使用模版,直接由程序动态组装。

  模版被存放在该应用安装目录的template/page下,通常以html或htm后缀结尾(命名区分大小写)。除内容符合标准HTML、CSS、JavaScript规范外,模版标签变量定义的规则是,将需要程序生成的部分以<#变量名>替换,最终由HtmlPageTemplate.merge()混合成用户浏览器中的html内容。

如果该模版中出现的文字需要进行多语言处理,可以<I18N#变量名>定义,其中变量名是为该应用抽取的多语言配置项的Item的Key。

在View层程序中完成模版处理的示例

public class ABCWeb extends ActionWeb {
    public ABCWeb (UserContext uc) {
        super(uc);
    }
    public String getMainPage(String p2) {
        Map<String, Object> macroLibraries = new HashMap<String, Object>();
        macroLibraries.put("page_title", "hello");
        macroLibraries.put("p2", p2);
        macroLibraries.put("sid", getContext().getSessionId());
        return HtmlPageTemplate.merge("%AppId%", "模版文件名", macroLibraries);
    }
}

HTML模板示例

<!DOCTYPE html>
<html>
<head>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
    <title><#page_title></title>
    <!-- 在JS中使用变量,如果需要 -->
    <script>
        var sid = '<#sid>';
    </script>
</head>
<body>
    <form action="./w" method=post name=frmMain >
        <!-- 在HTML中使用变量,如果需要 -->
        p2的值是<b><#p2></b>
     </form>
</body>
</html>

模版文件默认编码字符集

utf-8

HTML模版资源引用范围

  • AWS平台Web目录commons/下的js、css、图片等公共资源
  • AWS平台Web目录apps/%appId%/下自定义的资源(appId为本应用的id)
  • 建议使用相对路径,如../apps/xxx、../commons/xxx

2.3.2 非HTML结果

一些来自前端Ajax操作或服务请求通常需要响应一个结构化数据:

  • 一个操作结果是否成功
  • 一个被封装的数据结构

类型支持:

  • JSON
  • XML

ResponseObject

com.actionsoft.bpms.commons.mvc.view.ResponseObject

ResponseObject对象是AWS为开发者提供的一个通用数据对象封装,用于封装一个结构化处理结果。ResponseObject支持三种处理状态:

常量说明
ResponseObject.SUCCESS ok 被成功处理。对应AWS UI中simpleAlertd的ok或info提示
ResponseObject.WARNING warning 操作发生警告。对应AWS UI中simpleAlertd的warning提示
ResponseObject.ERROR error 操作发生错误。对应AWS UI中simpleAlertd的error提示

状态值可在返回结果的result项检查,这是一个必须结构

msg-信息

当处理成功或发生失败时,应该将进阶的信息反馈给调用者。

//success msg
return ResponseObject.newOkResponse("a订单被取消").toString();
//或者
ResponseObject ro=ResponseObject.newOkResponse();
ro.msg("a订单被取消");
return ro.toString();

//warning msg
return ResponseObject.newWarnResponse("a订单已经发出").toString();
//或者
ResponseObject ro=ResponseObject.newWarnResponse();
ro.msg("a订单已经发出");
return ro.toString();

//error msg
return ResponseObject.newErrResponse("a订单取消失败,原因是xxxx").toString();
//或者
ResponseObject ro=ResponseObject.newErrResponse();
ro.msg("a订单取消失败,原因是xxxx");
return ro.toString();

补充信息可在返回结果的msg项检查,这是一个附加结构

data-数据

以key/value形式由开发者自定义,value可以是一个简单Java对象也可以是一个集合对象。

Map<String, Object> orderDetails = new HashMap<>();
orderDetails.put("orderId", "001");
orderDetails.put("customerId", "999");
orderDetails.put("customerName", "Actionsoft");
orderDetails.put("amount", Double.valueOf("980.01"));
ResponseObject ro = ResponseObject.newOkResponse();
ro.put("enabled", true);
ro.put("orderDetails", orderDetails);
return ro.toString();

数据可在返回结果的data项检查,这是一个附加结构

处理成JSON

默认ResponseObjecttoString()将数据结构拼装成JSON串。

return ro.toString();

含有状态信息的JSON结构

{
    data: {
        enabled: true,
        orderDetails: {
            amount: 980.01,
            customerName: "Actionsoft",
            customerId: "999",
            orderId: "001"
        }
    },
    msg: "",
    result: "ok"
}
return ro.toDataString();

只有数据的JSON结构

{
    enabled: true,
    orderDetails: {
        amount: 980.01,
        customerName: "Actionsoft",
        customerId: "999",
        orderId: "001"
    }
}

处理成XML

return ro.toXML();

含有状态的XML Document片段

<result type="ok" errorCode="" msg="" />

数据来自文件系统

如果你的Java程序读取本地文件系统的文件,建议文件名和内容强制以utf-8读和写。这样做的原因是中文系统的Window默认是936字符集(可以使用chcp命令查看和设置),而大部分OS Server以utf-8进行编码。

如果程序强制以utf-8处理文件,当AWS PaaS在部署一段时间后,客户希望在Windows和Linux之间做迁移时,能够确保用户数据编码格式的一致性。

 

2.4 业务对象封装

与其他MVC开发框架一样,编写出结构清晰的程序代码,需要对业务实体对象进行属性封装(概念如POJOJavaBean)。

AWS MVC提供了设计业务实体对象的父类ModelBeanIModelBean接口,采用ModelBean封装的业务实体对象,还有以下优势:

  • 提供方法转换成JSON数据结构
  • 提供方法转换成XML数据结构
  • 作为DAO处理的实体表结构对象
  • 作为集群Cache的数据结构对象

TestModel开发示例

public class TestModel extends ModelBean implements IModelBean {
    private String id;
    private String f1;
    private double f2;

    public TestModel() {
    }

    public String getId() {
        return id;
    }
    public void setId(String id) {
        this.id = id;
    }
    public String getF1() {
        if (f1 == null) {
            f1 = "";
        }
        return f1;
    }
    public void setF1(String f1) {
        this.f1 = f1;
    }
    public double getF2() {
        return f2;
    }
    public void setF2(double f2) {
        this.f2 = f2;
    }
}

IModelBean接口声明

/**
 * AWS MVC框架中,表示实体业务对象接口
 */
public interface IModelBean extends Serializable {
    /**
     * 将当前对象转换成json处理对象
     */
    public JSONObject toJsonObject();
    /**
     * 将当前对象转换成json串
     */
    public String toJson();
    /**
     * 将当前对象转化成XML片段
     */
    public String toXML();
}

2.5 DAO封装

  AWS的DAO基于对JDBC的直接封装,与当今流行的Hibernate、iBATIS相比,去除了O/R映射关系的直接依赖,并借鉴了Apache DBUtils设计思想,只在需要的场景完成O/R Mapping。

  采用AWS MVC的DAO优势如下:

  • 代码简单、直接,性能高
  • 无需拼写SQL语句,自动提供表查询、删除和更新操作
  • 屏蔽分页技术,自动适应不同数据库的物理分页
  • 提供自定义的RowMapper,将JDBC对象转换成业务实体对象(ModelBean
  • 提供操作复杂数据库操作的DBSql工具类
  • 所有操作受控于AWS SLA的质量和性能监控、告警

DaoObject类和方法介绍

Dao(Data Access Object)是一个设计模式,它是对数据访问的一种抽象。com.actionsoft.bpms.commons.mvc.dao.IDaoObject是AWS设计的数据库访问接口,DaoObject是实现了IDaoObject接口用于访问AWS本地数据库的抽象父类,该类对单主键数据库表的查询/更新/删除等常规操作提供了实现。

TestDao开发示例

  1. 继承ModelBean,实现业务实体对象
  2. 继承DaoObject,实现Dao处理对象

    public class TestDao extends DaoObject<TestModel> {
    
     public TestDao() {
     }
    
     /**
      * 插入一条测试记录
      */
     @Override
     public int insert(TestModel model) throws DataAccessException{
         model.setId(UUIDGener.getUUID());
         String sql = "INSERT INTO " + entityName() + "(ID,F1,F2)VALUES(:ID,:F1,:F2)";
         Map<String, Object> paraMap = new HashMap<>();
         paraMap.put("ID", model.getId());
         paraMap.put("F1", model.getF1());
         paraMap.put("F2", model.getF2());
         return DBSql.update(sql, paraMap);
     }
    
     /**
      * 更新一条测试记录
      */
     @Override
     public int update(TestModel model) throws DataAccessException{
         if (UtilString.isEmpty(model.getId())) {
             throw new DataAccessException("Method getId() Does Not Allow Empty!");
         }
         Map<String, Object> paraMap = new HashMap<>();
         paraMap.put("F1", model.getF1());
         paraMap.put("F2", model.getF2());
         // 不需要写sql,可调用基类封装的update方法
         return update(model.getId(), paraMap);
     }
    
     /**
      * 封装测试
      *
      * @param id
      * @param f2
      * @return
      */
     public int updateF2(String id, double f2) throws DataAccessException{
         Map<String, Object> paraMap = new HashMap<>();
         paraMap.put("F2", f2);
         return update(id, paraMap);
     }
    
     /**
      * 封装DBSql测试
      *
      * @return
      */
     public long count() {
         return DBSql.getLong("SELECT COUNT(ID) AS C FROM " + entityName(), "C");
     }
    
     /**
      * 封装DBSql测试
      *
      * @param f2
      * @return
      */
     public List<TestModel> queryByF1(String f1) {
         return query("F1=?", f1).orderBy("F2").desc().list();
     }
    
     /**
      * 该Dao实现的表名称
      */
     @Override
     public String entityName() {
         return "TEST_DAO";
     }
    
     /**
      * 构建该Dao从一条记录转换成对象的映射对象
      */
     @Override
     public RowMapper<TestModel> rowMapper() {
         return new Mapper();
     }
    
     /**
      * TestDao Mapper
      */
     private class Mapper implements RowMapper<TestModel> {
         public TestModel mapRow(ResultSet rset, int rowNum) throws SQLException {
             TestModel model = new TestModel();
             try {
                 model.setId(rset.getString("ID"));
                 model.setF1(rset.getString("F1"));
                 model.setF2(rset.getDouble("F2"));
             } catch (Exception e) {
                 e.printStackTrace();
             }
             return model;
         }
    
     }
    }
    

    异常处理

    当操作发生错误时,框架将抛出uncheck异常(如AWSDataAccessException),如果你的逻辑没有方案或需求去处理这个异常可以继续向外抛出。

当操作发生参数非法、执行非法等常见Dao处理逻辑场景时,建议抛出如下异常(详细请参见异常处理章节)

  • AWSIllegalArgumentException 非法参数异常造成错误的访问请求,对应400错误
  • AWSObjectNotFindException 资源未找到异常,对应404错误
  • AWSForbiddenException 访问被拒绝异常,对应403错误

默认编码字符集

utf-8

事务处理与资源释放

Spring MVC自动接管了事务与资源释放,这是非常棒的编程体验。为提高性能,目前AWS DAO框架未接管JDBC事务和自动完成Connection释放。

当你的程序需要事务支持时,可以遵循标准的JDBC编程规范,对Connection对象进行事务的开启、回滚或提交。

如果你的程序获得了一个新的Connection(如通过DBSql.open()),那么最终需要你的代码通过DBSql.close()释放这个连接。

物理表

建议使用AWS的BO模型设计和维护你的物理表结构,如果某些表必须由自己的sql创建(不推荐),那么需要遵循App开发规范中约定的表名前缀和sql安装/升级脚本规范。

数据库连接池

AWS MVC的数据库连接池使用了tomcat-jdbc,相关高级参数调优,可修改对应AWS安装目录bin/conf/db_pool.properties文件。

DBSql类和方法介绍

com.actionsoft.bpms.util.DBSql是和AWS数据库交互的工具类,它基于PreparedStatement实现了数据库的查询、插入、更新、批处理,DBSql一般用于DaoObject中。

  • get方法用于简单类型查询
  • query方法用于自定义复杂查询
  • update方法用于数据库的更新/插入/删除
  • batch方法用于批处理
  • open/close数据库连接/释放
  • 其它工具方法
// 查询单个字段
String value_a = DBSql.getString("select a from table_test where id=?", new Object[]{"id1"});

// 查询多个字段
Map<String,Object> value_map = DBSql.getMap("select a,b,c from table_test where id=?", "id1");

// 查询返回自定义对象,MyBean是java类
List<MyBean> list = DBSql.query("select a,b,c from table_test where id=?", new RowMapper<MyBean>() {

    @Override
    public MyBean mapRow(ResultSet rs, int rowNum) throws SQLException {
        MyBean mybean = new MyBean();
        mybean.setA(rs.getString("a"));
        return mybean;
    }

}, "id1");

详细请参见aws-api-docDBSql

 

2.6 Cache缓存

AWS MVC的Cache框架支持本地缓存和集群缓存,可以通过扩展AWS平台的Cache插件(Plugin)实现自己的缓存对象。

有关Cache开发详细内容,参见《AWS 插件扩展开发参考指南》

 

3 异常处理框架

  AWS MVC框架的异常处理过程如下所示

异常处理框架

在上图中,请求者(用户或服务API)被前端控制器封装成指令并传输至AWS服务器,当图中红色、橙色和黄色区域异常发生后,由AWS的顶层异常拦截器捕获,向请求者返回错误消息。

在这个章节中,你将了解如下内容:

  • 错误码定义
  • 抛出异常
  • 处理异常

3.1 错误码

  通过对错误码定义,能够简单的帮助用户或开发者识别和理解异常性质,错误码与错误不是一对一关系,是错误类型的一种抽象代号。

  AWS的错误码参照了HTTP状态码定义。错误码以3位的数字代码表达,所有错误码首数字代表的一类错误。

首数字说明
4 客户端错误
5 服务端错误
7 服务可用性
8 配合限制和其他

  当请求返回了AWS非预期消息或错误时,除了提供错误码,通常我们会根据具体的场景提供问题概要说明,如果是出错类异常,详细信息一定被记录到AWS的error.log文件。

 

码表

Code描述异常或发生场景
400 错误的参数请求(Bad Request)

通常当缺少必要参数,或者参数值格式不正确时,会返回该错误信息。此时可以查看相关文档确认每个参数的格式是否正确。例如:执行启动流程方法时,未提供processInstId参数
AWSIllegalArgumentException

* 当客户端请求AWS服务时,若处于该场景将以该错误码返回到ResponseObject数据结构
401 未授权被拒绝(Unauthorized)

当用户提供了无效的session,或者请求的鉴权结果不正确时,会返回该错误信息。
目前只适用于SOAP/REST API的Web层封装的错误代码,不适用于服务器端异常处理

当客户端请求AWS服务时,若处于该场景将以该错误码返回到ResponseObject数据结构
403 访问被拒绝(Forbidden)

该请求是有效的,但操作是被禁止且不应该再次尝试时,会返回该错误信息。例如:要执行的任务实例已结束
AWSForbiddenException

* 当客户端请求AWS服务时,若处于该场景将以该错误码返回到ResponseObject数据结构
404 找不到资源对象(Not Find Resource)

当用户试图访问不存在的资源时,会返回该错误信息。例如:要执行的任务实例已不存;账户不存在
AWSObjectNotFindException
405 请求方法不允许(Method Not Allowed)

使用了不支持的请求方法去操作资源时,会返回该错误信息。例如:使用GET请求一个POST操作
* 目前只适用于REST API的Web层封装的错误代码,不适用于服务器端异常处理
408 资源请求超时(Request Timeout)

请求超出了等待时间,会返回该错误信息。例如:在调用AWS的SOAP或REST服务,连接至AWS服务器超时
* 目前只适用于SOAP/REST API的Web层封装的错误代码,不适用于服务器端异常处理
500 内部错误(Internal Server Error)

当服务器执行请求过程中,遇到内部处理错误时,会返回该错误信息。遇到这种错误,请检查AWS的错误日志,及时与我们联系
AWSException
所有继承该异常子类,未实现错误号的异常(getAPIErrorCode())
所有JDK或非AWS异常抛出,被系统捕获的

* 当客户端请求AWS服务时,若处于该场景将以该错误码返回到ResponseObject数据结构
520 引擎错误(Engine Error)

当服务器执行请求过程中,遇到流程引擎、表单引擎等引擎类错误时,会返回该错误信息。遇到这种错误,请检查AWS的错误日志,及时与我们联系
AWSEngineException
AWSExpressionException
521 传输转换错误(Transfer Error)

当服务器执行请求过程中,遇到导入导出或格式转换错误时,会返回该错误信息。遇到这种错误,请检查AWS的错误日志,及时与我们联系
AWSTransferException
530 应用容器错误(App Container Error)

当服务器执行请求过程中,遇到PaaS应用容器类错误时,会返回该错误信息。遇到这种错误,请检查AWS的错误日志,及时与我们联系
AppContainerException
540 应用商店错误(AppStore Error)

当服务器执行请求过程中,遇到PaaS与AppStore相互处理过程中发生错误时,会返回该错误信息。遇到这种错误,请检查AWS的错误日志,及时与我们联系
AppStoreException
550 业务错误(BPMN Error)

当BPMN引擎执行时由开发者抛出BPMNError业务异常时,会返回该错误信息。遇到这种错误,请与业务开发者联系
BPMNError
590 AWS Instance Server连接失败

当客户端以HTTP(S)经Web连接至AWS服务器时,未能正常建立通信连接时会返回该错误信息。遇到这种错误,可能是AWS服务已停止或Web与AWS之间的网络故障
Web层错误
591 处理AWS Instance Server响应时发生错误

当客户端以HTTP(S)与AWS服务器建立连接后,在发生指令请求等待返回结果发生错误时,会返回该错误信息。遇到这种错误,请检查AWS的错误日志,及时与我们联系
Web层错误
760 服务正在启动(Instance Starting)

当服务器正在启动尚未就绪时,会返回该错误信息。遇到这种错误,请稍后执行
*当客户端请求AWS服务时,若处于该场景将以该错误码返回到ResponseObject数据结构
761 服务正在关闭(Instance Stoping)

当服务器正在关闭时,会返回该错误信息。遇到这种错误,请不要再重复请求,服务器将被关闭
* 当客户端请求AWS服务时,若处于该场景将以该错误码返回到ResponseObject数据结构
762 服务脱机(Instance Offline)

当服务器处于运行中,由运维人员暂停客户端响应时,会返回该错误信息。遇到这种错误,请联系系统管理员
* 当客户端请求AWS服务时,若处于该场景将以该错误码返回到ResponseObject数据结构
770 应用正在启动(App Starting)

当访问的应用正在启动尚未就绪时,会返回该错误信息。遇到这种错误,请稍后执行
* 当客户端请求AWS服务时,若处于该场景将以该错误码返回到ResponseObject数据结构
771 应用正在关闭(App Stoping)

当访问的应用正在关闭时,会返回该错误信息。遇到这种错误,请不要再重复请求,应用将被关闭
* 当客户端请求AWS服务时,若处于该场景将以该错误码返回到ResponseObject数据结构
772 应用脱机(App Offline)

当应用处于就绪但依赖的应用未安装或暂停后,会返回该错误信息,如果该应用未被授权或订阅也会处于772状态。遇到这种错误,排出错误后再启动该应用
* 当客户端请求AWS服务时,若处于该场景将以该错误码返回到ResponseObject数据结构
773 应用已卸载(App Uninstall)

当应用已经被卸载后,会返回该错误信息。遇到这种错误可以忽略,或访问应用管理,将卸载的应用还原
* 当客户端请求AWS服务时,若处于该场景将以该错误码返回到ResponseObject数据结构
774 应用出错暂停(App Failed)

当应用启动失败后,会返回该错误信息。遇到这种错误,请检查该应用的日志,排出错误后再启动该应用
* 当客户端请求AWS服务时,若处于该场景将以该错误码返回到ResponseObject数据结构
800 许可配额限制(Quota Limit)

当资源配额超过许可限制时,会返回该错误信息。例如:超过许可的注册用户数
AWSQuotaException

* 当客户端请求并超过许可证允许的最大并发返回到ResponseObject中

如何使用错误码

  • 如果错误来自AWSException及其子类,可以通过getAPIErrorCode()在Java代码中获取
  • 如果错误来自客户端API,可通过ResponseObject结构的errorCode读取

 

 

3.2 异常抛出

  AWS平台提供的异常对象都是uncheck类型,开发者可以根据处理的需要进行捕获,如果开发者非常明确的要抛出这些异常,那么可以不对其处理。

AWSAPIException外,所有AWS内部异常的父类都是AWSExceptione,常见异常如下:

Exception说明
AWSAPIException API调用异常(uncheck)
AWSException AWS平台异常(以下均内部使用,uncheck)【通用】
AWSClassLoaderException 类加载异常
AWSEngineException 引擎内部异常(流程、表单、报表等,见该类常量)
BPMNDefException BPMN定义异常(设计阶段)
BPMNError BPMN规范要求捕获的异常抛出(运行阶段)
AWSDataAccessException 数据操作异常。如数据库操作、JSON数据操作
AWSIllegalArgumentException 参数校验异常【通用】
AppStoreServiceException 访问AWS企业应用商店异常

常用异常

虽然AWS平台定义了很多异常对象,但是对于应用开发者,只需要熟练掌握以下几个,即可满足大部分开发场景的需要:

  • AWSIllegalArgumentException(400)
  • AWSForbiddenException(403)
  • AWSObjectNotFindException(404)
  • AWSException(500)
//参数合法性异常
throw new AWSIllegalArgumentException("参数1", AWSIllegalArgumentException.FORMAT,"参数必须是0-9数字");
throw new AWSIllegalArgumentException("参数1", AWSIllegalArgumentException.EMPT);
throw new AWSIllegalArgumentException("参数1不能为空");

//操作被拒绝
throw new AWSForbiddenException("流程已经挂起,操作被拒绝");
throw new AWSForbiddenException("ctx类型不当,应给定begin()返回的上下文对象");

//对象不存在
throw new AWSObjectNotFindException("App文件不存在[" + appFile.getPath() + "]");
throw new AWSObjectNotFindException("流程定义未找到。processDefinitionId:" + processDefinitionId);

注意事项

  • 4类异常,通常可以直接抛出给前端处理
  • 5类异常,除非开发者捕获该异常也无法提供解决方案,否则应在上层逻辑捕获并处理
  • 7和8类异常属于底层非预期信息,开发者不必捕获可直接抛出
  • 在捕获异常时,不建议直接捕获Exception,除非你的意图是处理掉所有异常的抛出
  • 如果开发者的程序捕获了所有异常,应当使用e.printStackTrace(System.err)记录日志

3.3 异常处理

当异常被AWS MVC顶层框架捕获后,会根据cmd请求类型处理成客户端能够理解的文档格式。

请求类型格式
./w HTML Document
./jd JSON Document
./xd XML Document

Java Exception处理

当发生底层Java异常或其他非AWSException异常时,AWS MVC将该异常封装成500错误。

异常日志

捕获的异常被记录至AWS logs目录下

  • error.log(单一部署)
  • error-%AWS节点名%.log(集群部署)

HTML Document警告页面

html异常页面

JSON Document数据结构

JSON数据结构

XML Document数据结构

XML数据结构

 

4 国际化

4.1 多语言

AWS MVC框架对HTML/JavaScript多语言和Java程序多语言提供了一整套完善的开发方案,以下为AWS MVC的多语言处理架构。

多语言框架

AWS MVC每次接收到用户请求时,在会话对象(UserContext.getLanguage())提供了该用户的界面语言信息,AWS MVC为应用提供了两种常见解决方案:

Java程序

通过SDK.getAppAPI().i18nValue()获得指定用户界面的多语言资源配置

SDK.getAppAPI().i18NValue("com.actionsoft.apps.poc.api", me, "info1")

HTML模版

在HTML页面或JavaScript中出现的多语言标签,使用<I18N#变量名>替代,其中变量名为多语言资源配置ItemKey

<!doctype html>
<html>
<head>
    <meta charset="UTF-8">
    <title><I18n#PageTitle></title>

    <script>
    var var1 = "<I18n#Var1>";
    var var2 = "<I18N#Var2>";
    alert(var1);
    alert(var2);
    </script>
</head>
<body>
Hi,<I18n#PageTitle>
</body>
</html>

多语言资源文件

遵循AWS PaaS对App的资源定义规范,每个AWS应用的多语言资源配置文件被存放在应用安装目录/i18n/下。

该目录允许存在1个或多个符合Schema规范的xml文件,建议为自己的应用创建一个名为resource.xml的资源配置文件。

说明
item/key 多语言资源名,同个App下不允许重复
item/cn 中文简体
item/en 英文
item/big5 中文繁体
item/%lang% 其他扩展的语言名

resource.xml示例

<locale-config>
    <lang>
        <item key="info1">
            <cn><![CDATA[这是中文简体语言]]></cn>
            <en><![CDATA[This is the English language]]></en>
            <big5><![CDATA[這是中文繁體語言]]></big5>
        </item>
        <item key="PageTitle">
            <cn><![CDATA[这是标题]]></cn>
            <en><![CDATA[This is Title]]></en>
            <big5><![CDATA[這是標題]]></big5>
        </item>
        <item key="Var1">
            <cn><![CDATA[这是变量1]]></cn>
            <en><![CDATA[This is Var1]]></en>
            <big5><![CDATA[這是變量1]]></big5>
        </item>
        <item key="Var2">
            <cn><![CDATA[这是变量2]]></cn>
            <en><![CDATA[This is Var2]]></en>
            <big5><![CDATA[這是變量2]]></big5>
        </item>
    </lang>
</locale-config>

多语言资源文件管理

建议访问AWS企业应用商店,安装“多语言管理工具”。

该工具提供了可视化的多语言配置工具,能够极大提高翻译人员的工作效率。

如何扩展更多种语言

默认AWS PaaS只提供了中文简体、英文和中文繁体三种界面语言,可以安装“多语言管理工具”扩展更多种语言。

以下代码打印出当前AWS PaaS支持的语言集

List<LanguageModel> langs = SDK.getPlatformAPI().getlanguages();
for (LanguageModel lang : langs) {
  System.out.println(lang.getName());
}

4.2 时区和工作日历

对于跨国企业/组织或长期国际差旅时,用户需要根据工作地点的时区获得与当地时间一致的日期信息,并遵守当地的节假日和企业工作时间要求。

在AWS MVC框架中,时区主要解决符合用户当地时间的日期显示;工作日历主要解决对员工有效工作时间的计算,如流程执行效率,并提供API来处理业务需求,如差旅请假时长计算。

AWS是如何处理时区的

时区和工作日历的管理和设置

工作日历API接口

 

5 附录1-编程资源

AWS MVC是一个开放的编程架构,你可以方便的将自己需要的工具包加入到自己的App中使用:

  • 三方Jar类库 存放到App安装目录的lib下
  • 三方JavaScript、CSS库 存放到Web层的apps/%appId%/

AWS PaaS提供的开源Jar类库

可在你的程序中直接使用AWS PaaS自带的一些第三方开源类库,这些Jar文件存放在AWS安装目录的bin/libbin/jdbc下。

类库说明
commons-* Apache提供的若干工具类
barcode4j 条形码生成
cajo RMI工具类
csv CSV数据处理工具
xalan/... XML数据处理
json-lib JSON数据处理
poi Office文件处理
httpclient HTTP客户端
itext PDF处理
sigar 系统性能监控
log4j Java日志处理系统
cxf Web服务处理框架
... ...

AWS PaaS封装的Web UI

这些组件全部基于JQuery和JQueryMobile底层框架封装,使用这些UI有助于开发的应用界面与AWS其他应用保持一致的交互习惯。详细说明参见AWS UI章节

5.1 SDK API

AWS PaaS作为App运行的容器环境和资源平台,为App开发者提供了丰富的API,这些API可以直接在你的Java程序中使用。

SDK结构

对于API详细说明及用法,参见aws-api-doc(一个Java API Doc)。

MVC编程常用API

说明
SDK SDK API的总入口
ActionWeb Web(View)请求处理的父类
ModelBean 业务实体对象父类
DaoObject DAO对象父类
ResponseObject 返回JSON、XML结构化数据,如操作状态、业务数据
UserContext 用户会话,获得用户会话串、登录IP、语言、设备类型、用户组织等
AppAPI 多语言处理、跨应用的ASLP调用、应用日志
ORGAPI 访问组织结构相关接口
PermAPI 访问AWS权限相关接口
PortalAPI 访问或构建门户应用相关接口
RuleAPI 规则处理接口
DCAPI 文件处理接口

平台系统常用API

说明
PlatformAPI 查询平台及服务状态接口
ConfAPI 查询平台常用配置参数接口
SLAAPI 监控告警接口

BPM引擎常用API

说明
ProcessAPI 流程实例控制接口
TaskAPI 任务实例控制接口
ProcessExecuteQuery 引擎执行结果查询接口
ProcessQueryAPI 流程实例查询接口
TaskQueryAPI 任务查询接口
HistoryTaskQueryAPI 历史任务查询接口
DelegationAPI 任务委托/代理接口
RepositoryAPI 模型资源库访问接口
BOAPI BO操作接口
BOQueryAPI BO查询接口

监听器常见接口(事件编程)

说明
ValueListener 取值类监听器父类
ExecuteListener 执行类监听器父类
InterruptListener 中断类监听器父类

注意事项

  • 不推荐直接调用aws-api-doc未提供的接口方法
  • SDK API适用于在AWS Server端执行,不能用于Web层开发。如果你在Web层使用了不符合AWS MVC框架的开发模式,可以通过AWS CC发布Server API或封装ASLP服务来访问AWS Server端操作

 

5.2 AWS UI

为统一PaaS用户的交互体验(UX)和界面展示(UI),AWS MVC框架为开发者提供了一套基于JQuery封装的JavaScript UI组件库。目前这个UI库仍处在完善中,如果增强了用户体验或封装了新的UI类型,欢迎你的贡献!请邮件联系liujx@actionsoft.com.cn

了解每个AWS UI组件的文档、示例,可本地启动AWS服务后,在浏览器输入以下url访问。

http://localhost:8088/portal/commons/js/jquery/demo/index.html

在你的页面引入AWS UI

以下代码可将JQuery、AWS基本UI加入到你的页面中。对于部分专用UI,请参见相关示例引入特定的资源

<link rel="stylesheet" type="text/css" href="../commons/css/awsui.css">
<script type="text/javascript" src="../commons/js/jquery/scripts/jquery.js"></script>
<script type="text/javascript" src="../commons/js/awsui.js"></script>

 

5.3 量命名词汇表

为规范App开发者对专业变量的命名和识别,在这里给出一个词汇表参考。

设计期

命名参考
存储模型ID boDefId
表单模型ID formDefId
表单子表模型ID formItemDefId
流程模型ID processDefId
节点模型ID 通用:activityDefId
特定:userTaskDefId、serviceTaskDefId..
报表模型ID reportDefId
DW模型ID dwDefId
各种Context对象 如UserContext、TaskBehaviorContext...

建议:单独出现时命名变量为ctx或context,同时出现多个不同类型的Context时,使用userContext、taskContext区分
登录账户名 uid、uids(多个),对应ORGUSER的USERID字段
单位ID companyId
部门ID departmentId
角色ID roleId
小组ID teamId
小组成员ID teamMemberId

运行期

 

命名参考
流程实例ID 建议processInstId,可以使用processInstanceId
流程实例对象 建议processInst,可以使用processInstance
任务实例ID 建议taskInstId,可以使用taskId、taskInstanceId
任务实例对象 通用:建议taskInst,可以使用task、taskInstance
特定:建议historyTaskInst,可以使用historyTask、historyTaskInstance
BO表ID boId
BO表与流程实例绑定 通用:bindId
特定:纯流程驱动场景下,也可使用processInstId

 

6 附录2-程序文件

应用的安装、部署和运行由AWS PaaS自动化完成,但在开发阶段需要开发者了解这些资源结构。

Web层资源

Web层是指部署在Web Server(如Tomcat)的资源。AWS PaaS为每个App分配了独立的目录,被称为Web层根资源根目录。

你可以在这个目录中规划自己的js、css等资源结构。

//存放Web参数解析配置
%AWS-HOME%/webserver/webapps/portal/apps/%appId%/action.xml
//存放css
%AWS-HOME%/webserver/webapps/portal/apps/%appId%/css/
//存放js
%AWS-HOME%/webserver/webapps/portal/apps/%appId%/js/
//存放图片
%AWS-HOME%/webserver/webapps/portal/apps/%appId%/img/
//存放jsp程序(*不允许jsp直连数据库的开发模式,使用MVC cmd开发)
%AWS-HOME%/webserver/webapps/portal/apps/%appId%/jsp/
//自定义
%AWS-HOME%/webserver/webapps/portal/apps/%appId%/.../
  • %AWS-HOME%是AWS PaaS的安装根目录
  • %appId%是应用的Id名

App层资源

App层是指部署在AWS Server的资源。AWS PaaS为每个App分配了独立的目录,并通过安装、卸载库进行管理。

在开发的应用在容器仓库里处于install状态,与MVC编程相关的目录资源如下

//存放App的配置描述
%AWS-HOME%/apps/install/%appId%/manifest.xml
//存放App的LOGO
%AWS-HOME%/apps/install/%appId%/icon16.png
%AWS-HOME%/apps/install/%appId%/icon64.png
%AWS-HOME%/apps/install/%appId%/icon96.png
//存放程序编译的jar文件和第三方类库
%AWS-HOME%/apps/install/%appId%/lib/
//存放HTML模版
%AWS-HOME%/apps/install/%appId%/template/page/
//存放多语言资源
%AWS-HOME%/apps/install/%appId%/i18n/resource.xml

7 附录3-开发工具

AWS MVC的开发者可以根据自己的编程习惯选择开发工具。

AWS Developer

当安装了AWS PaaS开发环境后,会为开发者提供一个专有的开发工具。该工具基于Eclipse IDE封装,提供了专有的扩展插件,能够更直观、高效的进行AWS App开发。

//开发工具根目录
%AWS-HOME%/developer/

使用AWS Developer的优点

  • 无需配置即可在工程代码中启动、调试、编译和分发操作
  • 根据Controller类自动生成Web端接参配置文件,提高工作效率
  • 向导式开发
    • 创建App应用
    • 创建cmd请求
    • 创建ModelBean
    • 创建Dao
    • 创建Cache
    • 管理系统Jar包依赖
    • 创建各种AWS扩展插件...
  • 提供对App各类参数的配置管理
  • 内嵌支持SVN代码版本管理

Eclipse

对于熟悉AWS PaaS和App应用资源配置结构的开发者,也可以直接使用Eclipse完成所有的任务目标。

  1. 新建Java普通工程
  2. Java Build Path>Libraries下创建aws_lib库,增加以下资源
    %AWS-HOME%/bin/lib/*.jar(含子目录)
    %AWS-HOME%/bin/jdbc/*.jar
    
  3. 启动aws-infrastructure-common.jarStartUp
    com.actionsoft.bpms.server.AWSServer.StartUp
    
  4. 指定启动选项中Working directory目录
    %AWS-HOME%/bin
    

developer.csr

在你的团队正式开发应用前,应获得应用开发者证书(ISV)。证书文件路径如下

%AWS-HOME%/apps/developer.csr


8 微信企业号框架

微信企业号框架基于AWS MVC框架,将请求根据请求类型(URL跳转、消息/事件)交由不同的Servlet处理,再根据请求的映射规则分发给相应的后端逻辑控制器进行处理。

AWS MVC编程架构

上图中的三个Servlet(AWS Web Server / 一般请求Servlet,WS Web Server /重定向Servlet,WS Web Server /回调Servlet),后端处理控制(AWS App Server / Controller)和微信企业号管理平台是微信企业号的核心通信框架,AWS微信App是开发者实现业务逻辑的区域,主要元素描述如下:

说明
AWS Web Server 安装有AWS Portal的标准Servlet容器,例如:Tomcat、WebLogic
AWS App Server 安装有AWS Server的应用服务器,所有的业务逻辑在这里处理
一般请求Servlet 接收URL跳转请求,收到请求后会检查是否带有包含用户认证信息的Cookie,如果包含则封装后转发给后端控制器,若不包含则构造微信OAuth验证链接,重定向到下面的重定向Servlet
重定向Servlet 处理微信OAuth验证请求,封装后转发给后端控制器,最终交由微信管理平台处理并返回验证结果。如果验证通过,将验证信息写入Cookie,最后重定向到原始URL
回调Servlet 微信企业号应用的回调地址指向此处。处理微信消息/事件请求,封装后转发给后端控制器,再由微信管理平台转发到指定的AWS微信App处理请求
后端处理控制器 通过注解拦截到方法,绑定逻辑处理程序
微信企业号管理平台 管理微信企业号应用,可为企业号应用设置、绑定菜单和指定处理消息/事件的AWS 微信App
AWS微信App 实现业务逻辑,一般包含H5页面(View视图组装,Model/Dao/Cache模型,Template模板),和消息/事件处理实现类。

 

9 文档历史记录

下表说明了对该文档所做出的重要更改。

类型说明修改日期
首次发布 这是AWS MVC框架参考指南的首次发布 2015年01月22日

转载于:https://www.cnblogs.com/ang-r/p/10647853.html

2018-12-10 11:07:49 webmote 阅读数 0

原文参考:链接
无服务器架构是什么,可以下载一篇免费book了解:链接

无服务器架构介绍

在云上构建系统可以有另外一种选择:无服务器架构。它是BAAS(Backends-as-a-Service)的基础,易扩展,能很方便的处理数据库、消息、用户管理等。其包括常见的FAAS(Functions-as-a-Service)——能够定制小的、事件触发、函数式功能,并部署在一个完全受管理的平台,FAAS托管了所有的部署、资源分配、扩展、维护以及监控工作。

设计无服务器系统意味着对基础设施的节省,因为它屏蔽了有关底层的设施建设、错误处理、低级的操作,并且显著的缩短了产品或特性的上市周期。
有三类应用可以很好的利用无服务器的优势:

  1. 移动端、单页面web或B2B产品的后台同步响应的API接口。无服务架构可以加速其上市周期;
  2. 多种操作或API集成的逻辑“胶水”组合。无服务架构可以发挥超级好的作用,因为其最适合小片段逻辑的书写。
  3. 异步消息和文件处理。无服务架构可以自动伸缩负载。
    这篇文章会介绍第3中情况。

一个异步处理的例子

“异步消息和文件处理”并不是严谨的描述,其实这个概念很泛,因此这里描述的应用或组件,应该会有下列特性:

  1. 被一个消息触发,文件或其他类型的对象被传递在他们的上下文中;
  2. 执行一些输入事件的处理;
  3. 输出到数据仓储、消息组件、api终结点上,而不是同步返回到事件源;
  4. 可以使用本地状态并且为了完成处理从外部组件拉取数据。

在AWS云上,AWS Lambda的应用之一是图片文件处理。如果你有一个移动App或单页面WebApp,并且需要处理上传的照片,这样使用时非常棒的!就如同下面的流程一样。
在这里插入图片描述
对比以上提出的特性,图片文件处理应用:

  1. 被AWS S3原始文件的建立触发
  2. 用图片封装类库处理新版本
  3. 输出图片的不同版本到 S3
  4. 无状态,不需要任何外部组件。
    更复杂的例子是数据管道。一个数据管道是链接到异步服务的生态系统,执行数据的处理,ETL处理,导入到分析系统或其他。这些系统在持续处理大数据上是特别流行的,并提供给移动app查看的功能。
    无服务器数据管道是FAAS平台的应用之一,例如AWS Lambda,为了计算的需要,总是用一批宿主到无服务器BAAS的服务处理数据存储和传输,就像下图所示。
    在这里插入图片描述

注意,上图简化了实际的产品应用场景,例如一般使用Kinesis Firehouse加载数据到RedShift。
对照上述定义:

  • 在消息总线上触发事件,例如:AWS Kinesis Streams 或SNS 或 AWS S3的文件或对象的建立,或者AWS的DynamoDB表数据的更新;
  • 经典询问或传输事件,或者拆分子事件,或组合事件
  • 可以在不同总线上建立新消息,写S3的新文件,更新统计数据库,和其他HTTP服务通信
  • 请求远程服务获取需要处理的事件数据,这些服务可以使无服务,例如:DynamoDB或Lambda Http服务,也可以是传统服务,例如Redis,Sql数据库等。组件可以跨越多个消息缓存本地状态,但不能使用自己的本地内存或硬盘数据,因为FAAS的生命周期很短暂。

理解AWS生态的无服务器组件

在上述例子中使用到了云上的无服务器组件,其提供商是 Amazon,当然其他的云服务商也有类似的组件。
简述AWS组件功能如下:

  • Lambda:AWS的FAAS平台,其提供了大量的易扩展的,轻量级编程模型,运行语言可选:Javascript,Python,Java/JVM 和c#
  • S3:AWS的最老的一个服务,其是对象存储组件,其提供高扩展、高可用并且精确合理的费用,S3的事件可以轻易结合Lambda服务。
  • Kinesis Stream:AWS的高吞吐、持久、耐用的排序日志组件,对标Apache的Kafaka。Lambda集成它,执行消息处理。
  • SNS:短信组件;
  • DynamoDB:AWS的高性能NoSql数据服务。它可以作为无服务数据处理的事件源触发。

混合非无服务器架构服务

非无服务器组件或服务也可以是无服务器架构的一部分,这才是一个完美的世界。例如,Sql数据库需要在Lambda服务内用到,但其并不是无服务的架构,它仍然可能是AWS RDS的一部分。
混用需要留心的是两者的可扩展性不同,你需要使用不同的方式进行扩展。

使用Java实现数据处理

我们已经提到在AWS的生态中,Lambda可以写FAAS代码,并且Java是其中被支持的一种语言之一。
Java在Lambda世界里有个不太好的名声,因为其相对Javascript和Python有比较高的冷启动消耗。
冷启动是发生在AWS为你的函数建立一个新容器时,它一般发生在你部署了一个新版本的服务并且第一次运行时。某些场景下,冷启动Java需要几秒时间。然而,在数据处理场景下,java经常被选用:

  • 通过一些相当简单的调整,冷启动影响通常可以减少到几百毫秒。对于许多异步事件处理系统,这是完全合理的。此外,对于连续处理事件的系统,只有很小一部分可能会受到冷启动。我们的意思是微小,可能只有0.001%。
  • 处理相当连续的事件流时,Java通常可以以比Javascript和Python更低的延迟执行。这不仅有利于应用程序性能,而且对于运行时间超过100毫秒的中等复杂的lambda函数,您可以看到使用Java作为您的环境可以节省成本,因为Lambda的计费方式非常精确。
  • 由于已经在Java中实现了大量现有的非无服务器数据处理应用程序,因此您可以通过将Java域逻辑从旧应用程序移植到新的Lambda组件来看到重用代码的机会。

结论

无服务器架构在基础架构成本,人工和上市时间方面提供了显着的优势。在本文中,我们展示了无服务器架构如何用于构建各种类型和复杂性的无服务器数据处理应用程序。

无服务器仍然是构建系统的一种新方式 - 当您使用这些创意时,您将无法使用惯例指导您的每一步。在接下来的几年中,使用这些技术的最佳方式将变得更加清晰。但对于那些愿意屡起袖子的人来说,从这个令人兴奋的行业发展中获得的好处已经有很多了。