Coder Social home page Coder Social logo

tao's Introduction

Tao

Light-weight TCP Asynchronous gOlang framework 轻量级TCP异步框架,Go语言实现 1.6.0

GitHub stars GitHub forks GitHub license GoDoc

Requirements

  • Golang 1.9 and above

Installation

go get -u -v github.com/leesper/tao

Usage

A Chat Server Example in 50 Lines

package main

import (
	"fmt"
	"net"

	"github.com/leesper/holmes"
	"github.com/leesper/tao"
	"github.com/leesper/tao/examples/chat"
)

// ChatServer is the chatting server.
type ChatServer struct {
	*tao.Server
}

// NewChatServer returns a ChatServer.
func NewChatServer() *ChatServer {
	onConnectOption := tao.OnConnectOption(func(conn tao.WriteCloser) bool {
		holmes.Infoln("on connect")
		return true
	})
	onErrorOption := tao.OnErrorOption(func(conn tao.WriteCloser) {
		holmes.Infoln("on error")
	})
	onCloseOption := tao.OnCloseOption(func(conn tao.WriteCloser) {
		holmes.Infoln("close chat client")
	})
	return &ChatServer{
		tao.NewServer(onConnectOption, onErrorOption, onCloseOption),
	}
}

func main() {
	defer holmes.Start().Stop()

	tao.Register(chat.ChatMessage, chat.DeserializeMessage, chat.ProcessMessage)

	l, err := net.Listen("tcp", fmt.Sprintf("%s:%d", "0.0.0.0", 12345))
	if err != nil {
		holmes.Fatalln("listen error", err)
	}
	chatServer := NewChatServer()
	err = chatServer.Start(l)
	if err != nil {
		holmes.Fatalln("start error", err)
	}
}

Changelog

v1.6.0

  1. Bugfix: writeLoop() drains all pending messages before exit;
    writeLoop()函数退出前将所有的网络数据包发送完毕;
  2. Renaming getter methods according to Effective Go;
    根据Effective Go重命名getter方法;
  3. Bugfix: timer task expired forever due to system clock affected by NTP;
    修复因为受NTP协议校正系统时钟偏差的影响,导致定时任务永远过期的bug;
  4. Bugfix: asyncWrite() do not return error if called after ServerConn or ClientConn closes;
    修复网络连接关闭后调用asyncWrite()不返回错误的bug;
  5. Providing WorkerSizeOption() for tuning the size of worker go-routine pool;
    提供WorkerSizeOption()来调节工作者线程池大小;
  6. Providing BufferSizeOption() for tuning the size of buffered channel;
    提供BufferSizeOption()来调节缓冲通道大小;
  7. Providing ReconnectOption() for activating ClientConn's reconnecting mechanism;
    提供ReconnectOption()来启动ClientConn的断线重连机制;
  8. Providing CustomCodecOption() for setting self-defined codec;
    提供CustomCodecOption() 来设置自定义编解码器;
  9. Providing TLSCredsOption() for running a TLS server;
    提供TLSCredsOption()来运行TLS服务器;
  10. Providing OnConnectOption(), OnMessageOption(), OnCloseOption() and OnErrorOption() for setting callbacks of the four situations respectively;
    提供OnConnectOption(), OnMessageOption(), OnCloseOption() 和 OnErrorOption()来设置四种情况下的回调函数;
  11. Use the standard sync.Map instead of map guarded by rwmutex;
    使用标准库中的sync.Map替换使用rwmutex保护的map;

