[转载]android开发我的新浪微博客户端-用户首页面功能篇(5.2)

mikel阅读(742)

[转载]android开发我的新浪微博客户端-用户首页面功能篇(5.2) – 遇见未知的自己 – 博客园.

上一篇完成用户首页的UI实现,本篇接下来讲功能部分的实现,本页面主要的功能就用户关注的最新微博列表,从上一篇中知道本列表是用ID为Msglist的ListView控件来实现,本篇的主要就讲解如果获取微博列表数据给这个ListView提供显示数据。ListView每一条子数据分别由用户头像、用户昵称、发布时间、是否包含照片、微博内容这五部分组成,根据这五部分定义一个名为WeiBoInfo.java实体类,代码如下:

代码

public class WeiBoInfo {
//文章id
private String id;
public String getId(){
return id;
}
public void setId(String id){
this.id=id;
}
//发布人id
private String userId;
public String getUserId(){
return userId;
}
public void setUserId(String userId){
this.userId=userId;
}

//发布人名字
private String userName;
public String getUserName(){
return userName;
}
public void setUserName(String userName){
this.userName=userName;
}

//发布人头像
private String userIcon;
public String getUserIcon(){
return userIcon;
}
public void setUserIcon(String userIcon){
this.userIcon=userIcon;
}

//发布时间
private String time;
public String getTime(){
return time;
}
public void setTime(String time)
{
this.time=time;
}

//是否有图片
private Boolean haveImage=false;
public Boolean getHaveImage(){
return haveImage;
}
public void setHaveImage(Boolean haveImage){
this.haveImage=haveImage;
}

//文章内容
private String text;
public String getText(){
return text;
}
public void setText(String text){
this.text=text;
}

}

然后在res/layout目录下新建名为weibo.xml的Layout用来控制ListView子项的显示部件,代码很简单不多解释了,直接看下面代码:

代码

<?xml version=”1.0″ encoding=”utf-8″?>
<LinearLayout
xmlns:Android=”http://schemas.Android.com/apk/res/android”
android:layout_width
=”wrap_content”
android:layout_height
=”wrap_content”
android:orientation
=”horizontal”>
<ImageView
android:id=”@+id/wbicon”
android:layout_width
=”wrap_content”
android:layout_height
=”wrap_content”
android:src
=”@drawable/usericon”
android:layout_margin
=”8px”>
</ImageView>
<LinearLayout
android:layout_width=”fill_parent”
android:layout_height
=”wrap_content”
android:orientation
=”vertical”
android:paddingLeft
=”0px”
android:paddingRight
=”5px”
android:layout_marginTop
=”5px”
android:layout_marginBottom
=”5px”>
<RelativeLayout
android:layout_width=”fill_parent”
android:layout_height
=”wrap_content”>
<TextView
android:id=”@+id/wbuser”
android:layout_width
=”wrap_content”
android:layout_height
=”wrap_content”
android:textSize
=”15px”
android:textColor
=”#424952″
android:layout_alignParentLeft
=”true”>
</TextView>
<ImageView
android:id=”@+id/wbimage”
android:layout_width
=”wrap_content”
android:layout_height
=”wrap_content”
android:layout_marginTop
=”3px”
android:layout_marginRight
=”5px”
android:layout_toLeftOf
=”@+id/wbtime”>
</ImageView>
<TextView
android:id=”@+id/wbtime”
android:layout_width
=”wrap_content”
android:layout_height
=”wrap_content”
android:layout_alignParentRight
=”true”
android:textColor
=”#f7a200″
android:textSize
=”12px”>
</TextView>
</RelativeLayout>
<TextView
android:id=”@+id/wbtext”
android:layout_width
=”wrap_content”
android:layout_height
=”wrap_content”
android:textColor
=”#424952″
android:textSize
=”13px”
android:layout_marginTop
=”4px”>
</TextView>
</LinearLayout>
</LinearLayout>

接下来为列表控件定义一个数据Adapter,代码如下:

代码

private List<WeiBoInfo> wbList;

