2019年10月31日 By mikel 分类: 数据库

来源: MSSQl分布式查询 – chenkai – 博客园

MSSQLServer所谓的分布式查询(Distributed Query)是能够访问存放在同一部计算机或不同计算机上的SQL Server或不同种类的数据源, 从概念上来说分布式查询与普通查询区别 它需要连接多个MSSQL服务器也就是具有多了数据源.实现在服务器跨域或跨服务器访问. 而这些查询是否被使用完全看使用的需要.

 

本篇将演示利用SQLExpress链接远程SQLServer来获取数据方式来详细说明分布式查询需要注意细节.先看一下系统架构数据查询基本处理:

memcached

 

 

 

 

 

 

 

 

 

 

 

 

 

当然如果采用了分布式查询 我们系统采取数据DataBase也就可能在多个远程[Remote Server]上访问时:

2010-09-08_181001

 

 

 

 

 

 

 

 

 

 

 

 

如上截取系统架构中关于数据与缓存流向中涉及的分布式查询业务, 当我们从客户端Client发起请求数据时. 首先检查MemCache Server缓存服务器是否有我们想要数据. 如果没有我需要查询数据库.  而此时数据要求查询多个远程服务器上多个数据库中表, 这时利用分布式查询.获得数据 然后更新我们在缓存服务器MemCache Server上数据保持数据更新同步, 同时向客户端Client直接返回数据.那如何来执行这一系列动作中最为关键分布式查询?

<1>分布式查询方式

我们知道Microsoft微软公用的数据访问的API是OLE_DB, 而对数据库MSSQlServer 2005的分布式查询支持也是OLE_DB方式.SQL Server 用户可以使用分布式查询访问以下内容:

A:存储在多个 SQL Server 实例中的分布式数据

B:存储在各种可以使用 OLE DB 访问接口访问的关系和非关系数据源中的异类数据

OLE DB 访问接口将在称为行集的表格格式对象中公开数据。SQL Server 允许在 Transact-SQL 语句中像引用 SQL Server 表一样引用 OLE DB 访问接口中的行集,[其实不用关心这个行集概念 它的功能类似SQl中临时表 不过它容积更大 能容纳类型更多 更丰富]

SQL Server 实例的客户机与 OLE DB 访问接口之间的连接 如下图:

2010-09-08_182420

 

 

 

 

 

 

 

 

上图可以看出.客户端借助OLEDB接口可以访问Oracle/MS Jet/MS SQL/ODBC/第三方等这些丰富数据源来我们分布式查询提供数据. 说了这么多关于OLEDB底层支持. 关于在MSSQL2005中则支持两种方式来进行分布式查询:

<A>使用添加链接服务器方式(Add Link Server)

<B>使用特定名称及特定数据源来直接指定(Add Host Names)

 

其实这两种方式在实际运用中是有区别的:

方式A:Add Link Server方式建立服务器之间关联.创建一个链接的服务器,使其允许对分布式的、针对 OLE DB 数据源的异类查询进行访问. 一般适用于持久的数据操作 对于数据量偏大 服务器之间交付时间长特点.

方式B: Add Host Name 利用域来唯一识别数据库以及数据库表对象. 来实现跨服务器访问. 这种方式一般比较简单 主要适用于对数据需求临时性查询是使用偏多. 不适合做大批量数据提取. 有性能瓶颈.

<2>分布式查询实现

 

在进行实现分布式查询之前.本次测试Demo对应的SQL版本:

2010-09-09_110812

 

 

 

 

 

 

 

确定SQLServer版本后如下会演示两种方式来实现分布式查询,并对Distributed Query中详细细节进行说明.

<2.1>链接服务器查询

链接服务器配置使 SQL Server 可以对远程服务器上的 OLE DB 数据源执行命令。链接服务器具有以下优点:

  1. 访问远程服务器。
  2. 能够对企业内的异类数据源发出分布式查询、更新、命令和事务。
  3. 能够以相似的方式确定不同的数据源

下图显示了链接服务器配置的基础:

IC99086

 

 

 

 

 

 

 

 

 

 

现在利用链接服务器方式实现数据访问远程服务器数据库CustomerDB中Users表数据先本地添加LinkServer:

   1:  -- 建立连接服务器  第一步建立连接  IP方式来控制
   2:  
   3:  EXEC sp_addlinkedserver   '192.168.10.104' , 'SQL Server'
   4:  
   5:  -- 查看链接服务器信息  [测试连接成功]
   6:  
   7:  select name , product, provider, data_source, query_timeout, lazy_schema_validation, is_remote_login_enabled, is_rpc_out_enabled
   8:  from sys.servers
   9:  where is_linked= 1

 

如上市建立连接服务器最简单方式.建立链接服务器过程其实调用了系统存储过程Sp_addlinkedserver. 第一个参数为Name 其实用来唯一标识链接服务器. 当然可以其他任何有意义字符串来定义,但我个人建议使用远程服务器的IP来标识.第二个参数是要添加为链接服务器的 OLE DB 数据源的产品名称. 默认为Null,如果指定”SQlServer“则无需指定其他参数.

如果你的本地装有多个数据库实例. 第一个种方式就不适用.这是就需要用SQl2005架构来唯一标识:

   1:  -- 含架构名  查询数据两种模式

   3:  select top 10 * from [192.168.10.104]. wl . 架构名 . 表名

   5:  -- 架构名 [采用默认架构名 ]

   7:  select top 10 * from [192.168.10.104]. CustomerDB . dbo. Users

 

对于Sql2005架构这个概念很多人比较陌生:

架构是形成单个命名空间的数据库实体的集合。命名空间是一个集合,其中每个元素的名称都是唯一的。 例如,为了避免名称冲突,同一架构中不能有两个同名的表。两个表只有在位于不同的架构中时才可以同名 例如本次Demo 在CustomerDB后对应DBO既是默认的架构名.

创建后.如果需要修改连接服务器属性可以通过sp_serveroption系统Proc来设置:

   1:  -- 配置链接服务器属性 sp_serveroption为远程服务器和链接服务器设置服务器选项
   2:  -- 语法  sp_serveroption [@server =] 'server',[@optname =] 'option_name',[@optvalue =] 'option_value'
   4:  exec sp_serveroption '192.168.10.104','name','192.168.10.104'
   6:  -- 查看连接服务器
   7:  select * from sys.servers

建立后我就可以直接来查询远程服务器上数据:

   1:  -- 查询远程服务器数据

   3:  select * from [192.168.10.104].CustomerDB.dbo.Users   --[成功]

   5:  -- sp_droplinkedsrvlogin 删除链接服务器登录名映射 [删除登录映射]
   6:  -- 如果为 NULL,那么将会删除由 sp_addlinkedserver 创建的默认映射 [第二个参数]
   8:  exec sp_droplinkedsrvlogin '192.168.10.104' ,NULL
  10:  -- 删除链接服务器属性 [删除服务器]
  12:  exec sp_dropserver 'mytest' --[删除成功 同时也删除了Sys_Server信息]
  14:  -- 查看服务器详细信息
  15:  EXEC sp_helpserver
查询结果:
 2010-09-09_120510

 

 

 

 

 

 

测试查询成功.远程数据成功获取.

当测试完成后我们不需要这个连接服务器是即可利用SP_DroplinkServer删除掉. 对应参数为创建时Name唯一标识. 通过Sp_helpserver来查看连接服务器详细信息.

注意如上创建连接服务器时设置srvproduct参数即OLED数据源名称时我们采用了SQlServer方式.

下面说明这种方式特点.:

这种方式是最为简单直接的一种建立链接服务器方式. 但是存在前提的. 测试发现:

在所有数据库的远程连接 dbo 的方式必须建立在 SA 密码相同的基础上 ,否则容易产生无法连接的情况 Sa用户登录失败. 你也就明白这个SQlServer参数其实就是在本地数据拷贝服务器角色SysAdmin下用户SA.来对服务器进行登录. 如果你的本地Sa密码与远程服务器上密码不一致 则无法正常连接.

经过测试还发现一种情况:

利用Windows7访问XP(Sp2)系统时始终提示无法解析或拒绝连接SQlServer2005.这个问题我整了好久后来才到官方链接参数中发现.:如果你的XP系统没有打上SP4的补丁包 这个问题会始终出现. 需要特别注意.

<2.2>直接指定数据源分布式查询

其实相对第一种方式, 直接指定方式在SQlServer架构中 其实跳过本地与远程服务器建立映射关系的这一步. 通过链接关系建立 其实就是建立一种内部映射关系. 如果没有映射关系则 大部分设置需要手动控制.

直接指定数据源方式 需要开启分布式查询的基本权限 来进行查询:

   2:  -- 如果想使用分布式查询,必须先开通分布式查询 [外围配置 这点是所有查询操作前提]
   3:  -- sp_configure--显示或更改当前服务器的全局配置设置
   4:  -- reconfigure 指定如果配置设置不需要服务器停止并重新启动,则更新当前运行的值
   5:  -- SQL2005默认是没有开启’Ad Hoc Distributed Queries’ 组件 
   6:  
   7:  -- 启用权限
   8:  exec sp_configure 'show advanced options',1  -- 显示高级配置
   9:  reconfigure -- 更新值
  10:  exec sp_configure 'Ad Hoc Distributed Queries',1 -- 启用分布式查询
  11:  reconfigure
  12:  go
  14:  
  15:  -- 关闭分布式查询
  16:  exec sp_configure 'Ad Hoc Distributed Queries',0
  17:  reconfigure
  18:  exec sp_configure 'show advanced options',0
  19:  reconfigure
  20:  go
  23:  -- 开启权限后 另外一种查询方式
  24:  -- 查询格式
  25:  SELECT * FROM OPENDATASOURCE(
  26:   'SQLOLEDB',
  27:   'Data Source=远程ip;User ID=sa;Password=密码'
  28:   ).库名.dbo.表名
  29:   WHERE 条件

  31:  -- 需要开启权限 
  32:  -- 开启权限 提示[远程的SqlServer不允许远程连接]
  34:  select * from OPENDATASOURCE('SQLOLEDB','Data Source=192.168.10.67; User ID=sa; Password=chenkai').wl.dbo.Users

开启权限后. 需要里利用ReConfig命令来确认.对目前分布式查询权限的修改. 如果在使用完分布式查询后注意关闭.最后查询结果:

