Giter VIP home page Giter VIP logo

orange's Introduction

Orange

Build Status license

中文 | English | Website

A Gateway based on OpenResty(Nginx + Lua) for API Monitoring and Management.

Install & Usages

Install of Production Environment (Not Support macOS)

1) Install Dependencies

We recommend that you use luarocks to install Orange to reduce problems caused by dependency extensions in different operating system releases.

System dependencies (openresty, resty-cli, luarocks, etc.) necessary to install Orange on different operating systems, See: Install Dependencies Document.

2) Install Lor Framework

Check the official documentation for Lor Framework or execute the following command.

git clone https://github.com/sumory/lor.git
cd lor
sudo make install

3) Install Orange

curl -Lo install.sh https://raw.githubusercontent.com/orlabs/orange/master/install/install-orange.sh
sudo sh install.sh

After the installation process is completed, the output message orange 0.8-1 is now installed in /usr/local/orange/deps (license: MIT) indicates that the installation was successful.

4) Import MySQL

Requirements: MySQL Version 5.5+

  • Login to the MySQL client, create an orange database.

  • Import the data table (/usr/local/orange/conf/orange-v0.8.1.sql).

  • Modify the Orange configuration file (/usr/local/orange/conf/orange.conf) MySQL related configuration.

5) Start Orange

sudo orange start

After the Orange launches successfully, the dashboard and API Server are started:

  • Access Dashboard via http://localhost:9999.
  • Access API Server via http://localhost:7777.

At this point, Orange has all been installed and configured, please enjoy it.

Install of Development Environment (Not Support macOS)

1) Dependencies and Lor

Please use the Install Dependencies and Install Lor Framework methods in Install of Production Environment to install.

2) Install Orange

git clone https://github.com/orlabs/orange.git
cd orange
sudo make dev

After the installation process is completed, the output message Stopping after installing dependencies for orange-master 1.0-0 indicates that the installation was successful.

3) Import MySQL

Please use the Import MySQL methods in Install of Production Environment to import.

Note: Install Orange in Development Environment.

  • The MySQL Data Table file and the Orange Config file are located in the conf folder of the current project.

  • Import the data table (/usr/local/orange/conf/orange-master.sql).

4) Start Orange

sudo ./bin/orange start

Access method after the successful startup of Orange, please refer to: Start Orange in Install of Production Environment.

Usages

CLI tools

orange help to check usages:

Usage: orange COMMAND [OPTIONS]

The commands are:

start   Start the Orange Gateway
stop    Stop current Orange
reload  Reload the config of Orange
restart Restart Orange
store   Init/Update/Backup Orange store
version Show the version of Orange
help    Show help tips

Documents

Find more about Orange on its website. There is only a Chinese version for now.

Docker

https://store.docker.com/community/images/syhily/orange maintained by @syhily

Contributors

See also

The plugin architecture is highly inspired by Kong.

License

MIT

orange's People

Contributors

769344359 avatar easonfeng5870 avatar imocat avatar khlipeng avatar liulei18 avatar near-zhang avatar noname007 avatar oracle219 avatar roronoaly avatar shuaijinchao avatar spacewander avatar sumory avatar sydowma avatar syhily avatar tyzam avatar wang-xiaowu avatar wujunze avatar zhangbao0325 avatar zhjwpku avatar zhousoft avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

orange's Issues

配置多个选择器,多个规则,性能问题

经我们测试发现:
1.重写选择器配置多个,请求全部匹配
2.分流选择器配置多个,匹配后会跳出

我们选择器都是配置的url match,这样就都是是通过正则匹配的。
然后每个选择器又有多个规则,也是url match,也要正则匹配。

1.这样策略一多,每个请求就经过了很多 正则匹配, 是否会很损耗性能?

2.如果选择器只配一个,用全流量方式, 只在规则那匹配,这样会少了很多正则匹配,相对上面性能是否会好一些?

