Science and technology

A information to Pipy, a programmable community proxy for cloud

Pipy is an open supply, cloud-native, community stream processor. It is modular by design and may create a high-performance community proxy. It’s written in C++ and is constructed on prime of the Asio asynchronous I/O library. Pipy is right for a wide range of use instances starting from edge routers, load balancers, proxy options, API gateways, static HTTP servers, service mesh sidecars, and extra.

Pipy additionally comes with built-in JavaScript assist by PipyJS. PipyJS is very customizable and predictable in efficiency, with no rubbish assortment overhead. Currently, PipyJS is a part of the Pipy code base, nevertheless it has no dependency on it and sooner or later it might be moved to a standalone bundle.

Pipy fast begin information

You can run the manufacturing model of Pipy utilizing Podman or Docker with one of many tutorial scripts offered on the official Pipy Git repository. The Pipy container picture may be configured with just a few setting variables:

  • PIPY_CONFIG_FILE=</path/to/config-file> units the situation of the Pipy configuration file.

  • PIPY_SPAWN=n units the variety of Pipy cases you wish to begin, the place n is the variety of cases. This is a zero-based index, so 0 represents 1 occasion. For instance, use PIPY_SPAWN=3 for 4 cases.

Start the Pipy server with this instance script:

$ docker run --rm -e PIPY_CONFIG_FILE=
https://uncooked.githubusercontent.com/flomesh-io/pipy/predominant/tutorial/01-hello
-e PIPY_SPAWN=1 -p 8080:8080 flomesh/pipy-pjs:newest

You could discover that as a substitute of a neighborhood file, this code gives a hyperlink to a distant Pipy script by the setting variable PIPY_CONFIG_FILE. Pipy is sensible sufficient to deal with that.

For your reference, listed below are the contents of the file tutorial/01-hello/hi there.js:

pipy()
.pay attention(8080)
.serveHTTP(
new Message('Hi, there!n')
)

This easy script defines one Port pipeline, which listens on port 8080 and returns “Hi, there!” for every HTTP request acquired on the listening port.

As you have uncovered native port 8080 with the docker run command, you’ll be able to proceed with a take a look at on the identical port:

$ curl http://localhost:8080

Executing the above command shows Hi, there! on the console.

For studying, improvement, or debugging functions it is really helpful to proceed with the native set up (both construct Pipy from sources or obtain a launch to your OS) of Pipy, because it comes with an admin net console together with documentation and tutorials.

Once put in domestically, working pipy with none arguments begins the admin console on port 6060, however it may be configured to pay attention on the totally different port with the --admin-port possibility.

(Ali Naqvi, CC BY-SA 40)

To construct Pipy from supply, or to put in a precompiled binary to your working system, seek advice from README.md on the Pipy Git repository.

Explore the open supply cloud

Running Pipy in a terminal

To begin a Pipy proxy, run Pipy with a PipyJS script file, for instance, the script in tutorial/01-hello/hi there.js in the event you want a easy echo server that responds with the identical message physique acquired with each incoming request:

$ pipy tutorial/01-hello/hi there.js

Alternatively, whereas growing and debugging, one can begin Pipy with a builtin net UI:

$  pipy tutorial/01-hello/hi there.js --admin-port=6060

To see all command-line choices, use the --help flag:


Pipy is a stream processor

Pipy operates on community streams utilizing an event-driven pipeline the place it consumes the enter stream, performs user-provided transformations, and outputs the stream. A pipy information stream takes uncooked information and abstracts it into an occasion. An occasion can belong to considered one of 4 classes:

  • Data: Network streams are composed of information bytes and are available chunks. Pipy abstracts out chunks right into a Data occasion.
  • MessageStart, MessageFinish, StreamEnd: These three non-data occasions work as markers, giving the uncooked byte streams high-level semantics for enterprise logic to depend on.

Pipy Design

The inside workings of Pipy are just like Unix Pipelines however not like Unix pipelines, which take care of discreet bytes, Pipy offers with streams of occasions.

Pipy processes incoming streams by a sequence of filters, the place every filter offers with common considerations like request logging, authentication, SSL offloading, request forwarding, and so forth. Each filter reads from its enter and writes to its output, with the output of 1 filter related to the enter of the subsequent.

Pipelines