2010-09-09_120510

 

 

 

 

 

 

测试成功.

有些人说使用数据库角色SysAdmin角色下的Sa用户进行远程数据传输和验证. 不安全. 其实在使用过程中应该不难看出. 在从远程服务器拉取数据库过程中. 本地数据库需要对权限,创建连接服务器都需要最大用户权限来操作. 而服务器呢, 只需要能连接上 同时对指定数据CustomerDB具有读写的权限即可. 当然你更多远程操作可以把用户赋予CustomerDB的OWner角色.

这时我们如何用非SA用户来来连接远程用户?

我们现在远程服务器上对连接创建一个用户名为Test的用户 服务器角色设置Public即可:

2010-09-09_131004

 

 

 

 

 

在用户角色设置中需要对指定访问数据CustomerDB具有读写权限:

2010-09-09_131120

 

 

 

 

 

 

 

在远程服务器创建TEst用户时使用SQlServer身份验证方式登录 这时设置密码为RemoteDB.在使用非Sa用户进行远程:

   1:  -- 执行前先删除已经存在数据
   2:  Exec sp_droplinkedsrvlogin [192.168.10.76],Null
   3:  Exec sp_dropserver 'demodb'
   4:  
   5:  -- 创建服务器连接
   6:  EXEC  sp_addlinkedserver
   7:        @server='demodb',-- 被访问的服务器别名 
   8:        @srvproduct='',
   9:        @provider='SQLOLEDB',
  10:        @datasrc='192.168.10.76'   -- 要访问的服务器
  12:  
  13:  EXEC sp_addlinkedsrvlogin
  14:       'demodb', -- 被访问的服务器别名
  15:       'false',
  16:       NULL,
  17:       'Test', -- 帐号
  18:       'RemoteDB' -- 密码

如上我们首先清除已经可能创建服务器数据记录. 然后创建服务器连接.sp_addlinkedSrvlogin系统存储过程用来创建链接服务器上远程登录之间的映射 . 即我们可以详细设置本地与远程服务器详细的映射信息. 例如设置我们特定用户访问的用户名和密码.

查询数据:

   1:  -- 查询指定用户Test数据
   2:  select * from [demodb].CustomerDB.dbo.Users -- [如上测试成功]
查询结果:
2010-09-09_120510

 

 

 

 

 

 

指定用户Test对CustomerDB访问数据方式测试成功.

<3>问题排查与更多查询方式

 

当我们在实际编程中进行访问远程数据时 因为不同操作环境会引发各种各样的异常,如下我会提出一种常见的异常方式解决办法和关于远程数据操作更多查询方式.

<3.1>无法建立远程连接

其实这个问题在做分布式查询时极其常见. 而引起这个问题的因素过多. 我们一时无法判断真正引发这个异常地方. 只能通过逐个排查方式来进行设置:

例如我们在建立关联关系后 进行查询时会遇到:

2010-09-09_133411

 

 

 

 

提示是: 在进行远程连接时超时, 引起这个问题原因可能是远程服务器积极拒绝访问!

首先要在Sql Server Configuation Manager中保证你服务已经运行 且是开机自动运行.

再次检查SQl2005外围配置DataBaseEngine允许远程连接:

2010-09-09_133756

 

 

 

 

 

 

 

 

 

 

 

 

 

 

设置完成后.我们还需要设置Sql Server Analysis Services分析服务也支持远程数据查询:

2010-09-09_134058

 

 

 

 

 

 

 

 

 

 

 

 

 

 

在远程服务器上如果启用了防火墙则可能对目前SQl Server方位实例进行拦截. 所以在服务器端启用防火墙情况下要为SQl DAtaBase创建例外.防止客户端请求被拦截.

<3.2>进程被其他用户占用

当我们在远程分布式查询中有创建动作或是类似创建一个新的数据库. 有时会提示 “该数据库无法操作 已经别其他进程占用”异常. 导致我们无法访问数据库. 或是执行我们要做的创建操作.

遇到这种情况我们可以利用SA权限查询到Master数据库对应数据库被占用的进程 并杀掉Kill Process.查询:

   1:  -- [sysprocesses 表中保存关于运行在 Microsoft® SQL Server™ 上的进程的信息。
   2:  -- 这些进程可以是客户端进程或系统进程。sysprocesses 只存储在 master 数据库中]
   4:  use Master
   5:  go

   7:  SELECT * FROM sysprocesses ,sysdatabases WHERE sysprocesses.dbid=sysdatabases.dbid AND sysdatabases.Name='CustomerDB'

   9:  select * from sysprocesses

  11:  select * from sysdatabases

  13:  -- 杀死占用进程
  14:  kill 5

 

当我们对进程占用清除时有可能访问数据库被系统进程占用. 则这时用Sa无法杀死.这时提示:

2010-09-09_134851

 

 

 

 

“Only use Process can be Kill ”在SQl2005 只有只有用户进程才能Kill掉.

<3.3>更多的查询操作

往往我们在实际操作中需要对数据读写有更多要求. 例如从远程连接多个服务器进行数据读取或是把本地数据提交到服务器上. 为了提高效率和性能采用分布式事务来进行批量操作等等. 如下简单介绍在分布式查询中多中数据操作:

把远程数据导入本地:

   1:  -- 导入数据操作
   2:  select top(3) * into TestDB.dbo.CopyDb from  [192.168.10.76].wl.dbo.Users

 

导入时使用Into方式 自动在本地创建CopyDB表完全复制远程服务器上Users表的数据结构.但是要注意在进行后 的CopyDB将不包含原表的主键和索引约束. 虽然能快构建 但是主键和索引设置都会丢失.

本地数据导入远程:

-- 把本地表导入远程表 [openWset方式]
 insert openrowset( 'SQLOLEDB ', 'sql服务器名 '; '用户名 '; '密码 ',数据库名.dbo.表名)  select *from 本地表 
-- 把本地表导入远程表 [open Query方式]
 insert openquery(ITSV, 'SELECT * FROM 数据库.dbo.表名 ')

更新本地表数据:

   1:  -- 把本地表导入远程表 [opendataSource方式]
   2:    insert opendatasource( 'SQLOLEDB ', 'Data Source=ip/ServerName;User ID=登陆名;Password=密码 ').数据库.dbo.表名
   3:  
   4:  -- 更新本地表 [openowset方式]
   5:   update b  set b.列A=a.列A  from openrowset( 'SQLOLEDB ', 'sql服务器名 '; '用户名 '; '密码 ',数据库名.dbo.表名)
   6:   as a inner join 本地表 b  on a.column1=b.column1

 

当然还有更多方式来操作分布式查询操作.各位都可以尝试.

 

<4>尾 语

 

如上是我最近在项目中处理关于分布式查询涉及到方方面面. 从系统架构到分部是查询具体操作细节.基本都是一些非常基础运用.当然也参考不少资料.以及动手来验证整个过程出现问题原因所在. 篇幅有限 写的有些仓促. 难免有纰漏地方 还望各位指正.

MSSQl分布式查询 – chenkai – 博客园已关闭评论
2019年10月31日 By mikel 分类: C#, 数据库

来源: SqlServer2008 跨服务器同步数据 – 小丑不戴面具 – 博客园

最近工作中需要跨服务器同步数据,在数据库DB1中的表T1插入数据,同时触发T1的触发器(这里暂不讨论触发器的效率问题),向另一台服务器DB2中的相同的一张表T2插入数据,查看了一些资料说,

需要打开DTC(分布式交易协调器)协调跨多个数据库、消息队列、文件系统等资源管理器的事务,

于是按照网上说的,将自己的DTC打开启动,步骤如下:

DTC位置:控制面板--管理工具--服务--Distributed Transaction Coordinator

然后在本机SQLServer里新建一个触发器测试了一下,提示“该伙伴事务管理器已经禁止了它对远程/网络事务的支持”

出现这个问题的原因是对方电脑没有配置好DTC,所以需要将另一台服务器也做如下配置:

两台服务器电脑做如下配置(windows2003系统):

2. 单击“添加/删除 Windows 组件”。
3. 选择“应用程序服务器”,然后单击“详细信息”。
4. 选择“启用网络 DTC 访问”,然后单击“确定”。
5. 单击“下一步”;单击“完成”。
6. 在”开始”->”运行”中输入dcomcnfg.exe启动”组件服务”。
7. 右键“我的电脑”->“属性”,在MSDTC选项卡中,点击“安全配置”按钮。
8. 在安全配置窗口中做如下设置:
(1)选中“网络DTC访问”
(2)在客户端管理中选中“允许远程客户端”“允许远程管理”
(3)在事务管理通讯中选“允许入站”“允许出站”“不要求进行验证”
(4)保证DTC登陆账户为:NT Authority\NetworkService
(5)单击”确定”。这样将会提示您”MS DTC 将会停止并重新启动。
所有的依赖服务将被停止。请按’是’继续”。单击”是”继续。

 

 

我当时先拿同事的电脑做了下测试,同事的系统是Win7,按上面的步骤没有找到“应用程序服务器”,需要按下面的方法操作:

Vista, Windows 7,Windows Server 2008 MSDTC配置
1. 打开“控制面板(Control Panel)―管理工具(Administrative Tools)―组件服务(Component
Service)”(或者开始-运行 “Dcomcnfg.exe”)

2. 打开“组件服务(Component Service)―计算机(Computers)”

3. 在“我的电脑(My Computer)”上点击右键,点击“属性(Properties)”

4. 在Local DTC Properties对话框中,点击“安全(Security)”选项卡。      在安全配置选项卡中做如下设置:

选中“网络DTC访问(Network DTC Access)”   在客户端管理(Client and Administration)中选中“允许远程客户端(Allow remote Clients)”
和“允许远程管理(Allow Remote Administration)”
在事务管理通讯(Transaction Manager Communication)中选“允许入站(Allow
Inbound)”“允许出站(Allow Outbound)”“不要求进行验证(No Authentication Required)”
保证DTC登陆账户为:NT Authority\Network Service

再次测试出现提示:“其他会话正在使用事务的上下文”,这是因为对方服务器表中也有触发器,需要进行一下判断。

********************************当两台服务器不在一个网段的时候,还有可能出现一个问题*******************************

如果两台服务器在一个网段的时候,按上面的设置,基本就可以搞定了,但是当两台服务器不在一个网段的时候,SQLServer还有可能报下面的错误:

链接服务的OLE DB 访问接“SQLNCLI10”返回了消息“该事务管理已经禁止了它对远程事务的支持”

然后我对要请求访问的服务器A做了以下操作,修改A服务器的host文件,增加B的IP地址和服务器名,然后问题就解决了。

 

下面给出测试的例子:

复制代码
--创建测试表
CREATE TABLE [dbo].[TB1](
    [id] [int] NULL,
    [age] [money] NULL
) ON [PRIMARY]

GO

--创建链接服务器 
exec sp_addlinkedserver   'it38 ', ' ', 'SQLOLEDB ', '172.16.3.38' --对方服务器地址
exec sp_addlinkedsrvlogin  'it38 ', 'false ',null, 'sa', 'sa' --对方服务器用户名密码
--删除创建的链接服务器时使用
--exec sp_dropserver  'it38 ', 'droplogins ' 
--为表TB1创建触发器
CREATE TRIGGER [dbo].[tr_qiao] on [dbo].[TB1]
FOR INSERT
AS
BEGIN
  SET NOCOUNT ON
  SET XACT_ABORT ON 
 INSERT INTO it38.[数据库名].dbo.TB1(id,age) 
  SELECT id, age FROM inserted;
END
GO

--***********************************************
--查询对方数据库表数据
SELECT * FROM it38.[数据库名].[dbo].[TB1]
--插入一条数据测试
INSERT INTO TB1(id,age) values(5,55);
复制代码

 

SqlServer2008 跨服务器同步数据 – 小丑不戴面具 – 博客园已关闭评论
2019年10月30日 By mikel 分类: C#

来源: c#中使用log4net工具记录日志 – 吃馒头的火鸡 – 博客园

首先,去官网下载log4net工具 链接http://logging.apache.org/log4net/download_log4net.cgi

目前最新的版本 log4net-1.2.15-bin-newkey.zip

下载之后,目录log4net-1.2.15-bin-newkey\log4net-1.2.15\bin\net\4.0\release 里面的log4net.dll就是我们要引用的dll了。

新建一个winform程序,添加引用

然后在程序配置文件App.conifg中加入配置,这里要申明的一点是,不是一定要把log4net的配置放在这个文件里,重新创建一个xml文件也是可以的,一会儿将在下面说到。

我配置得比较简单,需要进行复杂配置的朋友可以去参照他给的示例xml,里面有注释。配置文件App.config如下:

<?xml version=”1.0″ encoding=”utf-8″ ?>
<configuration>

<configSections>
<section name=”log4net” type=”log4net.Config.Log4NetConfigurationSectionHandler”/>
</configSections>
<startup>
<supportedRuntime version=”v4.0″ sku=”.NETFramework,Version=v4.5″ />
</startup>
<log4net>

<logger name=”ErrorLog”>
<level value=”ALL” />
<appender-ref ref=”ErrorAppender” />
</logger>
<appender name=”ErrorAppender” type=”log4net.Appender.RollingFileAppender”>
<file value=”log\\ErrorLog\\”/>
<appendToFile value=”true”/>
<rollingStyle value=”Date”/>
<staticLogFileName value=”false” />
<datePattern value=”_yyyyMMdd&quot;.txt&quot;”/>
<layout type=”log4net.Layout.PatternLayout”>
<conversionPattern value=”%-d{yyyy-MM-dd HH\:mm\:ss} – %p%n%m%n%n”/>
</layout>
</appender>
</log4net>
</configuration>

值得注意的是,一个log需要有一个对应的appender。appender中可以配置日志的格式啊,追加日志的方式啊等等。

然后有一点需要特别注意的就是。一定要在Properties中的AssemblyInfo.cs中添加一行:

[assembly: log4net.Config.XmlConfigurator(Watch = true)]

这里回答一下上面的问题。在这一行配置中,是可以配置log4net的配置文件位置的,如果像上面一样,就是程序默认的配置文件App.config中读取配置信息,如果你想写在其他地方,单独写一个xml,比如要写在最终生成的exe同级目录下的log4net.xml中,那么这一行就该这么写

[assembly: log4net.Config.XmlConfigurator(ConfigFile = “log4net.xml”, Watch = true)]

注意watch=true是必须的,否则程序就不会对这个进行监听,换个意思,这里可以做开关日志记录的操作。

然后配置完成。看效果。

我们添加一个按钮,直接来看效果:

然后在这个按钮的点击事件中:

private void button1_Click(object sender, EventArgs e)
{
log4net.ILog log = log4net.LogManager.GetLogger(“ErrorLog”);
log.Error(“日志测试”);
}

这里略加提醒,这个ErrorLog是在配置文件中配置的logger的name属性,不可以胡乱写,不然程序会找不到那个logger。另外,可以在配置文件中配置各种logger,很多logger,比如业务方面的,数据库方面的,错误信息,换一个logger名字和对应的Appender的名字就可以了。

然后点击按钮。按照代码,应该写一个错误日志,日志内容是“日志测试”,我们去看看结果。

从上图可以看到,在Debug目录新建了一个文件夹log,点击进去

这里这个文件夹就是在配置文件中配置的logger名称了,你配置了多个logger,这里就会有多个对应的文件夹,

这个日志文件的名字格式也是在对应的appender 中可以配置的,打开txt文件:

成功!!!同理,这里所有的格式,都是可以配置的。赶快去使用吧。

 

 

总结一下需要注意的点:

1、配置文件一个logger要有一个对应的appender,这个appender可以决定日志的名字,格式,写入方式等等。

2、配置好以后一定不要放了去Properties中的AssemblyInfo.cs中添加一行:[assembly: log4net.Config.XmlConfigurator(Watch = true)],如果你的配置不是写在程序默认配置文件中的,这里要设置好文件的路径,具体例子看上面正文。要关掉日志,Wacth=false即可。不用去程序中注释原来所有的日志代码。

c#中使用log4net工具记录日志 – 吃馒头的火鸡 – 博客园已关闭评论
2019年10月30日 By mikel 分类: C#

来源: [转]C#操作INI文件 – rainbow70626 – 博客园

在很多的程序中,我们都会看到有以.ini为后缀名的文件,这个文件可以很方便的对程序配置的一些信息进行设置和读取,比如说我们在做一个程序后台登陆的时候,需要自动登录或者是远程配置数据库连接,及保存密码设置等等(在Winform程序中),若在ASP.NET程序中有另外的解决方法,此C#操作INI文件的文章仅在winform程序中进行写入和读取操作。

为了方便起见,现在以一个简单的小实例来对C#操作INI文件进行讲解:

窗体的大致布局如下

当点击写入按钮的时候就会把文本框中输入的值写入到INI文件中,结果会如图所示

当点击读取按钮的时候就会把INI文件中的节点信息的值填充到窗体中的文本框中

以上就是用C#操作INI文件的整个流程,现在来介绍后台代码是怎样实现的:

在项目名称空间的上方要添加以下的引用:

using System.Runtime.InteropServices;//引用命名空间

然后再程序的后台声明一些系统函数的变量,代码如下

复制代码
声明变量

  #region "声明变量"

        /// <summary>
        /// 写入INI文件
        /// </summary>
        /// <param name="section">节点名称[如[TypeName]]</param>
        /// <param name="key">键</param>
        /// <param name="val">值</param>
        /// <param name="filepath">文件路径</param>
        /// <returns></returns>
        [DllImport("kernel32")]
        private static extern long WritePrivateProfileString(string section,string key,string val,string filepath);
        /// <summary>
        /// 读取INI文件
        /// </summary>
        /// <param name="section">节点名称</param>
        /// <param name="key">键</param>
        /// <param name="def">值</param>
        /// <param name="retval">stringbulider对象</param>
        /// <param name="size">字节大小</param>
        /// <param name="filePath">文件路径</param>
        /// <returns></returns>
        [DllImport("kernel32")]
        private static extern int GetPrivateProfileString(string section,string key,string def,StringBuilder retval,int size,string filePath);

        private string strFilePath = Application.StartupPath + "\\FileConfig.ini";//获取INI文件路径
        private string strSec =""; //INI文件名
         
        #endregion
复制代码

先说明下我的INI配置文件是放在程序的Debug文件夹下的,然后单击写入按钮,在写入前没有进行写入数值的验证,代码如下:

复制代码
写入事件

 //写入按钮事件
        private void btnWrite_Click(object sender, EventArgs e)
        {
            try
            {
 
                    //根据INI文件名设置要写入INI文件的节点名称
                    //此处的节点名称完全可以根据实际需要进行配置
                    strSec = Path.GetFileNameWithoutExtension(strFilePath);
                    WritePrivateProfileString(strSec, "Name", txtName.Text.Trim(), strFilePath);
                    WritePrivateProfileString(strSec, "Sex", txtSex.Text.Trim(), strFilePath);
                    WritePrivateProfileString(strSec, "Age", txtAge.Text.Trim(), strFilePath);
                    WritePrivateProfileString(strSec, "Address", txtAddress.Text.Trim(), strFilePath);
                    MessageBox.Show("写入成功");
             
            }catch(Exception ex){
                MessageBox.Show(ex.Message.ToString());
            
            }
        }
复制代码

 

此时运行此实例就会把数值写入到INI文件中,写入的结果就像第二个截图效果显示的那样。然后我们在单击读取按钮事件,把INI文件中的信息填充到窗体的文本框中,代码如下:

复制代码
读取事件

  //读取按钮事件
        private void btnRead_Click(object sender, EventArgs e)
        {
            if (File.Exists(strFilePath))//读取时先要判读INI文件是否存在
            {

                strSec = Path.GetFileNameWithoutExtension(strFilePath);
                txtName.Text = ContentValue(strSec, "Name");
                txtSex.Text = ContentValue(strSec, "Sex");
                txtAge.Text = ContentValue(strSec, "Age");
                txtAddress.Text = ContentValue(strSec, "Address");

            }
            else {

                MessageBox.Show("INI文件不存在");
            
            }
        }
复制代码

 

在读取的时候用到了自定义读取函数的方法,在该方法中调用了系统函数,

复制代码
}
      /// <summary>
      /// 自定义读取INI文件中的内容方法
      /// </summary>
      /// <param name="Section">键</param>
      /// <param name="key">值</param>
      /// <returns></returns>
        private string ContentValue(string Section,string key) {

            StringBuilder temp = new StringBuilder(1024);
            GetPrivateProfileString(Section, key, "", temp, 1024, strFilePath);
            return temp.ToString();
        }
复制代码

 

以上所述的就是简单的用C#语言操作INI文件的过程,只用到了系统函数中的两个(写入函数和读取函数)还有其他的函数比如说时删除INI文件函数等等,删除INI文件函数其实就是把键对应的值设置为null就可以了。

自动登录和连接设置都用到了INI文件,文章到此结束。

参考链接:柄棋先生的博文:《C#操作INI文件

[转]C#操作INI文件 – rainbow70626 – 博客园已关闭评论
2019年10月30日 By mikel 分类: C#

来源: c#分页读取GB文本文件 – tneduts – 博客园

应用场景:

a.我在做BI开发测试的时候,有可能面对source文件数GB的情况,如果使用一般的文本编辑器,则会卡死,或要等很久才能显示出来。

b.有时候,我们使用ascii(01)或ascii(02)作为行或列的分隔符,这样的临时文件用于导数据到DB,如果文件导入过程中有错误,需要查看文件 的时候,普通的编辑器不支持换行,则会很恐怖。

为解决这两个需求,我使用C#完成了一个简单的winform的应用程序。

功能列表:

1.根据配置的行数,写测试文件,指定行终止符,列分隔符暂时没有使用上。

2根据指定的行终止符,和pagesize,分页读取文件内容,而且可以在text和byte间转换。

Sourcecode:

主要的代码如下,使用了一个迭代器:

复制代码
private IEnumerator<string> ReadLines(string filename)

{

/*

string line;

using (TextReader reader = File.OpenText(filename))

{

while ((line = reader.ReadLine()) != null)

yield return line;

}

*/

StringBuilder sb = new StringBuilder();

using (FileStream fs = File.OpenRead(filename))

{

int b = 0;

while ((b=fs.ReadByte())!=-1)

{

//textbox3 store the row terminator

if (b.ToString() == textBox3.Text.Trim())

{

yield return sb.ToString();

sb.Clear();

}

else

sb.Append(UnicodeEncoding.ASCII.GetString(new byte[] { byte.Parse(b.ToString()) }));

}

}

}
复制代码

 

 

 

示例截图:

我测试了一次,生成了1个亿的数据行,在系统中显示占用6GB的空间,

我尝试读写,无卡顿情况。

可以进一步更新:

根据列行分隔符显示到gridview中,这样更清晰。

可以从后往前读文件。

c#分页读取GB文本文件 – tneduts – 博客园已关闭评论
2019年10月30日 By mikel 分类: 架构设计

来源: 使用FastReport报表工具生成标签打印文档 – 伍华聪 – 博客园

在我们实际开发报表的时候,我们需要按一定的业务规则组织好报表的模板设计,让报表尽可能的贴近实际的需求,在之前的随笔中《使用FastReport报表工具生成报表PDF文档》介绍了FastReport生成常规报表的处理,本篇随笔回顾常规报表的处理效果,并介绍基于FastReport生成标签纸打印需要的报表格式。

1、常规报表的处理

我们一般处理报表的时候,大多数情况碰到的是明细报表,或者有主从表这种样式的报表格式,明细报表就是只需要设计一个表头,按列表展示即可,如下所示格式。

 

或者类似一个基于XtraReport报表的功能界面,如下面图示所示。

这里面涉及的字段,包括字符型、日期型,数值型、枚举类型等,还有统计值、打印时间(参数),因此也算一个比较完整的报表展示了。

或者一些有一条记录构建成的报表信息,如下报表所示,这些也只是设计模板上的差异,在模板里面绑定对应的字段或者参数即可实现。

还有一种是主表有信息,从表有明细的数据展示方式,这个在随笔《使用FastReport报表工具生成报表PDF文档》有介绍过。

 

2、基于标签信息报表的打印处理

之前在随笔《在Winform开发中使用Grid++报表》和《在Bootstrap开发框架中使用Grid++报表》中介绍了使用锐浪报表来设计展现标签打印报表的处理,如下效果所示。

本篇随笔介绍基于FastReport报表工具生成标签打印文档的操作。

其实如果细心查找,FastReport也提供了很多Demo案例,其中就有标签的案例介绍,设计效果如下所示。

 

报表预览效果如下所示。

 

我们要做的类似,不过我们需要增加二维码、条码上去进行打印而已。

首先我们需要设计一个报表模板,设计格式如下所示。

 

在其中我们需要加入一个数据源进行绑定和测试预览效果,我们选择SQLServer一个表的数据进行创建数据源,如下所示。

 

设计报表,我们需要根据实际标签纸张预先设计好报表页面大小,如下所示。

 

如果需要展示多少列,可以通过页面设置中的列进行指定划分多少列,如下所示。

 

不过在实际测试的时候,这样的属性设置,报表渲染的时候,是按指定高度,从左列到右列进行依次展现的,如果我们需要按实际渲染高度,那么这里可以设置为1,然后由数据区进行设置列的数量即可。如下所示。

 

数据区设置列数为实际需要展现的列数,这个根据宽度预览看大概设计多少列合适,让页面高宽尽可能利用好即可。

二维码标签报表格式设计效果大概如下所示。

 

我们使用数据源的数据预览下效果,效果还是杠杠的。

 

报表模板弄好了,我们就需要如何生成FastReport报表或者导出PDF了。

我在之前的随笔《使用FastReport报表工具生成报表PDF文档》里面介绍了FastReport报表的处理代码,这里做法依旧差不多,绑定数据源即可展示或者导出PDF了。

最主要的代码如下所示。

复制代码
//生成PDF报表文档到具体文件
Report report = new Report();
report.Load(reportPath);

//定义参数和数据格式
var dict = new Dictionary<string, object>();
var dt = DataTableHelper.CreateTable("ID,Name,CurrDept,Code,UsePerson,KeepAddr");
if (list != null)
{
    foreach (var info in list)
    {
        var dr = dt.NewRow();
        dr["ID"] = info.ID;
        dr["Name"] = info.Name;
        dr["CurrDept"] = info.CurrDept;
        dr["Code"] = info.Code;
        dr["UsePerson"] = info.UsePerson;
        dr["KeepAddr"] = info.KeepAddr;
        dt.Rows.Add(dr);
    }
}

//刷新数据源
foreach (string key in dict.Keys)
{
    report.SetParameterValue(key, dict[key]);
}
report.RegisterData(dt, "T_Asset");

//运行报表
report.Prepare();
复制代码

如果需要导出PDF,那么代码增加部分导出处理即可。

//导出PDF报表
PDFExport export = new PDFExport();
report.Export(export, realPath);
report.Dispose();

我们来看看实际在浏览器预览的效果,如下所示。

以上效果结合了pdfJS的在线预览PDF操作,如果需要了解PDF的在线处理,参考下随笔《实现在线预览PDF的几种解决方案》。

使用FastReport报表工具生成标签打印文档 – 伍华聪 – 博客园已关闭评论
2019年10月30日 By mikel 分类: 架构设计

来源: Kubernetes+Docker+Istio 容器云实践 – 宜信技术 – 博客园

随着社会的进步与技术的发展,人们对资源的高效利用有了更为迫切的需求。近年来,互联网、移动互联网的高速发展与成熟,大应用的微服务化也引起了企业的热情关注,而基于Kubernetes+Docker的容器云方案也随之进入了大众的视野。开普勒云是一个基于Kubernetes+Docker+Istio的微服务治理解决方案。

一、Microservices

1.1 解决大应用微服务化后的问题

现在各大企业都在谈论微服务,在微服务的大趋势之下技术圈里逢人必谈微服务,及微服务化后的各种解决方案。

1.2 当我们在讨论微服务的时候我们在讨论什么?

使用微服务架构有很多充分的理由,但天下没有免费的午餐,微服务虽有诸多优势,同时也增加了复杂性。团队应该积极应对这种复杂性,前提是应用能够受益于微服务。

1.2.1 如何微服务化的问题

  • 微服务要如何拆分
  • 业务API规则
  • 数据一致性保证
  • 后期可扩展性考虑

当然这不是本文主要讨论的问题,我不讲微服务具体要如何拆分,每个企业每个应用的情况都不太一样,适合自己的方案就是最好的拆分方案。我们主要来解决微服务化后所带来的一些问题。

1.2.2 微服务化后带来的问题

  • 环境一致性
  • 如何对资源快速分配
  • 如何快速度部署
  • 怎么做基本监控
  • 服务注册与发现
  • 负载均衡如何做

以上都是大应用微服务化所需要解决的基础问题,如果还按照传统的方式使用虚拟机来实现,资源开支将会非常大。那么这些问题要怎么解决呢?比如: 

  • 流量管理
  • 服务降级
  • 认证、授权

当然面对上述这些问题我们广大的猿友们肯定是有解决方案的。

1.3 Service governance

1.3.1 Java 体系

假设我们是Java体系的应用,那解决起来就很方便了,比如我们可以考虑使用SpringCloud全家桶系列。也可以拆分使用: 

  • Eureka
  • Hystrix
  • Zuul
  • Spring-cloud
  • Spring-boot
  • ZipKin

Java体系下能很方便的做以我们微服务化后的基础部分,但依然不能非常舒服地解决环境一致性,并且如果有其他语系的服务将很难融入进去。

我们来看基础编程语言一般有什么组合方式来解决基础问题。

1.3.2 其他体系

  • Consul
  • Kong
  • Go-kit
  • Jaeger/Zipkin

假设我们是使用Golang语言,这里再捧一下Golang语言。go语言简直就是天生为微服务而生的语言,实在不要太方便了。高效的开发速度及相当不错的性能,简单精悍。

跑题了~我们使用上面这些工具也可以组成一套还不错的微服务架构。

  • Consul: 当作服务发现及配置中心来使
  • Kong: 作为服务网关
  • Jaeger: 作为链路追踪来使
  • Go-kit: 开发组件

但是这种方案也有问题,对服务的侵入性太强了,每个服务都需要嵌入大量代码,这还是很头疼的。

二、Docker & Kubernetes

基于Docker+k8s搭建平台的实践方案。

2.1 Docker

Docker 是一个非常强大的容器。

  • 资源利用率的提升
  • 环境一致性、可移植性
  • 快速度扩容伸缩
  • 版本控制

使用了Docker之后,我们发现可玩的东西变多了,更加灵活了。不仅仅是资源利用率提升、环境一致性得到了保证,版本控制也变得更加方便了。

以前我们使用Jenkins进行构建,需要回滚时,又需要重新走一次jenkins Build过程,非常麻烦。如果是Java应用,它的构建时间将会变得非常长。

使用了Docker之后,这一切都变得简单了,只需要把某个版本的镜像拉下来启动就完事了(如果本地有缓存直接启动某个版本就行了),这个提升是非常高效的。

(图片来源网络)

既然使用了Docker容器作为服务的基础,那我们肯定需要对容器进行编排,如果没有编排那将是非常可怕的。而对于Docker容器的编排,我们有多种选择:Docker Swarm、Apache Mesos、Kubernetes,在这些编排工具之中,我们选择了服务编排王者Kubernetes。

2.1.1 Docker VS VM

  • VM: 创建虚拟机需要1分钟,部署环境3分钟,部署代码2分钟。
  • Docker: 启动容器30秒内。

2.2 Why choose Kubernetes

我们来对比这三个容器编排工具。

2.2.1 Apache Mesos

Mesos的目的是建立一个高效可扩展的系统,并且这个系统能够支持各种各样的框架,不管是现在的还是未来的框架,它都能支持。这也是现今一个比较大的问题:类似Hadoop和MPI这些框架都是独立开的,这导致想要在框架之间做一些细粒度的分享是不可能的。

但它的基础语言不是Golang,不在我们的技术栈里,我们对它的维护成本将会增高,所以我们首先排除了它。

2.2.2 Docker Swarm

Docker Swarm是一个由Docker开发的调度框架。由Docker自身开发的好处之一就是标准Docker API的使用。Swarm的架构由两部分组成:

(图片来源网络)

它的使用,这里不再具体进行介绍。

2.2.3 Kubernetes

Kubernetes是一个Docker容器的编排系统,它使用label和pod的概念来将容器换分为逻辑单元。Pods是同地协作(co-located)容器的集合,这些容器被共同部署和调度,形成了一个服务,这是Kubernetes和其他两个框架的主要区别。相比于基于相似度的容器调度方式(就像Swarm和Mesos),这个方法简化了对集群的管理.

不仅如此,它还提供了非常丰富的API,方便我们对它进行操作,及玩出更多花样。其实还有一大重点就是符合我们的Golang技术栈,并且有大厂支持。

Kubernetes 的具体使用这里也不再过多介绍,网站上有大把资料可以参考。

2.3 Kubernetes in kubernetes

kubernetes(k8s)是自动化容器操作的开源平台,这些操作包括部署、调度和节点集群间扩展。

  • 自动化容器的部署和复制
  • 随时扩展或收缩容器规模
  • 将容器组织成组,并且提供容器间的负载均衡
  • 很容易地升级应用程序容器的新版本
  • 提供容器弹性,如果容器失效就替换它,等等…

2.4 Kubernetes is not enough either

到这里我们解决了以下问题:

  • Docker: 环境一致性、快速度部署。
  • Kubernetes: 服务注册与发现、负载均衡、对资源快速分配。

当然还有监控,这个我们后面再说。我们先来看要解决一些更高层次的问题该怎么办呢?

在不对服务进行侵入性的代码修改的情况下,服务认证、链路追踪、日志管理、断路器、流量管理、错误注入等等问题要怎么解决呢?

这两年非常流行一种解决方案:Service Mesh。

三、Service Mesh

处理服务间通信的基础设施层,用于在云原生应用复杂的服务拓扑中实现可靠的请求传递。

  • 用来处理服务间通讯的专用基础设施层,通过复杂的拓扑结构让请求传递的过程变得更可靠。
  • 作为一组轻量级高性能网络代理,和程序部署在一起,应用程序不需要知道它的存在。

在云原生应用中可靠地传递请求可能非常复杂,通过一系列强大技术来管理这种复杂性: 链路熔断、延迟感知、负载均衡,服务发现、服务续约及下线与剔除。

市面上的ServiceMesh框架有很多,我们选择了站在风口的Istio。

3.1 Istio

连接、管理和保护微服务的开放平台。

  • 平台支持: Kubernetes, Mesos, Cloud Foundry。
  • 可观察性:Metrics, logs, traces, dependency 。visualisation。
  • Service Identity & Security: 为服务、服务到服务的身份验证提供可验证的标识。
  • Traffic 管理: 动态控制服务之间的通信、入口/出口路由、故障注入。
  • Policy 执行: 前提检查,服务之间的配额管理。

3.2 我们为什么选择Istio?

因为有大厂支持~其实主要还是它的理念是相当好的。

虽然它才到1.0版本,我们是从 0.6 版本开始尝试体验,测试环境跑,然后0.7.1版本出了,我们升级到0.7.1版本跑,后来0.8.0LTS出了,我们开始正式使用0.8.0版本,并且做了一套升级方案。

目前最新版已经到了1.0.4, 但我们并不准备升级,我想等到它升级到1.2之后,再开始正式大规模应用。0.8.0LTS在现在来看小规模还是可以的。

3.3 Istio 架构

我们先来看一下Istio的架构。

其中Istio控制面板主要分为三大块,Pilot、Mixer、Istio-Auth。

  • Pilot: 主要作为服务发现和路由规则,并且管理着所有Envoy,它对资源的消耗是非常大的。
  • Mixer: 主要负责策略请求和配额管理,还有Tracing,所有的请求都会上报到Mixer。
  • Istio-Auth: 升级流量、身份验证等等功能,目前我们暂时没有启用此功能,需求并不是特别大,因为集群本身就是对外部隔离的。

每个Pod都会被注入一个Sidecar,容器里的流量通过iptables全部转到Envoy进行处理。

四、Kubernetes & Istio

Istio可以独立部署,但显然它与Kuberntes结合是更好的选择。基于Kubernetes的小规模架构。有人担心它的性能,其实经过生产测试,上万的QPS是完全没有问题的。

4.1 Kubernetes Cluster

在资源紧缺的情况下,我们的k8s集群是怎么样的?

4.1.1 Master集群

  • Master Cluster:
    • ETCD、Kube-apiserver、kubelet、Docker、kube-proxy、kube-scheduler、kube-controller-manager、Calico、 keepalived、 IPVS。

4.1.2 Node节点

  • Node:
    • Kubelet、 kube-proxy 、Docker、Calico、IPVS。

(图片来源网络)

我们所调用的Master的API都是通过 keepalived 进行管理,某一master发生故障,能保证顺滑的飘到其他master的API,不影响整个集群的运行。

当然我们还配置了两个边缘节点。

4.1.3 Edge Node

  • 边缘节点
  • 流量入口

边缘节点的主要功能是让集群提供对外暴露服务能力的节点,所以它也不需要稳定,我们的IngressGateway 就是部署在这两个边缘节点上面,并且通过Keeplived进行管理。

4.2 外部服务请求流程

最外层是DNS,通过泛解析到Nginx,Nginx将流量转到集群的VIP,VIP再到集群的HAproxy,将外部流量发到我们的边缘节点Gateway。

每个VirtualService都会绑定到Gateway上,通过VirtualService可以进行服务的负载、限流、故障处理、路由规则及金丝雀部署。再通过Service最终到服务所在的Pods上。

这是在没有进行Mixer跟策略检测的情况下的过程,只使用了Istio-IngressGateway。如果使用全部Istio组件将有所变化,但主流程还是这样的。

4.3 Logging

日志收集我们采用的是低耦合、扩展性强、方便维护和升级的方案。

  • 节点Filebeat收集宿主机日志。
  • 每个Pods注入Filebeat容器收集业务日志。

Filebeat会跟应用容器部署在一起,应用也不需要知道它的存在,只需要指定日志输入的目录就可以了。Filebeat所使用的配置是从ConfigMap读取,只需要维护好收集日志的规则。

上图是我们可以从Kibana上看到所采集到的日志。

4.4 Prometheus + Kubernetes

  • 基于时间序列的监控系统。
  • 与kubernetes无缝集成基础设施和应用等级。
  • 具有强大功能的键值数据模型。
  • 大厂支持。

4.4.1 Grafana

4.4.2 Alarm

目前我们支持的报警有Wechat、kplcloud、Email、IM。所有报警都可在平台上配置发送到各个地方。

4.4.3 整体架构

整个架构由外围服务及集群内的基础服务组成,外围服务有:

  • Consul作为配置中心来使用。
  • Prometheus+Grafana用来监控K8s集群。
  • Zipkin提供自己定义的链路追踪。
  • ELK日志收集、分析,我们集群内的所有日志会推送到这里。
  • Gitlab代码仓库。
  • Jenkins用来构建代码及打包成Docker镜像并且上传到仓库。
  • Repository 镜像仓库。

集群有:

  • HAProxy+keeprlived 负责流量转发。
  • 网络是Calico, Calico对kube-proxy的ipvs代理模式有beta级支持。如果Calico检测到kube-proxy正在该模式下运行,则会自动激活Calico ipvs支持,所以我们启用了IPVS。
  • 集群内部的DNS是 CoreDNS。
  • 我们部署了两个网关,主要使用的是Istio的 IngressGateway,TraefikIngress备用。一旦IngressGateway挂了我们可以快速切换到TraefikIngress。
  • 上面是Istio的相关组件。
  • 最后是我们的APP服务。
  • 集群通过Filebeat收集日志发到外部的ES。
  • 集群内部的监控有:
    • State-Metrics 主要用来自动伸缩的监控组件
    • Mail&Wechat 自研的报警服务
    • Prometheus+Grafana+AlertManager 集群内部的监控,主要监控服务及相关基础组件
    • InfluxDB+Heapster 流数据库存储着所有服务的监控信息

4.5 有了Kubernetes那怎么部署应用呢?

4.5.1 研发打包成镜像、传仓库、管理版本

  • 学习Docker。
  • 学习配置仓库、手动打包上传麻烦。
  • 学习k8s相关知识。

4.5.2 用Jenkins来负责打包、传镜像、更新版本

  • 运维工作增加了不少,应用需要进行配置、服务需要做变更都得找运维。
  • 需要管理一堆的YAML文件。

有没有一种傻瓜式的,不需要学习太多的技术,可以方便使用的解决方案?

