spring-cloud / spring-cloud-function

Apache License 2.0
1.04k stars 615 forks source link

"Wiretap" or "export" feature #26

Closed dsyer closed 6 years ago

dsyer commented 7 years ago

Basic idea: user declares (somehow) an interest in "exporting" items of a given POJO type Foo (say), and framework generates 2 beans: one is a Consumer<Foo> and the other is a Supplier<Flux<Foo>>. The framework already automatically adds the Supplier to the FunctionCatalog, so it can be used to send Foos to an external stream or HTTP client. The Consumer is implemented in such a way that it pushes each Foo into the Flux stream created by the Supplier.

I implemented it here as @EnableWiretap but the name could be changed: https://github.com/dsyer/petflix/blob/master/videos/src/main/java/com/example/VideoApplication.java#L15.

dsyer commented 7 years ago

I suppose the user might want multiple exports for objects of the same type, so @EnableWiretap(Foo.class) is not enough for the general case. There's a naming problem anyway (the bean names need to be customizable a bit in general). So maybe @EnableWiretap(@Wiretap(type=Foo.class, name="foos")) would work better, or as an alternative.

markfisher commented 7 years ago

interesting idea - I wonder if we want to support this via properties in a way similar to other currently configurable functions. For example, we currently have compile, import, etc:

spring:
  cloud:
    function:
      compile:
        uppercase:
          lambda: s->s.toUpperCase()
          inputType: String
          outputType: String
        hello:
          type: supplier
          lambda: ()->"hello"
          outputType: String
      import:
        lowercase:
          location: file:///tmp/function-registry/functions/lowercase.fun
      scan:
        packages: fun1, fun2
      inject:
        greeter:
          greeting: howdy

So perhaps we could add export.<name>, and we could even use SpEL for the "definition" thereby supporting @bean.whatever() syntax.

dsyer commented 7 years ago

Not sure. I don't see as much value in the properties approach here because you don't need an export/wiretap unless you are going to use it in your code. So you might as well declare it there.

markfisher commented 7 years ago

I think I agree.

dsyer commented 7 years ago

I'm swinging back the other way, having watched the Azure function demo from Serverless 2017. In Azure you have "function apps" (a lot like a SCFn app with 1-n functions) and you "bind" them to inputs and outputs using JSON. What we have right now is kind of automatic binding based on the classpath, and the input and output are always to the same transport (web, rabbit, etc.). So tweaking that to reflect the user's intentions via the Spring environment seems like a good idea, and keeps the runtime separate from the code. Spring Cloud Stream actually already supports this via something like:

spring.cloud.stream.bindings.input.binder=kafka
spring.cloud.stream.bindings.output.binder=rabbit

So, we could use the exact same idiom (if we add a "web" binder somehow), or we could abstract it into something different, e.g.

spring.cloud.function.bindings.uppercase.input=web
spring.cloud.function.bindings.uppercase.output=rabbit

to accept an incoming HTTP request and relay the processed result to rabbit.

Interestingly, the example above splits the "uppercase" Function into an "input" Consumer and an "output" Supplier, with a wiretap precisely as described in the original feature request. Internally we could implement it that way - the FunctionCatalog could be virtualized and the split/wiretap could take place generically in that layer.

dsyer commented 6 years ago

The "servlet" binder lets you do the thing in the last comment. I think I'll call this closed.