Sql Server 事务/回滚 - 你啵哥哥哥哥 - 博客园

mikel阅读(682)

来源: Sql Server 事务/回滚 – 你啵哥哥哥哥 – 博客园

Sql Server 事务/回滚

复制代码
复制代码

set XACT_ABORT ON   —如果不设置该项为ON,在SQL中默认为OFF,那么只只回滚产生错误的 Transact-SQL 语句;设为ON,回滚整个事务

begin tran t1 —启动一个事务

update [water].[dbo].[ErrorInf]
set ErrorMessage=’test’
where ID=6

insert into [water].[dbo].[ErrorInf]([ID],ErrorMessage,[Description])
Values(1,’test1′,’test1′)

commit tran t1  —提交事务

复制代码
复制代码

功能:实现begin tran 和commit tran之间的语句,任一如果出现错误,所有都不执

 

事务不是有错就回滚的,在不写rollback的情况下,并不是什么错误都会回滚事务,有时回滚当前语句,有时回滚整个事务

如例

begin tran
insert into dbo.area values(‘1111’)
insert into dbo.area values(‘2222’)
select 1/0
insert into dbo.area values(‘333’)
commit

 

像这样,就算中间有错,也不会回滚,结果会成功添加三条记录

但有人说,比如重大错误,这事务也会所有回滚,只是我无法重现重大错误罢了

普通错误如果想回滚整个事务,只要加个set XACT_ABORT on就可以了

复制代码
复制代码

set XACT_ABORT on

begin tran

insert into dbo.area values(‘1111’)
insert into dbo.area values(‘2222’)
select 1/0
insert into dbo.area values(‘333’)

commit

复制代码
复制代码

 

 

但也有人写一堆@@error,如

复制代码
复制代码

begin tran

insert into dbo.area values(‘1111’)
if @@error>0
rollback

insert into dbo.area values(‘2222’)
if @@error>0
rollback

select 1/0
if @@error>0
rollback

insert into dbo.area values(‘333’)
if @@error>0
rollback

commit

复制代码
复制代码

 

当然也行,不过写起来太麻烦了.

后来发现SQL2005支持try

复制代码
复制代码

BEGIN TRY
BEGIN TRANSACTION
insert into dbo.area values(‘1111’)
insert into dbo.area values(‘2222’)
select 1/0
insert into dbo.area values(‘333’)
COMMIT
END TRY
BEGIN CATCH
IF @@TRANCOUNT > 0
ROLLBACK

DECLARE @ErrMsg nvarchar(4000), @ErrSeverity int
SELECT @ErrMsg = ERROR_MESSAGE(),
@ErrSeverity = ERROR_SEVERITY()

RAISERROR(@ErrMsg, @ErrSeverity, 1)
END CATCH

碰到嵌套事务-笔记 - YoungPay - 博客园

mikel阅读(781)

来源: 碰到嵌套事务-笔记 – YoungPay – 博客园

相关SQL语句:

BEGIN TRAN

SAVE TRAN

COMMIT TRAN

ROLLBACK TRAN

嵌套事务示例:

BEGIN TRAN Tran1

BEGIN TRAN Tran2

COMMIT TRAN 可以单独指定某个事务名,如Tran1,Tran2进行提交。其实也没什么效果,即使Tran2提交成功了,只要将外层事务Tran1回滚,Tran2保存的数据照样被回滚!
ROLLBACK TRAN 不能指定某个事务名进行回滚!只能ROLLBACK TRAN 或者 ROLLBACK TRAN Tran1,也就说只能回滚最外层的事务名。如果执行ROLLBACK TRAN Tran2,SQL会提示“无法回滚 Tran2。找不到该名称的事务或保存点”,出错的原因就是因为Tran2不是最外层事务。总结一句话:ROLLBACK 要么就所有事务都回滚,要么就回滚时异常,一个事务都没回滚!
ROLLBACK 可以回滚某个事务保存点(SAVE TRAN TranSave1), 如ROLLBACK TRAN TranSave1,但是要明白的是,回滚事务保存点并不会使事务数@TranCount减少,你嵌套了几个事务,它还是有几个事务数.
特别注意,如果在父存储过程创建一个事务Tran1,然后在子存储过程执行ROLLBACK TRAN后,子存储过程会抛出异常!事务只能在同一个存储过程里面创建、回滚和提交,不允许分离在不同的存储过程里面。
基于以上特点,我个人觉得嵌套事务的作用不大,SQL的内部处理其实最终就是处理一个最外层的事务点。SQL抛出事务相关的异常,并不是代码有何问题,而是在提醒我们注意事务的控制。 我们只要采用TRY CATCH 方式捕获相关异常就可以,我们只要确保设计的事务点能正常回滚或提交就OK了。

解决方法1:

TRY CATCH 捕获相关异常

解决方法2:

如果外部已经有事务了,就不再创建内部事务。我想SQL的事务异常提醒就是为了告诉你,不能随便一个地方放事务。

IF @TRANCOUNT =0

BEGIN TRAN

 

TRY CATCH 注意事项:

SQL语句不加try catch,即使出现异常,后续的SQL语句也会执行。但是一旦外部加了try catch,则会捕获异常,导致后续的SQL语句没有执行。
是否SQL的异常有分致命和普遍的,在没加try catch的情况下,普通的可以继续往下走,但是致命的就不往下走了。这个和C#编程语言有重大的不同,编程语言一旦出现异常,后续代码就不再执行!
在处理嵌套事务时,要特别注意,不论如何要确保事务被完整的关闭或被回滚!

回滚比较好控制。无论有多少级事务数,只要ROLLBACK 一次就可以。不过如果是ROLLBACK TRAN TRANNAME,TranName不是第一级的话,则会出现异常,等于没有执行ROLLBACK操作。
提交就要特别注意了。BEGIN TRAN 创建事务3个,则必须COMMIT TRAN 提交事务3次,才能确保事务数被完整提交。可以通过@@TRANCOUNT来查看当前事务数。一旦存储过程没有完整提交事务,则可能出现事务锁表的情况!如果创建事务的进程销毁了,即使有未提交的事务,应该也销毁,算回滚了吧?

SQL Server 中 RAISERROR 的用法 - 钢钢 - 博客园

mikel阅读(657)

来源: SQL Server 中 RAISERROR 的用法 – 钢钢 – 博客园

raiserror  是由单词 raise error 组成
raise  增加; 提高; 提升
raiserror 的作用: raiserror 是用于抛出一个错误。[ 以下资料来源于SQL server 2005的帮助 ]

其语法如下:

复制代码
RAISERROR ( { msg_id | msg_str | @local_variable }
{ ,severity ,state }
[ ,argument [ ,…n ] ]
)
[ WITH option [ ,…n ] ]
复制代码

简要说明一下:

第一个参数:{ msg_id | msg_str | @local_variable }
msg_id:表示可以是一个sys.messages表中定义的消息代号;
使用 sp_addmessage 存储在 sys.messages 目录视图中的用户定义错误消息号。
用户定义错误消息的错误号应当大于 50000。