=============
我们有对orange做ab压测,发现相比openresty,未开启分流损耗性能20%,开启一条分流策略,性能损耗30%。

很担心选择器和规则过多,又都是正则匹配,性能会很差。

请问 你那边有相关的性能数据吗? 规则过多有什么好的优化方式?
我们打算选择器只用一个全流量。

谢谢!

More flexible Makefile

Add a configure script to generate Makefile.

  • Altenative prefix
  • Custom Openresty path
  • Custom Lor path
  • uninstall support

New restful URI variable extract directive

It's unquestionable, given this issue for urging multiply variables extractor from a request URI, that it's leaving a lot to be desired.

Yet, as demonstrated by the wide range of different ideas on how it should be improved, it's hard to find consensus on what 'better' actually looks like. Having looked through the discussions in QQ group, there are two broad categories into which the concerns fall into: the first is adding dyadic array for all variable extractors and the second is adding features for new rest template directive.

Dyadic array would increase complexity for configuration and process. But this would be the best solution for URI variable regular matching searching and would support a max range of URI.

So a new custom variable extract directives is a niche requirement, and we want to focus our efforts on making apps a possibility so that it is possible to add a brand new directive suits your use-case for getting multiply variables for a URI, rather than modify old ones. Therefore this issue exists purely to address a new restful URI extractor and a new directive. As for me, I would like to name them as Restful Template.

Old variable extractor using ${number} directive to identify expected variables. But Restful Template would using a new directive differ before. It is {variable name}.

There are two key things which makes this new directive quite desirable:

  1. It matches our request uri by predefined placeholders, and would easily assemble them with old directive ${number}.
  2. It reduce the complexity comparing to dyadic array.

I would give some restrictions on what this Restful Template could and couldn't.

  1. Every rewrite or redirect rule would only have one restful template extractor.
  2. If you using restful template extractor, your must configured it in condition judgement model by define your URI using restful template directive.
  3. {variable name} directive is equal to (.+).
  4. There are some predefined placeholders for this directive. Like queryString, head and body.
  5. variable name would be a unique word, it don't have any blank symbol and couldn't equal to predefined placeholder. (Because we would store the url variables by using your defined name as a key in url variable store.)

Predefined placeholder

  1. {queryString[variable]} means exist query string, if you leave the variable blank like {queryString}, this would append all query string from client.
  2. {head[variable]} would exactor header value.
  3. {body[variable]} would just get the variable from request body(Ajax, url encoded, multipart).

Best practice
We have a public restful api for user login:

Name Variables
GET /api/v3/account/login
Query String username={username}&password={password}&apiKey={apiKey}
Rewrite /dashboard/account?action=login&version=v3&username={username}&password={password}

This would be defined like:

"conditions": [
    {
        "type": "URI",
        "operator": "restful",
        "value": "/api/{version}/{service}/{action}"
    }
]

"handle": {
    "trim_qs": false,
    "url_tmpl": "/dashborad/{service}?action={action}&version={version}&{queryString}",
    "log": true
}

A real request could like:

GET    http://orange_request_host:8888/api/v3/account/login?username=syhily&password=123456&apiKey=312b3b123123ubcbjkh324

REWRITE    http://upstream_service_host:8080/dashborad/account?action=login&version=v3&username=syhily&password=123456&apiKey=312b3b123123ubcbjkh324

安装好orange后,全局监控里面没有任何数据

按照文档安装好orange之后,全局监控里看不到一点数据。

启动nginx的时候有如下报错,不知道会不会有影响:
2016/06/20 23:24:26 [error] 7939#0: [lua] config_loader.lua:21: load(): Load config file from ./orange.conf
2016/06/20 23:24:26 [error] 7939#0: [lua] file_store.lua:16: new(): load file store configurations... ./data.json

使用mysql存储错误

