Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

BOOKKEEPER-1100: Add module for Bookkeeper Http Endpoint #278

Closed
wants to merge 12 commits into from
Closed

BOOKKEEPER-1100: Add module for Bookkeeper Http Endpoint #278

wants to merge 12 commits into from

Conversation

yzang
Copy link

@yzang yzang commented Jul 24, 2017

Add Bookkeeper Http Server as a new module [bookkeeper-http]

  1. Add children module [http-server], which provide generic api for setting up a Http Server for bookkeeper, allowing to plugin different Http frameworks and inject the implementation of different BK related services to Http Handler to handle different endpoints

  2. Add children module [vertx-http-server], which is a Vertx.io based http server implementation

  3. Add children module [twitter-http-server], which is a TwitterServer based http server implementation

  4. Add a package or org.apache.bookkeeper.http in [bookkeeper-server], which include all the implementation for the services that handle different http endpoint request.


Be sure to do all of the following to help us incorporate your contribution
quickly and easily:

  • Make sure the PR title is formatted like:
    <Issue # or BOOKKEEPER-#>: Description of pull request
    e.g. Issue 123: Description ...
    e.g. BOOKKEEPER-1234: Description ...
  • Make sure tests pass via mvn clean apache-rat:check install findbugs:check.
  • Replace <Issue # or BOOKKEEPER-#> in the title with the actual Issue/JIRA number.

Yiming Zang added 4 commits June 24, 2017 21:10
Provide a general interface for HttpServer, which can be easily implemented by different HTTP frameworks.
This change include two implementations of HttpServer, one is TwitterServer and another one is Vertx.

Provide a general AbstractHandlerFactory, which is able to create handlers to handler different http apis,
and is able to be implemented for different HTTP frameworks.

Provide a service level classes, which include all the logics that http handlers will use.

Add test cases to test HttpServer
[http-server] is a child module which provide generic http server, http handlers and http service APIs.

[vertx-http-server] and [twitter-http-server] are two implementations of http server.
Copy link
Member

@sijie sijie left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM +1

Copy link
Member

@sijie sijie left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

actually I think there are a few checkstyle errors in twitter-http-server module. @yzang do you mind taking a look into it?

@yzang
Copy link
Author

yzang commented Jul 25, 2017

Sure, I'll address the styles issues

Copy link
Contributor

@eolivelli eolivelli left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Some comments.
Overall the approach is good.
I would like to try to implement a Servlet based http server.
I wonder if we really need to implement so many variants of the http server or simply implement one

