科学 1

2.1.1 变量命名

Go语言中的变量名、常量叫做、类型名、函数名及包名等有着的命名暨C语言一样都照这样一个简易的命名规则:一个名字务必盖一个假名或下划线开头,后面可以与任意数量之字母、数字还是生划线。对于字母区分轻重缓急写,例如:name和Name是个别独不等之名。

命名不可知与重大字相同,Go语言提供了25独重要字,只能在特定的语法中运用。

25个关键字

break

default

func

interface

select

case

defer

go

map

struct

chan

else

goto

package

switch

const

fallthrough

if

range

type

continue

for

import

return

var

 

另外,Go语言还有约30基本上单预定义的名,主要用以内建的常量、类型及函数。这些名字不是重大字,可以再次定义和应用,虽然于有的特种状况中再定义是发出含义的,但建议尽量不要再次定义,以致使语义混乱问题。

内建常量

true false iota nil

内建类型

int int8 int16 int32 int64

uint uint8 uint16 uint32 uint64 uintptr

float32 float64 complex128 complex64

bool byte rune string error

内建函数

make len cap new append copy close delete

complex real imag

panic recover

 

  为什么如此说啊?比如针对某方法(方法名:GetUserList)我们而开展1秒钟最多10赖的限,现在我们就算新建一个int型的Cache对象,然后设置1秒钟后过消失。那么以访问GetUserList方法前,我们即便优先判断这个Cache对象的价是否超越10,如果超出10便无执GetUserList方法,如果低于10虽说允许实施。每当访问该目标的当儿要非存在或者逾期就新建,这样循环,则该目标永远不容许跨越10。

2.6作用域

一个声明语句以顺序中之实业和一个名关闭,比如一个函数或一个变量。声明语句的作用域是赖源代码中可以中利用此名字的界定。

并非用作用域和生命周期混为一谈。声明语句的作用域对应之是一个源代码的公文区域,它是一个编译时之性质。一个变量的生命周期是依程序运行时变量是的实用时间段,在斯日区域外,它可让先后的其余一些引用,是一个运作时之概念。

语法块是由花括号所蕴涵的平层层讲话。语法块内部宣称的名是力不从心给标语法块访问的。语句块决定了里面宣称的名的作用域范围。有一个语法块为任何源代码,称为全局语法块;然后是每个包之包语法块;每个for、if和switch语句的语法块;每个switch或select的分为来独立的语法块;当然也发生显书写的语法块(花括号包含的口舌)。

宣示语句对应的词法域决定了作用域范围的深浅。对于坐的类、函数和常量,例如int、len和true等还是大局作用域的;任何在函数外部声明的讳可以于保管之旁来源文件中访问,是包级作用域。对于导入的保,则是针对性应源文件级的作用域。控制流标号,就是break、continue或goto语句子后继之的那种标号,是函数级作用域。

当编译器遇到一个名引用时,如果它是一个扬言,首先由不过内层的作用域向全局作用域查找。如果搜索未果,则错误。如果名字在内部及表面分别声明了,则中块的扬言首先为找到,它见面遮掩外部同名的声明。

根据这种简易缓存过期策略的模子,在就2秒钟内,我们虽然平均每秒钟都看了10糟,满足这确定,但是只要我们于中取一个里段,0.5秒~1.5秒内,也是1秒钟,但是可的确的拜访了14次!远远超过了我们设置的
1秒钟最多看10潮的 限制。

2.4.1 指针概念

指南针是一个项目,该种的变量称为指针变量。指针变量存储一个变量的地方。它不同让一般的变量,一般变量存放的是数本身,而指针变量存放的是数据的地点。

 科学 2

1   if ((int)HttpRuntime.Cache["GetUserListNum"] > 10) //大于10请求失败
2   {
3      Console.WriteLine("禁止请求");
4   }
5   else
6   {
7      HttpRuntime.Cache["GetUserListNum"] = (int)HttpRuntime.Cache["GetUserListNum"] + 1; //否则该缓存对象的值+1
8      Console.WriteLine("允许请求");
9   }

2.3.1 整数

1 整数类型

Go语言的数值类涵盖了几乎种不同长短的平头、浮点数和复数。每种数值类且决定了对应的取值范围以及是否支持正负号。

 

类型

长度(字节)

取值范围

int8

1

(0~255)

uint8

1

(-128~127)

int16

2

(0~65535)

uint16

2

(-32768~32767)

int32

4

(-2147483648~2147483647)

uint32

4

(0~4294967295)

int64

8

(-9223372036854775808~9223372036854775807)

uint64

8

(0~18446744073709551615)

int

4或8

与机器字长和编译器都有关系

uint

4或8

与机器字长和编译器都有关系

uintptr

4或8

32平台4个字节,64位平台8个字节,底层编程才需要

byte

1

与uint8等价,通常表示一个unicode字符编码

rune

4

与int32等价,一般强调是一个原始数据而不是一个小整数。在一个字符串中,表示一个字符对应utf8的码点。

 

2 运算符

Go语言提供了长的置运算符,包括算术运算符、比较运算符、逻辑运算符、位运算符、赋值运算符和外运算符等。

算术运算符:

运算符

描述

+

*

/

%

模运算(求余数)

++

自增

自减

 

在Go语言中,%取模运算符的标志和于取模的记总是一样的,因此5%3暨5%-3底结果尚且是2。除法运算符的结果虽然因让操作数是否全都否整数,例如5.0/4.0之结果是1.25,但是7/4的结果吗1,去丢小数部分,而非是四放弃五可。

关系(比较)运算符:

简单单相同之平头类型可以利用下的二元关系运算符进行比较,比较表达式的结果是布尔种。

运算符

描述

==

相等

!=

不等

<

小于

<=

小于或等于

>

大于

>=

大于或等于

 

 

逻辑运算

运算符

描述

!

&&

||

 

位运算:

前4独操作运算符并无区分是产生号子还是无符号数:

运算符

描述

&

位与and (左侧和右侧都为1,则为1;否则为0)

|

位或 or(左侧或右侧只要有一个为1,结果为1;都为0结果才为0)

^

位异或 xor (相同为0,不同为1)

&^

位清空and not(右侧是0,左侧数不变;右侧是1,则左侧数清零)

<<

左移

>>

右移

号运算的例证:

X=2,y=15

二进制结果

十进制结果

0000 0010 & 0000 1111

0000 0010

 2

0000 0010 | 0000 1111

0000 1111

15

0000 0010 ^ 0000 1111

0000 1101

13

0000 0010 &^ 0000 1111

0000 0000

0

0000 0010<<3  

0001 0000

16

0000 0010>>1

0000 0001

1

 

运算符优先级:

 

 


2.2.3 iota常量生成器

Go语言预定义的常量有:true、false和iota,其中iota比较新鲜。常量声明可以以iota常量生成器初始化,它用来深成一组为一般规则初始化的常量,但是不用每行都写一不折不扣初始化表达式。在一个const声明语句被,在首先只声明的常量所当的施行,iota将会见给置为0,然后以每一个起常量声明的行加一。下面是源于time包的例证。这种定义法在Go语言中司空见惯用于定义枚举值。

type weekday int

const(

    Sunday weekday iota   //0

    Monday                //1   

    Tuesday               //2

    Wednesday             //3

    Thursday              //4

    Friday                //5 

    Saturday              //6

)

咱们啊足以当千头万绪的常量表达式中应用iota,例如下面每个常量都是1024之掩盖。

const (

    _ = 1 << (10 * iota)

    KiB

    MiB

    GiB

    TiB

)

fmt.Println(KiB, MiB, GiB, TiB)

打印结果:1024 1048576 1073741824 1099511627776

小结:var 声明变量,const声明常量。声明时得带来项目。也得无带项目,通过右想。

正文为闹片只示范,一个凡据悉单机环境之兑现,第二独则是冲分布式的Redis实现

2.7.1 标准输出函数

Print( )函数采用默认格式将其参数格式化并形容副标准输出。如果少独相邻之参数都不是字符串,会于其的出口之间添加空格。返回写副的字节数和遇的旁错误。函数原型如下:

func Print(a …interface{}) (n int, err error)

 

Println( )与Print( )函数的力量基本一致,唯一不同之是当输出了晚,自动增加换行。函数原型如下:

func Println(a …interface{}) (n int, err error)

 

Printf()函数根据format参数生成格式化的字符串并写副标准输出。返回写副的字节数和遇的别不当。函数原型如下:

func Printf(format string, a …interface{}) (n int, err error)

 

2.7.2 标准输入函数

Scan( )函数从标准输入扫描文本,将打响读取的空白分隔的值保存进遂传送让本函数的参数。换行视为空白。返回成功扫描的章个数与遇的外错误。如果读取的条款比提供的参数少,会回来一个错误报告原因。函数原型如下:

func Scan(a …interface{}) (n int, err error)

Scanln类似Scan,但会在换行时止扫描。最后一个条款后务必有换行或者到了位置。函数原型如下:

func Scanln(a …interface{}) (n int, err error)

Scanf从标准输入扫描文本,根据format 参数指定的格式将成功读取的空分隔的值保存进遂传送给本函数的参数。返回成功扫描的条文个数和遇的别样错误。函数原型如下:

 

func Scanf(format string, a …interface{}) (n int, err error)

那哪些是的来解决地方的题目呢?我们得经模拟对话级别之信号量立马同样心眼,这也便是咱今天之主题了。
   什么是信号量?仅就以代码而言,  static
SemaphoreSlim semaphoreSlim = new SemaphoreSlim(5); 
它的意思就是意味着以多线程情况下,在其余一样时时,只能以5单线程去顾。

2.4.4 二级指针(多级指针)

二级指针保存一级指针变量的地点。

package main

 

import "fmt"

 

func main() {

 

    var a int = 100

    var pa *int = &a

    var ppa **int = &pa

 

    //打印a的值

    fmt.Printf("%v,%v,%v\n", a, *pa, **ppa)

    //打印a的地址

    fmt.Printf("%v,%v,%v", &a, pa, *ppa)

 

}

//打印结果:

100,100,100

0xc0420401d0,0xc0420401d0,0xc0420401d0

 

 

2.2.1 字面常量

所谓字面常量,是依程序中硬编码的常量,如:

25

3.14159

2+3i

true

"hello"

以其他语言中,常量通常发生一定的档次,Go语言的字面常量是无类型的。只要这常量在相应品种的值域范围外,就可视作该类型的常量。例如,25足赋值给int、 uint、int32、int64、float32、float64、complex64、complex128齐门类的变量。

诸如此类的沉思和贯彻相对来说非常简单,但是因这样的一个模型设定,那么即使见面面世这种景象:

2.2.2 常量声明

利用const来声称常量,可以吃常量一个投机的讳例如:

const pi = 3.1415926

为堪批量宣称:

const (

     e = 2.7182818

     pi = 3.1415926

)

一个常量的扬言也可界定品种,但无是少不了的。如果没出示指定项目,那么她同字面量一样,是无类型常量。常量定义的右值也得是一个于编译期运算的常量表达式,例如:

const i = 1 << 3 //右值是常量表达式

设若是批量声称的常量,除第一个他其他的常量的右手的初始化表达式都可简单,默认使用前常量的初始化表达式写法。例如:

const (

        a = 1

        b

        c = 2

        d

        e

    )

    fmt.Println(a, b, c, d, e)

打印结果:1 1 2 2 2

而齐图,每个点代表一律赖拜访请求,我在0秒的时刻
新建了一个名字也GetUserListNum的缓存对象。
在0~0.5秒内
我访问了3不成,在0.5~1秒内,我们走访了7次。此时,该对象消失,然后我们跟着访问,该目标重置为0.
              
 在第1~1.5秒中,还是看了7糟糕,在第1.5秒~2秒中走访了3次等。

2.4指针

4容器4线程模型

现在,在贯彻代码的前头我们先行规划一个模子。

科学 3

  假设我们发出一个用户A的管道,这个管道里装着用户A的伸手,比如用户A在平等秒钟发出了10浅呼吁,那么每一个呼吁过来,管道里之因素还见面多一个。但是我们设定是管道最多只能容10独元素,而且每个元素的存活期为1秒,1秒后则该因素消失。那么这样设计吧,无论是速率还是多少的突进,都见面发生管道长度的限定。这样一来,无论由哪一个时光节点还是时间距离出发,这个管道还能够满足我们的频率限制要求。

若是这里的管道,就非得和会话Id来对号入座了。每当发生新会话进来的时段就是特别成一个初管道。这个会话id根据自己场景所必然,可以是sessionId,可以是ip,也得是token。

这就是说既这个管道是会讲话级别之,我们定得用一个器皿,来装这些管道。现在,我们为IP来命名会话管道,并将持有的管道都装在一个容器中,如图

科学 4

如果根据刚才之设定,我们尚用对容器内之各级条管道的素进行拍卖,把过的叫删除掉,为夫,还得单独为该容器开辟出一个线程来也各条管道进行元素的清理。而当管道的元素呢0时,我们即便彻底掉该管道,以便节省容器空间。

 科学 5

理所当然,由于用户量基本上,一个器皿内可能存在上万只管道,这个时刻仅用一个器皿来装来清理,在效率上强烈是不够的。这个上,我们虽得对容器进行横向扩张了。

  比如,我们可以根据Cpu核心数自动生成对应之数码的器皿,然后因一个算法,对IP来开展导流。我当下cpu是4独逻辑核心,就生成了4只容器,每当用户访问的时,都见面首先经过一个算法,这个算法会对IP进行处理,如192.168.1.11~192.168.1.13是Ip段进第一单容器,xxx~xxx进第二单容器,依次类推,相应的,也就生了4只线程去分别处理4个容器被的管道。

科学 6

 

那,最终便形成了咱的4容器4线程模型了。

现,着眼于编码实现:

  首先我们用一个能承载这些器皿的载体,这个载体类似于连接池的定义,可以依据局部欲自动生成适应数量之器皿,如果来特殊要求的语句,还足以以容器上切出一个容器管理的当,在线程上切出一个线程管理之照以便让实时监察以及调度。如果确要是举行如此一个系,那么
容器的调度 和 线程的调度功能
是必不可少的,而本Demo则是做到了重要功用,像容器与线程在代码中本人耶从未退开来,算法为是一直写很的,实际设计中,对算法的设计尚是异常重大的,还有多线程模型中,怎样上锁才会吃效率最大化为是关键的。

比方这里以案例之直观就一直写很成4只容器。

public static List<Container> ContainerList = new List<Container>(); //容器载体
static Factory()
{
     for (int i = 0; i < 4; i++)
     {
        ContainerList.Add(new Container(i));  //遍历4次  生成4个容器
     }
     foreach (var item in ContainerList)
     {
        item.Run();    //开启线程
     }
}

现,我们而 有编号也 0 到 40 这样的 41独用户。那么这个导流算法
我呢就一直写死,编号0至9底用户
将他们的要让丢转到第一只容器,编号10~19之用户
放到第二只容器,编号20~29拓宽至第三只容器,编号30~40的用户放第四独容器。

那么这代码就是这么的:

 static Container GetContainer(int userId, out int i) //获取容器的算法
 {
     if (0 <= userId && userId < 10)    //编号0至9的用户  返回第一个容器  依次类推
     {
          i = 0;
          return ContainerList[0];
     }
     if (10 <= userId && userId < 20)
     {
          i = 1;
          return ContainerList[1];
     }
     if (20 <= userId && userId < 30)
     {
          i = 2;
          return ContainerList[2];
      }
      i = 3;
      return ContainerList[3];
  }

当我们的对话请求经过算法的导流之后,都须调用一个主意,用于辨别管道数量。如果管道数量已经高于10,则要失败,否则成功

  public static void Add(int userId)
  {
       if (GetContainer(userId, out int i).Add(userId))
            Console.WriteLine("容器" + i + " 用户" + userId + "  发起请求");
       else
            Console.WriteLine("容器" + i + " 用户" + userId + "  被拦截");
  }

搭下就容器Container的代码了。

这边,对容器的选型用线程安全的ConcurrentDictionary类。
  线程安全:当多只线程同时读写及一个共享元素的当儿,就会见冒出数量错乱,迭代报错等安全问提
  ConcurrentDictionary:除了GetOrAdd方法要慎用外,是.Net4.0把持为缓解Dictionary线程安全要来之初类型
  ReaderWriterLockSlim:较ReaderWriterLock优化的朗诵写锁,多个线程同时做客读锁
或  一个线程访问写锁

private ReaderWriterLockSlim obj = new ReaderWriterLockSlim();  //在每个容器中申明一个读写锁
public ConcurrentDictionary<string, ConcurrentList<DateTime>> dic = new ConcurrentDictionary<string, ConcurrentList<DateTime>>(); //创建该容器 dic

接下来当您向容器上加相同条管道中之多寡是通过这方式:

 public bool Add(int userId)
 {
     obj.EnterReadLock();//挂读锁,允许多个线程同时写入该方法
     try
     {
         ConcurrentList<DateTime> dtList = dic.GetOrAdd(userId.ToString(),t=>{ new ConcurrentList<DateTime>()}); //如果不存在就新建 ConcurrentList
         return dtList.CounterAdd(10, DateTime.Now); //管道容量10,当临界管道容量后 返回false
     }
     finally
     {
         obj.ExitReadLock();
     }
 }

 这里,为了以后的线程遍历删除ConcurrentList的管道的早晚保证ConcurrentList的安全性,所以这边设加读锁。

 而ConcurrentList,因为.Net没有推出List集合类的线程安全(这里自己说明下:之所以不用ConcurrentBag是为一旦保管count和add的一致性,这里补充一下),所以自己新建了一个继承给List<T>的安康路,在此处
封装了3独需要运用的办法。

public class ConcurrentList<T> : List<T>
{
    private object obj = new object();

    public bool CounterAdd(int num, T value)
    {
        lock (obj)
        {
            if (base.Count >= num)
                return false;
            else
                base.Add(value);
            return true;
        }
    }
    public new bool Remove(T value)
    {
        lock (obj)
        {
            base.Remove(value);
            return true;
        }
    }
    public new T[] ToArray() 
    {
        lock (obj)
        {
            return base.ToArray();
        }
    }
}

终极就是是线程的周转方式:

 public void Run()
 {
     ThreadPool.QueueUserWorkItem(c =>
     {
         while (true)
         {
             if (dic.Count > 0)
             {
                 foreach (var item in dic.ToArray())
                 {
                     ConcurrentList<DateTime> list = item.Value;
                     foreach (DateTime dt in list.ToArray())   
                     {
                         if (DateTime.Now.AddSeconds(-3) > dt)
                         {
                             list.Remove(dt);
                             Console.WriteLine("容器" + seat + " 已删除用户" + item.Key + "管道中的一条数据");
                         }
                     }
                     if (list.Count == 0)
                     {
                         obj.EnterWriteLock();
                         try
                         {
                             if (list.Count == 0)
                             {
                                 if (dic.TryRemove(item.Key, out ConcurrentList<DateTime> i))
                                 { Console.WriteLine("容器" + seat + " 已清除用户" + item.Key + "的List管道"); }
                             }
                         }
                         finally
                         {
                             obj.ExitWriteLock();
                         }
                     }
                 }

             }
             else
             {
                 Thread.Sleep(100);
             }
         }
     }
   );
 }

末了,是意义图,一个凡根据控制台的,还一个凡是因Signalr的。

 科学 7科学 8

2.3.4 布尔型

一个布尔型的值才发生少种植:true和false。布尔值不会见隐式转换为数值0或者1。布尔值可以和&&、||操作符结合,并且可能会见出梗塞行为。如果运算符左边已经足以确定整个布尔表达式的价,那么右边的表达式将不再求值。

var s string

//s = "mazhiguo"

if s != "" && s[0] == ‘m’ {

   fmt.Println("OK")

else {

   fmt.Println("error")

}

 

2.3.2 浮点数

浮点数用于表示包含小数触及之数据。Go语言提供了片栽精度的浮点数,float32和float64。float32与float64之间需要强制转换。强制转换的方式T(V),T为而转换的对象项目,V需要换的变量。

1 浮点数表示

var f1 float32

f1 = 10

f2 := 12.0 //带小数点的自动推导为float64

f2 = float64(f1) //需强制转换

 

2 浮点数比较

为浮点数不是均等种标准的表达方式,所以不克像整型那样直接用==比较。推荐的办法如下,引入math包,计算两只数值的异之绝对值,如果这个结果大小,我们便以为就半单数值是当的。至于此累略至啊水平定义也当,程序员可以因项目求协调定义。

import  "math"

func IsEqual(f1, f2, p float64) bool {

    return math.Abs(f1-f2) < p

}

 

3 科学计数法

管一个频繁表示成a(1≤a<10,n为整数)与10的幂相乘的形式,这种记数法叫做科学记数法。例如:1990=1.99×10^3。计算器或者微机表达10之遮盖是一般是用E或e,也不怕是1.99E3=1990。

f1 := 1.99e+3   //1990

f2 := 1.99e-3   //0.00199

 

今天,因为种种因素,你要对一个要或措施进行频率高达之访问限制。
准,
你对外提供了一个API接口,注册用户每秒钟最多好调用100蹩脚,非注册用户每秒钟最多可调用10坏。
按,
有一个充分吃服务器资源的措施,在平等时刻不可知过10私调用这个主意,否则服务器满载。
遵照, 有一些与众不同的页面,访客并无克数的拜访还是发言。
按, 秒杀活动相当进行。

,防范DDOS,当上一定频率后调用脚本iis服务器ip黑名单,防火墙黑名单。
比方齐种的比方,也就是说,如何自一个断面的角度对调用的法子开展频率高达之限量。而对效率限制,服务器层面还起极直白的缓解方式,现在自己说之虽是代码层面上之频率管控。

2.4.3勤组指针和指针数组

数组指针是只是一个指南针变量保存的是累累组的地点。指针数组,是指数组的每个元素都是依靠针类型。

package main

 

import "fmt"

 

func main() {

 

    var ptr *[3]int //数组指针

    arr := [3]int{1, 2, 3}

    ptr = &arr //保存了数组的地址

    fmt.Println(*ptr)

 

    var ptr2 [3]*int //指针数组,每一个元素都是指针

    a, b, c := 10, 20, 30

    ptr2[0] = &a

    ptr2[1] = &b

    ptr2[2] = &c

    fmt.Println(ptr2) //ptr2数组中的3个指针分别保存了a,b,c的地址

 

}

// 打印结果:

[1 2 3]

[0xc04200a2c8 0xc04200a2e0 0xc04200a2e8]

 

 

分布式下Redis

上面介绍了平等种频率限制的模子,分布式与单机相比,无非就是是载体不同,我们若拿此容器的载体从程序及移植出来,来打成一个单独的劳务或直接借用Redis也是行之。

这边就介绍分布式情况下,Redis的实现。

不等让Asp.Net的多线程模型,大概为Redis的各种类型的要素非常粒度的操作导致各种加锁之错综复杂,所以于网络要处理这块Redis是单线程的,基于Redis的实现则为单线程的缘故在编码角度不用太多着想到和逻辑无关之问题。

  简单介绍下,Redis是一个内存数据库,这个数据库属于非关系型数据库,它的概念不同于一般的我们体会的Mysql
Oracle
SqlServer关系型数据库,它从未Sql没有字段名尚未表名这些概念,它同HttpRunTime.Cache的定义差不多一样,首先从操作及属于键值对模式,就使
Cache[“键名”]
这样便会收获到价值类似,而且得对每个Key设置过策略,而Redis中的Key所对应之价值并无是怀念存啥就存啥的,它支持五种多少列:string(字符串),hash(哈希),list(列表),set(集合)及sorted
set(有序聚集)。

今只要说的凡Sorted
set有序聚集,有序聚集相比其他的汇聚类型的特殊点在于,使用有序聚集的上还能给插入的素指定一个
积分score,我们管此积分score理解为扫除序列,它里面会针对积分进行排序,积分允许再,而不变聚集中的素虽然是唯一。

  还是一样的笔触,每当有用户访问的早晚,都针对拖欠用户之
管道(有序聚集)中上加一个因素,然后设置该因素的积分也当前工夫。接着以次中初步个线程,来针对管道被积分小于约定时间之素进行清理。因为规定有序聚集中的素只能是唯一值,所以在赋值方面使是满足uuid即可。

 科学 9

那用Redis来实现之代码那就是是相仿这种:

科学 10

通过using语法糖实现IDisposable而卷入的Redis分布式锁,然后里面正常的逻辑判断。

然的代码虽然也能够形成功能,但切莫足够好。Redis是个依据内存的数据库,于性能而言,瓶颈在于网络
IO 上,与Get一差发生同样次呼吁相比,能不能够经过一致段落脚本来实现多数逻辑吗?

有的,Redis支持 Lua脚本:
  Lua
是如出一辙栽轻量小巧的脚本语言,用标准C语言编写并为自代码形式开放,
其设计目的是以放置应用程序中,从而为应用程序提供灵活的扩展以及定制功能。
  大致意思就是是,直接通往Redis发送一截脚本或者给其直接本地读取一截脚本从而一直促成有的逻辑。

/// <summary>
/// 如果 大于10(AccountNum) 就返回1   否则就增加一条集合中的元素 并返回 空
/// </summary>
/// <param name="zcardKey"></param>
/// <param name="score"></param>
/// <param name="zcardValue"></param>
/// <param name="AccountNum"></param>
/// <returns></returns>
public string LuaAddAccoundSorted(string zcardKey, double score, string zcardValue, int AccountNum)
{
    string str = "local uu = redis.call('zcard',@zcardKey) if (uu >=tonumber(@AccountNum)) then return 1 else redis.call('zadd',@zcardKey,@score,@zcardValue)  end";
    var re = _instance.GetDatabase(_num).ScriptEvaluate(LuaScript.Prepare(str), new { zcardKey = zcardKey, score = score, zcardValue = zcardValue, AccountNum=AccountNum });
    return re.ToString();
}

local
uu就是发明一个乎名uu的变量的意,redis.call就是redis命令,这段脚本意思就是是要
大于10(AccountNum) 就回1   否则就大增一长集合中之要素 并回到 空。

管道内元素处理的办法就是是:

 /// <summary>
 /// 遍历当前所有前缀的有序集合,如果数量为0,那么就返回1 否则 就删除 满足最大分值条件区间的元素,如果该集合个数为0则消失
 /// </summary>
 /// <param name="zcardPrefix"></param>
 /// <param name="score"></param>
 /// <returns></returns>
public string LuaForeachRemove(string zcardPrefix, double score)
 {
     StringBuilder str = new StringBuilder();
     str.Append("local uu = redis.call('keys',@zcardPrefix) "); //声明一个变量 去获取 模糊查询的结果集合
     str.Append("if(#uu==0) then");    //如果集合长度=0
     str.Append("   return 1 ");
     str.Append("else ");
     str.Append("   for i=1,#uu do ");   //遍历
     str.Append("       redis.call('ZREMRANGEBYSCORE',uu[i],0,@score) ");  //删除从0 到 该score 积分区间的元素
     str.Append("       if(redis.call('zcard',uu[i])==0) then ");  //如果管道长度=0
     str.Append("           redis.call('del',uu[i]) ");   //删除
     str.Append("       end ");
     str.Append("   end ");
     str.Append("end ");
     var re = _instance.GetDatabase(_num).ScriptEvaluate(LuaScript.Prepare(str.ToString()), new { zcardPrefix = zcardPrefix + "*", score = score });
     return re.ToString();

立马2截代码通过发送Lua脚本的形式来就了全经过,因为Redis的纱型原因,所以管LuaForeachRemove方法吃取出来开个劳务来单独处理即可。至于那种多容器多线程的落实,则完全可起来多独Redis的实例来促成。最后放上效果图。

科学 11

说到底,我拿这些还吃做成了单Demo。但是没找到确切的上传网盘,所以大家好留给邮箱(留了就算犯),或者直接加QQ群文件自取,讨论交流:166843154

 

我爱跟自同一的人数交朋友,不让环境影响,自己是好的良师,欢迎加群
.Net web交流群, QQ群:166843154 欲望和挣扎

 

作者:小曾
出处:http://www.cnblogs.com/1996V/p/8127576.html 欢迎转载,但任何转载必须保留完整文章及博客园出处,在显要地方显示署名以及原文链接。
.Net交流群, QQ群:166843154 欲望与挣扎 

2.2 常量

每当Go语言中,常量是凭借编译期间就已经知且不可更改的价值。常量的暧昧类型且是基础项目,包括整型、浮点型、复数型、布尔型及字符串类型等。

坐第一个API接口需求呢例,先说生单机环境下的兑现。
论惯性思维,我们当然会想到缓存的晚点策略这种艺术,但是严格来讲就是HttpRuntime.Cache而言,通过缓存的晚点策略来针对要进行频率的出现控制是不合适的。
  HttpRuntime.Cache
是应用程序级别之Asp.Net的休息存技术,通过者技术可以说明多单缓存对象,可以为每个对象设置过时,当过工夫到达晚该缓存对象就是见面磨(也就是当您拜访该目标的时候吗Null)

2.3.3 复数

Go语言提供了少数种精度的复数类型:complex64跟complex128,分别指向应float32和float64两种浮点数精度。内建函数和当之修道。

x := complex(1, 2) //内建函数

y := 1 + 2i     //自然书写

// real返回实部,imag返回虚部

fmt.Println(x, y, real(x), imag(x), real(y), imag(y))

打印结果:(1+2i) (1+2i) 1 2 1 2

2.5 type定义类型

每当外程序中都见面有有变量有着一样的内部结构,但是却表示完全不同之概念。
一个类型声明语句创建了一个初的项目名称,和现有项目有相同之底部结构。

type 类型名字 底层类型

例如:

type Age int    //年龄

type Height int //身高

type Grade int  //分数

type绝不只是针对诺给C/C++中的typedef,它不是用以定义一多元之别名。更要之凡,它定义了同样文山会海互不相干的作为特征:通过这些互不相干的表现特征,本质上一样之物表现出不同事物之特点:整数还是平头,但年龄也不是惊人为未是分。我们好分级吗Age、Height、Grade定
义出下列不同的行(表示也方式还是函数):

type Age int    //年龄

type Height int //身高

type Grade int  //分数

 

func (a Age) IsOld() bool {

// 超过50岁算老年

return a > 50

}

func (h Height ) NeedTicket() bool {

// 高于120cm需要买票

return h > 120

}

func (g Grade) Pass() bool {

// 60分及格

return g >= 60

}

项目声明语句一般出现于保证一级,因此若新创建的类型名字首配母大写,则以包外可以使用。对于每一个类型T,都生一个相应的类型转换操作T(x),用于将x转换为T类型。

package main

 

import (

    "fmt"

)

 

type Integer int

 

func (a Integer) Less(b Integer) bool {

    return a < b

}

func main() {

    var a Integer = 1

    fmt.Println(a.Less(2))

    var b int

    //不能直接赋值,需要T(x)类型转换

    b = a

    b = int(a)

    fmt.Println(b)

}

 

2.4.2 声明指针变量

声明指针变量的一般式如下:

var 变量名 *类型

例如:

var ip *int     //指向int类型的变量

var fp *float32 //指向float32类型的变量

var ptr [MAX]*int;//指向数组的指针

 

指南针操作注意事项:

  1. 默认值 nil,没有 NULL 常量。
  2. 操作符 “&” 取变量地址, “*”
    通过了指针访问目标靶。
    未⽀持指针运算,不⽀持
    “->” 运算符,直接⽤ “.” 访问目标成员
  3. 匪可知针对指针做加减法等运算
  4. 莫在函数的指针

package main

 

import "fmt"

 

type Student struct {

    Name string

    Age  int

}

 

func main() {

    a := 10

    b := 12.5

    var pa *int = &a

    var pb *float64 = &b

    //1 打印变量的值

    fmt.Printf("%v,%v\n", a, b)

    fmt.Printf("%v,%v\n", *pa, *pb)

    // 2 打印变量的地址

    fmt.Printf("%v,%v\n", &a, &b)

    fmt.Printf("%v,%v\n", pa, pb)

    // 3 指针默认值为nil

    var pc *int

    fmt.Printf("%v,\n", pc)

    // 4 通过指针访问对象成员

    ps := &Student{"张三", 18}

    fmt.Println(ps.Name, ps.Age)

}

章由作者马志国以博客园的原创,若转载请给大庭广众处于标记出处:http://www.cnblogs.com/mazg/

2.3.5 字符串

1 字符串常用操作

在Go语言科学中字符串也是均等栽基本型。一个字符串是一个不可变更之字节序列。常用的字符串操作如下表所示:

运算

含义

备注

s1+s2

字符串连接

 

len(s)

字符串长度

字符串中的字节数,不是字符数

s[i]

取字符

索引i不能越界

s[i:j]

取子字符串

左闭右开,包含s[i],不包含s[j]。子字符串是一个新的字符串。

i,j都可能被忽略,忽略时,从0开始,最后一个字符结束。

 

s := "hello " + "world"

fmt.Println(len(s))// 11

fmt.Println(s[0], s[len(s)-1])//104 100 (h 和 d)

fmt.Println(s[1:4])//"ell"

fmt.Println(s[:5])//"hello"

fmt.Println(s[6:])//"world"

fmt.Println(s[:])//"hello world"

2 字符串值不可变

字符串的值是不可变的:一个字符串包含的字节序列永远不见面为移,当然我们得于一个字符串变量分配一个新字符串值。

s := "hello world"

s[0] = "H"  //这是错误演示,字符串序列不能修改

s = "Hello" //给字符串变量s重新赋值

3 字符串遍历

字符串遍历支持因字节的法门遍历和为字符的章程遍历。

s := "hello 世界"

n := len(s)

//以字节的方式遍历

for i := 0; i < n; i++ {

  fmt.Println(i, s[i])

}

//以字符的方式遍历

for i, ch := range s {

  fmt.Println(i, ch)

}

 

打印结果:

0 104

1 101

2 108

3 108

4 111

5 32

6 228

7 184

8 150

9 231

10 149

11 140

0 104

1 101

2 108

3 108

4 111

5 32

6 19990

9 30028

 

4转义序列

每当一个双引号包含的字符串字面值中,可以据此反斜杠\开的转义序列插入任意的数。

泛的ASCII控制代码的转义方式:

 

 

 

\a

响铃

\b

退格

\f

换页

\n

换行

\r

回车

\t

水平制表符

\v

垂直制表符

\’

单引号

\”

双引号

\\

反斜杠

 

 

5原生字符串字面值

原生的字符串字面值,用` `替双引号。可用以编写正则表达式。常用来HTML模板、JSON面值、命令提示信息以及要扩大及多行的面貌。

tips := `请按要求执行以下操作:

 1 输入参数

 2 计算

 3 打印结果`

fmt.Println(tips)

 

6 UTF8编码

UTF8编码是一致栽字符编码,使用1至4独字节表示一个字符。ASCII部分字符只以1只字节,常因此字符部分以2还是3单字节。变长的编码无法直接通过查找引来访问第n单字符。

Go语言的源文件采用UTF8编码,unicode/utf8担保供提供了用于rune字符序列的UTF8编码和解码功能。如果关注每个unicode字符,可以运用UTF8解码器。unicode/utf8包括提供了拖欠意义。

s := "hello 世界"

fmt.Println(len(s))    //12

fmt.Println(utf8.RuneCountInString(s))//8

将一个整数型转换为字符串意思是挺成因为单含对承诺unicode编码字符的UFT8配符串,如果对应的编码的字符无效,将为此‘\uFfFD’无效字符作为替换:

fmt.Println(string(65))      //"A"

fmt.Println(string(0x4eac))  //"京"

fmt.Println(string(12345678)) //无效字符

 

string 接受到[]rune的类型转换,可以以一个UTF8编码的字符串解码为unicode字符串序列:

s := "世界"

fmt.Printf("%x\n", s) //e4b896e7958c,utf8编码

r := []rune(s)

fmt.Printf("%x\n", r) //[4e16 754c],unicode编码

 

 

比如”汉”字的Unicode编码是6C49。6C49在0800-FFFF之间,所以只要为此3字节模板:1110xxxx 10xxxxxx 10xxxxxx。将6C49写成二进制是:0110 1100 0100 1001,将这个比特流按三字节模板的分层方法分为0110 110001 001001,依次代替模板被的x,得到:1110-0110 10-110001 10-001001,即E6 B1 89,这虽是那个UTF8的编码。

s := "汉"

fmt.Printf(" %x\n", s) // e6b189,UTF8编码

r := []rune(s)

fmt.Printf("%x\n", r)  //[6c49],unicode编码

unicode相当于字符编码,即字典。utf8、uft16凡是如何以字节的法囤这个编码。字符串可正如、可遍历、不可修改。

2.1.3 赋值

1 简单赋值

赋值语句是翻新一个变量的值,最简便易行的赋值”变量名= 新值的表达式”

var i int

i = 1      //简单赋值

 

2复合赋值运算符

特定的次初次算术运算符和赋值语句的复合操作有一个简短之形式

var i int

i = i + 1

i += 1    //与i = i + 1等价

 

数值变量也可支撑++递增和–递减语句。注意其是唇舌,不是表达式,所以x=i++这样的表达式是一无是处的。

3基本上还赋值

大多重复赋值允许而更新多只变量的价值。在赋值之前,赋值语句右边的具有表达式会先进行求值,然后再统一更新左边对应的变量的价值。这样于召开简单独数值交换时,不待引入第三单变量了。

x,y=y,x

4 _标识符

稍加表达式会出多只价,例如调用一个发生差不多单返回值的函数。可以动用下划线空白标识符_来废弃弃不欲的值。

_, err := io.Copy(dst, src)//丢弃字节数

 

2.1 变量

变量是本着同样片内存空间的命名,程序可以经过定义一个变量来报名一片内存空间。然后可以经过引用变量名来使用这块存储空间。

 科学 12

2.1.2 变量声明

1 一般宣称格式

用var可以创造一个一定类型的变量。变量声明的形似语法如下:

var 变量名 类型 =  表达式

里头“类型”或“=表达式”两单部分好大概其中的一个。如果简单的凡类型信息,那么将基于初始化表达式来演绎变量的类别。如果表达式省略,将故零值初始化该变量。

var i int = 1

var j int   //j的值初始化为0

var k = 10  // k的类型自动推导

要是一个名字在函数内部定义,那么她不得不于函数内部用,如果当函数外部定义,那么在眼前确保的具有文件都可拜。名字首假名的深浅写决定了它在包外的可见性,首字母大写于包外可以拜。包本身的名一般总是用小写字母。

在保险级别声明的变量会以main入口函数执行前形成初始化,局部变量将当宣称语句为实践的下做到初始化。如果无显示初始化,那么用为此零值初始化该变量。一个变量声明后没有使用呢会惹编译错误。

2 简短变量声明

“名字 := 表达式”,变量的花色因表达式自动推导。由于用简易利落,被大用于大部分之一对变量的声明与初始化。注意,简短变量声明非能够用于包级别之变量声明。

i := 100

 

3 多只变量声明

var i, j, k int = 1, 2, 3

var m, n int

var a, b, c = 1, 2, 3

d, e, f := 1, 2, 3

name, age := "张三", 20

为可这样勾画:

var (

     name string

     age  int

)

 

2.7 数据输入输出

2.3 数据类型

Go语言将数据类型分为四类:基础项目、复合类型、引用类型和接口类型。

  1. 中心数据类:数价、字符串和布尔型。
  2. 复合数据类型:数组和结构体。
  3. 引用类型:指针、切片、字典、函数和通道。
  4. 接口类型。