Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

新增客户端和服务器端传递共享数据的功能 #7

Open
andot opened this issue May 9, 2018 · 19 comments
Open

新增客户端和服务器端传递共享数据的功能 #7

andot opened this issue May 9, 2018 · 19 comments

Comments

@andot
Copy link
Member

andot commented May 9, 2018

在 Hprose 1.x 和 2.0 版本中,每个调用的上下文对象都是在客户端和服务器端独立传递的,虽然这在一定程度上能够满足大多数情况的需要。但是有时我们还希望能够通过方法参数和返回结果以外的方式在客户端和服务器之间传递某些共享数据信息,实现类似于 HTTP Header 一样的功能。

下面我列出两种解决方案,请大家投票。

@andot
Copy link
Member Author

andot commented May 9, 2018

不改变现有通信协议的实现方案

此方案是通过在实现中,服务器端和客户端对发布的方法增加约定来实现的。

服务器端在发布方法时,通过设置一个选项,来约定客户端传来的第一个参数为单独的信息头(Header),该参数固定为 Map 结构的数据。当读取参数时,将其从参数列表中弹出,放入上下文对象中。用户可以在过滤器,中间件,服务器事件以及服务器发布的方法中,通过上下文对象获取或修改这些信息。

当返回结果时,将其作为引用参数返回。

客户端调用该方法时,自己手动传入作为信息头的参数,或者通过客户端中间件来自动附加上这个参数。或者将这个过程直接内置于客户端的实现中。

优点

可以兼容之前的通信协议。即使在老版本的 Hprose 中,用户也可以自己通过自定义中间件实现。

缺点

1、对于不需要引用参数传递的方法,在返回结果时,会返回多余的参数,对性能有一定影响。

2、可能会影响其它对参数个数敏感的功能的实现,比如推送以及反向调用等。

3、在使用上不够方便,扩展性有限。

@andot
Copy link
Member Author

andot commented May 9, 2018

新增标记的实现方案

在请求和响应中新增一个 H 标记用来标识信息头(Header)。H 标记后跟随标识头内容的 Map 数据(即由 m 标记表示的数据)。

当客户端设置了全局 Header 或者在单独的调用中设置了 Header 选项,则发起的请求中,首先传递 H 标记,之后才会传递表示调用的 C 标记。

如果客户端没有设置全局 Header,也没有在单独的调用中设置了 Header 选项,则发起的请求按照原来的方式调用,即不传递 H 标记。

当服务器端读取到 H 标记时,将其放入上下文对象中,用户可以在过滤器,中间件,服务器事件以及服务器发布的方法中,通过上下文对象获取或修改这些信息。

在服务器返回数据时,如果上下文对象中的 Header 信息有被添加或修改,则返回结果时,首先返回 H 标记,再返回 R 标记,否则直接返回 R 标记。

客户端收到返回的 Header 信息后,对上下文中的 Header 数据做出相应的修改,用户可以在输入过滤器,中间件中对其进行访问。调用返回时,默认不会改动客户端的全局 Header 设置,但是用户可以在输入过滤器,中间件中根据上下文中已经改动的 Header 来自行对全局 Header 设置做出改动。

优点

1、服务器端只要未使用新增的 Header 功能,就完全兼容旧版本的客户端。

2、客户端只要未使用新增的 Header 功能,就完全兼容旧版本的服务器端。

3、服务器在返回 Header 时,只传递修改或新增的部分,如果服务器端仅仅读取客户端传递来的 Header,返回结果时甚至不需要传递 H 标记,因此可以减少不必要数据的传递,节省带宽。

4、对原有的参数处理不做改动,在实现时更加容易,也不会影响对参数个数敏感的功能实现。

5、使用上更加方便灵活。

缺点

因为对通信协议有做出修改。用户无法在不修改旧版本实现的情况下实现该功能。

@andot
Copy link
Member Author

andot commented May 9, 2018

大家如果有更好的解决方案,或者对以上方案有更好的修改,欢迎大家在下面提出。

