-
Notifications
You must be signed in to change notification settings - Fork 903
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
Conversation
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.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM +1
There was a problem hiding this 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?
Sure, I'll address the styles issues |
There was a problem hiding this 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); |
There was a problem hiding this comment.
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" ?
There was a problem hiding this comment.
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(); |
There was a problem hiding this comment.
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[]>
There was a problem hiding this comment.
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} |
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed.
bookkeeper-server/pom.xml
Outdated
<dependency> | ||
<groupId>org.apache.bookkeeper.http</groupId> | ||
<artifactId>http-server</artifactId> | ||
<version>4.5.0-SNAPSHOT</version> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
use ${project.version}
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed.
bookkeeper-server/pom.xml
Outdated
<scope>test</scope> | ||
</dependency> | ||
<dependency> | ||
<groupId>org.json</groupId> |
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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(); |
There was a problem hiding this comment.
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
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed.
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 { |
There was a problem hiding this comment.
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()".
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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: |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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; |
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yup, fixed.
@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. |
Another comment. My bad I did not think about it before. What about security? 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(); |
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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 |
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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) { |
There was a problem hiding this comment.
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:
- make at least port/address/protocol (http/https) configurable
- 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 ?
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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:
-
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 -
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
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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() { |
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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) { |
There was a problem hiding this comment.
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 ?
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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.
There was a problem hiding this 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
@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. |
@sijie for the "security". Yes I think it is not required and this time as httpserver will be turned off by default |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
…behavior, and add basic http server test cases
…behavior, and add basic http server test cases
@eolivelli @dcastor just to check if we are good to go with this pull request. |
Yes I am ok. Thank you for asking my ack. |
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
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
Add Bookkeeper Http Server as a new module [bookkeeper-http]
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
Add children module [vertx-http-server], which is a Vertx.io based http server implementation
Add children module [twitter-http-server], which is a TwitterServer based http server implementation
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.