//微博列表Adapater
public class WeiBoAdapater extends BaseAdapter{

private AsyncImageLoader asyncImageLoader;

@Override
public int getCount() {
return wbList.size();
}

@Override
public Object getItem(int position) {
return wbList.get(position);
}

@Override
public long getItemId(int position) {
return position;
}

@Override
public View getView(int position, View convertView, ViewGroup parent) {
asyncImageLoader
= new AsyncImageLoader();
convertView
= LayoutInflater.from(getApplicationContext()).inflate(R.layout.weibo, null);
WeiBoHolder wh
= new WeiBoHolder();
wh.wbicon
= (ImageView) convertView.findViewById(R.id.wbicon);
wh.wbtext
= (TextView) convertView.findViewById(R.id.wbtext);
wh.wbtime
= (TextView) convertView.findViewById(R.id.wbtime);
wh.wbuser
= (TextView) convertView.findViewById(R.id.wbuser);
wh.wbimage
=(ImageView) convertView.findViewById(R.id.wbimage);
WeiBoInfo wb
= wbList.get(position);
if(wb!=null){
convertView.setTag(wb.getId());
wh.wbuser.setText(wb.getUserName());
wh.wbtime.setText(wb.getTime());
wh.wbtext.setText(wb.getText(), TextView.BufferType.SPANNABLE);
textHighlight(wh.wbtext,
new char[]{#},new char[]{#});
textHighlight(wh.wbtext,
new char[]{@},new char[]{:, });
textHighlight2(wh.wbtext,
http://, );

if(wb.getHaveImage()){
wh.wbimage.setImageResource(R.drawable.images);
}
Drawable cachedImage
= asyncImageLoader.loadDrawable(wb.getUserIcon(),wh.wbicon, new ImageCallback(){

@Override
public void imageLoaded(Drawable imageDrawable,ImageView imageView, String imageUrl) {
imageView.setImageDrawable(imageDrawable);
}

});
if (cachedImage == null) {
wh.wbicon.setImageResource(R.drawable.usericon);
}
else{
wh.wbicon.setImageDrawable(cachedImage);
}
}

return convertView;
}

上面的这个Adapter实现没有什么特别的很普通,不过这个中使用了AsyncImageLoader的方法,这个是用来实现用户头像图标的异步载入显示,这样能提高列表显示的速度,提高用户体验,AsyncImageLoader的代码如下:

代码

public class AsyncImageLoader {
//SoftReference是软引用,是为了更好的为了系统回收变量
private HashMap<String, SoftReference<Drawable>> imageCache;
public AsyncImageLoader() {
imageCache
= new HashMap<String, SoftReference<Drawable>>();
}

public Drawable loadDrawable(final String imageUrl,final ImageView imageView, final ImageCallback imageCallback){
if (imageCache.containsKey(imageUrl)) {
//从缓存中获取
SoftReference<Drawable> softReference = imageCache.get(imageUrl);
Drawable drawable
= softReference.get();
if (drawable != null) {
return drawable;
}
}
final Handler handler = new Handler() {
public void handleMessage(Message message) {
imageCallback.imageLoaded((Drawable) message.obj, imageView,imageUrl);
}
};
//建立新一个新的线程下载图片
new Thread() {
@Override
public void run() {
Drawable drawable
= loadImageFromUrl(imageUrl);
imageCache.put(imageUrl,
new SoftReference<Drawable>(drawable));
Message message
= handler.obtainMessage(0, drawable);
handler.sendMessage(message);
}
}.start();
return null;
}

public static Drawable loadImageFromUrl(String url){
URL m;
InputStream i
= null;
try {
m
= new URL(url);
i
= (InputStream) m.getContent();
}
catch (MalformedURLException e1) {
e1.printStackTrace();
}
catch (IOException e) {
e.printStackTrace();
}
Drawable d
= Drawable.createFromStream(i, src);
return d;
}

//回调接口
public interface ImageCallback {
public void imageLoaded(Drawable imageDrawable,ImageView imageView, String imageUrl);
}
}

完成上述的工作后,接下来就是显示微薄列表, 在HomeActivity的onCreate方法中调用loadList();代码如下:

代码

@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.home);

。。。。。。
loadList();
}

private void loadList(){
if(ConfigHelper.nowUser==null)
{

}
else
{
user
=ConfigHelper.nowUser;
//显示当前用户名称
TextView showName=(TextView)findViewById(R.id.showName);
showName.setText(user.getUserName());

OAuth auth=new OAuth();
String url
= http://api.t.sina.com.cn/statuses/friends_timeline.json;
List params
=new ArrayList();
params.add(
new BasicNameValuePair(source, auth.consumerKey));
HttpResponse response
=auth.SignRequest(user.getToken(), user.getTokenSecret(), url, params);
if (200 == response.getStatusLine().getStatusCode()){
try {
InputStream is
= response.getEntity().getContent();
Reader reader
= new BufferedReader(new InputStreamReader(is), 4000);
StringBuilder buffer
= new StringBuilder((int) response.getEntity().getContentLength());
try {
char[] tmp = new char[1024];
int l;
while ((l = reader.read(tmp)) != 1) {
buffer.append(tmp,
0, l);
}
}
finally {
reader.close();
}
String string
= buffer.toString();
//Log.e(“json”, “rs:” + string);
response.getEntity().consumeContent();
JSONArray data
=new JSONArray(string);
for(int i=0;i<data.length();i++)
{
JSONObject d
=data.getJSONObject(i);
//Log.e(“json”, “rs:” + d.getString(“created_at”));
if(d!=null){
JSONObject u
=d.getJSONObject(user);
if(d.has(retweeted_status)){
JSONObject r
=d.getJSONObject(retweeted_status);
}

//微博id
String id=d.getString(id);
String userId
=u.getString(id);
String userName
=u.getString(screen_name);
String userIcon
=u.getString(profile_image_url);
Log.e(
userIcon, userIcon);
String time
=d.getString(created_at);
String text
=d.getString(text);
Boolean haveImg
=false;
if(d.has(thumbnail_pic)){
haveImg
=true;
//String thumbnail_pic=d.getString(“thumbnail_pic”);
//Log.e(“thumbnail_pic”, thumbnail_pic);
}

Date date=new Date(time);
time
=ConvertTime(date);
if(wbList==null){
wbList
=new ArrayList<WeiBoInfo>();
}
WeiBoInfo w
=new WeiBoInfo();
w.setId(id);
w.setUserId(userId);
w.setUserName(userName);
w.setTime(time);
w.setText(text);

w.setHaveImage(haveImg);
w.setUserIcon(userIcon);
wbList.add(w);
}
}

}catch (IllegalStateException e) {
e.printStackTrace();
}
catch (IOException e) {
e.printStackTrace();
}
catch (JSONException e) {
e.printStackTrace();
}
}

if(wbList!=null)
{
WeiBoAdapater adapater
= new WeiBoAdapater();
ListView Msglist
=(ListView)findViewById(R.id.Msglist);
Msglist.setOnItemClickListener(
new OnItemClickListener(){
@Override
public void onItemClick(AdapterView<?> arg0, View view,int arg2, long arg3) {
Object obj
=view.getTag();
if(obj!=null){
String id
=obj.toString();
Intent intent
= new Intent(HomeActivity.this,ViewActivity.class);
Bundle b
=new Bundle();
b.putString(
key, id);
intent.putExtras(b);
startActivity(intent);
}
}

});
Msglist.setAdapter(adapater);
}
}
loadingLayout.setVisibility(View.GONE);
}

上面的loadList() 方法通过新浪Api接口http://api.t.sina.com.cn/statuses/friends_timeline.json获取当前登录用户及其所关注用户的最新微博消息,然后显示到列表中。

这样就完成了用户首页功能的开发。

[原创]获得Android系统的root权限

mikel阅读(957)

刚买了款中兴 X876的Android2.1的手机,发现不少联通绑定的功能很是烦人,动不动开机就自动启动,既占内存又影响使用,找个软件要半天!于是想卸载,发现联通真的做的很绝,居然卸载不了,居然是系统应用,这可怎么办?!

于是去Android相关网站逛了逛,发现款可以获得root权限的软件:

一键root 1.6 推荐给大家

下载地址:点击下载

软件简介:

想要Root吗?想到要抓不知道内容是什么的映像档,想到还要找记忆卡作Gold Card,再想到还要进工程模式,为的就是要取得Root权限(但取得Root跟刷ROM是两回事,只是刷好的ROM刚好开了Root权限而已),现在,似乎可以不用这么麻烦。天下没有没有漏洞的OS,就跟不会有没bug的程序一样,前阵子有人通过iOS的PDF漏洞成功JB,而Android自然也有漏洞,以便取得Root权限。这个程序叫做 “Universal Androot” ,便是把exploit走后门的流程写出UI形式,让使用者只要按一下按钮就可以Root,只是不是什么每支Android手机都可以成功,像 Samsung i9000就没办法,这是由台湾人写出来的,请大家多多测试,并帮忙回报。如果担心会破坏保修,可以按下 “UnRoot” ,就可回复原有的状态。使用机型列表: Google Nexus One (2.2) HTC Hero (2.1) HTC Magic (1.5) Dell Streak (2.1) Motorola Milestone (2.1) Motorola XT701 Motorola XT800 (2.1) Motorola ME511 Sony Ericsson X10 (1.6) Sony Ericsson X10 Mini Pro (1.6) Acer Liquid (2.1) Vibo A688 (1.6) 无法使用列表: Samsung i9000 (Galaxy S) Samsung i6500U HuaWei U8220 HTC Desire / Legend / Wildfire (/system 無法寫入) 这款一键RootUniversal Androot软件market里面还没有噢!因为太新了。如果想root的朋友看看自己的机型在不在可以使用的列表里面.

[转载]InfoQ: Android赢家密码的观点与实践——以软硬整合开发为例

mikel阅读(757)

[转载]InfoQ: Android赢家密码的观点与实践——以软硬整合开发为例.

观点与机会

大家都知道观点(又称视角)本身是没有对错的,但是观点会影响人们的决策,而决策又会影响行为,然后产生不同的结果。因之,基于“赢家密码”观点而实践策略,会带来赢的机会;反之,则可能会错失大好机会。

Android的三层API

在软硬整合开发里,最主要的议题就是,如何让底层硬件的创新性能或功能与上层应用程序(简称AP)的多样化相汇合,关于这项议题,我在《Android赢家密码》一书里,已经详细叙述了,就不再重述了。

Android平台上,规划上述软硬整合实践策略时,会涉及三层软件接口(泛称API),包括:

  • 上层的Framework API:这是位于Java层的AP与框架(Framework)之间。
  • 中层的JNI API:这是位于Java框架与C++层Android核心程序库(Library)之间。
  • 底层的HAL API:这是位于Android核心程序库与硬件驱动程序(Driver)之间。

俗语说:横看成岭侧成峰。意味着,事物本体只有一个,因为人人观点不同而有不同面貌的呈现。苏东坡《前赤壁赋》也写道:“盖将自其变者而观之,则天 地曾不能一瞬;自其不变者而观之,则物与我皆无尽也,而又何羡乎?”即使同一个人,基于不同角度,观察到的现象也不同,因为会影响到人生或其它事物的态 度、策略、行为和结果。

古典IT的观点

这个观点是基于“平台(Platform)”的概念,平台概念让人们联想到房屋的地基,AP就是房屋,而平台就是地基。其地位尊卑顺序是:主人->AP->平台,如下图1所示:

图1 古典IT观点下的Android三层API

在这观点下,其焦点在于:

  • 底层都是提供服务给上层调用,一切以User好用为目标,也就是追求优质的用户体验(User or Client Experience)。
  • API成为Façade的角色(可参考Façade Pattern),提供单一接口是实现优质用户体验的重要手段。
  • User希望AP稳定不变,AP希望Java框架不变,Java框架期待C/C++模块不变,C/C++模块期待底层的硬件驱动稳定不变。人人都期待脚底下的“平台”是不变的。

中国传统的观点

如果将Android里的三层API对应到中国清朝时期的三层城墙:万里长城、北京城和紫禁城。将可以让我们摆脱古老的IT观点,而得到新潮的观 点:中国永恒的智慧 + Android平台->赢家的软硬整合策略。这非常接近清代大臣张之洞所提倡的“中学为体,西学为用”观点。于是,得到下图:

图2 Android平台里的三层API

兹比较图1与图2:

  • 地位尊卑顺序相反。在图1里,愈上层的地位愈高。反之,在图2里,愈底层的地位愈高(例如皇帝居住于紫禁城内)。
  • 行为决策相反。采取图1观点的国度,人人争先恐后去做 AP,因为(误认为)AP的地位最高。反之,采取图2观点的国度,人人争先恐后去做框架和API,因为(真正)地位最高,必须建造API(城墙)避免敌人来争夺。
  • 建API成为最大赢家。Google、微软等公司采取图2观点,微软于2001年推出.NET框架;Google于2007年推出Android框架;两者都成为IT业武林盟主。
  • API让底层先获利。基于图2的观点,愈底层的地位愈高,万里长城让观内居民先获利(不是塞外先获利)。所以台湾宏达电(HTC)公司专注于开发Android底层硬件和驱动软件(关内部分),成为全球获利最大的Android手机厂商。

以POS系统开发为例——现实的POS情境和需求

POS软硬整合产品必须卖给业主(如大卖场的商家)。由于商家是做生意的人,不会设计卖场专用的POS应用程序(例如,将POS结帐款项送往ERP 系统)。所以,POS产品厂商就委托各地区或城市的当地应用软件开发商去帮助业主开发其专用的应用软件(简称AP)。于是,POS产品厂商就成为强龙,而 全球各地区的AP开发者成为地头蛇,这<强龙/地头蛇> 合作模式能提供给业主最佳的服务。例如,笔者就曾经担任NCR公司在台湾的地头蛇,服务台湾当地的银行POS应用软件系统。

过去,都是洋人企业扮演强龙角色,国内本地企业扮演地头蛇角色。如今,随着本地内需市场的急速成长,提供给本地企业跃居强龙地位的大好机会。所以, 本文不是叙述如何解决业主的需求,而是说明除了解决业主需求之外,又如何替POS产品(或手机)厂商设计出强龙系统架构,以即规划出其实现步骤。

第一步:从古典IT观点出发

在上一篇文章里,已经说明了古典IT观点的基本架构。现在,就来厘清它的两项基本流程:请求流程(Request Flow)和服务流程(Service Flow)。如下图1 所示:

图3 古典IT观点下的两项基本流程

在这观点下,并不太重视另一项流程,就是命令流程(Command Flow)。甚至很多人认为图里的请求流程就是命令流程。由于命令的来源是业主或AP(如古代的员外),让底层POS产品厂商成为长工,就无法实现其强龙的梦想了。

第二步:加上命令流程(Command Flow)

这是基于上一篇文章里的新潮观点,基于这个观点,就能凸显出命令流程的重要性,以及其流动方向。如下图2所示:

图4 新潮观点所凸显的命令流程

大家都知道命令的来源是紫禁城内,流向北京城外,再流到长城之外。于是,将上图1和图2整合起来就是新潮观点下的三项主要流程。

第三步:设立关口传达命令

为了确保紫禁城内清朝皇帝的强龙主导地位,必然会最第一道防线(即万里长城)设立关口,并重兵驻守,成为具有高度主导性的接口,例如山海关、居庸关 等。这种主导性关口,就是笔者在《Android赢家密码》一书所说的“主动型API”。唯有主动型的API(或称接口,或称关口)才能确保命令的传递和 执行。如下图3所示:

图5 山海关、居庸关就是主动型API

在这观点下,业主(或用户)和AP是在塞外,是要服从命令的,在直觉上对用户体验并不会有所贡献。然而,因为硬件(如宏达电HTC手机或联迪 POS)产品厂商位居紫禁城内,拥有强龙地位,其“强龙体验”的滋味是美好的。过去,软件开发人员一味地追求提升用户(地头蛇)体验,未能提升软件人员本 身的地位。如今,软件开发人员除了提升用户体验之外,也关注于提升底层硬件厂商的强龙体验。基于这种新观点,让台湾的宏达电公司成为最赚钱的公司,其 HTC手机也创造极佳的用户体验,至今(2011年)销售量全球第一,同样地台湾Android相关软件人员也因而获利。这说明了这个新潮观点,的确能创 造“硬件厂商、手机用户、软件人员”三赢的局势。其将业主和 AP视为塞外,并将用户体验降到第二顺位;反而大幅提升了用户体验。这就是笔者在《Android赢家密码》一书所说的“神秘力量之一”。

第四步:实现软件关口(即框架API)

具有主导性(或防御性)的关口,就是主动型(即主导性)的API。在软件上,其实现机制是极为简单的概念:基类(Base Class),又称为父类(Super Class)。如下图3所示:

图6 软硬整合系统的山海关等关口

软件关口就是Java或C++语言的基类,是每一位软件开发人员都具备的基本技能。

结语

俗语说:不为也;非不能也。如今,为何洋人喜欢撰写框架基类、掌握框架API,位居强龙;而我们身边的大多数软件开发人员,还是拥抱着古典IT观 点、孜孜不倦撰写AP子类,一未追求用户体验呢? 换个观点而已,做法非常简单;只是不去做,并非不会做。由于底层硬件功能是整体系统服务的源头,也是提升用户体验的源头;唯有采取中国固有的新潮观点,让 底层硬件服务视为九五至尊的强龙地位,才能真正实现好的用户体验,并带给软件开发人员极佳的获利机会。

关于作者

高焕堂,台湾软件架构设计大师,从事IT行业近30年,被称为“台湾OO技术教父级代表人物”。现任MISOO软件开发与管理顾问公司首席架构师,编著过十余本软件技术相关书籍。

[转载]打包Asp.Net 网站成为一个exe 方便快捷的进行客户演示

mikel阅读(828)

[转载]打包Asp.Net 网站成为一个exe 方便快捷的进行客户演示 – 葡萄城控件技术团队博客 – 博客园.

在Asp时代有一个NetBox 产品可以把整个Asp网站AllInOne的打包成一个exe,在没有IIS的情况下可以单独运行这个exe来开启整个网站。在ASP.NET 下一直没有类似的产品出现,可能是IIS已经非常的强大了,不需要类似的产品了? 但是在某种场景下还是需要一个类似功能的产品的,这个产品不是用来部分替代IIS来做一个轻量级的IIS,而是用来方便快捷的进行客户展示。
例如,当完成一个网站开发后,或者部分完成开发后,想给客户展示一下,收集一下客户的反馈,一般有两种做法:
1. 自己有主机和域名,把网站发布到Internet 上,让用户通过Internet访问网站。
2. 把网站部署到一台笔记本上,让一名工程师带着网站到客户那里收集客户反馈。
结合NetBox的思想是否可以把整个网站打包成一个exe,尽量把相关的东西都AllInOne到一个exe里,这样给客户演示的时候,就可以直接把这个exe发给用户,用户直接运行这个exe就可以看到网站的实现的情况了,这样做是否又给网站演示增加了一种新的手段。
Jelly.Packer.exe就是从这个想法而开发出来的 打包程序,把整个网站打包成一个AllInOne的exe,然后将打包生成的exe发给客户做演示。

Home Directory: 要打包网站所在的位置,某个你已经编译好的要发布的站点的文件夹,一般是Visual Studio站点publish输出的文件夹。
Virtual Directory: 站点虚拟路径,一般使用 “/”, 如果你打包了两个站点,想在同一端口运行,可以使用虚拟路径来区分。
List Directory: 在没有默认页面的时候,是否允许列出目录。
Authentication: 是否要求安全身份访问。
Auto Show: 是否自动开启站点,并同时开启默认浏览器访问此站点。
Default Files: 站点默认页面。
当 配置好上面的属性后,点 ”OK” 后,就会在Jelly.Packer.exe同目录下生成一个Jelly.SingleRunner.exe,这个 Jelly.SingleRunner.exe 就是指定网站AllInOne所打包好的exe,可以将这个exe发给客户做演示。

当运行Jelly.SingleRunner.exe 后,会在刚刚Jelly.Packer.exe所设置的端口上开启指定的网站:


最 初曾经考虑过把一个轻型的web server ,网站,和一个轻型浏览器打包在一起,这样就不需要占用端口了,就像MSDN帮助手册的 ms-help 协议那样来实现,后来考虑到需要让多个浏览器都可以访问,同时也可以把这个演示站点公开发布到本地局域网里,让客户本地网络里的别的机器也可以访问,基于 这种需求,把轻型web server和网站打包在一起,可能是比较好的选择吧。
打包程序下载:

附件: 06_003955_khaoJellyPacker.rar (2011-2-16 12:01:48, 116.80 K)

把BlogEngine V2.0打包成了exe:

附件: BlogEngine_Jelly_SingleRunner.rar (2011-2-16 12:01:48, 2639.24 K)

相关发布:

附件: 06_32323_new_JellyRelease.rar (2011-2-16 12:01:48, 263.65 K)

[转载]Web应用架构探索笔记 —— 查询

mikel阅读(932)

[转载]Web应用架构探索笔记 —— 查询 – dudu – 博客园.

在Web应用开发中,最常见也容易变化的一种需求是根据不同的查询条件获取数据列表。如何传递查询条件将影响程序应对需求变化的能力,一定要在架构中重点考虑。

开始时我们使用一堆参数传递查询条件,比如:

List<SiteMsg> GetMsgList(int pageIndex, int pageSize, int RecipientId);

结果,每个不同的查询都要写一个接口,产生了一堆接口;查询条件改变,接口也随之要改。写程序最痛苦的事莫过于接口的频繁变化。

后来使用查询对像,比如:

List<SiteMsg> GetMsgList(SiteMsgQuery msgQuery);

这样,查询条件改变时,只需修改SiteMsgQuery的定义,接口保持不变。采用这个方法后,写代码比之前少了很多痛苦。

但使用这个方法有个地方不爽,完成一次查询需要进行两次实例化,一次是查询对象SiteMsgQuery的实例化,一次是领域对象 SiteMsgManager(负责业务逻辑)的实例化。在博客园程序架构中,查询对象的实例化是在表现层完成的,如果是ajax调用,json会自动反 序列为查询对象;领域对象的实例化在服务层完成。

为了让代码写的更爽一些,我们又进行了尝试,取消查询对象SiteMsgQuery,将它的属性放到领域对象中。这样减少了一次实例化,只需一次,如果是ajax调用,可以实现服务器端“零实例化”。

下面看一下代码示例:

领域模型的定义:

[DataContract] public class SiteMsgManager { public SiteMsgManager() { } #region Properies [DataMember] public int PageIndex { get; set; } [DataMember] public int PageSize { get; set; } [DataMember] public int RecipientId { get; set; } public List<SiteMsg> List { get; set; } #endregion public void GetList() { using (SpaceObjectContext context = new SpaceObjectContext()) { this.List = context.SiteMsgs .Where(msg => msg.RecipientSpaceUserId == this.RecipientId) .OrderByDescending(msg => msg.id) .Skip((PageIndex - 1) * PageSize) .Take(this.PageSize) .ToList(); } }

服务实现类(也是WCF的服务实现):

public class MsgService : IMsgService { public List<SiteMsg> GetMsgList(SiteMsgManager siteMsgManager) { siteMsgManager.GetList(); return siteMsgManager.List; } }

UI层调用代码(WCF调用,ASP.NET MVC控制器):

public class MsgController : Controller { //ajax调用 [HttpPost] public ActionResult List(SiteMsgManager msgManager) { return View("MsgList", GetInboxMsgList(msgManager)); } public ActionResult Inbox() { SiteMsgManager msgManager = new SiteMsgManager() { PageIndex = 1, PageSize = 30 }; return View("Inbox", GetInboxMsgList(msgManager)); } private List<SiteMsg> GetInboxMsgList(SiteMsgManager msgManager) { int spaceUserId = Util.GetCurrentUser(System.Web.HttpContext.Current).SpaceUserID; msgManager.RecipientId = spaceUserId; MsgServiceClient client = new MsgServiceClient(); List<SiteMsg> siteMsgList = client.GetMsgList(msgManager).ToList(); try { client.Close(); } catch { client.Abort(); } return siteMsgList; } }

看看上面供ajax调用的List方法,不需要进行SiteMsgManager的实例化,系统根据ajax客户端传递过来的json参数自动反序列化生成SiteMsgManager对象。

再来看看ajax客户端代码:

function GetMsgList(pageIndex, pageSize) { var msgManager = {} msgManager.PageIndex = pageIndex; msgManager.PageSize = pageSize; $.ajaxSettings.dataType = 'plain/text'; $.ajaxSettings.url = '/msg/list'; $.ajaxSettings.data = '{"msgManager":' + JSON.stringify(msgManager) + '}'; $.ajaxSettings.success = function (data) { $("#msg_list").html(data); }; $.ajax(); }

js传递的也是一个对像。

整个ajax调用的流程是这样的:js对象 (msgManager)->json->MsgController(MVC控制器)->代理领域对象 SiteMsgManager(WCF客户端代理类的实例)->WCF服务接口->WCF服务实现(自动通过反序列化生成领域对象 SiteMsgManager,并调用GetList()方法)->领域对象完成业务逻辑操作返回数据。

采用这种方法,感觉写代码比以前更享受了。我们在实际开发中也开始使用这种架构,并根据实际使用情况进一步改进。

注:这篇随笔只是我们在探索博客园Web应用架构过程中的笔记,并不代表这是更好更合理的方法。写出来一是为了分享,二是帮助自己更好的思考,三是记录架构演变的过程。

[转载]googlemap数据采集器(四) [免费程序下载] - liongis - 博客园

mikel阅读(929)

[转载]googlemap数据采集器(四) [免费程序下载] – liongis – 博客园.

之前用GoogleMap的本地搜索接口写了一个简单的采集器,最近有朋友希望试用一下,所以继续完善了一下功能,特免费提供给大家下载。

新加的功能有:

1.去除重复数据。

2.支持导出CSV文件。

下载地址是:http://files.cnblogs.com/liongis/GMapDataDown.rar

程序需要.net framework 3.0支持,没有的朋友请到搜索一下,进行安装。

软件操作很简单,这里就不再说明,不会的可以问我。

照例上图:

[转载]android UI进阶之弹窗的使用

mikel阅读(1709)

[转载]android UI进阶之弹窗的使用 – noTice520 – 博客园.

其实这是第二遍写这篇博客……浏览器崩溃了。。。我还以为博客园和CSDN的一样自动保存,竟然没有!好吧,没事,重新写过。

年就算过完啦。新年第一篇来讲下Android中的PopupWindow。姑且就叫做弹窗吧,其实和AlertDialog一样,也是一种对话框,两者也经常混用,但是也各有特点。

下面就看看使用方法。首先初始化一个PopupWindow

1 PopupWindow mPop = new PopupWindow(getLayoutInflater().inflate(R.layout.window, null), 2 LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);

实现PopupWindow的构造函数,第一个参数导入布局,后面两个指定宽和高。弹窗一般有两种展示方法,用showAsDropDown()和showAtLocation()两种方法实现。一般参数有两种,有偏移和无偏移。

1 bt1.setOnClickListener(new OnClickListener(){ 2 3 @Override 4 public void onClick(View v) { 5 initPopWindow(); 6 mPop.showAsDropDown(v);//以这个Button为anchor(可以理解为锚,基准),在下方弹出 7   8 }}); 9 10 bt2.setOnClickListener(new OnClickListener(){ 11 12 @Override 13 public void onClick(View v) { 14 initPopWindow(); 15 mPop.showAsDropDown(v,20,-20);//横轴偏移20,纵轴-20,一个状态栏的长度 16   17 }}); 18 19 bt3.setOnClickListener(new OnClickListener(){ 20 21 @Override 22 public void onClick(View v) { 23 initPopWindow(); 24 mPop.showAtLocation(PopWindow.this.findViewById(R.id.rl), 25 Gravity.CENTER, 0, 0);//在屏幕居中,无偏移 26   27 }}); 28 29 30 bt4.setOnClickListener(new OnClickListener(){ 31 32 @Override 33 public void onClick(View v) { 34 initPopWindow(); 35 mPop.showAtLocation(PopWindow.this.findViewById(R.id.rl), 36 Gravity.TOP | Gravity.LEFT, 20, 20);//在屏幕顶部|居右,带偏移 37   38 }}); 39 40 bt5.setOnClickListener(new OnClickListener(){ 41 42 @Override 43 public void onClick(View v) { 44 if (mPop != null) { 45 mPop.dismiss(); 46 } 47 48 }});

其中initPopWindow()方法来初始化一个弹窗

1 private void initPopWindow() { 2 if (mPop == null) { 3 mPop = new PopupWindow(getLayoutInflater().inflate(R.layout.pop, null), 4 LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT); 5 } 6 if (mPop.isShowing()) { 7 mPop.dismiss(); 8 } 9 }

这里定义了4个按钮来呈现展示效果,一个按钮用来关闭,来看下第三个按钮点击效果,就不一一截图了,大家可以自己试下。

看过我以前这系列博客的朋友就会发现,PopupWindow里面的布局就是抽屉里面用的布局,后面的背景就是上篇博客讲的可延伸图像。为什么用这个呢,我们来看下UC的menu效果

可能这个是用AlertDialog做的,但是用PopupWindow也非常方便。很多的应用都使用了这样的menu。这里我就不去仿照UC来做了,就用上面那个布局,原理是一样的,换换图片就行。

要做的很简单,看代码

public boolean onKeyDown(int keyCode, KeyEvent event){ //截获按键事件 if(keyCode == KeyEvent.KEYCODE_MENU){ initPopWindow(); mPop.showAtLocation(this.findViewById(R.id.rl), Gravity.BOTTOM|Gravity.CENTER_HORIZONTAL, 0, 0); //在屏幕底部 }else if(keyCode == KeyEvent.KEYCODE_BACK){ if(mPop.isShowing()){ mPop.dismiss(); }else{ System.exit(0); } } return false; }

看下效果

图片似乎不是很给力,大家搞点漂亮点的图片就可以了。

我把这个代码全部贴出来

1 package com.notice.popWindow; 2 3  import android.app.Activity; 4  import android.os.Bundle; 5  import android.view.Gravity; 6 import android.view.KeyEvent; 7 import android.view.View; 8 import android.view.View.OnClickListener; 9 import android.view.ViewGroup.LayoutParams; 10 import android.widget.Button; 11 import android.widget.GridView; 12 import android.widget.PopupWindow; 13 14 public class PopWindow extends Activity { 15 private GridView gv; 16 private Button bt1; 17 private Button bt2; 18 private Button bt3; 19 private Button bt4; 20 private Button bt5; 21 private int[] icons={R.drawable.browser,R.drawable.gallery, 22 R.drawable.camera,R.drawable.gmail, 23 R.drawable.music,R.drawable.market, 24 R.drawable.phone,R.drawable.messages,R.drawable.maps}; 25 private String[] items={"浏览器","图片","相机","时钟","音乐","市场","拨号","信息","地图"}; 26 private PopupWindow mPop; 27 private View layout; 28 private void initPopWindow() { 29 if (mPop == null) { 30 mPop = new PopupWindow(layout, 31 LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT); 32 } 33 if (mPop.isShowing()) { 34 mPop.dismiss(); 35 } 36 } 37 /** Called when the activity is first created. */ 38 @Override 39 public void onCreate(Bundle savedInstanceState) { 40 super.onCreate(savedInstanceState); 41 setContentView(R.layout.main); 42 bt1 = (Button)findViewById(R.id.bt1); 43 bt2 = (Button)findViewById(R.id.bt2); 44 bt3 = (Button)findViewById(R.id.bt3); 45 bt4 = (Button)findViewById(R.id.bt4); 46 bt5 = (Button)findViewById(R.id.bt5); 47 layout = View.inflate(this, R.layout.window, null); 48 gv = (GridView) layout.findViewById(R.id.gv); 49 MyAdapter adapter=new MyAdapter(this,items,icons); 50 gv.setAdapter(adapter); 51 52 bt1.setOnClickListener(new OnClickListener(){ 53 54 @Override 55 public void onClick(View v) { 56 initPopWindow(); 57 mPop.showAsDropDown(v);//以这个Button为anchor(可以理解为锚,基准),在下方弹出 58 59 }}); 60 61 bt2.setOnClickListener(new OnClickListener(){ 62 63 @Override 64 public void onClick(View v) { 65 initPopWindow(); 66 mPop.showAsDropDown(v,20,-20);//横轴偏移20,纵轴-20,一个状态栏的长度 67 68 }}); 69 70 bt3.setOnClickListener(new OnClickListener(){ 71 72 @Override 73 public void onClick(View v) { 74 initPopWindow(); 75 mPop.showAtLocation(PopWindow.this.findViewById(R.id.rl), 76 Gravity.CENTER, 0, 0);//在屏幕居中,无偏移 77 78 }}); 79 80 81 bt4.setOnClickListener(new OnClickListener(){ 82 83 @Override 84 public void onClick(View v) { 85 initPopWindow(); 86 mPop.showAtLocation(PopWindow.this.findViewById(R.id.rl), 87 Gravity.TOP | Gravity.LEFT, 20, 20);//在屏幕顶部|居右,带偏移 88 89 }}); 90 91 bt5.setOnClickListener(new OnClickListener(){ 92 93 @Override 94 public void onClick(View v) { 95 if (mPop != null) { 96 mPop.dismiss(); 97 } 98 99 }}); 100 101 } 102 public boolean onKeyDown(int keyCode, KeyEvent event){ 103 //截获按键事件 104 if(keyCode == KeyEvent.KEYCODE_MENU){ 105 initPopWindow(); 106 mPop.showAtLocation(this.findViewById(R.id.rl), 107 Gravity.BOTTOM|Gravity.CENTER_HORIZONTAL, 0, 0); //在屏幕底部 108 }else if(keyCode == KeyEvent.KEYCODE_BACK){ 109 if(mPop.isShowing()){ 110 mPop.dismiss(); 111 }else{ 112 System.exit(0); 113 } 114 115 } 116 return false; 117 118 } 119 }

其中的MyAdapter类可以去看我以前的一篇写抽屉的博客,代码都在里面。然后就是window.xml

<?xml version="1.0" encoding="utf-8"?> <GridView xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/gv" android:background="@drawable/tbg" android:layout_width="wrap_content" android:layout_height="wrap_content" android:numColumns="3" android:gravity="center"> </GridView>

只有一个GridView。main.xml就不贴了,就几个Button。

当然弹窗的作用当然不仅限于menu,你还可以用他做的更多来为你UI增色。今天就写到这了,哎,写了好久。有问题可以留言交流。

[转载]android开发我的新浪微博客户端-用户首页面UI篇(5.1)

mikel阅读(921)

[转载]android开发我的新浪微博客户端-用户首页面UI篇(5.1) – 遇见未知的自己 – 博客园.

在前篇完成了用户登录功能后开始用户首页的开发,用户的首页主要的内容是当前登录用户关注的微博列表,本篇先来讲讲UI的实现,效果如上图,整个页面分 为上、中、下三部分,上面部分是工具条,显示当前登录用户的昵称以及写微博、刷新两个功能按钮;中间部分是当前用户关注的最新微博列表,下面部分是功能切 换栏,用来进行各个功能之间的切换。

首先新建名为HomeActivity.java的Activity作为用户首页,然后在res/layout目录下新建名为home.xml的Layout,具体代码如下:

代码

<?xml version=”1.0″ encoding=”utf-8″?>
<LinearLayout
xmlns:Android=”http://schemas.Android.com/apk/res/android”
android:id
=”@+id/layout”
android:orientation
=”vertical”
android:layout_width
=”fill_parent”
android:layout_height
=”fill_parent”>

<RelativeLayout
android:layout_width=”fill_parent”
android:layout_height
=”wrap_content”
android:layout_margin
=”3px”>
<ImageView
android:layout_width=”wrap_content”
android:layout_height
=”wrap_content”
android:src
=”@drawable/logo_ss”>
</ImageView>
<TextView
android:id=”@+id/showName”
android:layout_width
=”wrap_content”
android:layout_height
=”wrap_content”
android:layout_centerInParent
=”true”
android:textColor
=”#343434″
android:textSize
=”15px”>
</TextView>
<ImageButton
android:id=”@+id/writeBtn”
android:layout_width
=”wrap_content”
android:layout_height
=”wrap_content”
android:layout_toLeftOf
=”@+id/refreshBtn”
android:background
=”@drawable/btn_write_selector”>
</ImageButton>
<ImageButton
android:id=”@+id/refreshBtn”
android:layout_width
=”wrap_content”
android:layout_height
=”wrap_content”
android:layout_alignParentRight
=”true”
android:layout_marginLeft
=”12px”
android:background
=”@drawable/btn_refresh_selector”>
</ImageButton>
</RelativeLayout>

<LinearLayout
android:layout_width=”fill_parent”
android:layout_height
=”wrap_content”
android:background
=”@drawable/hr”>
</LinearLayout>

<RelativeLayout
android:layout_width=”fill_parent”
android:layout_height
=”fill_parent”>

<ListView
android:id=”@+id/Msglist”
android:layout_width
=”fill_parent”
android:layout_height
=”match_parent”
android:divider
=”@drawable/divider”
android:dividerHeight
=”2px”
android:layout_margin
=”0px”
android:background
=”#BBFFFFFF”
android:cacheColorHint
=”#00000000″
android:layout_above
=”@+id/toolbarLayout”
android:fastScrollEnabled
=”true”
android:focusable
=”true”>
</ListView>

<LinearLayout
android:id=”@+id/loadingLayout”
android:layout_width
=”wrap_content”
android:layout_height
=”wrap_content”
android:orientation
=”vertical”
android:visibility
=”invisible”
android:layout_centerInParent
=”true”>
<ProgressBar
android:id=”@+id/loading”
android:layout_width
=”31px”
android:layout_height
=”31px”
android:layout_gravity
=”center”
style
=”@style/progressStyle”>
</ProgressBar>
<TextView
android:layout_width=”wrap_content”
android:layout_height
=”wrap_content”
android:text
=”正在载入”
android:textSize
=”12px”
android:textColor
=”#9c9c9c”
android:layout_gravity
=”center”
android:layout_below
=”@+id/loading”>
</TextView>
</LinearLayout>

<LinearLayout
android:id=”@+id/toolbarLayout”
android:layout_width
=”fill_parent”
android:layout_height
=”44dip”
android:layout_alignParentBottom
=”true”>
</LinearLayout>
</RelativeLayout>
</LinearLayout>

这个布局首先是一个竖直的根LinearLayout,在这个根LinearLayout里面分别是两个RelativeLayout, 第一个RelativeLayout 用来显示页面的工具条,第二个RelativeLayout用来显示列表以及底部的功能栏,特别主要在这第二个 RelativeLayout中有一个id为loadingLayout的LinearLayout是用来显示数据载入中的动画,它的 android:visibility属性为invisible(也可以设置成gone,区别:invisible这个View在ViewGroupt中仍保留它的位置,不重新layout
gone>不可见,但这个View在ViewGroupt中不保留位置,重新layout,那后面的view就会取代他的位置。 ),也就是一开始不显示的意思,接下来看看

<ProgressBar
android:id=”@+id/loading”
android:layout_width=”31px”
android:layout_height=”31px”
android:layout_gravity=”center”
style=”@style/progressStyle”>
</ProgressBar>
这个ProgressBar控件就是用来显示动画用的,关键就是 style=”@style/progressStyle”,在res/values目录下新建名为loadingstyles.xml,内容如下:
代码

<?xml version=”1.0″ encoding=”UTF-8″?>
<resources>
<style name=”progressStyle” width=”38″ height=”38″ parent=”@android:style/Widget.ProgressBar.Small”>
<item name=”android:indeterminateDrawable”>@anim/loading</item>
</style>
</resources>

接着准备好r1.png – r8.png,八张不同的小图片分别代表每旋转45度图片,八张刚好是360度。把这些图片添加到res/drawable-mdpi目录中。然后在res/anim目录下新建名为loading.xml动画文件,内容如下:

代码

<?xml version=”1.0″ encoding=”UTF-8″?>
<animation-list android:oneshot=”false”
xmlns:android
=”http://schemas.android.com/apk/res/android”>
<item android:duration=”200″ android:drawable=”@drawable/r1″ />
<item android:duration=”200″ android:drawable=”@drawable/r2″ />
<item android:duration=”200″ android:drawable=”@drawable/r3″ />
<item android:duration=”200″ android:drawable=”@drawable/r4″ />
<item android:duration=”200″ android:drawable=”@drawable/r5″ />
<item android:duration=”200″ android:drawable=”@drawable/r6″ />
<item android:duration=”200″ android:drawable=”@drawable/r7″ />
<item android:duration=”200″ android:drawable=”@drawable/r8″ />
</animation-list>

关于Android播放动画实现我是参考http://terryblog.blog.51cto.com/1764499/388226

本篇到这里就结束了,下一篇继续讲用户首页的功能实现,请关注。

[转载]构建高性能ASP.NET站点 第六章—性能瓶颈诊断与初步调优(下后篇)—减少不必要的请求

mikel阅读(1318)

[转载]【原创】构建高性能ASP.NET站点 第六章—性能瓶颈诊断与初步调优(下后篇)—减少不必要的请求 – ASP.NET 架构 – 博客园.

服务端的要处理的请求越 多,无疑服务端的压力也就越大,尤其是有些请求需要访问一些比较昂贵的资源,例如数据库,服务端的文件等。但是我们必须知道,在到达服务端的请求中,有些 请求时我们希望的,例如网站的用户的请求,有些请求其实是不必要,甚至是我们不想要的,为此,我们要避免这样的请求,节省服务端的资源,从而提高性能。

搜索引擎

首先来看看有关搜索引擎的问题。

然后搜索引擎爬到我们的站点是一件好的事情,很多的SEO可以进行,推广站点。同时,在站点中,有些文件或者资源比较的私密,或者我们不希望被搜索引擎请求和收录的,因为每次搜索引擎在请求这些资源的时候,就是发送请求到我们的站点服务器,势必会加重服务器的负载。

不需要被搜索引擎请求的文件一般如下:

1. 图片资源

2. Js脚本,css

3. 一些需要身份验证或者授权才能看的页面(如果页面需要验证之后才能看,搜索引擎收录了也作用不大)

我们可以设置一下,告诉搜索引擎的蜘蛛程序如何爬我们的站点。

步骤如下:

1. 在站点的根目录下面,创建一个robots.txt的文件。

2. 写入文件。如果我们希望阻止所有的搜索引擎来爬我们的站点的页面,那么就可以在文件中写入下面的配置:

User-agent: *

Disallow: /

如果希望阻止搜索引擎爬某个文件夹,可以配置如下:

User-agent: *

Disallow: /images/

Disallow: /js/

Disallow: /css/

Disallow: /private/

更有趣的是:对于某些搜索引擎,我们还可以改变他们的蜘蛛程序爬我们站点的频率,设置如下:

User-agent: *

Crawl-delay: 10

大家可以去上网找下一些如何影响Google,百度等蜘蛛程序的设置。

热链接问题

就是在A网站上面显示一个来自B网站的图片链接。例如我们在自己的站点上面有一个链接如下:<img src=”http://www.xxx.com/yyy.gif”/>,那么在别人在浏览我们的站点的时候,就回去别人的那个站点(http://www.xxx.com/yyy.gif)去请求这个图片,那么势必会消耗他们的服务器的资源。发过来,如果别人在他们的站点上采用了我们的图片或者其他的链接资料,那么用户在浏览别人的站点的时候就会消耗我们站点的服务端资源和带宽。

为一个组件就可以阻止这种情况的发生:http://www.iis.net/community/default.

aspx?tabid=34&i=1288&g=6.大家去看看。

验证码(CAPTCHA)

我们常常在站点中加入一些验证码的功能来防止网络注册机。一般是生成一张有文字的图片,然后根据验证用户输入的文字和图片中的文字是否一样来判断此时的用户是人还是注册机。

通过验证码阻止了注册机随意的消耗站点资源(如果没有验证码,注册机可以不断的注册信息,大小服务器和数据库资源,而且产生很多的垃圾数据)

我们自己写生成验证码的程序,一般通过GDI+来做,同时也可以采用一些第三方的库实现,例如:reCAPTCHA: http://recaptcha.net/,大家上网找下,很多的。

网络刮刀(Scrapers)

这个问题必须引起重视。如果我们的站点上面有很多的有用的信息,那么别人可能就可能开发一个程序来到我们的站点抓取信息,然后把这些内容放到自己的站点上面。例如,很多的内容型的站点每天都从博客园的首页上面来抓取信息,然后放到他们的站点上,增加他们的访问量。

本来站点被搜索引擎抓就有点消耗性能了,如果还被很多的这样的网络刮刀来抓内容,对站点的性能影响可想而知。

如果那些网络刮刀程序的的IP地址变化不频繁,而且请求我们站点的频率比较的由规律,那么我们就可以采用一些代码的方式来防止这样的请求。例如,我们可以监测:同一个IP是否在20min之内发送了100个请求,如果是,我们就推测:可能是别人在抓我们的站点内容,我们就拒绝这个IP的请求。

当然了,上面只是一些简单的方法,对于一些复杂的Dos攻击,上面的监测代码基本没有作用。因为Dos攻击中,攻击的IP地址是变化的。

下面我们就写一些代码来防止简单的网络刮刀程序和简单的Dos攻击。基本的思想就是:如果在给定的时间段内,如果某个用户的请求很多,超过了一定的数量,那么我们就认为这个用户可能是网络刮刀程序,然后就拒绝下面的请求,一段时间之后,再次允许这个从这个IP发出的请求。

下面的代码中:假设如果一个用户在5秒之内发出了100个请求,那么我们就认为这是网络刮刀程序或者是网站的攻击者。当然,我们还考虑这个发送请求的用户是否是搜索引擎的蜘蛛程序。(下面的代码只是简单作为演示,不是实际生产的代码,抛砖引玉)

private const int intervalSeconds = 30;
private const int maxRequestsInInterval = 5;

如果认为这个用户是攻击者,那么我们就阻止用户的请求,阻止时间是20

private const int blockedPeriodSeconds = 20;

下面,我们创建一个类来描述一个访问者的信息。如下:

代码

private class VisitorInfo
{
public int nbrHits;
public bool blocked;

public VisitorInfo()
{
nbrHits
= 1;
blocked
= false;
}
}

BotDefence类中加入一个方法IsBotAttach来判断一个请求是否是攻击性的请求。如下:

代码

public static bool IsDosAttack()
{
string visitorIP = HttpContext.Current.Request.UserHostAddress;

VisitorInfo visitorInfo = (VisitorInfo)HttpContext.Current.Cache[visitorIP];
if (visitorInfo == null)
{
HttpContext.Current.Cache.Insert(
visitorIP,
new VisitorInfo(), null,
DateTime.Now.AddSeconds(intervalSeconds),
System.Web.Caching.Cache.NoSlidingExpiration);
}
else
{
if (visitorInfo.blocked)
{
return true;
}

visitorInfo.nbrHits++;
if (visitorInfo.nbrHits > maxRequestsInInterval)
{
visitorInfo.blocked
= true;
HttpContext.Current.Cache.Insert(
visitorIP, visitorInfo,
null,
DateTime.Now.AddSeconds(blockedPeriodSeconds),
System.Web.Caching.Cache.NoSlidingExpiration);
return true;
}
}
return false;
}

上面的代码都是自解释的,很容易看懂,就不赘述了。

当然了,上面的代码很简单,我们可以保存每个请求IP的地址,然后分析。相关的方法网络上面有很多,大家自己找!

本篇就到这里了。下一章就详细的讲述如何解决内存的性能瓶颈,敬请关注。

[转载]构建高性能ASP.NET站点 第六章—性能瓶颈诊断与初步调优(下前篇)—简单的优化措施

mikel阅读(838)

[转载]【原创】构建高性能ASP.NET站点 第六章—性能瓶颈诊断与初步调优(下前篇)—简单的优化措施 – ASP.NET 架构 – 博客园.

部署优化

我们都知道,不同的部署方式对站点的性能是有影响的,可能有些朋友已经知道了这点,不管怎样,我们这里还是详细系统的讲述一下这个问题,熟悉的朋友权当回顾J

Release方式编译项目

如果我们的项目是用Project的方式建立的,也就是说:我们的站点包含在一个Solution解决方案中,那么在发布之前,编译项目的时候,采用release方式,这种方式会减少CPU的使用率。因为采用Debug的方式发布,编译器会编译后的代码中加入很多的信息,如调试信息等。

操作步骤:

1. VS中,选择Build | Configuration Manager”.如下:

2. Active Solution Configuration下拉框现在””Release”,然后Close”.那么Solution就以Release方式编译。(其实使得Solution编译为Release的方法很多,例如在Solution上面右击属性,然后去设置也是可以的)

现在虽然SolutionRelease方式了,但是如果我们去查看这个Solution下面的ASP.NET站点程序的config文件,发现还是deubg方式的。那么我们在发布站点的时候,需要手动的去修改为release

:如果Solution是以Debug方式编译,即使web.config设置了release,最后发布的站点的代码还是方式的。

站点发布

发布的步骤如下:

1. 修改web.config配置如下:

2. 在站点上面右键选择Publish”.如下:

减少不必要的回传

我们都知道,从服务端到客户端的回传每次都是需要花费一定的时间的,而且加长了用户等待的时间。所以有些回传则是可免则免。

Server.Transfer Vs Response.Redirect

如果我们需要在服务端把用户定向到另外的一个页面,那么考虑一下:尽量使用Server.Transfer,而不是使用Response.Redirect

因为当使用Response.Redirect的时候,服务端会向客户端的浏览器发送一个响应:告诉浏览器去加载转向的那个页面。然后浏览器再次发送请求到服务端去请求另外的那个页面。

当我们使用Server.Transfer的时候,服务端就立刻执行跳转。这样做的一个不好的地方可能就是:此时请求的是A.aspx,其实服务端已经跳转到了B.aspx页面,但是浏览器上面的Url还是显示的A.aspx

当使用Server.Transfer需要注意:确定每次访问A页面都需要跳转到B页面的时候,就是用Server.Transfer。例如,拿博客园来举例,当用户在没有登录的时候想对正在阅读的一篇文章评论,那么此时,页面就会跳转到Login的登陆页面,登陆之后,页面就跳转到之前看文章的那个页面,然后写评论。此时的这个跳转就不适合用Server.Transfer,而采用Response.Redirect。如果不管用户在哪里,只要用户登陆,那么总是跳到一个固定的页面,那么就可以使用Server.Transfer

还有就是Server.Transfer毕竟会消耗服务端的资源,使用的时候要注意。

通过上面可以看出:调优本来就是一个折中的过程,不是绝对的。调优最后说到底就是时空转换时间换空间,空间换时间

声明站点的默认页面

当我们请求一个站点的时候,如http://domain/folderIIS会自动进行一些重定向到http://domain/folder/。同时,http.sys也不会把没有声明默认页面的站点的默认首页加入到内核的缓存中(可能说的有点的绕),例如,如果在程序中,我们设置站点的默认页面时Default.aspx,但是我们在部署到IIS的时候,没有配置Default.aspx就是站点的默认页面,那么这个页面的内容不会被http.sys缓存到内核中。所以为了避免IIS重定向和允许http.sys缓存页面,我们在IIS中要配置站点的默认页面(或者每次在浏览器中输入http://domain/folder/default.aspx,但是我们不能控制用户的行为,所以这招这几乎不可能)

永久跳转相关话题

如果我们站点的某个页面过期了或者不再用了,那么我们就可以采用301永久跳转。当服务端向客户端发出301响应的时候,浏览器和代理都会去更新他们的缓存(如果之前的旧页面采用了缓存),而且搜索引擎也会采用新的页面。

要让服务端向客户端发送301响应,如下的方式:

1.代码:

Response.StatusCode = 301;
Response.AddHeader(
Location, NewPage.aspx);
Response.End();

ASP.NET 4.0 及以后的版本:

Response.RedirectPermanent(NewPage.aspx);

2. IIS配置

a) IIS 6配置

1. IIS中站点中,选中你想跳转的文件或者目录。

2. 选中”A redirection to a URL”

3. 然后输入你想跳转到的页面。

4. 然后选中”The exact url entered above””A permanent redirect for this resource”

b) IIS 7

Server 2008上面

1. 打开开始”->”管理工具”->”服务器管理

2. IIS上面添加角色服务

3. 常见Http功能下面选中”Http重定向

4. 然后安装。

Win7 上面,如下:

然后,在我们的站点的web.config配置如下:

代码

<configuration>
<location path=”OldPage.aspx”>
<system.webServer>
<httpRedirect enabled=”true” destination=”NewPage.aspx” httpResponseStatus=”Permanent” />
</system.webServer>
</location>
</configuration>

今天就到这里,多谢各位!