@pavlelee
Copy link
Member

pavlelee commented May 9, 2018

在服务器返回数据时,如果上下文对象中的 Header 信息有被添加或修改,则返回结果时,首先返回 H 标记,再返回 R 标记,否则直接返回 R 标记。

客户端收到返回的 Header 信息后,对上下文中的 Header 数据做出相应的修改,用户可以在输入过滤器,中间件中对其进行访问。调用返回时,默认不会改动客户端的全局 Header 设置,但是用户可以在输入过滤器,中间件中根据上下文中已经改动的 Header 来自行对全局 Header 设置做出改动。

服务端可以修改并且返回H这个感觉不是很必要,这个好像也不是上下文应该有的。实现方式觉得方法2会好一点

@andot
Copy link
Member Author

andot commented May 9, 2018

原来客户端的独立的上下文还是仅在客户端传递,服务器独立的上下文也是仅在服务器端传递。服务器端返回 H 相当于 HTTP 服务器返回的 Header,但是这样做的话就把 Header 可以独立于 HTTP 协议了。不论是 HTTP,还是 TCP、WebSocket 以及后面可能要加的什么 KCP、ZMQ 之类的都可以支持了。

@pavlelee
Copy link
Member

pavlelee commented May 9, 2018

抽离出来H不依赖http、tcp是对的,我也理解你说的。按照你说的应该是Request H和Response H,而上下文应该使用Request H 来传递,这样层次更清晰。因为Response H并不是上下文该有的东西

@andot
Copy link
Member Author

andot commented May 9, 2018

但是如果不返回 Response H 的话,那服务器端该如何通过结果以外的方式返回客户端需要的东西呢。比如服务器如果希望通过非结果方式来返回一个 token,或者唯一请求 id(反向调用)。

@pavlelee
Copy link
Member

pavlelee commented May 9, 2018

是的,请求包里面包含:Request H、R
响应包里面包含:Response H、R
Context通过Request H 传输,token可以被Response H传输。类似我们定义一个协议,Context、token等只是通过协议传输的信息,这样在定义上更加明确一些。

@andot
Copy link
Member Author

andot commented May 9, 2018

我的意思是这样的,客户端上设置有:

setHeader(name, value);
getHeader(name);

方法。

对于 HTTP 客户端,如果要存取 HTTP 的头通过:

setHttpHeader(name, value);
getHttpHeader(name);

方法来跟 Hprose 的全局 Header 存取来区分。

在客户端的 invoke 方法的设置选项中参数中会增加一个 Header 项,这个项中的内容以及全局设置的 Headerinvoke 方法的实现中,会将它们复制到 context.Header 对象中。这样在客户端的 outputFilter 以及中间件中都可以通过 context.Header 来进行存取和修改。

对于 HTTP 客户端的 invoke 方法的设置选项参数中会增加一个 HttpHeader 项,与上面类似,最后会复制到 context.HttpHeader 对象中。

当进行序列化传输时,context.Header 中的内容会作为 H 标记序列化传递给服务器,如果 context.Header 中的内容为空,则不传递 H 标记。

对于 HTTP 客户端,context.HttpHeader 中的内容最终会作为 HTTP 的 Header 发送。

服务器端读取到 H 标记后,会把它的内容反序列化到服务器端的 context.Header 中,然后用户在服务器端的 inputFilter,中间件,服务器事件,服务端发布的方法,outputFilter 中都可以通过 context.Header 来存取它。最后当服务器返回结果时,如果 context.Header 没有做过修改,则不返回 H 标记,否则,只将有差异的部分作为 H 标记返回。

客户端在收到服务器端的 H 标记后,将其内容读入到之前的 context.Header 中,如果服务器没有返回 H 标记,则 context.Header 中的内容保持不变。

也就是相当于服务器返回的只是修改和增量的部分。但是对于客户端来说,context.Header 中的内容跟服务器端修改之后的 context.Header 内容是一致的。