2016/07/06 12:44:09 [error] 19220#0: [lua] config_loader.lua:21: load(): Load config file from /opt/nginx/orange.conf
2016/07/06 12:44:09 [error] 19222#0: [lua] mysql_db.lua:35: query(): connected to mysql, reused_times:0 sql:select key, value from meta where key like "%.enable", context: ngx.timer
2016/07/06 12:44:09 [error] 19222#0: [lua] mysql_store.lua:48: query(): MySQLStore:query empty, sql:select key, value from meta where key like "%.enable", context: ngx.timer
2016/07/06 12:44:09 [error] 19222#0: [lua] mysql_db.lua:35: query(): connected to mysql, reused_times:1 sql:select value from monitor order by id asc, context: ngx.timer
2016/07/06 12:44:09 [error] 19222#0: [lua] mysql_store.lua:48: query(): MySQLStore:query empty, sql:select value from monitor order by id asc, context: ngx.timer
2016/07/06 12:44:09 [error] 19222#0: [lua] mysql_db.lua:35: query(): connected to mysql, reused_times:2 sql:select value from redirect order by id asc, context: ngx.timer
2016/07/06 12:44:09 [error] 19222#0: [lua] mysql_store.lua:48: query(): MySQLStore:query empty, sql:select value from redirect order by id asc, context: ngx.timer
2016/07/06 12:44:09 [error] 19222#0: [lua] mysql_db.lua:35: query(): connected to mysql, reused_times:3 sql:select value from rewrite order by id asc, context: ngx.timer
2016/07/06 12:44:09 [error] 19222#0: [lua] mysql_store.lua:48: query(): MySQLStore:query empty, sql:select value from rewrite order by id asc, context: ngx.timer
2016/07/06 12:44:09 [error] 19222#0: [lua] mysql_db.lua:35: query(): connected to mysql, reused_times:4 sql:select value from waf order by id asc, context: ngx.timer
2016/07/06 12:44:09 [error] 19222#0: [lua] mysql_store.lua:48: query(): MySQLStore:query empty, sql:select value from waf order by id asc, context: ngx.timer
2016/07/06 12:44:09 [error] 19222#0: [lua] mysql_db.lua:35: query(): connected to mysql, reused_times:5 sql:select value from divide order by id asc, context: ngx.timer
2016/07/06 12:44:09 [error] 19222#0: [lua] mysql_store.lua:48: query(): MySQLStore:query empty, sql:select value from divide order by id asc, context: ngx.timer

无法跳登录问题

orange.conf设为true后,登录界面一直进不去,出现502,

"dashboard": {
"auth": true,
"session_secret": "y0ji4pdj61aaf3f11c2e65cd2263d3e7e5",
"whitelist": [
"^/auth/login$",
"^/error/$"
]
},

启动是成功的

justhacker:orange jhp$ sudo orange restart
[INFO] Orange: 0.6.1
[INFO] ngx_lua: 10006
[INFO] nginx: 1011002
[INFO] Lua: LuaJIT 2.1.0-beta2
[INFO] Stop orange...
[INFO] args:
[INFO] ngx_conf:/usr/local/orange/conf/nginx.conf
[INFO] prefix:/usr/local/orange
[INFO] orange_conf:/usr/local/orange/conf/orange.conf
[INFO] args end.
[INFO] ORANGE_CONF=/usr/local/orange/conf/orange.conf nginx -p /usr/local/orange -c /usr/local/orange/conf/nginx.conf -s stop
[SUCCESS] Orange stoped.
[INFO] Start orange...
[INFO] args:
[INFO] ngx_conf:/usr/local/orange/conf/nginx.conf
[INFO] prefix:/usr/local/orange
[INFO] orange_conf:/usr/local/orange/conf/orange.conf
[INFO] args end.
[INFO] Start orange command execute.
[INFO] ORANGE_CONF=/usr/local/orange/conf/orange.conf nginx -p /usr/local/orange -c /usr/local/orange/conf/nginx.conf
[SUCCESS] Orange started.

看日志出现这个问题

2017/02/17 21:02:00 [error] 52233#0: *25 lua entry thread aborted: runtime error: content_by_lua(nginx.conf:165):3: loop or previous error loading module 'dashboard.main'
stack traceback:
coroutine 0:
[C]: in function 'require'
content_by_lua(nginx.conf:165):3: in function <content_by_lua(nginx.conf:165):1>, client: 127.0.0.1, server: localhost, request: "GET / HTTP/1.1", host: "localhost:9999"

启动报错:nginx: [error] init_by_lua error: init_by_lua:2: module 'orange.orange' not found

[root@localhost openresty-1.11.2.2]# orange start
[INFO] Orange: 0.6.2
[INFO] ngx_lua: 10007
[INFO] nginx: 1011002
[INFO] Lua: LuaJIT 2.1.0-beta2
[INFO] args:
[INFO]  ngx_conf:/usr/local/orange/conf/nginx.conf
[INFO]  prefix:/usr/local/orange
[INFO]  orange_conf:/usr/local/orange/conf/orange.conf
[INFO] args end.
[INFO] Start orange command execute.
[INFO] ORANGE_CONF=/usr/local/orange/conf/orange.conf nginx -p /usr/local/orange -c /usr/local/orange/conf/nginx.conf
nginx: [error] init_by_lua error: init_by_lua:2: module 'orange.orange' not found:
        no field package.preload['orange.orange']
        no file '../orange/orange.lua'
        no file '/usr/local/lor/orange/orange.lua'
        no file '/usr/local/openresty/site/lualib/orange/orange.lua'
        no file '/usr/local/openresty/site/lualib/orange/orange/init.lua'
        no file '/usr/local/openresty/lualib/orange/orange.lua'
        no file '/usr/local/openresty/lualib/orange/orange/init.lua'
        no file './orange/orange.lua'
        no file '/usr/local/openresty/luajit/share/luajit-2.1.0-beta2/orange/orange.lua'
        no file '/usr/local/share/lua/5.1/orange/orange.lua'
        no file '/usr/local/share/lua/5.1/orange/orange/init.lua'
        no file '/usr/local/openresty/luajit/share/lua/5.1/orange/orange.lua'
        no file '/usr/local/openresty/luajit/share/lua/5.1/orange/orange/init.lua'
        no file '/usr/local/openresty/site/lualib/orange/orange.so'
        no file '/usr/local/openresty/lualib/orange/orange.so'
        no file './orange/orange.so'
        no file '/usr/local/lib/lua/5.1/orange/orange.so'
        no file '/usr/local/openresty/luajit/lib/lua/5.1/orange/orange.so'
        no file '/usr/local/lib/lua/5.1/loadall.so'
        no file '/usr/local/openresty/site/lualib/orange.so'
        no file '/usr/local/openresty/lualib/orange.so'
        no file './orange.so'
        no file '/usr/local/lib/lua/5.1/orange.so'
        no file '/usr/local/openresty/luajit/lib/lua/5.1/orange.so'
        no file '/usr/local/lib/lua/5.1/loadall.so'
stack traceback:
        [C]: in function 'require'
        init_by_lua:2: in main chunk

image

集群部署问题

目前支持集群部署吗?我研究了下,好像一些限制都是基于单机的

关于dashboard的2点改进意见

1、全局统计中的address应为服务器的IP地址,不应该是IP地址和端口号
2、uptime应该为持续运行时间,不应该是启动时的那个时刻时间

New admin dashboard

Create a spa dashboard, remove lor template. This would reduce the dependency of lua and get better performance. Also, it's easy to deploy and test.

