Docker 常用命令

Docker 常用命令

操作容器

启动容器

  • 启动容器并启动bash(交互方式):
$docker run -i -t <image_name/continar_id> /bin/bash
  • 启动容器以后台方式运行(更通用的方式):
$docker run -d -it  image_name

ps:这里的 image_name 包含了tag:hello.demo.kdemo:v1.0

附着到容器

  • 附着到正在运行的容器
docker attach <idcontainer_name>
  • 进入正在运行的容器内部,同时运行bash(比attach更好用)
docker exec -t -i <id/container_name>  /bin/bash

ps:docker exec是如此的有用,以至于我们通常是将其封装为一个脚本,放到全局可调用的地方,比如,可以写成一个indocker.sh:

$cat indocker.sh 
docker exec -t -i $1 /bin/bash
# 查看需要附着的容器id
$docker ps | less -S
CONTAINER ID        IMAGE                                                 
9cf7b563f689        hello.demo.kdemo:v160525.202747

$./indocker.sh 9cf7b563f689

查看容器日志

  • 查看容器日志
docker logs <id/container_name>
  • 实时查看日志输出
docker logs -f <id/container_name> (类似 tail -f) (带上时间戳-t

查看容器

  • 列出当前所有正在运行的container
$docker ps
  • 用一行列出所有正在运行的container(容器多的时候非常清晰)
$docker ps | less -S
  • 列出所有的container
$docker ps -a  
  • 列出最近一次启动的container
$docker ps -l 
  • 显示一个运行的容器里面的进程信息
$docker top Name/ID  
  • 查看容器内部详情细节:
$docker inspect <id/container_name>
  • 在容器中安装新的程序
$docker run image_name apt-get install -y app_name  

Note: 在执行apt-get 命令的时候,要带上-y参数。如果不指定-y参数的话,apt-get命令会进入交互模式,需要用户输入命令来进行确认,但在docker环境中是无法响应这种交互的。apt-get 命令执行完毕之后,容器就会停止,但对容器的改动不会丢失。

  • 从容器里面拷贝文件/目录到本地一个路径
$docker cp Name:/container_path to_path  
$docker cp ID:/container_path to_path
  • 保存对容器的修改(commit) 当你对某一个容器做了修改之后(通过在容器中运行某一个命令),可以把对容器的修改保存下来,这样下次可以从保存后的最新状态运行该容器。
$docker commit ID new_image_name  

Note: image相当于类,container相当于实例,不过可以动态给实例安装新软件,然后把这个container用commit命令固化成一个image。

  • 删除单个容器
$docker rm Name/ID 

-f, –force=false; -l, –link=false Remove the specified link and not the underlying container; -v, –volumes=false Remove the volumes associated to the container

  • 删除所有容器
$docker rm `docker ps -a -q`
  • 停止、启动、杀死、重启一个容器
$docker stop Name/ID  
$docker start Name/ID  
$docker kill Name/ID  
$docker restart name/ID

操作Image

  • 列出镜像
$sudo docker images

-a, –all=false Show all images; –no-trunc=false Don’t truncate output; -q, –quiet=false Only show numeric IDs

  • 从dockerhub检索image
$docker search image_name
  • 下载image
$docker pull image_name
  • 删除一个或者多个镜像;
$docker rmi image_name  

-f, –force=false Force; –no-prune=false Do not delete untagged parents

  • 显示一个镜像的历史;
$docker history image_name
  • 发布docker镜像
$docker push new_image_name

ps:要发布到私有Registry中的镜像,在镜像命名中需要带上Registry的域名(如果非80端口,同时需要带上端口号)比如:

$docker push dockerhub.yourdomain.com:443/hello.demo.kdemo:v1.0
  • 拉取docker镜像
$docker pull image_name

网络操作

  • 查看docker0的网络(宿主机上操作)
$ip a show docker0
  • 查看容器的IP地址
$docker inspect -f '{{ .NetworkSettings.IPAddress }}' <idcontainer_name>

附着到容器内部查看其内部ip:

$ip a show eth0

查看docker基础信息

查看docker版本

$docker version

查看docker系统的信息

$docker info

Posted by: 大CC | 29MAY,2016 博客:blog.me115.com [订阅] Github:大CC

iOS 即时通讯 + 仿微信聊天框架 + 源码

这些你造吗?

即时通讯(IM),在IOS这片江湖里面已经算是一个老者了,我这小旋风也是在很早以前巡山的时候,就知道有即时通讯这个妖怪,以前也多多少少接触过一些,在造APP的时候用过,哎呀,说着说着就感觉要跑题了,脑海中怎么波涛翻滚的样子。不好,才开头了两句,~~(>_<)~~忍住!!说回正题,我知道的即时通讯有用 CocoaAsyncSocket 还有 XMPPFramework 实现的,当然也有现在也有大部分接入环信的。有些东西掩盖不住的,可能可能许多同行和我一样,感觉你知道的越来越多,就越会觉得自己像是个文盲,其实不懂的真的还有好多好多,还有那么多等着你去学习!!这时候果断就要引入几句有哲学含义的话,安慰一下自己。比如“你一定要努力,但千万别着急”。O(∩_∩)O哈哈~,感觉会被简书的孩子打,不能装逼了。前面那句是简书主编的一本书的书名,顺便也推荐给大家去看看,陶冶一下情操吧。

应该很多同行也都知道这些个三方,知道他们是怎么用的,要是还清楚网络协议底层的知识,其实挺让我羡慕的,毕竟不是计算机专业的,懂得不多,也知道还有很多和我一样,半路出家的孩子,我还是建议有时间的话去了解学习一下这些最底层,最基础的东西。我也在不断的学习尝试和总结中,相信这些底层的基础的是你走向大神路,必不可少的,恩,对,必不可少 !

这篇文章我们就说说怎样自己搭建一个仿模仿微信的聊天框架和怎样搭建Openfire服务器来实现一个模仿微信的聊天。

要不造该咋办?

下面是一些应该能帮到大家的东西,可以去好好看一看,当然很多理论上的东西,你得花时间,前提是你得有时间(=@__@=)!!

一: Socket通信 花时间去了解一下吧,在接触 CocoaAsyncSocket 之前,记得先了解一下它的原理也是必要的,因为 CocoaAsyncSocket 是对苹果 CFNetwork 的封装,所以你了解一下 CFNetwork 也是好滴。

二: XMPP协议 了解XMPP到底是什么,我知道可能有许多刚PX出来的宝宝们,也经常听到会用XMPP做即时通讯,那你知道 XMPP 到底是什么吗?了解他是什么了,OK ,它里面一些类和一些方法的使用,也全都在 XMPPFramework使用 这里了。

三: 环信,我自己觉得它真的是给前端开发者省了很大很大的功夫,其实有详细开发文档的东西难度都是下降了一万点的。它还有UI聊天框架,你要做的,就是把它接到自己的APP里面,还有啥好说的,说了也说不过官方文档。O(∩_∩)O哈哈~

下面你要看的其实好多!

还是按顺序来吧,我们先说说微信的整个聊天框架搭建的一个思路(对方不想和你说话,并向你丢了一堆 高仿微信聊天框架源码 I Need You 星星!)。我也是看着 CocoaChina 上大神的一步步分析,模仿写的,在源代码里面注释满满的,你要认真看,应该没问题!

 

说说上面的框架,采用的是在 控制器1 中添加 控制器2 和控制器3 的方式,控制器1就是一个普通的控制器,他是控制器2 和 控制器3 的代理,控制器2呢就是一个 TableViewController 用来显示我们的聊天消息的。控制器3就主要是控制下面键盘部分的东西,键盘部分其实感觉应该是整个的关键,也是有键盘输入,语音,表情,更多这几部分组成的。整个一个主要的思路就是,控制器3 上的事件都是点击后它的代理 控制器1 响应,然后在控制器1 中去改变 控制器2 上要显示的东西。

简单罗列一下 Git上源码的一个大概,方便大家看吧。

整个聊天框架在 Chat 文件夹里面,Chat文件夹下面的 Models 里面存放的肯定就是各种 Model了,在 Views 文件夹下面,Message 存放的是各种要显示的 cell , Box 存放的是各种控制器3里面的界面。

控制器1: XYDJViewController   控制器2:ZXChatMessageController  控制器3:ZXChatBoxController

代码里面有详细的注释说明  点击下载聊天框架源码

说说聊天服务器吧!

以前项目有用到 AsyncSocket,环信就不多说了,说再多也然并卵,你能说的比人家官方文档详细吗?O(∩_∩)O哈哈。。看着文档去啪啪啪敲键盘就够了,那就说说 XMPPFramework 吧。你要是没有后台也没有服务器,自己玩,那就和自己玩自己一样,我是利用 Openfire 服务器和 Mysql 数据库 还有 Spark 或者Adium 前端来使用的。

关于 Openfire 服务器和 Mysql 数据库的安装配置,大家去看看这篇  Openfire配置安装

根据我自己配置的情况,给大家说说下面注意的几点!先记得去配置安装数据库,再去配置安装服务器!当然也不是死的,只是这个顺序合理一点!

配置数据库的时候有几个注意点提一下:

 

在这个新的 Connections 下面去添加一个新的数据库,在配置服务器的时候,大家一定注意:

一: 你得去安装 Java 的JDK。我也试着安装过最新的JAVA环境,事实证明,还是得安装JDK。。JAVA/jdk链接  安装 8U91版 MAC OSX的。

二: 如果还是开启失败。可能就是权限的问题,在终端中输入:

sudo chmod -R 777 /usr/local/openfire/bin
sudo su
cd /usr/local/openfire/bin
export JAVA_HOME=`/usr/libexec/java_home`
echo $JAVA_HOME /Library/Java/JavaVirtualMachines/jdk1.8.0_51.jdk/Contents/Home
cd /usr/local/openfire/bin
./openfire.sh

三: 在链接数据库的时候:

 

用户名和密码,就按照你刚开始在配置数据库的时候的用户名和密码填写。和上上图的 1 2 对应。

还有这个数据库URL,你在最上面选择了 .MySQL之后,下面就会有自动的填充,其中的 [host-name]就是你配置数据库的时候 3 处的地址,[database-name] 就是你在配置数据库的时候在新建的 Connections 里面创建的数据库的名字,不是 Connections 的名字,注意一下。

Spark 或者 Adium 大家点击链接下载。你可以两个都下载了,创建两个用户,分别登录加好友聊聊天试试,看服务器有没有什么问题。还有问题的可以加我QQ详聊,QQ我博客园首页有。

那结果呢?

先是创建了两个用户,聊两句天看看我们的服务器OK么,结果它是没问题的。

 

那我们用XMPPFramework呢?

关于 XMPPFramework具体的方法怎么使用 的,我就不再多说了,你可以找到的太多太多的文章去学习。在上面的框架中简单的测试了一下能不能接收到消息,证明是没问题的。

顺便说说这个XMPP接收到的数据格式是 XML 格式的,有些宝宝可能接触到的 JSON 格式的数据比较多,XML格式的数据解析不怎么熟悉,链接XML解析 给大家,数据解析的讲解的非常详细,写的也是棒棒的!!

结束了?

恩,写到这里差不多就结束了,得说明一点,上面的XMPP的测试代码还没有加进去我们写的框架当中去,是写在 XWViewController 里面的,大家可以下载代码自己去完善一下,有时间我自己也会把它加进去的。。

给大家灌一口贴在我们公司墙壁上的鸡汤,“你不努力,谁也给不了你想要的生活!” ,随笔写的有问题的地方大家留言指正,在首页也有我 QQ 可以加 QQ交流 。。。

dotnet core多平台开发体验

前言

随着net core rc2的发布,园子里面关于net core的入门文章也也多了起来,但是大多数都是在一个平台上面来写几个简单的例子,或者是在解释代码本身,并没有体现说在一个平台上面创建一个项目,然后通过源码管理签出到另一个平台上继续开发。还有就是一次编译到处运行是如何体现的?这样类型的文章好像还没有在哪里看到过,于是我就想自己来一遍,并把这个过程分享出来给大家。

首先来个官方的介绍:

.NET Core – .NET 使用 .NET Core 跨平台运行

Announcing .NET Core RC2 and .NET Core SDK Preview 1

一、mac os x 上的开发

本来环境安装是不想写的,因为有太多的文章,但是不写的话又感觉少了些什么。然后我只有一个mac和一个安装win10的pc,所以就只针对这两个平台来,linux等我有钱买新电脑再来折腾。废话不多说,下面开始介绍。

mac os x环境下 dotnet core 的安装

参考链接: https://www.microsoft.com/net/core#macosx

首先要先安装一个 OpenSSL 这个是以后 dotnet core 要下载一些依赖库什么的会用到的。可以使用 brew 来安装OpenSSL

打开命令终端输入brew安装命令

/usr/bin/ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)"

