Wednesday, February 10, 2016

linkedin Rest.li



https://github.com/linkedin/rest.li/wiki/Rest.li-User-Guide
The Rest.li server framework consists of libraries that provide annotations and helper classes for describing your resources, as well as an inversion-of-control dispatcher that handles incoming requests and automatically invokes the appropriate methods in your resources.

Asynchronous APIs

Rest.li is built on simple asynchronous APIs. These APIs allow both servers to run in non-blocking event based frameworks and allow client code to be written to make non-blocking calls. This approach has a couple major benefits. On the server, it means that our servers can be leaner and scale to high request throughput because we don’t need large, per request, thread pools. On the client, it makes it easy to stitch together multiple requests to servers in sophisticated flows where independent calls can be made in parallel.
Rest.li’s client implementation is Netty-based and is designed to work seamlessly with ParSeq to construct complex asynchronous request flows.
There are several server implementations:
  • Servlet — Battle tested and ready for production use. Containers supporting Servlet 3.0 APIare required to benefit from asynchronous, non-blocking request processing. Jetty 8.x supports Servlet 3.0 and has been used in large production environments.
  • Netty — Experimental
  • Embedded Jetty — Primarily for integration testing as it’s trivial to spin up as part of a test suite
@RestLiCollection(name = "fortunes", namespace = "com.example.fortune")
public class FortunesResource extends CollectionResourceTemplate<Long, Fortune>
{
  /**
   * Gets a fortune for a random number.
   */
  @Override
  public Fortune get(Long key)
  {
    // retrieve data and return a Fortune object ...
  }
}
Resources providing the BATCH_GET resource method must override the following method signature:
public Map<K, V> batchGet(Set<K> ids);
Resources may also return BatchResult, which allows errors to be returned along with entities that were successfully retrieved.
Example of a batch get:
public BatchResult<Long, Greeting> batchGet(Set<Long> ids)
{
  Map<Long, Greeting> batch = new HashMap<Long, Greeting>();
  Map<Long, RestLiServiceException> errors = new HashMap<Long, RestLiServiceException>();
  for (long id : ids)
  {
    Greeting g = _db.get(id);
    if (g != null)
    {
      batch.put(id, g);
    }
    else
    {
      errors.put(id, new RestLiServiceException(HttpStatus.S_404_NOT_FOUND));
    }
  }
  return new BatchResult<Long, Greeting>(batch, errors);
}
Clients should make requests to a batch resource using buildKV() (not build(), it is deprecated), for example:
new FortunesBuilders().batchGet().ids(...).buildKV()
https://engineering.linkedin.com/restli/linkedins-restli-moment
Back 2002-2003 LinkedIn chose to use serialized Java objects over RPC as our communication mechanism. While this system made sense then, it does not in our current polyglot ecosystem with apps and services being written in Java, Node.js, Python, Ruby, and Scala.

Our mobile services are primarily written in Node.js, which had made it very hard to communicate with Java object based RPC services. Since Rest.li is simply JSON over HTTP our software engineers can now develop mobile applications at a much faster rate than before.

Our previous RPC system allowed for non-uniform interfaces, which meant that engineers had to potentially re-learn a new API each time they communicated with a different service. Since Rest.li is based on REST, this is not the case anymore, resulting in uniform service access semantics across LinkedIn.
Most LinkedIn Rest.li services use D2 for load balancing and dynamic discovery (though Rest.li does not depend on D2). Dynamic discovery frees us from having to configure URLs for each service that we need to talk to. D2 has allowed us to reduce the usage of hardware load balancers in our service deployments as the load balancing is now distributed amongst clients. Elimination of hardware load balancers also results in fewer network hops. Client side load balancing allows us to build service invocation features likescatter gather requestssending a request to all partitions of a cluster etc. easily. It also gives us the ability to do things like graceful degradation and call dropping to help services recover.
You as a developer implement the Resource classes in the server. Rest.li provides the platform code and infrastructure for dispatching and handling requests. It also generates the Record Templates and RequestBuilder classes:



  • R2, a REST transport layer abstraction in Java.
  • D2,  a dynamic discovery and client-side load balancing layer. It is interesting to see the use of Apache Zookeeper as their registry for services.
  • Rest.li, a Java framework for building REST-style services. It contains Java client libraries for use on both client and server side. The framework also specifies an IDL for describing REST resources.
