Giter VIP home page Giter VIP logo

light-4j's Introduction

A fast, lightweight and cloud-native microservices framework.

Stack Overflow | Google Group | Gitter Chat | Subreddit | Youtube Channel | Documentation | Contribution Guide |

Build Status codecov.io

Why call it Light-4J

Light means lightweight, lightning fast and shedding light on how to program with modern Java SE for cloud-native deployment.

Why am I doing it

I had been working on the Java EE platforms since early 2000 and suffered the performance and productivity issues. In 2014, I realized that the IT industry was moving from Monolithic to Microservices and from on-premise data centers to the public clouds.

To reduce the production cost for my applications, I need to find a lightweight platform that has a small memory footprint, high throughput, and low latency in Java. Java EE and Spring/Spring Boot are too heavy to be considered. Other lightweight Java platforms all have different issues on the separation between the business logic and technical cross-cutting concerns. Moreover, they are not cloud-native or designed for container/cloud.

Without finding anything suitable, I started to build my open-source platform light-4j on top of the Undertow HTTP core by following the principles. Because my approach is very radical, and it is against products from big corporations, I have been attacked by some entities online and offline constantly. However, I am so convinced that my approach is the future direction and spend all my effort to continue the development. Today, a lot of companies are using light-4j on production with very positive feedback. It further encourages me to devote myself to the framework and to extend the platform to build an ecosystem.

For users who benefit from the platform, you can become a sponsor for me so that I can put all my effort into the open-source projects.

Why use this framework

Fast and small memory footprint to lower production cost.

It is 44 times faster than the most popular microservices platform Spring Boot embedded Tomcat and use only 1/5 of memory. Here are the benchmark results compare with Spring Boot and other microservices frameworks. Here is the third party comparison with other Web frameworks.

Provide an embedded gateway to address cross-cutting concerns.

  • Plugin architecture for startup/shutdown hooks and middleware components
  • Distributed OAuth2 JWT security verification as part of the framework
  • Request and response validation against OpenAPI specification at runtime
  • Metrics collected in Influxdb/Prometheus and viewed from Grafana Dashboard for both services and clients
  • Global exception handling for runtime exception, API exception, and other checked exceptions
  • Mask sensitive data like the credit card, sin number, etc. before logging
  • Sanitize cross-site scripting for query parameters, request headers and body
  • Audit to dump important info or entire request and response.
  • Body parser to support different content types
  • Standardized response code and messages from the configuration file
  • Externalized configuration for all modules for the dockerized environment
  • CORS pre-flight handler for SPA (Angular or React) from another domain
  • Rate limiting for services that exposed outside to the Internet
  • Service registry and discovery support direct, Consul and Zookeeper
  • Client-side discovery and load balance to eliminate proxies
  • A client module that is tightly integrated with Light-OAuth2 and supports traceability

Design and Test driven development to increase productivity

Design OpenAPI specification and generate the service from it. The specification is also part of the framework to drive security verification and request validation at runtime.

Unit/End-to-End test stubs are generated to enable test driven approach for quality product.

Debugging within IDE just like standalone application for better developer productivity.

Built-in DevOps flow to support continuous integration to production

Dockerfile and DevOps supporting files are generated to support dockerization and continuous integration to production.

Multiple frameworks for different type of microservices

  • light-rest-4j is a RESTful microservice framework with OpenAPI specification for code generation and runtime security and validation
  • light-graphql-4j is a GraphQL microservice framework that supports schema generation from IDL and plugin.
  • light-hybrid-4j is a hybrid microservice framework that takes advantages of both monolithic and microservice architectures.
  • light-eventuate is a messaging based microservice framework based on Kafka, event sourcing and CQRS

Multiple languages support

All the open sourced frameworks are built in Java and we are working on Nodejs framework internally. In the future, we might provide Golang framework as well and all them are sharing the same eco-system and market place.

OAuth2 server, portal and services to form ecosystem

