Git Product home page Git Product logo

node-celery-ts's Introduction

Status

NPM MaintainabilityTest Coverage

Description

node-celery-ts is a Celery client for Node.js written in TypeScript. node-celery-ts supports RabbitMQ and Redis result brokers and RPC (over RabbitMQ) and Redis result backends. node-celery-ts provides higher performance than Celery on PyPy and provides greater feature support than node-celery, including Redis Sentinel and Cluster, RPC result backends, YAML serialization, zlib task compression, and Promise-based interfaces. node-celery-ts uses amqplib and ioredis for RabbitMQ and Redis, respectively. node-celery-ts does not support Amazon SQS or Zookeeper message brokers, nor does it support SQLAlchemy, Memcached, Cassandra, Elasticsearch, IronCache, Couchbase, CouchDB, filesystem, or Consul result backends.

Usage

Basic

import * as Celery from "celery-ts";

const client: Celery.Client = Celery.createClient({
	brokerUrl: "amqp://localhost",
	resultBackend: "redis://localhost",
});

const task: Celery.Task<number> = client.createTask<number>("tasks.add");
const result: Celery.Result<number> = task.applyAsync({
	args: [0, 1],
	kwargs: { },
});

const promise: Promise<number> = result.get();

promise.then(console.log)
	.catch(console.error);

Advanced

import * as Celery from "celery-ts";

const id = "7a5b72ab-03d1-47d9-8a9d-54af7c26bd59";
const brokers: Array<Celery.MessageBroker> = [
	Celery.createBroker("amqp://localhost"),
];
const backend: Celery.ResultBackend = Celery.createBackend("redis://localhost");

const client: Celery.Client = new Celery.Client({
	backend,
	brokers,
	id,
});

Message Broker Failover

const id = "7a5b72ab-03d1-47d9-8a9d-54af7c26bd59";
const brokers: Array<Celery.MessageBroker> = [
	Celery.createBroker("amqp://localhost"),
	Celery.createBroker("amqp://localhost:5673"),
];
const backend: Celery.ResultBackend = Celery.createBackend("redis://localhost");

const failoverStrategy: Celery.FailoverStrategy = (
	brokers: Array<Celery.MessageBroker>,
): Celery.MessageBroker => {
	return brokers[Math.floor(Math.random() * 2)];
};

const client: Celery.Client = new Celery.Client({
	backend,
	brokers,
	failoverStrategy,
	id,
});

Task Options

const client: Celery.Client = Celery.createClient({
	brokerUrl: "amqp://localhost",
	resultBackend: "redis://localhost",
});

const task: Celery.Task<number> = client.createTask<number>("tasks.add");
const result: Celery.Result<number> = task.applyAsync({
	args: [0, 1],
	compression: Celery.Compressor.Zlib,
	eta: new Date(Date.now() + 1000),
	expires: new Date(Date.now() + 5000),
	kwargs: { },
	serializer: Celery.Serializer.Yaml,
});

const promise: Promise<number> = result.get();

promise.then(console.log)
	.catch(console.error);

RabbitMQ

AmqpBroker

const options: Celery.AmqpOptions = {
	hostname: "localhost",
	protocol: "amqp",
};
const broker = new Celery.AmqpBroker(options);

RpcBackend

const id = "7a5b72ab-03d1-47d9-8a9d-54af7c26bd59";
const options: Celery.AmqpOptions = {
	hostname: "localhost",
	protocol: "amqp",
};
const backend = new Celery.RpcBackend(id, options);

Redis

RedisBackend and RedisBroker both accept a RedisOptions object, which is an interface that can be extended by the user to allow new creational patterns.

TCP

const tcp: RedisOptions = new Celery.RedisTcpOptions({
	host: "localhost",
	protocol: "redis",
});

Unix Socket

const socket: RedisOptions = new Celery.RedisSocketOptions({
	path: "/tmp/redis.sock",
	protocol: "redis+socket",
});

If you so desire, you may also provide options directly to ioredis when using a TCP or Unix Socket connection. See BasicRedisOptions for the full list.

Sentinel

const sentinel: RedisOptions = new Celery.RedisSentinelOptions({
	sentinels: [
		{ host: "localhost", port: 26379 },
		{ host: "localhost", port: 26380 },
	],
	name: "mymaster",
});

Cluster

const cluster: RedisOptions = new Celery.RedisClusterOptions({
	nodes: [
		{ host: "localhost", port: 6379 },
		{ host: "localhost", port: 6380 },
	],
});

Thanks

node-celery-ts was inspired by node-celery. Special thanks to Cameron Will for his guidance.

License

node-celery-ts is licensed under the BSD-3-Clause license.

node-celery-ts's People

Contributors

cwill747 avatar dependabot[bot] avatar equlnox avatar gregory-meyer avatar kimahriman avatar stevemart 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

node-celery-ts's Issues

Celery Beat for node

This library seems very useful. But are there any support for celerybeat to schedule periodic tasks. In python there is celerybeat which could be used to schedule periodic tasks. If not what is the preferred way to do this in node ensuring that multiple app instances doesn't schedule duplicate tasks.

Documentation is not up to date

For example, Client does not document several of its options that it destructures, nor is the ClientOptions object that it destructures documented.

RedisBackend and RpcBackend crashes upon UUID collision

If RedisBackend or RpcBackend receive a task result message that has a matching UUID to a previously received message, an error will be thrown and the result message will not be saved into the backend. Since UUIDs are 128 bits, the risk of a collision is minimal, but still possible. If a collision occurs, a better strategy would be to overwrite the existing task message. This should be remedied modifying PromiseMap to overwrite existing keys instead of throwing an error. This more closely matches the behavior of an ES6 Map.

Query parsing uses a lot of duplicated code

There should be a generic QueryParser class, where T is the target interface. Users should be able to define rules for adding queries to parse and whatnot with minimal duplication, such that all users have to do is provide either a filter function, map function, or neither, for each parameter.

lodash should be used instead of underscore

lodash is overall a more modern, more consistent, and better maintained library compared to underscore. It offers pretty much the same functionality as underscore. When this issue is closed, lodash should be in package.json and underscore should not be.

How to remove a task?

I have some usage issues.

  1. How to remove a task in queue?
  2. How to get if a task is being queued for execution?
  3. How to get the total number of unexecuted tasks in the queue?

Project status

Hi! Thank you for all of your hard work on this project. Is the project currently maintained?

Incompatible node version

Hello Team. I can't install the package due to the below issue. I know that an obvious solution would be to downgrade node to somewhat closer version to 8 but I wouldn't want to downgrade node just for one package. Thank you.
error [email protected]: The engine "node" is incompatible with this module. Expected version "^8.11.3". Got "11.4.0"

Can't Send Tasks to Queue in Redis

I have a task that is registered to a specific queue.

In node-celery I am able to pass messages. However, unable to sent messages through node-celery-ts

const client = Celery.createClient({
  brokerUrl: `redis://localhost:6379`,
  resultBackend: `redis://localhost:6379`
});

client.createTask("src.queues.tasks.intakeTask").applyAsync({
  args: ["up and away"],
  kwargs: { },
  queue: "intake_queue"
});

Any thoughts on why this isn't working?

npm install does not work

I was looking for a more recent (maintained) version of node-celery and found your project.

However installation is broken because you did not publish the dist folder but only the src folder to npm. It works if I manually run cd node_modules/celery-ts && npm install && npm run build.

Probably a prepublish trigger is missing?

change default encoding to Base64

  1. In python celery, the default encoding without any compression is base64. So we need to switch default behavior.
  2. Maybe add an option to switch encoding, without relying on the compression option on applyAsync

I have this problem https://github.com/rusty-celery/rusty-celery expects only base64 encoded JSON body, and in this package, I can't switch to base64.

README.md has no benchmark data

While the readme claims that this library is faster than Celery running on PyPy, there is no proof presented of that claim. When this issue is closed, README.md should have lots of numbers and maybe even charts to back up that claim.

Waiting queue of ResourcePool is a leaky abstraction

The ResourcePool queue of waiting operations should probably not be the way it is, as the manual shift/pop instructions lend too much insight into the nature of abstraction. A better model might use a condition variable or ticket lock abstraction.

Incompatibility Issue between node-celery-ts (client) and node-celery (worker) due to Different Body Encoding Expectations

Hello everyone,

I have been encountering an issue while trying to use node-celery-ts as a client and node-celery as a worker. The problem arises due to a mismatch in the expected body encoding between the two.

In my setup, the node-celery-ts client sends a message with a body_encoding property set to "utf-8". However, the node-celery worker expects the body_encoding to be "base64". As a result, the worker throws an error "unsupported body encoding utf-8".

Here is a snippet of the worker code where the error is thrown:

// now supports only base64 of body_encoding
if (rawMsg.properties.body_encoding !== "base64") {
    throw new Error(`queue ${queue} item: unsupported body encoding ${rawMsg.properties.body_encoding}`);
}

Unfortunately, the node-celery-ts package does not seem to provide an option to change the body_encoding property.

I have considered several workarounds, such as modifying the worker code to accept "utf-8" encoding, using a different client or worker, or modifying the client code to send "base64" encoding. However, these are not ideal solutions.

Ideally, the node-celery-ts and node-celery packages should be compatible with each other. This might require changes to the packages themselves to ensure they can work together regardless of the body encoding.

Has anyone else encountered this issue? Any suggestions or solutions would be greatly appreciated.

Thank you!

Redis broker does not correctly emulate behavior from Celery

While node-celery merely uses LPUSH to publish, the genuine article uses some more complex methods for communication. This is the output of redis-cli monitor when sending a task and waiting for a worker to pick it up:

OK
1532120506.119506 [0 127.0.0.1:63627] "PING"
1532120506.120940 [0 127.0.0.1:63628] "MULTI"
1532120506.120957 [0 127.0.0.1:63628] "LLEN" "61b82fbe-228f-3e78-977c-8b6147c19b13"
1532120506.120964 [0 127.0.0.1:63628] "LLEN" "61b82fbe-228f-3e78-977c-8b6147c19b13\x06\x163"
1532120506.120983 [0 127.0.0.1:63628] "LLEN" "61b82fbe-228f-3e78-977c-8b6147c19b13\x06\x166"
1532120506.120989 [0 127.0.0.1:63628] "LLEN" "61b82fbe-228f-3e78-977c-8b6147c19b13\x06\x169"
1532120506.120996 [0 127.0.0.1:63628] "EXEC"
1532120506.129488 [0 127.0.0.1:63628] "MULTI"
1532120506.129513 [0 127.0.0.1:63628] "LLEN" "celery"
1532120506.129517 [0 127.0.0.1:63628] "LLEN" "celery\x06\x163"
1532120506.129520 [0 127.0.0.1:63628] "LLEN" "celery\x06\x166"
1532120506.129523 [0 127.0.0.1:63628] "LLEN" "celery\x06\x169"
1532120506.129527 [0 127.0.0.1:63628] "EXEC"
1532120506.129954 [0 127.0.0.1:63628] "SADD" "_kombu.binding.celery" "celery\x06\x16\x06\x16celery"
1532120506.130416 [0 127.0.0.1:63628] "LPUSH" "celery" "{\"body\": \"W1swLCAwXSwge30sIHsiY2FsbGJhY2tzIjogbnVsbCwgImVycmJhY2tzIjogbnVsbCwgImNoYWluIjogbnVsbCwgImNob3JkIjogbnVsbH1d\", \"content-encoding\": \"utf-8\", \"content-type\": \"application/json\", \"headers\": {\"lang\": \"py\", \"task\": \"tasks.add\", \"id\": \"186551b8-3f6d-4637-91cd-ac5ae3c43a74\", \"shadow\": null, \"eta\": null, \"expires\": null, \"group\": null, \"retries\": 0, \"timelimit\": [null, null], \"root_id\": \"186551b8-3f6d-4637-91cd-ac5ae3c43a74\", \"parent_id\": null, \"argsrepr\": \"(0, 0)\", \"kwargsrepr\": \"{}\", \"origin\": \"[email protected]\"}, \"properties\": {\"correlation_id\": \"186551b8-3f6d-4637-91cd-ac5ae3c43a74\", \"reply_to\": \"61b82fbe-228f-3e78-977c-8b6147c19b13\", \"delivery_mode\": 2, \"delivery_info\": {\"exchange\": \"\", \"routing_key\": \"celery\"}, \"priority\": 0, \"body_encoding\": \"base64\", \"delivery_tag\": \"a1dd7b7e-b5d8-4a19-88dc-65a81e46a9c7\"}}"
1532120506.137320 [0 127.0.0.1:63629] "PING"
1532120506.138652 [0 127.0.0.1:63630] "MULTI"
1532120506.138668 [0 127.0.0.1:63630] "LLEN" "61b82fbe-228f-3e78-977c-8b6147c19b13"
1532120506.138687 [0 127.0.0.1:63630] "LLEN" "61b82fbe-228f-3e78-977c-8b6147c19b13\x06\x163"
1532120506.138693 [0 127.0.0.1:63630] "LLEN" "61b82fbe-228f-3e78-977c-8b6147c19b13\x06\x166"
1532120506.138698 [0 127.0.0.1:63630] "LLEN" "61b82fbe-228f-3e78-977c-8b6147c19b13\x06\x169"
1532120506.138705 [0 127.0.0.1:63630] "EXEC"
1532120506.139224 [0 127.0.0.1:63629] "BRPOP" "61b82fbe-228f-3e78-977c-8b6147c19b13" "61b82fbe-228f-3e78-977c-8b6147c19b13\x06\x163" "61b82fbe-228f-3e78-977c-8b6147c19b13\x06\x166" "61b82fbe-228f-3e78-977c-8b6147c19b13\x06\x169" "1"
1532120507.143466 [0 127.0.0.1:63630] "SETNX" "unacked_mutex" "c51d23da-bfac-4865-856d-ade591da216a"
1532120507.143743 [0 127.0.0.1:63630] "TTL" "unacked_mutex"
1532120507.183269 [0 127.0.0.1:63629] "BRPOP" "61b82fbe-228f-3e78-977c-8b6147c19b13" "61b82fbe-228f-3e78-977c-8b6147c19b13\x06\x163" "61b82fbe-228f-3e78-977c-8b6147c19b13\x06\x166" "61b82fbe-228f-3e78-977c-8b6147c19b13\x06\x169" "1"
1532120508.215872 [0 127.0.0.1:63629] "BRPOP" "61b82fbe-228f-3e78-977c-8b6147c19b13" "61b82fbe-228f-3e78-977c-8b6147c19b13\x06\x163" "61b82fbe-228f-3e78-977c-8b6147c19b13\x06\x166" "61b82fbe-228f-3e78-977c-8b6147c19b13\x06\x169" "1"
1532120509.245445 [0 127.0.0.1:63629] "BRPOP" "61b82fbe-228f-3e78-977c-8b6147c19b13" "61b82fbe-228f-3e78-977c-8b6147c19b13\x06\x163" "61b82fbe-228f-3e78-977c-8b6147c19b13\x06\x166" "61b82fbe-228f-3e78-977c-8b6147c19b13\x06\x169" "1"
1532120510.270271 [0 127.0.0.1:63629] "BRPOP" "61b82fbe-228f-3e78-977c-8b6147c19b13" "61b82fbe-228f-3e78-977c-8b6147c19b13\x06\x163" "61b82fbe-228f-3e78-977c-8b6147c19b13\x06\x166" "61b82fbe-228f-3e78-977c-8b6147c19b13\x06\x169" "1"
1532120511.306186 [0 127.0.0.1:63629] "BRPOP" "61b82fbe-228f-3e78-977c-8b6147c19b13" "61b82fbe-228f-3e78-977c-8b6147c19b13\x06\x163" "61b82fbe-228f-3e78-977c-8b6147c19b13\x06\x166" "61b82fbe-228f-3e78-977c-8b6147c19b13\x06\x169" "1"
1532120512.326951 [0 127.0.0.1:63629] "BRPOP" "61b82fbe-228f-3e78-977c-8b6147c19b13" "61b82fbe-228f-3e78-977c-8b6147c19b13\x06\x163" "61b82fbe-228f-3e78-977c-8b6147c19b13\x06\x166" "61b82fbe-228f-3e78-977c-8b6147c19b13\x06\x169" "1"
1532120513.352350 [0 127.0.0.1:63629] "BRPOP" "61b82fbe-228f-3e78-977c-8b6147c19b13" "61b82fbe-228f-3e78-977c-8b6147c19b13\x06\x163" "61b82fbe-228f-3e78-977c-8b6147c19b13\x06\x166" "61b82fbe-228f-3e78-977c-8b6147c19b13\x06\x169" "1"
1532120514.382107 [0 127.0.0.1:63629] "BRPOP" "61b82fbe-228f-3e78-977c-8b6147c19b13" "61b82fbe-228f-3e78-977c-8b6147c19b13\x06\x163" "61b82fbe-228f-3e78-977c-8b6147c19b13\x06\x166" "61b82fbe-228f-3e78-977c-8b6147c19b13\x06\x169" "1"
1532120515.402292 [0 127.0.0.1:63629] "BRPOP" "61b82fbe-228f-3e78-977c-8b6147c19b13" "61b82fbe-228f-3e78-977c-8b6147c19b13\x06\x163" "61b82fbe-228f-3e78-977c-8b6147c19b13\x06\x166" "61b82fbe-228f-3e78-977c-8b6147c19b13\x06\x169" "1"
1532120516.426728 [0 127.0.0.1:63629] "BRPOP" "61b82fbe-228f-3e78-977c-8b6147c19b13" "61b82fbe-228f-3e78-977c-8b6147c19b13\x06\x163" "61b82fbe-228f-3e78-977c-8b6147c19b13\x06\x166" "61b82fbe-228f-3e78-977c-8b6147c19b13\x06\x169" "1"
1532120517.428688 [0 127.0.0.1:63630] "SETNX" "unacked_mutex" "7f7ae079-8379-4899-b71c-497ff206f3c3"
1532120517.428905 [0 127.0.0.1:63630] "TTL" "unacked_mutex"
1532120517.453165 [0 127.0.0.1:63629] "BRPOP" "61b82fbe-228f-3e78-977c-8b6147c19b13" "61b82fbe-228f-3e78-977c-8b6147c19b13\x06\x163" "61b82fbe-228f-3e78-977c-8b6147c19b13\x06\x166" "61b82fbe-228f-3e78-977c-8b6147c19b13\x06\x169" "1"
1532120518.484251 [0 127.0.0.1:63629] "BRPOP" "61b82fbe-228f-3e78-977c-8b6147c19b13" "61b82fbe-228f-3e78-977c-8b6147c19b13\x06\x163" "61b82fbe-228f-3e78-977c-8b6147c19b13\x06\x166" "61b82fbe-228f-3e78-977c-8b6147c19b13\x06\x169" "1"
1532120519.493303 [0 127.0.0.1:63629] "BRPOP" "61b82fbe-228f-3e78-977c-8b6147c19b13" "61b82fbe-228f-3e78-977c-8b6147c19b13\x06\x163" "61b82fbe-228f-3e78-977c-8b6147c19b13\x06\x166" "61b82fbe-228f-3e78-977c-8b6147c19b13\x06\x169" "1"
1532120520.522028 [0 127.0.0.1:63629] "BRPOP" "61b82fbe-228f-3e78-977c-8b6147c19b13" "61b82fbe-228f-3e78-977c-8b6147c19b13\x06\x163" "61b82fbe-228f-3e78-977c-8b6147c19b13\x06\x166" "61b82fbe-228f-3e78-977c-8b6147c19b13\x06\x169" "1"
1532120521.550498 [0 127.0.0.1:63629] "BRPOP" "61b82fbe-228f-3e78-977c-8b6147c19b13" "61b82fbe-228f-3e78-977c-8b6147c19b13\x06\x163" "61b82fbe-228f-3e78-977c-8b6147c19b13\x06\x166" "61b82fbe-228f-3e78-977c-8b6147c19b13\x06\x169" "1"
1532120522.585413 [0 127.0.0.1:63629] "BRPOP" "61b82fbe-228f-3e78-977c-8b6147c19b13" "61b82fbe-228f-3e78-977c-8b6147c19b13\x06\x163" "61b82fbe-228f-3e78-977c-8b6147c19b13\x06\x166" "61b82fbe-228f-3e78-977c-8b6147c19b13\x06\x169" "1"
1532120523.613762 [0 127.0.0.1:63629] "BRPOP" "61b82fbe-228f-3e78-977c-8b6147c19b13" "61b82fbe-228f-3e78-977c-8b6147c19b13\x06\x163" "61b82fbe-228f-3e78-977c-8b6147c19b13\x06\x166" "61b82fbe-228f-3e78-977c-8b6147c19b13\x06\x169" "1"
1532120524.636582 [0 127.0.0.1:63629] "BRPOP" "61b82fbe-228f-3e78-977c-8b6147c19b13" "61b82fbe-228f-3e78-977c-8b6147c19b13\x06\x163" "61b82fbe-228f-3e78-977c-8b6147c19b13\x06\x166" "61b82fbe-228f-3e78-977c-8b6147c19b13\x06\x169" "1"
1532120525.671950 [0 127.0.0.1:63629] "BRPOP" "61b82fbe-228f-3e78-977c-8b6147c19b13" "61b82fbe-228f-3e78-977c-8b6147c19b13\x06\x163" "61b82fbe-228f-3e78-977c-8b6147c19b13\x06\x166" "61b82fbe-228f-3e78-977c-8b6147c19b13\x06\x169" "1"
1532120526.702574 [0 127.0.0.1:63629] "BRPOP" "61b82fbe-228f-3e78-977c-8b6147c19b13" "61b82fbe-228f-3e78-977c-8b6147c19b13\x06\x163" "61b82fbe-228f-3e78-977c-8b6147c19b13\x06\x166" "61b82fbe-228f-3e78-977c-8b6147c19b13\x06\x169" "1"
1532120527.705308 [0 127.0.0.1:63630] "SETNX" "unacked_mutex" "661b8924-b263-44c9-ad8d-235900b85f53"
1532120527.705752 [0 127.0.0.1:63630] "TTL" "unacked_mutex"
1532120527.732688 [0 127.0.0.1:63629] "BRPOP" "61b82fbe-228f-3e78-977c-8b6147c19b13" "61b82fbe-228f-3e78-977c-8b6147c19b13\x06\x163" "61b82fbe-228f-3e78-977c-8b6147c19b13\x06\x166" "61b82fbe-228f-3e78-977c-8b6147c19b13\x06\x169" "1"
1532120528.766904 [0 127.0.0.1:63629] "BRPOP" "61b82fbe-228f-3e78-977c-8b6147c19b13" "61b82fbe-228f-3e78-977c-8b6147c19b13\x06\x163" "61b82fbe-228f-3e78-977c-8b6147c19b13\x06\x166" "61b82fbe-228f-3e78-977c-8b6147c19b13\x06\x169" "1"

Add ResourcePool#use

A frequently used pattern with ResourcePool is to take a resource, use it, then release it after a function is executed. ResourcePool#use would have the following interface:

public use<U>(f: (resource: T) => U | PromiseLike<U>): Promise<U>

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.