v1.5.0

  1. A Golang-style redesigning of the overall framework, a reduce about 500+ lines of codes;
    按照Go语言风格重新设计的整体框架,精简500多行代码;
  2. Providing new Server, ClientConn and ServerConn struct and a WriteCloser interface;
    提供Server,ClientConn和ServerConn三种新结构和WriteCloser新接口;
  3. Using standard context package to manage and spread request-scoped data acrossing go-routines;
    使用标准库中的context包在多个Go线程中管理和传播与请求有关的数据;
  4. Graceful stopping, all go-routines are related by context, and they will be noticed and exit when server stops or connection closes;
    优雅停机,所有的Go线程都通过上下文进行关联,当服务器停机或连接关闭时它们都会收到通知并执行退出;
  5. Providing new type HandlerFunc func(context.Context, WriteCloser) for defining message handlers;
    提供新的HandlerFunc类型来定义消息处理器;
  6. Developers can now use NewContextWithMessage() and MessageFromContext() to put and get message they are about to use in handler function's context, this also leads to a more clarified design;
    开发者现在可以通过NewContextWithMessage()和MessageFromContext()函数来在上下文中存取他们将在处理器函数中使用的消息,这样的设计更简洁;
  7. Go-routine functions readLoop(), writeLoop() and handleLoop() are all optimized to serve both ServerConn and ClientConn, serveral dead-lock bugs such as blocking on channels are fixed;
    优化Go线程函数readLoop(),writeLoop()和handleLoop()使得它们能同时为ServerConn和ClientConn服务,修复了多个“通道阻塞”的死锁问题;
  8. Reconnecting mechanism of ClientConn is redesigned and optimized;
    重新设计和优化ClientConn的断线重连机制;

v1.4.0

  1. bugfix:TLS重连失败问题;
    bugfix: failed to reconnect the TLS connection;
  2. bugfix:ConnectionMap死锁问题;
    bugfix: ConnectionMap dead-lock problem;
  3. 优化TCP网络连接的关闭过程;
    Optimize the closing process of TCP connection;
  4. 优化服务器的关闭过程;
    Optimize the closing process of server;
  5. 更优雅的消息处理注册接口;
    More elegant message handler register interface;

v1.3.0

  1. bugfix:修复断线重连状态不一致问题;
    bugfix: fixed inconsistent status caused by reconnecting;
  2. bugfix:修复ServerConnection和TimingWheel在连接关闭时并发访问导致崩溃问题;
    bugfix: fixed a corruption caused by concurrent accessing between ServerConnection and TimingWheel during connection closing;
  3. 无锁且线程安全的TimingWheel,优化CPU占用率;
    Lock-free and thread-safe TimingWheel, optimized occupancy rate;
  4. bugfix:修复TLS配置文件读取函数;
    bugfix: Fixed errors when loading TLS config;
  5. 添加消息相关的Context结构;简化消息注册机制,直接注册处理函数到HandlerMap;
    A message-related Context struct added; Register handler functions in HandlerMap directly to simplify message registration mechanism;
  6. 合并NewClientConnection()和NewTLSClientConnection(),提供一致的API;
    Combine NewTLSConnection() into NewClientConnection(), providing a consistent API;
  7. 工作者线程池改造成单例模式;
    Make WorkerPool a singleton pattern;
  8. 使用Holmes日志库代替glog;
    Using Holmes logging package instead of glog;
  9. 添加metrics.go:基于expvar标准包导出服务器关键信息;
    Add metrics.go: exporting critical server information based on expvar standard pacakge;
  10. 编写中文版框架设计原理文档,英文版正在翻译中;
    A document about framework designing principles in Chinese, English version under developed;