OAuth2 Server for security and Portal for production monitor and management. The portal is also a marketplace to link clients and services together.

Getting Started

There are two ways to start your project:

Light-codegen generator

You can use light-codegen to generate a working project. Currently, it supports light-rest-4j, light-graphql-4j, light-hybrid-server-4j and light-hybrid-service-4j. light-eventuate code generator is coming.

The light-codegen project README.md describes four ways to use the generator with examples.

  • Clone and build the light-codgen and use the codegen-cli command line utility
  • Use docker image networknt/light-codegen to run the codegen-cli command line utility
  • Use generate.sh from model-config repo to generate projects based on conventions.
  • Generate code from web site with codegen-web API. (API is ready but UI needs to be built)

Starting from an example project

The other way to start your project is to copy from light-example-4j.

You can find the description of these examples

Also, there are some tutorials

Debugging

To run/debug from IDE, you need to configure a Java application with main class "com.networknt.server.Server" and working directory is your project folder. There is no container and you are working on just a standalone Java application.

Start Server

In IDE

create a Java application that main class is com.networknt.server.Server and working directory is your project root folder. You can debug your server just like a POJO application.

From Maven

mvn exec:exec

Command Line

java -jar target/demo-0.1.0.jar

Stop Server

you can use Ctrl+C to kill the server but for production use the following command

kill -s TERM <pid>

The server has a shutdown hook and the above command allow it to clean up. For example, complete in-flight requests and close the database connections etc. If service registry and discovery is used, then the server will send shutdown event to service registry and keep processing requests for 30 seconds until all clients refreshes their local cache before shutting down.

Appreciation

License

Light-4j and all light-*-4j frameworks are available under the Apache 2.0 license. See the LICENSE file for more info.

light-4j's People

Contributors

balloonwen avatar ddobrin avatar dependabot[bot] avatar diogofkt avatar dz-1 avatar fortunadoralph avatar gavinchenyan avatar happyhacker123 avatar helloalbin avatar jaswalkiranavtar avatar jefperito avatar jelgun avatar jiachen1120 avatar jsu216 avatar kalevgonvick avatar kellyshao avatar lanphan avatar lkoolma avatar logi avatar miklish avatar nicholasazar avatar pre-commit-ci[bot] avatar rpinaa avatar ruslanys avatar saaivenkat avatar sachin-walia avatar santoshaherkar avatar sreeni1990 avatar stevehu avatar wswjwjccjlu 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

light-4j's Issues

JWT token verification with cached token and expire time

As most service to service calls with be secured by client credentials jwt token with 10 to 15 minutes expiration and signature verification is very expensive. In order to save some CPU cycles, it would be a good idea to put the token into a hashmap with value of expire time. The question is how to remove the expired token from the cache. Need to think though it.

Mask body in list or map format instead of string format

We have a mask module to mask sensitive data from header, query parameter, path parameter and body. It works with JSON object with JSON Path with configuration. With the introduction of Body module that parses body of application/json to list or map, we need to update the mask module to work with list and map. Regarding to traverse the list or map, here is an example.
https://github.com/networknt/light-java/blob/master/sanitizer/src/main/java/com/networknt/sanitizer/SanitizerHandler.java

Find the best location to inject server info to the routing handler

Light-Java is designed to work with swagger specification to generate the code and trigger scope verification in security and validation in validator. However, this make injection of common services with is not defined in the service swagger specification hard to be injected and by pass all the swagger validation. Need to find a way to inject server info which is the endpoint that output all information about components wired in and their configuration.

Implement GraphQL support with light-java-graphql

I read that GraphQL is a mid-term goal on the road map. I am interested in this.

From the docs I see that it seems to strongly encourage starting a project from a Swagger spec so I'm interested to understand how GraphQL support might be incorporated into this project (just at a high level)?

Exchange already complete in jwt token if scope is mismatched

