[转载]sqlserver数据库大型应用解决方案总结

mikel阅读(1053)

[转载]sqlserver数据库大型应用解决方案总结 – @Dylan – 博客园.

随着互联网应用的广泛普及,海量数据的存储和访问成为了系统设计的瓶颈问题。对于一个大型的互联网应用,每天百万级甚至上亿的PV无疑对数据库造成了相当高的负载。对于系统的稳定性和扩展性造成了极大的问题。

一、负载均衡技术
负载均衡集群是由一组相互独立的计算机系统构成,通过常规网络或专用网络进行连接,由路由器衔接在一起,各节点相互协作、共同负载、均衡压力,对客户端来说,整个群集可以视为一台具有超高性能的独立服务器。

1、实现原理
实现数据库的负载均衡技术,首先要有一个可以控制连接数据库的控制端。在这里,它截断了数据库和程序的直接连接,由所有的程序 来访问这个中间层,然后再由中间层来访问数据库。这样,我们就可以具体控制访问某个数据库了,然后还可以根据数据库的当前负载采取有效的均衡策略,来调整 每次连接到哪个数据库。
2、实现多据库数据同步
对于负载均衡,最重要的就是所有服务器的数据都是实时同步的。这是一个集群所必需的,因 为,如果数不据实时、不同步,那么用户从一台服务器读出的数据,就有别于从另一台服务器读出的数据,这是不能允许的。所以必须实现数据库的数据同步。这 样,在查询的时候就可以有多个资源,实现均衡。比较常用的方法是Moebius for SQL Server集群,Moebius for SQL Server集群采用将核心程序驻留在每个机器的数据库中的办法,这个核心程序称为Moebius for SQL Server 中间件,主要作用是监测数据库内数据的变化并将变化的数据同步到其他数据库中。数据同步完成后客户端才会得到响应,同步过程是并发完成的,所以同步到多个 数据库和同步到一个数据库的时间基本相等;另外同步的过程是在事务的环境下完成的,保证了多份数据在任何时刻数据的一致性。正因为Moebius 中间件宿主在数据库中的创新,让中间件不但能知道数据的变化,而且知道引起数据变化的SQL语句,根据SQL语句的类型智能的采取不同的数据同步的策略以 保证数据同步成本的最小化。

数据条数很少,数据内容也不大,则直接同步数据
数据条数很少,但是里面包含大数据类型,比如文本,二进制数据等,则先对数据进行压缩然后再同步,从而减少网络带宽的占用和传输所用的时间。
数据条数很多,此时中间件会拿到造成数据变化的SQL语句, 然后对SQL语句进行解析,分析其执行计划和执行成本,并选择是同步数据还是同步SQL语句到其他的数据库中。此种情况应用在对表结构进行调整或者批量更改数据的时候非常有用。
3、优缺点
(1) 扩展性强:当系统要更高数据库处理速度时,只要简单地增加数据库服务器就 可以得到扩展。
(2) 可维护性:当某节点发生故障时,系统会自动检测故障并转移故障节点的应用,保证数据库的持续工作。
(3) 安全性:因为数据会同步的多台服务器上,可以实现数据集的冗余,通过多份数据来保证安全性。另外它成功地将数据库放到了内网之中,更好地保护了数据库的安全性。
(4) 易用性:对应用来说完全透明,集群暴露出来的就是一个IP

(1) 不能够按照Web服务器的处理能力分配负载。
(2) 负载均衡器(控制端)故障,会导致整个数据库系统瘫痪。

二、数据库的读写分离
1,实现原理:读写分离简单的说是把对数据库读和写的操作分开对应不同的数据库服 务器,这样能有效地减轻数据库压力,也能减轻io压力。主数据库提供写操作,从数据库提供读操作,其实在很多系统中,主要是读的操作。当主数据库进行写操 作时,数据要同步到从的数据库,这样才能有效保证数据库完整性。

(ebay的读写比率是260:1,ebay的读写分离)

(微软数据库分发)

2,实现方法:在MS Sql server中可以使用发布定义的方式实现数据库复制,实现读写分离,复制是将一组数据从一个数据源拷贝到多个数据源的技术,是将一份数据发布到多个存储 站点上的有效方式。使用复制技术,用户可以将一份数据发布到多台服务器上。复制技术可以确保分布在不同地点的数据自动同步更新,从而保证数据的一致性。 SQL SERVER复制技术类型有三种,分别是:快照复制、事务复制、合并复制。SQL SERVER 主要采用出版物、订阅的方式来处理复制。源数据所在的服务器是出版服务器,负责发表数据。出版服务器把要发表的数据的所有改变情况的拷贝复制到分发服务 器,分发服务器包含有一个分发数据库,可接收数据的所有改变,并保存这些改变,再把这些改变分发给订阅服务器。

3,优缺点
(1)数据的实时性差:数据不是实时同步到自读服务器上的,当数据写入主服务器后,要在下次同步后才能查询到。

(2)数据量大时同步效率差:单表数据量过大时插入和更新因索引,磁盘IO等问题,性能会变的很差。

(3)同时连接多个(至少两个)数据库:至少要连接到两个数据数据库,实际的读写操作是在程序代码中完成的,容易引起混乱

(4)读具有高性能高可靠性和可伸缩:只读服务器,因为没有写操作,会大大减轻磁盘IO等性能问题,大大提高效率;只读服务器可以采用负载均衡,主数据库发布到多个只读服务器上实现读操作的可伸缩性。

三、数据库/数据表  拆分(分布式)

通过某种特定的条件,将存放在同一个数据库中的数据分散存放到多个数据库上,实现分布存储,通过路由规则路由访问特定的数据库,这样一来每次访问面 对的就不是单台服务器了,而是N台服务器,这样就可以降低单台机器的负载压力。提示:SQLServer 2005版本之后,可以友好的支持“表分区”。

垂直(纵向)拆分:是指按功能模块拆分,比如分为订单库、商品库、用户库…这种方式多个数据库之间的表结构不同。

水平(横向)拆分:将同一个表的数据进行分块保存到不同的数据库中,这些数据库中的表结构完全相同。

(纵向拆分)

(横向拆分)

1,实现原理:使用垂直拆分,主要要看应用类型是否合适这种拆分方式,如系统可以分为,订单系统,商品管理系统,用户管理系统业务系统比较明的,垂 直拆分能很好的起到分散数据库压力的作用。业务模块不明晰,耦合(表关联)度比较高的系统不适合使用这种拆分方式。但是垂直拆分方式并不能彻底解决所有压 力问题,例如 有一个5000w的订单表,操作起来订单库的压力仍然很大,如我们需要在这个表中增加(insert)一条新的数据,insert完毕后,数据库会针对这 张表重新建立索引,5000w行数据建立索引的系统开销还是不容忽视的,反过来,假如我们将这个表分成100个table呢,从table_001一直到 table_100,5000w行数据平均下来,每个子表里边就只有50万行数据,这时候我们向一张只有50w行数据的table中insert数据后建 立索引的时间就会呈数量级的下降,极大了提高了DB的运行时效率,提高了DB的并发量,这种拆分就是横向拆分

2,实现方法:垂直拆分,拆分方式实现起来比较简单,根据表名访问不同的数据库就可以了。横向拆分的规则很多,这里总结前人的几点,

(1)顺序拆分:如可以按订单的日前按年份才分,2003年的放在db1中,2004年的db2,以此类推。当然也可以按主键标准拆分。

优点:可部分迁移

缺点:数据分布不均,可能2003年的订单有100W,2008年的有500W。