msg_str:表示也可以是一个用户定义消息,该错误消息最长可以有 2047 个字符;
(如果是常量,请使用N’xxxx’,因为是nvarchar的)
当指定 msg_str 时,RAISERROR 将引发一个错误号为 5000 的错误消息。

@local_variable:表示也可以是按照 msg_str 方式的格式化字符串变量。

第二个参数:severity
用户定义的与该消息关联的严重级别。(这个很重要)
任何用户都可以指定 0 到 18 之间的严重级别。
[0,10]的闭区间内,不会跳到catch;
如果是[11,19],则跳到catch;
如果[20,无穷),则直接终止数据库连接;

第三个参数:state
如果在多个位置引发相同的用户定义错误,
则针对每个位置使用唯一的状态号有助于找到引发错误的代码段。

介于 1 至 127 之间的任意整数。(state 默认值为1)
当state 值为 0 或大于 127 时会生成错误!

第四个参数:argument
用于代替 msg_str 或对应于 msg_id 的消息中的定义的变量的参数。

第五个参数:option
错误的自定义选项,可以是下表中的任一值:
LOG :在错误日志和应用程序日志中记录错误;
NOWAIT:将消息立即发送给客户端;
SETERROR:将 @@ERROR 值和 ERROR_NUMBER 值设置为 msg_id 或 50000;

[SQL]代码示例
–示例1
DECLARE @raiseErrorCode nvarchar(50)
SET @raiseErrorCode = CONVERT(nvarchar(50), YOUR UNIQUEIDENTIFIER KEY)
RAISERROR(‘%s INVALID ID. There is no record in table’,16,1, @raiseErrorCode)

 

–示例2
复制代码
RAISERROR (
N’This is message %s %d.’, — Message text,
10,                        — Severity,
1,                         — State,
N’number’,                 — First argument.
5                          — Second argument.
);
— The message text returned is: This is message number 5.
GO
复制代码
–示例3
复制代码
RAISERROR (N'<<%*.*s>>’, — Message text.
10,           — Severity,
1,            — State,
7,            — First argument used for width.
3,            — Second argument used for precision.
N’abcde’);    — Third argument supplies the string.
— The message text returned is: <<    abc>>.
GO
复制代码
–示例4
复制代码
RAISERROR (N'<<%7.3s>>’, — Message text.
10,           — Severity,
1,            — State,
N’abcde’);    — First argument supplies the string.
— The message text returned is: <<    abc>>.
GO
复制代码
–示例5

–A. 从 CATCH 块返回错误消息
以下代码示例显示如何在 TRY 块中使用 RAISERROR 使执行跳至关联的 CATCH 块中。
它还显示如何使用 RAISERROR 返回有关调用 CATCH 块的错误的信息。

复制代码
BEGIN TRY
RAISERROR (‘Error raised in TRY block.’, — Message text.
16, — Severity.
1 — State.
);
END TRY
BEGIN CATCH
DECLARE @ErrorMessage NVARCHAR(4000);
DECLARE @ErrorSeverity INT;
DECLARE @ErrorState INT;

SELECT
@ErrorMessage = ERROR_MESSAGE(),
@ErrorSeverity = ERROR_SEVERITY(),
@ErrorState = ERROR_STATE();

RAISERROR (@ErrorMessage,  — Message text.
@ErrorSeverity, — Severity.
@ErrorState     — State.
);
END CATCH;

复制代码
–示例6

–B. 在 sys.messages 中创建即席消息
以下示例显示如何引发 sys.messages 目录视图中存储的消息。
该消息通过 sp_addmessage 系统存储过程,以消息号50005添加到 sys.messages 目录视图中。

复制代码
sp_addmessage @msgnum = 50005,
@severity = 10,
@msgtext = N'<<%7.3s>>’;
GO

RAISERROR (50005, — Message id.
10,    — Severity,
1,     — State,
N’abcde’); — First argument supplies the string.
— The message text returned is: <<    abc>>.
GO

sp_dropmessage @msgnum = 50005;
GO

复制代码
–示例7
–C. 使用局部变量提供消息文本
以下代码示例显示如何使用局部变量为 RAISERROR 语句提供消息文本。
复制代码
sp_addmessage @msgnum = 50005,
@severity = 10,
@msgtext = N'<<%7.3s>>’;
GO

RAISERROR (50005, — Message id.
10,    — Severity,
1,     — State,
N’abcde’); — First argument supplies the string.
— The message text returned is: <<    abc>>.
GO

sp_dropmessage @msgnum = 50005;
GO

复制代码

参考来源:

http://msdn.microsoft.com/zh-cn/library/ms178592.aspx

ms-help://MS.SQLCC.v9/MS.SQLSVR.v9.zh-CHS/tsqlref9/html/483588bd-021b-4eae-b4ee-216268003e79.htm

SqlServer2008中事务使用的一些细节 - Hello World - CSDN博客

mikel阅读(758)

来源: SqlServer2008中事务使用的一些细节 – Hello World – CSDN博客

测试存储过程代码时候碰到了一个事务的细节问题,是在使用if else语句时候,报错begin tran和commit tran不配对,以前使用时候并没怎么留意这些小细节,所以出现了一些想当然的错误

存储过程如下:

Alter PROCEDURE [dbo].[proc_test]
@id int,
@result INT=-1 OUTPUT,
@msg AS varchar(50) OUTPUT
AS
BEGIN
BEGIN TRAN
BEGIN TRY
DECLARE @num INT=0
SELECT @num=num from temp_num where id=@id
IF (@num=0)
BEGIN
insert into temp_num values(floor(rand()*200)-100);–插入-100到100的随机数
SET @msg=’处理成功’
SET @result=1
END
ELSE
BEGIN
SET @msg=’处理失败’
SET @result=0
RAISERROR(@msg,16,1)
END
COMMIT TRAN
END TRY
BEGIN CATCH
SET @msg=ERROR_MESSAGE()
SET @result=0
ROLLBACK TRAN
END CATCH

RETURN @result
END

现在因为业务需求,增加了一个else if分支,调整后如下:
Alter PROCEDURE [dbo].[proc_test]
@id int,
@result INT=-1 OUTPUT,
@msg AS varchar(50) OUTPUT
AS
BEGIN
BEGIN TRAN
BEGIN TRY
DECLARE @num INT=0
SELECT @num=num from temp_num where id=@id
IF (@num=0)
BEGIN
insert into temp_num values(floor(rand()*200)-100);–插入-100到100的随机数
SET @msg=’处理成功’
SET @result=1
END
ELSE IF(@num<0)
BEGIN
update temp_num set num=-1*num where id=@id;–将负数变正
ROLLBACK TRAN
END
ELSE
BEGIN
SET @msg=’处理失败’
SET @result=0
RAISERROR(@msg,16,1)
END
COMMIT TRAN
END TRY
BEGIN CATCH
SET @msg=ERROR_MESSAGE()
SET @result=0
ROLLBACK TRAN
END CATCH

RETURN @result
END
那么问题来了,该存储过程执行到新增的else if分支就直接报错了“ROLLBACK TRANSACTION 请求没有对应的 BEGIN TRANSACTION。”

ELSE IF(@num<0)
BEGIN
update temp_num set num=-1*num where id=@id;–将负数变正
ROLLBACK TRAN
END
C#代码时候

public int TestMethod()

{

if(a>0)

{

a=a+1;

}

else if(a<0)

{

return 0;

}

else

{

a=a-1;

}

return 1;

}

C#中这么写,没毛病,编译器也不会报错,但是想当然的把begin tran、commit tran/rollback tran当成return这样的用就有问题了,SQLServer语法检查通不过!

begin tran 和 commit tran、rollback tran的配对,在碰到if else时候,如果你在分支里写了commit tran、rollback tran,那么每个分支都需要提交或者回滚,不能部分写分支里,部分写if else语句结束之后

所以调整后的存储过程

Alter PROCEDURE [dbo].[proc_test]
@id int,
@result INT=-1 OUTPUT,
@msg AS varchar(50) OUTPUT
AS
BEGIN
BEGIN TRAN
BEGIN TRY
DECLARE @num INT=0
SELECT @num=num from temp_num where id=@id
IF (@num=0)
BEGIN
insert into temp_num values(floor(rand()*200)-100);–插入-100到100的随机数
SET @msg=’处理成功’
SET @result=1
COMMIT TRAN
END
ELSE IF(@num<0)
BEGIN
update temp_num set num=-1*num where id=@id;–将负数变正
COMMIT TRAN
END
ELSE
BEGIN
ROLLBACK TRAN
SET @msg=’处理失败’
SET @result=0
RAISERROR(@msg,16,1)
END

END TRY
BEGIN CATCH
SET @msg=ERROR_MESSAGE()
SET @result=0
ROLLBACK TRAN
END CATCH

RETURN @result
END

也就是说,如果你在分支里使用了commit、rollback,那么每个分支里都必须提交或者回滚,并且if else语句之后不能再次提交或者回滚。这样写起来比较麻烦,其实最好的做法是提前定义一个变量,if else中对变量赋值,结尾判断值再进行提交或者回滚操作。
此外,begin tran写在begin if之内或者之外都不影响catch语句中的回滚

———————
作者:数据的流
来源:CSDN
原文:https://blog.csdn.net/shujudeliu/article/details/78783613
版权声明:本文为博主原创文章,转载请附上博文链接!

微信分享网页链接自定义图片和文字描述 - 走召爱木土的博客 - CSDN博客

mikel阅读(810)

来源: 微信分享网页链接自定义图片和文字描述 – 走召爱木土的博客 – CSDN博客

今天开发需要微信分享网页链接自定义图片和文字描述,于是网上各种查找资料和各种看手册,遇到了好多坑,不过啃了半天以后终于把问题解决了,以防以后忘记,今天记录一下;
首先,由于微信已经发布了想要分享链接必须调用认证了的公众号的JSSDK接口,所以,像以前的在html页面中的head中放入图片,我试过已经不管用了,当然不是绝对的,但是我这个方法试了多好次,不能用,所以没有办法只好调接口来做这个功能了;
废话不多说,
首先第准备工作:
需要有一个认证了的公众号;

然后开始了我们的代码操作:
一: 在html中引入js文件
<script type=”text/JavaScript” src=”http://res.wx.qq.com/open/js/jweixin-1.2.0.js”></script>

这里写图片描述

二: 在控制器中假如一下代码,引入jssdk.php文件,我放在extend文件夹下面,
这个不是强制要求,大家随意,只要文件引入正确
//获取apptoken
        include EXTEND_PATH . "\jssdk\jssdk.php";
        $jssdk = new \JSSDK('你的appid','你的APPSecret');//这里填写自己的appid 和secret
        $signPackage = $jssdk->GetSignPackage();

三: 回到html中,将发送来的数据放在head中;这个好处就是,数据不需要写死,以防以后出现问题;
<script>
		wx.config({
            debug: false,
            appId: '{$signPackage["appId"]}',
            timestamp: '{$signPackage["timestamp"]}',
            nonceStr: '{$signPackage["nonceStr"]}',
            signature: '{$signPackage["signature"]}',
            jsApiList: [
	            //我只调用了两个分享接口,分别是朋友圈和个人,大家有其它需要可以去文档中查找接口
				'onMenuShareTimeline',
				'onMenuShareAppMessage'
			]
		});
		wx.ready(function () {
			var shareData = {
				title: '标题',	//	标题
				desc: '描述',	//	描述
				link: '链接',	//	分享的URL,必须和当前打开的网页的URL是一样的
				imgUrl: '缩略图完整路径'	//	缩略图地址
			};
			wx.onMenuShareAppMessage(shareData);
			wx.onMenuShareTimeline(shareData);
		});
		wx.error(function (res) {
			//alert(res.errMsg);//错误提示
		});
	 </script>

这里写图片描述

四: 将文件夹放入到你的项目中,也可以和第二步一起进行,在其中测试错误;


五:一定要在公众号里设置IP的白名单,否则一直报错,说你没有权限可以拿到token!


好了,大概就是这样,按流程走完,就可以分享网页链接的时候带上你自定义的图片和内容描述了

基于MUI的电影新闻的webapp项目开发 - luoshao2000的博客 - CSDN博客

mikel阅读(774)

来源: 基于MUI的电影新闻的webapp项目开发 – luoshao2000的博客 – CSDN博客

写在前面:之前,本人在自学webapp的过程中,一直没有找到一份比较完整的开发步骤文档,都是一些零零散散的文档,走了很多弯路,也耽误了很多的时间,其中尝试了多种框架,感觉MUI这种H5+的模式是比较适合的一种WEBAPP开发模式,开发难度稍高于纯H5,但应用流畅度接近原生APP(本人和dcloud没有一毛钱关系),是值得推荐的一种混合型APP开发模式。为了让广大WEBAPP初学者少走弯路,大家可以跟着本项目文档走下来,基本上就掌握了WEBAPP开发的很多方面。本项目不涉及服务端,服务端实现较为简单,可以使用任意的语言实现,只需要能根据url返回json数据即可,所以本项目直接使用了网上公开的数据API。

1    项目简介
项目功能为加载知乎日报和豆瓣电影信息,可以查看新闻、影评和观看预告片。
2    项目资源
https://www.jianshu.com/p/42630373e1bc
2.1    知乎日报API
2.1.1    最新新闻列表:https://news-at.zhihu.com/api/4/news/latest
2.1.2    某条新闻内容:https://news-at.zhihu.com/api/4/news/3892357
2.2    豆瓣电影API
2.2.1    在映电影列表:https://douban.uieee.com/v2/movie/in_theaters?city=福州
2.2.2    某部电影信息:https://douban.uieee.com/v2/movie/subject/26985127
2.3    Json格式工具
打开http://www.bejson.com/,把获得的json字符串直接粘贴到输入框,然后验证
2.4    HTML模板工具
http://www.jq22.com/JQuery-info1097
JS资源下载
打开https://github.com/aui/art-template/tree/master/lib ,下载template-web.js

3    开发工具
3.1    HBuilder
http://download.dcloud.net.cn/HBuilder.9.1.14.windows.zip
3.2    夜神安卓模拟器
https://res06.bignox.com/full/20180816/b78465a3b8a9457eb47df54e477a1a60.exe?filename=nox_setup_v6.2.2.3_full.exe

4    开发过程
4.1    项目创建
使用Hbuilder新建移动APP项目,把template-web.js拷贝到js目录下; 新建subpages子目录,以后的页面都是放在该目录下。
4.2    入口页开发
打开index.html,写上一个页头mHeader和一个页脚导航mTab,修改链接和名称。

效果如下:

在subpages目录下,新建对应的四个页面,先写上简单的文字标识,后面会进行功能完善。

采用webView的方式来进行Tab切换,代码有点多,可以拷贝官方文档,进行局部修改,其实只要修改subpages即可,主要要和mTab中的设置要一致,否则会报告错误,因为名字错了,就无法找到文件资源,也就无法初始化了。

※初始化代码要写在plusReady事件中。
4.3    首页开发
暂时先留空,因为这个页面的内容是其他模块信息的综合而已,先做其他页面,在来组装本页的内容
4.4    新闻列表
打开newslist.html,新建一个mbody,
引入<script src=”../js/template-web.js” type=”text/JavaScript” charset=”utf-8″></script>
在mui.plusReady中编写ajax,获取数据

新建一个mListMedia块,删除多余的li,留下一个,用于制作模板(具体做法请参考art-template),最后模板如下:

※注意此图与上图的对应关系,script的id=“newlistScript”与template函数的参数名“newlistScript”是对应的。Mui(“#newlist”)是放置当前模板内容的父元素id,要记得给mbody的div加上id=‘newlist’。
※在这个过程当中,可以先用浏览器查询下数据,把数据放入www.bejson.com中进行观察,来指导编写代码。
绑定新闻条目的点击事件:

※解释:off是为了先把以前绑定的事件解除(一般不做也没有关系),on绑定tap点击事件到当前元素下的所有li元素上;this.getAttribute是获得li上的id属性值;openWindow是当点击某个li后,打开下一个网页,extras是传参;
页面效果如下:

4.5    新闻详情页
新建一个mHeader,取名为新闻详情,并带有返回键<
创建一个mBody,取id为news
引入template-web.js
创建mui.plusReady事件,编写代码:

创建一个卡片视图,(奇怪,hbuilder没有卡片的自动生成快捷键?),删除多余的div,留下一个做为模板用,模板最后如下:

页面效果如下:

4.6    豆瓣电影
打开movielist.html,
创建一个mbody
引入template-web.js
创建mui.plusReady事件,并编写代码

创建自定义模板

绑定tap事件

5    电影详情页
创建movie.html,
创建mHeader,创建mbody,取id为movie
创建卡片布局(见官网)
创建mui.plusReady事件,并编写代码

创建模板
把影片图片放在卡片的头部,把影片信息,预告片信息,影评信息分别放入卡片布局的内容区,具体代码见源代码。

※由于横向滚动条的预告片部分是动态生成的,所以要让它有滚动效果,必须重新初始化

6    首页开发
6.1    头部滚动条
滚动条为当前评分较高的电影海报,
在mui.plusReady中获取电影列表

自定义排序比较函数:降序排序

模板生成

6.2    下部列表
在mui.plusReady中拉数据

生成模板

7    项目效果预览

首页:

新闻列表

豆瓣电影

新闻详情页:

电影详情页:

预告片播放:

8. 项目源代码:

https://download.csdn.net/download/luoshao2000/10618471
———————
作者:包包老师
来源:CSDN
原文:https://blog.csdn.net/luoshao2000/article/details/81914525
版权声明:本文为博主原创文章,转载请附上博文链接!

html5+JS+PHP 预览并异步上传图片的功能 - 转身_错过 - CSDN博客

mikel阅读(760)

来源: html5+JS+PHP 预览并异步上传图片的功能 – 转身_错过 – CSDN博客

Html部分

<div class=”from-group clearfix from-group-padding”>
<label class=”col-sm-1 label-right”>微缩图 :</label>
<div class=”col-sm-2″>
<a href=”JavaScript:;” id=”uploadimg”>
<img src=”/images/default-thumbnail.png” width=”180″ style=”” id=”imgpre”>
<input type=”file” id=”imgFile” onchange=”imgPreview(this);return false;”/>
<input type=”hidden” name=”course_pic” value=”” id=”getpic”>
</a>
</div>
<div class=”input-note”>尺寸:640px × 363px</div>
</div>

CSS部分

/* 图片上传CSS */
#uploadimg{display:block; width:180px; height:180px;position:relative; overflow:hidden;}
#imgFile{position:absolute; right:0; top:0; font-size:100px; opacity:0; filter:alpha(opacity=0);cursor:pointer}
js部分
function imgPreview(fileDom){

//判断是否支持FileReader
if (window.FileReader) {
var reader = new FileReader();
} else {
toastr.error(‘不支持FileReader API 请升级您的设备’);
}
var file = fileDom.files[0];
var imageType = /^image\//;
//是否是图片
if (!imageType.test(file.type)) {
toastr.error(‘请选择图片!’);
return;
}
//读取完成
reader.onload = function(e) {
// //获取图片展示的dom
// var img = document.getElementById(“imgpre”);
// // 图片路径设置为读取的图片
// img.src = e.target.result;
// ajax 上传图片
$.post(“/Course/uploadimg”, {course_img: e.target.result}, function(ret) {
if (ret.img!=”) {
toastr.success(‘图片上传成功!’);
$(‘#imgpre’).attr(‘src’,’/’+ret.img);
$(‘#getpic’).attr(‘value’,’/’+ret.img);
alert(ret.img);
// $(‘#showimg’).html(‘<img src=”‘ + ret.img + ‘”>’);
// 返回给前台 图片地址 放到隐藏框中
} else {
toastr.error(‘图片上传错误!’);
}
},’json’);
};
reader.readAsDataURL(file);
}//imgPreview结束
php部分

public function uploadimg(){
$pic = Request::param();
//获取图片信息
list($type,$file) = explode(‘,’,$pic[‘course_img’]);
// 判断类型
if(strstr($type,’image/jpeg’)!=”){
$ext = ‘.jpg’;
}elseif(strstr($type,’image/gif’)!=”){
$ext = ‘.gif’;
}elseif(strstr($type,’image/png’)!=”){
$ext = ‘.png’;
}
// 生成的文件名
$photo = md5(time()).$ext;
$file_dir = ‘uploads/images/’.Date(‘Ymd’);
//判断文件夹是否存在
if (!is_dir($file_dir)) {
mkdir($file_dir,755,true);
}
if(file_put_contents($file_dir.’/’.$photo,base64_decode($file), true)){
$ret = [‘img’=>$file_dir.’/’.$photo];
//这里必须用echo返回json 用return JQuery接收不到
echo json_encode($ret);
}
}

———————
作者:转身_错过
来源:CSDN
原文:https://blog.csdn.net/qq_41321149/article/details/81293872
版权声明:本文为博主原创文章,转载请附上博文链接!

Tensorflow源码解析1 -- 内核架构和源码结构 - 阿里云云栖社区 - 博客园

mikel阅读(782)

1 主流深度学习框架对比 当今的软件开发基本都是分层化和模块化的,应用层开发会基于框架层。比如开发Linux Driver会基于Linux kernel,开发Android app会基于Android

来源: Tensorflow源码解析1 — 内核架构和源码结构 – 阿里云云栖社区 – 博客园

1 主流深度学习框架对比

当今的软件开发基本都是分层化和模块化的,应用层开发会基于框架层。比如开发Linux Driver会基于Linux kernel,开发Android app会基于Android Framework。深度学习也不例外,框架层为上层模型开发提供了强大的多语言接口、稳定的运行时、高效的算子,以及完备的通信层和设备层管理层。因此,各大公司早早的就开始了深度学习框架的研发,以便能占领市场。当前的框架有数十种之多,主流的如下(截止到2018年11月)

显然TensorFlow是独一无二的王者。第二名Keras,它是对TensorFlow或Theano接口的二次封装,严格意义上并不是一个独立的深度学习框架。TensorFlow目前也已经集成了Keras,使得安装了TensorFlow的用户就可以直接使用Keras了。

TensorFlow之所以能够从数十种框架中脱颖而出,主要优点有

  1. 出身高贵,是谷歌出品的。但其他很多框架出身也不差,例如PyTorch之于Facebook,MXNET之于Amazon
  2. 2015年就开源了,比较早的俘获了一大批开发者。这个确实是tf的一大先发优势,但PyTorch的前身Caffe,以及MXNET开源时间都不晚,而且Caffe流行时间比tf早,后来才被赶超的。更有Theano这样的绝对老前辈。由此可见,软件开源是多么重要。目前流行的深度学习框架也基本都开源了。
  3. 支持的开发语言多,支持Python Java Go C++等多种流行语言。相比某些框架,确实是优势很大。相比MXNET则小巫见大巫了。MXNET早期发展的一个主要方向就是前端多语言的支持,连MATLAB R Julia等语言都支持了。
  4. 运行效率高。早期的时候,其实tf的运行效率比很多框架都要低一些的。
  5. 安装容易,用户上手快,文档齐全,社区活跃。这个是tf的一个较大优势,特别是社区方面,也就是我们常说的生态优势。互联网头部集中效应十分明显,体现在开源软件上也是一样。这也是我认为最大的一个优势。

总结起来,TensorFlow虽然每个方面都不是绝对领先的优势,但贵在每个方面都做的不错,因此最终能够一骑绝尘,独领风骚。

学习Tensorflow框架内核,可以理解前端接口语言的支持,session生命周期,graph的构建、分裂和执行,operation的注册和运行,模块间数据通信,本地运行和分布式运行模式,以及CPU GPU TPU等异构设备的封装支持等。学习这些,对于模型的压缩 加速 优化等都是大有裨益的。

2 TensorFlow系统架构

TensorFlow设计十分精巧,基于分层和模块化的设计思想进行开发的。框架如下图

整个框架以C API为界,分为前端和后端两大部分。

  1. 前端:提供编程模型,多语言的接口支持,比如Python Java C++等。通过C API建立前后端的连接,后面详细讲解。
  2. 后端:提供运行环境,完成计算图的执行。进一步分为4层
    1. 运行时:分为分布式运行时和本地运行时,负责计算图的接收,构造,编排等。
    2. 计算层:提供各op算子的内核实现,例如conv2d, relu等
    3. 通信层:实现组件间数据通信,基于GRPC和RDMA两种通信方式
    4. 设备层:提供多种异构设备的支持,如CPU GPU TPU FPGA等

模型构造和执行流程

TensorFlow的一大特点是,图的构造和执行相分离。用户添加完算子,构建好整图后,才开始进行训练和执行,也就是图的执行。大体流程如下

  1. 图构建:用户在client中基于TensorFlow的多语言编程接口,添加算子,完成计算图的构造。
  2. 图传递:client开启session,通过它建立和master之间的连接。执行session.run()时,将构造好的graph序列化为graphDef后,以protobuf的格式传递给master。
  3. 图剪枝:master根据session.run()传递的fetches和feeds列表,反向遍历全图full graph,实施剪枝,得到最小依赖子图
  4. 图分裂:master将最小子图分裂为多个Graph Partition,并注册到多个worker上。一个worker对应一个Graph Partition。
  5. 图二次分裂:worker根据当前可用硬件资源,如CPU GPU,将Graph Partition按照op算子设备约束规范(例如tf.device(‘/cpu:0’),二次分裂到不同设备上。每个计算设备对应一个Graph Partition。
  6. 图运行:对于每一个计算设备,worker依照op在kernel中的实现,完成op的运算。设备间数据通信可以使用send/recv节点,而worker间通信,则使用GRPC或RDMA协议。

3 前端多语言实现 – swig包装器

TensorFlow提供了很多种语言的前端接口,使得用户可以通过多种语言来完成模型的训练和推断。其中Python支持得最好。这也是TensorFlow之所以受欢迎的一大原因。前端多语言是怎么实现的呢?这要归功于swig包装器。

swig是个帮助使用C或者C++编写的软件能与其它各种高级编程语言进行嵌入联接的开发工具。在TensorFlow使用bazel编译时,swig会生成两个wrapper文件

  1. pywrap_tensorflow_internal.py:对接上层Python调用
  2. pywrap_tensorflow_internal.cc:对接底层C API调用。

pywrap_tensorflow_internal.py 模块被导入时,会加载_pywrap_tensorflow_internal.so动态链接库,它里面包含了所有运行时接口的符号。而pywrap_tensorflow_internal.cc中,则注册了一个函数符号表,实现Python接口和C接口的映射。运行时,就可以通过映射表,找到Python接口在C层的实现了。

4 tensorflow 源码结构

TensorFlow源码基本也是按照框架分层来组织文件的。如下

其中core为tf的核心,它的源码结构如下

5 总结

TensorFlow框架设计精巧,代码量也很大,我们可以从以下部分逐步学习

    1. TensorFlow内核架构和源码结构。先从全局上对框架进行理解。
    2. 前后端连接的桥梁–Session,重点理解session的生命周期,并通过相关源码可以加深理解Python前端如何调用底层C实现。
    3. TensorFlow核心对象—Graph。图graph是TensorFlow最核心的对象,基本都是围绕着它来进行的。graph的节点为算子operation,边为数据tensor。
    4. TensorFlow图的节点 — Operation。operation是图graph的节点,承载了计算算子。
    5. TensorFlow图的边 — Tensor。Tensor是图graph的边,承载了计算的数据。
    6. TensorFlow本地运行时。
    7. TensorFlow分布式运行时。和本地运行时有一些共用的接口,但区别也很大。
    8. TensorFlow设备层。主要了解设备层的定义规范,以及实现。
    9. TensorFlow队列和并行运算。
    10. TensorFlow断点检查checkpoint,模型保存Saver,以及可视化tensorboard。这三个为TensorFlow主要的工具。

原文链接
本文为云栖社区原创内容,未经允许不得转载。

供应链金融&区块链应用 - 莱布尼茨 - 博客园

mikel阅读(821)

现代管理教育对供应链的定义为“供应链是围绕核心企业,通过对商流,信息流,物流,资金流的控制,从采购原材料开始,制成中间产品以及最终产品,最后由销售网络把产品送到消费者手中的将供应商,制造商,分销商,零

来源: 供应链金融&区块链应用 – 莱布尼茨 – 博客园

现代管理教育对供应链的定义为“供应链是围绕核心企业,通过对商流,信息流,物流,资金流的控制,从采购原材料开始,制成中间产品以及最终产品,最后由销售网络把产品送到消费者手中的将供应商,制造商,分销商,零售商,直到最终用户连成一个整体的功能网链结构。那什么是核心企业呢?

当供应链中某一企业在整个供应链中占据主导地位,对其他成员具有很强的辐射能力和吸引能力,通常称该企业为核心企业或主导企业。核心企业是供应链的物流中心、信息中心和资金周转中心。在供应链竞争中,核心企业承担供应链组织者和管理者的职能。 核心企业的关键在技术,而在技术更高一层的是管理,例如精益管理的鼻祖丰田汽车,它就是以自身的生产为核心,提供定单,围绕这个企业,形成了一二三个批次的供应链条,成百上千个中小企业为之服务。


供应链金融

在传统供应链金融业务模式下,业务主体包括供应商(上游企业)、经销商(下游企业)、核心企业、银行、仓储机构以及物流公司等,业务模式主要包括三种,分别为应收类(应收账款融资模式)、预付类(保兑仓融资模式)以及存货类(融通仓融资模式)(可参看 传统供应链金融业务模式:应收账款融资、保兑仓融融资、融通仓融资)。其中应收账款融资与保理业务关系密切。

保理

全称保付代理,又称托收保付,卖方将其现在或将来的基于其与买方订立的货物销售/服务合同所产生的应收账款转让给保理商(提供保理服务的金融机构),由保理商向其提供资金融通、买方资信评估、销售账户管理、信用风险担保、账款催收等一系列服务的综合金融服务方式。

国际上对保理的定义五花八门,我们只要掌握精髓就好。

由来:保理业务是从出口代理交易方式演变而来的,起源于14世纪英国毛纺工业。当时英国毛纺织品是在寄售基础上委托专业代理商代销的。这些代理商向国外买主出售商品,同时向出口商担保买主的商业信用。当时由于交通不便,外贸业务往来活动比较缓慢。如果在国外没有可靠的代理人进行协助,任何出口企业很难取得成功。到18世纪,美国的一些代理商逐步以其高度效率和雄厚资金掌握了为扩大其国内市场所需要的代贷管理工作。他们的地位也逐步由以前被委托的代理人身分演变为独立的经济实体——保理商。保理商根据保理合同专门为有关商业企业提供信贷和信用管理服务。经过不断地发展,现代保理商已能提供一揽子服务,包括向卖方提供买方资信调查,100%货款商业风险担保,应收账款管理和资金融通等。

盈利模式:保理业务的盈利模式一般为向客户收取利息及保理费,不仅能带来丰厚的利润,更为重要的是可为保理公司创造可观的中间业务收入。一般来说,销售方保理商除了可以获得发票金额一定比例的佣金外,还可以通过向销售商提供融资服务获得融资利息;而作为购买方保理商,由于承担买方信用,收取的佣金比例更高。

坏账担保(full protection against bad debts)保理商为自己核准的应收账款提供100%的担保,有效消除因买方信用给出口方造成的坏账风险的担保服务。

扩展阅读:五分钟教你看懂供应链金融(文尾抛出的问题可看读者评论)

融资租赁

融资租赁和供应链金融是不同概念,前者是一种融资形式,它可以融入到供应链金融的保兑仓融资融通仓融资这两种模式中,当然它是独立于具体金融应用领域的。

融资租赁分为直租和售后回租两种模式。直租类似于分期付款,可以降低客户购买设备的门槛,但在企业角度看,它规避了分期付款的缺点,可以使企业实现一次性回款,减少了企业的应收账款。售后回租也类似于分期付款,只不过商品原本是承租人自己的,转卖给融资租赁公司,融资租赁公司再租售给承租人而已,绕这么一大弯,承租人手里的可用资金却多了。

保兑仓&融通仓

要了解两个概念仓单质押银行承兑汇票

仓单质押

仓单:是指仓储公司签发给存储人或货物所有权人的记载仓储货物所有权的唯一合法的物权凭证,仓单持有人随时可以凭仓单直接向仓储方提取仓储货物。仓单质押指银行与借款人(出质人)、保管人(仓储公司)签订合作协议,以保管人签发的借款人自有或第三方持有的存货仓单作为质押物向借款人办理贷款的信贷业务。

扩展阅读:贷款按照有无担保一般分为信用贷款和担保贷款,其中担保贷款又分为保证贷款、抵押贷款和质押贷款。一般来说,银行认可的抵押物主要是不动产如房屋、厂房、机器设备等,质押物主要是银行存单、国债等有价证券。尽管生产企业或商业企业的存货也具有一定的价值,理论上也可以进行抵押,但是由于银行难以对存货进行有效的监管,同时缺乏对存货市场价值的评估,一般银行不愿意接受存货抵押借款的方式。这样,对于那些缺乏合适抵押品的企业,尽管其拥有大量的存货,却难以从银行获得贷款支持。大多数中小企业都存在这种尴尬的局面。仓单融资实质是一种存货抵押融资方式,通过银行、仓储公司和企业的三方协议,引入专仓储公司在融资过程中发挥监督保管抵押物、对抵押物进行价值评估、担保等作用,实现以企业存货仓单为抵押的融资方式。仓单融资适用于流通性较高的大宗货物,特别是具有一定国际市场规模的初级产品,如有金属及原料、黑色金属及原料、煤炭、焦碳、橡胶、纸浆以及大豆、玉米等农产品。任何特制的商品、专业机械设备、纺织服装,家电等产品,一般难以取得银行仓单融资的机会(这就给了其它融资机构提供服务的机会)。

银行承兑汇票

这是一个银行、供应方、采购方三方共赢的商业模式,可参看 张虎成讲票据(三)终于有人把银行承兑汇票彻底讲清楚了。目下很多平台也提供票据结算业务,比如找钢网,应该是与企业低贴息结算(相对银行贴息率较低),然后等着票据到期去银行取钱,赚这一部分贴息率。

保兑仓:企业分批赎货。融通仓:融资企业拿这批货抵押贷款,核心企业提供担保和[可能的]回购。

传统供应链金融存在的问题

  1. 由于银行依赖核心企业信用,出于风控的考虑,银行仅愿意对核心企业有直接应付账款义务的上游供应商(即一级供应商)提供保理业务,或对其一级经销商提供预付款或者存货融资。这就导致了有巨大融资需求的二级、三级等供应商/经销商的资金需求得不到满足,无法充分挖掘整个供应链的潜力。
    资金来自于银行等金融机构,银行在放款上具有强有力的话语权,企业在向银行提出融资申请后,按照它自己的标准来对企业进行评估,看它是否具有偿还能力。例如,有些中小企业虽然基于真实的交易背景具有偿还能力,可是并没能从账面财务信息表达,特别是当这些企业没有有效的、可供抵押的固定价值资产时,银行只能拒绝给企业融资。在这种情况下,中小企业只能以更高的价格,转向其他中小金融机构(如保理公司、民间借贷等)进行融资。如何将真实的交易背景层层穿透至供应链的末端,成为了最大的难题。
  2. 由于牵涉到多方以及多环节,各方须签署复杂的纸质合同,另外还涉及到合同的不同版本,因此维护和管理难度较大。在办理业务时,常需要找到特定版本的原始文档,及指定专人前来签署(如在实际金融操作中,银行非常关注应收账款债权“转让通知”的法律效应,如果核心企业无法签回,银行不会愿意授信。据了解,银行对于签署这个债权“转让通知”的法律效应很谨慎,甚至要求核心企业的法人代表去银行当面签署)。
  3. 信息不对称、不透明,金融方需要花费人力物力去鉴别交易真伪和核实线下抵押品,这反过来提高了融资方的融资成本。

国内市场前景

国家统计局数据显示,2016年末,我国规模以上工业企业应收账款12.6万亿元,同比增长10% ,这其中产生了企业巨大的融资需求。而相比于巨大的应收账款,2015年我国年商业保理量仅在2000亿元左右,可以看出,还有大量供应链需求没有被满足,因而供应链金融行业发展空间巨大。网贷天眼发布的《2016互联网+供应链金融研究报告》,预测2020年我国供应链金融市场规模将达到15万亿左右,这是一个非常巨大的市场。


区块链应用

笔者认为,虽然区块链脱胎于数字货币这种纯虚拟产物,但供应链金融反而是更能培养她成长的合适土壤。这是因为首先,供应链金融这个场景具有万亿级别的市场规模,天花板足够高;其次,这个场景天然需要多方合作,却又没有一个传统中心化的机构在治理,需要用区块链来建立信任;同时,在技术上这个场景并不需要高并发,目前的区块链技术能够满足。

与业务相关的核心功能就是基于真实共享的上链交易数据自动产生数字票据,该票据可以在公开透明、多方见证的情况下进行随意的拆分、汇总和转移。相当于把整个供应链中的信用变得可传导、可追溯,为大量原本无法融资的中小企业提供了融资机会,极大地提高票据的流转效率和灵活性,降低中小企业的资金成本。

如何将实体资产映射为数字资产,并实时同步状态,是个较为麻烦的事情。以目前的技术来说,通过RFID、NFC等射频方案可自动同步信息,然而资产损耗(如丢失、芯片故障)等场外因素仍需要手动盘点。

如何切入

如何切入是个难点,特别是涉及到多方合作的项目,可以从核心企业的影响力强制要求上下游加入 or 提供一个让人无法拒绝的服务:)

  • 联盟链模式——直接与核心企业/平台合作,为其提供区块链底层解决方案,在积累足够多数据之后,通过搭建联盟链,对接资金方提供金融服务。
  • 私有链模式——从提供供应链管理服务入手,比如溯源、追踪、可视化等,将信息流、物流和资金流整合到一起,在此基础之上从事金融服务。

大致可分为以下几步:

  1. 数据上链,将交易数据放到链上,利用区块链的特性使其不可篡改,并提供数据的确权,溯源等服务。
  2. 资产数字化,把仓单、合同、以及可代表融资需求的区块链票据都变成数字资产,且具有唯一、不可篡改、不可复制等特点。
  3. 数字资产的交易,供应链金融平台将转变成一个金融资产交易所,将非标的企业贷款需求转变成标准化的金融产品,进行代币化,对接投融资需求,进行价值交易。

最终,区块链技术将能有效地增强供应链金融资产的流动性,调动新型的融资工具和风控体系帮助覆盖中小企业融资的长尾市场,催生供应链金融即服务。

作用

提高行业透明度:区块链为供应链提供了交易状态实时、可靠的视图,有效提升了交易透明度,这将大大方便中介机构基于常用的发票、库存资产等金融工具进行放款。其中抵押资产的价值将根据现实时间实时更新,最终这将有助于建立一个更可靠和稳定的供应链金融生态系统。

降低金融成本:同样的,由于区块链填补了信任鸿沟,并能实时查阅包括合同条款在内的所有数据,另外还有智能合约的加持,减少了人工核实和反馈的成本,这部分同样会反映到金融服务上。

新的商业模式:新型供应链金融平台,主要的参与者包括平台本身、保理机构、中介金融机构、企业、个人甚至是算法公司。平台提供数据,算法公司可以基于平台提供的API接口,开发金融模型,并出售给第三方金融机构和保理公司。

提升用户体验:由于用户数据(包括交易数据、征信、资产配置等)都已上链(敏感数据可配置为只有特定机构可查询),平台可推送合适的金融方案给到用户,用户选择后资方会自动划拨款项到用户银行账户,而不需要漫长的资料准备、各种复杂的条款、审批等等。

ps:并不是说区块链上的数据都一定是真实的,但至少比传统系统如ERP之类的方便排查的多,且更易于发现。


其它

敞口:一般指的是风险敞口。指在金融活动中存在金融风险的部分以及受金融风险影响的程度。

托盘:钢贸行业的仓单质押,属于保兑仓。

外贸

T/T: 电汇,也就是说客户直接从他的开户银行把钱付到你的开户银行中,或者反之。前T/T就是预先付款,后T/T就是先发货后付款。前T/T当然就对卖家有保障对买家不利了。
L/C是指信用证。一般都要求买家开不可撤销信用证。意思是买家把钱存在银行,然后委托银行按照一定的外贸流程,预先设定好受益人,交易方式,所需单据等要求,卖方根据信用证要求出货并把单据寄到对方银行,单据都没错银行就直接给你放款,不受买家的要求影响。对卖方买方都有保障。对单据要求比较高,而且手续费高。

长尾

长尾效应,英文名称Long Tail Effect。“头”(head)和“尾”(tail)是两个统计学名词。正态曲线中间的突起部分叫“头”;两边相对平缓的部分叫“尾”。从人们需求的角度来看,大多数的需求会集中在头部,而这部分我们可以称之为流行,而分布在尾部的需求是个性化的,零散的小量的需求。而这部分差异化的、少量的需求会在需求曲线上面形成一条长长的“尾巴”,而所谓长尾效应就在于它的数量上,将所有非流行的市场累加起来就会形成一个比流行市场还大的市场。

 

参考资料:

供应链金融三类模式的最全对比分析

研究报告:区块链+供应链金融

 

转载请注明本文出处:https://www.cnblogs.com/newton/p/9827715.html

.Net Core in Docker - 在容器内编译发布并运行 - Agile.Zhou - 博客园

mikel阅读(906)

Docker可以说是现在微服务,DevOps的基础,咱们.Net Core自然也得上Docker。.Net Core发布到Docker容器的教程网上也有不少,但是今天还是想来写一写。 你搜.Net c

来源: .Net Core in Docker – 在容器内编译发布并运行 – Agile.Zhou – 博客园

Docker可以说是现在微服务,DevOps的基础,咱们.Net Core自然也得上Docker。.Net Core发布到Docker容器的教程网上也有不少,但是今天还是想来写一写。
你搜.Net core程序发布到Docker网上一般常见的有两种方案:

  • 1、在本地编译成Dll文件后通过SCP命令或者WinSCP等工具上传到服务器上,然后构建Docker镜像再运行容器。该方案跟传统的发布很像,麻烦的地方是每次都要打开相关工具往服务器上复制文件。
  • 2、在服务端直接通过Git获取最新源代码后编译成Dll然后构建Docker镜像再运行容器。该方案免去了往服务器复制文件这步操作,但是服务器环境需要安装.Net Core SDK 来编译源代码。
    自从用了Docker简直懒的不能自理,我既不想手工复制文件到服务器,也不想在服务器装.Net Core环境。显然只要Docker镜像包含.Net Core SDK环境就可以在Docker内帮我们编译代码然后运行,这样连我们的服务器都不用装啥.Net Core的环境拉。

    在Docker内编译发布.Net Core程序并运行

    新建一个ASP.NET Core MVC项目

    我们使用一个ASP.NET Core MVC程序来演示如何发布到Docker并运行。
    新建项目
    使用vs新建一个ASP.NET core mvc项目

  public class HomeController : Controller
    {
        public IActionResult Index()
        {
            return Content($"Core for docker , {DateTime.Now} , verson 2");
        }
    }

修改HomeController下的index Action,直接输出一段文字

    public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
            WebHost.CreateDefaultBuilder(args)
            .UseKestrel(op =>
            {
                op.ListenAnyIP(5000);
            })
            .UseStartup<Startup>();

修改Program下的CreateWebHostBuilder方法,让Kestrel监听5000端口


本地运行一下试试

推送源码到代码仓库

把我们的代码推送到对应的Git仓库,方便我们从部署服务器上直接拉取最新的代码。

X:\workspace\CoreForDocker>git remote add origin https://gitee.com/kklldog/CoreForDocker.git

X:\workspace\CoreForDocker>git push -u origin master
Username for 'https://gitee.com': xxx@gmail.com
Password for 'https://xxx@gmail.com@gitee.com':
Counting objects: 88, done.
Delta compression using up to 4 threads.
Compressing objects: 100% (83/83), done.
Writing objects: 100% (88/88), 527.07 KiB | 2.43 MiB/s, done.
Total 88 (delta 7), reused 0 (delta 0)
remote: Powered By Gitee.com
To https://gitee.com/kklldog/CoreForDocker.git
 * [new branch]      master -> master
Branch 'master' set up to track remote branch 'master' from 'origin'.

添加Dockerfile文件

在CoreForDocker下新增一个Dockerfile文件,注意没有任何扩展名。我们需要基于microsoft/dotnet:latest这个镜像构建一个新的镜像。并且在构建的过程中直接对源码进行编译并发布。

FROM microsoft/dotnet:latest
WORKDIR /app
COPY /. /app
RUN dotnet restore
RUN dotnet publish -o /out -c Release
EXPOSE 5000
ENTRYPOINT ["dotnet", "/out/CoreForDocker.dll"]

大概解释下Dockerfile的意思:
FROM microsoft/dotnet:latest:使用dotnet的最新镜像,这个镜像其实对应的应该就是2.2-sdk这个镜像,里面包含了dotnet-core 2.2 sdk
WORKDIR /app:指定工作目录为app
COPY /. /app复制宿主机当前目录的内容到容器的app文件夹
RUN dotnet restore:还原nuget包
RUN dotnet publish -o /out -c Release编译并发布程序集到容器的out目录
EXPOSE 5000:暴露5000端口
ENTRYPOINT [“dotnet”, “/out/CoreForDocker.dll”]:容器启动的时候执行dotnet命令,参数为/out/CoreForDocker.dll


Dockerfile的文件属性设置为始终复制
新建好Dockerfile后git push到代码仓库。

在服务器上构建Docker镜像

这里以Ubuntu为例,ssh登录到服务器后使用git clone命令拉取源代码。

git clone https://gitee.com/kklldog/CoreForDocker.git

进入源码目录

cd CodeForDocker\CodeForDocker

使用docker build命令构建新的镜像,注意不要忘记最后一个’.’

docker build -t image_code4docker .

运行容器

如果以上步骤都没有报错,那么恭喜你镜像已经构建成功了,我们可以使用此镜像运行Docker容器了。

docker run -d --name code4docker -p 5000:5000 -v /ect/localtime:/ect/localtime image_core4docker

使用image_core4docker镜像运行一个名为core4docker的容器,绑定宿主机的5000到容器的5000口。其中需要注意的是-v参数映射宿主机的/ect/localtime文件夹到容器的/ect/localtime文件夹,因为经过实践发现容器中的时区有可能跟宿主机不一致,需要映射宿主机的/ect/localtime让容器的时区跟宿主机保持一致。


访问一下服务器的5000端口,发现能够正确返回数据表示我们的Asp.net Core程序在容器中运行成功了

以后当我们对源码进行修改,并提交后,我们只需在服务器上拉取最新的代码然后使用docker build,docker run命令来再次生成镜像并运行容器。但是手工输入docker build,docker run的命令好像也很麻烦,参数又那么多,太烦了。

使用shell脚本简化操作

为了偷懒不想敲那么长的命令,我们可以构建一个脚本,把命令一次性写好,以后只要运行一次脚本就可以了。
使用vim新建一个publish.sh的文件

vim publish.sh

键盘上按i进入编辑模式,输入以下内容

cd CoreForDocker/CoreForDocker
git pull
docker stop core4docker
docker rm core4docker
docker rmi image_core4docker
docker build -t image_core4docker .
docker run --name core4docker -d -p 5000:5000 -v /etc/localtime:/etc/localtime image_core4docker

以上命令,不光有新建镜像跟运行容器的命令,还有移除原来的容器跟镜像的命令
按ecs进入命令模式,退出保存

:wq

让我们模拟修改一下源代码,并提交到代码仓库

    public IActionResult Index()
    {
        return Content($"Core for docker , {DateTime.Now} , version 2");
    }

再次修改homecontroller的index action,输出内容上新增一个version
ssh登录到服务器,运行publish.sh文件

/bin/bash publish.sh


跑完之后我们再次访问下服务器的5000口,数据返回正确,表示服务器上跑的已经是最新的程序了

总结

通过以上演示我们基本了解如何通过git跟docker配合在Ubuntu服务器上不安装.Net Core SDK来发布.Net Core 程序到容器中运行,并且通过shell脚本的方式再次简化发布。但是尽管这样每次发布都需要ssh到服务器上然后运行脚本,特别是开发环境可能经常需要发布,还是觉得麻烦。有没有什么办法让我们push代码后服务器自动就开始部署最新的代码的到容器中运行了呢?
后面我会介绍下如何通过jenkins跟webhook来做CICD。