v1.2.0

  1. 更优雅的消息注册接口;
    More elegant message register interface;
  2. TCPConnection的断线重连机制;
    TCPConnection reconnecting upon closing;
  3. bugfix:协议未注册时不关闭客户端连接;
    bugfix: Don't close client when messages not registered;
  4. bugfix:在readLoop()协程中处理心跳时间戳更新;
    bugfix: Updating heart-beat timestamp in readLoop() go-routine;
  5. bugfix:Message接口使用Serialize()替代之前的MarshalBinary(),以免框架使用者使用gob.Encoder/Decoder的时候栈溢出;
    bugfix: Use Serialize() instead of MarshalBinary() in Message interface, preventing stack overflows when framework users use gob.Encoder/Decoder;
  6. bugfix:当应用层数据长度大于0时才对其进行序列化;
    bugfix: Serialize application data when its length greater than 0;
  7. 新API:SetCodec(),允许TCPConnection自定义编解码器;
    New API: SetCodec() allowing TCPConnection defines its own codec;
  8. 新API:SetDBInitializer(),允许框架使用者定义数据访问接口;
    New API: SetDBInitializer() allowing framework users define data access interface;
  9. 允许框架使用者在TCPConnection上设置自定义数据;
    Allowing framework users setting custom data on TCPConnection;
  10. 为新客户端连接的启动单独开辟一个对应的go协程;
    Allocating a corresponding go-routine for newly-connected clients respectively;
  11. bugfix:写事件循环在连接关闭时将信道中的数据全部发送出去;
    bugfix: writeLoop() flushes all packets left in channel when performing closing;
  12. bugfix:服务器和客户端连接等待所有go协程关闭后再退出;
    bugfix: Servers and client connections wait for the exits of all go-routines before shutting down;
  13. 重构Server和Connection,采用针对接口编程的设计;
    Refactoring Server and Connection, adopting a programming-by-interface design;
  14. 设置500毫秒读超时,防止readLoop()发生阻塞;
    Setting 500ms read-timeout prevents readLoop() from blocking;

v1.1.0

  1. 添加注释,提高代码可读性;
    Add comments, make it more readable;
  2. 限制服务器的最大并发连接数(默认1000);
    Server max connections limit (default to 1000);
  3. 新API:NewTLSTCPServer() 创建传输层安全的TCP服务器;
    New API: NewTLSTCPServer() for creating TLS-supported TCP server;
  4. 新特性:SetOnScheduleCallback() 由框架使用者来定义计划任务(比如心跳);
    New Feature: SetOnScheduleCallback() make scheduled task managed by framwork users(such as heart beat);
  5. 新特性:支持默认的消息编解码器TypeLengthValueCodec,并允许框架使用者开发自定义编解码器;
    Support TypeLengthValueCodec by default, while allowing framework users develop their own codecs;

v1.0.0

  1. 完全异步的读,写以及消息处理;
    Completely asynchronous reading, writing and message handling;
  2. 工作者协程池;
    Worker go-routine pool;
  3. 并发数据结构和原子数据类型;
    Concurrent data structure and atomic data types;
  4. 毫秒精度的定时器功能;
    Millisecond-precision timer function;
  5. 传输层安全支持;
    Transport layer security support;
  6. 应用层心跳协议;
    Application-level heart-beating protocol;

More Documentation

  1. Tao - Go语言实现的TCP网络编程框架
  2. English(TBD)

tao's People

Contributors

leesper avatar tyler-chang avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

tao's Issues

散列分配任务给worker不均匀

散列逻辑只会将任务平均分配到固定的几个worker中去执行,很多worker都是闲置的
写了一个demo,大概意思是将10000个任务分配到50个worker当中
m := make(map[uint32]uint32, 0)
for i := 0; i < 10000; i++ {
var a = uint64(i)
aa := &a
b := (*[4]byte)(unsafe.Pointer(aa))
// fmt.Println(*b)
h := fnv.New32a()
h.Write((*b)[:])
code := h.Sum32()
// fmt.Println(code)
workID := code & uint32(50)
// fmt.Println(workID)
m[workID]++
}
for k, v := range m {
fmt.Printf("k=%v,v=%v\n", k, v)
}
实际效果是50个worker只用到了其中的8个,其他42个都是闲置的
k=48,v=1251
k=18,v=1250
k=16,v=1249
k=0,v=1249
k=50,v=1250
k=34,v=1250
k=32,v=1251
k=2,v=1250
demo中的代码是根据tao框架的散列逻辑写的,demo很简单不知道有没有漏掉什么,如果没有,那这个worker分配就有问题了。