21:06:04.611 [XNIO-1 I/O-11] ERROR c.n.exception.ExceptionHandler - Exception:
java.lang.IllegalStateException: UT000139: Exchange already complete
at io.undertow.server.HttpServerExchange.addExchangeCompleteListener(HttpServerExchange.java:920)
at com.networknt.audit.SimpleAuditHandler.handleRequest(SimpleAuditHandler.java:100)
at com.networknt.body.BodyHandler.handleRequest(BodyHandler.java:106)
at com.networknt.security.JwtVerifyHandler.handleRequest(JwtVerifyHandler.java:174)
at com.networknt.swagger.SwaggerHandler.handleRequest(SwaggerHandler.java:88)
at com.networknt.metrics.MetricsHandler.handleRequest(MetricsHandler.java:133)
at com.networknt.exception.ExceptionHandler.handleRequest(ExceptionHandler.java:53)
at io.undertow.server.handlers.SetHeaderHandler.handleRequest(SetHeaderHandler.java:90)
at io.undertow.server.Connectors.executeRootHandler(Connectors.java:207)
at io.undertow.server.protocol.http.HttpReadListener.handleEventWithNoRunningRequest(HttpReadListener.java:243)
at io.undertow.server.protocol.http.HttpReadListener.handleEvent(HttpReadListener.java:134)
at io.undertow.server.protocol.http.HttpReadListener.handleEvent(HttpReadListener.java:58)
at org.xnio.ChannelListeners.invokeChannelListener(ChannelListeners.java:92)
at org.xnio.conduits.ReadReadyHandler$ChannelListenerHandler.readReady(ReadReadyHandler.java:66)
at org.xnio.nio.NioSocketConduit.handleReady(NioSocketConduit.java:88)
at org.xnio.nio.WorkerThread.run(WorkerThread.java:559)

Adding kid to the header of the JWT token issued by light-java.

This is necessary to support multiple public key certificates on light-oauth2. Also, for the resource service to verify a in coming JWT token, it won't need to iterate every key available but just pick the right key to verify the token. If token verification module cannot find the right key locally, it will call the light-oauth2 key service to get the public key certificate by kid.

Config server not read

I have this cause (with version 1.2.7):
i've generated project by swagger, changed port in src/main/resources/config/server.json but port not change! Then i thought for change port need use param httpPort but it not help too.
Actually light-java framework has own server.yml (*.yml config is more priority) and my app not read my server.json

It's look like bug, is not?

Switch server and client to HTTP/2

With the new service registry, discovery, cluster and load balance, the client module need to be updated as route doesn't make any sense anymore as the http calls will be based on the real ip instead of domain and connection pool management becomes more complicated on per IP basis. When you are thinking about local first load balance, things are getting more complicated. To make it simpler, we are going to try HTTP/2 instead of HTTP/1.1 for service to service calls. These days, most browsers support HTTP/2 so for the APIs that serve SPA, we should be OK.

To make the above refactor:

  1. Enable server for HTTP/2
  2. Update client to use OKHTTP instead of apache httpclient
  3. Update client to server communication

service - support parameterized constructor instead of default constructor and then set properties

For beans with properties, currently create the bean with default constructor and then set properties with the map passed in from service.json using Java reflection. It works for most of the cases, but doesn't work if you want to construct other object with the parameters passed into the constructor. For example.

    private ZkClient zkClient;

    private String zkServers;
    private int sessionTimeout;
    private int connectionTimeout;

    public ZooKeeperClientImpl() {

    }

    public ZooKeeperClientImpl(String zkServers, int sessionTimeout, int connectionTimeout) {
        zkClient = new ZkClient(zkServers, sessionTimeout, connectionTimeout);
    }

Centralize configurations to light-config-server which is backed by git repositories

When a service start the first time, it goes to central config server to load configurations and then cached them locally. All subsequent startups will get config from config server but if the server is not available, use the local copy to start. The local copy is just a backup in case a black out recovery and thousands services start at the same time and over load the config server or config server is not up yet.