安装好brew 之后继续输入OpenSSL安装命令

brew install openssl

brew link --force openssl

这两个东西安装后,我们就可以来安装我们我们的core了,下载这么一个安装包 点击下载 这是一个pkg安装包。我现在的时候是 dotnet-dev-osx-x64.1.0.0-preview1-002702.pkg 只要双击一下安装包,然后一直点击继续就可以了

安装完之后可以输入命令查看dotnet core 有没有安装成功

dotnet -v

命令行下的项目建立与运行

安装完环境后,我们就可以来开始我第一个项目。我们新建一个文件夹叫做LearnDotnetCore然后打开命令窗口 cd 到这个文件夹下面。然后用命令 dotnet new来建立初始化一个项目。初始化完后我们看看到文件夹下面多了两个文件 Program.cs project.json

Program.cs 是c#源码文件,可以用来实现我们的具体逻辑

project.json 是配置依赖框架,依赖包,版本号,还有其他一些编译会用到相关的东西。这个作用和nodejs的模式是相类似的。

项目创建完之后需要 dotnet restore 来下载我们配置好的依赖包。下载完后呢文件夹里面多出了一个 project.lock.json 文件,打开来看,可以看到我们下载了那些依赖库,还有一些编译的引用等。

下载完依赖库后,我们可以通过运行命令 dotnet run 来编译运行我们的程序。

编译完成后,文件夹内会多出两个文件夹 bin 和 obj 。

bin 是我们编译的完成的文件

obj 里面是一些编译输出和动态库连接等的一些信息。

然后运行我们可以看到输出 Hello World! 到这来,我们的命令行建立、编译和运行程序就到这来结束了。

使用vscode调试运行

参考链接:https://docs.asp.net/en/latest/tutorials/your-first-mac-aspnet.html#running-locally-using-kestrel

单纯使用命令行来开发的话,那么肯定是没有那么方便了,但是在但是目前好像也没有什么特别好的选,所以我们就使用 Visual Studio Code 然后再安装一个插件C# extension

还有一个叫做Xamarin Studio.,貌似还没有支持到 dotnet core的样子

当然还有一个叫做 Project Rider的东西,之前 net core 用 DNX 命令的时候下载来玩过一下,支持什么的还可以,不过很久没有玩了,最近一次编译更新是在 5月17号,也不知道现在支持如何。有兴趣的同学自己下载来玩下,内容不在本文章中介绍了。

打开vscode然后把项目的文件夹拖拉到vscode的窗口上面去,会在左边的工作空间栏目中显示项目的树形结构。然后我们选择project.json,编辑器会提示你是否要加入调试信息,然后我们点击yes,目录结构就会多出一个.vscode文件夹里面有两文件launch.jsontasks.json

然后我们去修改 launch.json 里面的内容,把program里面的内容改成我们编译完成后的dll路径,当然有的有时候会给你自动生成好的,不需要自己修改。

“program”: “${workspaceRoot}/bin/Debug/netcoreapp1.0/LearnDotnetCore.dll”,

还有就是args也修改一下,这个是main函数的传入值。

“args”: [“我试试看传入了什么”],

修改完后在Program.cs里面加个断点,接着直接按f5,或者点开debug面板,然后点击那个调试按钮开始调试

我们可以看到,一个变量的监视数据,还有就是控制台打印出了 Hello World!

代码上传

我选择了用github来保存代码,并且在window上通过git下载到vs里面进行继续开发

首先我们打开git面板来初始化git储存库

初始化完后,我们可以看到我们有15个文件要提交,但是我们并不需要把这么多个文件都给提交了,我们只要提交Program.cs project.json 这两个文件,然后在其他地方下载后来生成其他文件就可以了。

于是,我们就添加了一个.gitignore文件,文件内容添加要排除的东西,然后我们就剩下三个文件提交了

最后,输入提交的信息然后点击那个勾符号,就提交到本地的库里面了。

完成了上面的步骤后,我们还需要把库同步到github上面去。首先,我们去注册一个账户,然后新建一个repository,建立过程自己摸索,就不写的那么详细。建完后如下图

然后到我们的目录下输入下面的两条命令

git remote add origin https://github.com/kotcmm/LearnDotnetCore.git

git push -u origin master

如果有用户名密码,就输入用户名密码,然后存储库就同步到了github上面去了。记住地址什么的要改成自己的。

二、window 上的开发

切到win10上面来继续完成这个博文。

开发环境的安装

参考资料:https://www.microsoft.com/net/core#windows

首先要先下载下面的工具:

Visual Studio Community 2015

DotNetCore.1.0.0.RC2-VS2015Tools.Preview1

这两个软件的安装就不需要截图了,先安装Visual Studio Community 2015再安装DotNetCore.1.0.0.RC2-VS2015Tools.Preview1。安装完之后我们打开vs 2015,然后把我们之前在mac上面创建的项目给下载下来。

打vs后我们选择 open from source control 然后出现左边的面板,如果没有登录你的github账号,那么就先登录你的账号,然后再点击Clone出现一个窗口选择我们前面上传的那个项目。

下载完后双击我们的git项目,然后在弹出的窗口里面选择project.json,就会生成左边的那样项目工程了。

然后我们编译成功并运行起来。

到这里我们已经成功的把在mac上面创建的项目拉到win下不改任何代码,然后成功编译运行了。

发布运行

参考资料:两种部署方式

上面的开发过程中都是直接在id里面运行的,或者用 dotnet run 来直接运行的,那么我们编译好之后如何脱离IDE来运行呢?

  1. 我们也可以到编译成功的目录 netcoreapp1.0 下面输入命令 dotnet LearnDotnetCore.dll 来运行我们的程序。
  2. 可以在project.json加个 runtimes 属性,然后编译出一个exe文件,然后直接运行exe。

要编译成Release的话,可以用命令 dotnet build --configuration Release --no-dependencies --no-incremental 当然build的命令以后可以有单独的文章来讲解

第一种编译的配置

{
"version": "1.0.0-*",
"buildOptions": {
    "emitEntryPoint": true
},
"dependencies": {
    "Microsoft.NETCore.App": {
    "type": "platform",
    "version": "1.0.0-rc2-3002702"
    }
},
"frameworks": {
    "netcoreapp1.0": {
    "imports": "dnxcore50"
    }
}
}

第二种编译的配置

{
"version": "1.0.0-*",
"buildOptions": {
    "emitEntryPoint": true
},
"dependencies": {
    "Microsoft.NETCore.App": {
    //"type": "platform",
    "version": "1.0.0-rc2-3002702"
    }
},
"frameworks": {
    "netcoreapp1.0": {
    "imports": "dnxcore50"
    }
},
"runtimes": {
    "win10-x64": {},
    "osx.10.11-x64": {}
}
}

第二种配置使用dotnet publish会把core的支持运行一起打包处理,然后相关的平台就不用安装net core,但是不同平台需要不同编译。具体的效果和目录结构我就不进行截图了,有兴趣的自己编译看看。

三、改造成web项目

前面介绍了如何编译和运行控制台的程序,但是在这个互联网的时代,我感觉要做成一个web项目可能会更有趣,我们看看如何直接把前面控制台的项目变成web项目。

参考资料:https://docs.asp.net/en/latest/getting-started.html

vs修改运行

首先修改配置 project.json,在属性dependencies添加Kestrel依赖。

第二添加一个类Startup

using System;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;

namespace LearnDotnetCore
{
    public class Startup
    {
        public void Configure(IApplicationBuilder app)
        {
            app.Run(context =>
            {
                return context.Response.WriteAsync("Hello from ASP.NET Core!");
            });
        }
    }
}

然后修改Program.cs

using System;
using Microsoft.AspNetCore.Hosting;

namespace LearnDotnetCore
{
    public class Program
    {
        public static void Main(string[] args)
        {
            var host = new WebHostBuilder()
                .UseKestrel()
                .UseStartup<Startup>()
                .Build();

            host.Run();
        }
    }
}

然后点击运行按钮,程序成功的运行起来

打开浏览器访问 http://localhost:5000/ 我们可以看到浏览器打印出 Hello from ASP.NET Core!证明我们的程序已经编译并运行成功。

发布并部署iis

虽然说直接运行可以访问成功,但是有的人就是喜欢部署到IIS上面去,所以这里也尝试一下部署到IIS上面是什么样的。

参考资料:https://docs.asp.net/en/latest/publishing/iis.html

安装完IIS后需要安装 DotNetCore.1.0.0.RC2-WindowsHosting来支持 net core 在iis上面运行

然后修改project.json的内容,添加下面三个东西,来支持iis运行

然后修改Program.cs,添加iis支持

using System;
using System.IO;
using Microsoft.AspNetCore.Hosting;

namespace LearnDotnetCore
{
    public class Program
    {
        public static void Main(string[] args)
        {
            var host = new WebHostBuilder()
                .UseKestrel()
                
                .UseContentRoot(Directory.GetCurrentDirectory())
                .UseIISIntegration()
                
                .UseStartup<Startup>()
                .Build();

            host.Run();
        }
    }
}

添加文件 web.config

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

<!--
    Configure your application settings in appsettings.json. Learn more at http://go.microsoft.com/fwlink/?LinkId=786380
-->

<system.webServer>
    <handlers>
    <add name="aspNetCore" path="*" verb="*" modules="AspNetCoreModule" resourceType="Unspecified"/>
    </handlers>
    <aspNetCore processPath="%LAUNCHER_PATH%" arguments="%LAUNCHER_ARGS%" stdoutLogEnabled="false" stdoutLogFile=".\logs\stdout" forwardWindowsAuthToken="false"/>
</system.webServer>
</configuration>

上面三个修改完之后,就可以来发布,选择项目邮件,然后选择发布会出现一个窗口

输入名称,然后next或者直接publish,操作完成后会生成一堆文件。然后到iis里面创建 Web 站点,然后绑定发布目录就行,应用程序池的模式需要改为“无代码托管”。

启动网站运行后,浏览器输入http://localhost/ 可以看到浏览器打印出 Hello from ASP.NET Core!和上面没有在iis里面运行的效果是一样的。

四、申请vps并上传项目运行

在文章开头说了,没有Linux的系统,但是后来想了一下,还是也体验一下看看,于是就去 host1plus 买了一个vps,一个是可以体验一下发布一个net core 做的网站,另一个就是以后还可以放一些运行示例什么的。

我买的vps安装的是centos-7系统,可以使用ssh远程登录上去管理。先给安装一个 net core 的运行环境。

参考资料:https://www.microsoft.com/net/core#centos

输入安装命令:

curl -sSL https://raw.githubusercontent.com/dotnet/cli/rel/1.0.0-preview1/scripts/obtain/dotnet-install.sh | bash /dev/stdin –version 1.0.0-preview1-002702 –install-dir ~/dotnet

结果返回了错误。

dotnet_install: Error: Unable to locate libunwind. Install libunwind to continue

dotnet_install: Error: Unable to locate libicu. Install libicu to continue

dotnet_install: Error: Unable to locate gettext. Install gettext to continue

看起来是缺少了几个玩意,那好吧先来下载安装,使用yum命令来安装

yum install libunwind

yum install libicu

yum install gettext

重新输入安装命令:

curl -sSL https://raw.githubusercontent.com/dotnet/cli/rel/1.0.0-preview1/scripts/obtain/dotnet-install.sh | bash /dev/stdin –version 1.0.0-preview1-002702 –install-dir ~/dotnet

这次返回的信息是