Tao框架写是server端报错?请问这是什么情况?

用的花生壳,连接电脑连的是我的本机

测试方法一、通过串口服务器工具测试服务器端可以接收数据
15:42:53 收到数据:~~~~64888672247000001590041095500000

测试方法二、自己从网上找的程序server 也没问题

测试方法三、自己写的client端用tao框架写的server端也是可以收到数据

测试方法四、只用tao框架写server端接收不到数据直接报2017/07/19 15:20:09 ERROR [tao.TypeLengthValueCodec.Decode] (message.go:197) - message(type 2122219134) has bytes(943207478) beyond max 8388608
2017/07/19 15:20:09 ERROR [tao.readLoop] (conn.go:604) - error decoding message more than 8M data

tao编译

兄弟,你这个tao项目要怎么编译运行起来啊,我好像没有看到编译步骤介绍

每个连接创建3个goroutine有个疑问点

传统的reactor在收包时往往是异步的,它内部实现可能是线程池,或者系统底层信号处理,所以它的线程其实是复用的,也就是说一般不会产生特别多的线程,但是Tao中每个连接创建3个goroutine,而且都是阻塞的,一般服务器同时万数量级连接应该很正常,此时可能就需要3*connCount 量级的goroutine,没有研读过goroutine源码,但大致了解内部应该也是线程池处理,此时每个goroutine都是阻塞的,那么就是所有连接相关的goroutine都得不到回收,那么万数量级的线程切换给cpu带来的性能消耗肯定是低效的,不知道这块是设计上的失误还是我理解不透彻?

MessageHandler与SetOnMessageCallback有何不同?

从这段代码看,像是如果没有注册Handler就调用OnMessageCallback(),否则就调用Handler。
那么为这两者有何区别呢?分别适用于哪种应用场景?

662         handlerFactory := HandlerMap.Get(msg.MessageNumber())
663         if handlerFactory == nil {
664             if conn.GetOnMessageCallback() != nil {
665                 glog.Infof("readLoop Message %d call onMessage()\n", msg.MessageNumber())
666                 conn.GetOnMessageCallback()(msg, conn)
667             } else {
668                 glog.Infof("readLoop No handler or onMessage() found for message %d", msg.MessageNumber())
669             }
670             continue
671         }
672 
673         // send handler to handleLoop
674         handler := handlerFactory(conn.GetNetId(), msg)
675         if !conn.IsClosed() {
676             conn.GetHandlerReceiveChannel() <- handler
677         }

几个疑点麻烦解答

最近正在学习go网络编程看到你的框架感觉还行,准备应用到项目里面去,以下是我阅读你源码的几个疑点。
1、我看你已经开放了自定义的Decode接口,能否提供一个example供我参考一下。(优先级低些,因为我的通讯协议跟你一样)
2、我的业务里面需要对每个会话定时轮询以处理一些业务,比如定时检查是否需要主动向某个会话推送内容。能否给个例子。
3、长连接关闭问题,我看之前有个issue已经提过这个问题了,但是我看你给的示例代码有点老了,如下
someServer.SetOnScheduleCallback(40 * time.Second, func(now time.Time, data interface{}) {
cli := data.(tao.Connection)
lastTimestamp := cli.GetHeartBeat()
session, ok := cli.GetExtraData().(common.Session)
if ok {
timeout = session.TimeOut
}
if now.UnixNano() - lastTimestamp > int64(timeout) * 1e9 {
holmes.Warn("Client %d timeout, close it\n", cli.GetNetId())
cli.Close()
}
})
后面SetOnScheduleCallback改成Sched这个函数名,我发现代码里面已经没了tao.Connection这个了,这段代码怎么改
tips:
刚刚学习go,可能有点菜,麻烦不吝赐教。

用Go实现异步真的有必要吗?

据我所知,其它语言实现异步的主要原因是其并发性能太差(究其原因是进程、线程都太重了),比如:JS和Java的异步IO,有一些语言开始支持协程(如JS、Python等)——可认为是更加轻量级且自动实现同步锁的线程。之所以开始流行协程,其中之一就是异步太复杂(看看JS,一步一步在朝着同步方向走,JS7标准开始内建协程—— asyncawait),否则大家都用异步得了。

我们公司五年前开发的软件架构,用异步,甚至令人胆寒到一个TCP应答请求居然拆成了两个TCP连接@_@,现在改起来都很难。如果现在谁在这样设计(即把一个TCP请求拆成两个连接),真想一口唾沫淹死他!

而 Go 用 goroutine 实现了协程,但又与原始的协程不同:原始的协程要求协程之间能自动实现同步锁,这样的结果就是不能同一时间利用多核(即并行,非并发)。另外,Go的理念是:不要通过共享内存来通信,而是通过通信来共享内存。因此,Go中的 goroutine 并没有实现同步锁,这由用户自己实现。

在 Go 中,goroutine 是很廉价的,因此并发性很高,使用简单且是同步的。这个时候,在 Go 中再去实现异步真的还有必要吗?现在大家都在往同步方向走(哪怕底层用异步来实现同步),这个项目却是把一个不错的同步变成异步,我怎么觉得有点本末倒置呢。

以上仅是在不知道你的业务需求下的一家之言,勿怪 :)

限速

可以弄个限速的版本吗

服务如何stop

没有优雅的stop方式吗?都是用sh的kill方式吗?如果是这样会不会产生数据不完整的情况?

请教一下“应用层心跳”

在 conn.go 和 message.go 文件中都有heartbeat的设定,它的使用也需要在 Register 函数中注册一下吗,有相应的使用示例吗?

MonitorOn函数还没有实现web展示测量值吗?

func MonitorOn(port int) {
go func() {
if err := http.ListenAndServe(fmt.Sprintf(":%d", port), nil); err != nil {
holmes.Errorln(err)
return
}
}()
}
MonitorOn函数还没有实现web展示测量值吗?我看这里只是开启了一个http服务

some undefined TLS

[root@localhost tao]# go version
go version go1.4.2 linux/amd64
[root@localhost tao]# go get
# github.com/leesper/tao
./server.go:353: undefined: tls.TLS_RSA_WITH_AES_128_GCM_SHA256
./server.go:354: undefined: tls.TLS_RSA_WITH_AES_256_GCM_SHA384
./server.go:364: undefined: tls.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384
./server.go:365: undefined: tls.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384

Client连接异常情况,发包失败

在Client发包里,可否加一个连接正常状态的检查?看了一遍源码,比如一种case在连接刚被各种异常网络情况断开后,发送了message,此时message将会发送失败,wirteLoop会退出。可否在asyncWrite中加上连接是否正常的状态检查,连接不正常,则重新进行Dial连接,但是这样会阻塞asyncWrite的调用。

msgType 取值问题。

我理解是每一组序列化协议就要定义一个ID,例如:项目中有两组序列化协议。一组序列化协议是处理二进制的这时ID=1,同时还有一组协议处理字符串序列化这时ID=2;这是正确的使用方法吧?还有其他最佳实践吗?

Tao框架server端使用注册方法时报错?注册方法的第二个方法没跑进去这是什么原因呢?

他每次发的都很小不会超过42个字节,按道理说他应该可以走到第二个方法那里去。他就是到第二个方法那里执行了差不多一分多钟就开始报超出了8兆是什么原因呢?他不是刚请求就奔溃是过了一分多钟就开始奔溃。

然后我做了如下的测试:

环境:用的花生壳,连接电脑连的是我的本机

测试方法一、通过串口服务器工具测试服务器端可以接收数据
15:42:53 收到数据:~~~~64888672247000001590041095500000

测试方法二、自己从网上找的程序server 也没问题

