Teleport is a versatile, high-performance and flexible socket framework.
It can be used for peer-peer, rpc, gateway, micro services, push services, game services and so on.
Self Test
A server and a client process, running on the same machine
CPU: Intel Xeon E312xx (Sandy Bridge) 16 cores 2.53GHz
Memory: 16G
OS: Linux 2.6.32-696.16.1.el6.centos.plus.x86_64, CentOS 6.4
Go: 1.9.2
Message size: 581 bytes
Message codec: protobuf
Sent total 1000000 messages
teleport
client concurrency | mean(ms) | median(ms) | max(ms) | min(ms) | throughput(TPS) |
---|---|---|---|---|---|
100 | 1 | 0 | 16 | 0 | 75505 |
500 | 9 | 11 | 97 | 0 | 52192 |
1000 | 19 | 24 | 187 | 0 | 50040 |
2000 | 39 | 54 | 409 | 0 | 42551 |
5000 | 96 | 128 | 1148 | 0 | 46367 |
client concurrency | mean(ms) | median(ms) | max(ms) | min(ms) | throughput(TPS) |
---|---|---|---|---|---|
100 | 0 | 0 | 14 | 0 | 225682 |
500 | 2 | 1 | 24 | 0 | 212630 |
1000 | 4 | 3 | 51 | 0 | 180733 |
2000 | 8 | 6 | 64 | 0 | 183351 |
5000 | 21 | 18 | 651 | 0 | 133886 |
Comparison Test
Environment | Throughputs | Mean Latency | P99 Latency |
---|---|---|---|
version | status | branch |
---|---|---|
v4 | release | v4 |
v3 | release | v3 |
v2 | release | v2 |
v1 | release | v1 |
go get -u -f github.com/henrylee2cn/teleport
Header
and Body
two partsHeader
contains metadata in the same format as HTTP headerBody
coding types separately, e.g JSON
Protobuf
string
tcp
, tcp4
, tcp6
, unix
, unixpacket
and so onpackage main import ( "fmt" "time" tp "github.com/henrylee2cn/teleport" ) func main() { // graceful go tp.GraceSignal() // server peer srv := tp.NewPeer(tp.PeerConfig{ CountTime: true, ListenPort: 9090, PrintDetail: true, }) // router srv.RouteCall(new(Math)) // broadcast per 5s go func() { for { time.Sleep(time.Second * 5) srv.RangeSession(func(sess tp.Session) bool { sess.Push( "/push/status", fmt.Sprintf("this is a broadcast, server time: %v", time.Now()), ) return true }) } }() // listen and serve srv.ListenAndServe() } // Math handler type Math struct { tp.CallCtx } // Add handles addition request func (m *Math) Add(arg *[]int) (int, *tp.Rerror) { // test query parameter tp.Infof("author: %s", m.Query().Get("author")) // add var r int for _, a := range *arg { r += a } // response return r, nil }
package main import ( "time" tp "github.com/henrylee2cn/teleport" ) func main() { // log level tp.SetLoggerLevel("ERROR") cli := tp.NewPeer(tp.PeerConfig{}) defer cli.Close() cli.RoutePush(new(Push)) sess, err := cli.Dial(":9090") if err != nil { tp.Fatalf("%v", err) } var result int rerr := sess.Call("/math/add?author=henrylee2cn", []int{1, 2, 3, 4, 5}, &result, ).Rerror() if rerr != nil { tp.Fatalf("%v", rerr) } tp.Printf("result: %d", result) tp.Printf("wait for 10s...") time.Sleep(time.Second * 10) } // Push push handler type Push struct { tp.PushCtx } // Push handles '/push/status' message func (p *Push) Status(arg *string) *tp.Rerror { tp.Printf("%s", *arg) return nil }
Message.Body
Abstracts the data message(Message Object) of the application layer and is compatible with HTTP message:
You can customize your own communication protocol by implementing the interface:
type ( // Proto pack/unpack protocol scheme of socket message. Proto interface { // Version returns the protocol's id and name. Version() (byte, string) // Pack writes the Message into the connection. // Note: Make sure to write only once or there will be package contamination! Pack(*Message) error // Unpack reads bytes from the connection to the Message. // Note: Concurrent unsafe! Unpack(*Message) error } ProtoFunc func(io.ReadWriter) Proto )
Next, you can specify the communication protocol in the following ways:
func SetDefaultProtoFunc(ProtoFunc) type Peer interface { ... ServeConn(conn net.Conn, protoFunc ...ProtoFunc) Session DialContext(ctx context.Context, addr string, protoFunc ...ProtoFunc) (Session, *Rerror) Dial(addr string, protoFunc ...ProtoFunc) (Session, *Rerror) Listen(protoFunc ...ProtoFunc) error ... }
Default protocol RawProto
(Big Endian):
{4 bytes message length} {1 byte protocol version} {1 byte transfer pipe length} {transfer pipe IDs} # The following is handled data by transfer pipe {2 bytes sequence length} {sequence} {1 byte message type} # e.g. CALL:1; REPLY:2; PUSH:3 {2 bytes URI length} {URI} {2 bytes metadata length} {metadata(urlencoded)} {1 byte body codec id} {body}
Transfer filter pipe, handles byte stream of message when transfer.
// XferFilter handles byte stream of message when transfer. type XferFilter interface { // Id returns transfer filter id. Id() byte // Name returns transfer filter name. Name() string // OnPack performs filtering on packing. OnPack([]byte) ([]byte, error) // OnUnpack performs filtering on unpacking. OnUnpack([]byte) ([]byte, error) } // Get returns transfer filter by id. func Get(id byte) (XferFilter, error) // GetByName returns transfer filter by name. func GetByName(name string) (XferFilter, error) // XferPipe transfer filter pipe, handlers from outer-most to inner-most. // Note: the length can not be bigger than 255! type XferPipe struct { // Has unexported fields. } func NewXferPipe() *XferPipe func (x *XferPipe) Append(filterId ...byte) error func (x *XferPipe) AppendFrom(src *XferPipe) func (x *XferPipe) Ids() []byte func (x *XferPipe) Len() int func (x *XferPipe) Names() []string func (x *XferPipe) OnPack(data []byte) ([]byte, error) func (x *XferPipe) OnUnpack(data []byte) ([]byte, error) func (x *XferPipe) Range(callback func(idx int, filter XferFilter) bool) func (x *XferPipe) Reset()
The body's codec set.
type Codec interface { // Id returns codec id. Id() byte // Name returns codec name. Name() string // Marshal returns the encoding of v. Marshal(v interface{}) ([]byte, error) // Unmarshal parses the encoded data and stores the result // in the value pointed to by v. Unmarshal(data []byte, v interface{}) error }
Plug-ins during runtime.
type ( // Plugin plugin background Plugin interface { Name() string } // PreNewPeerPlugin is executed before creating peer. PreNewPeerPlugin interface { Plugin PreNewPeer(*PeerConfig, *PluginContainer) error } ... )
// Start a server var peer1 = tp.NewPeer(tp.PeerConfig{ ListenPort: 9090, // for server role }) peer1.Listen() ... // Start a client var peer2 = tp.NewPeer(tp.PeerConfig{}) var sess, err = peer2.Dial("127.0.0.1:8080")
type Aaa struct { tp.CallCtx } func (x *Aaa) XxZz(arg *) ( , *tp.Rerror) { ... return r, nil }
// register the call route: /aaa/xx_zz peer.RouteCall(new(Aaa)) // or register the call route: /xx_zz peer.RouteCallFunc((*Aaa).XxZz)
func XxZz(ctx tp.CallCtx, arg *) ( , *tp.Rerror) { ... return r, nil }
// register the call route: /xx_zz peer.RouteCallFunc(XxZz)
type Bbb struct { tp.PushCtx } func (b *Bbb) YyZz(arg *) *tp.Rerror { ... return nil }
// register the push route: /bbb/yy_zz peer.RoutePush(new(Bbb)) // or register the push route: /yy_zz peer.RoutePushFunc((*Bbb).YyZz)
// YyZz register the route: /yy_zz func YyZz(ctx tp.PushCtx, arg *) *tp.Rerror { ... return nil }
// register the push route: /yy_zz peer.RoutePushFunc(YyZz)
func XxxUnknownCall (ctx tp.UnknownCallCtx) (interface{}, *tp.Rerror) { ... return r, nil }
// register the unknown call route: /* peer.SetUnknownCall(XxxUnknownCall)
func XxxUnknownPush(ctx tp.UnknownPushCtx) *tp.Rerror { ... return nil }
// register the unknown push route: /* peer.SetUnknownPush(XxxUnknownPush)
AaBb
-> /aa_bb
Aa_Bb
-> /aa/bb
aa_bb
-> /aa/bb
Aa__Bb
-> /aa_bb
aa__bb
-> /aa_bb
ABC_XYZ
-> /abc/xyz
ABcXYz
-> /abc_xyz
ABC__XYZ
-> /abc_xyz
// NewIgnoreCase Returns a ignoreCase plugin. func NewIgnoreCase() *ignoreCase { return &ignoreCase{} } type ignoreCase struct{} var ( _ tp.PostReadCallHeaderPlugin = new(ignoreCase) _ tp.PostReadPushHeaderPlugin = new(ignoreCase) ) func (i *ignoreCase) Name() string { return "ignoreCase" } func (i *ignoreCase) PostReadCallHeader(ctx tp.ReadCtx) *tp.Rerror { // Dynamic transformation path is lowercase ctx.UriObject().Path = strings.ToLower(ctx.UriObject().Path) return nil } func (i *ignoreCase) PostReadPushHeader(ctx tp.ReadCtx) *tp.Rerror { // Dynamic transformation path is lowercase ctx.UriObject().Path = strings.ToLower(ctx.UriObject().Path) return nil }
// add router group group := peer.SubRoute("test") // register to test group group.RouteCall(new(Aaa), NewIgnoreCase()) peer.RouteCallFunc(XxZz, NewIgnoreCase()) group.RoutePush(new(Bbb)) peer.RoutePushFunc(YyZz) peer.SetUnknownCall(XxxUnknownCall) peer.SetUnknownPush(XxxUnknownPush)
type PeerConfig struct { Network string `yaml:"network" ini:"network" comment:"Network; tcp, tcp4, tcp6, unix or unixpacket"` LocalIP string `yaml:"local_ip" ini:"local_ip" comment:"Local IP"` ListenPort uint16 `yaml:"listen_port" ini:"listen_port" comment:"Listen port; for server role"` DefaultDialTimeout time.Duration `yaml:"default_dial_timeout" ini:"default_dial_timeout" comment:"Default maximum duration for dialing; for client role; ns,µs,ms,s,m,h"` RedialTimes int32 `yaml:"redial_times" ini:"redial_times" comment:"The maximum times of attempts to redial, after the connection has been unexpectedly broken; for client role"` DefaultBodyCodec string `yaml:"default_body_codec" ini:"default_body_codec" comment:"Default body codec type id"` DefaultSessionAge time.Duration `yaml:"default_session_age" ini:"default_session_age" comment:"Default session max age, if less than or equal to 0, no time limit; ns,µs,ms,s,m,h"` DefaultContextAge time.Duration `yaml:"default_context_age" ini:"default_context_age" comment:"Default CALL or PUSH context max age, if less than or equal to 0, no time limit; ns,µs,ms,s,m,h"` SlowCometDuration time.Duration `yaml:"slow_comet_duration" ini:"slow_comet_duration" comment:"Slow operation alarm threshold; ns,µs,ms,s ..."` PrintDetail bool `yaml:"print_detail" ini:"print_detail" comment:"Is print body and metadata or not"` CountTime bool `yaml:"count_time" ini:"count_time" comment:"Is count cost time or not"` }
SetMessageSizeLimit sets max message size. If maxSize<=0, set it to max uint32.
func SetMessageSizeLimit(maxMessageSize uint32)
SetSocketKeepAlive sets whether the operating system should send keepalive messages on the connection.
func SetSocketKeepAlive(keepalive bool)
SetSocketKeepAlivePeriod sets period between keep alives.
func SetSocketKeepAlivePeriod(d time.Duration)
SetSocketNoDelay controls whether the operating system should delay message transmission in hopes of sending fewer messages (Nagle's algorithm). The default is true (no delay), meaning that data is sent as soon as possible after a Write.
func SetSocketNoDelay(_noDelay bool)
SetSocketReadBuffer sets the size of the operating system's receive buffer associated with the connection.
func SetSocketReadBuffer(bytes int)
SetSocketWriteBuffer sets the size of the operating system's transmit buffer associated with the connection.
func SetSocketWriteBuffer(bytes int)
package | import | description |
---|---|---|
json | import "github.com/henrylee2cn/teleport/codec" |
JSON codec(teleport own) |
protobuf | import "github.com/henrylee2cn/teleport/codec" |
Protobuf codec(teleport own) |
plain | import "github.com/henrylee2cn/teleport/codec" |
Plain text codec(teleport own) |
form | import "github.com/henrylee2cn/teleport/codec" |
Form(url encode) codec(teleport own) |
package | import | description |
---|---|---|
auth | import "github.com/henrylee2cn/teleport/plugin/auth" |
A auth plugin for verifying peer at the first time |
binder | import binder "github.com/henrylee2cn/teleport/plugin/binder" |
Parameter Binding Verification for Struct Handler |
heartbeat | import heartbeat "github.com/henrylee2cn/teleport/plugin/heartbeat" |
A generic timing heartbeat plugin |
proxy | import "github.com/henrylee2cn/teleport/plugin/proxy" |
A proxy plugin for handling unknown calling or pushing |
secure | import secure "github.com/henrylee2cn/teleport/plugin/secure" |
Encrypting/decrypting the message body |
package | import | description |
---|---|---|
rawproto | import "github.com/henrylee2cn/teleport/proto/rawproto |
A fast socket communication protocol(teleport default protocol) |
jsonproto | import "github.com/henrylee2cn/teleport/proto/jsonproto" |
A JSON socket communication protocol |
pbproto | import "github.com/henrylee2cn/teleport/proto/pbproto" |
A Protobuf socket communication protocol |
package | import | description |
---|---|---|
gzip | import "github.com/henrylee2cn/teleport/xfer/gzip" |
Gzip(teleport own) |
md5 | import "github.com/henrylee2cn/teleport/xfer/md5" |
Provides a integrity check transfer filter |
package | import | description |
---|---|---|
multiclient | import "github.com/henrylee2cn/teleport/mixer/multiclient" |
Higher throughput client connection pool when transferring large messages (such as downloading files) |
websocket | import "github.com/henrylee2cn/teleport/mixer/websocket" |
Makes the Teleport framework compatible with websocket protocol as specified in RFC 6455 |
html | html "github.com/xiaoenai/tp-micro/helper/mod-html" |
HTML render for http client |
project | description |
---|---|
TP-Micro | TP-Micro is a simple, powerful micro service framework based on Teleport |
Pholcus | Pholcus is a distributed, high concurrency and powerful web crawler software |
Teleport is under Apache v2 License. See the LICENSE file for the full license text
任意传送门——Teleport Vue3 的组合式 API 以及基于 Proxy 响应式原理已经有很多文章介绍过了,除了这些比较亮眼的更新; Vue3 还新增了一个内置组件:Teleport。这个组件的作用主要用来将模板内的 DOM 元素移动到其他位置。 为什么我们需要 Teleport Teleport 是一种能够将我们的模板移动到 DOM 中 Vue app 之外的其他位置的技术,就有点像哆啦
Teleport 是一种能够将我们的组件html结构移动到指定位置的技术。 <teleport to="移动位置"> <div v-if="isShow" class="mask"> <div class="dialog"> <h3>我是一个弹窗</h3> <button @click="isShow = false">关闭弹窗</button> </div> </div>
一、前言 Vue.js 的核心思想之一是组件化,组件就是 DOM 的映射,我们通过嵌套的组件构成了一个组件应用程序的树。但是,有些时候组件模板的一部分在逻辑上属于该组件,而从技术角度来看,最好将模板的这一部分移动到应用程序之外的其他位置。一个常见的场景是创建一个包含全屏模式的对话框组件。在大多数情况下,我们希望对话框的逻辑存在于组件中,但是对话框的定位 CSS 是一个很大的问题,它非常容易受到外层
问题内容: 我现在两次遇到问题,生产者线程会产生N个工作项,将它们提交给an ,然后需要等待,直到所有N个项都已处理完毕。 注意事项 N事先未知 。如果是这样,我将简单地创建一个然后具有生产者线程,直到完成所有工作。 使用a 是不合适的,因为尽管我的生产者线程需要阻塞(即,通过调用),但 无法表示所有工作都已完成 ,从而导致生产者线程停止等待。 我当前喜欢的解决方案是使用整数计数器,并在提交工作项
Gravitational Teleport 是一个先进的 SSH 服务器,可通过 SSH 或者 HTTPS 远程访问 Linux 服务器。其目的是为了替代 sshd。Teleport 可以轻松让团队以最佳实践来使用 SSH,例如: 无需分发密钥,Teleport 使用基于证书的访问并实现自动过期 增强了两阶段身份验证 集群支持,每个 Teleport 节点是集群的一部分,而且可通过 Web UI
flex-basis 我们使用flex-basis属性在分配空间之前定义flex-item的默认大小。 以下示例演示了flex-basis属性的用法。 在这里,我们创建了3个彩色盒子,并将它们的尺寸固定为150像素。 <!doctype html> <html lang = "en"> <style> .box{ font-size:15px;
组件1包括:1.txt,2.txt 组件2包括:3.txt 4.txt 当组件1和组件2都选中时安装所有文件,当组件2选中而组件1未选中时安装文件3.txt,不安装组件2所属的文件4.txt。当组件1选中而组件2未选中时仅安装组件1所属的文件。 用逻辑库实现: LOGICLIB_SECTIONCMP !include LogicLib.nsh Section "区段1" Sec1 File 1.
Code Generators v0.17 - Beta! What · Quick Setup · Ecosystem · Documentation · Development · Planning · Contributions We are not far from the first official version of the code generators, but meanwhi
本文向大家介绍灵活使用asp.net中的gridview控件,包括了灵活使用asp.net中的gridview控件的使用技巧和注意事项,需要的朋友参考一下 gridview是asp.net常用的显示数据控件,对于.net开发人员来说应该是非常的熟悉了。gridview自带有许多功能,包括分页,排序等等,但是作为一个.net开发人员来说熟练掌握利用存储过程分页或者第三方自定义分页十分重要,这不仅是项