负载均衡主要是提供客户端的软件的负载均衡的算法,将Netflix的中间层服务连在一起,并提供超市连接,重试。Ribbon的服务链表有两种方式,一种是自己维护列表。这种方式,ribbon可以自动剔除实例。但增加新实例时,需要手工调用接口实现。第二种可以通过Eureka获取服务信息。
负载均衡主要是在实现服务调用的时候使用算法来决定当前过来的接口应该转接到多个服务中的哪个服务中去。所以过程中需要用到服务发现。服务发现有很多种,我们暂时先介绍一下RestTemplate的方式。
RestTemplate简介:
RestTemplate是Spring提供的用于访问Rest服务的客户端,RestTemplate提供了多种便捷访问远程Http服务的方法,能够大大提高客户端的编写效率。RestTemplate能大幅简化了提交表单数据的难度,并且附带了自动转换JSON数据的功能。
RestTemplate是Spring用于同步client端的核心类,简化了与http服务的通信,并满足RestFul原则,程序代码可以给它提供URL,并提取结果。默认情况下,RestTemplate默认依赖jdk的HTTP连接工具。当然你也可以 通过setRequestFactory属性切换到不同的HTTP源,比如Apache HttpComponents、Netty和OkHttp
RestTemplate方法简介
RestTemplate也分为几类,get、post、delete、put等,以下列出几种常用的,其他的不在列出。
url请求方式 | RestTemplate的方法 |
---|---|
GET | getForEntity |
GET | getForObject |
POST | postForObject |
POST | postForObject |
PUT | put |
DELETE | delete |
any | exchange |
any | excute |
其中any表示任意接口,所以在使用exchange和excute的时候需要表明自己调用的是什么方法。
RestTemplate实现
首先建立一个项目。在启动类中加入RestTemplate的bean
@SpringBootApplication
@EnableDiscoveryClient
public class LoadbalanceServiceApplication {public static void main(String[] args) {SpringApplication.run(LoadbalanceServiceApplication.class, args);}@Bean@LoadBalancedRestTemplate restTemplate(){RestTemplate restTemplate = new RestTemplate();return restTemplate;}}
在获取RestTemplate的方法中加入@Bean这样就可以RestTemplate就可以被注入任意的service bean的强化型RestTemplate的实例。@LoadBalance可以实现负载均衡的能力。在其他service中只需要:
@Autowired
RestTemplate restTemplate;
就可以使用RestTemplate来调用其他服务。
RestTemplate-GET实现
共性:
请求url:无论是get还是post或者其他的方法。方法中需要传入请求url。url的格式为:请求协议://服务名称/请求路径?参数
请求协议:常用的 http,https
服务名称:需要调用的服务注册到eureka上的名称
请求路径和参数:格式和正常一样
/**@param url 请求路径,格式为http://{service-name}/{path}?{key}={value}* url是请求路径,其中* service-name:是被调用的服务注册到注册服务上的名称。* path:接口路径。* {key}={value}:参数。*@param responseType 返回结果类型*@param uriVariables 泛型参数。根据url地址进行填充* 例如 url = http://sevice/api/test?param1={1}¶m2={2}* uriVariables 为:1111, 2222* 则处理之后url=http://sevice/api/test?param1=1111¶m2=2222* */
<T> T getForObject(String url, Class<T> responseType, Object... uriVariables) throws RestClientException;/**@param url 请求路径,格式为http://{service-name}/{path}?{key}={value}* url是请求路径,其中* service-name:是被调用的服务注册到注册服务上的名称。* path:接口路径。* {key}={value}:参数。*@param responseType 返回结果类型*@param uriVariables 泛型参数。根据url地址的key值获取map中的value* 例如 url = http://sevice/api/test?param1={param1}¶m2={param2}* uriVariables 为:{"param1":"1111", "param1":"2222"}* 则处理之后url=http://sevice/api/test?param1=1111¶m2=2222* */
<T> T getForObject(String url, Class<T> responseType, Map<String, ?> uriVariables) throws RestClientException;/**@param url 请求封装,将请求路径,请求参数都已经封装到类Url中*@param responseType 返回结果类型* */
<T> T getForObject(URI url, Class<T> responseType) throws RestClientException;<T> ResponseEntity<T> getForEntity(String url, Class<T> responseType, Object... uriVariables) throws RestClientException;<T> ResponseEntity<T> getForEntity(String url, Class<T> responseType, Map<String, ?> uriVariables) throws RestClientException;<T> ResponseEntity<T> getForEntity(URI url, Class<T> responseType) throws RestClientException;
其中get方法主要分为两类 getForObject和getForEntity。getForObject主要是返回一个我们需要的对象pojo。getForEntity是一个包装。其中包含了status、hearder、body。body就是其他服务成功返回的结果。
这里用几个示例:
/*** get请求 传入参数, 返回string* @param service* @param param* @return*/@RequestMapping(value = "/{service}/get1",method = RequestMethod.GET)public Object get1(@PathVariable(value = "service") String service, @RequestParam("param") String param){//解析{service-name}/hello 的路径调用不同的地址return restTemplate.getForEntity("http://"+ service +"/"+ service +"/get1?param="+param, String.class);}/*** 获取返回结果, Entity* @param service* @return*/@RequestMapping(value = "/{service}/get2",method = RequestMethod.GET)public Object get2(@PathVariable(value = "service") String service){//解析{service-name}/hello 的路径调用不同的地址ResponseEntity<ReturnBean> responseEntity = restTemplate.getForEntity("http://"+ service +"/"+ service +"/get2", ReturnBean.class);return responseEntity;}/*** 获取返回结果, object* @param service* @return*/@RequestMapping(value = "/{service}/get3",method = RequestMethod.GET)public Object getParamR(@PathVariable(value = "service") String service, @RequestParam("param") String param){//解析{service-name}/hello 的路径调用不同的地址ReturnBean returnBean = restTemplate.getForObject("http://"+ service +"/"+ service +"/get3?param=" +param, ReturnBean.class);return returnBean;}/*** 获取返回结果, object* @param service* @return*/@RequestMapping(value = "/{service}/get4",method = RequestMethod.GET)public Object get3(@PathVariable(value = "service") String service, @RequestParam("url") String url,@RequestParam("param") String param){//解析{service-name}/hello 的路径调用不同的地址((DefaultUriTemplateHandler)restTemplate.getUriTemplateHandler()).setStrictEncoding(true);ReturnBean returnBean = restTemplate.getForObject("http://"+ service +"/"+ service +"/get4/{param}?param={param}", ReturnBean.class, url, param);return returnBean;}
我这里的服务的名称是由前端传入的。然后我在调用指定的服务。这些服务都已经注册到eureka上并实现了相关的接口。相信大家都能看得懂。这里主要介绍的是方法中带Object... uriVariables 的不定参数的方法。这个方法是我们可以做请求协议、参数和url的替换。匹配的规则是用 {xxx} ,方法被调用之后,会顺序扫描{xxx},并将不定参数按顺序进行替换。
举个例子。假设url=http://localhost:8080/balance/{param}?param={param}。不定参数的值是("22222","11111")。则替换的结果是http://localhost:8080/balance/22222?param=11111。其中{param}可以是除了{?}和{}之外的任意形式。
其中还有一个种形式是参数是map<String,?>,这种方式的参数是将url中的需要传入的参数的值的表示作为map中的key。
举个例子。假设url=http://localhost:8080/balance/{param}?param={param1}。不定参数的值是("param"="22222","param1"="11111")。则替换的结果是http://localhost:8080/balance/22222?param=11111。其中{xxx}必须在map中油才能获取到。
由于篇幅过长,这里不再展示测试结果。
RestTemplate-POST实现
post的方法和get其实没有太大的差别。只是多了一个类型为Object的request参数。其实这个参数最终的类型是HttpEntity。我们可以在HttpEntity加入请求头和请求体。
public HttpEntity(T body) {this(body, null);}public HttpEntity(MultiValueMap<String, String> headers) {this(null, headers);}
public HttpEntity(T body, MultiValueMap<String, String> headers) {this.body = body;HttpHeaders tempHeaders = new HttpHeaders();if (headers != null) {tempHeaders.putAll(headers);}this.headers = HttpHeaders.readOnlyHttpHeaders(tempHeaders);}
现在看一下post方法:
/**@param url 请求路径,格式为http://{service-name}/{path}?{key}={value}* url是请求路径,其中* service-name:是被调用的服务注册到注册服务上的名称。* path:接口路径。* {key}={value}:参数。*@param request 请求参数,可以是具体类型,也可是封装后的HttpEntity*@param uriVariables 泛型参数。根据url地址进行填充* 例如 url = http://sevice/api/test?param1={1}¶m2={2}* uriVariables 为:1111, 2222* 则处理之后url=http://sevice/api/test?param1=1111¶m2=2222* */
@Override
public URI postForLocation(String url, Object request, Object... uriVariables) throws RestClientException {}/**@param url 请求路径,格式为http://{service-name}/{path}?{key}={value}* url是请求路径,其中* service-name:是被调用的服务注册到注册服务上的名称。* path:接口路径。* {key}={value}:参数。*@param request 请求参数,可以是具体类型,也可是封装后的HttpEntity*@param uriVariables 泛型参数。根据url地址的key值获取map中的value* 例如 url = http://sevice/api/test?param1={param1}¶m2={param2}* uriVariables 为:{"param1":"1111", "param1":"2222"}* 则处理之后url=http://sevice/api/test?param1=1111¶m2=2222* */
@Override
public URI postForLocation(String url, Object request, Map<String, ?> uriVariables) throws RestClientException {}@Override
public URI postForLocation(URI url, Object request) throws RestClientException {}/**@param url 请求路径,格式为http://{service-name}/{path}?{key}={value}* url是请求路径,其中* service-name:是被调用的服务注册到注册服务上的名称。* path:接口路径。* {key}={value}:参数。*@param request 请求参数,可以是具体类型,也可是封装后的HttpEntity*@param responseType 返回数据类型*@param uriVariables 泛型参数。根据url地址进行填充* 例如 url = http://sevice/api/test?param1={1}¶m2={2}* uriVariables 为:1111, 2222* 则处理之后url=http://sevice/api/test?param1=1111¶m2=2222* */
@Override
public <T> T postForObject(String url, Object request, Class<T> responseType, Object... uriVariables)throws RestClientException {}@Override
public <T> T postForObject(String url, Object request, Class<T> responseType, Map<String, ?> uriVariables)throws RestClientException {}@Override
public <T> T postForObject(URI url, Object request, Class<T> responseType) throws RestClientException {}@Override
public <T> ResponseEntity<T> postForEntity(String url, Object request, Class<T> responseType, Object... uriVariables)throws RestClientException {}/**@param url 请求路径,格式为http://{service-name}/{path}?{key}={value}* url是请求路径,其中* service-name:是被调用的服务注册到注册服务上的名称。* path:接口路径。* {key}={value}:参数。*@param request 请求参数,可以是具体类型,也可是封装后的HttpEntity*@param responseType 返回数据类型*@param uriVariables 泛型参数。根据url地址的key值获取map中的value* 例如 url = http://sevice/api/test?param1={param1}¶m2={param2}* uriVariables 为:{"param1":"1111", "param1":"2222"}* 则处理之后url=http://sevice/api/test?param1=1111¶m2=2222* */
@Override
public <T> ResponseEntity<T> postForEntity(String url, Object request, Class<T> responseType, Map<String, ?> uriVariables)throws RestClientException {}@Override
public <T> ResponseEntity<T> postForEntity(URI url, Object request, Class<T> responseType) throws RestClientException {}
接下来给出几个例子:
/*** 带 请求头 和 请求体 的post请求** @param service 服务名称* @return*/@RequestMapping(value = "/{service}/postForEntity",method = RequestMethod.POST)public Object helloTest(@PathVariable(value = "service") String service, @RequestBody QueryBean queryBean){HttpHeaders headers = new HttpHeaders();headers.setContentType(MediaType.APPLICATION_JSON);headers.set("name","test");HttpEntity entity = new HttpEntity(queryBean, headers);ResponseEntity<ReturnBean> responseEntity= restTemplate.postForEntity("http://"+ service +"/"+ service +"/postForEntity", entity , ReturnBean.class);return responseEntity;}/*** 带url参数的post请求** @param service 服务名称* @return*/@RequestMapping(value = "/{service}/postForEntity1",method = RequestMethod.POST)public Object postForEntity1(@PathVariable(value = "service") String service, @RequestBody QueryBean queryBean){HttpHeaders headers = new HttpHeaders();headers.setContentType(MediaType.APPLICATION_JSON);headers.set("name","test");HttpEntity entity = new HttpEntity(queryBean, headers);ResponseEntity<ReturnBean> responseEntity= restTemplate.postForEntity("http://"+ service +"/"+ service +"/postForEntity1?param={param}", entity , ReturnBean.class,"带参数");return responseEntity;}/*** 带url参数的post请求** @param service 服务名称* @return*/@RequestMapping(value = "/{service}/postForObject",method = RequestMethod.POST)public Object postForObject(@PathVariable(value = "service") String service, @RequestParam("url") String url, @RequestBody QueryBean queryBean){HttpHeaders headers = new HttpHeaders();headers.setContentType(MediaType.APPLICATION_JSON);headers.set("name","test");HttpEntity entity = new HttpEntity(queryBean, headers);ReturnBean returnBean = restTemplate.postForObject("http://"+ service +"/"+ service +"/postForObject/{param}?param={param}",entity, ReturnBean.class, url, service);return returnBean;}
可以看到post是可以传入请求体和请求头的,我们可以根据实际应用中的要求来筛选要求。返回结果跟get的方法识相似的。测试结果这里就不再给出。
这里展示的已经基本上满足了大部分的要求。但是在有的情况下,我们的get请求和其他的非post请求也需要header的信息。但是这里的get请求并不能让我们设置这些。所以RestTemplate还提供exchange和excute方法:
<T> ResponseEntity<T> exchange(String url, HttpMethod method, HttpEntity<?> requestEntity,Class<T> responseType, Object... uriVariables) throws RestClientException;<T> ResponseEntity<T> exchange(String url, HttpMethod method, HttpEntity<?> requestEntity,Class<T> responseType, Map<String, ?> uriVariables) throws RestClientException;<T> ResponseEntity<T> exchange(URI url, HttpMethod method, HttpEntity<?> requestEntity,Class<T> responseType) throws RestClientException;<T> ResponseEntity<T> exchange(String url,HttpMethod method, HttpEntity<?> requestEntity,ParameterizedTypeReference<T> responseType, Object... uriVariables) throws RestClientException;<T> ResponseEntity<T> exchange(String url, HttpMethod method, HttpEntity<?> requestEntity,ParameterizedTypeReference<T> responseType, Map<String, ?> uriVariables) throws RestClientException;<T> ResponseEntity<T> exchange(URI url, HttpMethod method, HttpEntity<?> requestEntity,ParameterizedTypeReference<T> responseType) throws RestClientException;<T> ResponseEntity<T> exchange(RequestEntity<?> requestEntity, Class<T> responseType) throws RestClientException;<T> ResponseEntity<T> exchange(RequestEntity<?> requestEntity, ParameterizedTypeReference<T> responseType)throws RestClientException;<T> T execute(String url, HttpMethod method, RequestCallback requestCallback,ResponseExtractor<T> responseExtractor, Object... uriVariables) throws RestClientException;<T> T execute(String url, HttpMethod method, RequestCallback requestCallback,ResponseExtractor<T> responseExtractor, Map<String, ?> uriVariables) throws RestClientException;
<T> T execute(URI url, HttpMethod method, RequestCallback requestCallback,ResponseExtractor<T> responseExtractor) throws RestClientException;
方法的调用和get、post相似,只不过多加了一个请求类型的参数。这里就不再展示测试结果了。大家有兴趣可以试一下。