https://engineering.linkedin.com/restli/restli-2x-and-protocol-upgrade-story
  • Changing our URL format to make the URLs more compact and easy to read and parse.
  • Changing our developer API to make it more consistent.
  • Removing code that has been marked as deprecated to simplify our codebase.
Rest.li 2.0 URL: /foo/(key:(x:List(a1,a2)),y:123,key.with.dots:val)
Rest.li 1.0 URL: /foo/key.x[0]=a1&key.x[1]=a2&key.y=123&key~2Ewith~2Edots=val

https://github.com/linkedin/rest.li/wiki/Asynchronous-Servers-and-Clients-in-Rest.li
  <async-supported>true</async-supported>
@RestMethod.Get
public void get(final Long id, @CallbackParam final Callback<Greeting> callback) {
  String path = "/data/" + id;
  // _zkClient is a regular ZooKeeper client
  _zkClient.getData(path, false, new DataCallback() {
    public void processResult(int i, String s, Object o, byte[] b, Stat st) {
      if (b.length == 0) {
        callback.onError(new RestLiServiceException(HttpStatus.S_404_NOT_FOUND));
      }
      else {
        callback.onSuccess(buildGreeting(b));
      }
    }
  }, null);
}

Callback<Response<Greeting>> cb = new Callback() {
  void onSuccess(Response<Greeting> response) {
    // do something with the returned Greeting
  }
  void onError(Throwable e) {
    // whoops
  }
}

Request<Greeting> getRequest = BUILDERS.get().id(1L).build();

_restClient.sendRequest(getRequest, new RequestContext(), cb);

http://www.slideshare.net/jpbetz/introduction-to-restli
Step 1. Write a Data Schema
Fortune.pdsc
{
"name" : "Fortune",
"namespace" : "com.example",
"type" : "record",
"fields" : [
{ "name" : "message", "type" : "string" }
]
}
Rest.li schemas are designed for JSON.
Rest.li’s automatically generates a binding class from our data schema:
@RestLiCollection(name = "fortunes")
class FortunesResource implements KeyValueResource<Long, Fortune> { !
@RestMethod.GET
  public Fortune get(Long key) {
  return new Fortune().setMessage("Today’s your lucky day!");
  }
}
From our resource implementation,
Rest.li’s automatically generates an interface definition, and client bindings.fortunes.restspec.json
{
“path”: “/fortunes”,
“supports”: [ “get” ],
}
Step 3. Write a Client
Response response = restClient.sendRequest(new FortunesBuilders.get().id(1L)).get();
Fortune fortune = response.getEntity();
But wait, this request is blocking! The .get() forces the
thread block and wait for a response from the server. We’ll show how to make this non-blocking shortly.

Importance of async, non-blocking request
handling
Async processing is ideal for making multiple requests to backend systems in parallel and then
composing the results of those parallel requests into a single response. For modern internet
architectures, where many backend systems are involved in handling each request from a consumer,
making calls in parallel to these backend systems dramatically reduces the overall time to response
back to the consumer.
Servers running async code scale better because they can handle very large numbers of
concurrent requests. This is because, when you write async code, no threads are blocked waiting
for something to complete. If you don’t write async code and you need to handle large numbers of
concurrent requests, you’ll need one thread per concurrent request. Each thread takes up
considerable memory, and when you run out of memory (or max out a thread pool), the server is
unable to take on more requests, resulting in timed out requests and in cases of many complex
architectures, cascading failure.

Async in Rest.li
Rest.li integrates with ParSeq for both client and server side
async.
Using ParSeq, we will write Tasks. Tasks can be composed
together in parallel (par) or sequence (seq) into larger tasks.
Tasks are executed asynchronously by the ParSeq engine.

