Java 有处理REST客户机服务器列表的框架吗?
其思想是可以使用REST服务器列表配置REST客户端。因此,服务器列表将以循环方式在REST客户机上循环 e、 g.REST客户端应用程序。我将配置服务器列表(REST\u服务器1、REST\u服务器2、REST\u服务器3)Java 有处理REST客户机服务器列表的框架吗?,java,rest,restlet,rest-client,Java,Rest,Restlet,Rest Client,其思想是可以使用REST服务器列表配置REST客户端。因此,服务器列表将以循环方式在REST客户机上循环 e、 g.REST客户端应用程序。我将配置服务器列表(REST\u服务器1、REST\u服务器2、REST\u服务器3) 我搜索了太多,找不到支持此功能的适当框架 >害怕添加一组组件来复杂化应用程序设置,我可能会考虑一个快速、肮脏、纯java的解决方案。 下面是我使用Spring的REST模板想到的一些有趣的东西。如果您对拦截器、方面和其他可以封装方法调用的东西感到满意,那么您可以应用这些原
我搜索了太多,找不到支持此功能的适当框架 >害怕添加一组组件来复杂化应用程序设置,我可能会考虑一个快速、肮脏、纯java的解决方案。 下面是我使用Spring的REST模板想到的一些有趣的东西。如果您对拦截器、方面和其他可以封装方法调用的东西感到满意,那么您可以应用这些原则来封装所有各种REST模板REST调用。见
import org.junit.Test;
导入org.springframework.http.HttpMethod;
导入org.springframework.web.client.*;
导入org.springframework.web.util.UriTemplate;
导入org.springframework.web.util.UriUtils;
导入java.io.UnsupportedEncodingException;
导入java.net.URI;
导入java.net.URISyntaxException;
导入java.util.array;
导入java.util.Collection;
导入java.util.Iterator;
导入java.util.Map;
公共类的东西{
//使其可配置。
收集服务器列表;
//或者做一些更聪明的事情,比如这个界面。我将在这个例子中使用它。
服务器查找服务器查找;
接口服务器查找{
迭代器getValidServerListIterator();
void markUnreachableServer(字符串url);
}
//围绕RestTemplate进行外部操作。。。
@试验
public void testNormalRestTemplate()引发异常{
RestTemplate RestTemplate=新RestTemplate();
迭代器serverIterator=serverLookup.getValidServerListIterator();
while(serverIterator.hasNext()){
String server=serverIterator.next();
试一试{
Object obj=restemplate.getForObject(server+“/objectIdentifier/511”,Object.class);
打破
}捕获(资源访问异常){
serverLookup.markUnreachableServer(服务器);
}
}
}
//或者您可以尝试“增强”RestTemplate以包含重试逻辑。这有点粗糙,但更有趣。
@试验
公共void testMyRestTemplate(){
RestTemplate rt=新的MyRestTemplate();
Object obj=rt.getForObject(“/objectIdentifier/511”,Object.class);
删除(“/objectIdentifier/511”);
}
//这里有一种(黑客般地)使用重试功能扩充RestTemplate的方法
类MyRestTemplate扩展了RestTemplate{
//不幸的是,RestTemplate的设计可能没有太多的可扩展性。URI对象不能从中生成
//URL片段,所以这两种方法是我们可以覆盖和覆盖所有RestTemplate的“最远的”
//其他方法。
@凌驾
public T execute(字符串url、HttpMethod方法、RequestCallback、RequestCallback、,
ResponseExtractor ResponseExtractor,对象…urlVariables)引发RestClientException{
迭代器serverIterator=serverLookup.getValidServerListIterator();
while(serverIterator.hasNext()){
String server=serverIterator.next();
//为服务器传入的URL片段添加前缀
字符串fullUrl=server+url;
UriTemplate UriTemplate=新的HttpUrlTemplate(完整URL);
uriexpanded=uriTemplate.expand(urlVariables);
试一试{
return-doExecute(扩展、方法、requestCallback、responseExtractor);
}捕获(资源访问异常){
serverLookup.markUnreachableServer(服务器);
}
}
抛出新的RuntimeException(“无法访问“+url”的服务器列表中的任何服务器);
}
@凌驾
public T execute(字符串url、HttpMethod方法、RequestCallback、RequestCallback、,
ResponseExtractor ResponseExtractor,Map urlVariables)引发RestClientException{
迭代器serverIterator=serverLookup.getValidServerListIterator();
while(serverIterator.hasNext()){
String server=serverIterator.next();
//为服务器传入的URL片段添加前缀
字符串fullUrl=server+url;
UriTemplate UriTemplate=新的HttpUrlTemplate(完整URL);
uriexpanded=uriTemplate.expand(urlVariables);
试一试{
return-doExecute(扩展、方法、requestCallback、responseExtractor);
}捕获(资源访问异常){
serverLookup.markUnreachableServer(服务器);
}
}
抛出新的RuntimeException(“无法访问“+url”的服务器列表中的任何服务器);
}
/**RestTemplate内部类的精确副本。不能接触privates*/
类HttpUrlTemplate扩展了UriTemplate{
公共HttpUrlTemplate(字符串模板){
超级模板;
}
@凌驾
受保护的URI编码URI(字符串URI){
试一试{
String encoded=UriUtils.encodeHttpUrl(uri,“UTF-8”);
返回新的URI(编码);
}
捕获(不支持DencodingException ex){
//如果不发生这种情况,则始终支持UTF-8
抛出新的非法状态异常(ex);
}
捕获(URISyntaxException-ex){
抛出新的IllegalArgumentException(“无法从[“+uri+”]:“+ex,ex创建HTTP URL”);
}
}
}
}
}
我只需包装您的客户端,然后重复请求,直到成功。这样你就可以整齐地保持trac
1 request -> REST_SERVER1
2 request -> REST_SERVER2
3 request -> REST_SERVER3
4 request -> REST_SERVER1
import org.junit.Test;
import org.springframework.http.HttpMethod;
import org.springframework.web.client.*;
import org.springframework.web.util.UriTemplate;
import org.springframework.web.util.UriUtils;
import java.io.UnsupportedEncodingException;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.Arrays;
import java.util.Collection;
import java.util.Iterator;
import java.util.Map;
public class Stuff {
// Make this configurable.
Collection<String> serverList;
// or do something a little smarter, like this interface. I'll use this in this example.
ServerLookup serverLookup;
interface ServerLookup {
Iterator<String> getValidServerListIterator();
void markUnreachableServer(String url);
}
// Do it externally around RestTemplate...
@Test
public void testNormalRestTemplate() throws Exception {
RestTemplate restTemplate = new RestTemplate();
Iterator<String> serverIterator = serverLookup.getValidServerListIterator();
while (serverIterator.hasNext()) {
String server = serverIterator.next();
try {
Object obj = restTemplate.getForObject(server + "/objectIdentifier/511", Object.class);
break;
} catch (ResourceAccessException e) {
serverLookup.markUnreachableServer(server);
}
}
}
// or you can try to 'enhance' RestTemplate to contain the retry logic within. It's a bit hacky, but more fun.
@Test
public void testMyRestTemplate() {
RestTemplate rt = new MyRestTemplate();
Object obj = rt.getForObject("/objectIdentifier/511", Object.class);
rt.delete("/objectIdentifier/511");
}
// Here's a way to (hackily) augment RestTemplate with retry functionality
class MyRestTemplate extends RestTemplate {
// Unfortunately RestTemplate probably wasn't designed for much extensibility. URI objects can't be made from
// URL fragments, so these two methods are the 'furthest in' that we can override and cover all RestTemplate
// REST methods.
@Override
public <T> T execute(String url, HttpMethod method, RequestCallback requestCallback,
ResponseExtractor<T> responseExtractor, Object... urlVariables) throws RestClientException {
Iterator<String> serverIterator = serverLookup.getValidServerListIterator();
while (serverIterator.hasNext()) {
String server = serverIterator.next();
// prefix the URL fragment passed in with a server
String fullUrl = server + url;
UriTemplate uriTemplate = new HttpUrlTemplate(fullUrl);
URI expanded = uriTemplate.expand(urlVariables);
try {
return doExecute(expanded, method, requestCallback, responseExtractor);
} catch (ResourceAccessException e) {
serverLookup.markUnreachableServer(server);
}
}
throw new RuntimeException("Unable to reach any servers in the server list for " + url);
}
@Override
public <T> T execute(String url, HttpMethod method, RequestCallback requestCallback,
ResponseExtractor<T> responseExtractor, Map<String, ?> urlVariables) throws RestClientException {
Iterator<String> serverIterator = serverLookup.getValidServerListIterator();
while (serverIterator.hasNext()) {
String server = serverIterator.next();
// prefix the URL fragment passed in with a server
String fullUrl = server + url;
UriTemplate uriTemplate = new HttpUrlTemplate(fullUrl);
URI expanded = uriTemplate.expand(urlVariables);
try {
return doExecute(expanded, method, requestCallback, responseExtractor);
} catch (ResourceAccessException e) {
serverLookup.markUnreachableServer(server);
}
}
throw new RuntimeException("Unable to reach any servers in the server list for " + url);
}
/** Exact duplicate of the inner class of RestTemplate. Can not touch privates. */
class HttpUrlTemplate extends UriTemplate {
public HttpUrlTemplate(String uriTemplate) {
super(uriTemplate);
}
@Override
protected URI encodeUri(String uri) {
try {
String encoded = UriUtils.encodeHttpUrl(uri, "UTF-8");
return new URI(encoded);
}
catch (UnsupportedEncodingException ex) {
// should not happen, UTF-8 is always supported
throw new IllegalStateException(ex);
}
catch (URISyntaxException ex) {
throw new IllegalArgumentException("Could not create HTTP URL from [" + uri + "]: " + ex, ex);
}
}
}
}
}
import java.io.IOException;
import java.net.URL;
import java.util.Calendar;
import com.sun.jersey.api.client.Client;
import com.sun.jersey.api.client.ClientResponse;
import com.sun.jersey.api.client.WebResource;
public class RestClient {
private final String mediaType = "application/json";
private RestServer[] servers = new RestServer[] {new RestServer("server1", 8080), new RestServer("server2", 8080)};
protected RestClient() {
}
protected ClientResponse post(String methodUrl, Object postData) throws IOException {
return doRequest(methodUrl, postData, true);
}
protected ClientResponse get(String methodUrl) throws IOException {
return doRequest(methodUrl, null, false);
}
private ClientResponse doRequest(String methodUrl, Object postData, boolean isPost) throws IOException {
Client client = Client.create();
for (RestServer restServer : servers) {
if (!restServer.shouldTry()) {
System.out.println(restServer + " not ready");
continue;
}
System.out.println("Trying with " + restServer);
try {
URL url = new URL("http", restServer.getHost(), restServer.getPort(), '/' + methodUrl);
WebResource webResource = client.resource(url.toString());
System.out.println("Calling " + url);
ClientResponse response = isPost
? webResource.type(mediaType).post(ClientResponse.class, postData)
: webResource.type(mediaType).get(ClientResponse.class);
if (response.getStatus() < 300) {
restServer.succeeded();
return response;
}
restServer.failed();
} catch (Exception ex) {
System.out.println(restServer + " failed with exception " + ex.getMessage());
restServer.failed();
}
}
// No servers worked
return null;
}
}
class RestServer {
private final int TIME_TO_WAIT_BEFORE_TRYING_AGAIN = 1000 * 30; // 30 seconds
private String host;
private int port;
private Calendar lastAttempted;
private boolean lastCallFailed;
public RestServer(String host, int port) {
this.host = host;
this.port = port;
lastAttempted = Calendar.getInstance();
}
public String getHost() {
return host;
}
public int getPort() {
return port;
}
public void failed() {
lastCallFailed = true;
lastAttempted = Calendar.getInstance();
}
public void succeeded() {
lastCallFailed = false;
lastAttempted = Calendar.getInstance();
}
public boolean shouldTry() {
if (!lastCallFailed)
return true;
return Calendar.getInstance().compareTo(lastAttempted) > TIME_TO_WAIT_BEFORE_TRYING_AGAIN;
}
@Override
public String toString() {
return new StringBuilder(host).append(':').append(port).toString();
}
}