应用间通信
HTTP vs RPC
- Spring Cloud (HTTP)
- Dubbo (RPC)
1.SpringCloud中服务间两种restful调用方式
- RestTemplate
- Feign
方式一、RestTemplate:是一个http客户端
RestTemplate有三种方式
1.直接写url :http://localhost:8080/msg
使用restTemplate.getForObject("http://localhost:8080/msg", String.class);方法
@RestController
@Slf4j
public class ClientController {
    @GetMapping("/getProductMsg")
    public String getProductMsg(){
        //1. 第一种方式
        RestTemplate restTemplate = new RestTemplate();
        String response = restTemplate.getForObject("http://localhost:8080/msg", String.class);
        log.info("response={}",response);
        return response;
    }
}
缺点:地址是写死的,如果对方服务有多台,这样很不好。
2.通过 ServiceInstance serviceInstance = loadBalancerClient.choose("ServerId"); 获得服务的信息。
ServerId :服务的名字
  @Autowired
  private LoadBalancerClient loadBalancerClient;
@RestController
@Slf4j
public class ClientController {
    @Autowired
    private LoadBalancerClient loadBalancerClient;
    
    @GetMapping("/getProductMsg")
    public String getProductMsg(){
        //2.第二种
        ServiceInstance serviceInstance= loadBalancerClient.choose("PRODUCT");
        String url = String.format("http://%s:%s",serviceInstance.getHost(),serviceInstance.getPort())+"/msg";
        RestTemplate restTemplate = new RestTemplate();
        String response = restTemplate.getForObject(url, String.class);
        log.info("response={}",response);
        return response;
    }
- 先增加@Autowired LoadBalancerClient
- 通过loadBalancerClient.choose("ServerId");返回一个ServiceInstance
- serviceInstance.getHost()、serviceInstance.getPort()可以分别获得主机以及端口号
- 拼接主机以及端口
- 通过第一种方法的restTemplate.getForObject(url, String.class)请求数据
| 方法 | 说明 | 返回例子 | 
|---|---|---|
| serviceInstance.getUri() | 路径 | http://localhost:8080 | 
| serviceInstance.getHost() | 主机 | localhost | 
| serviceInstance.getServiceId() | 服务id | PRODUCT | 
| serviceInstance.getPort() | 端口号 | 8080 | 
优点:我们可以不知道服务的路径,通过服务Id或者服务路径信息
缺点:编码比较繁琐
3.用@LoadBalanced 可在restTemplate里使用应用名字来访问
@Component
public class RestTemplateConfig {
    @Bean
    @LoadBalanced
    public RestTemplate restTemplate(){
        return new RestTemplate();
    }
}
@RestController
@Slf4j
public class ClientController {
    @Autowired
    private RestTemplate restTemplate;
    @GetMapping("/getProductMsg")
    public String getProductMsg(){
        //3.第三种方式 利用@LoadBalanced 可在restTemplate里使用应用名字
        String response = restTemplate.getForObject("http://PRODUCT/msg", String.class);
        log.info("response={}",response);
        return response;
    }
}
总结
RestTemplate有三种方式:
- url写死,用new RestTemplate () 访问url来获取消息
- 使用loadBalancerClient获取到url,然后使用new RestTemplate () 访问url 获取信息
- 利用@LoadBalanced注解restTemplate,然后restTemplate就可以使用应用名称来访问(通过ribbon依据某种规则,如简单轮询、随机连接去连接目标服务来实现负载均衡)
方式二、Feign
- 本质还是http客户端
- 声明式REST客户端(伪RPC)
- 才用了基于接口的注解
- 内部也使用Ribbon做负载均衡
通过feign我们能把http远程调用对开发者完全透明,得到与调用本地方法一样的编码体验
使用方法:
1.在pom中增加Feign依赖
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-feign</artifactId>
            <version>2.0.0.M1</version>
        </dependency>
2.在启动类增加注解@EnableFeignClients 开启Feign功能 (org.springframework.cloud.netflix.feign.EnableFeignClients;)
package com.imooc.order;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.netflix.feign.EnableFeignClients;
@SpringBootApplication
@EnableDiscoveryClient
@EnableFeignClients
public class OrderApplication {
    public static void main(String[] args) {
        SpringApplication.run(OrderApplication.class, args);
    }
}
3.在客户端定义好要调用的服务和接口
- 接口使用@FeignClient(name="需要调用的应用名称")
- 方法的注解是需要调用接口的地址(也可以使用@RequestMapping、@PostMapping)
 如我们想调用的接口地址为“/order/addOrder”,且为GET请求, 那就写@GetMapping("/order/addOrder")
package com.imooc.order.client;
import org.springframework.cloud.netflix.feign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
@FeignClient(name="product")
public interface ProductClient {
    @GetMapping("/msg")
    String productMsg();
}
4.客户端依赖注入③定义好的接口,然后直接调用即可。
package com.imooc.order.controller;
import com.imooc.order.client.ProductClient;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.loadbalancer.LoadBalancerClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;
@RestController
@Slf4j
public class ClientController {
    @Autowired
    private ProductClient productClient;
    @GetMapping("/getProductMsg")
    public String getProductMsg(){
        String response = productClient.productMsg();
        log.info("response={}",response);
        return response;
    }
}