@RestLiCollection(name = "fortunes")
class FortunesResource
implements KeyValueResource<Integer, Fortune> { !
@RestMethod.GET
public Task<Fortune> get(Long key) {
Task<String> retrieveFortuneStr = … ; !
Task<Fortune> buildFortune =
Tasks.action("getFortune", new Callable<Fortune>() {
@Override public Fortune call() throws Exception {
return new Fortune().setMessage(retieveFortuneStr.get());
}
});
return Tasks.seq(retrieveFortuneStr, buildFortune);
}
}

Async Client
Task<Response<Fortune>> getFortune =
parSeqClient.createTask(new FortunesBuilders.get().id(1L)); !
final Task<Void> printFortune =
Tasks.action("printFortune", new Runnable() {
@Override public void run() {
Response<Fortune> response = getFortune.get();
Fortune fortune = getResponseEntity();
System.out.println(fortune.getMessage());
}
}); !
engine.run(Tasks.seq(getFortune, printFortune)); !
printFortune.await();

https://github.com/linkedin/rest.li/wiki/Dynamic-Discovery
Dynamic Discovery (D2) is a layer of indirection similar to DNS for the rest.li framework. Functionally speaking, D2 translates a URI like d2://<my d2 service> to another address likehttp://myD2service.something.com:9520/someContextPath.
Rest.li uses D2 to help decouple a REST resource from the real address of the resource. D2 also acts as a client side load balancer.
Note that D2 is an optional layer in the rest.li framework. In fact, rest.li client can communicate directly to rest.li servers without D2. But D2 provides nice benefits like partitioning, load balancing, and many others. Vice versa, D2 can also be used outside of the rest.li framework. Because in essence D2 is just a name service and a load balancer.
Running D2 requires a Zookeeper ensemble running somewhere.
all the load balancing happened in the client side. D2 client keep tracks of the health of the cluster.
There are 2 types of mode that we can use to load balance traffic.
  • Shifting traffic away from one server to the other a.k.a LOAD_BALANCING
  • Dropping request on the floor a.k.a CALL_DROPPING.


Labels

Review (572) System Design (334) System Design - Review (198) Java (189) Coding (75) Interview-System Design (65) Interview (63) Book Notes (59) Coding - Review (59) to-do (45) Linux (43) Knowledge (39) Interview-Java (35) Knowledge - Review (32) Database (31) Design Patterns (31) Big Data (29) Product Architecture (28) MultiThread (27) Soft Skills (27) Concurrency (26) Cracking Code Interview (26) Miscs (25) Distributed (24) OOD Design (24) Google (23) Career (22) Interview - Review (21) Java - Code (21) Operating System (21) Interview Q&A (20) System Design - Practice (20) Tips (19) Algorithm (17) Company - Facebook (17) Security (17) How to Ace Interview (16) Brain Teaser (14) Linux - Shell (14) Redis (14) Testing (14) Tools (14) Code Quality (13) Search (13) Spark (13) Spring (13) Company - LinkedIn (12) How to (12) Interview-Database (12) Interview-Operating System (12) Solr (12) Architecture Principles (11) Resource (10) Amazon (9) Cache (9) Git (9) Interview - MultiThread (9) Scalability (9) Trouble Shooting (9) Web Dev (9) Architecture Model (8) Better Programmer (8) Cassandra (8) Company - Uber (8) Java67 (8) Math (8) OO Design principles (8) SOLID (8) Design (7) Interview Corner (7) JVM (7) Java Basics (7) Kafka (7) Mac (7) Machine Learning (7) NoSQL (7) C++ (6) Chrome (6) File System (6) Highscalability (6) How to Better (6) Network (6) Restful (6) CareerCup (5) Code Review (5) Hash (5) How to Interview (5) JDK Source Code (5) JavaScript (5) Leetcode (5) Must Known (5) Python (5)

Popular Posts