Timeout when looking up from consul with ecwid client

02:17:08.977 [Thread-5]  INFO  com.networknt.consul.ConsulRegistry - command data not change: serviceName=com.networknt.apic-1.0.0, command=: 
02:17:13.023 [Thread-2]  ERROR com.networknt.consul.ConsulRegistry - service lookup thread fail!
com.ecwid.consul.transport.TransportException: java.net.SocketTimeoutException: Read timed out
	at com.ecwid.consul.transport.AbstractHttpTransport.executeRequest(AbstractHttpTransport.java:96)
	at com.ecwid.consul.transport.AbstractHttpTransport.makeGetRequest(AbstractHttpTransport.java:55)
	at com.ecwid.consul.v1.ConsulRawClient.makeGetRequest(ConsulRawClient.java:81)
	at com.ecwid.consul.v1.health.HealthConsulClient.getHealthServices(HealthConsulClient.java:99)
	at com.ecwid.consul.v1.health.HealthConsulClient.getHealthServices(HealthConsulClient.java:80)
	at com.ecwid.consul.v1.ConsulClient.getHealthServices(ConsulClient.java:399)
	at com.networknt.consul.client.ConsulEcwidClient.lookupHealthService(ConsulEcwidClient.java:61)
	at com.networknt.consul.ConsulRegistry.lookupConsulService(ConsulRegistry.java:256)
	at com.networknt.consul.ConsulRegistry.lookupServiceUpdate(ConsulRegistry.java:214)
	at com.networknt.consul.ConsulRegistry.access$200(ConsulRegistry.java:20)
	at com.networknt.consul.ConsulRegistry$ServiceLookupThread.run(ConsulRegistry.java:334)
Caused by: java.net.SocketTimeoutException: Read timed out
	at java.net.SocketInputStream.socketRead0(Native Method)
	at java.net.SocketInputStream.socketRead(SocketInputStream.java:116)
	at java.net.SocketInputStream.read(SocketInputStream.java:170)
	at java.net.SocketInputStream.read(SocketInputStream.java:141)
	at org.apache.http.impl.conn.LoggingInputStream.read(LoggingInputStream.java:87)
	at org.apache.http.impl.io.SessionInputBufferImpl.streamRead(SessionInputBufferImpl.java:139)
	at org.apache.http.impl.io.SessionInputBufferImpl.fillBuffer(SessionInputBufferImpl.java:155)
	at org.apache.http.impl.io.SessionInputBufferImpl.readLine(SessionInputBufferImpl.java:284)
	at org.apache.http.impl.conn.DefaultHttpResponseParser.parseHead(DefaultHttpResponseParser.java:140)
	at org.apache.http.impl.conn.DefaultHttpResponseParser.parseHead(DefaultHttpResponseParser.java:57)
	at org.apache.http.impl.io.AbstractMessageParser.parse(AbstractMessageParser.java:261)
	at org.apache.http.impl.DefaultBHttpClientConnection.receiveResponseHeader(DefaultBHttpClientConnection.java:165)
	at org.apache.http.impl.conn.CPoolProxy.receiveResponseHeader(CPoolProxy.java:167)
	at org.apache.http.protocol.HttpRequestExecutor.doReceiveResponse(HttpRequestExecutor.java:272)
	at org.apache.http.protocol.HttpRequestExecutor.execute(HttpRequestExecutor.java:124)
	at org.apache.http.impl.execchain.MainClientExec.execute(MainClientExec.java:271)
	at org.apache.http.impl.execchain.ProtocolExec.execute(ProtocolExec.java:184)
	at org.apache.http.impl.execchain.RetryExec.execute(RetryExec.java:88)
	at org.apache.http.impl.execchain.RedirectExec.execute(RedirectExec.java:110)
	at org.apache.http.impl.client.InternalHttpClient.doExecute(InternalHttpClient.java:184)
	at org.apache.http.impl.client.CloseableHttpClient.execute(CloseableHttpClient.java:71)
	at org.apache.http.impl.client.CloseableHttpClient.execute(CloseableHttpClient.java:220)
	at org.apache.http.impl.client.CloseableHttpClient.execute(CloseableHttpClient.java:164)
	at org.apache.http.impl.client.CloseableHttpClient.execute(CloseableHttpClient.java:139)
	at com.ecwid.consul.transport.AbstractHttpTransport.executeRequest(AbstractHttpTransport.java:80)
	... 10 common frames omitted
02:17:13.139 [Thread-4]  ERROR com.networknt.consul.ConsulRegistry - service lookup thread fail!
com.ecwid.consul.transport.TransportException: java.net.SocketTimeoutException: Read timed out
	at com.ecwid.consul.transport.AbstractHttpTransport.executeRequest(AbstractHttpTransport.java:96)
	at com.ecwid.consul.transport.AbstractHttpTransport.makeGetRequest(AbstractHttpTransport.java:55)
	at com.ecwid.consul.v1.ConsulRawClient.makeGetRequest(ConsulRawClient.java:81)
	at com.ecwid.consul.v1.health.HealthConsulClient.getHealthServices(HealthConsulClient.java:99)
	at com.ecwid.consul.v1.health.HealthConsulClient.getHealthServices(HealthConsulClient.java:80)
	at com.ecwid.consul.v1.ConsulClient.getHealthServices(ConsulClient.java:399)
	at com.networknt.consul.client.ConsulEcwidClient.lookupHealthService(ConsulEcwidClient.java:61)
	at com.networknt.consul.ConsulRegistry.lookupConsulService(ConsulRegistry.java:256)
	at com.networknt.consul.ConsulRegistry.lookupServiceUpdate(ConsulRegistry.java:214)
	at com.networknt.consul.ConsulRegistry.access$200(ConsulRegistry.java:20)
	at com.networknt.consul.ConsulRegistry$ServiceLookupThread.run(ConsulRegistry.java:334)
Caused by: java.net.SocketTimeoutException: Read timed out
	at java.net.SocketInputStream.socketRead0(Native Method)
	at java.net.SocketInputStream.socketRead(SocketInputStream.java:116)
	at java.net.SocketInputStream.read(SocketInputStream.java:170)
	at java.net.SocketInputStream.read(SocketInputStream.java:141)
	at org.apache.http.impl.conn.LoggingInputStream.read(LoggingInputStream.java:87)
	at org.apache.http.impl.io.SessionInputBufferImpl.streamRead(SessionInputBufferImpl.java:139)
	at org.apache.http.impl.io.SessionInputBufferImpl.fillBuffer(SessionInputBufferImpl.java:155)
	at org.apache.http.impl.io.SessionInputBufferImpl.readLine(SessionInputBufferImpl.java:284)
	at org.apache.http.impl.conn.DefaultHttpResponseParser.parseHead(DefaultHttpResponseParser.java:140)
	at org.apache.http.impl.conn.DefaultHttpResponseParser.parseHead(DefaultHttpResponseParser.java:57)
	at org.apache.http.impl.io.AbstractMessageParser.parse(AbstractMessageParser.java:261)
	at org.apache.http.impl.DefaultBHttpClientConnection.receiveResponseHeader(DefaultBHttpClientConnection.java:165)
	at org.apache.http.impl.conn.CPoolProxy.receiveResponseHeader(CPoolProxy.java:167)
	at org.apache.http.protocol.HttpRequestExecutor.doReceiveResponse(HttpRequestExecutor.java:272)
	at org.apache.http.protocol.HttpRequestExecutor.execute(HttpRequestExecutor.java:124)
	at org.apache.http.impl.execchain.MainClientExec.execute(MainClientExec.java:271)
	at org.apache.http.impl.execchain.ProtocolExec.execute(ProtocolExec.java:184)
	at org.apache.http.impl.execchain.RetryExec.execute(RetryExec.java:88)
	at org.apache.http.impl.execchain.RedirectExec.execute(RedirectExec.java:110)
	at org.apache.http.impl.client.InternalHttpClient.doExecute(InternalHttpClient.java:184)
	at org.apache.http.impl.client.CloseableHttpClient.execute(CloseableHttpClient.java:71)
	at org.apache.http.impl.client.CloseableHttpClient.execute(CloseableHttpClient.java:220)
	at org.apache.http.impl.client.CloseableHttpClient.execute(CloseableHttpClient.java:164)
	at org.apache.http.impl.client.CloseableHttpClient.execute(CloseableHttpClient.java:139)
	at com.ecwid.consul.transport.AbstractHttpTransport.executeRequest(AbstractHttpTransport.java:80)
	... 10 common frames omitted