String className = conf.getString(HTTP_SERVER_CLASS);
if (className != null) {
try {
Class cls = Class.forName(className);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we should use ReflectionUtils but I think it is not possible as it is in bookkeeper-server
@sijie should we create a new minor project "bookeeper-utils" ?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@eolivelli we should refactor the module layout, but I would defer that to next release.

public class ServiceRequest {
private String body;
private HttpServer.Method method = HttpServer.Method.GET;
private Map params = new HashMap();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it is better to declare explicit types of the param, maybe <String,String> o <String, String[]>

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed.

* the License.
*/
/**
* @TODO: Write JavaDoc comment {@link https://github.com/apache/bookkepeer/issues/247}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

link is broken, anyway we just fixed issue #247 so please add a minimal javadoc

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sure, I'll add some javadoc

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed.

<dependency>
<groupId>org.apache.bookkeeper.http</groupId>
<artifactId>http-server</artifactId>
<version>4.5.0-SNAPSHOT</version>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

use ${project.version}

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, good catch, I'll fix it.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed.

<scope>test</scope>
</dependency>
<dependency>
<groupId>org.json</groupId>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

please consider using http://json-b.net/ which is the current (brand new) standard for Java-JSON mapping.
org.json is really bad.
otherwise we could use Jackson Mapper, which is actually the best

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

sure, I'll try jsonb or jackson, thanks.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed.

public ServiceResponse handle(ServiceRequest request) {
ServiceResponse response = new ServiceResponse();
Map configMap = toMap(conf);
JSONObject jsonResponse = new JSONObject();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

see previous comment about not using org.json legacy library

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed.

@sijie
Copy link
Member

sijie commented Jul 25, 2017

"I wonder if we really need to implement so many variants of the http server or simply implement one"

different organizations might have different preferences, just like stats providers. so I do not see a problem for that. a simple implementation can be the default one.

* It also provide the interface to inject service provider,
* which is the implementation of services for http endpoints.
*/
public interface HttpServer {
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it'd be good to return a list of available endpoints here. That way you can check to see what all is available and its corresponding endpoint. Something like "getEndpoints()".

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are you suggesting adding a service to return all endpoints so that people can curl to see all possible endpoints, or just adding a function getEndpoints() in this interface? If we're just adding the function, when will we use that function?

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's do that in a future iteration. Nothing pressing. I think with so few services, it'd be fairly self explanatory.

It's basically akin to doing a "help" function in any other program -- including bookie shell help command. Just to be able to see what all is available on the current implementation.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Make sense, maybe we can add a endpoint in the future to query the list of available http endpoints in the future.

return HttpServer.Method.DELETE;
case PUT:
return HttpServer.Method.PUT;
default:
Copy link

@dcastor dcastor Jul 27, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could just omit first case GET and let it default.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch, fixed.

protected ServerConfiguration conf;

public ConfigurationService(ServerConfiguration conf) {
assert conf != null;
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we throw an exception instead?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we can consider using Preconditions.checkNull

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yup, fixed.

@sijie
Copy link
Member

sijie commented Jul 28, 2017

@eolivelli : @yzang already addressed your comments, can you review it again?

@dcastor : can you review @yzang 's comment? let's move this forward if possible.

@eolivelli
Copy link
Contributor

Another comment. My bad I did not think about it before. What about security?
We are exposing bookie config and so disk layout, this usually is seen as some sort of information disclosure useful for attacks.
I think that at this moment it is not really relevant and we can go on, httpserver is optional.

Please give me some other day and I will be back with my +1 soon

public class JsonUtil {

public static String toJson(Object object) throws ParseJsonException {
ObjectMapper mapper = new ObjectMapper();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Object Mapper is threadsafe and usually it can be kept as a static field. It cachea the structure of classes and so it is more performant to keep only one instance please use only a static field

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Make sense, will fix it

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed

}

/**
* Get whether the http server start with auto-recovery service or not
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

comment is not correct at 100%, this options applied both to the bookie and to the auto-recovery daemon

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch, I'll fix it

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed

* Port to listen on
* @return server configuration
*/
public ServerConfiguration setHttpServerPort(int port) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@yzang @sijie
I wonder if we need to make the "port" configurable, it is be a "specific" value for each implementation

If you want to let the bookie "compose" the base URL of the service you will need protocol + hostname + port, and not just the port. We can add a method to the HttpServer interface to make the Bookie query the httpserver for the actual endpoint URL

Ideas:

  1. make at least port/address/protocol (http/https) configurable
  2. leave the 'port' parameter to each specific httpserver implementation (drop it from ServerConfiguration)

An example: in my case I will run my bookie inside a JVM which alredy bundles its own Tomcat based server and I will implement an HttpServlet. The "configuration" of the actual http endpoint will not be known to the Bookie at all.

In this case the httpserver port attribute will be ignored and I know that my case will work, but now that we are introducing this new feature a would like to have only the minimal set of configuration options.

Thoughts ?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@eolivelli
For the first point, I kinda agree with you, but I'm still thinking about what's the benefit of doing that, do you minding sharing some examples of under when will expose and make port/address/protocol configurable be beneficial?

For the second point, it is already achieved in this change, the interface in HttpServer allows youto pass the port in when you start the http server. The ServerConfiguration is just another way to make it possible to get the port from configuration, which could be useful in some other use cases. For example, in Twitter, we don't have a static port available for HttpServer, because we use aurora as the scheduler, the http port is dynamically assigned by aurora scheduler, so the only way to get the port is from configuration.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@eolivelli typically in a containerized environment, port is assigned by the scheduler. being able to configure a port is required, but I am not sure about protocol and base url, those two settings are somehow beyond the scope for bookkeeper, because the goal for this isn't to implement a generic http framework.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@sijie @yzang I apologize I was not clear in my comment.
I think that there are two opposite ways:

  1. the implementation handles all of the parameters
    so Vertx, TwitterServer impl can have their own "port" parameter, there will be no 'standard' http port configuration parameter

  2. we define that there are the common 3 parameters for every httpserver implementation:

  • port (defaults to xxxx)
  • binding address (defaults to 0.0.0.0)
  • secure: true|false (default to false, maybe not implemented by vertx/twitter server)

maybe we can add 'secure' in a future time, but if we make configurable the port, let's add at least the 'binding address'.

if we make address/port/secure configurable with 'standard' attributes it will be easy to recompose the URLs of the API just by reading the configuration file

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@eolivelli we can take the second approach and agree on it.

but regarding the binding address, can we defer this to a separate pull request? because it would be good to re-use the binding address the bookie service, rather than introducing new settings. let's try to introduce settings when we really need it.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@sijie OK, I the address of the bookie service is already accessible from other machines and I think it is a very convenient 'default'. Let's stick to it now.

So I am OK with this patch

this.code = code;
}

public String getBody() {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it would be more future-proof not to return a java.lang.String.
IMHO It would be better to have these methods:

  • writeTo(OutputStream method) (or a getInputStream())
  • getContentType()
  • getContentLength()

If in the future maybe we will deal with large responses and storing them all in a a single String variable could be a problem

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think for the first version, maybe let's not make it that complicated? We currently don't expected very huge response (is larger than a single String can handle). We have an API in Twitter, which return all the ledgers that is underreplicated, and the largest response once return 300k ledgerIds, and it also works fine.

So I think for the current being, "String" is probably good enough, we can definitely extend the behavior in the future if we have more strong requirements. What do you think? @eolivelli

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

+1 for starting with simpler solution.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@yzang ok for the String version, it is simpler and will handle most of the first usecases

while (iterator.hasNext()) {
String key = iterator.next().toString();
List values = conf.getList(key);
if (values.size() > 0) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lists of more than one value ?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We usually expect there's only one value for each configuration property, so I think the value size won't be greater than one here.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think we will ever get value more than one.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@yzang want about using Object conf.getProperty(key) ?
in case of lists we will dump a list (which will be marshalled by the JSON encoder) and for every other kind of property we will get the same result as the current code by in a clearer way

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's a great idea, fixed in latest commit.

Copy link
Contributor

@eolivelli eolivelli left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have left some comments.
Overall is OK and I think it is a great feature
I would like to make some aspects clear, see the comments about the Response API and the 'port' configuration

@sijie
Copy link
Member

sijie commented Aug 1, 2017

@eolivelli the http port can be turned off by default. I don't see a strong point to include security at this point. we can come up with security supported in subsequent pull requests.

@eolivelli
Copy link
Contributor

@sijie for the "security". Yes I think it is not required and this time as httpserver will be turned off by default

Copy link
Contributor

@eolivelli eolivelli left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

+1 LGTM I think this is a great feature for BK ! Thank you @yzang @sijie

@sijie sijie added this to the 4.6.0 milestone Aug 1, 2017
Yiming Zang added 2 commits August 1, 2017 17:39
…behavior, and add basic http server test cases
…behavior, and add basic http server test cases
@sijie
Copy link
Member

sijie commented Aug 14, 2017

@eolivelli @dcastor just to check if we are good to go with this pull request.

@eolivelli
Copy link
Contributor

Yes I am ok. Thank you for asking my ack.
+1

@sijie sijie mentioned this pull request Aug 15, 2017
@sijie sijie closed this in de9eecc Aug 16, 2017
caliuf pushed a commit to caliuf/bookkeeper that referenced this pull request Aug 28, 2017
Add Bookkeeper Http Server as a new module [bookkeeper-http]

1. Add children module [http-server], which provide generic api for setting up a Http Server for bookkeeper, allowing to plugin different Http frameworks and inject the implementation of different BK related services to Http Handler to handle different endpoints

2. Add children module [vertx-http-server], which is a Vertx.io based http server implementation

3. Add children module [twitter-http-server], which is a TwitterServer based http server implementation

4. Add a package or org.apache.bookkeeper.http in [bookkeeper-server], which include all the implementation for the services that handle different http endpoint request.

Author: Yiming Zang <yzang@twitter.com>

Reviewers: Enrico Olivelli <eolivelli@gmail.com>, Sijie Guo <sijie@apache.org>

This patch had conflicts when merged, resolved by
Committer: Sijie Guo <sijie@apache.org>

This closes apache#278 from yzang/yzang/BOOKKEEPER-1100
jiazhai added a commit that referenced this pull request Oct 11, 2017
Descriptions of the changes in this PR:

#278 introduces BookKeeper Http Endpoint module. However there are only two endpoints, which is “/heartbeat” and “/api/config/serverConfig”. In order to fully leverage the http modules, The goal is to add more endpoints to this modules.
Please reference BP-17 for more details.

Author: Jia Zhai <zhaijia@apache.org>

Reviewers: Enrico Olivelli <eolivelli@gmail.com>

This closes #521 from zhaijack/http_endpoint, closes #520
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants