常用的告警方式大致有:短信、邮件、应用程序 (beep提示,图标提示,升窗提示等),可是不能一直坐在电脑前看着应用程序,或者用脚本部署监控,根本没有程序界面,所以通常用短信、邮件两种方式告警。
一. 告警方式
1. 短信
用程序发短信的方式一般有这两种:
(1) 硬件
需要1张SIM卡,1个SIM卡读卡设备 (比如:短信猫),然后把设备连接到电脑,应用程序根据设备的软件接口,传参并发送短信。记得把SIM卡设备放在信号好,无干扰的地方;
如果有大量短信要发,1张SIM卡是不够用的,而且发送过度频繁,可能被运营商视为恶意短信,把SIM卡号加入黑名单,那么就需要多张SIM卡甚至多个读卡设备。
显示号码为当前SIM卡号码,大多供应商都支持DLL、HTTP等多种接口,当然也可以基于接口二次开发。
DLL接口方法参考:SmsManager.sendTextMessage(…)
(2) 第三方短信接口
有多种接口形式提供,比如:
Web Service形式,HTTP形式,还有邮件接口:往1380013900@xxx.com发个短小的邮件,这个邮件会以短信的形式转发到
手机上,等等。只要往接口传参数,告诉它发给谁,发什么内容,就可以了。
显示号码为某个SIM卡号码,或者为固定号码 (如:106开头的),这取决于短信平台和运营商的实现方式,因为运营商发短信是不要卡的,直接可以发给目标号码,而且可以显示为固定的某个号码。
Web Service接口地址参考:http://123.456.789.000/SmsManager.asmx?wsdl
Http接口地址参考:http://api.abc.xyz/sms/send.html
2. 邮件
凡是实现了SMTP协议的组件,都可以发送邮件。
在
Windows环境下,有系统自带的组件CDO (Collaboration Data Objects,以前叫OLE Messaging 或者Active Messaging),是MAPI库的COM封装。不管是自己开发程序,使用VBS,还是
SQL Server的SQL Mail/Database Mail,通常都是调用的这个组件。
SMTP协议要求的参数大致如下:
SMTP Hostname: SMTP服务器名,如mail.test.com或者IP
SMTP Port: SMTP服务端口,25
SMTP Username: 通过SMTP发送邮件用来验证的用户名, 如果不要求身份验证,留空
SMTP Password: 通过SMTP发送邮件用来验证的密码, 如果不要求身份验证,留空
二. 选择告警方式并配置
1. 短信
不管是选择硬件,还是第三方接口,都需要一个程序来调用,可以是监控工具、脚本、甚至
数据库。
(1) 监控工具/应用程序中,通常都留有短信接口的配置,配置接口地址即可;
(2) 在脚本中配置,Windows环境通常要借助OLE Automation;
OLE Automation后来改名叫Automation,是Windows上基于COM,用于脚本语言实现进程间通讯的机制,脚本如:VBS, SQL, Powershell,不包括BAT(BAT可以调用VBS)。
SQL Server中使用OLE Automation调用Web Service短信接口如下:
exec sp_configure 'show advanced options', 1; RECONFIGURE; exec sp_configure 'Ole Automation Procedures', 1; RECONFIGURE; declare @text_message nvarchar(180) ,@phone_number nvarchar(15) ,@soap_object int ,@status int ,@output nvarchar(255) set @text_message = N'Testing Mail' set @phone_number = N'138000139000' --Create MSSOAP.SoapClient object exec @status=sp_OACreate 'MSSOAP.SoapClient', @soap_object out --SmsManager is Web Service name exec @status = sp_OAMethod @object, 'mssoapinit', null, 'http://123.456.789.000/SmsManager.asmx?wsdl', 'SmsManager' --SendTextMessage is webservice method exec @status = sp_OAMethod @object, 'SendTextMessage', @output OUT, @phone_number, @text_message if @status <> 0 begin exec sp_OAGetErrorInfo @soap_object select @soap_object end else begin select @output end --Destroy MSSOAP.SoapClient object exec @status = sp_OADestroy @soap_object GO |
对于HTTP, DLL接口,和SOAP接口类似,用OLE Automation也都可以调用,主要区别就是在CreateObject() 时。
以VBS为例,调用HTTP, DLL时CreateObject()如下:
Dim http
Set http = CreateObject("Msxml2.XMLHTTP")
Dim dll
Set dll = CreateObject("工程名.类名")
2. 邮件
(1) 监控工具/应用程序中,通常都留有SMTP配置项,配置SMTP参数即可;
(2) 在脚本中配置,Windows环境通常要借助OLE Automation;
VBS发送邮件如下:
Dim ns ns = "http://schemas.microsoft.com/cdo/configuration/" Dim title, content title = "db_maint_alert" content = "" content = content&"Hi All," content = content&chr(13)&chr(10) content = content&" " content = content&chr(13)&chr(10) content = content&"----test mail----" Msgbox('~1~') Set cm = CreateObject("CDO.Message") cm.from = "from_user_name@abc.com" cm.to = "to_user_name@abc.com" cm.cc = "cc_user_name@abc.com" cm.subject = title cm.textbody = content 'cm.AddAttachment "" Msgbox('~2~') 'sendusing: 1 = pickup, 2 = port 'smtpauthenticate: 0 = anonymous,1 = common,2 = NTLM 'smtpusessl: 0 = no,1 = yes With cm.configuration.fields .item(ns & "sendusing") = 2 .item(ns & "smtpserver") = "xxx.xxx.xxx.xxx" .item(ns & "smtpserverport") = 25 .item(ns & "smtpauthenticate") = 1 .item(ns & "sendusername") = "user_name@abc.com" .item(ns & "sendpassword") = "*****************" .item(ns & "smtpconnectiontimeout") = 10 .item(ns & "smtpusessl") = 0 .update End With Msgbox('~3~') cm.send Set cm = nothing Msgbox('~success~') |
SQL Server 2000发送邮件如下:
SQL Server 2000有SQL Mail,不过必须要同服务器上安装一个实现了MAPI的邮件程序,如:OUTLOOK,因为SQL Mail需要借用邮件应用程序的MAPI来发送邮件,配置起来不太方便,所以使用类似上面VBS的OLE Automation方法。
use master; if OBJECT_ID('sp_SendDatabaseMail') is not null drop proc sp_SendDatabaseMail go CREATE PROCEDURE sp_SendDatabaseMail @recipients varchar(8000), --'001@abc.com; 002@abc.com;' @Subject varchar(400) = '', @HtmlBody varchar(8000) = '' as Declare @From varchar(100) Declare @To varchar(100) Declare @Bcc varchar(500) Declare @AddAttachment varchar(100) Declare @object int Declare @hr int Declare @source varchar(255) Declare @description varchar(500) Declare @output varchar(1000) set @From = 'SqlAlert@abc.com' set @To = @recipients set @Bcc = '' set @AddAttachment = '' --set @HtmlBody= '<body><h1><font color=Red>' +@HtmlBody+'</font></h1></body>' EXEC @hr = sp_OACreate 'CDO.Message', @object OUT EXEC @hr = sp_OASetProperty @object, 'Configuration.fields("http://schemas.microsoft.com/cdo/configuration/sendusing").Value','2' EXEC @hr = sp_OASetProperty @object, 'Configuration.fields("http://schemas.microsoft.com/cdo/configuration/smtpserver").Value', 'xxx.xxx.xxx.xxx' EXEC @hr = sp_OASetProperty @object, 'Configuration.fields("http://schemas.microsoft.com/cdo/configuration/smtpserverport").Value','25' EXEC @hr = sp_OASetProperty @object, 'Configuration.fields("http://schemas.microsoft.com/cdo/configuration/smtpauthenticate").Value','1' EXEC @hr = sp_OASetProperty @object, 'Configuration.fields("http://schemas.microsoft.com/cdo/configuration/sendusername").Value','user_name@abc.com' EXEC @hr = sp_OASetProperty @object, 'Configuration.fields("http://schemas.microsoft.com/cdo/configuration/sendpassword").Value','*****************' EXEC @hr = sp_OAMethod @object, 'Configuration.Fields.Update', null EXEC @hr = sp_OASetProperty @object, 'To', @To EXEC @hr = sp_OASetProperty @object, 'Bcc', @Bcc EXEC @hr = sp_OASetProperty @object, 'From', @From EXEC @hr = sp_OASetProperty @object, 'Subject', @Subject EXEC @hr = sp_OASetProperty @object, 'HtmlBody', @HtmlBody --add attachment if @AddAttachment<>'' EXEC @hr = sp_OAMethod @object, 'AddAttachment',NULL,@AddAttachment IF @hr <>0 select @hr BEGIN EXEC @hr = sp_OAGetErrorInfo NULL, @source OUT, @description OUT IF @hr = 0 BEGIN SELECT @output = ' Source: ' + @source PRINT @output SELECT @output = ' Description: ' + @description PRINT @output END ELSE BEGIN PRINT ' sp_OAGetErrorInfo failed.' RETURN END END --send mail EXEC @hr = sp_OAMethod @object, 'Send', NULL IF @hr <>0 select @hr BEGIN EXEC @hr = sp_OAGetErrorInfo NULL, @source OUT, @description OUT IF @hr = 0 BEGIN SELECT @output = ' Source: ' + @source PRINT @output SELECT @output = ' Description: ' + @description PRINT @output END ELSE BEGIN PRINT ' sp_OAGetErrorInfo failed.' RETURN END end PRINT 'Send Success!!!' --destroy object EXEC @hr = sp_OADestroy @object |
调用上面这个SP来发邮件:
EXEC sp_SendDatabaseMail
@recipients = '001@test.com; 002@test.com;',
@body = 'This is a testing mail',
@HtmlBody = 'Testing Database Mail'
SQL Server 2005起,使用Database Mail,脚本如下:
--1. 启用database mail
use master
GO
exec sp_configure 'show advanced options',1
reconfigure
exec sp_configure 'Database mail XPs',1
reconfigure
GO
--2. 添加account
exec msdb..sysmail_add_account_sp
@account_name = 'SqlAlert' -- mail account
,@email_address = 'SqlAlert@test.com' -- sendmail address
,@display_name = 'SqlAlert' -- sendusername
,@replyto_address = null
,@description = null
,@mailserver_name = '***,***,***,***' -- SMTP Address
,@mailserver_type = 'SMTP' -- SQL 2005 only support SMTP
,@port = 25 -- port
--,@username = '*********@test.com' -- account
--,@password = '******************' -- pwd
,@use_default_credentials = 0
,@enable_ssl = 0 --is ssl enabled on SMTP server
,@account_id = null
--3. 添加profile
exec msdb..sysmail_add_profile_sp
@profile_name = 'SqlAlert' -- profile name
,@description = 'dba mail profile' -- profile description
,@profile_id = null
--4. 关联account and profile
exec msdb..sysmail_add_profileaccount_sp
@profile_name = 'SqlAlert' -- profile name
,@account_name = 'SqlAlert' -- account name
,@sequence_number = 1 -- account order in profile
--5. 发送database mail
EXEC msdb.dbo.sp_send_dbmail
@profile_name = 'SqlAlert',
@recipients = '001@test.com; 002@test.com;',
@body = 'This is a testing mail',
@subject = 'Testing Database Mail';
GO
注意:SMTP服务器的配置,比如:是否使用smtp用户验证,SSL是否开启,必须要和服务端一致,否则无法发送邮件。
其他
(1) 告警的次数:被告警的问题也许正在处理中,告警还在反复频繁发送,尤其用脚本轮询时,注意设置次数和发送间隔;
(2) 告警的历史记录:短信或者邮件告警,最好都在数据库中留一份记录;
(3) 字符编码:如果应用程序/接口不支持中文,可以把中文转成UTF-8的字符编码发送,然后再解析回来。
目录
在ACM/ICPC中使用Java需要注意的问题
Java与高精度计算
1.Java在ACM/ICPC中的特点
Java的语法和C++几乎相同
Java在执行计算密集任务的时候并不比C/C++慢多少,只是IO操作较慢而已
Java 简单而功能强大,有些东西用Java实现起来更为方便
比如:输入输出、字符串解析、高精度
Java不易犯细微的错误
C/C++中的指针
“if (n = m) ... ”
Java与Eclipse
2.在ACM/ICPC中使用Java需要注意的问题
java程序结构
Java I/O
JDK1.5.0新增的Scanner类很适合用于AMC/ICPC的输入
使用Scanner类的一般步骤
1.导入Scanner类
import java.util.Scanner;
2.创建Scanner类的对象
Scanner cin=new Scanner(System.in); //从标准输入读入数据
Scanner cin=new Scanner(“12 30”)); //从字符串读入数据
3.使用Scanner类的对象读入各种类型的数据
cin.nextInt()
cin.nextDouble();
…
Scanner类的常用方法
1.读入数据
2.判断是否还有数据
cin.hasNext() 或 cin.hasNextInt() 或 cin.hasNextDouble()
Scanner类的用方法示例:
import java.util.*;
public class Main{
public static void main(String[] args){
Scanner sc=new Scanner(System.in);
while(sc.hasNext()){
char ch=(char)sc.nextInt();
System.out.print(ch);
}
}
}
标准输出
System.out.print(…); //不输出换行 System.out.println(…); //输出换行 import java.io.*; public class Main{ static PrintStream cout=System.out; public static void main(String[] args){ int n=3,m=5; cout.println(n); // 输出3 //同一行输出多个整数可以用 cout.println(n+" "+m); } } |
用DecimalFormat类控制浮点数小数位数
import java.text.DecimalFormat;
控制方法
构造特定的DecimalFormat对象:DecimalFormat f=new DecimalFormat(“#.00#”);
构造函数中的参数是模式字符串,0指一位数字,#指除0以外的数字
使用DecimaFormat对象格式化需要输出的浮点数:System.out.println(f.format(12.1234));
DecimalFormat示例
import java.text.*; public class decimalformat{ public static void main(String[] args){ DecimalFormat f = new DecimalFormat("#.00#"); DecimalFormat g = new DecimalFormat("0.000"); double a = 123.4509, b = 0.12; System.out.println(f.format(a)); System.out.println(g.format(a)); System.out.println(f.format(b)); System.out.println(g.format(b)); } } |
运行结果:
123.451
123.451
.12
0.120
格式化输出的另一种方法是利用System.out.printf(“格式字符串”,…),其用法和c的printf基本一致
int a=10;
float b=2.35f;
System.out.printf("%d %10.5f\n", a, b);
字符串(String)
String类常用方法:
构造字符串:
String s=“abcde”;
char[] chs={‘a’,’b’,’c’,’d’,’e’};
String s=new String(chs);
取得字符串中某个字符:
char ch=s.charAt(1); //ch=‘b’;
求子串:
System.out.println(s.substring(0, 3)) // output “abc"
System.out.println(s.substring(1, 3)) // output “bc"
System.out.println(s.substring(1)) // output “bcde"
拆分字符串:
String s=“123:34:55”;
String[] ss = s.split(“:”);
for(int i=0;i<ss.length;i++) System.out.println(ss[i]);
运行结果:
123
34
55
替换字符串:
String s=“2009-07-26”;
System.out.println( s.replace(‘-’,’//’) ); //输出2009/07/26
String s=“0.123456”;
System.out.println( s.replaceAll(“^0”,””) ); //输出.123456
String中的字符不能改变,如果需要改变可以使用StringBuffer
其他注意的事项
Java数组是对象,定义后必须初始化,如 int[] a = new int[100]; 数组长度由length成员得到,如System.out.println(a.length);
Arrays类提供的一些有用方法:
Arrays.fill()
Arrays.sort()
Arrays.binarySearch()
布尔类型为 boolean,只有true和false二值,在 if (...) / while (...) 等语句的条件中必须为boolean类 型。
在C/C++中的 if (n % 2) ... 在Java中无法编译通过。
Java也提供了类似STL的集合类:
Vector,ArrayList,Map,Queue,Stack,Hashtable
3.Java与高精度计算
PKU1001-exponentiation(求幂):
Sample Input 95.123 12 0.4321 20 5.1234 15 6.7592 9 98.999 10 1.0100 12 Sample Output 548815620517731830194541.899025343415715973535967221869852721 . 00000005148554641076956121994511276767154838481760200726351203835 429763013462401 43992025569.928573701266488041146654993318703707511666295476720493 953024 29448126.764121021618164430206909037173276672 90429072743629540498.107596019456651774561044010001 1.126825030131969720661201 |
用C/C++解决的方法
C/C++的pow函数无法达到需要的精度
C/C++用数组来模拟乘法运算提高精度
java代码:
import java.math.*; import java.util.*; public class Main{ public static void main(String[] args){ Scanner in=new Scanner(System.in); while(in.hasNext()){ BigDecimal val=in.nextBigDecimal(); int n=in.nextInt(); BigDecimal ret=val.pow(n).stripTrailingZeros(); System.out.println( ret.toPlainString().replaceAll("^0", "") ); } } } |
BigDecimal类
高精度的有符号十进制数字
构造一个高精度数字
BigDecimal (int val) BigDecimal (double val) BigDecimal (String val) BigDecimal d1=new BigDecimal(1); BigDecimal d2=new BigDecimal(0.1); BigDecimal d3=new BigDecimal("0.1"); System.out.println("d1="+d1); System.out.println("d2="+d2); System.out.println("d3="+d3); BigDecimal类常用方法: BigDecimal add(BigDecimal augend) // “+” BigDecimal subtract(BigDecimal subtrahend) // “-” BigDecimal multiply(BigDecimal multiplicand) // “*” BigDecimal divide(BigDecimal divisor) // “/” BigDecimal remainder(BigDecimal divisor) // “%” BigDecimal pow(int n) //“求幂” String toPlainString() //返回不带指数的字符串表示 String toString() //返回带指数的字符串表示 |
PKU高精度计算题目:
1131、1205、1220、1405、1503、1604 1894、2084、2305、2325、2389、2413 3101、3199
自打兄弟我成为了一个高大上的码农之后,就难免接触到各种
软件开发的方法论。什么CMM啦,
敏捷啦,
测试驱动啦,不一而足。
咱们码农其实是很单纯的,说白了搬砖怎么搬不是搬啊对不对?所以老板让咋搬咱们就怎么搬,大部分人也没搞明白这些玩意儿背后到底是个什么思路。
但是兄弟我和其他码农不一样,我是个爱思考的人。所以在搬砖之余,我就会去找一些书来看,比如经济学、心理学、业务流程管理、销售管理之类的,一来可以涨涨姿势,二来也尝试站在老板的角度来观察一下咱们搬砖的情况。
一开始吧,我也看不出啥门道。各种方法论看上去都挺客观的,据说有的可以准确控制搬砖的进度,有的能统一砖块的质量,还有的能让咱们搬砖的时候既高效又开心。
这样问题就来了,搬砖技术到底哪家强呢?
哥思考了很久,后来站在老板的角度终于想明白了:之所以有各种不同的方法论,是因为对于软件开发有不同的假设前提(assumption — 顺带说一句,这个单词厉害了,做过科研的同学们都知道,所有的科学理论都是基于一套assumption的。一般的科学家对前人的成果修修补补,牛叉的科学家发现新的方法和领域,而传奇的科学家都是推翻前人的assumption,直接颠覆或创立一整套理论。啊,跑题了…)。不同的假设前提意思是说,在老板眼里码农是个啥角色?不同角色对应的就是不同的管理思路,就会导致不同的管理方法论。
历史的轮回总是惊人的相似。其实软件开发是一个行业,也难免经历其他行业的发展历程。我们先来看看工业界:工业革命之前,日用品都是手
工作坊生产出来的,工匠的手艺差别很大,这种差别就体现在产品和工匠的收入上,比如普通鞋匠花五天做一双鞋卖给村民,一个月能挣3个银币,而顶级鞋匠花三个月给贵族订制一双鞋,挣30个金币。那个时候,衡量工匠工作效率的核心指标不是工作时间,而是手艺。
这个阶段如果有管理方法的话,主要也是针对学徒的。学徒一直跟着师傅
学习,直到手艺也达到师傅的标准,可以独当一面。这时候的管理方法,主要是让学徒尽可能掌握师傅的手艺,提高技术水平。
后来工业革命了,有聪明人做出了蒸汽机以及各种自动机器,实现了流水线生产,根据某些顶级工艺师设计好的模具进行自动化生产,流水线上的工人只是负责某个环节的装配和质检等简单工作,就不需要有太高的手艺了,只要具备基本知识,经过流水线操作培训就可以上岗。这时候,衡量工人工作效率的核心指标就不是手艺,而是工作时间。
这是为什么呢?因为,流水线的生产速度是固定的,质量也是标准的,所以一个工人每天能做出多少产品和他站在流水线旁的时间长短成正比。所以这个阶段的管理方法也不再看重工人提高手艺,而是关注出勤情况,所以相应的考勤打卡、病事假管理等等制度就一步步完善起来。
其实说白了,现在的软件开发行业就正在经历从作坊到工厂的转变,有些公司转变得比较快,有的转变得慢一些。越来越多的开发框架、开发工具、库、模板就构成了很多的自动化流水线,让程序员这个职业的门槛越来越低。作为软件工程师,在老板的眼里,也有偏工人和偏工匠的不同,所以就会有不同的管理方法出来。
再说到管理方法论和相应的假设前提,比如CMM是什么?就是软件行业的富士康管理方法。在实施CMM的公司老板眼里,他关注的其实是开发的流程。只要流程执行好了,每个码农都是无差别的,就像流水线上的一个个工人一样,今天走一个,明天就能招来一个,要求也不高,基本上会写if…else…就行了,对整体工作完全没有影响。考核员工的标准也比较容易量化,比如代码行数、出勤时间之类。
敏捷开发呢?老板对工程师创造力的期望要高很多,希望每个团队成员能独当一面,这种模式下的工程师更像一个工匠,做事的自由度也会大一些。这种管理方式下,绩效考核的复杂度就要大一些了,不能简单地看写了多少代码,可能是看功能点数,或者结合其他成员的主观评价。当然,这种方式下对工程师水平的要求也会比较高,作坊里总不能随便找个人就开始干活吧?
其他种种管理方法,其实都可以从这个角度去分析。不一定要看到它的开发方法论,从公司的外部表现就能大概齐看出来了。比如一家公司说福利好管三餐,上班要打卡甚至是996模式,办公室里还免费提供睡袋,薪水也不高,那么你基本就是去做工人的。反之,如果某公司不用打卡不记考勤,只管午饭但很好吃,没有老板天天盯着你写了多少代码,身边都是高手,而且薪水又很高……呃,国内真有这样的公司?假如有,那其他的就不用管了,赶紧去吧。
大部分公司其实都是介乎两个极端之间的,你可以具体分析,是离工人近一点呢,还是离工匠近一点。
为啥要从这个角度去看呢?因为对自己的成长影响是不一样的,工人讲究的是熟练,工作中钻研的方向也不是大的技术创新(老板不需要你钻研这个,而且你也没有时间没有条件去钻研工作中用不到的东西),而是如何优化模板等特别细节上的改进,一旦失去这个工作,你的经验可能在别的地方用处不大。工厂适合踏实、肯吃苦、执行力强、能干脏活累活的人,在这种人员流动大的地方能坚持下来,其实也会有很好的职业发展机会。
如果你是那种喜欢新鲜感、爱钻研新技术、对于重复性的劳动兴趣不大的人,最好还是尽量去那种强调巧干胜过苦干的地方。一般那里高人比较多,学习的条件好,提升空间比较大,但是前提是你要聪明、勤快、自学能力强。在这种地方呆上几年,就能积累很多东西。
不过现在行业里也有让人看不懂的公司,没办法用这个套路分析,或者说,它们是一种奇怪的混合变异体。比如有的
互联网公司强调以人为本,取消了一线管理职位,实行扁平化的管理,又实行996严格考勤制度下的敏捷开发,对程序员主要考核代码量和bug数等等……
这种思路大概是想把高水平的工匠们集中到工厂里进行流水线化的管理,类似中西医结合,先拍片子再抓方子。老码农在此无奈地表示,这种变异体的管理方式太有创意,无法评价,我只能承认搬砖技术最强的就是他们家了。
误打误撞进入
配置管理这个行业约2年,还是有很多东西不清楚。网上倒是有很多软件类配置管理的知识可以获取、
学习。但总觉得使不上劲,好像有一层云雾笼罩着,摸不清方向。曾经一度想加入
测试或研发团队,通过了解研发流程,以便提出合适的配置流程和方案。无奈还是隔靴搔痒地在综合办里坐着,只能通过和研发人员沟通、看博客混论坛来了解这个领域的状态。
今天又仔细看了构建管理的相关定义,突然有了一些豁然开朗的感觉。写出来和各位同仁探讨,不足之处,还望不吝赐教。
配置管理是缩写是CM (Configuration Management),而业界很多人却称自己是SCM(Software Configuration Mangement, 软件配置管理)。但是却没有与之相对应的HCM (Hardware Configauration Management)。如果在
谷歌或
百度搜索配置管理,顺藤摸瓜会发现与之关联密切的一些术语,比如:构建,编译,打包,发布,部署等等。
软件配置管理的体系和工具都已经很成熟(但这不表示我们这个国家的多数软件公司已经在用它们),而硬件方面的就较少。而这套成熟的软件系统又显然不太适用于芯片类和系统类的产品。
造成这种配置管理软件强而硬件弱的现象,笔者考虑主要是下面的原因:
1)配置管理对工具的要求较高。由于软件行业在这方面有着天然的优势,对应的解决方案就比较多(实现相对容易)。例如
IBM和MS就不乏这样针对软件研发过程的产品。
2)软件公司的开发模型大致相近(或分为几类)。这类产品将标准的软件研发过程包含在内,很快在其它软件公司中得到应用和推广。
而芯片类和系统类的工程师在开发类似定制软件的技术实力和动力方面都不足(不会像软件公司那样做好了还可以作为产品销售)。因此,芯片行业缺少通用的配置流程和可选工具就不奇怪了。
目前,我们能做的就是按照公司的研发流程和cmmi等标准的要求,参考当前软件配置管理的优秀实践,定制地开发复合公司需求的配置管理方案。解决代码管理,编译,测试,发布等问题。
芯片产品包括:芯片设计(最终形成芯片的硬件部分)和固件设计(boot、cos、驱动、下载工具等)。
对于芯片硬件的设计,其研发流程很长。与软件类的差别就比较大了,比如加入了仿真、模拟、版图等环节。
对于芯片固件的设计,可以参考普通软件类产品的配置管理流程。
当然,虽然可以借鉴现成的流程,但工具却不一定能套用。因为芯片固件采用的是嵌入式开发(例如用C语言编写)。
软件配置管理的思路有很多值得借鉴之处——比如,构建自动化、测试自动化、自动打包、自动编译。这些工具或环境,其实就是将研发流程中可以让机器做(而且可能比人做更高效、准确)的部分单独拿出来,尽可能地让机器(编译服务器、构建服务器)实现。 包括版本控制、质量控制(自动测试)、编译。
当然,以上的分析也只是为芯片类的配置管理找到了一个可能的方向,剩下就是进一步的确定需求、制定解决方案、实施、试点、推广。
如果有在
华为海思或其它芯片类公司
工作的配管,看到这篇
文章,也可以与我交流。 yanyuzuo@qq.com
和做配管的同行交流多了,似乎有一种观点,配置管理和
项目管理、
质量管理一样,在不同的公司有很大的差异性。
但是,拿发展成熟的制造业来说,ERP系统几乎已经成为标配,各个公司的ERP系统也许会有细节不同的地方,但其功能却相差无几。
配置管理也是一样,我们可能需要定制,但是一定有一套基础的系统(base),是放之四海而皆准的。
以前关注点一直在怎么提高应用程序的质量,没太在意代码级别的质量。最近因为某些因素的推动,需要关注到代码级别的质量去,把质量
工作尽量往前推,也符合质量控制的原则。 试用了一下
sonarqube(老版本的叫sonar,ww.sonarqube.org),对代码的提升的确有很多的作用,sonarqube能从7个维度来对代码质量进行度量。多大的作用,大家实践下就很容易看出来。尤其是建议大家把rules里面的说明和例子都好好看看,对以后自己写代码的时候,质量提高有很大好处。
Sonarqube安装:
Sonarqube一共分3 部分:
CREATEDATABASEsonarCHARACTERSETutf8COLLATEutf8_general_ci;
赋予后面连接sonarqube的数据库用户读写权限即可
web服务: 修改sonarqube/conf/sonar.properties
# Permissions to create tables, indices and triggers must be granted to JDBC user. # The schema must be created first. sonar.jdbc.username=mysql_username sonar.jdbc.password=mysql_password # Comment the following line to deactivate the default embedded database. #sonar.jdbc.url=jdbc:h2:tcp://localhost:9092/sonar #----- MySQL 5.x # Comment the embedded database and uncomment the following line to use MySQL sonar.jdbc.url=jdbc:mysql://192.168.22.99:3306/sonarqube?useUnicode=true&characterEncoding=utf8&rewriteBatchedStatements=t sonar.web.host=0.0.0.0 sonar.web.context=/sonarqube sonar.web.port=9001 |
sonarqube自带
web服务器,性能也足够好,不需要配置tomcat什么的,到这里整个sonar web服务配置完成了,到sonarqube/bin/linux-x86-64目录下,启动./sonar.sh start即可,启动后有任何问题可以查看log: sonarqube/logs/sonar.log, 通过浏览器访问http://192.168.22.99:9001/sonarqube, 打开登陆页面,默认管理员账户是admin/admin
分析器:
Sonarqube通过插件 支持20+种语言, Java, python, C#, C/C++, PL/SQL, Cobol等, 但C语言的插件是收费的。到这里http://docs.codehaus.org/display/SONAR/Plugin+Library 下载对应语言的插件,放置到sonarqube/extensions/plugins目录下,重启web服务即可。
分析器主要5种:
SonarQube Runner(万能,支持后面几种方式的工程),
Maven(和maven编译工程集成),
SonarQube Ant Task(和ant编译工程集成),
Gradle(和Gradle编译工具集成,很少听过),
CI Engine(主要和Jenkins , Hudson等CI工具集成)。
以下主要讲Sonarqube runner分析器的使用:
下载Sonarqube 分析器:http://docs.codehaus.org/display/SONAR/Installing+and+Configuring+SonarQube+Runner, 解压后修改conf目录下的sonar-runner.properties, 如下例子。
#----- Default SonarQube server sonar.host.url=http://192.168.23.94:9001/sonarqube #----- PostgreSQL #sonar.jdbc.url=jdbc:postgresql://localhost/sonar #----- MySQL sonar.jdbc.url=jdbc:mysql://192.168.23.99:3306/sonarqube_qa?useUnicode=true&characterEncoding=utf8 #----- Oracle #sonar.jdbc.url=jdbc:oracle:thin:@localhost/XE #----- Microsoft SQLServer #sonar.jdbc.url=jdbc:jtds:sqlserver://localhost/sonar;SelectMethod=Cursor #----- Global database settings sonar.jdbc.username=mysql_username sonar.jdbc.password=mysql_password #----- Default source code encoding sonar.sourceEncoding=UTF-8 #----- Security (when 'sonar.forceAuthentication' is set to 'true') sonar.login=admin sonar.password=admin |
把sonarruner/bin加入到path目录下,在环境变量里面加上SONAR_RUNNER_HOME="/home//sonarruner"。
到这里整个Sonarqube的运行环境就全部配置完成了,下一篇讲解怎么运行分析器。
很多情况下,写了一堆的
test case,希望某一些test case必须在某个test case之后执行。比如,
测试某一个Dao代码,希望添加的case在最前面,然后是修改或者查询,最后才是删除,以前的做法把所有的方法都集中到某一个方法去执行,一个个罗列好,比较麻烦。比较幸福的事情就是JUnit4.11之后提供了MethodSorters,可以有三种方式对test执行顺序进行指定,如下:
/** * Sorts the test methods by the method name, in lexicographic order, with {@link Method#toString()} used as a tiebreaker */ NAME_ASCENDING(MethodSorter.NAME_ASCENDING), /** * Leaves the test methods in the order returned by the JVM. Note that the order from the JVM may vary from run to run */ JVM(null), /** * Sorts the test methods in a deterministic, but not predictable, order */ DEFAULT(MethodSorter.DEFAULT); |
可以小试牛刀一下:
使用DEFAULT方式:
package com.netease.test.junit; import org.apache.log4j.Logger; import org.junit.FixMethodOrder; import org.junit.Test; import org.junit.runners.MethodSorters; /** * User: hzwangxx * Date: 14-3-31 * Time: 15:35 */ @FixMethodOrder(MethodSorters.DEFAULT) public class TestOrder { private static final Logger LOG = Logger.getLogger(TestOrder.class); @Test public void testFirst() throws Exception { LOG.info("------1--------"); } @Test public void testSecond() throws Exception { LOG.info("------2--------"); } @Test public void testThird() throws Exception { LOG.info("------3--------"); } } /* output: 2014-03-31 16:04:15,984 0 [main] INFO - ------1-------- 2014-03-31 16:04:15,986 2 [main] INFO - ------3-------- 2014-03-31 16:04:15,987 3 [main] INFO - ------2-------- */ |
换成按字母排序
package com.netease.test.junit; import org.apache.log4j.Logger; import org.junit.FixMethodOrder; import org.junit.Test; import org.junit.runners.MethodSorters; /** * User: hzwangxx * Date: 14-3-31 * Time: 15:35 */ @FixMethodOrder(MethodSorters.NAME_ASCENDING) public class TestOrder { private static final Logger LOG = Logger.getLogger(TestOrder.class); @Test public void testFirst() throws Exception { LOG.info("------1--------"); } @Test public void testSecond() throws Exception { LOG.info("------2--------"); } @Test public void testThird() throws Exception { LOG.info("------3--------"); } } /* 2014-03-31 16:10:25,360 0 [main] INFO - ------1-------- 2014-03-31 16:10:25,361 1 [main] INFO - ------2-------- 2014-03-31 16:10:25,362 2 [main] INFO - ------3-------- */ |
1. Nikto
以下是引用片段: 这是一个开源的
Web 服务器扫描程序,它可以对Web 服务器的多种项目(包括3500个潜在的危险 文件/CGI,以及超过900 个服务器版本,还有250 多个服务器上的版本特定问题)进行全面的测 试。其扫描项目和插件经常更新并且可以自动更新(如果需要的话)。 Nikto 可以在尽可能短的周期内
测试你的Web 服务器,这在其日志文件中相当明显。不过,如果 你想试验一下(或者测试你的IDS系统),它也可以支持LibWhisker 的反IDS方法。 不过,并非每一次检查都可以找出一个安全问题,虽然多数情况下是这样的。有一些项目是仅提 供信息(“info only” )类型的检查,这种检查可以查找一些并不存在安全漏洞的项目,不过Web 管理员或安全工程师们并不知道。这些项目通常都可以恰当地标记出来。为我们省去不少麻烦。
2. Paros proxy
以下是引用片段: 这是一个对Web 应用程序的漏洞进行评估的代理程序,即一个基于Java 的web 代理程序,可以评估Web 应用程序的漏洞。它支持动态地编辑/查看HTTP/HTTPS,从而改变cookies和表单字段 等项目。它包括一个Web 通信记录程序,Web 圈套程序(spider),hash 计算器,还有一个可以测试常见的Web 应用程序攻击(如
SQL 注入式攻击和跨站脚本攻击)的扫描器。
3. WebScarab
以下是引用片段: 它可以分析使用HTTP 和HTTPS 协议进行通信的应用程序,WebScarab 可以用最简单地形式记录 它观察的会话,并允许操作人员以各种方式观查会话。如果你需要观察一个基于HTTP(S)应用程序的运行状态,那么WebScarabi 就可以满足你这种需要。不管是帮助开发人员调试其它方面的难题,还是允许安全专业人员识别漏洞,它都是一款不错的工具。
4. WebInspect
以下是引用片段: 这是一款强大的Web 应用程序扫描程序。SPI Dynamics 的这款应用程序安全评估工具有助于确认Web 应用中已知的和未知的漏洞。它还可以检查一个Web 服务器是否正确配置,并会尝试一些常见的Web 攻击,如参数注入、跨站脚本、目录遍历攻击(directory traversal)等等。
5. Whisker/libwhisker
以下是引用片段: Libwhisker 是一个Perla 模块,适合于HTTP 测试。它可以针对许多已知的安全漏洞,测试HTTP服务器,特别是检测危险CGI 的存在。Whisker 是一个使用libwhisker的扫描程序。
6. Burpsuite
以下是引用片段: 这是一个可以用于攻击Web 应用程序的集成平台。Burp 套件允许一个攻击者将人工的和自动的技术结合起来,以列举、分析、攻击Web应用程序,或利用这些程序的漏洞。各种各样的burp工具协同
工作,共享信息,并允许将一种工具发现的漏洞形成另外一种工具的基础。
7. Wikto
以下是引用片段: 可以说这是一个Web 服务器评估工具,它可以检查Web 服务器中的漏洞,并提供与Nikto 一样的很多功能,但增加了许多有趣的功能部分,如后端miner 和紧密的
Google 集成。它为MS.NET 环境编写,但用户需要注册才能 其二进制文件和源代码。
8. Acunetix Web Vulnerability Scanner
以下是引用片段: 这是一款商业级的Web 漏洞扫描程序,它可以检查Web 应用程序中的漏洞,如SQL 注入、跨站脚本攻击、身份验证页上的弱口令长度等。它拥有一个操作方便的图形用户界面,并且能够创建专业级的Web 站点安全审核报告。
9. Watchfire AppScan
以下是引用片段: 这也是一款商业类的Web 漏洞扫描程序。AppScan 在应用程序的整个开发周期都提供
安全测试,从而测试简化了部件测试和开发早期的安全保证。它可以扫描许多常见的漏洞,如跨站脚本攻击、HTTP 响应拆分漏洞、参数篡改、隐式字段处理、后门/调试选项、缓冲区溢出等等。
10. N-Stealth
以下是引用片段: N-Stealth 是一款商业级的Web 服务器安全扫描程序。它比一些免费的Web 扫描程序,如 Whisker/libwhisker、Nikto 等的升级频率更高,它宣称含有“30000个漏洞和漏洞程序”以及 “每天增加大量的漏洞检查”,不过这种说法令人质疑。还要注意,实际上所有通用的VA 工具,如Nessus, ISS Internet Scanner, Retina, SAINT, Sara 等都包含Web 扫描部件。(虽然这些 工具并非总能保持软件更新,也不一定很灵活。)N-Stealth 主要为
Windows 平台提供扫描,但并不提供源代码。
商业产品*国外
·Acunetix Web Vulnerability Scanner 6
简称WVS,还是不错的扫描工具,不知道检查 的太细致还是因为慢,总之经常评估一个网站的时候一晚上不关电脑都扫描不万……但是报 表做的不错。一般用这个扫描的话,不用等那么久,像区县政府的,扫20 分钟就差不多了。
·IBM Rational AppScan
这个是IBM 旗下的产品,扫描速度中规中矩,报表功能相当 强大,可以按照法规遵从生成不同的报表,如:ISO27001、OWASP 等,界面也很商业化。
·HP WebInspect
没错,的确就是卖PC 的HP 公司旗下的产品,扫描速度比上面的2 个都快得多,东西还算不错。不过这几天在和NOSEC(下面说的“诺赛科技”)掐架,愣是说NOSEC 的iiScan 免费扫描平台侵犯隐私,说NOSEC 有国家背景……这市场了解的!
·N-Stealth
没装成功,不过很多地方在推荐这个
·Burp Suite
貌似是《黑客攻防技术宝典·WEB 实战篇》作者公司搞的,安全界牛人。
商业产品*国内
·智恒联盟WebPecker: 网站啄木鸟:程序做的不错,扫描速度很快。
·诺赛科技Pangolin、Jsky :Pangolin 做SQL 注入扫描,Jsky 全面评估,就是上文说的 NOSEC,网上扫描平台是iiScan,后台的牛人是zwell。
·安域领创WebRavor:记得流光(FluXay)否?是的,WebRavor 就是小榕所写!小榕 是谁?搜下……不用我介绍了吧?
·安恒MatriXay 明鉴WEB 应用弱点扫描器:还没用过,和NOSEC 一样,也有网上扫 描平台。
绿盟NSFOCUS RSAS 极光远程安全评估系统:极光扫描系统新增的WEB 安全评估插 件,在某客户处见到过扫描报告,不过没用过产品。依照绿盟的一贯风格和绿盟的实力,应 该不错。
免费产品
·Nikto:很多地方都在推荐,但游侠本人实在不喜欢命令行产品……各位喜欢的Google 或Baidu 下吧
·Paros Proxy:基于Java 搞的扫描工具,速度也挺快,在淘宝QA 团队博客也看到在介 绍这个软件。
·WebScarab:传说中很NB 的OWASP 出的产品,不过我看 地址的时候貌似更新挺 慢
·Sandcat:扫描速度很快,检查的项目也挺多。机子现在就装了这个。 ————
·NBSI:应该说是黑客工具更靠谱,国内最早的,可能也是地球上最早的一批SQL 注入 及后续工作利用工具,当年是黑站挂马必备……
·HDSI:教主所写,支持ASP 和PHP 注入,功能就不多说了,也是杀人越货必备!
·Domain:批量扫描的必备产品,通过whois 扫描服务器上的服务器,在很长一段时 间内风靡黑客圈。 ————
·Nessus:当然它有商业版,不过我们常用的是免费版。脆弱性评估工具,更擅长于主 机、服务器、网络设备扫描。
·NMAP:主要倾向于端口等的评估。
·X-Scan:安全焦点出品,多少年过去了,依然是很强悍的产品。大成天下曾经做过商 业版的“游刃”,但最近已经不更新了,很可惜。
其实能做评估的工具还有很多,如:
·Retina Network Security Scanner
·LANguard Network Security Scanner
榕基RJ-iTop 网络隐患扫描系统
不过和Nessus 和NMAP一样,主要倾向于主机安全评估,而不是WEB 应用安全评估。 但我们在WEB安全评估的时候,不可避免的要对服务器做安全扫描,因此也是必然要用的工具。
简单的ls实现,首先,我们需要遍历参数目录下的各个文件,再根据文件相应的性质,读取文件的权限,用户组,用户名,大小,最后一次访问的时间,再根据文件名排序后依次显示。
具体的函数声明如下:
1 #include <stdio.h> 2 #include <stdlib.h> 3 #include <string.h> 4 #include <sys/stat.h> 5 #include <fcntl.h> 6 #include <unistd.h> 7 #include <dirent.h> 8 #include <sys/types.h> 9 #include <pwd.h> 10 #include <grp.h> 11 #include <time.h> 12 #define CNT 256 13 int file_name(DIR *fp, char *path, char name[][CNT]); 14 void str_sort(char name[][CNT], int cnt); 15 void mode_to_char(mode_t mode, char *buf); 16 char *time_change(char *time); 17 void show(char name[][CNT], int cnt); |
目录的遍历,我们需要知道目录下读取到的文件个数,所以需要返回相应的int型值。
目录的遍历实现如下:
1intfile_name(DIR*fp,char*path,charname[][CNT]) 2{ 3intcnt=0; 4structdirent*p; 5while((p=readdir(fp))!=NULL) 6{ 7if(strncmp(p->d_name,".",1)==0||strncmp(p->d_name,"..",2)==0) 8continue; 9strcpy(name[cnt],path); 10strcat(name[cnt],"/"); 11strcat(name[cnt],p->d_name); 12cnt++; 13} 14closedir(fp); 15returncnt; 16} |
然后我们需要了解文件的权限,文件权限保存在相对应的参数char *buf中。
文件权限的解读实现如下:
1 void mode_to_char (mode_t mode, char *buf) 2 { 3 memset(buf, '-', 10); 4 if(S_ISDIR(mode)) 5 buf[0] = 'd'; 6 if(mode & S_IRUSR) 7 buf[1] = 'r'; 8 if(mode & S_IWUSR) 9 buf[2] = 'w'; 10 if(mode & S_IXUSR) 11 buf[3] = 'x'; 12 if(mode & S_IRGRP) 13 buf[4] = 'r'; 14 if(mode & S_IWGRP) 15 buf[5] = 'w'; 16 if(mode & S_IXGRP) 17 buf[6] = 'x'; 18 if(mode & S_IROTH) 19 buf[7] = 'r'; 20 if(mode & S_IWOTH) 21 buf[8] = 'w'; 22 if(mode & S_IXOTH) 23 buf[9] = 'x'; 24 } |
想应的,时间的显示不需要那么精确,所以我们应适当的缩短时间精确度。
时间的显示实现如下:
1 char *time_change(char *time)
2 {
3 int index = strlen(time) - 1;
4 for(; time[index] != ':'; index --);
5 time[index] = '\0';
6 return time + 4;
7 }
然后,我们需要根据文件名称按照字典序排序。
排序的实现如下:
1 void str_sort(char name[][CNT], int cnt) 2 { 3 int index, pos; 4 char str[CNT]; 5 for(pos = 1; pos < cnt; pos ++) 6 { 7 strcpy(str, name[pos]); 8 for(index = pos - 1; index >= 0; index --) 9 if(strcmp(name[index], str) > 0) 10 strcpy(name[index + 1], name[index]); 11 else 12 break; 13 strcpy(name[index + 1], str); 14 } 15 } |
最后,我们在编写一个简单的show()函数,来显示各个文件的信息。
show函数实现如下:
1 void show(char name[][CNT], int cnt) 2 { 3 int index; 4 char mode[10]; 5 char *str; 6 struct stat buf; 7 for(index = 0; index < cnt; index ++) 8 { 9 memset(&buf, 0, sizeof(buf)); 10 if(stat(name[index], &buf) == -1) 11 { 12 printf("stat error!!\n"); 13 exit(1); 14 } 15 mode_to_char(buf.st_mode, mode); 16 str = ctime(&buf.st_atime); 17 str = time_change(str); 18 int i; 19 for(i = strlen(name[index]) - 1; name[index][i] != '/'; i --); 20 i++; 21 printf("%10s.%2d %5s %5s%5d%13s %s\n", mode, buf.st_nlink, getpwuid(buf.st_uid)->pw_name, getgrgid(buf.st_gid)->gr_name, buf.st_size, str, name[index] + i); 22 } 23 } |
这里需要注意:
getpwuid()返回的不是我们要的用户名,我们需要的是该结构体中的一个变量——pw_name,同样的getgrid()也应做相应的转换。
测试代码如下:
1 #include "head.h" 2 int main(int argc, char *argv[]) 3 { 4 DIR *fp; 5 char name[CNT][CNT]; 6 int cnt; 7 fp = opendir(argv[1]); 8 if(fp == NULL) 9 { 10 printf("opendir error!!\n"); 11 exit(1); 12 } 13 cnt = file_name(fp, argv[1], name); 14 str_sort(name, cnt); 15 show(name, cnt); 16 return 0; 17 } |
对于一个以
数据库为中心的应用,数据库的优化直接影响到程序的性能,因此数据库性能至关重要。一般来说,要保证数据库的效率,要做好以下几个方面的
工作:
1、 数据库表设计:
表的设计合理化(符合3NF);
2、添加适当索引(index):
普通索引:
主键索引: primary 效率最高,但是只能有一个
唯一索引:unique
空间索引:SPATIAL 很少使用
3、分表技术:
水平分割
垂直分割
4、读写分离:
写:update/delete/insert
5、存储过程
模块化编程,可以提高速度
配置最大并发数(默认是100),在my.ini配置文件中,一般网站调整到1000左右。一个并发开一个进程为之服务,过大的话将会占用很大的内存;
调整缓存大小;
7、MySQL硬件服务器升级
8、定时的清除不需要的数据,定时进行碎片整理(MyISAM)
默认已经用Groovy把外部数据给读取出来了,关键是读取出来后,如何加载到request中去?这里提供了两种方法:
1.该Groovy脚本的名称是"setUp"
def num = Integer.parseInt(testRunner.testCase.getPropertyValue( "count" )) log.info num num = (++num) % 2 testRunner.testCase.setPropertyValue( "count", num + "") String[] acList = ["Loginn"+String.valueOf(Math.random()).substring( 0, 5 ),"Loginn"+String.valueOf(Math.random()).substring( 0, 6 )] log.info num log.info acList[num] acList[num] |
上面的例子是把数据放到了一个数组中去了,在request中这样写,然后再加一个dataloop,就可以循环的来把值赋给request中,然后运行request.
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:sam="http://www.soapui.org/sample/"> <soapenv:Header/> <soapenv:Body> <sam:login> <username>${setUp#result}</username> </sam:login> </soapenv:Body> </soapenv:Envelope> |
2.该Groovy脚本的名称是"demo"
testRunner.testCase.testSuite.getTestCaseByName("TestCaseDemo").setPropertyValue("username","Loginn"+String.valueOf(Math.random()).substring( 0, 5 ))
testRunner.testCase.testSuite.getTestCaseByName("TestCaseDemo").setPropertyValue("password","Loginn123")
上面的例子中,TestCaseDemo是指testcase的名称,在request中这样写:
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:sam="http://www.soapui.org/sample/"> <soapenv:Header/> <soapenv:Body> <sam:login> <username>${#TestCase#username}</username> <password>${#TestCase#password}</password> </sam:login> </soapenv:Body> </soapenv:Envelope> |