A sequence of filters is known as a pipeline and Pipy categorizes pipelines in 3 totally different classes in response to their enter sources.

  • Port pipeline: Reads in Data occasions from a community port, processes them, after which writes the consequence again to the identical port. This is essentially the most generally used request and response mannequin. For occasion, when Pipy works like an HTTP server, the enter to a Port pipeline is an HTTP request from the purchasers, and the output from the pipeline can be an HTTP response despatched again to purchasers.

  • Timer pipeline: Gets a pair of MessageStart and MessageFinish occasions as its enter periodically. Useful when Cron job-like performance is required.

  • Sub-pipeline: Works along side a be part of filter, equivalent to hyperlink, which takes in occasions from its predecessor pipeline, feeds them right into a sub-pipeline for processing, reads again the output from the sub-pipeline, after which pumps it right down to the subsequent filter.

    The greatest manner to have a look at sub-pipelines and be part of filters is to consider them as callees and callers of a subroutine in procedural programming. The enter to the joint filter is the subroutine’s parameters, the output from the joint filter is its return worth.

    A root pipeline, equivalent to Port or Timer, can’t be known as from be part of filters.

    To get an inventory of builtin filters and their parameters:

    $  pipy --list-filters
    $  pipy --help-filters

Context

Another vital notion in Pipy is that of contexts. A context is a set of variables hooked up to a pipeline. Every pipeline will get entry to the identical set of variables throughout a Pipy occasion. In different phrases, contexts have the identical form. When you begin a Pipy occasion, the very first thing you do is outline the form of the context by defining variable(s) and their preliminary values.

Every root pipeline clones the preliminary context you outline at the beginning. When a sub-pipeline begins, it both shares or clones its father or mother’s context, relying on which joint filter you utilize. For occasion, a hyperlink filter shares its father or mother’s context whereas a demux filter clones it.

To the scripts embedded in a pipeline, these context variables are their international variables, which implies that these variables are at all times accessible to scripts from wherever in the event that they stay in the identical script file.

This might sound odd to a seasoned programmer as a result of international variables normally imply they’re globally distinctive. You have just one set of those variables, whereas in Pipy we are able to have many units of them (contexts) relying on what number of root pipelines are open for incoming community connections and what number of sub-pipelines clone their mother and father’ contexts.

Writing a Network Proxy

Suppose you are working separate cases of various companies and also you wish to add a proxy to ahead visitors to the related companies based mostly on the request URL path. This would provide the advantage of exposing a single URL and scaling your companies within the again finish with out customers having to recollect a definite service URL. In regular conditions, your companies can be working on totally different nodes and every service might have a number of cases working. In this instance, assume you are working the companies beneath, and wish to distribute visitors to them based mostly on the URI.

  • service-hi at /hello/* (127.0.0.1:8080, 127.0.0.1:8082)

  • service-echo at /echo (127.0.0.1:8081)

  • service-tell-ip at /ip_/_* (127.0.0.1:8082)

Pipy scripts are written in JavaScript, and you should utilize any textual content editor of your option to edit them. Alternatively, when you’ve got put in Pipy domestically, you should utilize Pipy admin Web UI, which comes with syntax highlighting, auto-completion, hints, in addition to the flexibility to run scripts, all from the identical console.

Start a Pipy occasion, with none arguments, so the Pipy admin console launches on port 6060. Now open your favourite net browser and navigate to [http://localhost:6060](http://localhost:6060/ to see the built-in Pipy Administration Web UI.

(Ali Naqvi, CC BY-SA 40)

Create a Pipy program

A superb design observe is that code and configurations are separated. Pipy helps such modular design by its Plugins, which you’ll consider as JavaScript modules. That mentioned, you retailer your configuration information within the config folder, and your coding logic in separate information underneath the plugins folder. The predominant proxy server script is saved within the root folder, the principle proxy script (proxy.js) will embrace and mix the performance outlined in separate modules. In the tip, your last folder construction is:

├── config
│ ├── balancer.json
│ ├── proxy.json
│ └── router.json
├── plugins
│ ├── balancer.js
│ ├── default.js
│ └── router.js
└── proxy.js

 

      1.Click New Codebase, enter /proxy for the Codebase identify within the dialog after which click on Create.

  1. Click the + button so as to add a brand new file. Enter /config/proxy.json for its filename after which click on Create. This is the configuration file used to configure your proxy.

  2. You now see proxy.json listed underneath the config folder within the left pane. Click on the file to open it and add the configuration proven beneath and be sure to save your file by clicking the disk icon on the highest panel.
     

    {
    "listen": 8000,
    "plugins": [
    "plugins/router.js",
    "plugins/balancer.js",
    "plugins/default.js" ]
    }
  3. Repeat steps 2 and three to create one other file, /config/router.json, to retailer route data. Enter this configuration information:

    {
    "routes": {
    "/hi/*": "service-hi",
    "/echo": "service-echo",
    "/ip/*": "service-tell-ip" }
    }
  4. Repeat steps 2 and three to create one other file, /config/balancer.json to retailer your service-to-target map. Enter the next information:

    {
    "services": {
    "service-hi" : ["127.0.0.1:8080", "127.0.0.1:8082"],
    "service-echo" : ["127.0.0.1:8081"],
    "service-tell-ip" : ["127.0.0.1:8082"] }
    }
  5. Now it is time to write your very first Pipy script, which might be used as a default fallback when your server receives a request for which you have no goal (an endpoint) configured. Create the file /plugins/default.js. The identify right here is only a conference and Pipy would not depend on names, so you’ll be able to select any identify you want. The script will include the code proven beneath, which returns the HTTP Status code 404 with a message of No handler discovered:
     

    pipy()
    .pipeline('request')
    .exchangeMessage(
    new Message({ standing: 404 }, 'No handler discovered'))

       7.Create the file /plugins/router.js, which shops your routing logic:

(config =>
pipy({
_router: new algo.URLRouter(config.routes), })
.export('router', {
__serviceID: '', })
.pipeline('request')
.deal withMessageStart(
msg => (
__serviceID = _router.discover(
msg.head.headers.host,
msg.head.path, )
) )
)(JSON.decode(pipy.load('config/router.json')))

  1. Create the file /plugins/balancer.js, which shops your load balancing logic as a side-note. Pipy comes with a number of Load Balancing algorithms, however for simplicity, you are utilizing the Round Robin algorithm right here.

    (config =>

    pipy({
      _services: (
        Object.fromEntries(
          Object.entries(config.companies).map(
            ([k, v]) => [
              k, new algo.RoundRobinLoadBalancer(v)
            ]
          )
        )
      ),

      _balancer: null,
      _balancerCache: null,
      _target: '',
    })

    .import({
      __turnDown: 'proxy',
      __serviceID: 'router',
    })

    .pipeline('session')
      .handleStreamStart(
        () => (
          _balancerCache = new algo.Cache(
            // okay is a balancer, v is a goal
            (okay  ) => okay.choose(),
            (okay,v) => okay.deselect(v),
          )
        )
      )
      .handleStreamEnd(
        () => (
          _balancerCache.clear()
        )
      )

    .pipeline('request')
      .deal withMessageStart(
        () => (
          _balancer = _services[__serviceID],
          _balancer && (_target = _balancerCache.get(_balancer)),
          _target && (__turnDown = true)
        )
      )
      .hyperlink(
        'ahead', () => Boolean(_target),
        ''
      )

    .pipeline('ahead')
      .muxHTTP(
        'connection',
        () => _target
      )

    .pipeline('connection')
      .join(
        () => _target
      )

    )(JSON.decode(pipy.load('config/balancer.json')))

  2. Now write the entry level, or the proxy server script, to make use of the above plugins. Creating a brand new code base (step 1) creates a default predominant.js file as an entry level. You can use that as your predominant entry level, or in the event you desire to go together with a distinct identify, be happy to delete predominant.js and create a brand new file with the identify of your selection. For this instance, delete it and create a brand new file named /proxy.js. Make certain you click on the highest flag icon to make it the principle entry level, to make sure script execution is began once you hit the run button (the arrow icon on the best).

    (config =>

    pipy()

    .export('proxy', {
      __turnDown: false,
    })

    .pay attention(config.pay attention)
      .use(config.plugins, 'session')
      .demuxHTTP('request')

    .pipeline('request')
      .use(
        config.plugins,
        'request',
        'response',
        () => __turnDown
      )

    )(JSON.decode(pipy.load('config/proxy.json')))

So far, your workspace seems like this:

(Ali Naqvi, CC BY-SA 40)

To run your script, click on the play icon button (4th from proper). Pipy runs your proxy script, and also you see output just like this:

(Ali Naqvi, CC BY-SA 40)

This exhibits that your proxy server is listening on port 8000 (which you configured in your /config/proxy.json). Use curl to run a test:
 

$ curl -i [http://localhost:8000](http://localhost:8000)
HTTP/1.1 404 Not Found
content-length: 10
connection: keep-alive
No handler discovered

That response is sensible as a result of you have not configured any goal for root. Try considered one of your configured routes, equivalent to /hello:

$ curl -i [http://localhost:8000/hi](http://localhost:8000/hello)
HTTP/1.1 502 Connection Refused
content-length: 0
connection: keep-alive

You get 502 Connection Refused as a result of you haven’t any service working in your configured goal port.

You can replace /config/balancer.json with particulars just like the host and port of your already working companies to make it match to your use case, or you’ll be able to simply write a script in Pipy to pay attention in your configured ports, and return easy messages.

Save this code to a file in your native laptop named mock-proxy.js, and keep in mind the situation the place you saved it:

pipy()

.pay attention(8080)
  .serveHTTP(
    new Message('Hi, there!n')
  )

.pay attention(8081)
  .serveHTTP(
    msg => new Message(msg.physique)
  )

.pay attention(8082)
  .serveHTTP(
    msg => new Message(
      `You are requesting ${msg.head.path} from ${__inbound.remoteAddress}n`
    )
  )

Open a brand new terminal window and run this script with Pipy (change /path/to to the situation the place you saved this script file):
 

$ pipy /path/to/mock-proxy.js
2022-01-11 18:56:31 [INF] [config]
2022-01-11 18:56:31 [INF] [config] Module /mock-proxy.js
2022-01-11 18:56:31 [INF] [config] ================
2022-01-11 18:56:31 [INF] [config]
2022-01-11 18:56:31 [INF] [config] [Listen on :::8080]
2022-01-11 18:56:31 [INF] [config] ----->|
2022-01-11 18:56:31 [INF] [config] |
2022-01-11 18:56:31 [INF] [config] serveHTTP
2022-01-11 18:56:31 [INF] [config] |
2022-01-11 18:56:31 [INF] [config] <-----|
2022-01-11 18:56:31 [INF] [config]
2022-01-11 18:56:31 [INF] [config] [Listen on :::8081]
2022-01-11 18:56:31 [INF] [config] ----->|
2022-01-11 18:56:31 [INF] [config] |
2022-01-11 18:56:31 [INF] [config] serveHTTP
2022-01-11 18:56:31 [INF] [config] |
2022-01-11 18:56:31 [INF] [config] <-----|
2022-01-11 18:56:31 [INF] [config]
2022-01-11 18:56:31 [INF] [config] [Listen on :::8082]
2022-01-11 18:56:31 [INF] [config] ----->|
2022-01-11 18:56:31 [INF] [config] |
2022-01-11 18:56:31 [INF] [config] serveHTTP
2022-01-11 18:56:31 [INF] [config] |
2022-01-11 18:56:31 [INF] [config] <-----|
2022-01-11 18:56:31 [INF] [config]
2022-01-11 18:56:31 [INF] [listener] Listening on port 8080 at ::
2022-01-11 18:56:31 [INF] [listener] Listening on port 8081 at ::
2022-01-11 18:56:31 [INF] [listener] Listening on port 8082 at ::

You now have your mock companies listening on ports 8080, 8081, and 8082. Do a take a look at once more in your proxy server to see the proper response returned out of your mock service.

Summary

You’ve used various Pipy options, together with variable declaration, importing and exporting variables, plugins, Pipelines, sub-pipelines, filter chaining, Pipy filters like deal withMessageStart, handleStreamStart, and hyperlink, and Pipy lessons like JSON, algo.URLRouter, algo.RoundRobinLoadBalancer, algo.Cache, and others. For extra data, learn the superb Pipy documentation, and thru Pipy’s admin net UI, and comply with the step-by-step tutorials that include it.

Conclusion

Pipy from Flomesh is an open supply, extraordinarily quick, and light-weight community visitors processor. You can use it in a wide range of use instances starting from edge routers, load balancing and proxying (ahead and reverse), API gateways, static HTTP servers, service mesh sidecars, and lots of different functions. Pipy is in lively improvement and is maintained by full-time committers and contributors.

Most Popular

To Top