最近云计算,云存储炒的是热火朝天,本蛙也来凑个热闹,和大家一起来DIY一个云存储服务。像live mesh目前就是个网络的OS,能把本机的东西存上去,也不是结构化的,我觉得云存储最好能够存储结构化的数据,而且管理起来要像数据库一样灵活。这样人人都可以把自己的备忘录,联系人信息放在自己的云存储服务里,并且方便的访问。


蛙蛙推荐:蛙蛙牌云存储服务

摘要:最近云计算,云存储炒的是热火朝天,本蛙也来凑个热闹,和大家一起来DIY一个云存储服务。像live mesh目前就是个网络的OS,能把本机的东西存上去,也不是结构化的,我觉得云存储最好能够存储结构化的数据,而且管理起来要像数据库一样灵活。这样人人都可以把自己的备忘录,联系人信息放在自己的云存储服务里,并且方便的访问。

项目网址:​​http://www.codeplex.com/wawcloudstoreservice/​

思路:

1、对已通过身份验证的用户每人分配一个目录,在这个目录里动态创建sqliet数据库

2、提供一套webservice接口来让用户创建数据结构,添加、获取及更新数据。

当然有人说了,人家google,亚马逊,ms的云存储多NB呀,自动负载均衡,高可靠性,速度又快,你这算啥呀,的确,我没有经过压力测试,不过据说sqlite的性能还不错,当然没有bigtable和hoodlop那些玩意牛了,本文主要是提供一种思路,接口有了,以后的实现可以慢慢来,对吧,以后慢慢来提高性能和稳定性,如果你做一个云存储服务一天就几百个人用,我觉得用sqlite都有点浪费,一切从需求出发。

困了,直接上代码算了,关于一些问题,咱们评论里再讨论

接口如下

namespace WasaSoft.StoreService
{
public enum DataStoreType
{
Null,
Integer,
Real,
Text,
Blob,
IdentityInteger,
}

public class DictionayItem<T,V>
{
public T Key;
public V Value;
}
[WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1, Name = "IDataStoreService", Namespace = "http://pim.fetionmm.com/IDataStoreService/")]
public interface IDataStoreService
{
[WebMethod(Description = "创建库")]
string CreateDataBase();
[WebMethod(Description = "创建表")]
string CreateTable(string tableName, List<DictionayItem<string, DataStoreType>> cols);
[WebMethod(Description = "增加数据")]
string Add(string tableName, List<DictionayItem<string,string>> data);
[WebMethod(Description = "获取所有数据")]
string GetAll(string tableName,List<string> cols, string orderCol, bool isDesc);
[WebMethod(Description = "获取详细信息")]
string GetDetail(string tableName, string identityCol, string colValue);
[WebMethod(Description = "删除一条数据")]
string DeleteOne(string tableName, string identityCol, string colValue);
[WebMethod(Description = "批量删除数据")]
string DeleteSome(string tableName, string identityCol, List<string> identityCols);
[WebMethod(Description = "清空表")]
string Clear(string tableName);
[WebMethod(Description = "更新数据")]
string Update(string tableName,string identityCol, string colValue, List<DictionayItem<string, string>> data);
}
}

蛙蛙推荐:蛙蛙牌云存储服务_sql蛙蛙推荐:蛙蛙牌云存储服务_sql

蛙蛙推荐:蛙蛙牌云存储服务_sql


实现如下

蛙蛙推荐:蛙蛙牌云存储服务_sql蛙蛙推荐:蛙蛙牌云存储服务_sql

[WebService(Namespace = "http://tempuri.org/")]
[WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
[ToolboxItem(false)]
public class StoreService : WebService, IDataStoreService
{
private const string DbName = @"E:\huhao\project\WawaCloudStoreService\StoreService\bin\test.db3";

IDataStoreService 成员#region IDataStoreService 成员

public string CreateDataBase()
{
if (!File.Exists(DbName)) return "出错";
File.Delete(DbName);
SQLiteConnection.CreateFile(DbName);
return "ok";
}

public string CreateTable(string tableName, List<DictionayItem<string, DataStoreType>> cols)
{
DbProviderFactory factory = SQLiteFactory.Instance;
using (DbConnection conn = factory.CreateConnection())
{
conn.ConnectionString = string.Format("Data Source={0}", DbName);
conn.Open();

StringBuilder builder = new StringBuilder();
int i = 1;
foreach (DictionayItem<string, DataStoreType> col in cols)
{
string colType = col.Value == DataStoreType.IdentityInteger
? "INTEGER PRIMARY KEY"
: col.Value.ToString();
builder.AppendFormat("{0} {1}{2}", col.Key, colType, i++ == cols.Count ? "" : ",");
}

string sql = string.Format("create table [{0}] ({1})", tableName, builder);
DbCommand cmd = conn.CreateCommand();
cmd.Connection = conn;
cmd.CommandText = sql;
cmd.ExecuteNonQuery();

return sql;
}
}

public string Add(string tableName, List<DictionayItem<string, string>> data)
{
DbProviderFactory factory = SQLiteFactory.Instance;
using (DbConnection conn = factory.CreateConnection())
{
conn.ConnectionString = string.Format("Data Source={0}", DbName);
conn.Open();
DbCommand cmd = conn.CreateCommand();

StringBuilder builder = new StringBuilder();
StringBuilder builder2 = new StringBuilder();
for (int i = 0; i < data.Count; i++)
{
builder.AppendFormat("{0} {1}", data[i].Key, (i + 1) == data.Count ? "" : ",");
builder2.AppendFormat("{0} {1}", "?", (i + 1) == data.Count ? "" : ",");
cmd.Parameters.Add(cmd.CreateParameter());
}

string sql = string.Format("insert into {0} ({1})values({2})", tableName, builder, builder2);


for (int i = 0; i < data.Count; i++)
{
cmd.Parameters[i].Value = data[i].Value;
}
cmd.Connection = conn;
cmd.CommandText = sql;
cmd.ExecuteNonQuery();

return sql;
}
}

public string GetAll(string tableName, List<string> cols, string orderCol, bool isDesc)
{
DbProviderFactory factory = SQLiteFactory.Instance;
using (DbConnection conn = factory.CreateConnection())
{
StringBuilder builder = new StringBuilder();
for (int i = 0; i < cols.Count; i++)
{
builder.AppendFormat("{0} {1}", cols[i], (i + 1) == cols.Count ? "" : ",");
}
string sql = string.Format("select {0} from {1} ORDER BY {2} {3}", builder, tableName, orderCol,
isDesc ? "desc" : "asc");

SQLiteDataAdapter dataAdapter = new SQLiteDataAdapter(sql, string.Format("Data Source={0}", DbName));
DataSet ds = new DataSet();
dataAdapter.Fill(ds);

StringWriter sw = new StringWriter();
ds.WriteXml(sw);
return sw.ToString();
}
}

public string GetDetail(string tableName, string identityCol, string colValue)
{
DbProviderFactory factory = SQLiteFactory.Instance;
using (DbConnection conn = factory.CreateConnection())
{
conn.ConnectionString = string.Format("Data Source={0}", DbName);
conn.Open();
DbCommand cmd = conn.CreateCommand();
string sql = string.Format("select * from {0} where {1}=? limit 0,1", tableName, identityCol);
cmd.Connection = conn;
cmd.CommandText = sql;
cmd.Parameters.Add(cmd.CreateParameter());
cmd.Parameters[0].Value = colValue;
SQLiteDataAdapter dataAdapter = new SQLiteDataAdapter((SQLiteCommand) cmd);
DataSet ds = new DataSet();
dataAdapter.Fill(ds);

StringWriter sw = new StringWriter();
ds.WriteXml(sw);
return sw.ToString();
}
}

public string Update(string tableName, string identityCol, string colValue,
List<DictionayItem<string, string>> data)
{
DbProviderFactory factory = SQLiteFactory.Instance;
using (DbConnection conn = factory.CreateConnection())
{
conn.ConnectionString = string.Format("Data Source={0}", DbName);
conn.Open();
DbCommand cmd = conn.CreateCommand();

StringBuilder builder = new StringBuilder();
for (int i = 0; i < data.Count; i++)
{
builder.AppendFormat("{0}=? {1}", data[i].Key, (i + 1) == data.Count ? "" : ",");
cmd.Parameters.Add(cmd.CreateParameter());
}

string sql = string.Format("update {0} set {1} where {2}=?", tableName, builder, identityCol);

for (int i = 0; i < data.Count; i++)
{
cmd.Parameters[i].Value = data[i].Value;
}

cmd.Parameters.Add(cmd.CreateParameter());
cmd.Parameters[data.Count].Value = colValue;

cmd.Connection = conn;
cmd.CommandText = sql;
cmd.ExecuteNonQuery();

return sql;
}
}

public string DeleteOne(string tableName, string identityCol, string colValue)
{
DbProviderFactory factory = SQLiteFactory.Instance;
using (DbConnection conn = factory.CreateConnection())
{
conn.ConnectionString = string.Format("Data Source={0}", DbName);
conn.Open();
DbCommand cmd = conn.CreateCommand();
string sql = string.Format("delete from {0} where {1}=?", tableName, identityCol);
cmd.Connection = conn;
cmd.CommandText = sql;
cmd.Parameters.Add(cmd.CreateParameter());
cmd.Parameters[0].Value = colValue;
cmd.ExecuteNonQuery();
return sql;
}
}

public string DeleteSome(string tableName, string identityCol, List<string> identityCols)
{
foreach (string s in identityCols)
{
DeleteOne(tableName, identityCol, s);
}
return "ok";
}

public string Clear(string tableName)
{
DbProviderFactory factory = SQLiteFactory.Instance;
using (DbConnection conn = factory.CreateConnection())
{
conn.ConnectionString = string.Format("Data Source={0}", DbName);
conn.Open();
DbCommand cmd = conn.CreateCommand();
string sql = string.Format("delete from {0}", tableName);
cmd.Connection = conn;
cmd.CommandText = sql;
cmd.ExecuteNonQuery();
return sql;
}
}



#endregion
}

蛙蛙推荐:蛙蛙牌云存储服务_sql



单元测试如下,其中StoreService.StoreService是web服务的代理类

class Program
{
static void Main(string[] args)
{
try
{
CreateDatabase#region CreateDatabase
StoreService.StoreService service = new StoreService.StoreService();
Console.WriteLine(service.CreateDataBase());
Console.WriteLine("创建数据库成功");
#endregion

CreateTable#region CreateTable
List<DictionayItemOfStringDataStoreType> cols = new List<DictionayItemOfStringDataStoreType>();
DictionayItemOfStringDataStoreType item = new DictionayItemOfStringDataStoreType();
item.Key = "id";
item.Value = DataStoreType.IdentityInteger;
cols.Add(item);

item = new DictionayItemOfStringDataStoreType();
item.Key = "name";
item.Value = DataStoreType.Text;
cols.Add(item);

item = new DictionayItemOfStringDataStoreType();
item.Key = "password";
item.Value = DataStoreType.Text;
cols.Add(item);

Console.WriteLine(service.CreateTable("mytable", cols.ToArray()));
Console.WriteLine("创建表成功");
#endregion

AddData#region AddData
List<DictionayItemOfStringString> toAddData = new List<DictionayItemOfStringString>();

DictionayItemOfStringString dataItem = new DictionayItemOfStringString();
dataItem.Key = "name";
dataItem.Value = "蛙蛙王子";
toAddData.Add(dataItem);

dataItem = new DictionayItemOfStringString();
dataItem.Key = "password";
dataItem.Value = "www.cnblogs.com";
toAddData.Add(dataItem);

Console.WriteLine(service.Add("mytable", toAddData.ToArray()));

toAddData[0].Value = "谁染枫林醉";
toAddData[1].Value = "www.fetionmm.com";
Console.WriteLine(service.Add("mytable", toAddData.ToArray()));

toAddData[0].Value = "onlytiancai";
toAddData[1].Value = "www.fetionmm.com";
Console.WriteLine(service.Add("mytable", toAddData.ToArray()));

Console.WriteLine("添加数据成功");
#endregion

GetAll#region GetAll
Console.WriteLine(service.GetAll("mytable",new string[]{"name"}, "id", false));
Console.WriteLine("获取所有数据成功");
#endregion

GetDetails#region GetDetails
Console.WriteLine(service.GetDetail("mytable", "id", "1"));
Console.WriteLine("获取详细数据成功");
#endregion

Update#region Update
List<DictionayItemOfStringString> toUpdateData = new List<DictionayItemOfStringString>();
dataItem = new DictionayItemOfStringString();
dataItem.Key = "password";
dataItem.Value = "mm.fetionmm.com";
toUpdateData.Add(dataItem);
Console.WriteLine(service.Update("mytable", "id", "1", toUpdateData.ToArray()));
Console.WriteLine(service.GetAll("mytable", new string[] { "id","password" }, "id", false));
Console.WriteLine("更新数据成功");
#endregion

DeleteOne#region DeleteOne
Console.WriteLine(service.DeleteOne("mytable", "id", "1"));
Console.WriteLine(service.GetAll("mytable", new string[] { "id", "password" }, "id", false));
Console.WriteLine("删除一条数据成功");
#endregion

DeleteSome#region DeleteSome
Console.WriteLine(service.DeleteSome("mytable", "id", new string[] { "2" }));
Console.WriteLine(service.GetAll("mytable", new string[] { "id", "password" }, "id", false));
Console.WriteLine("删除多条数据成功");
#endregion

Clear#region Clear
Console.WriteLine(service.Clear("mytable"));
Console.WriteLine(service.GetAll("mytable", new string[] { "id", "password" }, "id", false));
Console.WriteLine("清空数据成功");
#endregion


}
catch (Exception ex)
{
Console.Write(ex);
}
Console.ReadKey();
}
}

蛙蛙推荐:蛙蛙牌云存储服务_sql

相关链接(依旧不是自动链接,要想打开链接,自己复制到地址栏吧)

Setting a breakpoint in managed code using Windbg

​http://blogs.msdn.com/kristoffer/archive/2007/01/02/setting-a-breakpoint-in-managed-code-using-windbg.aspx​​​

[转]如何高效使用SQLite .net (C#)

Sqlite.net 2.0使用笔记

​http://www.sqlitechina.org/html/1/20071221/70.html​​​

​http://extrafliu.blogspot.com/search/label/Sqlite.net%202.0使ç¨ç¬è®°​

SQLite 第三版中的数据类型

​http://www.sqlite.com.cn/MySqlite/5/127.Html​​​

sqlite3数据类型的请教  

​http://sourceforge.net/project/showfiles.php?group_id=132486​

SQLite不支持的SQL语法总结

​http://www.sqlite.com.cn/MySqlite/3/250.Html​​​

101个Google技巧 - Google技巧的终极收集

​http://www.cnbeta.com/articles/64073.htm​

云计算

​http://www.hudong.com/wiki/%E4%BA%91%E8%AE%A1%E7%AE%97​​​

云中漫步——迎接云计算时代的到来

​http://www.googlechinablog.com/2008/05/blog-post_09.html​​​

《互联网周刊》:“云计算”的容量

​http://www.cnbeta.com/articles/64169.htm​​​

Google App Engine 初体验

​http://www.javaeye.com/news/2066​​​

Google App Engine入门

​http://www.webfuny.cn/2008/08/09/app-engine-g.html​​​

补充

这个服务写的很简单,要想真的投入使用,还要考虑很多东西

1、身份验证:web服务的身份验证一般用单独的soap header来做,可以把从单点登录获取的凭证放在soap header里,服务端取出凭证,验证凭证,获取用户信息,并找到用户的profile,比如用户的数据库路径在哪里,是否是VIP用户,数据库配额是多少等等。关于基于令牌的web服务身份验证,可以看以下链接,当然这个方案很简单,没有使用对称加密等技术,关于SSO大家可以搜索一下相关帖子。

2、安全限制:安全在这里包括多方面,比如一个用户频繁的调用一个耗费资源很大的服务,或者传入一个超级大的参数,或者从数据库里select了10M的数据,或者由于杀毒软件查杀数据库是否有病毒,访问数据库的众多线程被hang住,造成IIS线程池耗尽等,因为这些,所以要在web服务外层加一个安全控制服务,控制用户的流量,以及细化代码,找出可能引擎服务器长时间执行的请求,把它拒绝掉。其它的sql语句等用户爱怎么写怎么写,反正数据库是它自己的,弄坏了也怪他自己。然后还可以考虑给用户增加一些磁盘配额等措施。

3、异常处理:本文例子没有进行任何异常处理,web service用soap exception来处理错误,所以在服务端出现异常后要包装成soap exception返回给用户,关于这种操作,博客园有多人说过。

4、可靠性:用无数的sqliet数据库来保存用户信息,当然不是最可靠的方式,以后再考虑一些容错的方案,比如用开源的hadoop等。但是用户多了,可以用多台机器来存储用户信息,前段收到用户请求后根据用户名的哈希值去寻找他的数据应该从哪台存储服务器上去读取,或者直接302重定向到另一台前端服务器上,这也算是负载均衡了。

5、性能:考虑在前端做一些缓存服务,尤其是对读操作,设计良好的读缓存能很有效的提高服务器性能,因为大多数请求时读操作,写缓存也可以考虑,看写请求的量来评估了,网上也有不少开源的缓存服务组件,Memcache,java的oscache,微软的企业库里的缓存块等,大家可以研究下用起来。


6、前端展现:光有了服务不行,要想让用户使,还得有个界面,web服务自身的特性让它可以和多种平台交互,所以我们可以为这个服务做多界面的应用,用delphi,c#,python甚至yui+google gear,只要能访问web服务,都可以做一个界面来使用我的云存储服务。当然app engine最大的特点就是有调用web服务的API,大家做app engine应用的时候可以考虑用下俺的云存储服务。

我知道live mesh要把live id,live contacts,live mail,live desktop,live office都要集成到一起,微软内部还有reddog, BitVault等项目来对抗开源和google的map/reduce,hadoop,bigtable,gfs等东西的。我发此帖的意思是不是只有大 公司才能推出云这个云那个,自己有好的想法也能去实现去,现在云计算,云存储等概念都不成熟,甚至salesforce等托管CRM厂商也说自己是云计算 服务,还有adobe的Adobe Share和ms的windows live skydrive就是一个网络硬盘,也成为云存储,那其它的AllMyData、Box.net、eSnips、 Freepository、GoDaddy、iStorage、Mofile、 Mozy、Omnidrive、Openomy、Streamload、 Strongspace以及Xdrive等更是云存储了,我觉得这些东西本质上和FTP有啥区别呀,就是弄了一套rest api或者一个ajax ui,用户也就是在上面存储一些文档文件啥的,存结构化数据不方便(当然上面列举的一些产品和服务我没有一一进行详细的了解,是看资料了解的,有些可能说 的不对。)。其它还有sun的Hydrazine计划及Insight计划,亚马逊的EC2云计算服务,EBS云存储服务,IBM的蓝云计划等,当然大公 司争的是企业市场,我们自己做云服务就针对个人来展开了,或者是把某一点做精。


参考:

云计算井喷

​http://server.zdnet.com.cn/server/2008/0422/825478.shtml ​

云计算——IBM的未来策略

​ http://laiba.tianya.cn/laiba/CommMsgs?cmm=14452&tid=2592400139121650923​

IBM云计算数据中心

​ http://www.freerainbow.cn/2008/08/03/ibm-yunjisuan.html​

亚马逊发力企业云计算市场

​http://www.builder.com.cn/2008/0415/815771.shtml​

Sun实施云计算Insight 计划挑战微软Live Mesh

​http://www.cnzz.cn/NewsInfo/6180.aspx​​​