之后就可以在客户端的 inputFilter,中间件中继续通过 context.Header 来存取它们了。

所以 Hprose 的这个 Header 跟 HTTP 的 Request HeaderResponse Header 是有区别的。Hprose 的 这个 Header 其实是远程调用中,客户端和服务器端的共享数据。

所以如果你觉得叫 Header 这个名字很容易让人误解成 HTTP 的 Request HeaderResponse Header 的话,我可以把标记和名字都改一改:

`H` 标记		->	`S` 标记
`setHeader`		->	`setShareData`
`getHeader`		->	`getShareData`
`context.Header`	->	`context.ShareData`

这样修改的话,可能就更容易理解这个东西的意图了。

不过我还是更喜欢 Header 这个名字,写起来更简单,用起来更方便。

@andot andot changed the title 新增客户端和服务器端传递上下文的功能 新增客户端和服务器端传递共享数据的功能 May 9, 2018
@pavlelee
Copy link
Member

我前面说的Request H并不是特指Http Request Header。我的想法是Hprose自己定义一个传输协议,这个协议不依赖于http、tcp、mq等,这个协议客户端调用包含:Request share data、R,服务端返回包含:Response share data、R。这个是协议部分的定义。

Context是客户端调用的时候告诉服务端上下文的,他是在Request share data里面传输。

最后当服务器返回结果时,如果 context.Header 没有做过修改,则不返回 H 标记,否则,只将有差异的部分作为 H 标记返回。

这个严格来看并不应该在Context里面定义(定义上不会出现被调用方修改上下文的情况),他可能叫其他的。或许我有些对定义偏执,但是我觉得明确之后利于理解

@andot
Copy link
Member Author

andot commented May 10, 2018

这个共享数据不仅仅是客户端告诉服务器的内容,而是在整个调用过程中,从客户端出发,到回到客户端结束这一整个流程的上下文中进行流转的内容。尤其是当实现了反向调用之后,客户端和服务器的角色可以互换。所以,这样定义才是对等的。

如果非要把服务器端返回的共享数据独立于上下文之外,那你该用什么方法来存取服务器端返回的共享数据呢。

@pavlelee
Copy link
Member

当你说反向调用我才知道你为什么Response share data 需要Context,但是我觉得这样设计反向调用不对。
image

@andot
Copy link
Member Author

andot commented May 10, 2018

反向调用并不是如上图那样实现的,反向调用是客户端提供服务,服务器端通过 Context.Clients.Invoke 来对客户端发布的方法进行调用。

Context.Clients.Invoke 可以是广播,多播,单播方式的调用,跟推送差不多,区别是推送没有返回值,推送的数据只有一个。而调用参数可以是多个,调用具有返回值。

@dsonet
Copy link

dsonet commented May 10, 2018

@andot BTW,

通过方法参数和返回结果以外的方式在客户端和服务器之间传递某些共享数据信息,实现类似于 HTTP Header 一样的功能。

这个在HTTP中是以Cookie实现的.大多数人都是接触HTTP协议,所以名称倒是可以按照这个来的.

@andot
Copy link
Member Author

andot commented May 10, 2018

@dsonet 可惜的是,Cc 的标记都已经被占用了。

@pavlelee
Copy link
Member

pavlelee commented May 10, 2018

反向调用并不是如上图那样实现的,反向调用是客户端提供服务,服务器端通过 Context.Clients.Invoke 来对客户端发布的方法进行调用。

这个设计会让调用方即使Client还是Server,这种做法太过针对、别扭。上面的图只是针对反调用给出的更加简单清晰的解决方式,可以使用中间件解决。Core最好灵活简单

@andot
Copy link
Member Author

andot commented May 10, 2018

是的,反向调用的部分我是打算在中间件中实现的。Hprose 3.0 的推送部分我也打算在中间件部分来实现。

@pavlelee
Copy link
Member

支持

@dsonet
Copy link

dsonet commented May 13, 2018

@andot 那么就用H也OK。:)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants