- Adobe Flex 4 Help
- Adobe Flex 4 Beta 语言参考
- 学习 Flash Builder 4 和 Flex 4 SDK(包含文章和视频教程,内容如下)
- Learn about Flash Builder 4
- Video: Introducing Flash Builder 4
Discover new design-centric, data-centric, and developer productivity features in the beta. - Tutorial: What’s New in Flash Builder 4
Learn about the three main feature themes in this release. - Resources: Flex for ColdFusion developers Learning Center
Explore the new data-centric development features of Flash Builder 4 beta.
- Video: Introducing Flash Builder 4
- Learn about Flex 4 SDK
- Tutorial: What’s New in Flex 4 SDK
Learn what you’ll find as you explore the Flex 4 SDK beta. - Tutorial: Differences between Flex 3 SDK and Flex 4 SDK
Learn about the differences in architecture, components, layouts, use of states, and effects. - Tutorial: Effects in Adobe Flex 4 SDK ― Part 1: Basic Effects
See the basic infrastructure of the new effects in this release.
- Tutorial: What’s New in Flex 4 SDK
- Working with Flash Builder 4
- Video: Developer Productivity in Flash Builder 4
Presenter: David Zuckerman; Adobe - Video: Handling Large Recordsets in Flash Builder 4
Presenter: Tim Buntel; Adobe - Video: Debug and Test Code in Flash Builder 4
Presenter: David Zuckerman; Adobe - Video: Work with Flex 4 SDK in Flash Builder 4
Presenter: David Zuckerman; Adobe - Video: PHP Services in Flash Builder 4 (Part 1)
Presenter: Tom Lane; Adobe - Video: PHP Services in Flash Builder 4 (Part 2)
Presenter: Tom Lane; Adobe
- Video: Developer Productivity in Flash Builder 4
- Learn about Flash Builder 4
- Flash Builder、 Flex SDK以及Flash Catalyst视频教程(很多,进入看看吧)
- GotoAndLearn上的两个关于Flash Catalyst 和 Flex 4 的视频教程:教程一、教程二
- Adobe Flex 新特性和Flex 3迁移手册(PDF下载)
- Flash Builder 4 新特性繁体介绍(感謝 RiS社群 提供好資源)
- Kenshin的资料总汇
[PJBlog]PJBlog转WordPress
PJBlog转WordPress
PJBlog是个非常好的ASP单用户blog程序,用户非常之多。台湾还有好多呢。我曾经就是PJBLOG的用户。后来转到WP。几乎是新建了一个。这几天搜集了一下PJBLOG转WordPress程序。总结如下:
有两个方法。utom的和aw的。AW是在UTOM的基础上改写的。转换更为彻底。推荐使用。因为主要代码是抽取UTOM的。所以转换步骤跟UTOM的是一样的。特别注意,导入时用Linux系统,Win主机的朋友可以找Linux的朋友帮下忙,导入后再将数据转出。转换方法如下:
1。先下载AW的转换程序并解压;
2。打开文件编辑数据库的位置;
3。上传到PJBlog根目录;
4。运行此程序并保存内容到本地;
5。编辑内容删掉第一行。保存为UTF-8模式;
6。WP登陆后台->导入-> Movable Type。 并上传之前保存在本地的内容;
Note: 能导入日志及评论。非Linux的服务器导入会出现一些小问题。 请登陆wp相关论坛进行求解。 如能导入少量内容并出错请刷新当前页面;
以上转自:utombox 以下转自:AW’S blog
总结。PJBlog到Word Press容易出问题的关键细节:
一、时间月份问题。这个我的fix已经解决了。
二、导入时用Linux系统。具体原因不太清楚……在Win32下面强制刷新也可以。但我有洁癖。不希望出现FatalError(为此我花了N个小时装Ubuntu……)Linux跑Apache/PHP/MySQL就是牛逼
三、用户名不得含有中文。目前我没有什么好方法解决。应该跟WP的表构造有关。懒得仔细看了。
版权声明
作者:dupola
原文标题:PJBlog转WordPress
原文链接:http://dupola.com/post/60
(C) dupola 版权所有,转载时必须以链接形式注明作者和原始出处及本声明
[CSS]css文字重复ie bug导致文字的奇怪复制
当多个浮动的元素彼此跟随,中间加注释的时候,最后一个浮动元素内的文本偶尔会复制到最下面去。学名Duplicate Characters Bug
程序代码
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "//www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="//www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=gb2312" />
<title>多了一只猪</title>
</head>
<body>
<div style="width:400px">
<div style="float:left"></div>
<!– –>
<div style="float:right;width:400px">↓这就是多出来的那只猪</div>
</div>
</body>
</html>
可以通过以下的办法来解决:
1、不放置注释。最简单、最快捷的解决方法
2、注释不要放置于2个浮动的区块之间。
3、将文字区块包含在新的<div></div>之间,如:<div style="float:right;width:400px"><div>↓这就是多出来的那只猪</div>& lt;/div>。
4、去除文字区块的固定宽度,与3有相似之处。
5、有的人在猪后加一个<br />或者空格,但只是消除现象。
6、不要给浮动元素设置多宽度,使其不会到达包含元素的底部,或者对最后一个元素设置margin-right: -3px;或者更小。
7、注释可以这样写:<!–[if !IE]>Put your commentary in here…<![endif]–>
[Flex]Flex与.NET互操作(十二):基于FluorineFx.Net的及时通信应用(Rem
远程共享对象(Remote Shared Objects) 可以用来跟踪、存储、共享以及做多客户端的数据同步操作。只要共享对象上的数据发生了改变,将会把最新数据同步到所有连接到该共享对象 的应用程序客户端。FluorineFx所提供的远程共享对象(Remote Shared Objects)和FMS的共享对象的功能是一样,对于熟悉FMS开发的朋友来说,学习FluorineFx的远程共享对象是非常简单的。
共享对象可以在服务器端创建,也可以在客户端创建。在客户端创建共享对象的方法和使用FMS开发是一样的,创建一个 NetConnection对象,通过该对象的connect()方法连接到服务器,然后通过SharedObject.getRemote()方法就可 以在客户端创建一个远程共享对象。如下实例代码:
{
var nc:NetConnection = new NetConnection();
nc.connect("rtmp://localhost:1617/SOAPP","username","password")
nc.addEventListener(NetStatusEvent.NET_STATUS,onStatusHandler);
nc.client = this;
}
private function onStatusHandler(event:NetStatusEvent):void
{
if(event.info.code == "NetConnectin.Connect.Success")
{
createSharedObject();
}
}
private function createSharedObject():void
{
var so:SharedObject = SharedObject.getRemote("OnLineUsers",nc.uri,false);
so.addEventListener(SyncEvent.SYNC,onSyncHandler);
so.connect(this.nc);
so.client = this;
}
private function onSyncHandler(event:SyncEvent):void
{
//
..do other}
在FluorineFx的服务器端创建远程共享对象和FMS有很大的区别,FluorineFx的 ISharedObjectService接口提供了专门用于创建远程共享对象的方法 CreateSharedObject(),ApplicationAdapter实现了此接口方法。定义如下:
{
ISharedObjectService service = (ISharedObjectService)ScopeUtils.GetScopeService(scope, typeof(ISharedObjectService));
return service.CreateSharedObject(scope, name, persistent);
}
如果要在服务器端创建远程共享对象,直接调用ApplicationAdapter类中的CreateSharedObject()方法就可以。如下在FluorineFx服务器端创建远程共享对象的代码块:
if (users_so == null)
{
//创建共享对象
CreateSharedObject(connection.Scope, "OnLineUsers", false);
users_so = GetSharedObject(connection.Scope, "OnLineUsers");
}
要想更新共享对象里的数据客户端还是使用setProperty()方法,而FluorineFx的服务器更新共享对象的方法则与 FMS不一样,使用的是FluorineFx.Messaging.Api.IAttributeStore接口提供的SetAttribute()和 RemoveAttribute()方法来更新共享对象里的数据。
陆续介绍了这么多,下面通过一个案例来看看该这么去应用远程共享对象。比如做IM、视频聊天、视频会议等及时通信类型的应用中,用户上线下线的频率非常高,这时候我们就可以使用远程共享对象去做在线用户的数据同步。
首先建立FluorineFx服务库,并建立一个应用类继承于ApplicationAdapter,通过重写ApplicationAdapter的相关方法来实现应用程序的不同需求,详细如下代码块:
using System.Collections.Generic;
using System.Text;
using FluorineFx.Messaging.Adapter;
using FluorineFx;
using FluorineFx.Messaging.Api;
using System.Diagnostics;
using FluorineFx.Messaging.Api.SO;
using FluorineFx.Exceptions;
using FluorineFx.Context;
using FluorineFx.Messaging.Api.Service;
using System.Collections;
using Fx.Adapter.DTO;
namespace Fx.Adapter
{
/// <summary>
/// 自定义ApplicationAdapter
/// </summary>
[RemotingService]
public class MyApp : ApplicationAdapter
{
/// <summary>
/// 应用程序启动
/// </summary>
/// <param name="application"></param>
/// <returns></returns>
public override bool AppStart(IScope application)
{
Trace.WriteLine("应用程序启动");
return true;
}
/// <summary>
/// 房间启动
/// </summary>
/// <param name="room"></param>
/// <returns></returns>
public override bool RoomStart(IScope room)
{
Trace.WriteLine("房间启动");
if (!base.RoomStart(room))
return false;
return true;
}
/// <summary>
/// 接收客户端的连接
/// </summary>
/// <param name="connection"></param>
/// <param name="parameters"></param>
/// <returns></returns>
public override bool AppConnect(IConnection connection, object[] parameters)
{
string userName = parameters[0] as string;
string password = parameters[1] as string;
if (password == null || password == string.Empty)
throw new ClientRejectedException(null);
connection.Client.SetAttribute("userName", userName);
//获取共享对象(OnLineUsers)
ISharedObject users_so = GetSharedObject(connection.Scope, "OnLineUsers");
if (users_so == null)
{
//创建共享对象
CreateSharedObject(connection.Scope, "OnLineUsers", false);
users_so = GetSharedObject(connection.Scope, "OnLineUsers");
}
//更新共享对象
users_so.SetAttribute(userName, userName);
return true;
}
/// <summary>
/// 加入房间
/// </summary>
/// <param name="client"></param>
/// <param name="room"></param>
/// <returns></returns>
public override bool RoomJoin(IClient client, IScope room)
{
Trace.WriteLine("加入房间 " + room.Name);
return true;
}
/// <summary>
/// 离开房间
/// </summary>
/// <param name="client"></param>
/// <param name="room"></param>
public override void RoomLeave(IClient client, IScope room)
{
Trace.WriteLine("离开房间 " + room.Name);
base.RoomLeave(client, room);
}
/// <summary>
/// 用户退出
/// </summary>
/// <param name="connection"></param>
public override void AppDisconnect(IConnection connection)
{
string userName = connection.Client.GetAttribute("userName") as string;
ISharedObject users_so = GetSharedObject(connection.Scope, "OnLineUsers");
if (users_so != null)
{
//从共享对象中移除当前退出系统用户
users_so.RemoveAttribute(userName);
}
base.AppDisconnect(connection);
}
}
}
开发好了ApplicationAdapter,还需要对此ApplicationAdapter进行通信配置,在FluorineFx的应用程序目录中添加app.config并进行如下配置:
<configuration>
<application-handler type="Fx.Adapter.MyApp"/>
</configuration>
另外还需要配置一个客户端方法的通信通道,通过FluorineFx网站下的WEB-INF/flex/service-config.xml配置:
<?xml version="1.0" encoding="utf-8" ?>
<services-config>
<channels>
<channel-definition id="my-rtmp" class="mx.messaging.channels.RTMPChannel">
<endpoint uri="rtmp://{server.name}:1617" class="flex.messaging.endpoints.RTMPEndpoint"/>
</channel-definition>
</channels>
</services-config>
如上便完成了服务器端的开发,在flash/felx客户端通过NetConnection去连接应用,并根据当前的连接去连接服务器端的远程共享对象,最后通过异步事件来实现数据同步更新。如下程序运行截图:
此时开多个浏览器窗口测试,不同窗口使用不同的用户名登录,可以很清楚的看到,我们已经实现了在线用户的数据同步功能,可以及时的反映用户上线离线,可以及时的同步在线用户列表的数据。
另外远程共享对象还有一个功能非常强大的特性方法,就是连接到共享对象的客户端之间可以直接广播消息(客户端调用客户端的方法)。就以 上面在线用户的案例为例,用户成功登陆服务器我需要广播一条消息,用户退出了我也需要广播一条消息,要实现这个功能就需要通过远程共享的客户端呼叫 (send()方法)来实现,如下代码块:
{
so.send("onSayMessage",message);
}
远程共享对象的send()方法调用了onSayMessage这个客户端方法来实现对连接到共享对象上的所有客户端广播消息,那么我们的在定义一个onSayMessage方法,如下:
/**
* 接受客户端呼叫—此方法必须是public修饰
*/
public function onSayMessage(message:Object):void
{
traceWriteln(message.toString());
}
private function traceWriteln(param:String):void
{
txtTraceArea.htmlText += param + "\n";
txtTraceArea.validateNow();
txtTraceArea.verticalScrollPosition = txtTraceArea.maxVerticalScrollPosition;
}

如果想实现用户退出广播,可以通过服务器端RPC的方法调用客户端的方法来实现,关于RPC请查看《Flex与.NET互操作(十一):基于FluorineFx.Net的及时通信应用(Remote Procedure Call)(二) 》有详细介绍。下面是Flex客户端的完整代码:
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute"
width="530" height="378" backgroundGradientAlphas="[1.0, 1.0]"
backgroundGradientColors="[#000000, #686868]" fontSize="12">
<mx:Script>
<![CDATA[
import mx.controls.Alert;
import dotnet.fluorinefx.VO.UserInfo;
private var nc:NetConnection;
private var so:SharedObject;
private var info:UserInfo;
private function connectionServer(event:MouseEvent):void
{
info = new UserInfo();
info.UserName = this.txtUserName.text;
info.Password = this.txtPassword.text;
nc = new NetConnection();
nc.connect("rtmp://localhost:1617/SOAPP",info.UserName,info.Password);
nc.addEventListener(NetStatusEvent.NET_STATUS,onStatusHandler);
nc.client = this;
this.txtUserName.text="";
this.txtPassword.text="";
this.txtUserName.setFocus();
}
private function onStatusHandler(event:NetStatusEvent):void
{
this.connStatus.text = "连接状态:" + event.info.code;
if(event.info.code == "NetConnection.Connect.Success")
{
//连接远程共享对象
so = SharedObject.getRemote("OnLineUsers",nc.uri,false);
if(so)
{
so.addEventListener(SyncEvent.SYNC,onSyncHandler);
so.connect(nc);
so.client = this;
}
onCallClient("用户【 <font color=\"#4100b9\">"+info.UserName+"</font>】登陆了系统!");
}
}
private function onSyncHandler(event:SyncEvent):void
{
var temp:Array = new Array();
for(var u:String in so.data)
{
//traceWriteln("异步事件->共享对象:" + u + ":" + so.data[u]);
temp.push(so.data[u]);
}
this.userList.dataProvider = temp;
}
private function traceWriteln(param:String):void
{
txtTraceArea.htmlText += param + "\n";
txtTraceArea.validateNow();
txtTraceArea.verticalScrollPosition = txtTraceArea.maxVerticalScrollPosition;
}
private function onCallClient(message:String):void
{
so.send("onSayMessage",message);
}
/**
* 接受客户端呼叫
*/
public function onSayMessage(message:Object):void
{
traceWriteln(message.toString());
}
]]>
</mx:Script>
<mx:Label x="24" y="134" id="connStatus" width="288" color="#FFFFFF"/>
<mx:List x="342" y="10" height="347" width="160" id="userList" >
</mx:List>
<mx:Form x="24" y="10" width="236">
<mx:FormItem label="用户名:" color="#FFFFFF">
<mx:TextInput id="txtUserName" width="130" color="#000000"/>
</mx:FormItem>
<mx:FormItem label="密 码:" color="#FFFFFF">
<mx:TextInput id="txtPassword" width="130"
color="#000000" displayAsPassword="true"/>
</mx:FormItem>
<mx:FormItem label="">
<mx:Button label="登陆服务器" click="connectionServer(event)"
enabled="{this.txtUserName.text.length>0?true:false}" color="#FFFFFF"/>
</mx:FormItem>
</mx:Form>
<mx:TextArea x="24" y="174" width="288" height="153" alpha="1.0"
backgroundColor="#F2D2D2" backgroundAlpha="0.26" color="#FFFFFF"
id="txtTraceArea" borderColor="#FFFFFF"/>
</mx:Application>
[手机开发]Windows Mobile 6.5 开发者工具包
在一段时间的等待之后,Microsoft终于在其下载中心开放了《Windows Mobile 6.5 开发者工具包》的下载。这对于广大Windows Mobile爱好者来说,无疑是一个好消息,就当是一个迟到的儿童节礼物吧。
Windows Mobile 6.5 Developer Tool Kit包括的组件:
l 说明文档
l 示例代码
l 头文件和库文件
l 模拟器镜像
l Visual Studio中创建Windows Mobile 6.5应用的工具
Windows Mobile 6.5 Developer Tool Kit包含Professional版本和Standard版本。目前,模拟器镜像的语言有6种,包括CHS(简体中文)、USA(英语)、GER(德语)、FRA(法语)、ITA(意大利语)和ESN(西班牙语)。Windows Mobile 6.5 Developer Tool Kit包括的模拟器镜像种类(主要是平台和分辨率的区别,和安装的Professional版本和Standard版本相关):
l Windows Mobile 6.5 Professional Square Emulator
l Windows Mobile 6.5 Professional QVGA Emulator
l Windows Mobile 6.5 Professional WQVGA Emulator
l Windows Mobile 6.5 Professional VGA Emulator
l Windows Mobile 6.5 Professional WVGA Emulator
l Windows Mobile 6.5 Standard Square Emulator
l Windows Mobile 6.5 Standard QVGA Emulator
对于广大开发人员来说,最具吸引力的应该就是其中的Windows Mobile 6.5 Gesture API,这可以在开发包中找到相关的例程,在”%"Windows Mobile 6.5 Developer Tool Kit"Samples" folder”目录下。当然,这个Gesture API也只是支持Windows Mobile Classic 和 Professional平台,因为大家知道,Classic是不支持触摸的。
我下载了一个中文的Windows Mobile 6.5 Developer Professional Tool Kit,尝试使用了一下Windows Mobile 6.5 Professional Emulator,截了一些图片上来。
第一次启动模拟器的时候,占用的资源还不小,如下图1所示:
图1:启动模拟器占用的资源
开始菜单,的确和我现在在使用的6.0有比较大的区别:
图2:开始菜单
Myphone和Marketplace应用:
图3:Myphone和Marketplace应用
Home界面:
图4:Home界面
设备信息:
图5:设备信息界面
相关链接:
下载地址:Windows Mobile 6.5 开发者工具包
[Lucene]多个TermQuery或一个MultiFieldQueryParser多条件查询
import java.io.IOException;
import org.apache.lucene.analysis.standard.StandardAnalyzer;
import org.apache.lucene.document.Document;
import org.apache.lucene.document.Field;
import org.apache.lucene.index.CorruptIndexException;
import org.apache.lucene.index.IndexWriter;
import org.apache.lucene.index.Term;
import org.apache.lucene.queryParser.MultiFieldQueryParser;
import org.apache.lucene.search.BooleanClause;
import org.apache.lucene.search.BooleanQuery;
import org.apache.lucene.search.Hits;
import org.apache.lucene.search.IndexSearcher;
import org.apache.lucene.search.Query;
import org.apache.lucene.search.TermQuery;
/**
* 与或非布尔查询——Lucene中的BooleanQuery
* @author USER
*
*/
public class TestBooleanQuery {
/**
* 主函数,运行测试程序
* @param args
* @throws Exception
*/
public static void main(String[] args) throws Exception {
//建索引
createIndex();
//多个TermQuery构建BooleanQuery检索
searchIndex4TermQuery();
//一个MultiFieldQueryParser构建BooleanQuery多个域的检索
searchIndex4MultiFieldQueryParser();
}
/**
* 建索引
* @throws Exception
*/
public static void createIndex() throws Exception {
Document doc1 = new Document();
Field field = null;
field = new Field("name", "word1 word2 word3", Field.Store.YES,
Field.Index.TOKENIZED);
doc1.add(field);
field = new Field("title", "doc1", Field.Store.YES, Field.Index.TOKENIZED);
doc1.add(field);
Document doc2 = new Document();
field = new Field("name", "word4 word5", Field.Store.YES,
Field.Index.TOKENIZED);
doc2.add(field);
field = new Field("title", "doc2", Field.Store.YES, Field.Index.TOKENIZED);
doc2.add(field);
Document doc3 = new Document();
field = new Field("name", "word1 word2 word6", Field.Store.YES,
Field.Index.TOKENIZED);
doc3.add(field);
field = new Field("title", "doc3", Field.Store.YES, Field.Index.TOKENIZED);
doc3.add(field);
/**
* 为测试MultiFieldQueryParser而添加的文档
*/
Document doc4 = new Document();
field = new Field("name", "word1 word2 word3", Field.Store.YES,
Field.Index.TOKENIZED);
doc4.add(field);
field = new Field("title", "doc1 word1", Field.Store.YES, Field.Index.TOKENIZED);
doc4.add(field);
/**
* 对MultiFieldQueryParser更深理解
*/
Document doc5 = new Document();
field = new Field("title", "北京2008年奥运会", Field.Store.YES,
Field.Index.TOKENIZED);
doc5.add(field);
field = new Field("name", "这是一届创造奇迹、超越梦想的…….", Field.Store.YES, Field.Index.TOKENIZED);
doc5.add(field);
Document doc6 = new Document();
field = new Field("title", "北京2008年奥运会", Field.Store.YES,
Field.Index.TOKENIZED);
doc6.add(field);
field = new Field("name", "这是一届创造奇迹、超越梦想的奥运会…….", Field.Store.YES, Field.Index.TOKENIZED);
doc6.add(field);
IndexWriter writer = new IndexWriter("e:\\java\\index",
new StandardAnalyzer(), true);
writer.addDocument(doc1);
writer.addDocument(doc2);
writer.addDocument(doc3);
writer.addDocument(doc4);
writer.addDocument(doc5);
writer.addDocument(doc6);
writer.close();
}
/**
* 由TermQuery和BooleanQuery构建的多个域检索
* @throws Exception
*/
public static void searchIndex4TermQuery() throws Exception{
TermQuery query1 = null;
TermQuery query2 = null;
TermQuery query3 = null;
TermQuery query4 = null;
TermQuery query5 = null;
TermQuery query6 = null;
BooleanQuery bquerymain = null;
BooleanQuery bquery1 = null;
BooleanQuery bquery2 = null;
BooleanQuery bquery3 = null;
Hits hits = null;
IndexSearcher searcher = new IndexSearcher("e:\\java\\index");
query1 = new TermQuery(new Term("name", "word1"));
query2 = new TermQuery(new Term("name", "word2"));
query3 = new TermQuery(new Term("name", "word3"));
query4 = new TermQuery(new Term("name", "word4"));
query5 = new TermQuery(new Term("name", "word5"));
query6 = new TermQuery(new Term("name", "word6"));
// 构造布尔查询(可根据你的要求随意组合)
bquerymain = new BooleanQuery();
bquery1 = new BooleanQuery();
bquery2 = new BooleanQuery();
bquery3 = new BooleanQuery();
bquery1.add(query1, BooleanClause.Occur.MUST);
bquery1.add(query3, BooleanClause.Occur.MUST);
bquery2.add(query3, BooleanClause.Occur.MUST);
bquery2.add(query4, BooleanClause.Occur.MUST);
bquery3.add(query5, BooleanClause.Occur.MUST);
bquery3.add(query6, BooleanClause.Occur.MUST_NOT);
bquerymain.add(bquery1, BooleanClause.Occur.SHOULD);
bquerymain.add(bquery2, BooleanClause.Occur.SHOULD);
bquerymain.add(bquery3, BooleanClause.Occur.MUST);
/**
* 根据你的要求建一个BooleanQuery对象,然后来查询
*/
hits = searcher.search(bquery3);
printResult(hits, bquery1.toString());
}
/**
* 由MultiFieldQueryParser和BooleanQuery构建的多个域检索
* @throws Exception
*/
public static void searchIndex4MultiFieldQueryParser() throws Exception{
Hits hits = null;
IndexSearcher searcher = new IndexSearcher("e:\\java\\index");
// 构造布尔查询(可根据你的要求随意组合)
BooleanClause.Occur[] flags = new BooleanClause.Occur[] {
BooleanClause.Occur.MUST, BooleanClause.Occur.MUST};
Query query = MultiFieldQueryParser.parse("word1", new String[] {
"name", "title"}, flags, new StandardAnalyzer());
/* //加深对MultiFieldQueryParser的理解(注意看建索引的文档doc5,doc6与检索后的结果)
Query query = MultiFieldQueryParser.parse("北京 奥运会", new String[] {
"name", "title"}, flags, new StandardAnalyzer()); */
hits = searcher.search(query);
printResult(hits, query.toString());
}
/**
* 打印输出检索出的文档,并输出检索的布尔语句
* @param hits
* @param key
* @throws Exception
*/
public static void printResult(Hits hits, String key) throws Exception {
System.out.println("查询 " + key);
if (hits != null) {
if (hits.length() == 0) {
System.out.println("没有找到任何结果");
} else {
System.out.println("找到" + hits.length() + "个结果");
for (int i = 0; i < hits.length(); i++) {
Document d = hits.doc(i);
String dname = d.get("title");
System.out.print(dname + " ");
}
System.out.println();
System.out.println();
}
}
}
}
[Lucene]Lucene 学习笔记(二)——搜索方式(一)
Lucene有多种搜索方式,可以根据需要选择不同的方式。
1、词条搜索(单个关键字查找)
主要对象是TermQuery
调用方式如下:
Term term=new Term(字段名,搜索关键字);
Query query=new TermQuery(term);
Hits hits=searcher.search(query);
2、组合搜索(允许多个关键字组合搜索)
主要对象是BooleanQuery
调用方式如下:
Term term1=new Term(字段名,搜索关键字);
TermQuery query1=new TermQuery(term1);
Term term2=new Term(字段名,搜索关键字);
TermQuery query2=new TermQuery(term2);
BooleanQuery booleanquery=new BooleanQuery();
booleanquery.add(query1,参数)
booleanquery.add(query2,参数)
Hits hits=searcher.search(booleanquery);
此方法中的核心在BooleanQuery的add方法上,其第二个参数有三个可选值,对应着逻辑上的与或非关系
参数如下:
BooleanClause.Occur.MUST 必须包含,类似于逻辑运算的与
BooleanClause.Occur.MUST _NOT 必须不包含,类似于逻辑运算的非
BooleanClause.Occur.SHOULD 可以包含,类似于逻辑运算的或
这三者组合,妙用无穷。
3、范围搜索(允许搜索指定范围内的关键字结果)
主要对象是RangeQuery
调用方式如下:
Term term1=new Term(字段名,起始值);
Term term2=new Term(字段名,终止值);
RangeQuery rangequery=new RangeQuery(term1,term2,参数);
Hits hits=searcher.search(rangequery);
此方法中的参数是Boolean类型的,表示是否包含边界 。
true 包含边界
false不包含边界
4、前缀搜索(搜索起始位置符合要求的结果)
主要对象是PrefixQuery
调用方式如下:
Term term=new Term(字段名,搜索关键字);
PrefixQuery prefixquery=new PrefixQuery (term);
Hits hits=searcher.search(prefixquery);
5、短语搜索(根据零碎的短语组合成新的词组进行搜索)
主要对象是PhraseQuery
调用方式如下:
Term term1=new Term(字段名,搜索关键字);
Term term2=new Term(字段名,搜索关键字);
PhraseQuery phrasequery=new PhraseQuery();
phrasequery.setSlop(参数);
phrasequery.add(term1);
phrasequery.add(term2);
Hits hits=searcher.search(phrasequery);
其中setSlop的参数是设置两个关键字之间允许间隔的最大值。
[ASP.NET]Web开发学习心得7——MasterPage的实现原理
MasterPage是ASP.NET2.0引入的一个非常实用的特性,怎么用,我想不用我说,基本上大家都会,这里要讲的是,它是如何实现的。
在深入源代码去探索MasterPage之前,我以为MasterPage的实现应该是比较复杂的,也一直纳闷为什么 MasterPage类会继承于UserControl类,感觉这两者好像差得很远。昨天晚上,我专门抽出时间,阅读了部分与MasterPage有关的 源代码,终于明白了是怎么回事,在那突然明白的那一刻,真有如醍醐灌顶,拍案叫绝,不得不佩服微软的那些guys。
下面就是我的探索之旅的过程(大家也可以跳过该部分,直接看后面的真相大白部分):
1、我首先查看的是Page.ProcessRequestMain方法,我们知道,Page类大部分特性,包括LifeCycle、 PostBack、ProcessPostData等都是在该方法中实现,所以,我想,MasterPage的实现肯定在该方法中有不少的体现。然而,令 我惊讶的是,我居然没有在该方法中找到任何有关MasterPage的线索,而仅仅在this.PerformPreInit()中,找到唯一一个 ApplyMasterPage()方法。而该方法也出奇的简单,感觉仅仅是将递归的各级MasterPage的._masterPageApplied 字段设为true而已。当时,我忽略了一个重要的东西,就是代码中对this.Master这个属性的访问,实际上,奥秘就在对这个属性的访问上(下文将 叙述)。
private void PerformPreInit()
{
this.OnPreInit(EventArgs.Empty);
this.InitializeThemes();
this.ApplyMasterPage();
this._preInitWorkComplete = true;
}
private void ApplyMasterPage()
{
if (this.Master != null)
{
ArrayList appliedMasterFilePaths = new ArrayList();
appliedMasterFilePaths.Add(this._masterPageFile.VirtualPathString.ToLower(CultureInfo.InvariantCulture));
MasterPage.ApplyMasterRecursive(this.Master, appliedMasterFilePaths);
}
}
2、我查看了MasterPage的源代码,出奇的是,竟也如此简单,以至于我也没有从该源代码中找到多少有价值的信息。
[ControlBuilder(typeof(MasterPageControlBuilder)), Designer("Microsoft.VisualStudio.Web.WebForms.MasterPageWebFormDesigner, Microsoft.VisualStudio.Web, Version=8.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a", typeof(IRootDesigner)), ParseChildren(false), AspNetHostingPermission(SecurityAction.LinkDemand, Level=AspNetHostingPermissionLevel.Minimal), AspNetHostingPermission(SecurityAction.InheritanceDemand, Level=AspNetHostingPermissionLevel.Minimal)]
public class MasterPage : UserControl
{
private IList _contentPlaceHolders;
private IDictionary _contentTemplateCollection;
private IDictionary _contentTemplates;
private MasterPage _master;
private bool _masterPageApplied;
private VirtualPath _masterPageFile;
internal TemplateControl _ownerControl;
[EditorBrowsable(EditorBrowsableState.Advanced)]
protected internal void AddContentTemplate(string templateName, ITemplate template)
{
if (this._contentTemplateCollection == null)
{
this._contentTemplateCollection = new Hashtable(10, StringComparer.OrdinalIgnoreCase);
}
try
{
this._contentTemplateCollection.Add(templateName, template);
}
catch (ArgumentException)
{
throw new HttpException(SR.GetString("MasterPage_Multiple_content", new object[] { templateName }));
}
}
internal static void ApplyMasterRecursive(MasterPage master, IList appliedMasterFilePaths)
{
if (master.Master != null)
{
string str = master._masterPageFile.VirtualPathString.ToLower(CultureInfo.InvariantCulture);
if (appliedMasterFilePaths.Contains(str))
{
throw new InvalidOperationException(SR.GetString("MasterPage_Circular_Master_Not_Allowed", new object[] { master._masterPageFile }));
}
appliedMasterFilePaths.Add(str);
ApplyMasterRecursive(master.Master, appliedMasterFilePaths);
}
master._masterPageApplied = true;
}
internal static MasterPage CreateMaster(TemplateControl owner, HttpContext context, VirtualPath masterPageFile, IDictionary contentTemplateCollection)
{
MasterPage child = null;
if (masterPageFile == null)
{
if ((contentTemplateCollection != null) && (contentTemplateCollection.Count > 0))
{
throw new HttpException(SR.GetString("Content_only_allowed_in_content_page"));
}
return null;
}
VirtualPath virtualPath = VirtualPathProvider.CombineVirtualPathsInternal(owner.TemplateControlVirtualPath, masterPageFile);
ITypedWebObjectFactory vPathBuildResult = (ITypedWebObjectFactory) BuildManager.GetVPathBuildResult(context, virtualPath);
if (!typeof(MasterPage).IsAssignableFrom(vPathBuildResult.InstantiatedType))
{
throw new HttpException(SR.GetString("Invalid_master_base", new object[] { masterPageFile }));
}
child = (MasterPage) vPathBuildResult.CreateInstance();
child.TemplateControlVirtualPath = virtualPath;
if (owner.HasControls())
{
foreach (Control control in owner.Controls)
{
LiteralControl control2 = control as LiteralControl;
if ((control2 == null) || (Util.FirstNonWhiteSpaceIndex(control2.Text) >= 0))
{
throw new HttpException(SR.GetString("Content_allowed_in_top_level_only"));
}
}
owner.Controls.Clear();
}
if (owner.Controls.IsReadOnly)
{
throw new HttpException(SR.GetString("MasterPage_Cannot_ApplyTo_ReadOnly_Collection"));
}
if (contentTemplateCollection != null)
{
foreach (string str in contentTemplateCollection.Keys)
{
if (!child.ContentPlaceHolders.Contains(str.ToLower(CultureInfo.InvariantCulture)))
{
throw new HttpException(SR.GetString("MasterPage_doesnt_have_contentplaceholder", new object[] { str, masterPageFile }));
}
}
child._contentTemplates = contentTemplateCollection;
}
child._ownerControl = owner;
child.InitializeAsUserControl(owner.Page);
owner.Controls.Add(child);
return child;
}
[Browsable(false), EditorBrowsable(EditorBrowsableState.Advanced)]
protected internal IList ContentPlaceHolders
{
get
{
if (this._contentPlaceHolders == null)
{
this._contentPlaceHolders = new ArrayList();
}
return this._contentPlaceHolders;
}
}
[Browsable(false), EditorBrowsable(EditorBrowsableState.Advanced)]
protected internal IDictionary ContentTemplates
{
get
{
return this._contentTemplates;
}
}
[WebSysDescription("MasterPage_MasterPage"), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), Browsable(false)]
public MasterPage Master
{
get
{
if ((this._master == null) && !this._masterPageApplied)
{
this._master = CreateMaster(this, this.Context, this._masterPageFile, this._contentTemplateCollection);
}
return this._master;
}
}
[WebSysDescription("MasterPage_MasterPageFile"), WebCategory("Behavior"), DefaultValue("")]
public string MasterPageFile
{
get
{
return VirtualPath.GetVirtualPathString(this._masterPageFile);
}
set
{
if (this._masterPageApplied)
{
throw new InvalidOperationException(SR.GetString("PropertySetBeforePageEvent", new object[] { "MasterPageFile", "Page_PreInit" }));
}
if (value != VirtualPath.GetVirtualPathString(this._masterPageFile))
{
this._masterPageFile = VirtualPath.CreateAllowNull(value);
if ((this._master != null) && this.Controls.Contains(this._master))
{
this.Controls.Remove(this._master);
}
this._master = null;
}
}
}
}
3、我又查看了ContentPlaceHolder类、Content类,心想,难道奥秘在这两个控件上?打开源代码一看,彻底晕 倒,这两个类简单得简直不能让人相信,ContentPlaceHolder居然是一个空类,仅仅起到一个标识的作用,而Content居然也仅仅只有 ContentPlaceHolderID唯一一个string属性。
public class ContentPlaceHolder : Control, INonBindingContainer, INamingContainer
{
}
4、此时,我几乎已经没法再从ASP.NET源代码中找到其他有关MasterPage的有价值的信息了。于是,我决定写一个简单的 Web应用程序,该应用程序仅仅只有一个MasterPage页与Default页,并将其编译,查看编译后的代码,看看是否能找到有价值的信息。
源代码如下:
<%@ Master Language="C#" AutoEventWireup="true" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title></title>
</head>
<body>
<form id="form1" runat="server">
<div>
<asp:ContentPlaceHolder id="ContentPlaceHolder" runat="server">
<asp:Button runat="server" ID="master" Text="master"/>
</asp:ContentPlaceHolder>
</div>
</form>
</body>
</html>
<%@ Page Title="" Language="C#" MasterPageFile="~/MasterPage.master" AutoEventWireup="true" %>
<%@ MasterType VirtualPath="~/MasterPage.master" %>
<asp:Content ID="Content" ContentPlaceHolderID="ContentPlaceHolder" Runat="Server">
<asp:Button runat="server" ID="default" Text="default" />
</asp:Content>
编译后代码如下:
[CompilerGlobalScope]
public class masterpage_master : MasterPage
{
private static bool __initialized;
private ITemplate __Template_ContentPlaceHolder;
protected ContentPlaceHolder ContentPlaceHolder;
protected HtmlForm form1;
protected Button master;
[DebuggerNonUserCode]
public masterpage_master()
{
base.AppRelativeVirtualPath = "~/MasterPage.master";
if (!__initialized)
{
__initialized = true;
}
base.ContentPlaceHolders.Add("contentplaceholder");
}
[DebuggerNonUserCode]
private ContentPlaceHolder __BuildControlContentPlaceHolder()
{
ContentPlaceHolder container = new ContentPlaceHolder();
this.ContentPlaceHolder = container;
container.ID = "ContentPlaceHolder";
if (base.ContentTemplates != null)
{
this.__Template_ContentPlaceHolder = (ITemplate) base.ContentTemplates["ContentPlaceHolder"];
}
if (this.__Template_ContentPlaceHolder != null)
{
this.__Template_ContentPlaceHolder.InstantiateIn(container);
return container;
}
IParserAccessor accessor = container;
accessor.AddParsedSubObject(new LiteralControl("\r\n "));
Button button = this.__BuildControlmaster();
accessor.AddParsedSubObject(button);
accessor.AddParsedSubObject(new LiteralControl("\r\n "));
return container;
}
[DebuggerNonUserCode]
private HtmlForm __BuildControlform1()
{
HtmlForm form = new HtmlForm();
this.form1 = form;
form.ID = "form1";
IParserAccessor accessor = form;
accessor.AddParsedSubObject(new LiteralControl("\r\n <div>\r\n "));
ContentPlaceHolder holder = this.__BuildControlContentPlaceHolder();
accessor.AddParsedSubObject(holder);
accessor.AddParsedSubObject(new LiteralControl("\r\n </div>\r\n "));
return form;
}
[DebuggerNonUserCode]
private Button __BuildControlmaster()
{
Button button = new Button();
this.master = button;
button.ApplyStyleSheetSkin(this.Page);
button.ID = "master";
button.Text = "master";
return button;
}
[DebuggerNonUserCode]
private void __BuildControlTree(masterpage_master __ctrl)
{
IParserAccessor accessor = __ctrl;
accessor.AddParsedSubObject(new LiteralControl("\r\n\r\n<!DOCTYPE html PUBLIC \"–//W3C//DTD XHTML 1.0 Transitional//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\">\r\n\r\n<html xmlns=\"http://www.w3.org/1999/xhtml\">\r\n<head>\r\n <title></title>\r\n</head>\r\n<body>\r\n "));
HtmlForm form = this.__BuildControlform1();
accessor.AddParsedSubObject(form);
accessor.AddParsedSubObject(new LiteralControl("\r\n</body>\r\n</html>\r\n"));
}
[DebuggerNonUserCode]
protected override void FrameworkInitialize()
{
base.FrameworkInitialize();
this.__BuildControlTree(this);
}
protected HttpApplication ApplicationInstance
{
get
{
return this.Context.ApplicationInstance;
}
}
protected DefaultProfile Profile
{
get
{
return (DefaultProfile) this.Context.Profile;
}
}
protected override bool SupportAutoEvents
{
get
{
return false;
}
}
[TemplateContainer(typeof(MasterPage)), TemplateInstance(TemplateInstance.Single)]
public virtual ITemplate Template_ContentPlaceHolder
{
get
{
return this.__Template_ContentPlaceHolder;
}
set
{
this.__Template_ContentPlaceHolder = value;
}
}
}
[CompilerGlobalScope]
public class default_aspx : Page, IRequiresSessionState, IHttpHandler
{
private static object __fileDependencies;
private static bool __initialized;
protected Button @default;
[DebuggerNonUserCode]
public default_aspx()
{
base.AppRelativeVirtualPath = "~/Default.aspx";
if (!__initialized)
{
string[] virtualFileDependencies = new string[] { "~/Default.aspx", "~/MasterPage.master" };
__fileDependencies = base.GetWrappedFileDependencies(virtualFileDependencies);
__initialized = true;
}
base.Server.ScriptTimeout = 0x1c9c380;
}
[DebuggerNonUserCode]
private void __BuildControlContent(Control __ctrl)
{
IParserAccessor accessor = __ctrl;
accessor.AddParsedSubObject(new LiteralControl("\r\n "));
Button button = this.__BuildControldefault();
accessor.AddParsedSubObject(button);
accessor.AddParsedSubObject(new LiteralControl("\r\n"));
}
[DebuggerNonUserCode]
private Button __BuildControldefault()
{
Button button = new Button();
this.@default = button;
button.TemplateControl = this;
button.ApplyStyleSheetSkin(this);
button.ID = "default";
button.Text = "default";
return button;
}
[DebuggerNonUserCode]
private void __BuildControlTree(default_aspx __ctrl)
{
__ctrl.Title = "";
__ctrl.MasterPageFile = "~/MasterPage.master";
this.InitializeCulture();
base.AddContentTemplate("ContentPlaceHolder", new CompiledTemplateBuilder(new BuildTemplateMethod(this.__BuildControlContent)));
IParserAccessor accessor = __ctrl;
accessor.AddParsedSubObject(new LiteralControl("\r\n\r\n"));
}
[DebuggerNonUserCode]
protected override void FrameworkInitialize()
{
base.FrameworkInitialize();
this.__BuildControlTree(this);
base.AddWrappedFileDependencies(__fileDependencies);
base.Request.ValidateInput();
}
[DebuggerNonUserCode]
public override int GetTypeHashCode()
{
return –2002306427;
}
[DebuggerNonUserCode]
public override void ProcessRequest(HttpContext context)
{
base.ProcessRequest(context);
}
protected HttpApplication ApplicationInstance
{
get
{
return this.Context.ApplicationInstance;
}
}
public masterpage_master Master
{
get
{
return (masterpage_master) base.Master;
}
}
protected DefaultProfile Profile
{
get
{
return (DefaultProfile) this.Context.Profile;
}
}
protected override bool SupportAutoEvents
{
get
{
return false;
}
}
}
我们首先观察default_aspx的BuildControlTree方法,该方法调用了 base.AddContentTemplate("ContentPlaceHolder", new CompiledTemplateBuilder(new BuildTemplateMethod(this.__BuildControlContent)));而该方法实质上只有一行代码,即 this._contentTemplateCollection.Add(templateName, template);其中,_contentTemplateCollection是IDictionary类型。因 此,base.AddContentTemplate只有一个功能,即在Page的_contentTemplateCollection中添加一个 CompliedTemplateBuilder类型的对象。那么该对象有啥作用呢?为了不岔开话题,这里不对该对象做详细描述,只给出结论:该对象实现 ITemplate接口,其接口方法InstantiateIn(Control container)具体实现为为container添加BuildTemplateMethod委托所创建的控件。这话有点拗口,简单地就刚才那个例子 来说,就是如果你调用该对象的InstantiateIn(Control container)方法,就为该container添加this.__BuildControlContent()方法所创建的控件做为子控件。
5、我们继续来看看masterpage_master的__BuildControlContentPlaceHolder()方 法,我们发现,该方法即调用了刚才讨论的那个InstantiateIn方法。哈,原来在这里,终于明白了,原来Page里Content控件中的所有内 容最终都将变成其对应的MasterPage中的ContentPlaceHolder的子控件。等一下,这个结论下得有点早,难道这里的 base.ContentTemplates属性就等于Page的_contentTemplateCollection字段吗?如果是,那么上面的结论 就是正确的。
6、我们重新回到1中的代码,查看Page.Master属性的实现,我们发现,它调用了MasterPage的CreateMaster静态方法,而该方法传送的其中一个参数就是._contentTemplateCollection字段。
public MasterPage Master
{
get
{
if ((this._master == null) && !this._preInitWorkComplete)
{
this._master = MasterPage.CreateMaster(this, this.Context, this._masterPageFile, this._contentTemplateCollection);
}
return this._master;
}
}
我们再回头来看看MasterPage.CreateMaster方法,在倒数第6行,我们发现,该方法果然将 contentTemplateCollection赋给了MasterPage实例(child)的_contentTemplates字段。再往下 看,我们还看到了owner.Controls.Add(child),什么意思呢?意思就是将该MasterPage实例作为普通控件加入到owner 实例的子控件集合中。而往上,我们又可以找到owner.Controls.Clear()语句,因此,该MasterPage将作为owner的唯一子 控件而存在。而该owner,常常就是Page实例。(在存在嵌套MasterPage的时候,该owner还可能是下一层次的MasterPage实 例)。
至此,真相大白,原来,MasterPage最终将作为Page的唯一子控件而存在,难怪它要继承自 UserControl,而Page中Content控件定义的各个子控件,又将作为该MasterPage的ContentPlaceHolder的子控件而存在,难 怪ContentPlaceHolder无需实现任何代码,因为它仅仅是一个容器。正因为MasterPage最终成为Page的唯一子控件,那么后来的 处理就与普通的控件没什么两样了,难怪ProcessRequestMain方法里无需为MasterPage单独编码,哈哈,一切都真相大白了。这里, 我们还发现一个比较有趣的现象,即Content控件本身却消失不见了,这应该是ASP.NET解析器所做的优化,因为ContentPlaceHolder完全没必要先装上Content控件,然后再装上Content中的那些控件。
另外,从Page.Master属性与Page.MasterPageFile属性的实现上,我们也不难明白为什么MasterPageFile属性只能在 PreInit 事件中进行设置的原因。
如何证明以上所说都是正确的呢?呵呵,其实很简单,我们可以观察最终页面的控件树,就可证明上面分析是正确的。(写这篇blog时才想起看控件树,要是早想起,就能让我少走不少歪路了,唉,幸好打开控件树发现结果与预期完全一致。)

那么嵌套MasterPage是如何实现的呢?呵呵,其实也一样,即Top1MasterPage成为Top2MasterPage的 唯一子控件,Top2MasterPage成为Top3MasterPage的唯一子控件,……,直到TopNMasterPage成为Page的唯一子 控件。
最后我用两幅图来做总结。
下图为初始的控件树结构:

下图为最终的控件树结构:

[优化]Memcahed分布式缓存服务替换Session解决方案
需求:
有个ASP.NET网站系统,有一级域名,二级域名,三级域名,系统的各个功能模块分布在不同的域名,同一域名的也有可能分布在不同省份的服务器或者同一同一省份的不同的服务器中,同一省份的的服务器可以通过内部局域网访问。在系统中,现在需要所有功能模块共享用户会话信私有数据。
常用的方式是启用Session的数据库持久会模式可以达到上面的需求(没尝试过),现在需要使用Memcached分布式缓存服务来达到保存用户的会话数据,而达到各个功能模块都能够跨省份、跨服务器共享本次会话中的私有数据的目的。
解决方案:
每个省份使用一台服务器来做为Memcached服务器来存储用话的会话中的数据,当然也可以多台服务器,但必须确保每个省份的做Memcached服务器数量必须一致,这样才能够保证Memcached客户端操作的是同一份数据,保证数据的一致性。
会话数据的添加、删除、修改:
Memcached客 户端,添加、删除和、修改会话信息数据时,不仅要添加、删除、修改本省的Memcached服务器数据,而且同时要对其它省份的Memcahed服务器做 同样的操作,这样用户访问其它省份的服务器的功能模块进也能读取到相同的会话数据。Memcached客户端服务器的列表使用局域网的内网 IP(如:192.168.1.179)操作本省的Memcahed服务器,使用公网的IP((如:202.183.62.210))操作其它省份的 Memcahe服务器。
会话数据的读取
系统所有模块读取会话数据的Memcached客户端服务器列表都设为本省Memcached服务器地址的内网IP来向Memcahed服务器中读取会话数据。
如 上图所示,A省有四台服务器,B省也有四台服务器,两个三份都有三台Web服务器、一台Memcached服务器,且A省四台服务器同在一个局域网内的可 能通过内网IP相互访问,B省也一样。假如:A省的A1,B1,C1这三台Web服务器其中的一台要添加或修改、或删除会话数据,它首先调服 Memcached客户端使服D1服务器的内网IP向D1服务器中添加或修改、或删除会话数据,操作完成后,还用调用Memcache客户端使服D2服务 器的公网IP向D2服务器做同样的操作,这样才算完整的操作过程,也可以当做一个事务来处理。假如:A省的A1,B1,C1这三台Web服务器其中一台服 务器想要读取会话的数据,只需要调用Memcached客户端使服D1服务器的内网IP读取数据即可,如果发现该数据不存在,即做向Memcached服 务器添加数据的相关业务处理。B省处理逻辑同A省。
同一会话的确认:
使用Cookie来保持客户与服务端的联系。 每一次会话开始就生成一个GUID作为SessionID,保存在客户端的Cookie中,作用域是顶级域名,这样二级、三级域名就可以共享到这个Cookie,系统中就使用这个SessionID来确认它是否是同一个会话。
会话数据的唯一ID
会话数据存储在Memcached服务器上的唯一键Key也就是会话数据数据的唯一ID定义为:SessionID_Name, SessionID就是保存在客户端Cookie中的SessionID, Name就是会话数据的名称,同一次会话中各个会话数据的Name必须是唯一的,否则新的会话数据将覆盖旧的会话数据。
会话的失效时间:
会话的失效通过控制Cookie的有效时间来实现,会话的时间设为SessionID或Cookie中的有效时间,且每一次访问SessionID时都要重新设置一下Cookie的有效时间,这样就达到的会话的有效时间就是两次间访问Cookie中SessionID值的的最长时间,如果两次访问的间隔时间超过用效时间,那么保存在SessionID的Cookie将会失效,并生成新的SessionID存放在Cookie中, SessionID改变啦,会话就结束啦。
Memcached服务器中会话数据的失效
每 一次向Memcache服务器中添加会话数据时,都把有效时间设为一天也就是24小时,让Memcached服务使用它内部的机制去清除,不必在程序中特 别做会话数据的删除操作。数据在Memcache服务器中有有效时间只是逻辑上的,就算是过了24 小时,如果分配给Memcached服务的内存还够用的话,数据还是保存在内存当中的,只是Memcache客户端读取不到而已。只有到了分配给 Memcached服务的内存不够用时,它才会清理没用或者比较旧的数据,也就是懒性清除。
[Flex]FluorineFx.NET与.Net互操作系列教程
Flex与.NET互操作系列文章索引
Flex与.NET互操作(一):基于Socket的网络连接
Flex与.NET互操作(二):基于WebService的数据访问(上)
Flex与.NET互操作(三):基于WebService的数据访问(下)
Flex与.NET互操作(四):使用HttpService、URLReqeust和URLLoader加载/传输数据
Flex与.NET互操作(五):使用FileReference+HttpHandler实现文件上传/下载
Flex与.NET互操作(六):Flex和.NET协同开发利器FluorineFx
Flex与.NET互操作(七):了解FluorineFx的环境配置(远程对象、网关、通道、目的地)
Flex与.NET互操作(八):使用FluorineFx网关实现远程访问
Flex与.NET互操作(九):FluorineFx.NET的认证(Authentication )与授权(Authorization)
Flex与.NET互操作(十):基于FluorineFx.Net的及时通信应用(ApplicationAdapter)(一)
Flex与.NET互操作(十一):基于FluorineFx.Net的及时通信应用(Remote Procedure Call)(二)
Mikel
