当前位置: 首页 > news >正文

书店网站模板凡科网站建设价格

书店网站模板,凡科网站建设价格,网络网重庆公司,wordpress 建站教程 下载这是老衣在2017年5月份总结的#xff0c;适用于中小团队跨平台微服务开发的实践指引#xff08;简化版#xff09;。若有有不当之处#xff0c;欢迎指点更正因本文涉及到大量第三方库或工具#xff0c;详细学习和了解需要参考相关官方文档。若您在使用Mac电脑#xff0c;… 这是老衣在2017年5月份总结的适用于中小团队跨平台微服务开发的实践指引简化版。若有有不当之处欢迎指点更正因本文涉及到大量第三方库或工具详细学习和了解需要参考相关官方文档。若您在使用Mac电脑建议安装使用Dash软件下载查阅其他操作系统上则考虑使用Chrome浏览器在http://devdocs.io/offline 上查阅文档值得一提的是该网页的文档支持离线模式。环境准备全局必要项node.js 可根据实际情况选择安装当前版或是长期支持版docker最新版 根据实际需要决定使用免费的Docker CE版或是收费的Docker EE版。注意 Windows 10 64位专业版和64位企业版可以直接安装依赖hyper-v其他旧版Windows需要使用docker toolbox。根据自己的喜好选择安装下列Git客户端:git scmSource TreeTortoiseGit 仅支持WindowsSmartGit全局可选项lua 最新版python最新版.NET Core 1.1或以上开发环境Visual Studio 2017仅支持Windows系统Visual Studio for MAC故名思议仅支持Mac系统Visual Studio Code跨平台代码编辑器。建议至少安装以下扩展其他扩展由团队根据使用的开发语言和相关工具链自行决定是否安装Docker 支持DockerFile的加亮和智能提示Angular 1 JavaScript and TypeScript Snippets for VS Code 支持angular1.x的一些代码片段和智能提示Azure Extension Pack 微软官方出品的在VSCode中处理Azure相关功能的一些扩展Azure Tools for Visual Studio Code 一些方便在VSCode使用的Azure工具Beautify 代码格式整理插件Bootstrap 3 Snippets bootstrap框架的代码片段Bower Bower的VSCode扩展C# Extensions 增加一些C#开发时的功能C# XML Documentation Comments 支持C#的///注释Can I Use 检查当前行的CSS属性兼容的浏览器版本HTML CSS Support 支持各种Web语言html、Razor、Vue、pug、php等Icon Fonts 字体图标(例如Font Awesome)的样式支持Lua linter 支持Lua语言智能提示和语法检查等mssql 可在VSCode中方便使用微软SQL Server数据库nginx.conf 支持nginx配置文件的代码加亮Python 支持Python的智能提示和代码格式化等功能yo 将前端项目模板工具Yo的相关命令内置到VSCodeWeb前端开发相关工具Yeoman简称Yo - 前端项目模板工具可以快速搭建前端项目模板Bower - 前端包管理工具类似于Visual Studio种的Nuget用于打包发布的工具: Grunt、Gulp、webpack 中的至少一种看个人喜欢或团队熟悉度动态CSS语言: LESS、SASS 根据团队需要选择其一Angular 1.x 适用于企业管理等相关用户场景的开发因为其对IE浏览器支持的版本相对较低适用面较广后端数据库建议向跨平台的数据库种类倾斜比如MySQL、PostgreSQL甚至是一些NOSQL数据库MongoDB。微软的SQL Server 2017支持Linux和Docker目前尚处于预览版状态不建议在生产环境使用等官方正式版发布后再尝试使用。PostgreSQL数据库对SQL标准的语法支持非常完整建议更多考虑。代码托管环境为了最大限度提功开发部署灵活性建议使用Git方式托管。所以可根据实际情况选择下列之一支持Git库的TFSGitHub 私有库或企业版Coding.net 私有库或企业版开源免费私有部署的Gogs具有丰富的认证方式选择并支持Slack平台的WebHookGitLab 可托管部署或私有部署建议在以上任何一种git服务中建立或使用一个支持证书登录的用户该证书用于构建环境自动拉取代码时使用不建议使用账号密码方式因为安全性较差。该证书对应的用户应该有所有需要自动构建的代码库读权限。不建议给该用户开放写权限避免一些潜在安全问题和代码冲突问题构建环境构建环境是专门用来自动化、编译、集成、测试、打包、发布的环境建议使用独立的计算机和服务器做为构建环境。因为该环境对代码和测试、生产环境都有较高的权限也会涉及到大量的安全证书或密钥等极敏感信息所以强烈建议该环境所在设备仅限极少数高度可信赖的人访问管理并有严格的安全规定不允许随便安装软件或向外复制数据建议使用linux作为构建环境的操作系统因为windows的命令行工具能力有限。并确保安装如下工具Node.js 我们需要依赖很多nodejs工具链所以这是必须安装的。通过下列命令确认是否安装以及什么版本node --version还需要确认npm是否安装npm --versionnpm安装新版可以通过自更新实现npm install -g npmShellJS 是在Node.js API之上的便携式(Windows/Linux/OS X)Unix Shell命令实现可以消除或减少您的Shell脚本对特定操作系统的依赖。通过下面的命令全局安装npm install -g shelljs老衣的实践是利用shelljs的脚本能力以及js语言的丰富特性和灵活度通过编写脚本的方式结合其他工具或平台实现编译、测试、打包、发布、通知等的自动化处理Docker 几乎是微服务架构的必需品我们通过Docker隔离和构建相关的服务通过下列命令确认是否安装以及什么版本docker --versiongit 确保安装使用了最新的git命令行客户端,可通过下列命令确认是否安装以及什么版本git --versionPython 很多辅助工具是用python开发的所以有备无患通过下列命令确认是否安装以及什么版本python --versionMono 是跨平台的.NET框架实现目前5.0以上版本已经完整匹配.NET 4.6.2的API集几乎除了Windows平台特有的API外Mono和.NET框架几乎是完全兼容的。所以如果你的微服务是用的.NET开发的这是必须安装的——当然如果你只打算在Windows上用可以不安装这个。通过下列命令确认是否安装以及什么版本mono --version.NET Core 如果你的微服务有用的.NET Core开发的这是必须安装的。通过下列命令确认是否安装以及什么版本dotnet --versionCake 是C# Make的缩写是一个基于C# DSL的跨平台自动化构建系统。它可以用来编译代码复制文件以及文件夹运行单元测试压缩文件以及构建Nuget包等等。TeamCity、Jenkins 这两个是独立的自动构建服务器软件如果不愿意使用shelljs之类的脚本自行编写构建任务可以通过他们在管理界面上设置不过学习成本和复杂度蛮高的如果团队内没有人熟悉这两个工具早期不建议使用。其他语言的基础框架根据实际的每个微服务所使用的语言环境决定安装哪些基础支持工具、框架、模块等自动构建服务在拉取代码、获取依赖包、编译、测试、打包、发布等各个环节都可能会发生错误或异常而编译不通过或测试不通过等情况也应该第一时间跟团队或项目管理者报告我们在实践中更推荐使用Slack来实现,实现方式请参考官方文档 https://api.slack.com/incoming-webhooks绝大部分情况下1-3行代码即可实现非常方便当然了这里有个小问题Slack的所有客户端web、手机App、桌面应用等都没有中文版。当然了你愿意使用电子邮件或企业微信或微信服务号来实现也可以只是实现成本和效果跟Slack比完全不在一个级别上。注意Slack的webhook地址通常类似于https://hooks.slack.com/services/T00000000/B00000000/XXXXXXXXXXXXXXXXXXXXXXXX 这属于保密的地址不应该在公开的文档或代码中体现否则容易在Slack的相关频道中产生虚假的信息切记当然了上述构建所需到基础依赖也可以在准备好到Docker容器中做可减少不同服务对基础依赖比如php、java、nodejs等版本不同而带来的构建冲突。早期团队可通过技术选型很大程度上避免基础依赖冲突所以可先不考虑使用大量Docker容器隔离不同到构建环境待团队逐渐成熟壮大后可根据需要慢慢向这个方式靠拢。我们在实践中更倾向于使用Docker构建相应的镜像来部署最终的微服务所以需要一些必要的基础镜像提前做准备。推荐安装如下镜像docker pull alpine docker pull busybox docker pull centos docker pull ubuntu docker pull postgres docker pull microsoft/dotnet docker pull microsoft/aspnetcore docker pull mono docker pull node docker pull golang docker pull mongo docker pull mysql docker pull redis docker pull rabbitmq docker pull memcached docker pull nginx docker pull openresty/openresty以上镜像如果需要安装特定版本可通过加tag参数来获取对应版本的镜像。特别一提的是其中alpine和busybox都是使用广泛的超小型基础镜像大小仅有几M而已对最终镜像大小很敏感的可考虑使用这些镜像作为基础。通过下列命令可在部署环境中确认上述镜像是否已经pull到docker images私有Docker Registry服务器Docker Store上已经有大量官方镜像和公开镜像可供参考使用但通常我们自己到私有服务的镜像是不希望公开让别人下载使用的。所以需要搭建自己的镜像库所以我们需要使用Docker Registry来搭建自己的私有镜像库。注意新版到registry要求必须使用https协议所以搭建时需要考虑购买或使用免费的SSL证书。目前国内的腾讯云和阿里云貌似都有免费一年到二级域名证书可考虑使用。使用私有的Docker Registry服务后每次构建好的服务镜像都是先push到私有服务上然后再在生产环境中pull最新版并部署使用。测试生产环境测试或生产环境基本上仅需要安装Docker通过相关的命令来拉取相关镜像并部署或维护所需服务的容器即可。单服务器上多个微服务如果有依赖关系或启动先后顺序等要求时建议使用Docker Compose对微服务的相关容器进行编排。是否使用Docker Swarm来做集群化编排更新则由实际业务要求和团队对Docker的熟练度来决定。需要特别指出的是关于数据库的处理微服务架构通常强调服务的无状态化其中原因主要是为了服务的可复制性和可迁移性所以目前的主流实践是数据库在正式生产环境时使用公有云的独立数据库服务或自己机房的独立数据库服务器的数据库不建议使用容器作为数据库的宿主——但测试环境可以根据情况考虑使用以方便模拟和测试各种情况。生产环境的服务发现考虑使用分布式服务发现和配置工具Consul。但如果微服务整体全部都是基于Docker的而且集群使用等Docker Swarm实现的那么就不必使用其他工具来做服务发现了因为docker swarm内置了服务发现机制。开发过程根据业务和系统架构拆分微服务为每个微服务设计 RESTful Web API的文档建议使用API Designer工具借助于RAML语言设计API根据API设计文档使用json-server来编写API的MockupWeb前端或App通过调用mock出来的api开发相关功能页面同时后端人员根据API设计文档开发后端API测试人员则根据设计文档和mockup版的API表现编写自动化测试脚本构建服务定期从git服务器上拉取最新代码根据项目的定义自动编译、测试相关微服务并通过Slack通知团队或项目负责人构建服务在编译和测试都通过后通过脚本自动构建Docker镜像然后push到私有的docker镜像库中并通过Slack通知团队或项目负责人测试生产环境根据提前编写好的脚本自动从私有镜像库中拉取最新版的服务镜像部署运行对应的新服务注意事项期间测试人员通过编写的自动化API测试脚本不断验证服务的最终表现是否正确不正确的不应该进入生产环境单元测试是开发人员自行编写的但是需要明确测试的命令行调用方式以便构建服务器能够不停的测试验证代码每个用到数据库的微服务都应该具备数据库的自动化迁移能力比如新增的表在服务启动时应该会自动创建而如果服务回滚到旧版本时必须能够将数据库对应回滚到原来的结构比如删除增加的表或字段等。前后端应该完全隔离在不同的镜像中在服务的总出口处通常是nginx之类带有反向代理的服务器将api和页面集成到同一域名下生产环境尽可能利用nginx服务器的热部署和热更新能力API的自动化测试可通过如下nodejs工具开发测试脚本chakrammockmochajs实际演练微服务有几个要点功能独立独立部署独立进程可替换性功能独立容易理解而难度是整体服务如何合理拆分其基本原则就是看该服务的数据是否需要关联另外一个服务的数据表才能查出如果是则应该合并到一个服务中否则可独立拆分独立部署和独立进程 告诉我们每个服务运行在独立的进程或服务器中每个服务都应该可以独立部署且不应该影响其他服务的运行可替换性则是说每个服务都应该可以随时用任何编程语言重新实现并替换掉旧有的服务而不会影响其他服务和整体系统的正常运行。鉴于每个微服务都可能随时更新为了保证整体系统运行的稳定性我们需要至少做到如下几点每个微服务的更新过程都要足够的快要尽可能在1秒内完成以便减少服务更新时出现的各种异常每个微服务调用其他服务时最后都具备retry机制以便在调用到的另外一个微服务出现短期故障比如更新服务时能够具备过一点时间后再访问的能力这样最前端的用户或者服务调用者会认为当前的服务调用速度只是比原来慢了而已集中的日志收集服务可以通过该服务的界面上查看到各个微服务的日志以便团队或项目领导者能够及时了解和分析系统的运行状态及时发现和修复各种问题异常微服务架构的复杂性决定了微服务总体出现异常的几率比单体系统要多所以单元测试和集成测试变得极其重要甚至可以说是必须的每个微服务对外的服务接口如WebAPI应该都是具备普适性、自释性、可预见性、明确性的。也就是每个微服务的对外接口实现可以不依赖于任何特定的语言每个微服务调用另外一个服务时都应该是尽可能使用通用的通信协议而不是私有的避免跨语言实现的难度即通畅是使用HTTP协议每个服务的最终路由地址是不可随意变更的每个服务的路径自身就是其功能的最简说明通过一个api地址就能较为准确的猜出另外一个服务的地址和访问方式基于以上观点并假定我们大部分的微服务都使用.NET、Mono或.NET Core编写那么如下相关的库和组件则应该是必须的或优先考量的NancyFx 这是一个非常优秀的Web框架独立于ASP.NET以外也就是说他自身不依赖System.Web命名空间下的任何东西。具有极好的跨平台支持能力和极佳的可测试性、组件可替换性、路由可视性。用它编写微服务会让开发过程变得很简单。当然如果你实在不喜欢这套框架的风格依然可以选用ASP.NET、ASP.NET MVC、ASP.NET Core等相对传统的技术毕竟这并不影响微服务实现的本身。Flurl 这是一个新出现的优雅的的Http客户端库它依然基于HttpClient但是提供了Fluent风格的编程模型所以写代码时会比传统方式要爽快和高效的多。虽然RestSharp也是一个很优秀的WebAPI访问库但是它目前不支持.NET Core等框架所以跨平台能力欠佳。xUnit.net 这是NUnit.net原作者几年前重新打造的一个.NET平台的单元测试框架具有丰富的工具链。微软官方的ASP.NET MVC等框架也都使用的该框架编写的所有单元测试而没有采用微软自家的单元测试框架。KestrelHttpServer 这是微软官方实现的ASP.NET Core的Host服务器框架可以实现极高性能的SelfHost服务而不必依赖IIS。TinyMapper 比老牌的AutoMapper具有更佳的性能大概快60倍而且老衣也曾贡献了部分代码增加了一些特性。不过目前对.NET Core支持方面还有点瑕疵但可以在.NET项目中大规模使用。Mapster 它跟TinyMapper一样是新型的Mapper库号称快速、有趣且激动人心的Mapper老衣目前在.NET Core项目中主要采用这个作为主要的Mapper库。Json.NET .NET平台下最流行的高性能但不是最快的JSON库。Polly 是.NET平台下,让开发者可以轻松实现线程安全的重试、熔断、超时代码大幅提升应用稳定性的绝对利器C-Sharp-Promise Promise在js开发领域大行其道深得开发者们的喜爱。C#程序员们可以用C-Sharp-Promise使用Promise的方式编程。一些时候你会发现它比async的方式更好Topshelf 当你需要将一个.NET的Console或者桌面应用作为Windows服务运行时它会很好的帮到你。它还支持Mono也就是说可以在Linux上玩Dapper 轻量级的通用ORM,支持市面上大多数关系型数据库。兼容.NET、Mono和.NET Core在.NET Core上使用Nancy创建一个Hello World级的微服务先确保node.js和npm已安装node --version npm --version如果上述命令显示未安装node.js请到Node.js官网下载对应操作系统的版本并安装好。确保yeoman已经安装yo --version如果上述命令未输出版本号提示yo命令不存在则应该使用下面到命令安装它npm install -g yo确保generator-aspnet已经安装yo aspnet如果上述命令提示未安装一个名叫aspnet的generator,则需要使用下面到命令安装generator-aspnetnpm install -g generator-aspnet上一步命令正常会输出如下结果     _-----_     ╭──────────────────────────╮|       |    │      Welcome to the      │|--(o)--|    │  marvellous ASP.NET Core │---------´   │        generator!        │( _´U_ )    ╰──────────────────────────╯/___A___\   /|  ~  |__.___.__´    |° ´ Y ? What type of application do you want to create? (Use arrow keys) ❯ Empty Web ApplicationEmpty Web Application (F#)Console ApplicationConsole Application (F#)Web ApplicationWeb Application Basic [without Membership and Authorization]Web Application Basic [without Membership and Authorization] (F#) (Move up and down to reveal more choices)按键盘的⬇键直到❯出现在Nancy ASP.NET Application这一行后按回车键在? Whats the name of your ASP.NET application? (NancyApplication)后面输入你的服务的名字比如hi后回车。提示: 由于这个名字会出现在代码中所以请使用类似C#变量的命名规范输入不要使用任何中文字符或-也不要是纯数字。假设上一步输入的名字是hi使用下列命令进入hi目录,并使用Visual Studio Code打开该目录cd hi code .用Visual Studio Code打开后查看修改global.json文件中的sdk version为最新安装的.NET Core SDK的版本。如果不知道当前安装的.NET Core SDK是什么版本可通过执行下列命令查看dotnet --version如果你安装的是1.0.4版则此时应该会看到结果是1.0.4。此时把global.json文件中version后面的版本号改为刚才命令中输出的版本号例如1.0.4。查看hi.csproj文件中的TargetFramework节点的值如果你打算使用.NET Core 1.0则应该是netcoreapp1.0,如果打算使用.NET Core 1.1则应该是netcoreapp1.1。你还需要根据自己选择版本修改Nuget引用的相关库的版本。还要修改AssemblyName的值为你期望的程序集名通常我们跟服务名一致本例中应该是hi这里我们使用.NET Core 1.1版来开发那么此时应该将hi.csproj文件的内容调整为如下Project ToolsVersion15.0  SdkMicrosoft.NET.Sdk.WebPropertyGroupTargetFrameworknetcoreapp1.1/TargetFrameworkDebugTypeportable/DebugTypeAssemblyNamehi/AssemblyNameOutputTypeExe/OutputType/PropertyGroupItemGroupPackageReference IncludeMicrosoft.AspNetCore.Hosting Version1.1.2 /PackageReference IncludeMicrosoft.AspNetCore.Server.Kestrel Version1.1.2 /PackageReference IncludeMicrosoft.AspNetCore.Owin Version1.1.2 /PackageReference IncludeNancy Version2.0.0-clinteastwood //ItemGroup/Project注意 Nancy的版本号应该使用最新支持.NET Core的版本。2.0.0-clinteastwood是编写此文时的NancyFx最新版。实际操练时需要根据新的情况对应修改。执行下列命令加载依赖包、编译并运行这个hi服务dotnet restore dotnet build dotnet run会看到如下结果Hosting environment: ProductionContent root path: /Users/yimingzhi/Projects/wang/hiNow listening on: http://localhost:5000Application started. Press CtrlC to shut down.在浏览器中访问http://localhost:5000地址可以看到Hello from Nancy running on CoreCLR。这样一个最简单的Hellow World级的微服务已经开发完成。其中HomeModule.cs文件就是这个服务的核心逻辑代码namespace hi {using Nancy;public class HomeModule : NancyModule{public HomeModule(){Get(/, args Hello from Nancy running on CoreCLR);}} }是不是非常简单呢 _目前这个服务看起来很美但是存在如下问题因此我们需要借助Docker这个容器工具来解决它如果你不确定是否安装了docker请运行下面的命令确认docker --version当然你也可以通过docker info查看docker的一些信息,还可以通过docker images查看已经pull到本地的docker镜像列表需要确认.NET Core的1.1.2运行时编写此文时的最新版本镜像是否存在,如果不存在请使用下列命令拉取镜像:docker pull microsoft/dotnet:1.1.2-runtime拉取完毕后我们就可以准备构建hi服务的docker镜像了需要运行主机上安装.NET Core的sdk或者运行时如果多个服务用的不同版本的.NET Core则可能会出现版本冲突等问题部署起来比较麻烦需要用ftp、rdp、ssh或rsync等工具部署大量文件基本不具备规模化横向部署能力比如一次性部署100个节点一旦服务崩溃或其他异常退出时无法自动重启在hi目录下创建一个名字为Dockerfile的文件——注意这个文件名是区分大小写并没有任何扩展名的用Visual Studio 2017或Visual Studio Code打开刚才创建的Dockerfile文件并将以下内容写入并保存FROM microsoft/dotnet:1.1.2-runtime ENV TZAsia/Shanghai COPY ./dist /app WORKDIR /app EXPOSE 5000/tcp ENTRYPOINT [dotnet,./hi.dll]提示ENV TZ Asia/Shanghai 表示该镜像的容器默认的运行时时区为上海时区这对于大陆的服务来说至关重要不加这行默认情况一般都是UTC时间切记 EXPOSE 5000/tcp 表示该镜像的容器默认向外暴露5000端口这里就是hi服务的默认端口 ENTRYPOINT [dotnet,./hi.dll] 中的hi.dll为服务的主运行程序不同服务这个地方会有所不同之所以使用runtime版的基础镜像而不是sdk版是因为sdk版的镜像太大大了几百兆对publish后的.NET Core应用也是没有必要的。在hi目录下运行下面的一组命令构建正式发布版的hi服务镜像dotnet restore dotnet build -c Release dotnet publish -c Release -o dist docker build -t hi . 使用docker images命令查看是否有名为hi的镜像出现在列表中。如果没有请检查上面的这组命令是否输出了什么错误信息修改代码并重新执行上面这组命令直到docker镜像列表中有名为hi的镜像出现为止。提示上面这组命令中hi服务被发布在dist目录中因此需要在.gitignore中将dist目录设置为忽略项不做版本控制。通过下面的命令执行hi镜像docker run --name hiServ --rm -p 5000:5000 hi此时会收到如下结果:Hosting environment: ProductionContent root path: /appNow listening on: http://localhost:5000Application started. Press CtrlC to shut down.第1行告诉你运行环境是产品环境还是开发环境第3行意思是你可以通过访问本机的http://localhost:5000看到结果。但是这时候我们在浏览器中访问http://localhost:5000会提示你:无法访问此网站。在另外一个控制台窗口(Windows下叫命令行窗口)中运行下面的命令进入容器docker exec -it hiServ bash运行命令:curl http://localhost:5000/此时会看到输出结果为Hello from Nancy running on CoreCLR,这说明hi服务可从内容内访问是容器外无法访问。输入exit命令退出容器打开Program.cs文件在.UseKestrel()下插入一行新代码.UseUrls(http://*:5000/)即Program.cs的代码变为namespace hi {using System.IO;using Microsoft.AspNetCore.Hosting;    public class Program{        public static void Main(string[] args){var host new WebHostBuilder()                .UseContentRoot(Directory.GetCurrentDirectory())                .UseKestrel()                .UseUrls(http://*:5000/)                .UseStartupStartup()                .Build();host.Run();}} }再次运行下面这组命令,重新构建hi服务的镜像dotnet restore dotnet build -c Release dotnet publish -c Release -o dist docker build -t hi .运行下面的命令临时将hi镜像以名为hiServ的容器运行docker run --name hiServ --rm -p 5000:5000 hi打开浏览器访问http://localhost:5000/,看到了Hello from Nancy running on CoreCLR。这样我们的hi服务容器镜像就算成功了。提示docker run --name hiServ --rm -p 5000:5000 hi 中的--name hiServ是为容器起一个特定名字hiServ; --rm是让这个容器在退出时立即删除; -p 5000:5000 是为了将容器的5000端口映射到当前主机的5000端口上以便在真机中可以测试访问这个服务。 最终生产环境中不会以当前用户的前置进程方式运行否则用户退出时容器就会结束运行。所以我们通常会额外附加-d参数让服务在后台执行详情请参考Docker CLI的官方文档。将hi服务增加数据有关的API前面我们演示了如何使用docker将一个.NET Core跑起来。下面为hi服务增加几个复杂的API增加一个服务状态的api在hi这个项目中增加一个新文件StatusModule.cs,内容如下namespace hi{using Nancy;    using System;    public class StatusModule : NancyModule{public StatusModule(){Get(/status, _ {                return new{running true,time DateTime.Now};});}} }用下面的一组命令运行起hi服务dotnet restore dotnet build dotnet run用postman或非windows系统的命令行工具curl访问http://localhost:5000/status会收到一个json数据类似于{running: true,time: 2017-05-16T18:03:34.486797008:00}但是如果你用浏览器访问http://localhost:5000/status你会收到一个错误页标题是500 - Internal Server Error不过你看不到错误内容——让你改一下代码才能看到错误。在hi目录中新建一个文件Bootstrapper.cs内容如下namespace hi{using Nancy;    public class Bootstrapper : DefaultNancyBootstrapper{public override void Configure(Nancy.Configuration.INancyEnvironment environment){#if DEBUGenvironment.Tracing(enabled: false, displayErrorTraces: true);#endif}} }再次编译运行dotnet restore dotnet build dotnet run用浏览器访问http://localhost:5000/status会看到一个很详细的Nancy.ViewEngines.ViewNotFoundException异常报告意思是找不到名为f__AnonymousType02的任何视图文件。这其实是Nancy框架的一个特性他可以实现一个地址即是webapi又是网页其具体的原理就是判断当前请求的Content-Type中是否以text/html开头如果是说明当前请求是一个浏览器那么Nancy就会使用当前地址返回的model类型名作为视图名查找视图文件找到了就用类似于mvc的方式显示视图页面如果找不到就报错。如果我们不希望用这个特性就是期望该地址永远返回一个json数据则需要使用Response.AsJson方法。StatusModule.cs应该修改为namespace hi{using Nancy;    using System;    public class StatusModule : NancyModule{public StatusModule(){Get(/status, _ {                return Response.AsJson(new{running true,time DateTime.Now});});}} }再次编译运行dotnet restore dotnet build dotnet run用浏览器访问http://localhost:5000/status我们会看到与在postman看到的一样漂亮的json数据。Great准备一个数据库为了跨平台我们选择PostgreSQL数据库。当然了在计算机上安装一个数据库是很麻烦的事情这里我们用Docker直接用官方的postgres镜像运行起一个容器即可。执行下列命令docker run --name pgdb -e POSTGRES_PASSWORD123456 -p 5432:5432 -d postgres此时我们就准备好了一个postgres数据库账号是postgres密码是123456并可以通过本机的5432端口访问到它。你可以使用pgAdmin链接并管理这个数据库老衣假定你已经学会使用pgAdmin的基本功能才继续下面的演练的。使用pgAdmin在pgdb这个数据库容器中创建一个新的数据库hiDB在hi项目上从Nuget引用最新版的Npgsql先在Bootstrapper.cs中通过Nancy自带的容器注册一个数据库连接namespace hi{using System.Data;    using Nancy;    public class Bootstrapper : DefaultNancyBootstrapper{public override void Configure(Nancy.Configuration.INancyEnvironment environment){#if DEBUGenvironment.Tracing(enabled: false, displayErrorTraces: true);#endif}        protected override void ConfigureApplicationContainer(Nancy.TinyIoc.TinyIoCContainer container){base.ConfigureApplicationContainer(container);container.RegisterIDbConnection((c, p) {                return new Npgsql.NpgsqlConnection(Hostlocalhost;Usernamepostgres;Password123456;DatabasehiDB;Timeout3);});}} }然后增加一个DbConnectExtensions.cs文件代码using System.Data;namespace hi {    public static class DbConnectExtensions{        public static bool IsConnectable(this IDbConnection conn){          bool connectable false;          try{conn.Open();var cmd conn.CreateCommand();cmd.CommandText select 1;;              int r (int)cmd.ExecuteScalar();connectable r 1;}          catch{connectable false;}          finally{              if (conn.State ConnectionState.Open){conn.Close();}}          return connectable;}} }之后在StatusModule.cs中增加数据库可连接性的状态检测代码如下namespace hi{using System;    using System.Data;    using Nancy;    public class StatusModule : NancyModule{public StatusModule(IDbConnection conn){Get(/status, _ {                return Response.AsJson(new{running true,dbConnectable conn.IsConnectable(),time DateTime.Now});});}} }再次调试运行hi项目然后访问http://localhost:5000/status会收到类似于下面这样的json数据:{running: true,dbConnectable: true,time: 2017-05-17T14:47:47.128705008:00 }其中dbConnectable表示数据库可连接性状态。如果你把数据库容器停止docker stop pgdb再次调试运行并访问http://localhost:5000/status会发现dbConnectable的值变为false了。 嗯脑袋聪明的你一定会想: 这个数据库检测的代码应该封装成一个方法这样就可以多个服务中都能方便做这个检查了; 而数据库连接字符串也应该用配置文件。老衣在这里先提示你这未必是好的至于原因吗后面会有讨论。当然了你也可以在这个api中增加CPU、内存等实时环境状态数据用于监控服务通过这个api定时获取到该服务的相关状态数据增加一组简单的新闻相关API先确保数据库容器pgdb处于启动状态docker start pgdb在数据库hiDB上建一张news表:create table news (id serial primary key,title varchar(200) not NULL,content text NOT NULL)在hi项目上从Nuget引用最新版的Dapper和Dapper.Contrib在hi项目中新建一个文件NewModule.cs:using System.Data;using Dapper.Contrib.Extensions;using Nancy;using Nancy.ModelBinding;namespace hi{public class NewsModule : NancyModule{public NewsModule(IDbConnection conn){Get(/api/news, _ {                return Response.AsJson(conn.GetAllNews());});Post(/api/news, _ {                var model this.BindNews();                var insertNumber conn.Insert(model);                return Response.AsJson(insertNumber 0);});Get(/api/news/{id:int}, x {                int id x.id;                var model conn.GetNews(id);if (model null){                    return Response.AsJson(new { error 该新闻不存在 }).WithStatusCode(HttpStatusCode.NotFound);}                return Response.AsJson(model);});}}[Table(news)]    public class News{public int id{            get;            set;}        public string title{            get;            set;}        public string content{            get;            set;}} }为了简单期间我把News的定义放在了同一个文件中实际项目中不应该这样写即增加了3个API用postman向http://localhost:5000/api/news POST 一条json数据:{title:test,content:this is only one test}收到true说明添加成功接着GET请求http://localhost:5000/api/news会获取含有刚添加的新闻的数组json[{id: 1,title: test,content: this is only one test} ]GET 请求 http://localhost:5000/api/news/1会获取id为1的新闻数据json:{id: 1,title: test,content: this is only one test}GET 请求 http://localhost:5000/api/news/2会获取一个状态码为404的json数据:{error: 该新闻不存在}很棒啊, 3个API都表现正常了。是不是感觉用Nancy替代ASP.NET MVC用Dapper替代EntityFramework 写代码变得更畅快淋漓呢嗯不过也不是说这些替代就任何场合都会更好这完全可以由开发者或团队根据实际情况自由控制。 但老衣在这里主要是想说一些其他选择也许可以让你的开发变得轻松有趣一些了甚至更多好处。总之以后我们也就可以使用以上类似的方式很快速的创建其他微服务了。所有新闻的列表添加一个新闻获取指定id的新闻嗯到这里不知道你是否想起了前面的数据库连接字符串中包含了Hostlocalhost 也就是说数据库服务器的所在地是本机。而我们的数据库实际上是在另外一个容器中只是因为我们运行容器时把本机端口和容器做了个映射所以造成了数据库在本机的假象。我们到底应该如何处理呢一台生产环境的机器上可能会有多个数据库容器或者其他服务的容器我们不能也不应该把所有服务的端口都在真实生产环境中上进行映射暴露。生产环境中数据库容器或类似的涉及信息安全的容器都应该尽可能不允许外部直接访问而是使用容器的相关通信手段。这样一来我们的hi服务访问的数据库应该是pgdb这个容器。所以在部署非开发阶段应该将Host的值改为pgdb并使用link方式将hi服务的容器与pgdb进行连接直接通过容器通信。改完数据库连接字符串后重新制作hi服务的容器镜像dotnet restore dotnet build -c Release dotnet publish -c Release -o dist docker build -t hi .构建完成后以容器方式连接pgdb并运行docker run --name hiServ --rm -p 5000:5000 --link pgdb:pgdb hi访问http://localhost:5000/status收到{running: true,dbConnectable: true,time: 2017-05-18T14:52:30.300905008:00 }可以看出数据库连接是成功的很棒。即使你把pgdb容器的端口映射移除也会发现hi服务仍可以访问pgdb容器的数据库但是你的pgadmin就访问不了挺安全吧 :D 至于如何跨服务器连接容器通信这属于高级话题有机会再细聊或者直接查相关文档和书籍研究一下。看起来这时候我们用一个数据库容器和一个hi服务容器实现了期望的功能是不是完美了呢当然不完美因为不完美的人类永远造不出完美的事物来只能尽力无限趋向完美来自老衣语录^_^把数据库容器也作为一个微服务我们都知道是个服务就有升级、更新、重启或宕机的时候。我们希望数据库容器中的数据库因某原因出现崩溃退出时能够自动重启数据库服务以便达到整体系统的高可用度。docker运行容器时指定--restart参数就可以非常简单做到这一点Great看起来很美是不是但请想一下如果用户刚好在数据库崩溃时访问了hi服务是不是就会收到服务器端异常呢就前面实现的代码来说答案是肯定的。那么怎么能够让用户感受不到这中间的短时间数据库失联呢前文中我们提到过.NET领域有个很好的库Polly可以再遇到一些异常时实现Retry。现在用它改造一下hi服务的代码让新闻有关的几个api实现数据库链接异常时自动Retry先在hi项目上用Nuget引用Polly然后修改NewsModule.cs的代码为using System.Data; using Dapper.Contrib.Extensions; using Nancy; using Nancy.ModelBinding; using Polly;namespace hi {    public class NewsModule : NancyModule{        public NewsModule(IDbConnection conn){Get(/api/news, _ {var list DbRetry().Execute(() { return conn.GetAllNews(); });                return Response.AsJson(list);});Post(/api/news, _ {var model this.BindNews();var insertNumber DbRetry().Execute(() { return conn.Insert(model); });                return Response.AsJson(insertNumber 0);});Get(/api/news/{id:int}, x {                int id x.id;var model DbRetry().Execute(() { return conn.GetNews(id); });                if (model null){                    return Response.AsJson(new { error 该新闻不存在 }).WithStatusCode(HttpStatusCode.NotFound);}                return Response.AsJson(model);});}Policy DbRetry(){            return Policy.HandleSystem.Net.Sockets.SocketException()                         .OrNpgsql.NpgsqlException()                         .RetryForever();}}[Table(news)]    public class News{        public int id{get;set;}        public string title{get;set;}        public string content{get;set;}} }编译运行hi服务后不要着急访问news相关api先模拟数据库失联docker stop pgdb等数据库停止后用浏览器访问http://localhost:5000/api/news你会发现浏览器一直在等待服务器响应…… 立即再切换到刚才的命令行窗口启动数据库容器docker start pgdb再次切换到刚才访问http://localhost:5000/api/news那个浏览器窗口你会惊奇的发现有数据了而不是返回数据库异常这就是Polly的强大之处。当然了这个库还有很多强大功能请移步到官方网站查看文档学习一下吧。到这里聪明的你是否已经联想到其他情况一个微服务请求另外一个微服务的时候也可以用Polly实现高可用呢用用OpenResty吧OpenResty 是一款基于 NGINX 和 LuaJIT 的 动态Web 平台。你可以根据自己的需求设定启用的模块并编译生成自己定制的OpenResty。官方的Docker镜像默认包含了一些模块file-aiohttp_addition_modulehttp_auth_request_modulehttp_dav_modulehttp_flv_modulehttp_geoip_moduledynamichttp_gunzip_modulehttp_gzip_static_modulehttp_image_filter_moduledynamichttp_mp4_modulehttp_random_index_modulehttp_realip_modulehttp_secure_link_modulehttp_slice_modulehttp_ssl_modulehttp_stub_status_modulehttp_sub_modulehttp_v2_modulehttp_xslt_moduledynamicipv6mailmail_ssl_modulemd5-asmpcre-jitsha1-asmstreamstream_ssl_modulethreads其中http_auth_request_module可以实现简单的登录后访问某资源的功能并且身份验证和资源是分开的具体介绍可参考http://ohmycat.me/nginx/2016/06/28/nginx-ldap.htmlhttp_image_filter_module模块则可以轻松实现实时缩略图、图片旋转等,可参考 https://ruby-china.org/topics/31498 了解。其他模块功能请自行Google吧。OpenResty继承了Nginx的高性能、反向代理等优点外还支持使用Lua脚本编写动态逻辑代码甚至是服务器端视图。这里我们说一下如何用openresty简单实现前面的新闻列表api的功能。先在一个新建的目录中创建一个Dockerfile用来创建带有postgres访问能力的openresty镜像:FROM centos:7MAINTAINER Evan Wies evanneomantra.net# Docker Build Arguments ARG RESTY_VERSION1.11.2.3 ARG RESTY_LUAROCKS_VERSION2.3.0 ARG RESTY_OPENSSL_VERSION1.0.2k ARG RESTY_PCRE_VERSION8.39 ARG RESTY_J1 ARG RESTY_CONFIG_OPTIONS\    --with-file-aio \    --with-http_addition_module \    --with-http_auth_request_module \    --with-http_dav_module \    --with-http_flv_module \    --with-http_geoip_moduledynamic \    --with-http_gunzip_module \    --with-http_gzip_static_module \    --with-http_image_filter_moduledynamic \    --with-http_mp4_module \    --with-http_random_index_module \    --with-http_realip_module \    --with-http_secure_link_module \    --with-http_slice_module \    --with-http_ssl_module \    --with-http_stub_status_module \    --with-http_sub_module \    --with-http_v2_module \    --with-http_xslt_moduledynamic \    --with-ipv6 \    --with-mail \    --with-mail_ssl_module \    --with-md5-asm \    --with-pcre-jit \    --with-sha1-asm \    --with-stream \    --with-stream_ssl_module \    --with-threads \    --with-http_postgres_module \    # These are not intended to be user-specified ARG _RESTY_CONFIG_DEPS--with-openssl/tmp/openssl-${RESTY_OPENSSL_VERSION} --with-pcre/tmp/pcre-${RESTY_PCRE_VERSION}# 1) Install yum dependencies# 2) Download and untar OpenSSL, PCRE, and OpenResty# 3) Build OpenResty# 4) CleanupRUN \    yum install -y \        gcc \        gcc-c \        gd-devel \        GeoIP-devel \        libxslt-devel \        make \        perl \        perl-ExtUtils-Embed \        readline-devel \        unzip \        zlib-devel \        postgresql-devel \     cd /tmp \     curl -fSL https://www.openssl.org/source/openssl-${RESTY_OPENSSL_VERSION}.tar.gz -o openssl-${RESTY_OPENSSL_VERSION}.tar.gz \     tar xzf openssl-${RESTY_OPENSSL_VERSION}.tar.gz \     curl -fSL https://ftp.pcre.org/pub/pcre/pcre-${RESTY_PCRE_VERSION}.tar.gz -o pcre-${RESTY_PCRE_VERSION}.tar.gz \     tar xzf pcre-${RESTY_PCRE_VERSION}.tar.gz \     curl -fSL https://openresty.org/download/openresty-${RESTY_VERSION}.tar.gz -o openresty-${RESTY_VERSION}.tar.gz \     tar xzf openresty-${RESTY_VERSION}.tar.gz \     cd /tmp/openresty-${RESTY_VERSION} \     ./configure -j${RESTY_J} ${_RESTY_CONFIG_DEPS} ${RESTY_CONFIG_OPTIONS} \     make -j${RESTY_J} \     make -j${RESTY_J} install \     cd /tmp \     rm -rf \        openssl-${RESTY_OPENSSL_VERSION} \        openssl-${RESTY_OPENSSL_VERSION}.tar.gz \        openresty-${RESTY_VERSION}.tar.gz openresty-${RESTY_VERSION} \        pcre-${RESTY_PCRE_VERSION}.tar.gz pcre-${RESTY_PCRE_VERSION} \     curl -fSL http://luarocks.org/releases/luarocks-${RESTY_LUAROCKS_VERSION}.tar.gz -o luarocks-${RESTY_LUAROCKS_VERSION}.tar.gz \     tar xzf luarocks-${RESTY_LUAROCKS_VERSION}.tar.gz \     cd luarocks-${RESTY_LUAROCKS_VERSION} \     ./configure \        --prefix/usr/local/openresty/luajit \        --with-lua/usr/local/openresty/luajit \        --lua-suffixjit-2.1.0-beta2 \        --with-lua-include/usr/local/openresty/luajit/include/luajit-2.1 \     make build \     make install \     cd /tmp \     rm -rf luarocks-${RESTY_LUAROCKS_VERSION} luarocks-${RESTY_LUAROCKS_VERSION}.tar.gz \     yum clean all \     ln -sf /dev/stdout /usr/local/openresty/nginx/logs/access.log \     ln -sf /dev/stderr /usr/local/openresty/nginx/logs/error.log# Add additional binaries into PATH for convenience ENV PATH$PATH:/usr/local/openresty/luajit/bin/:/usr/local/openresty/nginx/sbin/:/usr/local/openresty/bin/ENTRYPOINT [/usr/local/openresty/bin/openresty, -g, daemon off;]用这个文件构建一个镜像建议翻墙状态构建你懂的docker build -t openresty:postgres .在另外一个新建的目录假设名字叫newsOR中新建一个index.html文件html ng-appappheadtitle新闻列表/titlelink hrefhttps://cdn.bootcss.com/bootstrap/3.3.7/css/bootstrap.min.css relstylesheet/headbodydiv classcontainer ng-controllernewsListCtrlh2新闻列表/h2table classtable table-borderedtheadthID/thth标题/thth内容/th/theadtbodytr ng-repeatitem in listtd ng-binditem.id/tdtd ng-binditem.title/tdtd ng-binditem.content/td/tr/tbody/ul/divscript srchttps://cdn.bootcss.com/angular.js/1.6.4/angular.min.js/scriptscriptangular.module(app, []).controller(newsListCtrl, function ($scope, $http) {$scope.list [];$http.get(/api/news).then(function (res) {$scope.list res.data;});});    /script/body/html在newsOR目录中创建一个nginx.conf文件#user  nobody;worker_processes  1;events {    worker_connections  1024; }http {    include       mime.types;    default_type  application/octet-stream;    sendfile        on;    keepalive_timeout  65;    upstream pgsql {        postgres_server pgdb:5432 dbnamehiDB password123456 userpostgres;        postgres_keepalive off;}    server {        listen       80;        server_name  localhost;        charset utf-8;        location / {            root /www;            index index.html;}        location /api/news {            postgres_pass pgsql;            rds_json on;            postgres_query select * from news;}} }在上一步创建的目录中创建一个Dockerfile文件FROM openresty:postgres ENV TZAsia/Shanghai COPY ./nginx.conf /usr/local/openresty/nginx/conf/nginx.confEXPOSE 80ENTRYPOINT [/usr/local/openresty/bin/openresty, -g, daemon off;]构建这个镜像:docker build -t news:or .连接pgdb容器运行上一步构建的镜像:docker start pgdb docker run --rm -p 8000:80 --link pgdb:pgdb news:or用浏览器访问http://localhost:8000/api/news你会看到跟之前我们访问hi服务的新闻列表api获得一样的json数据。但这里我们实际上只是在nginx.conf中加了几行配置就实现了。更加简单快速虽然第一次准备支持postgres的openresty镜像有点慢但是这个镜像是可以复用的不必每次都重新构建访问http://localhost:8000/可以看到通过这个api获取数据后绑定到前端页面列表如果我们先把之前的hi服务容器hiServ运行起来并把前面步骤中的nginx.conf中到/api/news的配置改为:proxy_set_header Content-Type application/json;proxy_pass http://hiServ:5000/api/news;然后再次构建news:or镜像并重新运行这个镜像的容器你会发现效果跟第5步一样。只是原来上是通过反向代理访问了hiServ上的新闻api而已。至于如何使用openresty设置更复杂的反向代理、运行Lua脚本等细节请移步到http://agentzh.org/misc/slides/ngx-openresty-ecosystem/#1 了解一些相关特性也到https://moonbingbing.gitbooks.io/openresty-best-practices/content/查看相关最佳实践一次学习终生受用。用shelljs自动拉取代码构建镜像并推送Slack通知你可能已经发现了我们前面的内容中几乎所有的编译、发布、打包、容器的运行停止等都是用命令行来做的。原因就是命令行指令可以写成批处理脚本脚本并不涉及鼠标点击等人工干预所以就可以让机器自按照一些计划安排自动反复执行。我们在前面提到过的xunit.net、mocha等测试框架甚至grunt、gulp、webpack等web前端打包工具也都有对应的命令行CLI支持所以也都可以很方便的自动化执行。我们选择shelljs作为批处理工具的原因是使用熟悉的javascript语言写比其他有很多额外的好处比如更丰富的基础类库、工具链、依赖包等。至于Slack的介绍前面已经讲过我们主要用它把机器和人之间的交互打通让机器积极主动的告诉人或团队一些代码发生了什么事情。提示下面的例子代码可能仅兼容MacOS或Linux不支持Windows准备 Slack 集成的WebHook:假设你已经在Slack上注册并创建了自己的团队url是https://XXX.slack.com。那么请登录后访问https://XXX.slack.com/apps/manage/custom-integrations在Incoming WebHooks configuration中添加真对您某个频道(假设是YYYY)的配置此时会获得一个WebHook的Url,我们假设这个Url是https://hooks.slack.com/services/T00000000/B00000000/XXXXXXXXXXXXXXXXXXXXXXXX。incoming-webhooks的使用方式可以查看 https://api.slack.com/incoming-webhooks切换到hi服务所在目录的父级目录中新建一个用于推送Slack通知的slack.sh文件:channel$1nickname$2msg$3datapayload{\text\: \${msg}\, \channel\: \#${channel}\, \username\: \${nickname}\, \icon_emoji\: \:monkey_face:\}curl -X POST \ --data-urlencode ${data} \https://hooks.slack.com/services/T00000000/B00000000/XXXXXXXXXXXXXXXXXXXXXXXX新建一个build.jsvar SECONDS_FOR_BUILD 1 * 60; //两次构建之间间隔的秒数exec(clear);echo(每隔 SECONDS_FOR_BUILD 秒检查git代码是否更新);function build_dotnet(){cd(hi);    echo(从git上拉取最新代码...);exec(git pull);    echo(清理编译环境...); //清理的目的是为了强制更新可根据自己的实际需求决定是否需要这样做rm(-rf, ./bin);rm(-rf, ./obj);rm(-rf, ./dist);exec(dotnet clear);    echo(编译并发布...);exec(dotnet restore);    var buildResult exec(dotnet build -c Release);    if (buildResult.code ! 0) {        echo(编译失败!);exec(sh slack.sh YYYY CSharp编译器 糟糕代码没有编译通过, { silent: true });        return;}exec(dotnet publish -c Release -o dist);    echo(构建Docker镜像...);exec(docker build -t hi .);    //echo(推送镜像到私有Docker Registry)//exec(docker push xxx.xxxxx.xxx/hi); //将 xxx.xxxxx.xxx 改为自己的私有Docker Registry地址echo(完成);exec(sh slack.sh YYYY 构建器 恭喜主人新版本的镜像已经准备好了, { silent: true });cd(../); }build_dotnet(); //立即执行一次构建setInterval(function () {build_dotnet(); }, SECONDS_FOR_BUILD * 1000);运行命令:shjs build.js这个构建程序会每个一段时间上面代码中为60秒拉取最新代码然后编译、发布、打包docker镜像、推送docker镜像等一系列动作。如果代码没有编译通过你会在Slack中收到一个来自机器人CSharp编译器的消息推送糟糕代码没有编译通过如果编译通过、打包好新的Docker镜像推送至私有Docker Registry上后你又会在Slack中收到一个来自机器人构建器的消息推送恭喜主人新版本的镜像已经准备好了。是不是很酷呢团队或项目主管可以第一时间通过Slack了解构建程序执行的情况。生产环境中则可以用build.js类似的方式定期从私有Docker Registry上使用docker pull xxx.xxxxx.xxx/hi的命令拉取最新hi服务的Docker镜像并用新的镜像替换旧的。甚至可以使用docker-compose或docker-swarm等做docker的编排和集群更新。这里因为各自情况不同老衣就不给例子代码了你发挥一下自己的想象力和编程能力吧。附录有基于OpenResty的国产API网关:Orange, 可以参考学习一下因为中文文档的产品对国人来说太好了.NET Core领域的很多优秀库和项目可以参考https://github.com/thangchung/awesome-dotnet-coreActor Model(Actor模型)也可以实现一些场景的微服务建议学习和了解一些成熟的框架(例如 Akka、Akka.NET、Orleans等);目前我比较喜欢的是Proto.Actorzeit 是一家微服务领域很神奇的公司开源了不少不错的库比如pkg可以把nodejs项目直接打包成可执行程序而不必依赖Node.js环境micro 轻松实现基于Node.js的异步HTTP微服务hyper 构建在Web技术上的终端控制台;next.js 是在服务器端渲染React应用的框架。仔细学习研究一下会有很多收获的Jint 是一个.NET版的javascript解释器它提供了对ECMA5.1的完整兼容并且可以运行在任何.NET平台上。由于它不会动态生成.NET代码也不使用DLR所以它可以很快的运行相对较小的js脚本。NLua 是绑定.NET世界和Lua世界的项目。它可以让你在Windows, Linux, Mac, iOS , Android, Windows Phone 7 and 8等几乎任何C#应用中实现.NET与Lua的相互调用。原文http://yimingzhi.net/2018/03/lao-yi-de-wei-fu-wu-shi-jian-jian-yao-zhi-yin-2017-ban.NET社区新闻深度好文欢迎访问公众号文章汇总 http://www.csharpkit.com
http://mrfarshtey.net/news/16775/

相关文章:

  • 官方网站做背景墙厂家如何在网站中做内部链接
  • 什邡网站建设网站 验证
  • 平顶山市住房和城乡建设局网站wordpress 下载管理
  • wordpress仿站教学网站木马 代码
  • 网站由哪些部分组成部分网站服务费网络建设会计分录
  • 网站备案信息修改微网站简介
  • 什么叫网站开发陕西seo
  • 个人怎样免费建网站关于 建设 二级网站
  • 网站建设实验总结报告h5代码用什么软件编程
  • 怎么给网站设置关键字wordpress设置视频图片不显示图片
  • 网站无法下载视频 怎么做电脑做软件的app
  • 企业网站服务器跟域名都需要交钱吗企业logo标志设计公司
  • 河池网站建设教学网站怎么做
  • 如何网站防止采集国外工程建筑网站
  • 校园网站建设素材怎样建设个人手机网站
  • 怎样做才能让百度前两页有自己网站内容服装 多语言 网站源码
  • 网站备案 影响吗一学一做教育视频网站有哪些内容
  • 农产品网站设计推广普通话内容50字
  • 女生自己做网站女装网站建设的困难和不足
  • 沈阳 商城 网站 开发IP怎么屏蔽网站域名
  • 具有价值的专业网站建设平台做一网站APP多少钱
  • 在线相册jsp网站开发与设计建设网站功能
  • 备案空壳网站杭州老牌的网站建设
  • 石家庄网站建设电商广州建设工程造价管理站网站
  • 网站群建设 公司有没有做租赁的网站
  • 官方网站管理办法wordpress 交易
  • 站群cms系统呼叫中心系统源码
  • 网站运行方案六安网络上
  • 商城网站后续费用怎样登录沈阳科技网站
  • 网站设计师职位认识一键抠图永久免费