02:17:38.900 [Thread-3]  INFO  c.n.consul.client.ConsulEcwidClient - no command in serviceName: com.networknt.apib-1.0.0
02:17:38.901 [Thread-3]  INFO  com.networknt.consul.ConsulRegistry - command data not change: serviceName=com.networknt.apib-1.0.0, command=: 
InfluxDbReporter report is called.

Alternate Config impls ... S3, http?

The (default) com.networknt.config module implements a file-based config-properties system.
Any idea how hard it would be to re-implement using S3 or some other mechanism (like HTTP
to an external server)?

Add a system property to redirect jboss logs to slf4j

As Undertow is using jboss logging and we are using slf4j with logback in our framework, we cannot see any log from Undertow even errors. In order to make Undertow use our slf4j and logback config, we need to add a system property in the command line or just add a line in Server.java.

I think the second option is much better as we always need it.

        System.setProperty("org.jboss.logging.provider", "slf4j");

In order to capture all the exceptions in ExceptionHandler, dispatch in the exception handler as suggested by Bill O'Neil.

test: use random port

I'm a little concerned with http://localhost:8080/… being hard coded in the generated tests. I did have an issue where the tests can't run if the development server is running locally.

It seems like the TestServer class could assign a random port… and the tests could query TestServer.getInstance().getBaseUrl() or something.

At the least, the test server should run on a different port. (Maybe a src/test/resources/config… file will do this). I'm going to try that approach.

Unable to retrieve request body

In my code:
HttpServerExchange.getRequestReceiver().receiveFullBytes((exchange, data) -> { //do stuff with the data DATA IS EMPTY }, (exchange, exception) -> { //optional error handler } )
How can I get the body or the serialized JSON Object passed in my POST request? data is empty.
Thanks,
matt

quell “InfluxDbReporter report is called” ?

this doc says:

When you start a service, you might realized that every few minutes, “InfluxDbReporter report is called” will be shown up on the console. By default, all services will try to report metrics info to Influxdb and subsequently viewed from Grafana dashboard.

Is there a way to get InfluxDbReporter report is called to not show up on the console?

Token scope and spec scope mismatch error is not clear in logs

With service to service call, if token is not matched, the error message is not clear when service has the wrong token and what is the token scope and spec scope. Need to output service id and scopes to assist developers for debugging.

error response.

{"statusCode":403,"code":"ERR10005","message":"AUTH_TOKEN_SCOPE_MISMATCH","description":"Scopes in authorization token and spec scopes are not matched"}

Sanitizer mangles content

The Sanitizer is too aggressive and mangles content strings. I understand the intent, but had to disable it to retrieve the original strings sent in. In my case I am passing an XML document as a string.

Before disabling sanitizer: <?xml version=\"1.0\"?>
After disabling sanitizer: <?xml version="1.0"?>

In generated src/main/resources/META-INF/services/com.networknt.handler.MiddlewareHandler

#com.networknt.sanitizer.SanitizerHandler

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.