五、Kplcloud platform

5.1 开普勒云平台

开普勒云平台是一个轻量级的PaaS平台。

  • 为微服务化的项目提供一个可控的管理平台。
  • 实现每个服务独立部署、维护、扩展。
  • 简化流程,不再需要繁琐的申请流程,最大限度的自动化处理。
  • 实现微服务的快速发布、独立监控、配置。
  • 实现对微服务项目的零侵入式的服务发现、服务网关、链路追踪等功能。
  • 提供配置中心,统一管理配置。
  • 研发、产品、测试、运维甚至是老板都可以自己发布应用。

5.2 在开普勒平台部署服务

为了降低学习成本及部署难度,在开普勒平台上部署应用很简单,只需要增加一个Dockerfile 就好了。

Dockerfile 参考:

以上是普通模式,Jenkins代码Build及Docker build。

这是一种相对自由的部署方式,可以根据自己的需求进行定制,当然有学习成本。

5.2.1 为什么不自动生成Dockerfile呢?

其实完全可以做到自动生成Dockerfile,但每个服务的要求可能不一样,有些需要增加文件、有些在Build时需要增加参数等等。我们不能要求所有的项目都是一样的,这会阻碍技术的发展。所以退而求其次,我们给出模版,研发根据自己的需求调整。

5.3 工具整合

  • 开普勒云平台整合了 gitlab,Jenkins,repo,k8s,istio,promtheus,email,WeChat 等API。
  • 实现对服务的整个生命周期的管理。
  • 提供服务管理、创建、发布、版本、监控、报警、日志已及一些周边附加功能,消息中心、配置中心、还能登陆到容器,服务下线等等。
  • 可对服务进行一健调整服务模式、服务类型、一键扩容伸缩,回滚服务API管理以及存储的管理等操作。

5.4 发布流程

用户把自己的Dockerfile跟代码提交到Gitlab,然后在开普勒云平台填写一些参数创建自己的应用。

应用创建完后会在Jenkins创建一个Job,把代码拉取下来并执行Docker build(如果没有选择多阶构建会先执行go build或mvn),再把打包好的Docker image推送到镜像仓库,最后回调平台API或调用k8s通知拉取最新的版本。

用户只需要在开普勒云平台上管理好自己的应用就可以,其他的全部自动化处理。

5.5 从创建一个服务开始

我们从创建一个服务开始介绍平台。

平台主界面:

点击“创建服务”后进入创建页面。

填写基本信息:

填写详细信息: 

基本信息以Golang为例,当选择其他语言时所需填写的参数会略有不同。

如果选择了对外提供服务的话,会进入第三步,第三步是填写路由规则,如没有特殊需求直接默认提交就行了。

5.5.1 服务详情

Build 升级应用版本: 

调用服务模式,可以在普通跟服务网格之间调整。

服务是否提供对外服务的能力: 

扩容调整CPU、内存:

调整启动的Pod数量:

网页版本的终端:

5.5.2 定时任务

5.5.3 持久化存储

管理员创建StorageClass跟PersistentVolumeClaim,用户只需要在自己服务选择相关的PVC进行绑写就行了。

存储使用的是NFS。

5.5.4 Tracing

5.5.5 Consul

Consul当作配置中心来使用,并且我们提供Golang的客户端。

$ go get github.com/lattecake/consul-kv-client

 

它会自动同步consul的目录配置存在内存,获取配置只需要直接从内存拿就行了。

5.5.6 Repository

作者:王聪

首发:宜技之长

Kubernetes+Docker+Istio 容器云实践 – 宜信技术 – 博客园已关闭评论
2019年10月30日 By mikel 分类: 架构设计

来源: 钢铁B2B电商案例:供应链金融如何解决供应链金融痛点 – 宜信技术 – 博客园

一、区块链是什么

区块链是一种按照时间顺序将数据块以特定的顺序相连的方式组合成的链式数据结构,其上存储了系统诞生以来所有交易的记录。区块链上的数据由全网节点共同维护并共同存储,同时以密码学方式保证区块数据不可篡改和不可伪造。所以区块链本质是一个分布式共享数据库。 区块链让参与系统中的任意多个节点,通过密码学方法产生相关联数据块(即区块,block),每个数据块中都包含了一定时间内的系统全部信息交流的数据,并按照时间顺序将数据区块组合成一种链式数据结构。

区块链是分布式数据存储、点对点传输、共识机制、加密算法等计算机技术在互联网时代的创新应用模式,是一种解决信任问题、降低信任成本的信息技术方案。区块链技术的应用,可以取缔传统的信任中介,颠覆传统上存在了几千年的中心化旧模式,在不需要中心化信任中介的情况下,解决陌生人之间的信任问题,大幅降低信任成本。

二、名词解释

  • 分布式:相对于集中式而言,分布式是区块链的典型特征之一,对应的英文是Decentralized,完整的表达形式是不依赖与中心服务器(集群)、利用分布式的计算机资源进行计算的模式。
  • 共识机制:区块链系统中实现不同节点间建立信任、获取权益的数学算法。
  • 分布式数据库:一个可以在多个站点、不同地理位置或者多个机构组成的网络中分享的数据库。

2.1 区块+链=历史+验证

区块结构有两个非常重要的特点:

每个区块的块头包含了前一区块的交易信息的哈希值,因此从创世区块到当前区块形成了链条; 每个区块主体上的交易记录前一区块创建后、该区块创建前发生的所有价值交换活动; 绝大多数情况下,新区块创建成功被加入到链中,该区块的数据记录则不可被改变或更改。

图片描述

2.2 区块链的特点

  • 去中心化

区块链的分布式结构使得数据并不是记录和存储在中心化的电脑或主机上,而是让每一个参与数据交易的节点都记录并存储下所有的数据信息。为此,区块链系统采用了开源的、去中心化的协议来保证数据的完备记录和存储。区块链构建了一整套协议机制,让全网络的每个节点在参与记录数据的同时,也参与验证其他节点记录结果的正确性。只有当全网大部分节点(甚至所有节点)都确认记录的正确性时,该数据才会被写入区块。在区块链的分布式结构的网络系统中,参与记录的网络节点会实时更新并存放全网系统中的所有数据。因此,即使部分节点遭到攻击或破坏,也不会影响这个系统的数据更新和存储。

  • 不可伪造

区块链记录原理需要所有参与记录的节点,来共同验证交易记录的正确性。由于所有节点都在记录全网的每一笔交易,因此,一旦出现某节点记录的信息与其他节点的不符,其他节点就不会承认该记录,该记录也就不会写入区块。

  • 不可篡改

改变某一区块及区块内的交易信息几乎是不可能的。如果该区块被改变,那么之后的每一个区块都将被改变。因此试图篡改数据的人必须同时入侵至少全球参与记录的51%的节点并篡改数据。从技术上讲,这几乎是不可能的。

  • 数学加密

每笔交易需要一个有效签名才会被存储在区块中。只有有效的数字秘钥才能生成有效签名。密钥是成对出现的,由一个私钥和一个公钥组成。其中,公钥是公开的,私钥仅限拥有者可见并使用,用于交易签名,以证明数字身份。

三、区块链如何解决供应链金融痛点

供应链金融是以核心客户为依托,以真实贸易背景为前提,运用自偿性贸易融资的方式,通过应收账款质押登记、第三方监管等专业手段封闭资金流或控制物权,对供应链上下游企业提供的综合性金融产品和服务。

供应链金融的基础,又是供应链。供应链涉及信息流、资金流、物流和商流,天然是个多主体、多协作的业务模式。在这种情况下,要进行贸易融资,首先会遇到很多真实性的问题,比如交易的真实性,单据的真实性,这都需要多方确认,耗费大量的人力物力;其次,涉及的多主体,存在互联互通难的问题,例如每个主体用的供应链管理系统、企业资源管理系统,甚至是财务系统都有所不相同,导致对接难。就算对接上了,会由于数据格式、数据字典不统一,而导致信息共享很难。

供应链金融还存在一些行业痛点:

  • 第一,如前所述,供应链上存在很多信息孤岛,企业间信息的不互通制约了很多融资信息的验证;
  • 第二,核心企业信用并不能有效传递,根据合同法,核心企业是跟一级供应商签订合同,但是一级供应商和二级供应商签订合同时并没有核心企业参与,并不能传递相关的核心企业的信用到多级供应商;
  • 第三,银行缺乏中小企业的可信数据。在现存的银行风控体系下,中小企业无法证实贸易关系的存在,难以获得银行资金。相对地,银行业无法渗透入供应链进行获客和放款;
  • 第四,融资难、融资贵现象突出,在目前赊销模式盛行的市场背景下,供应链上游的供应商往往存在较大资金缺口,然而没有核心企业的背书,他们难以获得银行的优质贷款;
  • 第五,结算并不能自动完成。现在很多约定结算没有自动完成,涉及多级供应商结算时,不确定性因素更多。

因此,供应链金融需要数据穿透和信息共享,通过把资金流、信息流、物流,商流等融合在一起,来提升信息的真实性、信用的可传递和融资的高效率。

基于区块链的供应链金融,通过区块链技术将各个相关方链入一个平台,通过多方记账确权数据存储,实现数据的横向共享,进而实现核心企业的信任传递。基于物权法、电子合同法和电子签名法的约束,借助核心企业信用额度,提升中小企业的融资效率,降低小微企业的融资成本,加速实现普惠金融。

引入区块链带来哪些优势呢?

  • 第一,解决信息孤岛问题,多个利益相关方可以提前设定好规则,加速数据的互通和信息的共享;
  • 第二,根据物权法、电子合同法、电子签名法等,核心企业的应收账款凭证可以通过区块链转化为可流转、可融资的确权凭证,使得核心企业信用能沿着可信的贸易链路传递。基于相互的确权,整个凭证可以衍生出拆分、溯源等多种操作;
  • 第三,提供可信贸易数据,比如在区块链架构下提供线上化的基础合同、单证、支付等结构严密、完整的记录,提升了信息透明度,实现可穿透式的监管;
  • 第四,实现资本降本增效。核心企业信用传递后,中小企业可以使用核心企业的信贷授信额度,降低融资成本,提升融资效率;
  • 第五,实现合约智能清算。基于智能合约的自动清结算,减少人工干预,降低操作风险,保障回款安全。

总而言之,从整个信息流转来看,从以前的信息孤岛变成现在全链条的信息打通,从传统的核心企业只能覆盖一级供应商,变成能够覆盖多级供应商。基于加密数据的交易确权、基于存证的交易真实证明、基于共享账本的信任传递和基于智能合约的合约执行,形成回款封闭可控、穿透式监管、全链条数据打通的新生态,主要是有助于中小微企业解决“融资难,融资贵”的难题。

四、区块链应用案例-大大买钢网解决方案

翼启云服构建的Blockworm Baas(blockchain as a service)平台,是提供区块链服务的云平台,可以帮助用户快速构建区块链基础设施,将业务数据上链。 翼启云服以区块链上的数据为依托,为供应链上下游的中小企业提供金融服务。 平台采用区块链多链结构,B2B平台、供货商、采购方、仓储机构、物流机构作为数据录入节点,将供应链中的信息流、商流、物流数据存储在区块链上。首先,区块链打通了各个参与方的信息系统,提供了可信的协作环境,提高了交易协作的效率。其次,多个参与方基于交易本身协作,共同见证了交易的过程,为交易的真实性提供保障。最后,金融机构作为授信方和资金提供方,基于可信的数据源,利用数据分析等手段,为企业进行授信,放款。简化了融资流程,提高了融资效率,降低了融资成本。 以下为钢铁B2B电商大大买钢在Blockworm平台利用区块链进行业务数据记录的案例。 各方交易从订单生成的时刻起,包括仓储、物流过程中的关键节点都进行数据记录,数据存储在交易链上,整个交易可进行溯源,并根据各方信息交叉验证,防止虚假交易,保障交易真实性。

图片描述

图1. 交易全过程关键节点记录在交易链上

4.1 具体流程

  • 大大买钢网的新增订单的信息被记录在区块链上;
  • 大大买钢变更订单状态为待出库;
  • 第三方仓储在链上发现待出库订单,对货品进行出库操作,并标记订单状态为已出库;
  • 承接货品运输工作的第三方物流公司开始运送货物,并更新订单状态为运输中;
  • 下游买家收到货物,将订单状态标记为已签收。

所有交易中产生的应收账款、票据等可以作为融资标的,登记在资产链上,形成数字资产,此过程被称为资产数字化,这些数字资产本质是供应链中企业的债权。资产链记录所有资产的融资过程。部分过程可由链上的智能合约自动执行,节省人力成本。

图片描述

图2.融资链记录融资资产产生的过程

同时,在交易链中,数字化资产可以进行拆分,做为债权(或资本)向上游供货商进行采购,这样依托于核心企业的信用,就可以传递到整个供应链中,为供应链中的中小企业增信。

图片描述

图3.融资资产可以在链上进行流转

整合图(1) – 图(3), 最终就会演变成多链的模式(图(4)), 首先各个交易链提供资产的真实性溯源,中间是资产链,需要进行融资的资产都可以登记到资产链中,金融机构基于资产链,对资产的真实性进行溯源,评估风险,进行有针对性的金融服务。

图片描述

图4.多链结构,交易链为资产链上的融资资产增信

作者:于明扬

宜信技术学院

钢铁B2B电商案例:供应链金融如何解决供应链金融痛点 – 宜信技术 – 博客园已关闭评论
2019年10月25日 By mikel 分类: Debug, 架构设计

来源: 树莓派搭建个人服务器 – 农码一生 – 博客园

树莓派搭建个人服务器

前言

上一篇树莓派也跑Docker和.NET Core有对树莓派做简单的介绍。包括系统的烧录、基本的设置、docker的安装、.net core的运行等。有人问我是不是一定要跑docker,答案肯定是否定的。我之所以用docker,完全是因为它的便利(带环境)、对系统无污染和方便系统资源的查看和控制。本篇文章继续分享下我对树莓派的使用。

docker安装

mySQL安装

我说在树莓派上跑mySQL你还不信。

docker run --restart=always \
-p 3306:3306 \
--name mysql \
-v $PWD/conf:/etc/mysql/conf.d \
-v $PWD/logs:/logs \
-v $PWD/data:/var/lib/mysql \
-e MYSQL_ROOT_PASSWORD=mysqlpassword \
-d hypriot/rpi-mysql:5.5
--restart=always                   #容器自启动
3306                               #数据库端口
-v $PWD/conf:/etc/mysql/conf.d     #配置文件挂载路径
MYSQL_ROOT_PASSWORD=mysqlpassword  #数据库密码

redis安装

docker run --name redis \
-p 6379:6379 \
-v $PWD/data/redis:/data \
-d arm32v7/redis:3.2.10 redis-server --appendonly yes

gogs安装

有了.net core、mysql和redis,如果再能跑一个git服务器那不就吊炸天了。是的,树莓派安装gogs毫无压力。有了自己的代码管理服务器,再也不会因为GitHub访问慢而烦恼了,也不会因为某Git的广告而不爽了。

docker run --name=gogs -p 122:22 \
-p 1080:3000 \
-v /var/gogs:/data gogs/gogs-rpi

其中1080是浏览器访问端口,你也可以映射到80。122是ssh提交代码的端口。
注意:gogs需要依赖于mysql,第一次启动会引导你填入数据库相关信息。后面如果重启了树莓派,记得先启动mysql,再启动gogs,不然不能正常访问。

内外穿透

上面有了应用,有了数据库,还有了代码管理服务器,可我们树莓派总归是在一个局域网内。离开了这个网络上面说的那些功能好像就没什么用了。那我们有没有办法让我们的树莓派变成真正的服务器呢,答案是肯定的。我相信大家刚接触编程的时候,都幻想过自己写的网站能直接给别人访问。那个时候应该大多折腾过花生壳什么的,那就是个内网穿透的技术。只是别人提供了免费的服务器中转了下,速度也是慢得感人。
今天我们来自己利用frp软件实现内外穿透,当然还是需要服务器的 (没有服务器的同学,可以先借朋友的玩玩) 。

下载地址(各平台的都有):https://github.com/fatedier/frp/releases
因为我们是树莓派,可以下载最新版 frp_0.29.0_linux_arm.tar.gz(装客户端)和 frp_0.29.0_windows_amd64.zip(windows服务器)或者frp_0.29.0_linux_amd64.tar.gz(linux服务器)

首先把frp_0.29.0_linux_amd64.tar.gz上传到服务器,解压,跳转到目录里,编辑frps.ini

[common]
bind_addr = 0.0.0.0
bind_port = 7000
auto_token = token

然后运行./frps运行,启动服务端监听。

把frp_0.29.0_linux_arm.tar.gz上传到树莓派,解压,跳转到目录里,编辑frpc.ini

[common]
server_addr = 服务器ip
server_port = 7000
auto_token = token

[git]
type = tcp
local_ip = 127.0.0.1
local_port = 122
remote_port = 8000

[web]
type = tcp
local_ip = 127.0.0.1
local_port = 1080
remote_port = 8001

然后运行启动./frpc

然后就可以外网访问了,www.服务器ip:8001 就可以访问git服务器了。
注意:如果是云服务器记得把对应的8000、7000、8001端口加入到安全组里面,不然会被拦截掉。

frp的详细用法请参考:https://www.cnblogs.com/sanduzxcvbnm/category/1171545.html

其他安装

#安装nginx
docker run -d -p 80:80 --name nginx arm32v7/nginx:1.16

#dokcer仪表盘管理
docker run -d -p 9000:9000 --restart=always  -v /var/run/docker.sock:/var/run/docker.sock  --name prtainer-test portainer/portainer:arm

#一些其它树莓派的docker应用
https://hub.docker.com/u/arm32v7/
https://hub.docker.com/u/hypriot?page=1
https://cloud.docker.com/u/bennyzhao/repository/docker/bennyzhao/quartzui

树莓派还能做什么

可以做物联网数据的边缘计算啊,如设备的数据采集、监控预警、数据分析等等。再买点传感器做个简陋版“小爱同学”也不是很难,甚至遥控汽车玩具什么的,能想的到的也没什么是做不到的。

资源消耗截图


24小时不关机,零噪音,一直开心的运行着。

结束

推荐一个开源物联网组件:https://github.com/zhaopeiym/IoTClientdemo下载

树莓派搭建个人服务器 – 农码一生 – 博客园已关闭评论
2019年10月24日 By mikel 分类: C#

来源: C# Excel导入、导出【源码下载】 – 方木一 – 博客园

本篇主要介绍C#的Excel导入、导出。

目录

1. 介绍:描述第三方类库NPOI以及Excel结构

2. Excel导入:介绍C#如何调用NPOI进行Excel导入,包含:流程图、NOPI以及C#代码

3. Excel导出:介绍C#如何调用NPOI进行Excel导出,包含:流程图、NOPI以、C#代码以及代码分析

4. 源码下载:展示运行图及源码下载

1. 介绍

1.1 第三方类库:NPOI

说明:NPOI是POI项目的.NET 版本,可用于Excel、Word的读写操作。

优点:不用装Office环境。

下载地址:http://npoi.codeplex.com/releases

1.2 Excel结构介绍

工作簿(Workbook):每个Excel文件可理解为一个工作簿。

工作表(Sheet):一个工作簿(Workbook)可以包含多个工作表。

行(row):一个工作表(Sheet)可以包含多个行。

2. Excel导入

2.1 操作流程

2.2 NPOI操作代码

说明:把Excel文件转换为List<T>

步骤:

①读取Excel文件并以此初始化一个工作簿(Workbook);

②从工作簿上获取一个工作表(Sheet);默认为工作薄的第一个工作表;

③遍历工作表所有的行(row);默认从第二行开始遍历,第一行(序号0)为单元格头部;

④遍历行的每一个单元格(cell),根据一定的规律赋值给对象的属性。

/// <summary>
/// 从Excel2003取数据并记录到List集合里
/// </summary>
/// <param name="cellHeard">单元头的Key和Value:{ { "UserName", "姓名" }, { "Age", "年龄" } };</param>
/// <param name="filePath">保存文件绝对路径</param>
/// <param name="errorMsg">错误信息</param>
/// <returns>转换好的List对象集合</returns>
private static List<T> Excel2003ToEntityList<T>(Dictionary<stringstring> cellHeard, string filePath, out StringBuilder errorMsg) where T : new()
{
    errorMsg = new StringBuilder(); // 错误信息,Excel转换到实体对象时,会有格式的错误信息
    List<T> enlist = new List<T>(); // 转换后的集合
    List<string> keys = cellHeard.Keys.ToList(); // 要赋值的实体对象属性名称
    try
    {
        using (FileStream fs = File.OpenRead(filePath))
        {
            HSSFWorkbook workbook = new HSSFWorkbook(fs);
            HSSFSheet sheet = (HSSFSheet)workbook.GetSheetAt(0); // 获取此文件第一个Sheet页
            for (int i = 1; i <= sheet.LastRowNum; i++) // 从1开始,第0行为单元头
            {
                // 1.判断当前行是否空行,若空行就不在进行读取下一行操作,结束Excel读取操作
                if (sheet.GetRow(i) == null)
                {
                    break;
                }
                T en = new T();
                string errStr = ""// 当前行转换时,是否有错误信息,格式为:第1行数据转换异常:XXX列;
                for (int j = 0; j < keys.Count; j++)
                {
                    // 2.若属性头的名称包含'.',就表示是子类里的属性,那么就要遍历子类,eg:UserEn.TrueName
                    if (keys[j].IndexOf(".") >= 0)
                    {
                        // 2.1解析子类属性
                        string[] properotyArray = keys[j].Split(new string[] { "." }, StringSplitOptions.RemoveEmptyEntries);
                        string subClassName = properotyArray[0]; // '.'前面的为子类的名称
                        string subClassProperotyName = properotyArray[1]; // '.'后面的为子类的属性名称
                        System.Reflection.PropertyInfo subClassInfo = en.GetType().GetProperty(subClassName); // 获取子类的类型
                        if (subClassInfo != null)
                        {
                            // 2.1.1 获取子类的实例
                            var subClassEn = en.GetType().GetProperty(subClassName).GetValue(en, null);
                            // 2.1.2 根据属性名称获取子类里的属性信息
                            System.Reflection.PropertyInfo properotyInfo = subClassInfo.PropertyType.GetProperty(subClassProperotyName);
                            if (properotyInfo != null)
                            {
                                try
                                {
                                    // Excel单元格的值转换为对象属性的值,若类型不对,记录出错信息
                                    properotyInfo.SetValue(subClassEn, GetExcelCellToProperty(properotyInfo.PropertyType, sheet.GetRow(i).GetCell(j)), null);
                                }
                                catch (Exception e)
                                {
                                    if (errStr.Length == 0)
                                    {
                                        errStr = "第" + i + "行数据转换异常:";
                                    }
                                    errStr += cellHeard[keys[j]] + "列;";
                                }
                                
                            }
                        }
                    }
                    else
                    {
                        // 3.给指定的属性赋值
                        System.Reflection.PropertyInfo properotyInfo = en.GetType().GetProperty(keys[j]);
                        if (properotyInfo != null)
                        {
                            try
                            {
                                // Excel单元格的值转换为对象属性的值,若类型不对,记录出错信息
                                properotyInfo.SetValue(en, GetExcelCellToProperty(properotyInfo.PropertyType, sheet.GetRow(i).GetCell(j)), null);
                            }
                            catch (Exception e)
                            {
                                if (errStr.Length == 0)
                                {
                                    errStr = "第" + i + "行数据转换异常:";
                                }
                                errStr += cellHeard[keys[j]] + "列;";
                            }
                        }
                    }
                }
                // 若有错误信息,就添加到错误信息里
                if (errStr.Length > 0)
                {
                    errorMsg.AppendLine(errStr);
                }
                enlist.Add(en);
            }
        }
        return enlist;
    }
    catch (Exception ex)
    {
        throw ex;
    }
}

2.3 C#逻辑操作代码

说明:对Excel转换后的List<T>进行后续操作;如:检测有效性、持久化存储等等

步骤:

①调用2.2代码,把Excel文件转换为List<T>。

②对List<T>进行有效性检测:必填项是否为空、是否有重复记录等等。

③对List<T>进行持久化存储操作。如:存储到数据库。

④返回操作结果。

代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
public void ImportExcel(HttpContext context)
{
    StringBuilder errorMsg = new StringBuilder(); // 错误信息
    try
    {
        #region 1.获取Excel文件并转换为一个List集合
        // 1.1存放Excel文件到本地服务器
        HttpPostedFile filePost = context.Request.Files["filed"]; // 获取上传的文件
        string filePath = ExcelHelper.SaveExcelFile(filePost); // 保存文件并获取文件路径
        // 单元格抬头
        // key:实体对象属性名称,可通过反射获取值
        // value:属性对应的中文注解
        Dictionary<stringstring> cellheader = new Dictionary<stringstring> {
            "Name""姓名" },
            "Age""年龄" },
            "GenderName""性别" },
            "TranscriptsEn.ChineseScores""语文成绩" },
            "TranscriptsEn.MathScores""数学成绩" },
        };
        // 1.2解析文件,存放到一个List集合里
        List<UserEntity> enlist = ExcelHelper.ExcelToEntityList<UserEntity>(cellheader, filePath, out errorMsg);
        #endregion
        #region 2.对List集合进行有效性校验
        #region 2.1检测必填项是否必填
        for (int i = 0; i < enlist.Count; i++)
        {
            UserEntity en = enlist[i];
            string errorMsgStr = "第" + (i + 1) + "行数据检测异常:";
            bool isHaveNoInputValue = false// 是否含有未输入项
            if (string.IsNullOrEmpty(en.Name))
            {
                errorMsgStr += "姓名列不能为空;";
                isHaveNoInputValue = true;
            }
            if (isHaveNoInputValue) // 若必填项有值未填
            {
                en.IsExcelVaildateOK = false;
                errorMsg.AppendLine(errorMsgStr);
            }
        }
        #endregion
        #region 2.2检测Excel中是否有重复对象
        for (int i = 0; i < enlist.Count; i++)
        {
            UserEntity enA = enlist[i];
            if (enA.IsExcelVaildateOK == false// 上面验证不通过,不进行此步验证
            {
                continue;
            }
            for (int j = i + 1; j < enlist.Count; j++)
            {
                UserEntity enB = enlist[j];
                // 判断必填列是否全部重复
                if (enA.Name == enB.Name)
                {
                    enA.IsExcelVaildateOK = false;
                    enB.IsExcelVaildateOK = false;
                    errorMsg.AppendLine("第" + (i + 1) + "行与第" + (j + 1) + "行的必填列重复了");
                }
            }
        }
        #endregion
        // TODO:其他检测
        #endregion
        // 3.TODO:对List集合持久化存储操作。如:存储到数据库
        
        // 4.返回操作结果
        bool isSuccess = false;
        if (errorMsg.Length == 0)
        {
            isSuccess = true// 若错误信息成都为空,表示无错误信息
        }
        var rs = new { success = isSuccess,  msg = errorMsg.ToString(), data = enlist };
        System.Web.Script.Serialization.JavaScriptSerializer js = new System.Web.Script.Serialization.JavaScriptSerializer();
        context.Response.ContentType = "text/plain";
        context.Response.Write(js.Serialize(rs)); // 返回Json格式的内容
    }
    catch (Exception ex)
    {
     throw ex;
    }
}

3. Excel导出

3.1 导出流程

3.2 NPOI操作代码

说明:把List<T>转换为Excel

步骤:

①创建一个工作簿(Workbook);

②在工作簿上创建一个工作表(Sheet);

③在工作表上创建第一行(row),第一行为列头,依次写入cellHeard的值(做为列名)。

④循环遍历List<T>集合,每循环一遍创建一个行(row),然后根据cellHeard的键(属性名称)依次从List<T>中的实体对象取值存放到单元格内。

代码:

3.3 C#逻辑操作代码

说明:对Excel转换后的List<T>进行后续操作;如:检测有效性、持久化存储等等

步骤:

①获取List<T>集合。

②调用3.2,将List<T>转换为Excel文件。

③服务器存储Excel文件并返回下载链接。

代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
public void ExportExcel(HttpContext context)
{
    try
    {
        // 1.获取数据集合
        List<UserEntity> enlist = new List<UserEntity>() {
            new UserEntity{Name="刘一",Age=22,Gender="Male",TranscriptsEn=new TranscriptsEntity{ChineseScores=80,MathScores=90}},
            new UserEntity{Name="陈二",Age=23,Gender="Male",TranscriptsEn=new TranscriptsEntity{ChineseScores=81,MathScores=91} },
            new UserEntity{Name="张三",Age=24,Gender="Male",TranscriptsEn=new TranscriptsEntity{ChineseScores=82,MathScores=92} },
            new UserEntity{Name="李四",Age=25,Gender="Male",TranscriptsEn=new TranscriptsEntity{ChineseScores=83,MathScores=93} },
            new UserEntity{Name="王五",Age=26,Gender="Male",TranscriptsEn=new TranscriptsEntity{ChineseScores=84,MathScores=94} },
        };
        // 2.设置单元格抬头
        // key:实体对象属性名称,可通过反射获取值
        // value:Excel列的名称
        Dictionary<stringstring> cellheader = new Dictionary<stringstring> {
            "Name""姓名" },
            "Age""年龄" },
            "GenderName""性别" },
            "TranscriptsEn.ChineseScores""语文成绩" },
            "TranscriptsEn.MathScores""数学成绩" },
        };
        // 3.进行Excel转换操作,并返回转换的文件下载链接
        string urlPath = ExcelHelper.EntityListToExcel2003(cellheader, enlist, "学生成绩");
        System.Web.Script.Serialization.JavaScriptSerializer js = new System.Web.Script.Serialization.JavaScriptSerializer();
        context.Response.ContentType = "text/plain";
        context.Response.Write(js.Serialize(urlPath)); // 返回Json格式的内容
    }
    catch (Exception ex)
    {
        throw ex;
    }
}

3.4 代码分析

核心代码主要是cellheader与List<T>之间的映射关系:

4. 源码下载

4.1 运行图

4.2 下载地址

百度网盘: http://pan.baidu.com/s/1o69We8M

CSDN:http://download.csdn.net/download/polk6/8974195

2018/07/11更新,添加对Excel 2007的导入支持:

git:https://github.com/polk6/CSharp-Excel

C# Excel导入、导出【源码下载】 – 方木一 – 博客园已关闭评论
备案信息冀ICP 0007948