分流条件匹配规则疑问

  1. 设置 Header , [cookie] [not match] svn_test 然后转发到主机1上
    在使用curl测试的时候,默认是没有cookie这个Header,导致代码中获取到nil
    if condition_type == "URI" then
    real = ngx.var.uri
    elseif condition_type == "Query" then
    local query = ngx.req.get_uri_args()
    real = query[condition.name]
    elseif condition_type == "Header" then
    local headers = ngx.req.get_headers()
    real = headers[condition.name] ---- ******* 这个地方real值获取到的是nil
    elseif condition_type == "IP" then

===============
local function assert_condition(real, operator, expected)
if not real then
ngx.log(ngx.ERR, string_format("assert_condition error: %s %s %s", real, operator, expected))
return false
end
然后执行到了这里.

关于rewrite的问题

我的请求是这样的http://hostA/orange/say/hello, 我想通过rewrite模块将url地址重写成http://hostB/say/hello,(hostA变成hostB,并且去掉orange),但是我在配置了rewrite之后一直是404,后来看了代码发现orange的urlrewrite功能并不能改变host,我的变量提取配置的是(orange),处理配置的是${1},请问下orange的重定向是否无法满足我的这种需求??或者有其他的组合配置方式?

post请求怎么重定向或者代理?

使用post请求的化,重定向不允许,使用代理的化,没法接收post请求的参数。请问怎么处理?
使用nginx的反向代理可以用post请求,在orange怎么配置反向代理?

upstream支持keepalive

大拿,你好,orange做API网关时,新增API时,upstream能否增加keepalive参数啊

Distribution package for orange

  1. Homebrew formula. (I would like to work on this part.)
  2. Linux RPM distribution.
  3. Docker distribution.
  4. Mac pkg distribution.
  5. Luarocks rockspec distribution.

Use serf to update cluster's configuration cache, and easy monitor

Intro: https://www.serfdom.io/intro

Let's introduce a new sync flow for orange cluster.

Modify (plugin configuration) -> Test (Passed) -> Push to Data Store (MySQL) -> Notify to all orange cluster (using serf) -> Pull configuration from DB.

Rule

  1. Any modification will not save to database directly in case of misconfiguration.
  2. Configuration mainly stored and read from SHM except initialization.
  3. After successfully push to database orange should notify its child node to pull updation by using serf.
  4. Manual update operation should not be replaced by this auto sync flow.

launch error

1.install success openresty/luajit ,ln -s nginx , and ln -s luajit
2.install lor
2.user by root
sh start.sh , show error

2016/06/20 23:24:26 [error] 7939#0: [lua] config_loader.lua:21: load(): Load config file from ./orange.conf
2016/06/20 23:24:26 [error] 7939#0: [lua] file_store.lua:16: new(): load file store configurations... ./data.json

:) thx

dashboard首页500错误

dashboard首页在orange跑一段时间后就会出现500错误。

o1

错误日志:
2016/12/19 09:47:19 [error] 28429#0: *195 [lua] server.lua:72: fn(): /usr/local/orange/dashboard/routes/dashboard.lua:74: bad argument #1 to 'ipairs' (table expected, got nil)
stack traceback:
/usr/local/lor/lor/lib/router/layer.lua:120: in function </usr/local/lor/lor/lib/router/layer.lua:119>
[C]: in function 'ipairs'
/usr/local/orange/dashboard/routes/dashboard.lua:74: in function 'fn'
/usr/local/lor/lor/lib/router/layer.lua:118: in function </usr/local/lor/lor/lib/router/layer.lua:117>
[C]: in function 'xpcall'
/usr/local/lor/lor/lib/router/layer.lua:117: in function 'handle_request'
/usr/local/lor/lor/lib/router/route.lua:93: in function 'next'
/usr/local/lor/lor/lib/router/route.lua:97: in function 'fn'
/usr/local/lor/lor/lib/router/layer.lua:118: in function </usr/local/lor/lor/lib/router/layer.lua:117>
[C]: in function 'xpcall'
/usr/local/lor/lor/lib/router/layer.lua:117: in function 'handle_request'
/usr/local/lor/lor/lib/router/router.lua:194: in function 'next'
/usr/local/lor/lor/lib/router/router.lua:214: in function 'fn'
/usr/local/lor/lor/lib/router/layer.lua:118: in function </usr/local/lor/lor/lib/router/layer.lua:117>
[C]: in function 'xpcall'
/usr/local/lor/lor/lib/router/layer.lua:117: in function 'handle_request'
/usr/local/lor/lor/lib/router/router.lua:205: in function 'next'
/usr/local/lor/lor/lib/router/router.lua:214: in function 'handle'
/usr/local/lor/lor/lib/application.lua:95: in function 'handle'
/usr/local/lor/lor/lib/application.lua:54: in function 'run'
content_by_lua(dashboard.conf:28):4: in function <content_by_lua(dashboard.conf:28):1>,