测试方法三、自己写的client端用tao框架写的server端也是可以收到数据

测试方法四、只用tao框架写server端接收不到数据直接报2017/07/19 15:20:09 ERROR [tao.TypeLengthValueCodec.Decode] (message.go:197) - message(type 2122219134) has bytes(943207478) beyond max 8388608
2017/07/19 15:20:09 ERROR [tao.readLoop] (conn.go:604) - error decoding message more than 8M data

请教一下client的问题

我非常喜欢这个框架,感谢分享。
现在我遇见了这样一个问题:

其实client在获取rpc的结果的时候,其实是一个同步的方式。也就是说发送了,等返回。
但是如果server端有推送的话,那么client应该怎么区分推送和普通的返回结果呢?

而且,其实client的请求和返回并不是严格对应的。譬如乒乓那个例子。如果返回值不只是pang这么一种的话,是否有可能对不上了呢?如果一定严格要求对上的话,有没有什么好办法呢?

谢谢。

添加tao后flag无效

package main

import (
        "flag"
        "fmt"

        //_ "github.com/leesper/holmes"
        _ "github.com/leesper/tao"                 //开启这行后就会失败
)

// run -h
func main() {
        id := flag.Int("id", 1, "test")
        flag.Parse()
        fmt.Println(" id = ", *id)
}

未import tao时正常输出

Usage of ./xx:
  -id int
        test (default 1)

添加后输出

Usage of ./xx:

协议的问题

请问你们在实际在产品里面也是用的 type-length-data 这种方式吗?没有使用校验或者同步之类的字段吗? 如果数据错位的话,会不会一直就取不到正确的数据,虽然tcp是流的方式,有这种可能性的存在吗?

Close()时reconnect()会导致ClientConn的Race问题

==================
WARNING: DATA RACE
Write at 0x00c00018e080 by main goroutine:
github.com/leesper/tao.(*ClientConn).reconnect()
F:/workspace/tao/conn.go:440 +0x36f
github.com/leesper/tao.(*ClientConn).Close.func1()
F:/workspace/tao/conn.go:418 +0x3f0
sync.(*Once).doSlow()
C:/Program Files/Go/src/sync/once.go:68 +0x127
sync.(*Once).Do()
C:/Program Files/Go/src/sync/once.go:59 +0x46
github.com/leesper/tao.(*ClientConn).Close()
F:/workspace/tao/conn.go:381 +0x64
main.main·dwrap·2()
F:/workspace/tao/examples/chat/client/client.go:50 +0x39
main.main()
F:/workspace/tao/examples/chat/client/client.go:68 +0x745

Previous read at 0x00c00018e080 by goroutine 12:
github.com/leesper/tao.(*ClientConn).Close()
F:/workspace/tao/conn.go:381 +0x4e
github.com/leesper/tao.handleLoop.func1()
F:/workspace/tao/conn.go:733 +0x26b
github.com/leesper/tao.handleLoop()
F:/workspace/tao/conn.go:740 +0x10c8
github.com/leesper/tao.(*ClientConn).Start·dwrap·11()
F:/workspace/tao/conn.go:374 +0x66

Goroutine 12 (running) created at:
github.com/leesper/tao.(*ClientConn).Start()
F:/workspace/tao/conn.go:374 +0x2b0
main.main()
F:/workspace/tao/examples/chat/client/client.go:52 +0x3e4

请教关于go实现网络库的疑问

自己实现过 c++ 的iocp epoll 异步网络库,最近因为c++开发效率太慢打算使用go混合编程.想写个go的tcp server和 client ,但是网上的资料都是入门级的根本没法看.而且看了之后很纳闷 goroutine对线程安全这方面主要使用哪种同步方式?
我知道go的socket底层都是异步方式,提供上层调用时同步方式.我个人理解就是 基本上 goroutine之间都是在多线程编码了,这反而增加了开发的难度!

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.