wechat
wechat copied to clipboard
对于 officialAccount.GetServer() 的建议
问题及现象
func serveWechat(rw http.ResponseWriter, req *http.Request) {
// 传入request和responseWriter
server := officialAccount.GetServer(req, rw)
fmt.Printf("new server %T, %p",server,server)
//设置接收消息的处理方法
server.SetMessageHandler(func(msg message.MixMessage) *message.Reply {
return nil
})
//处理消息接收以及回复
err := server.Serve()
if err != nil {
fmt.Println(err)
return
}
//发送回复的消息
server.Send()
}
func main() {
http.HandleFunc("/wechat", serveWechat)
fmt.Println("wechat server listener at", ":80")
err := http.ListenAndServe(":80", nil)
if err != nil {
fmt.Printf("start server error , err=%v", err)
}
}
每次 http 请求过来都会去调用 officialAccount.GetServer,然而该方法每次都会新建一个 server 对象,但事实上只有 W 和 R 是不一样的。
建议
- 如果不想大幅度的改动代码,可以加一个 server 的对象池 (sync.pool )
- 如果想更好的性能,可以参考其他框架的 中间件设计。可以避免大量新建对象。
感谢您的建议;
这块的实现可以通过实现 http.Handler
来做。
感谢您的建议;
这块的实现可以通过实现
http.Handler
来做。
通过实现 http.Handler
自己来做 那么就用不到你的 server 封装了。
如果使 server 来实现 http.Handler
还是面临 W 和 R 的问题,因为 server 中 使用了 server.Request
和 server.Writer
//Server struct
type Server struct {
*context.Context
Writer http.ResponseWriter
Request *http.Request
skipValidate bool
openID string
messageHandler func(message.MixMessage) *message.Reply
RequestRawXMLMsg []byte
RequestMsg message.MixMessage
ResponseRawXMLMsg []byte
ResponseMsg interface{}
isSafeMode bool
random []byte
nonce string
timestamp int64
}
// GetServer 消息管理:接收事件,被动回复消息管理
func (officialAccount *OfficialAccount) GetServer(req *http.Request, writer http.ResponseWriter) *server.Server {
srv := server.NewServer(officialAccount.ctx)
srv.Request = req
srv.Writer = writer
return srv
}
在使用同一个 server 处理事件的时候 W R 可能被后来的覆盖,所以只能新建 server 来保证隔离性。
所以这又回到了最开始的问题,一直新建 server 来处理。
要同一个 server 处理,那么就不能有修改 server W 和 R 的操作。
是的,所以Request和Write就可能不能通过GetServer
来进行传入了。
可以把 Request 和 Write 提出来通过参数的方式传递,或者是 context 参数(类似gin.context ) ,这样就可以使用一个全局的server 来处理这一类的事件。
当然为了 兼容性,只能新增一个 server 实现。
我也觉得这样每次都创建一个server方式不太好
@ywanbing
新人能请教一下
gin 如何中集成 微信开发的入口吗?
`r.GET("/wechat/index",Windex)
func Windex() { wc := w.NewWechat() redisOpts := &cache.RedisOpts{ Host: "127.0.0.1", Database: 0, MaxActive: 10, MaxIdle: 10, IdleTimeout: 60, } redisCache := cache.NewRedis(redisOpts) oa := wc.GetOfficialAccount(cfg) s := oa.GetServer(req, rw) } ` req, rw 怎么定义呢?
@ywanbing 新人能请教一下 gin 如何中集成 微信开发的入口吗?
`r.GET("/wechat/index",Windex)
func Windex() { wc := w.NewWechat() redisOpts := &cache.RedisOpts{ Host: "127.0.0.1", Database: 0, MaxActive: 10, MaxIdle: 10, IdleTimeout: 60, } redisCache := cache.NewRedis(redisOpts) oa := wc.GetOfficialAccount(cfg) s := oa.GetServer(req, rw) } ` req, rw 怎么定义呢?
对于gin的框架其实也是把 req 和 resp 都封装在 gin.context
中了
gin中的源码:
// Context is the most important part of gin. It allows us to pass variables between middleware,
// manage the flow, validate the JSON of a request and render a JSON response for example.
type Context struct {
writermem responseWriter
Request *http.Request // req
Writer ResponseWriter // resp
...
}
你把 这两个参数拿出来 传入到 GetServer(req, rw) 中即可。
并且在这个 例子 中也存在gin的集成。
@ywanbing 谢谢了
感谢您的建议; 这块的实现可以通过实现
http.Handler
来做。通过实现
http.Handler
自己来做 那么就用不到你的 server 封装了。如果使 server 来实现
http.Handler
还是面临 W 和 R 的问题,因为 server 中 使用了server.Request
和server.Writer
//Server struct type Server struct { *context.Context Writer http.ResponseWriter Request *http.Request skipValidate bool openID string messageHandler func(message.MixMessage) *message.Reply RequestRawXMLMsg []byte RequestMsg message.MixMessage ResponseRawXMLMsg []byte ResponseMsg interface{} isSafeMode bool random []byte nonce string timestamp int64 } // GetServer 消息管理:接收事件,被动回复消息管理 func (officialAccount *OfficialAccount) GetServer(req *http.Request, writer http.ResponseWriter) *server.Server { srv := server.NewServer(officialAccount.ctx) srv.Request = req srv.Writer = writer return srv }
在使用同一个 server 处理事件的时候 W R 可能被后来的覆盖,所以只能新建 server 来保证隔离性。
所以这又回到了最开始的问题,一直新建 server 来处理。
要同一个 server 处理,那么就不能有修改 server W 和 R 的操作。
不就是实现单例模式吗?那更方便
感谢您的建议; 这块的实现可以通过实现
http.Handler
来做。通过实现
http.Handler
自己来做 那么就用不到你的 server 封装了。 如果使 server 来实现http.Handler
还是面临 W 和 R 的问题,因为 server 中 使用了server.Request
和server.Writer
//Server struct type Server struct { *context.Context Writer http.ResponseWriter Request *http.Request skipValidate bool openID string messageHandler func(message.MixMessage) *message.Reply RequestRawXMLMsg []byte RequestMsg message.MixMessage ResponseRawXMLMsg []byte ResponseMsg interface{} isSafeMode bool random []byte nonce string timestamp int64 } // GetServer 消息管理:接收事件,被动回复消息管理 func (officialAccount *OfficialAccount) GetServer(req *http.Request, writer http.ResponseWriter) *server.Server { srv := server.NewServer(officialAccount.ctx) srv.Request = req srv.Writer = writer return srv }
在使用同一个 server 处理事件的时候 W R 可能被后来的覆盖,所以只能新建 server 来保证隔离性。 所以这又回到了最开始的问题,一直新建 server 来处理。 要同一个 server 处理,那么就不能有修改 server W 和 R 的操作。
不就是实现单例模式吗?那更方便
在高并发的情况下,单例模式会导致后面的请求覆盖上一个请求,这是完全错误的想法。
我当前也是想实现连接池,看到已经有pr了,但是目前没有合并,可能是实现方面有一些问题,很想和各位讨论下打算如何做?
我的想法是将连接池放在OfficialAccount
结构体内部,当连接放回的时候,需要Server
提供reset方法重置。
我现在的想法是采用一个server 启动,注册一个handler 接受 req和resp 在内部采用对象池,用户无感知。
晚一点出一个demo 来讨论一下
// GetServerHandler 消息管理:接收事件,被动回复消息管理
func (officialAccount *OfficialAccount) GetServerHandler(messageHandler func(*message.MixMessage) *message.Reply) http.HandlerFunc {
return func(writer http.ResponseWriter, request *http.Request) {
srv := server.NewServer(officialAccount.ctx)
srv.Writer = writer
srv.Request = request
// 回收 server 对象
defer srv.Close()
srv.SetMessageHandler(messageHandler)
srv.Serve()
srv.Send()
}
}
采用注册 handler 的方式在内部管理 server 对象的创建,不知道这种方式是否可行; 毕竟距离上次已经有一段时间了,暂时还不清楚 server 是否可以封装在项目内部。
@silenceper
这个有最新的想法了吗?@ywanbing
我现在是自己复写了这个类,这个类只用来获取消息,不去新建server了。 https://github.com/tiantianlikeu/wechat-go-gin
我现在是自己复写了这个类,这个类只用来获取消息,不去新建server了。 https://github.com/tiantianlikeu/wechat-go-gin
赞~
GitHub
wechat-go-gin. Contribute to tiantianlikeu/wechat-go-gin development by creating an account on GitHub.
不知道作者出于什么考虑每次请求都要创建用于处理消息回复的server对象呢?逻辑上就不大对,对于微信发给服务端的请求,只有get/post两种类型,服务端收到请求做好消息体的解析并做相应的处理就可以了,没必要创建大量的server对象处理,参考WxJava库的实现
目前没有太多精力再开源项目上面;
如果针对这个问题有更好的建议,可以提供一个方案以及一些demo; 做一些可行性和易用性的验证,讨论之后 可以共同实现;