Better configuration allocation

  1. Remove file store.
  2. Add PostgreSQL support, pgmoon would be a good choice.
  3. Join orange.conf and nginx.conf together, a yml file would be a good chocie, just like kong.

./orange/utils/handle.lua:5: module 'resty.template' not found:

➜ orange git:(master) ✗ sh start.sh
start orange..
nginx: [error] [lua] orange.lua:159: init(): Startup error: ./orange/utils/utils.lua:170: ./orange/utils/handle.lua:5: module 'resty.template' not found:
no field package.preload['resty.template']
no file '../resty/template.lua'
no file '/usr/local/lor/resty/template.lua'
no file '/usr/local/openresty/site/lualib/resty/template.lua'
no file '/usr/local/openresty/site/lualib/resty/template/init.lua'
no file '/usr/local/openresty/lualib/resty/template.lua'
no file '/usr/local/openresty/lualib/resty/template/init.lua'
no file './resty/template.lua'
no file '/usr/local/openresty/luajit/share/luajit-2.1.0-beta2/resty/template.lua'
no file '/usr/local/share/lua/5.1/resty/template.lua'
no file '/usr/local/share/lua/5.1/resty/template/init.lua'
no file '/usr/local/openresty/luajit/share/lua/5.1/resty/template.lua'
no file '/usr/local/openresty/luajit/share/lua/5.1/resty/template/init.lua'
no file '/usr/local/openresty/site/lualib/resty/template.so'
no file '/usr/local/openresty/lualib/resty/template.so'
no file './resty/template.so'
no file '/usr/local/lib/lua/5.1/resty/template.so'
no file '/usr/local/openresty/luajit/lib/lua/5.1/resty/template.so'
no file '/usr/local/lib/lua/5.1/loadall.so'
no file '/usr/local/openresty/site/lualib/resty.so'
no file '/usr/local/openresty/lualib/resty.so'
no file './resty.so'
no file '/usr/local/lib/lua/5.1/resty.so'
no file '/usr/local/openresty/luajit/lib/lua/5.1/resty.so'
no file '/usr/local/lib/lua/5.1/loadall.so'

启动报错:nginx: [emerg] unknown directive "stub_status" in /usr/local/orange/conf/nginx.conf:139

[root@localhost chenghui]# orange start
[INFO] Orange: 0.6.2
[INFO] ngx_lua: 10007
[INFO] nginx: 1011002
[INFO] Lua: LuaJIT 2.1.0-beta2
[INFO] args:
[INFO] ngx_conf:/usr/local/orange/conf/nginx.conf
[INFO] prefix:/usr/local/orange
[INFO] orange_conf:/usr/local/orange/conf/orange.conf
[INFO] args end.
[INFO] Start orange command execute.
[INFO] ORANGE_CONF=/usr/local/orange/conf/orange.conf nginx -p /usr/local/orange -c /usr/local/orange/conf/nginx.conf
nginx: [emerg] unknown directive "stub_status" in /usr/local/orange/conf/nginx.conf:139

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.