(2)hash取模分: 对user_id进行hash(或者如果user_id是数值型的话直接使用user_id的值也可),然后用一个特定的数字,比如应用中需要将一个数据 库切分成4个数据库的话,我们就用4这个数字对user_id的hash值进行取模运算,也就是user_id%4,这样的话每次运算就有四种可能:结果 为1的时候对应DB1;结果为2的时候对应DB2;结果为3的时候对应DB3;结果为0的时候对应DB4,这样一来就非常均匀的将数据分配到4个DB中。
优点:数据分布均匀
缺点:数据迁移的时候麻烦;不能按照机器性能分摊数据 。
(3)在认证库中保存数据库配置
就是建立一个DB,这个DB单独保存user_id到DB的映射关系,每次访问数据库的时候都要先查询一次这个数据库,以得到具体的DB信息,然后才能进行我们需要的查询操作。
优点:灵活性强,一对一关系
缺点:每次查询之前都要多一次查询,会造成一定的性能损失。

[转载]Android SAX解析XML

mikel阅读(725)

[转载]Android SAX解析XML – 华德飞 – 博客园.

解析XML的方式有很多种,大家比较熟悉的可能就是DOM解析。

DOM(文件对象模型)解析:解析器读入整个文档,然后构建一个驻留内存的树结构,然后代码就可以根据DOM接口来操作这个树结构了。

优点:整个文档读入内存,方便操作:支持修改、删除和重现排列等多种功能。

缺点:将整个文档读入内存中,保留了过多的不需要的节点,浪费内存和空间。

使用场合:一旦读入文档,还需要多次对文档进行操作,并且在硬件资源充足的情况下(内存,CPU)。

为了解决DOM解析存在的问题,就出现了SAX解析。其特点为:

优点:不用实现调入整个文档,占用资源少。尤其在嵌入式环境中,如Android,极力推荐使用SAX解析。

缺点:不像DOM解析一样将文档长期驻留在内存中,数据不是持久的。如果事件过后没有保存数据,数据就会丢失。

使用场合:机器有性能限制。

SAX解析XML文档采用事件驱动模式。什么是事件驱动模式?它将XML文档转换成一系列的事件,由单独的事件处理器来决定如何处理。

基于事件驱动的处理模式主要是基于事件源和事件处理器(或者叫监听器)来工作的。一个可以产生事件的对象叫做事件源,而一个可以针对事件做出响应的对象就被叫做事件处理器。

在SAX接口中,事件源是org.xml.sax包中的XMLReader,他通过parse()方法开始解析XML文档,并根据文档内容产生事 件。而事件处理器则是org.xml.sax包中的ContentHandler、DTDHandler、ErrorHandler,以及 EntityResolver这四个接口。他们分别处理事件源在解析过程中产生不同类的事件(其中DTDHandler为解析文档DTD时所用)。详细介 绍如下表:

在上述四个接口中,最重要的就是ContentHandler这个接口,下面是对这个接口方法的说明:

//设置一个可以定位文档内容事件发生位置的定位器对象

public void setDocumentLocator(Locator locator)

//用于处理文档解析开始事件

public void startDocument()throws SAXException

//处理元素开始事件,从参数中可以获得元素所在名称空间的uri,元素名称,属性类表等信息

public void startElement(String namespacesURI , String localName , String qName , Attributes atts) throws SAXException

//处理元素结束事件,从参数中可以获得元素所在名称空间的uri,元素名称等信息

public void endElement(String namespacesURI , String localName , String qName) throws SAXException

//处理元素的字符内容,从参数中可以获得内容

public void characters(char[] ch , int start , int length)  throws SAXException

这里再介绍下XMLReader中的方法。

//注册处理XML文档解析事件ContentHandler
public void setContentHandler(ContentHandler handler)

//开始解析一个XML文档
public void parse(InputSorce input) throws SAXException

SAX实现实体解析的步骤:

Android中使用SAX是有迹可循的,完全可以按照下面的方法就可以轻松找到xml里的tag,然后得到想要的内容。具体实现步骤如下:

(一)第一步:新建一个工厂类SAXParserFactory,代码如下:

SAXParserFactory factory = SAXParserFactory.newInstance();

(二)第二步:让工厂类产生一个SAX的解析类SAXParser,代码如下:

SAXParser parser = factory.newSAXParser();

(三)第三步:从SAXPsrser中得到一个XMLReader实例,代码如下:

XMLReader reader = parser.getXMLReader();

(四)第四步:把自己写的handler注册到XMLReader中,一般最重要的就是ContentHandler,代码如下:

RSSHandler handler = new RSSHandler();
reader.setContentHandler(handler);

(五)第五步:将一个xml文档或者资源变成一个java可以处理的InputStream流后,解析正式开始,代码如下:

parser.parse(is);

上面几个步骤中,最重要、最关键的就是第四步,handler的实现。

下面通过一个RSS解析的例子说明handler的实现:

我们先是自己见一个rss的xml文档,实现本地解析,新建的rss文档如下:

<?xml version="1.0" encoding="UTF-8"?>
    <channel>
        <title>RSS 解析练习</title>
        <description>hehehaha</description>
        <link>http://www.cnblogs.com/felix-hua/</link>
        <language>zh-cn</language>


        <item>
            <title><![CDATA[头条]]></title>
            <link>http://mc.cz001.com.cn/images/menu/23_active.png</link>
            <category>0</category>
            <description>描述详细信息的</description>
            <pubDate>2012-01-09</pubDate>
        </item>
        
        <item>
            <title><![CDATA[新闻]]></title>
            <link>http://mc.cz001.com.cn/images/menu/23_active.png</link>
            <category>0</category>
            <description>描述详细信息的</description>
            <pubDate>2012-01-09</pubDate>
        </item>
        
        <item>
            <title><![CDATA[首页]]></title>
            <link>http://mc.cz001.com.cn/images/menu/23_active.png</link>
            <category>0</category>
            <description>描述详细信息的</description>
            <pubDate>2012-01-09</pubDate>
        </item>
        
        <item>
            <title><![CDATA[财经]]></title>
            <link>http://mc.cz001.com.cn/images/menu/23_active.png</link>
            <category>0</category>
            <description>描述详细信息的</description>
            <pubDate>2012-01-09</pubDate>
        </item>

建好后,我们命名为rssxml.xml,然后放到项目的根目录下:

然后我们可以建立两个实体类:

1、RSSFeed,与完整的xml文档相对应;

2、RSSItem,与item标签内的信息相对应。

这样在解析xml时,我们就可以把解析出来的信息放到实体类里,然后直接操作实体类就可以了。下面给出代码:

RSSFeed.java

package com.sax.org.entity;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Vector;

public class RSSFeed {
    private String title;
    private int itemcount;
    private List<RSSItem> itemlist;
    
    public RSSFeed(){
        itemlist = new Vector<RSSItem>(0);
    }
    
    /**
     * 负责将一个RSSItem加入到RSSFeed类中
     * @param item
     * @return
     */
    public int addItem(RSSItem item){
        itemlist.add(item);
        itemcount++;
        return itemcount;
    }
    
    public RSSItem getItem(int location){
        return itemlist.get(location);
    }
    
    public List<RSSItem> getAllItems(){
        return itemlist;
    }
    
    /**
     * 负责从RSSFeed类中生成列表所需要的数据
     * @return
     */
    public List getAllItemForListView(){
        List<Map<String, Object>> data = new ArrayList<Map<String,Object>>();
        int size = itemlist.size();
        for(int i=0 ; i<size ; i++){
            HashMap<String , Object> item = new HashMap<String, Object>();
            item.put(RSSItem.TITLE, itemlist.get(i).getTitle());
            item.put(RSSItem.PUBDATE, itemlist.get(i).getPubdate());
            data.add(item);
        }
        return data;
    }

    public String getTitle() {
        return title;
    }

    public void setTitle(String title) {
        this.title = title;
    }

    public int getItemcount() {
        return itemcount;
    }

    public void setItemcount(int itemcount) {
        this.itemcount = itemcount;
    }

    public List<RSSItem> getItemlist() {
        return itemlist;
    }

    public void setItemlist(List<RSSItem> itemlist) {
        this.itemlist = itemlist;
    }
    
}

RSSItem.java

package com.sax.org.entity;

public class RSSItem {
    public static String TITLE = "title";
    public static String PUBDATE = "pubdate";
    public String title;
    public String description;
    public String link;
    public String category;
    public String pubdate;
    public RSSItem() {
    }
    public String getTitle() {
        return title;
    }
    public void setTitle(String title) {
        this.title = title;
    }
    public String getDescription() {
        return description;
    }
    public void setDescription(String description) {
        this.description = description;
    }
    public String getLink() {
        return link;
    }
    public void setLink(String link) {
        this.link = link;
    }
    public String getCategory() {
        return category;
    }
    public void setCategory(String category) {
        this.category = category;
    }
    public String getPubdate() {
        return pubdate;
    }
    public void setPubdate(String pubdate) {
        this.pubdate = pubdate;
    }
    
    
}

下面就是最最重要的地方了,建立自己的ContentHandler.看下面的代码:

RSSHandler.java

package com.sax.org.handler;

import org.xml.sax.Attributes;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.DefaultHandler;

import com.sax.org.entity.RSSFeed;
import com.sax.org.entity.RSSItem;

public class RSSHandler extends DefaultHandler{
    RSSFeed RssFeed;
    RSSItem RssItem;
    final int RSS_TITLE = 1;
    final int RSS_LINK = 2;
    final int RSS_DESCRIPTION = 3;
    final int RSS_CATEGORY = 4;
    final int RSS_PUBDATE = 5;
    int currentstate = 0;
    
    public RSSHandler(){}
    
    public RSSFeed getFeed(){
        return RssFeed;
    }
    
    @Override
    public void startDocument() throws SAXException {
        // TODO Auto-generated method stub
        RssFeed = new RSSFeed();
        RssItem = new RSSItem();
    }
    
    @Override
    public void endDocument() throws SAXException {
        // TODO Auto-generated method stub
        
    }
    
    @Override
    public void startElement(String uri, String localName, String qName,
            Attributes attributes) throws SAXException {
        // TODO Auto-generated method stub
        if(localName.equals("channel")){
            currentstate = 0;
            return;
        }
        if(localName.equals("item")){
            RssItem = new RSSItem();
            return;
        }
        if(localName.equals("title")){
            currentstate = RSS_TITLE;
            return;
        }
        if(localName.equals("description")){
            currentstate = RSS_DESCRIPTION;
            return;
        }
        if(localName.equals("link")){
            currentstate = RSS_LINK;
            return;
        }
        if(localName.equals("category")){
            currentstate = RSS_CATEGORY;
            return;
        }
        if(localName.equals("pubDate")){
            currentstate = RSS_PUBDATE;
            return;
        }
        currentstate = 0;
    }
    
    @Override
    public void endElement(String uri, String localName, String qName)
            throws SAXException {
        // TODO Auto-generated method stub
        if(localName.equals("item")){
            RssFeed.addItem(RssItem);
            return;
        }
    }
    
    @Override
    public void characters(char[] ch, int start, int length)
            throws SAXException {
        // TODO Auto-generated method stub
        String theString = new String(ch, start, length);
        switch(currentstate){
        case RSS_TITLE:
            RssItem.setTitle(theString);
            currentstate = 0;
            break;
        case RSS_DESCRIPTION:
            RssItem.setDescription(theString);
            currentstate = 0;
            break;
        case RSS_LINK:
            RssItem.setLink(theString);
            currentstate = 0;
            break;
        case RSS_PUBDATE:
            RssItem.setPubdate(theString);
            currentstate = 0;
            break;
        case RSS_CATEGORY:
            RssItem.setCategory(theString);
            currentstate = 0;
            break;
        default:
            return;
        }
    }
}

就上面的代码分析,实现一个ContentHandler一般要一下几个步骤:

1、声明一个类,继承DefaultHandler。DefaultHandler是一个基类,这个类里面简单实现了一个ContentHandler。我们只需要重写里面的方法即可。

2、重写 startDocument() 和 endDocument(),一般解析将正式解析之前的一些初始化工资放到startDocument()里面,收尾的工作放到endDocument()里面。

3、重写startElement(),XML解析器遇到XML里面的tag时就会调用这个函数。经常在这个函数内是通过localName俩进行判断而操作一些数据。

4、重写characters()方法,这是一个回调方法。解析器执行完startElement()后,解析完节点的内容后就会执行这个方法,并 且参数ch[]就是节点的内容。这个例子里我们根据currentstate的不同,来判断当前那个tag的内容,并放到合适的实体类中。

5、重写endElement()方法,这个方法与startElement()相对应,解析完一个tag节点后,执行这个方法。再找个例子中,如果解析一个item结束,就将RSSIiem添加到RSSFeed中。

最后我们实现一个activity来展现解析的结果:

package com.sax.org;

import java.io.IOException;
import java.net.URL;

import javax.xml.parsers.ParserConfigurationException;
import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;

import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
import org.xml.sax.XMLReader;

import android.app.Activity;
import android.os.Bundle;
import android.widget.ListView;
import android.widget.SimpleAdapter;
import android.widget.TextView;

import com.sax.org.entity.RSSFeed;
import com.sax.org.entity.RSSItem;
import com.sax.org.handler.RSSHandler;

public class SAXReaderActivity extends Activity {
    /** Called when the activity is first created. */

    public String rssUrl = "http://mc.cz001.com.cn/a/indexconfig/index.rss";
    public RSSFeed feed;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        feed = getFeed(rssUrl);
        showList();
    }

    public RSSFeed getFeed(String rssUrl) {
        try {
// 这里我们实现了本地解析,所以注掉了这个取网络数据的。
//            URL url = new URL(rssUrl);
            SAXParserFactory factory = SAXParserFactory.newInstance();
            SAXParser parser = factory.newSAXParser();
            XMLReader reader = parser.getXMLReader();
            RSSHandler handler = new RSSHandler();
            reader.setContentHandler(handler);
            InputSource is = new InputSource(this.getClassLoader().getResourceAsStream("rssxml.xml"));//取得本地xml文件
            reader.parse(is);
            return handler.getFeed();
        } catch (ParserConfigurationException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (SAXException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        return null;
    }

    public void showList() {
        ListView rsslistview = (ListView) findViewById(R.id.rssList);
        TextView rsstitle = (TextView) findViewById(R.id.rsstitle);
        if (feed == null) {
            rsstitle.setText("访问失败...");
            return;
        }
        SimpleAdapter adapter = new SimpleAdapter(this,
                feed.getAllItemForListView(),
                android.R.layout.simple_list_item_2, new String[] {
                        RSSItem.TITLE, RSSItem.PUBDATE }, new int[] {
                        android.R.id.text1, android.R.id.text2 });
        rsslistview.setAdapter(adapter);
    }
}

展示下运行结果:

OK。

本文参考自《Google+Android入门与实践》,谢谢。

[转载]ucenter创始人密码忘了解决方法

mikel阅读(720)

[转载]ucenter创始人密码忘了解决方法[转]_百度一下_百度空间.

打开uc下面/data/config.inc.php文件

里面有

define(‘UC_FOUNDERPW’, ‘924a2bd32289075d8055e7e30261dfb1’);

define(‘UC_FOUNDERSALT’, ‘116414’);

记下116414这个值,然后

用php输出下面的一个代码

$ucfounderpw= md5(md5($ucfounderpw).$ucsalt);

其中$ucsalt就是116414(这个根据您的需要而定)

$ucfounderpw是您要设置的密码。

得到的$ucfounderpw替换

define(‘UC_FOUNDERPW’, ‘924a2bd32289075d8055e7e30261dfb1’);

这个里面的924a2bd32289075d8055e7e30261dfb1就可以了。

还有另外一种方法,将以下代码保存为PHP,传至UC根目录,运行即可

“<?php

define(ROOT_DIR,dirname(__FILE__).”/”);

if(!file_exists(‘./data/config.inc.php’) || !is_writeable(‘./data’)){

$isucdir= 0;

echo ‘UCenter创始人密码重置工具必须放在UCenter根目录下才能正常使用.’;

exit;

}else{

$isucdir = 1;

}

$info=””;

setucadministrator();

function setucadministrator(){

global $isucdir;

global $info;

if($_POST[‘setucsubmit’]){

if($isucdir){

$configfile = ROOT_DIR.”./data/config.inc.php”;

$uc_password = $_POST[“uc_password”];

$salt = substr(uniqid(rand()), 0, 6);

if(!$uc_password){

$info = “密码不能为空”;

}else{

$md5_uc_password = md5(md5($uc_password).$salt);

$config = file_get_contents($configfile);

$config = preg_replace(“/define\(‘UC_FOUNDERSALT’,\s*’.*?’\);/i”, “define(‘UC_FOUNDERSALT’, ‘$salt’);”, $config);

$config = preg_replace(“/define\(‘UC_FOUNDERPW’,\s*’.*?’\);/i”, “define(‘UC_FOUNDERPW’, ‘$md5_uc_password’);”, $config);

$fp = @fopen($configfile, ‘w’);

@fwrite($fp, $config);

@fclose($fp);

$info = “UCenter创始人密码更改成功为:$uc_password”;

}

}else{

$info = “本程序文件放置在UCenter跟目录,才能通过程序修改UCenter创始人管理员的密码<br />”;

}

}

templates(“setucadministrator”);

}

function errorpage($message,$title = ”,$isheader = 1,$isfooter = 1){

$message = “<h4>$title</h4><br><br><table><tr><th> 提示信息</th></tr><tr><td>$message</td>< /tr></table>”;

echo $message;

exit;

}

function templates($tpl){

switch ($tpl){

case “header”:

echo ‘<html>

<head>

<meta http-equiv=”Content-Type” content=”text/html; charset=gb2312″>

<title>UCenter 创始人密码更改工具</title>

<style type=”text/css”>

<!–

body {font-family: Arial, Helvetica, sans-serif, “宋体”;font-size: 12px;color:#000;line-height: 120%;padding:0;margin:0;background:#DDE0FF;overflow-x:hidden;word-break:break-all;white-space:normal;scrollbar-3d-light-color:#606BFF;scrollbar-highlight-color:#E3 EFF9;scrollbar-face-color:#CEE3F4;scrollbar-arrow-color:#509AD8;scrollbar-shadow-color:#F0F1FF;scrollbar-base-color:#CEE3F4;}

a:hover {color:#60F;}

ul {padding:2px 0 10px 0;margin:0;}

textarea,table,td,th,select{border:1px solid #868CFF;border-collapse:collapse;}

input{margin:10px 0 0px 30px;border-width:1px;border-style:solid;border-color:#FFF #64A7DD #64A7DD #FFF;padding:2px 8px;background:#E3EFF9;}

input.radio,input.checkbox,input.textinput,input.specialsubmit {margin:0;padding:0;border:0;padding:0;background:none;}

input.textinput,input.specialsubmit {border:1px solid #AFD2ED;background:#FFF;height:24px;}

input.textinput {padding:4px 0;}     input.specialsubmit {border-color:#FFF #64A7DD #64A7DD #FFF;background:#E3EFF9;padding:0 5px;}

option {background:#FFF;}

select {background:#F0F1FF;}

#header {height:60px;width:100%;padding:0;margin:0;}

h2 {font-size:24px;font-weight:bold;position:absolute;top:24px;left:20px;padding:10px;margin:0;}

h3 {font-size:14px;position:absolute;top:28px;right:20px;padding:10px;margin:0;}

#content {height:510px;background:#F0F1FF;overflow-x:hidden;z-index:1000;}

#nav {top:60px;left:0;height:510px;width:180px;border-right:1px solid #DDE0FF;position:absolute;z-index:2000;}

#nav ul {padding:0 10px;padding-top:30px;}

#nav li {list-style:none;}

#nav li a {font-size:14px;line-height:180%;font-weight:400;color:#000;}

#nav li a:hover {color:#60F;}

#textcontent {padding-left:200px;height:510px;width:100%;line-height:160%;overflow-y:auto;overflow-x:hidden;}

h4,h5,h6 {padding:4px;font-size:16px;font-weight:bold;margin-top:20px;margin-bottom:5px;color:#006;}

h5,h6 {font-size:14px;color:#000;}

h6 {color:#F00;padding-top:5px;margin-top:0;}

.specialdiv {width:70%;border:1px dashed #C8CCFF;padding:0 5px;margin-top:20px;background:#F9F9FF;}

#textcontent ul {margin-left:30px;}

textarea {width:78%;height:320px;text-align:left;border-color:#AFD2ED;}

select {border-color:#AFD2ED;}

table {width:74%;font-size:12px;margin-left:18px;margin-top:10px;}

table.specialtable,table.specialtable td {border:0;}

td,th {padding:5px;text-align:left;}

caption {font-weight:bold;padding:8px 0;color:#3544FF;text-align:left;}

th {background:#D9DCFF;font-weight:600;}

td.specialtd {text-align:left;}

.specialtext {background:#FCFBFF;margin-top:20px;padding:5px 40px;width:64.5%;margin-bottom:10px;color:#006;}

#footer p {padding:0 5px;text-align:center;}

–>

</style>

</head>

<body>

<div id=”content”>

<div id=”textcontent”>’;

break;

case “footer”:

echo ‘

</div></div>

<div id=”footer”><p>UCenter 创始人密码更改工具 &nbsp;

版权所有 &copy;2001-2007 <a href=”http://www.comsenz.com” style=”color: #888888; text-decoration: none”>

康盛创想(北京)科技有限公司 Comsenz Inc.</a></font></td></tr><tr style=”font-size: 0px; line-height: 0px; spacing: 0px; padding: 0px; background-color: #698CC3″>

</p></div>

</body>

</html>’;

exit;

break;

case “setucadministrator”:

templates(“header”);

if(!empty($_POST[‘setucsubmit’])){

echo “<h5>UCenter 创始人密码更改工具</h5><h5> <font color=red>使用完毕后请及时删除本文件,以免给您造成不必要的损失</font></h5>”;

echo ‘<form action=”?action=setadmin” method=”post”><input type=”hidden” name=”action” value=”login” />’;

global $info;

errorpage($info,”,0,0);

echo ‘</form>’;

}else{

echo ‘<form action=”?action=setucadministrator” method=”post”>

<h5>UCenter 创始人密码更改工具</h5>

<h5> <font color=red>使用完毕后请及时删除本文件,以免给您造成不必要的损失</font></h5>

<table>

<tr><th width=”30%”>用户名</th><td width=”70%”><input class=”textinput” readonly=”readonly” disabled type=”text” name=”username” size=”25″ maxlength=”40″ value=”UCenter Administrator”></td></tr>

<tr><th width=”30%”>请输入密码</th><td width=”70%”><input class=”textinput” type=”text” name=”uc_password” size=”25″></td></tr>

</table>

<input type=”submit” name=”setucsubmit” value=”提 &nbsp; 交”>

</form>’;

}

templates(“footer”);

break;

}

}

?>”

[转载]火车票抢票助手简化版

mikel阅读(1045)

[转载]老话题,火车票抢票助手,简化版 – afeng124 – 博客园.

用了“二杠”兄弟的工具测试了抢票,先举个大拇指。牛!可是我实在看不惯他画的界面,而且需要认证和访问他服务器上的wcf服务。看到许多园友都号 称“破解”了。我也请出”Reflector”神器,尝试了下。发现把源exe文件作为app的资源隐藏到另一个exe里了。”Reflector”神器 直接“另存为”就搞定了。再继续Reflector源exe文件,OK搞定,源码展现在眼前了。后来尝试以自己的思路重写了一下,汗,还挺复杂,花了我3 个小时时间。当当当,成果奉上,有图有真相!

点了2次登录,自动终止了2次后成功。WebBrowser界面打开后会提示2次,你懂的!

然后点”我的12306″,添加常用联系人,这样下单是勾选就OK了。

就是这么顺利,这么简单!

需要执行程序的猛击这里:
下载: 订票助手简化版v1.0

看在半个老乡的份上,“二杠”兄弟不会怪我吧。你自己加强一下搞个混淆啊什么的吧。

[转载]我的Discuz!X2 、Ucenter 1.6、ASP.NET 应用程序整合经历

mikel阅读(1002)

[转载]我的Discuz!X2 、Ucenter 1.6、ASP.NET 应用程序整合经历 – 疯狂秀才 – 博客园.

对于ASP.NET 程序员来说,去整合PHP的应用实在有点别扭,但那也没有办法,即然是做程序的这些是避免不了的,既然免不了,那就上吧

第一步:准备相关程序

1、下载DISCUZ X2 的程序

2、在windows 2003 中配置PHP环境

3、安装DISCUZ X2程序 ,选择全新安装(包括UCENTER 1.6)

4、下载 Ucenter 的.NET 版本的API . (http://ucenterapidotnet.codeplex.com/)

此应用程序是一位大侠修改的.NET 4.0。在此表示感谢。在他的博客中的有介绍ucenter 的一些通信原理,有兴趣的朋友可以去逛逛:

http://www.dozer.cc/2011/01/ucenter-api-in-depth-1st/

5、如果你的.net程序运行在4.0环境下,下载下来的源码中有个地方需要修改下。

ucapibase.cs 中 使用P3P协议的地方,源码如下:

Response.Headers.Add(“P3P”, 修改为:

Response.AppendHeader(“P3P”,

第二步:参考这里吧,我就不码字啦 http://www.cnblogs.com/CoreCaiNiao/archive/2011/08/25/2153434.html

如果以上通信成功啦!那么恭喜你,你已经完成了一半啦

到这里问题就出来啦,在www站点可以登录和退出,且和X2也同步登录和退出啦,但是X2的登录和退出根本不会同步WWW的登录和退出,经过一系统 的监控手段发现。在X2中登录和退出均不调用应用管理中设置的API(即UC.PHP),我也不懂PHP代码,百思不得其解之下请出了百度大神,我搜啊搜 啊终于找到了一个解决此问题的(非常规手段)原文链接 ,看下图吧

注意,在更新缓存时,不是UCENTER中的更新缓存,是工具的 如下图:

更新完缓存后,在进入X2论坛中进行登录和退出,居然会同步啦。如果是低版本的Ucenter 可能不会出现这个问题,这个我没有去测试

关于修改密同步的问题,我是这样解决的

在X2中修改的密码只能在X2中登录有效,当在主站修改了密码后,在主站登录时更新X2中的密码,然后在做同步登录。

[转载]玩转git,让git成为个人工作备份利器

mikel阅读(1182)

[转载]玩转git,让git成为个人工作备份利器(即使是电脑小白也推荐学习) – 大宝pku – 博客园.

前言

不要把这篇文章当做给编程人员的技术文章,即使你对编程一无所知,按照这篇文章的一步步指引,仔细模仿,也可以很容易的学会如何去做。更重要的是, 这篇文章将让你学会如何使用git成为你的工作日志,成为你的备份工具,成为你的项目管理软件。没错,git让这一切变得如此简单。

背景

OK,什么是git? Wikipedia(http://en.wikipedia.org/wiki/Git_(software))告诉我们:“Git (/ɡɪt/) is a distributed revision control system with an emphasis on speed.“ 这是一个分布式版本控制系统。听起来似乎很霸气,那么它是谁开发的呢?这个名字更霸气, Linus Torvalds——Linux之父。

什么叫版本控制系统呢?

我们来举个例子。比如说,我要写个商业策划书,经过几天熬夜,总算在2012-01-01写了一个初稿 proposal.doc。又经过两天的奋战,你对之前的文档做了更深入的细化,于是你把之前的文件改名成了 proposal- 2012-01-01.doc 作为备份。经过你和老板的激烈讨论,文档的反复修改,你终于在2012-01-09把第7版交给了老板,算是完成了工作。回头看看当前的文件夹,呵,充满 了 proposal-2012-01-01.doc,proposal-2012-01-03.doc。。。proposal- 2012-01-06.doc 的文件。你不舍得删了它们,顺手打个包压缩下扔进了自己的归档文件夹里,也许永远也不会再来查看了;而且即使半年后想要来参考,也不过是看看最新版的文 件,这些历史版本之间的关系恐怕永远也想不起来了。

这个应该算是非常普遍的例子,任何用电脑写过报告的人都会有这种经历。保存某个修改的报告时我们习惯性的给之前的版本加个日期(或者其 他你感兴趣的方式)做个备份,生拍自己之后的某个修改不满意好恢复回去;如果有强迫症,估计恨不得写一行保存一次——如果你学会了git,恐怕这种强迫症 会更加强烈,因为用起来实在是太爽了。这就是一个非常朴素的版本控制系统,你人为的给某个文件加上时间的标记,把编辑的某些里程碑保存下来,方便自己将来 查阅、备份、修复。

既然我们有这么普遍而强烈的需求,版本控制系统软件自然备受关注,并不断更新升级。从 CVS 到 SVN 再到 GIT,版本控制系统的管理模式从集中到分布而演变。git的分布式特点就在于,虽然我们可以有一个最核心的中央服务器来管理所有的文件,你可以在本地生 成一个局部的版本控制系统,你的版本历史都可以先在本地保存,直到获得一个满意的版本后,再提交到服务器中。对于个人来说,甚至完全不用理会中央服务器是 否存在,自己玩单机版的版本控制就足够了——本文的目的也就在于教会你怎样玩转单机版git。

准备

工欲善其事,必先利其器。我们需要的软件很少,只有两个,TortoiseGit(http://code.google.com/p/tortoisegit/downloads/list)和msysgit(http://code.google.com/p/msysgit/downloads/list),前者是与 windows 的资源管理器(linux和mac用户借过一下)整合的git管理软件,后者是git的功能软件。

安装好TortoiseGit之后,重启电脑,鼠标右键里就会有这几个新的程序,git的相关功能都可以从这里访问到,连开始菜单都不要,是不是很方便?

(svn是另一个软件,请无视)


请选择setting,我们需要配置一下git的路径。

在红框里,我们要设置一下 git 的路径,就是你安装 msysgit的bin文件夹,比如说我的是 F:\software\develop\svn\git\PortableGit-1.7.8- preview20111206\bin,里面会有 git.exe 这个程序。

如此,我们的环境就配置好了,是不是非常简单?


实战

基础知识

接下来,我们要具体看看git如何成为我们备份的利刃。

先随便创建一个空白文件夹,作为我们实践的平台。

进入这个文件夹,鼠标右键选择 “ Git Create repository here…”,弹出的对话框不用管,那个复选框不要选,ok即可。这个文件夹里就会出现一个隐藏的 .git 文件夹。如何显示隐藏文件夹?这个请放狗去搜吧。

ok,一个版本库我们就创建出来了。走到上级目录,你也许会看到,文件加上出现了一个绿色的对勾符号,表示这个文件夹已经受到了版本的控制。(这个图标的出现不一定实时,也许你要多刷新几次,甚至重启电脑才行)

常见的图标有

这个文件(夹)很干净,是最新版本文件

这个文件(夹)发生了修改,需要提交

这个文件(夹)要添加进版本库

这个文件(夹)没有在版本库中,需要添加进来(转为上一个图标的状态),或者忽略(转为下一个图标的状态)

这个文件(夹)被忽略,不受版本控制

这个文件(夹)里的内容发生冲突(这是最危险的情况,但是对于个人用户来说,几乎不会出现,出现的情况是两个人同时对一个文件做修改,后提交的人在更新时无法与前者完美融合时,这个不需要深入理解)

通过这么直观的图标暂时,你就能对自己的工作文件夹了如指掌,那些文件做了修改,当前工作是否需要提交,都一目了然。

版本分支

在进行更进一步操作时,有必要了解一下版本分支(Branch)的概念。版本分支意味着,你从当前的项目中弄出来一个拷贝,按照自己的想法胡乱修 改,你之后的所有版本历史都保存在这个分支里。如果你对这个分支很满意,就可以把这个分支合并到原来的主分支里(Merge),甚至直接让这个分支作为主 分支;或者你感觉到越改越糟糕,就直接回到主分支工作,仿佛什么也没有发生一样(当然这个新的分支要通过某些复杂的操作来删除,你也可以留着,作为自己惨 痛教训的警钟)。也就是说,分支为你提供了一个尝试某些新道路的空间。

作为个人用户来说,有一个主分支和一个工作分支就够了。主分支作为基准一直不变,工作都在工作分支上进行,这样主分支与工作分支之间的变化就记录了你的所有工作历史。

我们来具体实践一下。

首先,git有个不太人性化的“bug“,不能够添加空白文件夹,因此我们需要先添加一个空白文件,比如 test.txt,然后在文件夹上右键选择 “Git commit”

选择添加的那个文件,写点message(git每次提交必须写上一些文字说明这次更新的内容,从而让你知道每个版本有什么变化)提交即可

在刚才的工作文件加上右键,选择 “Create Branch“

随便取名比如 develop,确定即可。

依然右键选择 “Show log“,出现如下对话框

可以看到,我们已经有了一个版本历史,它详细记录了我们的改变。上面有两个颜色标签,红色的master和绿色的develop,这个代表了我们 的分支。master是我们第一次提交时系统自动生成的分支;develop是我们手动创建的。红色的代表当前的工作分支,绿色的代表存在的历史分支。我 们需要把工作分支切换到 develop。

在文件夹上右键选择 “Switch/Checkout”, Branch里选择develop,再查看历史,我们就会发现,develop变成了红色。

开始工作

接下来我们就可以随心所欲的工作了。编辑一下test.txt,新建文件夹,添加word,excel文件,总之,随便工作。你会看到,修改的文件 加了红色的叹号,新加为被版本控制的文件加了蓝色的问号,对于蓝色问号,你可以选择右键里的 “Add“让它变成蓝色的加号(或者有时会一步到位变成绿色的对勾,这个不重要),或者不管他。

然后在根文件上,选择 Commit,再来看看历史,就会发现你的工作记录了

可以发现,develop进步了一个版本,master还停留在原处。历史显示,test.txt被修改,一个word和excel被添加;文件夹信息不回被记录。

双击下面的test.txt文件,它甚至会给你展示出这个文件发生了哪些变化。甚至连 excel 和 word 的变化也会给你选择合适的方式进行比较。

令人震惊吧!没错,git会完全忠实的记录的一点一滴,只要你commit,系统就会为你保存下来一个历史记录(不必担心空间,它会采用很先进的方式进行增量保存,远比你每次都复制要节省空间得多)

结束语

差不多已经把如何用git来进行工作的方法介绍完了,还有一个非常重要的功能没有说,怎么样恢复历史版本的文件呢?很简单,在想要恢复的文件上查看历史,选择revert即可!

即使你完全不懂编程,相信上面的讲解也可以教会你怎么样来使用了吧。赶紧来试试吧!

[转载][翻译]ASP.NET MVC 3 开发的20个秘诀(二十)[20 Recipes for Programming MVC 3]:缓存结果数据加速页面载入

mikel阅读(870)

[转载][翻译]ASP.NET MVC 3 开发的20个秘诀(二十)[20 Recipes for Programming MVC 3]:缓存结果数据加速页面载入 – O2DS – 博客园.

议题

随着网站的发展,大量用户访问流行内容和动态内容,这两个方面的因素会增加平均的载入时间,给Web服务器和数据库服务器造成大量的请求压力。而大量的数据请求就需要强大的数据库处理能力来支撑。改进更加只能的程序,以极少不必要的数据库访问或动态处理请求,可以节省添加更多服务器的费用以及显著提高Web应用程序的整体速度。

解决方案

实现OutputCacheAttribute类,缓存不经常改变的数据或者相对固定的动作。

讨论

MVC3中实现缓存是非常容易的事情。将下面的属性添加在某个控制器的动作上即可完成:

[OutputCache (Duration=600)]

这个语句将自动缓存该视图600秒(或10分钟)共享给每个访问此页的用户。这就意味着针对代码进行简单的修改当你有1000名用户同时访问此页,只需要通过一次数据加载,可节约数以千计的数据库请求以及IIS处理负载。

输出缓存的属性看起来非常简单,但是当你打开引擎盖的时候,也可能会发现这辆车的引擎原来如此复杂。当然除非你原本就是名机械师。这个属性提供了很多关于缓存的方式,必须持续缓存时间,甚至还添加了SQL依赖,在这个秘诀中我们会深入探讨一番。

按照时间缓存非常简单,你只需要告诉MVC视 图应该被缓存多少秒即可。至于缓存存储的位置,是客户端浏览器还是服务器或者是它们的组合,这个问题稍微有点复杂。一个好的做法是首先分析被缓存的内容数 据,如果是缓存多个用户共享的数据,缓存到服务器上更为合适;然而如果是个人数据,比如个人定制的网页,还是将其缓存在用户的浏览器中更好。虽然缓存可以 说是个伟大的发明,但是它也是有局限性的。通常情况下,缓存的最大限制是内存,不是一切都可以缓存在服务器上。

然而,最有趣的方式还是SQL依赖。OutputCache允许数据一直被缓存,直到它在数据中的内容发生了实际的变化,这是一个非常有用的功能。就拿图书来举例,新书并不是经常增加的,因此可以为图书设置一个缓存时间(例如24小时)。但是,如果在缓存过期之前添加了一本新书,又或者是在漫长的一周或几天里根本就没有添加新书?第一种情况,添加一本新书不能及时出现,用户是会不高兴的。而第二种情况,因为没有新书被添加,每天都要发起不必要的服务器请求。使用SQL依赖的方式,像我们希望的那样,当图书的表发生变化时缓存能被立即更新。

这是一个非常棒的功能,在其他编程语言中,你需要手动控制缓存,你需要自己手动更新无效缓存。相信我,这很容易就会让你错过一两个应该正常清除的缓存。

在接下来的示例中,将缓存图书列表页面。默认情况下,如果没有设置VaryByParam的值,MVC3将会自动创建一个唯一的缓存对象。这是个相当棒的功能,按二在这本书上面的例子里,搜索关键字也会作为输入参数之一,但是可能输入数以百计不同的关键字组合,所以这个变量不应该被缓存(这就是上面提高的会产生内存警告的问题)。下面修改后启用缓存的BooksController控制器:

using System;
using System.Collections.Generic;
using System.Data;
using System.Data.Entity;
using System.Linq;
using System.Linq.Dynamic;
using System.Web;
using System.Web.Mvc;
using MvcApplication4.Models;
using MvcApplication4.Utils;
using PagedList;
namespace MvcApplication4.Controllers
{
    public class BooksController : Controller
    {
        private BookDBContext db = new BookDBContext();
        //
        // GET: /Books/
        [OutputCache(Duration=600, VaryByParam=
            "sortOrder;filter;page")]
        public ViewResult Index(string sortOrder,
            string filter, string Keyword, int page = 1)
        {
            ...
            return View(books.ToPagedList(currentPage,
            maxRecords));
        }
        ...
    }

}

这段代码是非常不错的缓存方案,可以立即降低服务器负载。接下来,将这个例子扩展为SQL依赖,在开始工作之前必须还要做一些设置工作。首先是修改Web.config,在文件中定义一个数据库链接,然后,将缓存部分的SQL依赖设置如下:

<?xml version="1.0"?>
<configuration>
    <connectionStrings>
        <add name="ApplicationServices" connectionString=
            "data source=.\SQLEXPRESS;Integrated Security=SSPI;
                AttachDBFilename=|DataDirectory|aspnetdb.mdf;
                    User Instance=true" providerName="System.Data.SqlClient"
                        />
        <add name="BooksDBContext" connectionString=
            "Server=.\SQLEXPRESS;Database=
                MvcApplication4.Models.BookDBContext;
                    Trusted_Connection=true" providerName=
                        "System.Data.SqlClient" />
    </connectionStrings>
    ...
    <system.web>
        <caching>
            <sqlCacheDependency enabled="true" pollTime="2000">
                <databases>
                    <add name = "MvcApplication4.Models.BookDBContext"
                        connectionStringName = "BooksDBContext"/>
                </databases>
            </sqlCacheDependency>
        </caching>
        ...
    </system.web>
    ...
</configuration>

在上面的例子中,将pollTime设置为2000毫秒,也就是说,每2秒中,就会查询一次缓存数据库是否被更改,这个变量设置可以根据你的实际需求来修改。

现在,需要修改Global.asax.cs文件,必须在Application_Start方法中通过SqlCacheDependencyAdmin类的EnableTableForNotifications方法为每个需要缓存的表添加监听功能。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using System.Web.Routing;
using MvcApplication4.Models;
using System.Data.Entity;
using System.Globalization;
using System.Threading;
using MvcApplication4.Utils;

namespace MvcApplication4
{
    public class MvcApplication : System.Web.HttpApplication
    {
        ...
        protected void Application_Start()
        {
            Database.SetInitializer<BookDBContext>(
                new BookInitializer());
            
            AreaRegistration.RegisterAllAreas();
            
            RegisterGlobalFilters(GlobalFilters.Filters);
            
            RegisterRoutes(RouteTable.Routes);
            
            String connectionString =
                System.Configuration.ConfigurationManager.ConnectionStrings
                    ["BooksDBContext"].ConnectionString;
            
            System.Web.Caching.SqlCacheDependencyAdmin.
                EnableNotifications(connectionString);
            
            System.Web.Caching.SqlCacheDependencyAdmin.
                EnableTableForNotifications(connectionString, "books");
        }
        ...
    }

}

在命令行窗口中,执行如下步骤来完成SQL通知的功能配置工作:

点击“开始”按钮 -> 选择“运行”

输入“cmd”并回车

cd %windir%\Microsoft.NET\Framework\v4.0.30319\aspnet_regsql.exe -S .\SQLEXPRESS -ed-dMvcApplication4.Models.BookDBContext -et -t books -E

务必要你自己的信息,替换命令中的服务器名、数据库名以及表名。此外,如果你的数据库包含用户名和密码,将需要包含额外的输入参数(-U-P)。命令运行后,应该会显示两条成功信息,第一条,说明数据库缓存功能启用;第二条说明指定表缓存成功。

最后,使用SQL依赖缓存需要对BooksController稍作修改。此外,因为更换应用程序缓存方式,之前设置的缓存持续时间需要修改为Int32的最大值。

using System;
using System.Collections.Generic;
using System.Data;
using System.Data.Entity;
using System.Linq;
using System.Linq.Dynamic;
using System.Web;
using System.Web.Mvc;
using MvcApplication4.Models;
using MvcApplication4.Utils;
using PagedList;

namespace MvcApplication4.Controllers
{
    public class BooksController : Controller
    {
        private BookDBContext db = new BookDBContext();
        //
        // GET: /Books/
        [OutputCache(Duration=Int32.MaxValue, SqlDependency =
            "MvcApplication4.Models.BookDBContext:books",
                VaryByParam="sortOrder,filter,page")]
        public ViewResult Index(string sortOrder, string filter,
            string Keyword, int page = 1)
        {
            ...
            return View(books.ToPagedList(currentPage,
                maxRecords));
        }
        ...
    }
}

在以前的MVC版本中,是不支持局部缓存的,这就意味着每次都需要将整个动作的结果进行缓存。目前在MVC3版本中开始支持局部缓存。实现这个功能,需要创建一个类似于秘诀1.14中使用Ajax提交表单的自动作。在BookCommentsController的这两个活动都至返回分部视图,而都无需缓存父级动作中的缓存内容。这是一个非常棒的改进,可以将你的代码与那些不经常改变的缓存内容进行有效的隔离。

参考

OutputCacheAttribute SqlCacheDependencyAdmin 原书地址 书籍源代码

[转载]JSON那些事

mikel阅读(1060)

[转载]JSON那些事 – lengyuhong – 博客园.

JSON(JavaScript Object Notation)是一种轻量级的数据格式,采用完全独立于语言的文本格式,是理想的数据交换格式。同时,JSON是JavaScript原生格式,这意 味着在javascript中处理JSON数据不需要任何特殊的API或工具包,而且效率非常高。

JSON的结构如下:

  • “名称/值”对的集合(A collection of name/value pairs)。不同的语言中,它被理解为对象(object),纪录(record),结构(struct),字典(dictionary),哈希表(hash table),有键列表(keyed list),或者关联数组 (associative array)。
  • 值的有序列表(An ordered list of values)。在大部分语言中,它被理解为数组(array)

一个标准的json格式:

{"name":"jifeng","company":"taobao"}

总体而言,json是相对比较容易的理解和使用的,但同时存在很多的陷阱,如果不注意的话很容易掉进去,本文主要就是讲诉它容易让人犯错的地方。

一、json的的解析方法

json的解析方法共有两种:1. eval() ; 2.JSON.parse()。具体使用方法如下

var jsonstr = '{"name":"jifeng","company":"taobao"}';
//eval function
var evalJson = eval('(' + jsonstr + ')');
// JSON.parse function
var JSONParseJson = JSON.parse(jsonstr);

既然两者都能达到解析json的结果,但这两者有什么区别呢?我用以下的一个例子来进行说明:

1. 用eval方法:

var parse_json_by_eval = function(str){
    return eval('('+str+')');
}

var value = 1;
var jsonstr = '{"name":"jifeng","company":"taobao","value":++value}';
var json1 = parse_json_by_eval(jsonstr);
console.log(json1);
console.log('value: '+ value);

执行结果:

{ name: 'jifeng', company: 'taobao', value: 2 }
value: 2

2. 用JSON.parse方法

var parse_json_by_JSON_parse = function(str){
    return JSON.parse(str);
}

value = 1;
var jsonstr = '{"name":"jifeng","company":"taobao","value":++value}';
var json2 = parse_json_by_JSON_parse(jsonstr);
console.log(json2);
console.log(value);

执行结果:

不能顺利执行,报错

前者能顺利执行,并修改了全局变量value的值,而后者报错了。从上例就可以明显地看出,eval在解析字符串时,会执行该字符串中的代码(这样的后 果是相当恶劣的),如上例中,由于用eval解析一个json字符串而造成原先的value的值改变。《高性能Javascript》一书即指出:

警 告:关于JSON和eval需要注意的是:在代码中使用eval是很危险的,特别是用它执行第三方的JSON数据(其中可能包含恶意代码)时,尽可能使用 JSON.parse()方法解析字符串本身。该方法可以捕捉JSON中的语法错误,并允许你传入一个函数,用来过滤或转换解析结果。如果此方法以备 Firfox 3.5 、IE8 及 Safari 4 原生支持。大多数javascript类库包含的JSON解析代码会直接调用原生版本,如果没有原生支持的话,会调用一个略微不那么强大的非原生版本来处 理。

一、’\’ 对JSON的影响

由于JSON对象是从字符串解析而来,因此在了解 ‘\’ 对JSON影响之前,我们先来了解 ‘\’ 对字符串的影响。这里特别要强调的是在javascript中 ‘ 和 ” 在表示字符串时是等价,比如’a’ = “a”

众所周知,由于String数据类型包含转义字符,比如 ‘\n’表示换行,’\b’表示空格等,因此用表示 he say “hello” 这个字符串时,需要用 ‘\’ 这个特殊字符来表示,即”he say \”hello \”” 。在chrome的中console.log()即可明显的看出。

而JSON.parse()又是对字符串的真实含义进行解析,要表示 \ 必须要用”\\\\”表示,具体见下图:

在json中出现’\’时,必须万分小心,这的确是json中最容易出错的地方

在顺便提一个小插曲:当我知道在json解析时需要用”\\\\”时表示”\”时,你用JSON.parse(‘{“a”:”a\\b”}’), 竟然没有报错,难道我理解错了,细心的朋友应该看出来了,’\b’本身就是个转义字符,因此这里的第一个’\’是用来转义第二个’\’ ,这样字符串就编程’a\b'(‘\b’是个转义字符),所以JSON.parse()还是可以顺利解析的。

对这个概念还是有点绕的,读者需要多想想,自己当时也是花了较长的时间来想这个问题。

补充资料:

线上解析json网站http://json.parser.online.fr/

[转载]Android服务之Service(其一)

mikel阅读(974)

[转载]Android服务之Service(其一) – 东子哥 – 博客园.

Android中服务是运行在后台的东西,级别与activity差不多。既然说service是运行在后台的服务,那么它就是不可见的,没有界面 的东西。你可以启动一个服务Service来播放音乐,或者记录你地理信息位置的改变,或者启动一个服务来运行并一直监听某种动作。

Service和其他组件一样,都是运行在主线程中,因此不能用它来做耗时的请求或者动作。你可以在服务中开一一个线程,在线程中做耗时动作。

那么究竟Service怎么使用呢?

老规矩,先来点基础知识。

一.基础知识

服务一般分为两种:

1:本地服务 Local Service 用于应用程序内部。在Service可以调用Context.startService()启动,调用Context.stopService()结束。 在内部可以调用Service.stopSelf() 或 Service.stopSelfResult()来自己停止。无论调用了多少次startService(),都只需调用一次 stopService()来停止。

2:远程服务, Remote Service 用于Android系统内部的应用程序之间。可以定义接口并把接口暴露出来,以便其他应用进行操作。客户端建立到服务对象的连接,并通过那个连接来调用服 务。调用Context.bindService()方法建立连接,并启动,以调用 Context.unbindService()关闭连接。多个客户端可以绑定至同一个服务。如果服务此时还没有加载,bindService()会先加 载它。
提供给可被其他应用复用,比如定义一个天气预报服务,提供与其他应用调用即可。

那么先来看Service的生命周期吧:如图:


context.startService() ->onCreate()- >onStart()->Service running–调用context.stopService() ->onDestroy()

context.bindService()->onCreate()->onBind()->Service running–调用>onUnbind() -> onDestroy() 从上诉可以知道分别对应本地的,,以及远程的,也对应不同的方式启动这个服务。

二.实战

我们可以定义一个本地服务继承Service,然后在这个服务里播放媒体播放器或者记录地理位置变化。通常有时候我们的Service 要与Activity交互,那么可以可以定义一个内部类,返回这个Service,当然我们要考虑到如果是以绑定方式启动服务,那么内部类可以定义为继承 Binder,然后返回本地服务,具体代码如下。

View Code

package com.dongzi;

import android.app.Service;
import android.content.Intent;
import android.media.MediaPlayer;
import android.os.Binder;
import android.os.IBinder;
import android.util.Log;

public class LocalService extends Service {

    private static final String TAG = "LocalService"; 
    private IBinder binder=new LocalService.LocalBinder();
    
    @Override
    public IBinder onBind(Intent intent) {
         
        return binder;
    }
    MediaPlayer mediaPlayer=null;
    @Override 
    public void onCreate() { 
            Log.i(TAG, "onCreate"); 
            //这里可以启动媒体播放器
           // if(mediaPlayer==null)
           //     mediaPlayer=MediaPlayer.create(this, uri);
            super.onCreate(); 
    } 

    @Override 
    public void onStart(Intent intent, int startId) { 
            Log.i(TAG, "onStart"); 
            super.onStart(intent, startId); 
    } 

    @Override 
    public int onStartCommand(Intent intent, int flags, int startId) { 
          Log.i(TAG, "onStartCommand"); 
        return START_STICKY;
    }

    
    
    @Override 
    public void onDestroy() { 
            Log.i(TAG, "onDestroy"); 
            super.onDestroy(); 
    } 

    
    //定义内容类继承Binder
    public class LocalBinder extends Binder{
        //返回本地服务
        LocalService getService(){
            return LocalService.this;
        }
    }
    
    
}

我们可以从上面知道

//定义内容类继承Binder
public class LocalBinder extends Binder{
//返回本地服务
LocalService getService(){
return LocalService.this;
}
}

可以返回这个服务,然后activity可以通过服务调用服务的方法了。

那么如何启动服务呢?从上面基础知识中,我们知道有2种方法,如下:

View Code

  //启动服务
    private void startCustomService(){
         Intent intent=new Intent(this,LocalService.class);
         startService(intent);
    }

第2种绑定方式:

View Code

LocalService localService=null;
    //用bindService方法启动服务
    private void BinderService(){
         Intent intent=new Intent(this,LocalService.class);
         bindService(intent, new ServiceConnection(){
            @Override
            public void onServiceConnected(ComponentName componentName, IBinder binder) {
                //调用bindService方法启动服务时候,如果服务需要与activity交互,
                //则通过onBind方法返回IBinder并返回当前本地服务
                localService=((LocalService.LocalBinder)binder).getService();
                //这里可以提示用户,或者调用服务的某些方法
            }

            @Override
            public void onServiceDisconnected(ComponentName componentName) {
                localService=null;
                //这里可以提示用户
            }     
         }, Context.BIND_AUTO_CREATE);
    }

在绑定服务的时候,需要一个服务连接对象,ServiceConnection,服务一旦连接,就会调用 onServiceConnected方法,我们可以在这个方法里面返回我们的本地服务对象,具体看代码;而在服务断开时候会调用 onServiceDisconnected方法,我们可以清理一些服务资源。

[转载]Java的动态绑定机制

mikel阅读(924)

[转载]Java的动态绑定机制 – eric.dream – 博客园.

Java的动态绑定又称为运行时绑定。意思就是说,程序会在运行的时候自动选择调用哪儿个方法。

一、动态绑定的过程:

例子:

1 public class Son extends Father
2 Son son = new Son();
3 son.method();

1. 首先,编译器根据对象的声明类型和方法名,搜索相应类(Son)及其父类(Father)的“方法表”,找出所有访问属性为public的method方法。

可能存在多个方法名为method的方法,只是参数类型或数量不同。

2. 然后,根据方法的“签名”找出完全匹配的方法。

方法的名称和参数列表称为方法的签名。

在Java SE 5.0 以前的版本中,覆盖父类的方法时,要求返回类型必须是一样的。现在子类覆盖父类的方法时,允许其返回类型定义为原始类型的子类型。

1 public Father getFather(){...}  //父类中的方法
2 public Son getFather(){...}     //子类覆盖父类中的getFather()方法

3. 如果是private、static、final 方法或者是构造器,则编译器明确地知道要调用哪儿个方法,这种调用方式成为“静态调用”。

4. 调用方法。

如果子类Son中定义了 method() 的方法,则直接调用子类中的相应方法;如果子类Son中没有定义相应的方法,则到其父类中寻找method()方法。

二、Demo

1. 子类重写父类中的方法,调用子类中的方法

 1 public class Father{
 2     public void method(){
 3         System.out.println("父类方法:"+this.getClass());
 4     }
 5 }
 6 public class Son extends Father{
 7     public void method(){
 8        System.out.println("子类方法"+this.getClass());
 9     }
10     public static void main(String[] args){
11         Father instance = new Son();
12         instance.method();
13     }
14 }
15 //结果:子类方法:class Son

2. 子类没有重写父类中的方法,所以到父类中寻找相应的方法

public class Father{
    public void method(){
        System.out.println("父类方法:"+this.getClass());
    }
}
public class Son extends Father{
    public static void main(String[] args){
        Father instance = new Son();
        instance.method();
    }
}
//结果:父类方法:class Son

三、动态绑定只是针对对象的方法,对于属性无效。因为属性不能被重写。

 1 public class Father{
 2     public String name = "父亲属性";
 3 }
 4 public class Son extends Father{
 5     public String name = "孩子属性";
 6 
 7     public static void main(String[] args){
 8         Father instance = new Son();
 9         System.out.println(instance.name);
10     }
11 }
12 //结果:父亲属性