dotnet-install: Downloading https://dotnetcli.blob.core.windows.net/dotnet/beta/Binaries/1.0.0-preview1-002702/dotnet-dev-centos-x64.1.0.0-preview1-002702.tar.gz

dotnet-install: Extracting zip

dotnet-install: Adding to current process PATH: /root/dotnet. Note: This change will be visible only when sourcing script.

dotnet-install: Installation finished successfuly.

然后再输入命令

sudo ln -s ~/dotnet/dotnet /usr/local/bin

说明:ln是linux中又一个非常重要命令,它的功能是为某一个文件在另外一个位置建立一个同步的链接.当我们需要在不同的目录,用到相同的文件时,我们不需要在每一个需要的目录下都放一个必须相同的文件,我们只要在某个固定的目录,放上该文件,然后在 其它的目录下用ln命令链接(link)它就可以,不必重复的占用磁盘空间。

然后我们再来输入 dotnet 命令看看是否真的安装成功了。安装成功之后我们就要把之前编译发布好的文件给上传到vps上面去。(记住是要上传 dotnet publish发布出来的那些文件)

可以使用scp命令来上传,命令参考:http://www.cnblogs.com/peida/archive/2013/03/15/2960802.html

文件上传成功后我们执行 dotnet LearnDotnetCore.dll 命令来运行我们的网站程序。返回一下信息

Hosting environment: Production
Content root path: /root/www/publish
Now listening on: http://localhost:5000
Application started. Press Ctrl+C to shut down.

说明我们的程序已经成功运行起来,然后我们打开浏览器访问我们的vps地址,发现并没打印出信息。那是因为程序默认绑定的是http://localhost:5000, 所以我们访问ip的是访问不到,那有两种办法可以实现我们使用ip访问,第一种修改程序添加绑定例如下面:

    public static void Main(string[] args)
    {
        var host = new WebHostBuilder()
            .UseKestrel()
            .UseContentRoot(Directory.GetCurrentDirectory())
            .UseIISIntegration()
            .UseUrls("http://192.168.0.102:5000")
            .UseStartup<Startup>()
            .Build();

        host.Run();
    }
    

第二种可以使用代理,我这里选择Nginx。安装参考资料:https://www.digitalocean.com/community/tutorials/how-to-install-nginx-on-centos-7

由于没有玩过Linux所以经过几番波折啊,首先80端口被Apache http占用启动不了,然后防火墙阻止了80端口。最后终于成功的看到了这个页面

然后配置代理,找到Nginx里面的配置把location /改成如下

location / {

  # 传递真实IP到后端
  proxy_set_header Host $http_host;
  proxy_set_header X-Real-IP $remote_addr;
  proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;

  proxy_pass   localhost:5000;

}

保存后,重启一下Nginx,然后再运行我们前面上传上去的net core网站,再打开浏览访问我们vps的地址,浏览器打印出了 Hello from ASP.NET Core!

到这我们已经体验完成了net core 在 macosx、windows、linux三个平台下的开发和部署运行的过程。本文结束,谢谢观看。


由于本人水平有限,知识有限,文章难免会有错误,欢迎大家指正。如果有什么问题也欢迎大家回复交流。要是你觉得本文还可以,那么点击一下推荐。

Kafka图文详解

http://mp.weixin.qq.com/s?__biz=MzAwNjQwNzU2NQ==&mid=2650342678&idx=1&sn=fa132ac7eeca1f0b687a8a56db40fb98&scene=5&srcid=0528FlILopf2o512M9sfbnkN#rd

结合Jexus + Kestrel 部署 asp.net core 生产环境

http://mp.weixin.qq.com/s?__biz=MzAwNTMxMzg1MA==&mid=2654067494&idx=1&sn=8edf5e6656ce17d111f74c9d38eced25&scene=5&srcid=0524NgY6f9tGHmsqLafP1OZd#rd

ASP.NET Core 是微软的全新的框架。这一框架的目标 ︰

  1. 跨平台
  2. 针对云应用优化
  3. 解除 System.Web 的依赖。 获得下面三个方面的优势,你可以把它认为是一个C# 版本的NodeJS:

1) 模块化实现

2) 一切都尽可能的-异步

3) 依赖关系注入

微软已经如期发布了.NET Core R2, 具体参见文章《微软.NET Core RC2正式发布,横跨所有平台》,现在可以放心的基于.NET Core 构建 ASP.NET Core 。那么问题就来了,生产环境我们如何部署呢?

ASP.NET Core 的运行环境由新开发的 Kestrel Server 负责,IIS 退回到 HTTP 的侦听器的角色,微软也特别为了这个需求开发了 IIS Platform Handler,以处理 HTTP 与运行环境之间的信息转发工作,微软官方推荐在Linux服务器上使用Nginx,Haproxy等代理Kestrel Server。Jexus 5.8.1 版本的端口“转发”功能也类似于IIS 的实现方式,通过端口转发方式将Kestrel 保护起来,又可以高性能运行。Jexus的实现方式上刘冰的博客《为支持ASP.NET5跨平台,Jexus再添新举措》进行了解释。

下面我们来介绍下怎么设置通过Jexus 把asp.net core 的请求转发给Kestrel Server来处理。我假设你已经把asp.net core rc2 的网站准备好了,我这里演示的就是我的一个实验站点“Hello World”, 运行在Azure上的Centos 7.0上,服务端口是127.0.0.1:5000 。

 

在jexus上为之新建一个网站,在这个网站的配置文件中加一行:
AppHost.Port=5000
通过以上处理,访问jexus的这个网站,就会把请求转发到对应的应用上。下面是我的示例dotnetcore,配置文件放在/usr/jexus/siteconf,创建一个网站叫做dotnetcore:
port=5001
hosts=*

AppHost.Port=5000

 

通过 5001 端口转发到 5000端口,配置非常简单,这时的jexus也是一个侦听和数据转发的角色。当然,作为服务器,jexus也是一个“网站组织者”的角色。因为每个应用程序容器都有不同的端口,而服务器对外服务器不可能一个端口一个服务。通过jexus的“组织”,就能让不同的端口通过域名共同一个80端口。

保存后,重启Jexus服务器就生效了,就可以通过浏览器访问到asp.net core 网站了。

大家可以通过 http://mono.cloudapp.net:5001/  访问到我这个最简单的Hello World


如果我把后端的ASP.NET Core应用关掉,会看到下面的页面

从0到1:微信后台系统的演进之路

http://mp.weixin.qq.com/s?__biz=MjM5MDE0Mjc4MA==&mid=402340325&idx=1&sn=5b7bf6025b1a83a0e529e630fc95ae28&scene=5&srcid=0522CgKoFOWZdpPTKDag8k8Z#rd

从无到有

2011.1.21 微信正式发布。这一天距离微信项目启动日约为2个月。就在这2个月里,微信从无到有,大家可能会好奇这期间微信后台做的最重要的事情是什么?

我想应该是以下三件事:

1
确定了微信的消息模型

微信起初定位是一个通讯工具,作为通讯工具最核心的功能是收发消息。微信团队源于广硏团队,消息模型跟邮箱的邮件模型也很有渊源,都是存储转发。

图 1 微信消息模型

图1展示了这一消息模型,消息被发出后,会先在后台临时存储;为使接收者能更快接收到消息,会推送消息通知给接收者;最后客户端主动到服务器收取消息。

2
制定了数据同步协议

由于用户的帐户、联系人和消息等数据都在服务器存储,如何将数据同步到客户端就成了很关键的问题。为简化协议,我们决定通过一个统一的数据同步协议来同步用户所有的基础数据。

最初的方案是客户端记录一个本地数据的快照(Snapshot),需要同步数据时,将Snapshot带到服务器,服务器通过计算Snapshot与服务器数据的差异,将差异数据发给客户端,客户端再保存差异数据完成同步。不过这个方案有两个问题:一是Snapshot会随着客户端数据的增多变得越来越大,同步时流量开销大;二是客户端每次同步都要计算Snapshot,会带来额外的性能开销和实现复杂度。

几经讨论后,方案改为由服务计算Snapshot,在客户端同步数据时跟随数据一起下发给客户端,客户端无需理解Snapshot,只需存储起来,在下次数据同步数据时带上即可。同时,Snapshot被设计得非常精简,是若干个Key-Value的组合,Key代表数据的类型,Value代表给到客户端的数据的最新版本号。Key有三个,分别代表:帐户数据、联系人和消息。这个同步协议的一个额外好处是客户端同步完数据后,不需要额外的ACK协议来确认数据收取成功,同样可以保证不会丢数据:只要客户端拿最新的Snapshot到服务器做数据同步,服务器即可确认上次数据已经成功同步完成,可以执行后续操作,例如清除暂存在服务的消息等等。

此后,精简方案、减少流量开销、尽量由服务器完成较复杂的业务逻辑、降低客户端实现的复杂度就作为重要的指导原则,持续影响着后续的微信设计开发。记得有个比较经典的案例是:我们在微信1.2版实现了群聊功能,但为了保证新旧版客户端间的群聊体验,我们通过服务器适配,让1.0版客户端也能参与群聊。

3
定型了后台架构

图 2 微信后台系统架构

微信后台使用三层架构:接入层、逻辑层和存储层。

  1. 接入层提供接入服务,包括长连接入服务和短连接入服务。长连接入服务同时支持客户端主动发起请求和服务器主动发起推送;短连接入服务则只支持客户端主动发起请求。
  2. 逻辑层包括业务逻辑服务和基础逻辑服务。业务逻辑服务封装了业务逻辑,是后台提供给微信客户端调用的API。基础逻辑服务则抽象了更底层和通用的业务逻辑,提供给业务逻辑服务访问。
  3. 存储层包括数据访问服务和数据存储服务。数据存储服务通过MySQL和SDB(广硏早期后台中广泛使用的Key-Table数据存储系统)等底层存储系统来持久化用户数据。数据访问服务适配并路由数据访问请求到不同的底层数据存储服务,面向逻辑层提供结构化的数据服务。比较特别的是,微信后台每一种不同类型的数据都使用单独的数据访问服务和数据存储服务,例如帐户、消息和联系人等等都是独立的。

微信后台主要使用C++。后台服务使用Svrkit框架搭建,服务之间通过同步RPC进行通讯。

图 3 Svrkit 框架

Svrkit是另一个广硏后台就已经存在的高性能RPC框架,当时尚未广泛使用,但在微信后台却大放异彩。作为微信后台基础设施中最重要的一部分,Svrkit这几年一直不断在进化。我们使用Svrkit构建了数以千计的服务模块,提供数万个服务接口,每天RPC调用次数达几十万亿次。

这三件事影响深远,乃至于5年后的今天,我们仍继续沿用最初的架构和协议,甚至还可以支持当初1.0版的微信客户端。

这里有一个经验教训——运营支撑系统真的很重要。第一个版本的微信后台是仓促完成的,当时只是完成了基础业务功能,并没有配套的业务数据统计等等。我们在开放注册后,一时间竟没有业务监控页面和数据曲线可以看,注册用户数是临时从数据库统计的,在线数是从日志里提取出来的,这些数据通过每个小时运行一次的脚本(这个脚本也是当天临时加的)统计出来,然后自动发邮件到邮件组。还有其他各种业务数据也通过邮件进行发布,可以说邮件是微信初期最重要的数据门户。

2011.1.21 当天最高并发在线数是 491,而今天这个数字是4亿。

小步慢跑

在微信发布后的4个多月里,我们经历了发布后火爆注册的惊喜,也经历了随后一直不温不火的困惑。

这一时期,微信做了很多旨在增加用户好友量,让用户聊得起来的功能。打通腾讯微博私信、群聊、工作邮箱、QQ/邮箱好友推荐等等。对于后台而言,比较重要的变化就是这些功能催生了对异步队列的需求。例如,微博私信需要跟外部门对接,不同系统间的处理耗时和速度不一样,可以通过队列进行缓冲;群聊是耗时操作,消息发到群后,可以通过异步队列来异步完成消息的扩散写等等。

图 4 单聊和群聊消息发送过程

图4是异步队列在群聊中的应用。微信的群聊是写扩散的,也就是说发到群里的一条消息会给群里的每个人都存一份(消息索引)。为什么不是读扩散呢?有两个原因:

  1. 群的人数不多,群人数上限是10(后来逐步加到20、40、100,目前是500),扩散的成本不是太大,不像微博,有成千上万的粉丝,发一条微博后,每粉丝都存一份的话,一个是效率太低,另一个存储量也会大很多;
  2. 消息扩散写到每个人的消息存储(消息收件箱)后,接收者到后台同步数据时,只需要检查自己收件箱即可,同步逻辑跟单聊消息是一致的,这样可以统一数据同步流程,实现起来也会很轻量。

异步队列作为后台数据交互的一种重要模式,成为了同步RPC服务调用之外的有力补充,在微信后台被大量使用。

快速成长

微信的飞速发展是从2.0版开始的,这个版本发布了语音聊天功能。之后微信用户量急速增长,2011.5用户量破100万、2011.7 用户量破1000万、2012.3 注册用户数突破1亿。

伴随着喜人成绩而来的,还有一堆幸福的烦恼:

  1. 业务快速迭代的压力
  2. 微信发布时功能很简单,主要功能就是发消息。不过在发语音之后的几个版本里迅速推出了手机通讯录、QQ离线消息、查看附近的人、摇一摇、漂流瓶和朋友圈等等功能。
  3. 有个广为流传的关于朋友圈开发的传奇——朋友圈历经4个月,前后做了30多个版本迭代才最终成型。其实还有一个鲜为人知的故事——那时候因为人员比较短缺,朋友圈后台长时间只有1位开发人员。

后台稳定性的要求:

用户多了,功能也多了,后台模块数和机器量在不断翻番,紧跟着的还有各种故障。

帮助我们顺利度过这个阶段的,是以下几个举措:

1
极简设计

虽然各种需求扑面而来,但我们每个实现方案都是一丝不苟完成的。实现需求最大的困难不是设计出一个方案并实现出来,而是需要在若干个可能的方案中,甄选出最简单实用的那个。

这中间往往需要经过几轮思考——讨论——推翻的迭代过程,谋定而后动有不少好处,一方面可以避免做出华而不实的过度设计,提升效率;另一方面,通过详尽的讨论出来的看似简单的方案,细节考究,往往是可靠性最好的方案。

2
大系统小做

逻辑层的业务逻辑服务最早只有一个服务模块(我们称之为mmweb),囊括了所有提供给客户端访问的API,甚至还有一个完整的微信官网。这个模块架构类似Apache,由一个CGI容器(CGIHost)和若干CGI组成(每个CGI即为一个API),不同之处在于每个CGI都是一个动态库so,由CGIHost动态加载。

在mmweb的CGI数量相对较少的时候,这个模块的架构完全能满足要求,但当功能迭代加快,CGI量不断增多之后,开始出现问题:

  1. 每个CGI都是动态库,在某些CGI的共用逻辑的接口定义发生变化时,不同时期更新上线的CGI可能使用了不同版本的逻辑接口定义,会导致在运行时出现诡异结果或者进程crash,而且非常难以定位;
  2. 所有CGI放在一起,每次大版本发布上线,从测试到灰度再到全面部署完毕,都是一个很漫长的过程,几乎所有后台开发人员都会被同时卡在这个环节,非常影响效率;
  3. 新增的不太重要的CGI有时稳定性不好,某些异常分支下会crash,导致CGIHost进程无法服务,发消息这些重要CGI受影响没法运行。

于是我们开始尝试使用一种新的CGI架构——Logicsvr。

Logicsvr基于Svrkit框架。将Svrkit框架和CGI逻辑通过静态编译生成可直接使用HTTP访问的Logicsvr。我们将mmweb模块拆分为8个不同服务模块。拆分原则是:实现不同业务功能的CGI被拆到不同Logicsvr,同一功能但是重要程度不一样的也进行拆分。例如,作为核心功能的消息收发逻辑,就被拆为3个服务模块:消息同步、发文本和语音消息、发图片和视频消息。

每个Logicsvr都是一个独立的二进制程序,可以分开部署、独立上线。时至今日,微信后台有数十个Logicsvr,提供了数百个CGI服务,部署在数千台服务器上,每日客户端访问量几千亿次。

除了API服务外,其他后台服务模块也遵循“大系统小做”这一实践准则,微信后台服务模块数从微信发布时的约10个模块,迅速上涨到数百个模块。

3
业务监控

这一时期,后台故障很多。比故障更麻烦的是,因为监控的缺失,经常有些故障我们没法第一时间发现,造成故障影响面被放大。

监控的缺失一方面是因为在快速迭代过程中,重视功能开发,轻视了业务监控的重要性,有故障一直是兵来将挡水来土掩;另一方面是基础设施对业务逻辑监控的支持度较弱。基础设施提供了机器资源监控和Svrkit服务运行状态的监控。这个是每台机器、每个服务标配的,无需额外开发,但是业务逻辑的监控就要麻烦得多了。当时的业务逻辑监控是通过业务逻辑统计功能来做的,实现一个监控需要4步:

  1. 申请日志上报资源;
  2. 在业务逻辑中加入日志上报点,日志会被每台机器上的agent收集并上传到统计中心;
  3. 开发统计代码;
  4. 实现统计监控页面。

可以想象,这种费时费力的模式会反过来降低开发人员对加入业务监控的积极性。于是有一天,我们去公司内的标杆——即通后台(QQ后台)取经了,发现解决方案出乎意料地简单且强大:

1) 故障报告

之前每次故障后,是由QA牵头出一份故障报告,着重点是对故障影响的评估和故障定级。新的做法是每个故障不分大小,开发人员需要彻底复盘故障过程,然后商定解决方案,补充出一份详细的技术报告。这份报告侧重于:如何避免同类型故障再次发生、提高故障主动发现能力、缩短故障响应和处理过程。

2) 基于 ID-Value 的业务无关的监控告警体系

图 5 基于 ID-Value 的监控告警体系

监控体系实现思路非常简单,提供了2个API,允许业务代码在共享内存中对某个监控ID进行设置Value或累加Value的功能。每台机器上的Agent会定时将所有ID-Value上报到监控中心,监控中心对数据汇总入库后就可以通过统一的监控页面输出监控曲线,并通过预先配置的监控规则产生报警。

对于业务代码来说,只需在要被监控的业务流程中调用一下监控API,并配置好告警条件即可。这就极大地降低了开发监控报警的成本,我们补全了各种监控项,让我们能主动及时地发现问题。新开发的功能也会预先加入相关监控项,以便在少量灰度阶段就能直接通过监控曲线了解业务是否符合预期。

4
KVSvr

微信后台每个存储服务都有自己独立的存储模块,是相互独立的。每个存储服务都有一个业务访问模块和一个底层存储模块组成。业务访问层隔离业务逻辑层和底层存储,提供基于RPC的数据访问接口;底层存储有两类:SDB和MySQL。

SDB适用于以用户UIN(uint32_t)为Key的数据存储,比方说消息索引和联系人。优点是性能高,在可靠性上,提供基于异步流水同步的Master-Slave模式,Master故障时,Slave可以提供读数据服务,无法写入新数据。

由于微信账号为字母+数字组合,无法直接作为SDB的Key,所以微信帐号数据并非使用SDB,而是用MySQL存储的。MySQL也使用基于异步流水复制的Master-Slave模式。

  • 第1版的帐号存储服务使用Master-Slave各1台。Master提供读写功能,Slave不提供服务,仅用于备份。当Master有故障时,人工切读服务到Slave,无法提供写服务。为提升访问效率,我们还在业务访问模块中加入了memcached提供Cache服务,减少对底层存储访问。
  • 第2版的帐号存储服务还是Master-Slave各1台,区别是Slave可以提供读服务,但有可能读到脏数据,因此对一致性要求高的业务逻辑,例如注册和登录逻辑只允许访问Master。当Master有故障时,同样只能提供读服务,无法提供写服务。
  • 第3版的帐号存储服务采用1个Master和多个Slave,解决了读服务的水平扩展能力。
  • 第4版的帐号服务底层存储采用多个Master-Slave组,每组由1个Master和多个Slave组成,解决了写服务能力不足时的水平扩展能力。
  • 最后还有个未解决的问题:单个Master-Slave分组中,Master还是单点,无法提供实时的写容灾,也就意味着无法消除单点故障。另外Master-Slave的流水同步延时对读服务有很大影响,流水出现较大延时会导致业务故障。于是我们寻求一个可以提供高性能、具备读写水平扩展、没有单点故障、可同时具备读写容灾能力、能提供强一致性保证的底层存储解决方案,最终KVSvr应运而生。

KVSvr使用基于Quorum的分布式数据强一致性算法,提供Key-Value/Key-Table模型的存储服务。传统Quorum算法的性能不高,KVSvr创造性地将数据的版本和数据本身做了区分,将Quorum算法应用到数据的版本的协商,再通过基于流水同步的异步数据复制提供了数据强一致性保证和极高的数据写入性能,另外KVSvr天然具备数据的Cache能力,可以提供高效的读取性能。

KVSvr一举解决了我们当时迫切需要的无单点故障的容灾能力。除了第5版的帐号服务外,很快所有SDB底层存储模块和大部分MySQL底层存储模块都切换到KVSvr。随着业务的发展,KVSvr也不断在进化着,还配合业务需要衍生出了各种定制版本。现在的KVSvr仍然作为核心存储,发挥着举足轻重的作用。

平台化

2011.8 深圳举行大运会。微信推出“微信深圳大运志愿者服务中心”服务号,微信用户可以搜索“szdy”将这个服务号加为好友,获取大会相关的资讯。当时后台对“szdy”做了特殊处理,用户搜索时,会随机返回“szdy01”,“szdy02”…,“szdy10”这10个微信号中的1个,每个微信号背后都有一个志愿者在服务。

2011.9 “微成都”落户微信平台,微信用户可以搜索“wechengdu”加好友,成都市民还可以在“附近的人”看到这个号,我们在后台给这个帐号做了一些特殊逻辑,可以支持后台自动回复用户发的消息。

这种需求越来越多,我们就开始做一个媒体平台,这个平台后来从微信后台分出,演变成了微信公众平台,独立发展壮大,开始了微信的平台化之路。除微信公众平台外,微信后台的外围还陆续出现了微信支付平台、硬件平台等等一系列平台。

图 6 微信平台

走出国门

微信走出国门的尝试开始于3.0版本。从这个版本开始,微信逐步支持繁体、英文等多种语言文字。不过,真正标志性的事情是第一个海外数据中心的投入使用。

1
海外数据中心

海外数据中心的定位是一个自治的系统,也就是说具备完整的功能,能够不依赖于国内数据中心独立运作。

1) 多数据中心架构

图 7 多数据中心架构

系统自治对于无状态的接入层和逻辑层来说很简单,所有服务模块在海外数据中心部署一套就行了。

但是存储层就有很大麻烦了——我们需要确保国内数据中心和海外数据中心能独立运作,但不是两套隔离的系统各自部署,各玩各的,而是一套业务功能可以完全互通的系统。因此我们的任务是需要保证两个数据中心的数据一致性,另外Master-Master架构是个必选项,也即两个数据中心都需要可写。

2) Master-Master 存储架构

Master-Master架构下数据的一致性是个很大的问题。两个数据中心之间是个高延时的网络,意味着在数据中心之间直接使用Paxos算法、或直接部署基于Quorum的KVSvr等看似一劳永逸的方案不适用。

最终我们选择了跟Yahoo!的PNUTS系统类似的解决方案,需要对用户集合进行切分,国内用户以国内上海数据中心为Master,所有数据写操作必须回到国内数据中心完成;海外用户以海外数据中心为Master,写操作只能在海外数据中心进行。从整体存储上看,这是一个Master-Master的架构,但细到一个具体用户的数据,则是Master-Slave模式,每条数据只能在用户归属的数据中心可写,再异步复制到其他数据中心。

图 8 多数据中心的数据Master-Master架构

3) 数据中心间的数据一致性

这个Master-Master架构可以在不同数据中心间实现数据最终一致性。如何保证业务逻辑在这种数据弱一致性保证下不会出现问题?

这个问题可以被分解为2个子问题:

  • 用户访问自己的数据:

用户可以满世界跑,那是否允许用户就近接入数据中心就对业务处理流程有很大影响。如果允许就近接入,同时还要保证数据一致性不影响业务,就意味着要么用户数据的Master需要可以动态的改变;要么需要对所有业务逻辑进行仔细梳理,严格区分本数据中心和跨数据中心用户的请求,将请求路由到正确的数据中心处理。

考虑到上述问题会带来很高昂的实现和维护的复杂度,我们限制了每个用户只能接入其归属数据中心进行操作。如果用户发生漫游,其漫游到的数据中心会自动引导用户重新连回归属数据中心。

这样用户访问自己数据的一致性问题就迎刃而解了,因为所有操作被限制在归属数据中心内,其数据是有强一致性保证的。此外,还有额外的好处:用户自己的数据(如:消息和联系人等)不需要在数据中心间同步,这就大大降低了对数据同步的带宽需求。

  • 用户访问其他用户的数据:

由于不同数据中心之间业务需要互通,用户会使用到其他数据中心用户创建的数据。例如,参与其他数据中心用户创建的群聊,查看其他数据中心用户的朋友圈等。

仔细分析后可以发现,大部分场景下对数据一致性要求其实并不高。用户稍迟些才见到自己被加入某个其他数据中心用户建的群、稍迟些才见到某个好友的朋友圈动态更新其实并不会带来什么问题。在这些场景下,业务逻辑直接访问本数据中心的数据。

当然,还是有些场景对数据一致性要求很高。比方说给自己设置微信号,而微信号是需要在整个微信帐号体系里保证唯一的。我们提供了全局唯一的微信号申请服务来解决这一问题,所有数据中心通过这个服务申请微信号。这种需要特殊处置的场景极少,不会带来太大问题。

4) 可靠的数据同步

数据中心之间有大量的数据同步,数据是否能够达到最终一致,取决于数据同步是否可靠。为保证数据同步的可靠性,提升同步的可用性,我们又开发一个基于Quorum算法的队列组件,这个组件的每一组由3机存储服务组成。与一般队列的不同之处在于,这个组件对队列写入操作进行了大幅简化,3机存储服务不需要相互通讯,每个机器上的数据都是顺序写,执行写操作时在3机能写入成功2份即为写入成功;若失败,则换另外一组再试。因此这个队列可以达到极高的可用性和写入性能。每个数据中心将需要同步的数据写入本数据中心的同步队列后,由其他数据中心的数据重放服务将数据拉走并进行重放,达到数据同步的目的。

2
网络加速

海外数据中心建设周期长,投入大,微信只在香港和加拿大有两个海外数据中心。但世界那么大,即便是这两个数据中心,也还是没法辐射全球,让各个角落的用户都能享受到畅快的服务体验。

通过在海外实际对比测试发现,微信客户端在发消息等一些主要使用场景与主要竞品有不小的差距。为此,我们跟公司的架构平台部、网络平台部和国际业务部等兄弟部门一起合作,围绕海外数据中心,在世界各地精心选址建设了数十个POP点(包括信令加速点和图片CDN网络)。另外,通过对移动网络的深入分析和研究,我们还对微信的通讯协议做了大幅优化。微信最终在对比测试中赶上并超过了主要的竞品。

精耕细作

1
三园区容灾

2013.7.22 微信发生了有史以来最大规模的故障,消息收发和朋友圈等服务出现长达5个小时的故障,故障期间消息量跌了一半。故障的起因是上海数据中心一个园区的主光纤被挖断,近2千台服务器不可用,引发整个上海数据中心(当时国内只有这一个数据中心)的服务瘫痪。

故障时,我们曾尝试把接入到故障园区的用户切走,但收效甚微。虽然数百个在线模块都做了容灾和冗余设计,单个服务模块看起来没有单点故障问题;但整体上看,无数个服务实例散布在数据中心各个机房的8千多台服务器内,各服务RPC调用复杂,呈网状结构,再加上缺乏系统级的规划和容灾验证,最终导致故障无法主动恢复。在此之前,我们知道单个服务出现单机故障不影响系统,但没人知道2千台服务器同时不可用时,整个系统会出现什么不可控的状况。

其实在这个故障发生之前3个月,我们已经在着手解决这个问题。当时上海数据中心内网交换机异常,导致微信出现一个出乎意料的故障,在13分钟的时间里,微信消息收发几乎完全不可用。在对故障进行分析时,我们发现一个消息系统里一个核心模块三个互备的服务实例都部署在同一机房。该机房的交换机故障导致这个服务整体不可用,进而消息跌零。这个服务模块是最早期(那个时候微信后台规模小,大部分后台服务都部署在一个数据园区里)的核心模块,服务基于3机冗余设计,年复一年可靠地运行着,以至于大家都完全忽视了这个问题。

为解决类似问题,三园区容灾应运而生,目标是将上海数据中心的服务均匀部署到3个物理上隔离的数据园区,在任意单一园区整体故障时,微信仍能提供无损服务。

1) 同时服务

传统的数据中心级灾备方案是“两地三中心”,即同城有两个互备的数据中心,异地再建设一个灾备中心,这三个数据中心平时很可能只有一个在提供在线服务,故障时再将业务流量切换到其他数据中心。这里的主要问题是灾备数据中心无实际业务流量,在主数据中心故障时未必能正常切换到灾备中心,并且在平时大量的备份资源不提供服务,也会造成大量的资源浪费。

三园区容灾的核心是三个数据园区同时提供服务,因此即便某个园区整体故障,那另外两个园区的业务流量也只会各增加50%。反过来说,只需让每个园区的服务器资源跑在容量上限的2/3,保留1/3的容量即可提供无损的容灾能力,而传统“两地三中心”则有多得多的服务器资源被闲置。此外,在平时三个园区同时对外服务,因此我们在故障时,需要解决的问题是“怎样把业务流量切到其他数据园区?”,而不是“能不能把业务流量切到其他数据园区?”,前者显然是更容易解决的一个问题。

2) 数据强一致

三园区容灾的关键是存储模块需要把数据均匀分布在3个数据园区,同一份数据要在不同园区有2个以上的一致的副本,这样才能保证任意单一园区出灾后,可以不中断地提供无损服务。由于后台大部分存储模块都使用KVSvr,这样解决方案也相对简单高效——将KVSvr的每1组机器都均匀部署在3个园区里。

3) 故障时自动切换

三园区容灾的另一个难点是对故障服务的自动屏蔽和自动切换。即要让业务逻辑服务模块能准确识别出某些下游服务实例已经无法访问,然后迅速自动切到其他服务实例,避免被拖死。我们希望每个业务逻辑服务可以在不借助外部辅助信息(如建设中心节点,由中心节点下发各个业务逻辑服务的健康状态)的情况下,能自行决策迅速屏蔽掉有问题的服务实例,自动把业务流量分散切到其他服务实例上。另外,我们还建设了一套手工操作的全局屏蔽系统,可以在大型网络故障时,由人工介入屏蔽掉某个园区所有的机器,迅速将业务流量分散到其他两个数据园区。

4) 容灾效果检验

三园区容灾是否能正常发挥作用还需要进行实际的检验,我们在上海数据中心和海外的香港数据中心完成三园区建设后,进行了数次实战演习,屏蔽单一园区上千台服务,检验容灾效果是否符合预期。特别地,为了避免随着时间的推移某个核心服务模块因为某次更新就不再支持三园区容灾了,我们还搭建了一套容灾拨测系统,每天对所有服务模块选取某个园区的服务主动屏蔽掉,自动检查服务整体失败量是否发生变化,实现对三园区容灾效果的持续检验。

2
性能优化

之前我们在业务迅速发展之时,优先支撑业务功能快速迭代,性能问题无暇兼顾,比较粗放的贯彻了“先扛住再优化”的海量之道。2014年开始大幅缩减运营成本,性能优化就被提上了日程。

我们基本上对大部分服务模块的设计和实现都进行了重新review,并进行了有针对性的优化,这还是可以节约出不少机器资源的。但更有效的优化措施是对基础设施的优化,具体的说是对Svrkit框架的优化。Svrkit框架被广泛应用到几乎所有服务模块,如果框架层面能把机器资源使用到极致,那肯定是事半功倍的。

结果还真的可以,我们在基础设施里加入了对协程的支持,重点是这个协程组件可以不破坏原来的业务逻辑代码结构,让我们原有代码中使用同步RPC调用的代码不做任何修改,就可以直接通过协程异步化。Svrkit框架直接集成了这个协程组件,然后美好的事情发生了,原来单实例最多提供上百并发请求处理能力的服务,在重编上线后,转眼间就能提供上千并发请求处理能力。Svrkit框架的底层实现在这一时期也做了全新的实现,服务的处理能力大幅提高。

3
防雪崩

我们一直以来都不太担心某个服务实例出现故障,导致这个实例完全无法提供服务的问题,这个在后台服务的容灾体系里可以被处理得很好。最担心的是雪崩:某个服务因为某些原因出现过载,导致请求处理时间被大大拉长。于是服务吞吐量下降,大量请求积压在服务的请求队列太长时间了,导致访问这个服务的上游服务出现超时。更倒霉的是上游服务还经常会重试,然后这个过载的服务仅有的一点处理能力都在做无用功(即处理完毕返回结果时,调用端都已超时放弃),终于这个过载的服务彻底雪崩了。最糟糕的情况是上游服务每个请求都耗时那么久,雪崩顺着RPC调用链一级级往上传播,最终单个服务模块的过载会引发大批服务模块的雪崩。

我们在一番勒紧裤腰带节省机器资源、消灭低负载机器后,所有机器的负载都上来了,服务过载变得经常发生了。解决这一问题的有力武器是Svrkit框架里的具有QoS保障的FastReject机制,可以快速拒绝掉超过服务自身处理能力的请求,即使在过载时,也能稳定地提供有效输出。

4
安全加固

近年,互联网安全事件时有发生,各种拖库层出不穷。为保护用户的隐私数据,我们建设了一套数据保护系统——全程票据系统。其核心方案是,用户登录后,后台会下发一个票据给客户端,客户端每次请求带上票据,请求在后台服务的整个处理链条中,所有对核心数据服务的访问,都会被校验票据是否合法,非法请求会被拒绝,从而保障用户隐私数据只能用户通过自己的客户端发起操作来访问。

新的挑战

1
资源调度系统

微信后台有成千的服务模块,部署在全球数以万计的服务器上,一直依靠人工管理。此外,微信后台主要是提供实时在线服务,每天的服务器资源占用在业务高峰和低谷时相差很大,在业务低谷时计算资源被白白浪费;另一方面,很多离线的大数据计算却受制于计算资源不足,难以高效完成。

我们正在实验和部署的资源调度系统(Yard)可以把机器资源的分配和服务的部署自动化、把离线任务的调度自动化,实现了资源的优化配置,在业务对服务资源的需求有变化时,能更及时、更弹性地自动实现服务的重新配置与部署。

2
高可用存储

基于Quorum算法的KVSvr已经实现了强一致性、高可用且高性能的Key-Value/Key-Table存储。最近,微信后台又诞生了基于Paxos算法的另一套存储系统,首先落地的是PhxSQL,一个支持完整MySQL功能,又同时具备强一致性、高可用和高性能的SQL存储。

Windows批处理bat基础知识总结

编者按:这两天在写bat脚本,这是一个痛苦的周末,来个bat的基础知识,留着以后慢慢学。

转自:http://uule.iteye.com/blog/2076859

作者:uule

预定义的变量

下面是些已经被底层定义好可以直接使用的变量:不会出现在 SET 显示的变量列表中
%CD% – 扩展到当前目录字符串。
%DATE% – 用跟 DATE 命令同样的格式扩展到当前日期。
%TIME% – 用跟 TIME 命令同样的格式扩展到当前时间。
%RANDOM% – 扩展到 0 和 32767 之间的任意十进制数字。
%ERRORLEVEL% – 扩展到当前 ERRORLEVEL 数值。
%CMDEXTVERSION% – 扩展到当前命令处理器扩展名版本号。
%CMDCMDLINE% – 扩展到调用命令处理器的原始命令行。
%0 bat的完整路径名如”C:\Windows\system32\xxx.bat”
%1 bat参数1依次类推%2参数2…
%path% – 当前的环境变量。以分号隔开的路径列表,路径可包含空格,可以以’\’结尾, 可以以双引号包围之。

参数%0具有特殊的功能,可以调用批处理自身,以达到批处理本身循环的目的,也可以复制文件自身等等。

例:最简单的复制文件自身的方法

copy %0 d:\wind.bat

扩展变量

@ 与%i相关的变量(bat参数或者for循环的%i)
假设文件为C:\Documents and Settings\jinsun\桌面\ParseSinglePkgs.bat
%0        C:\Documents and Settings\jinsun\桌面\ParseSinglePkgs.bat
%~dp0  C:\Documents and Settings\jinsun\桌面\
%cd%   C:\Documents and Settings\jinsun\桌面
%~nx0   ParseSinglePkgs.bat
%~n0     ParseSinglePkgs
%~x0     .bat

@ 与%VAR%相关的变量
%VAR:str1=str2%   会将VAR中的str1替换为str2(str2如果为空则可以达到删除的效果,str1前可以加*,变量%ABC:*B=%是C)
%VAR:~0,-2%          会提取VAR 变量的所有字符,除了最后两个
%VAR:~2%              会提取VAR 变量的除前两个的所有字符
%VAR:~-2%             会提取VAR 变量的最后两个
%VAR:~2,5%           提取从第2个字符开始的5个字符

 

========================================================

在bat文件中,多个命令写在一行或括号内,如果使用行内或括号内定义或修改的变量,需要启用延迟变量扩充,用叹号引用变量

bat文件中用 setlocal enabledelayedexpansion 启用延迟变量扩充,命令提示符窗口启用延迟变量扩充,用 cmd /v:on

 

ren命令中的文件名最好加引号,防止文件名包含空格等特殊字符造成的错误。

cd /d %~dp0

批处理文件中的一条语句
意思是 更改当前目录为批处理本身的目录

比如你有个批处理a.bat在D:\qq文件夹下
a.bat内容为
cd /d %~dp0
在这里
cd /d 表示直接转换到后面的路径,否则如果切换盘符,就需要再输入盘符才能切换路径
cd /d %~dp0的意思就是cd /d d:\qq
%0代表批处理本身 d:\qq\a.bat
~dp是变量扩充
d既是扩充到分区号 d:
p就是扩充到路径 \qq
dp就是扩充到分区号路径 d:\qq

 

%0代指批处理文件自身
%~d0 是指批处理所在的盘符
%~dp0 是盘符加路径
cd %~dp0 就是进入批处理所在目录了

 

%cd%

%cd%代表当前执行批处理文件的路径(当前路径)

echo %cd%

 

%CD%  当前文件目录,不显示文件名,切通过其找到其他文件

%0    当前文件本身,包含完整路径名和文件名

 

如 “C:\Windows\system32\xxx.bat”,是一个路径字符串,有引号

 

*.*  代表所有文件

 

>和>>它们的作用都是改变各种提示信息的输出方向,把提示信息输出到指定的地方去——各种提示信息默认是输出到屏幕上去的。

单个的>表示以覆盖方式重定向提示信息,也就是说如果目的地原来有内容,将把原来的内容清除掉,用新内容填充;

>>表示以追加的方式重定向提示信息,也就是说,如果目的地原来有内容,原有的内容将保持不变,在原有内容之后追加新内容。

例如:假设d:\test.txt中的内容是:I Love batch。那么,在cmd窗口中,分别使用如下两条语句:echo me,too>d:\test.txt和echo me,too>>d:\test.txt,将会发现,使用第一条语句之后,test.txt中只有一行内容:me,too,而使用第二条语句之后,test.txt中将有两行内容,分别是I Love batch和me,too。

例如:tree /?>help.txt,这条命令语句就把 tree 命令的帮助信息重定向到了help.txt文件中,

 

help>nul 就把 help 命令的提示信息重定向到了空设备中去(nul表示空设备)。

 

|   命令管道符

格式:第一条命令 | 第二条命令 [| 第三条命令…]

将第一条命令的结果作为第二条命令的参数来使用,记得在unix中这种方式很常见。

例如:

dir c:\|find “txt”

以上命令是:查找C:\所有,并发现TXT字符串。

 

md

如果要创建的文件夹带有空格或&,需要用引号把文件夹名括起来,例如:md “test abc”、md “abc&xyz”。

 

如果不使用引号,又会带来什么后果呢?

 

1、如果文件夹名带空格,那么,md test abc 语句会在当前目录下创建test和abc这两个文件夹;利用这个特点,如果要创建abc def xyz这三个文件夹,直接使用 md abc def xyz 就行了,而无需连写三条md语句。

当然,md abc;def;xyz或者md abc,def,xyz 这样的写法也是可以的。

2、如果文件夹名中含有&,那么,md abc&xyz 会创建abc这个文件夹,并提示说:’xyz’不是内部或外部命令,也不是可运行的程序或批处理文件,这是因为,&是复合语句的连接符号,它把前后两部分视为两条子语句了。

 

忠告:如果文件夹名含有特殊符号,请不要忘记使用双引号!

 

md创建中级目录。也就是说,md a\b\c这样的命令,可以在当前目录下建立文件夹a,然后,在a下建立文件夹b,b之下再建立文件夹c

 

echo on/off

on 和 off 都是echo 的关键字,所谓关键字就是系统规定、有特殊用途的字符。

echo on 是打开回显,echo off 是关闭回显。

系统默认是echo on,所以每次为了关闭回显,都会在代码的行首加上echo off,加以关闭。

不关闭回显,连执行的命令符都会显示出来,所以一般会关闭。

 

前面加上@又是为什么?

echo off只能关闭 echo off后面的代码的回显,但不能关闭自身的回显。所以需要加上@关闭自身。

echo.

echo 后加一点表示换行,真奇葩的设计。

 

findstr默认是区分大小写的(跟find命令一样)

findstr /vm “^teststring” *.txt

findstr /v /m “teststring” *.txt

/i        不区分大小写 (/ignore)

/V       只打印不包含匹配的行。

/M       如果文件含有匹配项,只打印其文件名。

 

启动一个程序 

call与start区别

start upload.bat

不会等upload.bat执行完再执行下一条, 如此可实现同时执行多条命令.

 

call upload.bat

call 命令 是要等调用的程序结束以后才运行下面的命令.

 

特殊符号

&:第一条命令 & 第二条命令 [& 第三条命令…]

用这种方法可以同时执行多条命令,而不管命令是否执行成功。

 

&&:第一条命令 && 第二条命令 [&& 第三条命令…]

当碰到执行出错的命令后将不执行后面的命令,如果一直没有出错则一直执行完所有命令。

 

||:第一条命令 || 第二条命令 [|| 第三条命令…]

当碰到执行正确的命令后将不执行后面的命令,如果没有出现正确的命令则一直执行完所有命令;

 

========================================================

 

Sql代码  

  1. @echo off
  2. for /? > for.txt
  3. set /? > set.txt
  4. shift /? >shift.txt
  5. exit

 

执行后在当前路径下就生成for.txt、set.txt和shift.txt三个文件,里面分别记录了for命令、set命令和shift命令的帮助信息

 

 

1.Echo 命令
打开回显或关闭请求回显功能,或显示消息。
如果没有任何参数,echo 命令将显示当前回显设置。

语法
echo [{on|off}] [message]
Sample:
@echo off / echo hello world
在实际应用中我们会把这条命令和重定向符号(也称为管道符号,一般用> >> ^)结合来实现输入一些命令到特定格式的文件中.这将在以后的例子中体现出来。

2.@ 命令
表示不显示@后面的命令
在入侵过程中(例如使用批处理来格式化敌人的硬盘)自然不能让对方看到你使用的命令啦。
Sample:@echo off
@echo Now initializing the program,please wait a minite…
@format X: /q/u/autoset (format 这个命令是不可以使用/y这个参数的,可喜的是微软留了个autoset这个参数给我们,效果和/y是一样的。)

3.Goto 命令
指定跳转到标签,找到标签后,程序将处理从下一行开始的命令。
语法:goto label (label是参数,指定所要转向的批处理程序中的行。)
Sample:

Ruby代码  

  1. if {%1}=={} goto noparms
  2. if {%2}=={} goto noparms(如果这里的if、%1、%2你不明白的话,先跳过去,后面会有详细的解释。)
  3. @Rem check parameters if null show usage
  4. :noparms
  5. echo Usage: monitor.bat ServerIP PortNumber
  6. goto end

 

标签的名字可以随便起,但是最好是有意义的字母啦,字母前加个:用来表示这个字母是标签,goto命令就是根据这个:来寻找下一步跳到到那里。最好有一些说明这样你别人看起来才会理解你的意图啊。

4.Rem 命令
注释命令,在C语言中相当与/*——–*/,它并不会被执行,只是起一个注释的作用,便于别人阅读和你自己日后修改。
Rem Message
Sample:
@Rem Here is the description.

注释可以使用Rem,也可以使用双冒号即::

5.Pause 命令
运行 Pause 命令时,将显示下面的消息:
Press any key to continue . . .

Ruby代码  

  1. @echo off
  2. :begin
  3. copy a:*.* d:\back
  4. echo Please put a new disk into driver A
  5. pause
  6. goto begin

在这个例子中,驱动器 A 中磁盘上的所有文件均复制到d:\back中。显示的注释提示您将另一张磁盘放入驱动器 A 时,pause 命令会使程序挂起,以便您更换磁盘,然后按任意键继续处理。

6.Call 命令

call命令用来从一个批处理脚本中调用另一个批处理脚本

如果在脚本或批处理文件外使用 Call,它将不会在命令行起作用。

语法
call [[Drive:][Path] FileName [BatchParameters]] [:label [arguments]]
参数
[Drive:}[Path] FileName
指定要调用的批处理程序的位置和名称。filename 参数必须具有 .bat 或 .cmd 扩展名。

Ruby代码  

  1. call mvn clean

7.start 命令
调用外部程序,所有的DOS命令和命令行程序都可以由start命令来调用。

入侵常用参数:
MIN 开始时窗口最小化
SEPARATE 在分开的空间内开始 16 位 Windows 程序
HIGH 在 HIGH 优先级类别开始应用程序
REALTIME 在 REALTIME 优先级类别开始应用程序
WAIT 启动应用程序并等候它结束
parameters 这些为传送到命令/程序的参数
执行的应用程序是 32-位 GUI 应用程序时,CMD.EXE 不等应用程序终止就返回命令提示。如果在命令脚本内执行,该新行为则不会发生。

8.choice 命令
choice 此命令可以让用户输入一个字符,从而运行不同的命令。使用时应该加/c:参数,c:后应写提示可输入的字符,之间无空格。它的返回码为1234……

如: choice /c:dme defrag,mem,end
将显示
defrag,mem,end[D,M,E]?

Ruby代码  

  1. @echo off
  2. choice /c:dme defrag,mem,end
  3. if errorlevel 3 goto defrag (应先判断数值最高的错误码)
  4. if errorlevel 2 goto mem
  5. if errotlevel 1 goto end
  6.  
  7. :defrag
  8. c:\dos\defrag
  9. goto end
  10. :mem
  11. mem
  12. goto end
  13. :end
  14. echo good bye

此文件运行后,将显示 defrag,mem,end[D,M,E]? 用户可选择d m e ,然后if语句将作出判断,d表示执行标号为defrag的程序段,m表示执行标号为mem的程序段,e表示执行标号为end的程序段,每个程序段最后都以goto end将程序跳到end标号处,然后程序将显示good bye,文件结束。

9.If 命令
if 表示将判断是否符合规定的条件,从而决定执行不同的命令。 有三种格式:
1、if “参数” == “字符串”  待执行的命令

检测字符串

(注意,用if检测字符串是否相等的时候,后面用的不是“=”,而是“==”)
参数如果等于指定的字符串,则条件成立,运行命令,否则运行下一句。(注意是两个等号)
如if “%1″==”a” format a:
if {%1}=={} goto noparms
if {%2}=={} goto noparms

 

检测数值:(注意,批处理中大于符号不能用:“>”,而用”gtr”,其它的也类似)

比较运算符一览:

EQU – 等于

NEQ – 不等于

LSS – 小于

LEQ – 小于或等于

GTR – 大于

GEQ – 大于或等于

Sql代码  

  1. @echo off
  2. set /a num1=20
  3. set /a num2=15
  4. if %num1% gtr %num2% echo %num1%大于%num2%
  5. if %num1% EQU %num2% echo %num1%等于%num2%
  6. if %num1% LSS %num2% echo %num1%小于%num2%
  7. pause>nul

 
2、if exist 文件名  待执行的命令
如果有指定的文件,则条件成立,运行命令,否则运行下一句。
如if exist config.sys edit config.sys

3、if errorlevel / if not errorlevel 数字  待执行的命令
如果返回码等于指定的数字,则条件成立,运行命令,否则运行下一句。

说明:环境变量errorlevel的初始值为0,当一些命令执行不成功,就会返回一个数值,如:1 ,2 等
如if errorlevel 2 goto x2
DOS程序运行时都会返回一个数字给DOS,称为错误码errorlevel或称返回码,常见的返回码为0、1。

常用命令的返回值及其代表的意义

 

IF [NOT]  ERRORLEVEL number   do command

IF [NOT]  string1==string2   do command

IF [NOT]  EXIST filename   do command

 

10.for 命令
for 命令是一个比较复杂的命令,主要用于参数在指定的范围内循环执行命令。
在批处理文件中使用 FOR 命令时,指定变量请使用 %%variable

 

在批处理文件中使用 FOR 命令时,指定变量请使用 %%variable而不要用 %variable。变量名称是区分大小写的,所以 %i 不同于 %I

for /f %%i in (a.txt) do echo %%i

//这个会显示a.txt里面的内容,因为/f的作用,会读出a.txt中的内容。

for %%i in (a.txt) do echo %%i

//而这个只会显示a.txt这个名字,并不会读取其中的内容。

 

for %%a in (c:\*.*) do echo %%a

::显示C盘根目录下所有非隐藏、非系统属性文件

::只显示文件,不显示文件夹

 

一、/d

(只搜索目录,不搜索文件)

格式:FOR /D %variable IN (set) DO command [command-parameters]

 

for /d %%i in (c:/*) do echo %%i  –显示c盘根目录下的所有目录

for /d %%i in (???) do echo %%i   –显示当前目录下名字只有1-3个字母的目录(注意是目录)

 

二、/R

(只搜索文件,不搜索目录)

(搜索指定路径及所有子目录中与set相符合的所有文件)

格式:FOR /R [[drive:]path] %variable IN (set) DO command [command-parameters]

 

for /r c:/ %%i in (boot.ini) do echo %%i

–枚举了c盘所有目录,找到含有boot.ini的路径并显示

for /r d:/backup %%i in (1) do echo %%i

–枚举d/backup目录,现在当前路径和当前路径下所有文件夹,不过后面带了个1

d:\backup\1

for /r c:/ %%i in (boot.ini) do if exist %%i echo %%i

–很好的搜索命令,列举boot.ini存在的目录

 

三、/L 

(以增量形式从开始到结束的一个数字序列。可以使用负的 Step)

格式:FOR /L %variable IN (start,step,end) DO command [command-parameters]

该集表示以增量形式从开始到结束的一个数字序列。可以使用负的 Step

 

示例:

for /l %%i in (1,1,5) do @echo %%i  –输出1 2 3 4 5

for /l %%i in (1,2,10) do @echo %%i  –输出1,3,5,7,9

for /l %%i in (100,-20,1) do @echo %%i  –输出100,80,60,40,20

for /l %%i in (1,1,5) do start cmd  –打开5个CMD窗口

for /l %%i in (1,1,5) do md %%i  –建立从1~5共5个文件夹

for /l %%i in (1,1,5) do rd /q %%i  –删除从1~5共5个文件夹

 

========================================================

setlocal 与 变量延迟

例1:

@echo off

set a=4

set a=5 & echo %a%

pause

结果:4

解说:为什么是4而不是5呢?在echo之前明明已经把变量a的值改成5了?

 

(set a=5

echo %a%

::这种情况a直接是5

)

 

让我们先了解一下批处理运行命令的机制:

批处理读取命令时是按行读取的(另外例如for命令等,其后用一对圆括号闭合的所有语句也当作一行),在处理之前要完成必要的预处理工作,这其中就包括对该行命令中的变量赋值。我们现在分析一下例1,批处理

在运行到这句“set a=5 & echo %a%”之前,先把这一句整句读取并做了预处理——对变量a赋了值,那么%a%当然就是4了!(没有为什么,批处理就是这样做的。)

而为了能够感知环境变量的动态变化,批处理设计了变量延迟。简单来说,在读取了一条完整的语句之后,不立即对该行的变量赋值,而会在某个单条语句执行之前再进行赋值,也就是说“延迟”了对变量的赋值。

 

那么如何开启变量延迟呢?变量延迟又需要注意什么呢?举个例子说明一下:

例2:

@echo off

setlocal enabledelayedexpansion

set a=4

set a=5 & echo !a!

pause

结果:5

解说:启动了变量延迟,得到了正确答案。变量延迟的启动语句是“setlocal enabledelayedexpansion”,并且变量要用一对叹号“!!”括起来(注意要用英文的叹号),否则就没有变量延迟的效果。

 

分析一下例2,首先“setlocal enabledelayedexpansion”开启变量延迟,然后“set a=4”先给变量a赋值为4,“set a=5 & echo !a!”这句是给变量a赋值为5并输出(由于启动了变量延迟,所以批处理能够感知到动态变化,即不是先给该行变量赋值,而是在运行过程中给变量赋值,因此此时a的值就是5了)。

再举一个例子巩固一下。

例3:

@echo off

setlocal enabledelayedexpansion

for /l %%i in (1,1,5) do (

set a=%%i

echo !a!

)

pause

结果:

1

2

3

4

5

 

(若不加延迟变量,则会输出几个!a!字符)

解说:本例开启了变量延迟并用“!!”将变量扩起来,因此得到我们预期的结果。如果不用变量延迟会出现什

么结果呢?结果是这样的:

ECHO 处于关闭状态。

ECHO 处于关闭状态。

ECHO 处于关闭状态。

ECHO 处于关闭状态。

ECHO 处于关闭状态。

即没有感知到for语句中的动态变化。

========================================================

【续】[一日一教学]批处理系列帖子索引 http://www.bathome.net/thread-13798-1-1.html
 

序号 标    题 首发日期 更新日期
1 目录跳转:cd 20081112
2 以树形格式罗列文件:tree 20081112
3 内容重定向:>和>> 20081113
4 条件判断:if  20081114
5 流程跳转:goto 20081117
6 复制文件:copy 20081221
7 删除文件:del 20090101
8 重命名文件(夹):ren 20090212 20090226
9 创建文件夹:md 20090213
10 删除文件夹:rd 20090214
11 移动文件(夹):move 20090606 20080611
12 字符串查找:find 20090607 20090608
13 字符串查找增强:findstr
14 文件替换:replace
15 设置文件属性:attrib
16 显示磁盘卷标和序列号:vol
17 编辑磁盘卷标:label
18 转换磁盘系统格式:convert
19 格式化磁盘:format
20 创建虚拟盘符:subst
21 给磁盘做个体检:chkdsk
22 清理CMD屏幕上的信息:cls
23 更换CMD窗口的颜色:color
24 给CMD窗口换个标题:title
25 获取系统日期:date
26 获取系统时间:time
27 退出CMD窗口:exit
28 查看系统版本:ver
29 返回到上次路径:pushd/popd
30 操控注册表:reg
31 注册dll文件:regsver32
32 打开文件、运行程序:start
33 关闭或重启计算机:shutdown
34 显示进程清单:tasklist
35 关闭指定进程:taskkill
36 查看IP配置信息:ipconfig
37 测试IP连接:ping
38 解析地址:arp
39 配置计算机网络信息:netsh
40 传输文件:ftp
41 调用子过程或外部程序:call
42 显示提示信息:echo
43 暂停执行:pause
44 设置变量:set
45 注释语句:rem和::
46 更改参数的位置:shift
47 一次只显示单屏内容:more
48 字符串排序:sort
49 复合语句连接符:&、&&和||
50 管道符号:|

 

在.NET Core 1.0 RC2 上 运行 Orchard2

请保留链接:http://www.freeboygirl.com/running-orchard2-cms-on-core-rc2-net

文章背景:.NET Core1.0 RC2跨平台运行Orchard2,经过测试,竟然成功了。群里同学问详细操作步骤,我把安装测试过程写下来,互相学习。目前 VS 2015 还不支持 RC2 projects,大概需要4-6周可以。要把orchard2运行起来,有两种方法:

1.用vscode 编译和运行

2.用dos命令编译和运行

这篇讲在DOS下如何运行哦rchard2。vscode下运行也成功了,但是按管方的是不行的,太旧。如果大家对vscode如何运行有兴趣,在下面留言告诉我一下,想知道的多就写。

一、本文测试环境说明

OS:Windows server 2012 R2 x64

orchard2:最新的orchard2 源码,此项目跟随 .Net Core 1.0 RC2的脚步,更新很快。

dotnet:https://github.com/dotnet/cli  里面有最新的 .NET Core1.0 RC2下载。

 

二、准备工作说明

1.下载Orchard2 zip源码并解压

下载地址:https://github.com/OrchardCMS/Orchard2

本文下载的是:  https://github.com/OrchardCMS/Orchard2/archive/44603d4b3593201cd16c31ec1c31256b38675104.zip 

解压到:C:\tmp\Orchard2-master

2.搭建.NET Core编译环境

下载安装 .NET Core SDK和.NET Core CLI

从 dotnet cli github项目主页找到最新版的.Net Core SDK下载:https://github.com/dotnet/cli

点击标红处分别下载,.NET Core Installer 和.NET Core SDK Installer 分别安装 .NET Core runtime and framework 和 .NET Core CLI 编译工具。

安装完成后的情况

在dos里运行dotnet 试试,如下,说明环境搭建OK了

三、启动orchard2网站

1.下载orchard2所需要的程序包到本机

cd C:\tmp\Orchard2-master

dotnet restore

你可以看见在下载程序包

如果下载出现错误,需要多试几次,那是因为有时候通往国外的数据包路上堵车,你若放弃,必定失败,此叫半途而废。你要坚持,坚持就是胜利,语文老师教你的名句,你要用于实践,此叫理论联系实际。哈哈

2.启动orhard2网站

换个位置,进入Orchard.Web的主目录

cd C:\tmp\Orchard2-master\src\Orchard.Web

启动网站,dos 下运行如下命令

dotnet run

然后就进入漫长的编译过程,一个一个模块的编译。路漫漫其修远兮,苦瘪程序员将上下而求索。等吧。。。

突然,翻滚的界面停止了跳动,似一美貌少女静静的躺在那里。仿佛还在微笑着向我招手–来吧

Microsoft.AspNetCore.Server.Kestrel服务器在5000端口负责监听来访者。IIS?不要了!可怜的IIS,人老珠黄,迟早被抛弃,自己早作打算吧,还来得及。

 

三、访问orchard2网站

在浏览器输入:

http://localhost:5000

温馨提示:

1.因本人语文乃数学老师所教,如有疑问之处,属本人表述不清,敬请留言,愿作修改。

2.新站开通,百度等搜索引擎过门而不入,很是受伤。写个东西真花时间,累得要死,我愿意去搬砖。

3.转载本文,非常欢迎。但请留个全身,比如下面的内容。

请保留链接:http://www.freeboygirl.com/running-orchard2-cms-on-core-rc2-net

Flume+Kafka收集Docker容器内分布式日志应用实践

1 背景和问题

随着云计算、PaaS平台的普及,虚拟化、容器化等技术的应用,例如Docker等技术,越来越多的服务会部署在云端。通常,我们需要需要获取日志,来进行监控、分析、预测、统计等工作,但是云端的服务不是物理的固定资源,日志获取的难度增加了,以往可以SSH登陆的或者FTP获取的,现在可不那么容易获得,但这又是工程师迫切需要的,最典型的场景便是:上线过程中,一切都在GUI化的PaaS平台点点鼠标完成,但是我们需要结合tail -F、grep等命令来观察日志,判断是否上线成功。当然这是一种情况,完善的PaaS平台会为我们完成这个工作,但是还有非常多的ad-hoc的需求,PaaS平台无法满足我们,我们需要日志。本文就给出了在分布式环境下,容器化的服务中的分散日志,如何集中收集的一种方法。

2 设计约束和需求描述

做任何设计之前,都需要明确应用场景、功能需求和非功能需求。

2.1 应用场景

分布式环境下可承载百台服务器产生的日志,单条数据日志小于1k,最大不超过50k,日志总大小每天小于500G。

2.2 功能需求

1)集中收集所有服务日志。

2)可区分来源,按服务、模块和天粒度切分。

2.3 非功能需求

1)不侵入服务进程,收集日志功能需独立部署,占用系统资源可控。

2)实时性,低延迟,从产生日志到集中存储延迟小于4s。

3)持久化,保留最近N天。

4)尽量递送日志即可,不要求不丢不重,但比例应该不超过一个阈值(例如万分之一)。

4)可以容忍不严格有序。

5)收集服务属于线下离线功能,可用性要求不高,全年满足3个9即可。

 

3 实现架构

一种方案实现的架构如下图所示:

3.1 Producer层分析

PaaS平台内的服务假设部署在Docker容器内,那么为了满足非功能需求#1,独立另外一个进程负责收集日志,因此不侵入服务框架和进程。采用Flume NG来进行日志的收集,这个开源的组件非常强大,可以看做一种监控、生产增量,并且可以发布、消费的模型,Source就是源,是增量源,Channel是缓冲通道,这里使用内存队列缓冲区,Sink就是槽,是个消费的地方。容器内的Source就是执行tail -F这个命令的去利用linux的标准输出读取增量日志,Sink是一个Kafka的实现,用于推送消息到分布式消息中间件。

 

3.2 Broker层分析

PaaS平台内的多个容器,会存在多个Flume NG的客户端去推送消息到Kafka消息中间件。Kafka是一个吞吐量、性能非常高的消息中间件,采用单个分区按照顺序的写入的方式工作,并且支持按照offset偏移量随机读取的特性,因此非常适合做topic发布订阅模型的实现。这里图中有多个Kafka,是因为支持集群特性,容器内的Flume NG客户端可以连接若干个Kafka的broker发布日志,也可以理解为连接若干个topic下的分区,这样可以实现高吞吐,一来可以在Flume NG内部做打包批量发送来减轻QPS压力,二来可以分散到多个分区写入,同时Kafka还会指定replica备份个数,保证写入某个master后还需要写入N个备份,这里设置为2,没有采用常用的分布式系统的3,是因为尽量保证高并发特性,满足非功能需求中的#4。

 

3.3 Consumer层分析

消费Kafka增量的也是一个Flume NG,可以看出它的强大之处,在于可以接入任意的数据源,都是可插拔的实现,通过少量配置即可。这里使用Kafka Source订阅topic,收集过来的日志同样先入内存缓冲区,之后使用一个File Sink写入文件,为了满足功能需求#2,可区分来源,按服务、模块和天粒度切分,我自己实现了一个Sink,叫做RollingByTypeAndDayFileSink,源代码放到了github上,可以从这个页面下载jar,直接放到flume的lib目录即可。

 

4 实践方法

4.1 容器内配置

Dockerfile

Dockerfile是容器内程序的运行脚本,里面会含有不少docker自带的命令,下面是要典型的Dockerfile,BASE_IMAGE是一个包含了运行程序以及flume bin的镜像,比较重要的就是ENTRYPOINT,主要利用supervisord来保证容器内进程的高可用。

 FROM ${BASE_IMAGE}
MAINTAINER ${MAINTAINER}
ENV REFRESH_AT ${REFRESH_AT}
RUN mkdir -p /opt/${MODULE_NAME}
ADD ${PACKAGE_NAME} /opt/${MODULE_NAME}/
COPY service.supervisord.conf /etc/supervisord.conf.drvice.supervisord.conf
COPY supervisor-msoa-wrapper.sh /opt/${MODULE_NAME}/supervisor-msoa-wrapper.sh
RUN chmod +x /opt/${MODULE_NAME}/supervisor-msoa-wrapper.sh
RUN chmod +x /opt/${MODULE_NAME}/*.sh
EXPOSE
ENTRYPOINT [“/usr/bin/supervisord”, “-c”, “/etc/supervisord.conf”]

下面是supervisord的配置文件,执行supervisor-msoa-wrapper.sh脚本。

[program:${MODULE_NAME}]

command=/opt/${MODULE_NAME}/supervisor-msoa-wrapper.sh

下面是supervisor-msoa-wrapper.sh,这个脚本内的start.sh或者stop.sh就是应用程序的启动和停止脚本,这里的背景是我们的启停的脚本都是在后台运行的,因此不会阻塞当前进程,因此直接退出了,Docker就会认为程序结束,因此应用生命周期也结束,这里使用wait命令来进行一个阻塞,这样就可以保证即使后台运行的进程,我们可以看似是前台跑的。

 

这里加入了flume的运行命令,–conf后面的参数标示会去这个文件夹下面寻找flume-env.sh,里面可以定义JAVA_HOME和JAVA_OPTS。–conf-file指定flume实际的source、channel、sink等的配置。

#! /bin/bash

function shutdown()

{

date

echo “Shutting down Service”

unset SERVICE_PID # Necessary in some cases

cd /opt/${MODULE_NAME}

source stop.sh

}

## 停止进程

cd /opt/${MODULE_NAME}

echo “Stopping Service”

source stop.sh

## 启动进程

echo “Starting Service”

source start.sh

export SERVICE_PID=$!

## 启动Flume NG agent,等待4s日志由start.sh生成

sleep 4

nohup /opt/apache-flume-1.6.0-bin/bin/flume-ng agent –conf /opt/apache-flume-1.6.0-bin/conf –conf-file /opt/apache-flume-1.6.0-bin/conf/logback-to-kafka.conf –name a1 -Dflume.root.logger=INFO,console &

# Allow any signal which would kill a process to stop Service

trap shutdown HUP INT QUIT ABRT KILL ALRM TERM TSTP

echo “Waiting for $SERVICE_PID”

wait $SERVICE_PID

Flume配置

source本应该采用exec source,执行tailf -F日志文件即可。但是这里使用了一个自行开发的StaticLinePrefixExecSource,源代码可以在github上找到。之所以采用自定义的,是因为需要将一些固定的信息传递下去,例如服务/模块的名称以及分布式服务所在容器的hostname,便于收集方根据这个标记来区分日志。如果这里你发现为什么不用flume的拦截器interceptor来做这个工作,加入header中一些KV不就OK了吗?这是个小坑,我后续会解释一下。

 

例如原来日志的一行为:

[INFO]  2016-03-18 12:59:31,080 [main]  fountain.runner.CustomConsumerFactoryPostProcessor      (CustomConsumerFactoryPostProcessor.java:91)    -Start to init IoC container by loading XML bean definitions from classpath:fountain-consumer-stdout.xml

按照如下配置,那么实际传递给Channel的日志为:

service1##$$##m1-ocean-1004.cp  [INFO]  2016-03-18 12:59:31,080 [main]  fountain.runner.CustomConsumerFactoryPostProcessor      (CustomConsumerFactoryPostProcessor.java:91)    -Start to init IoC container by loading XML bean definitions from classpath:fountain-consumer-stdout.xml

channel使用内存缓冲队列,大小标识可容乃的日志条数(event size),事务可以控制一次性从source以及一次性给sink的批量日志条数,实际内部有个timeout超时,可通过keepAlive参数设置,超时后仍然会推送过去,默认为3s。

 

sink采用Kafka sink,配置broker的list列表以及topic的名称,需要ACK与否,以及一次性批量发送的日志大小,默认5条一个包,如果并发很大可以把这个值扩大,加大吞吐。

4.2 Broker配置

参考Kafka官方的教程,这里新建一个名称叫做keplerlog的topic,备份数量为2,分区为4。

 > binfka-topics.sh --create --zookeeper localhost:2181 --replication-factor 2 --partitions 4 --topic keplerlog

制造一些增量信息,例如如下脚本,在终端内可以随便输入一些字符串:

  > binfka-console-producer.sh --broker-list localhost:9092 --topic keplerlog

打开另外一个终端,订阅topic,确认可以看到producer的输入的字符串即可,即表示联通了。

 > binfka-console-consumer.sh --zookeeper localhost:2181 --topic keplerlog --from-beginning

4.3 集中接收日志配置

Flume配置

首先source采用flume官方提供的KafkaSource,配置好zookeeper的地址,会去找可用的broker list进行日志的订阅接收。channel采用内存缓存队列。sink由于我们的需求是按照服务名称和日期切分日志,而官方提供的默认file roll sink,只能按照时间戳,和时间interval来切分。

定制版RollingByTypeAndDayFileSink

源代码见github。RollingByTypeAndDayFileSink使用有两个条件:

1)Event header中必须有timestamp,否则会忽略事件,并且会抛出{@link InputNotSpecifiedException}

2)Event body如果是按照##$$##分隔的,那么把分隔之前的字符串当做模块名称(module name)来处理;如果没有则默认为default文件名。

 

输出到本地文件,首先要设置一个跟目录,通过sink.directory设置。其次根据条件#2中提取出来的module name作为文件名称前缀,timestamp日志作为文件名称后缀,例如文件名为portal.20150606或者default.20150703。

 

规整完的一个文件目录形式如下,可以看出汇集了众多服务的日志,并且按照服务名称、时间进行了区分:

~/data/kepler-log$ ls
authorization.20160512  
default.20160513  
default.20160505 
portal.20160512       
portal.20160505   
portal.20160514

不得不提的两个坑

坑1

回到前两节提到的自定义了一个StaticLinePrefixExecSource来进行添加一些前缀的工作。由于要区分来源的服务/模块名称,并且按照时间来切分,根据官方flume文档,完全可以采用如下的Source拦截器配置。例如i1表示时间戳,i2表示默认的静态变量KV,key=module,value=portal。

但是flume官方默认的KafkaSource(v1.6.0)的实现:

可以看出自己重写了Event header中的KV,丢弃了发送过来的header,因为这个坑的存在因此,tailf -F在event body中在前面指定模块/服务名称,然后RollingByTypeAndDayFileSink会按照分隔符切分。否则下游无法能达到KV。

 

坑2

exec source需要执行tail -F命令来通过标准输出和标准错误一行一行的读取,但是如果把tail -F封装在一个脚本中,脚本中再执行一些管道命令,例如tail -F logback.log | awk ‘{print “portal##$$##”$0}’,那么exec source总是会把最近的输出丢弃掉,导致追加到文件末尾的日志有一些无法总是“姗姗来迟”,除非有新的日志追加,他们才会被“挤”出来。这个问题比较诡异。暂时没有细致研究。以示后人不要采坑。

 

5 结语

从这个分布式服务分散日志的集中收集方法,可以看出利用一些开源组件,可以非常方便的解决我们日常工作中所发现的问题,而这个发现问题和解决问题的能力才是工程师的基本素质要求。对于其不满足需求的,需要具备有钻研精神,知其然还要知其所以然的去做一些ad-hoc工作,才可以更加好的leverage这些组件。

 

另外,日志的收集只是起点,利用宝贵的数据,后面的使用场景和想象空间都会非常大,例如

1)利用Spark streaming在一个时间窗口内计算日志,做流量控制和访问限制。

2)使用awk脚本、scala语言的高级函数做单机的访问统计分析,或者Hadoop、Spark做大数据的统计分析。

3)除了端口存活和语义监控,利用实时计算处理日志,做ERROR、异常等信息的过滤,实现服务真正的健康保障和预警监控。

4)收集的日志可以通过logstash导入Elastic Search,使用ELK方式做日志查询使用。

来源:neoremind.com

原文:http://neoremind.com/2016/05/flumekafka收集docker容器内分布式日志应用实践/

如有侵权或不周之处,敬请劳烦联系若飞(微信:1321113940)马上删除,谢谢!