SpringCloud笔记(2)—服务之间的通信

SpringCloud微服务即是由一系列细小的服务构成的,而服务是由SpringBoot创建构成的。因此,在项目的开发过程中我们将各个模块划分出来分别使用SpringBoot进行开发,例如 我们 将卖家端的 商品管理模块 和 订单管理模块 划分成两个SpringBoot微服务项目。此时就会出现问题,订单管理模块中 客户下单时,需要进行对 商品管理模块的 商品库存信息进行数据操作,那么 订单模块 如何调用 商品管理模块 的服务的?

应用间通信方式

两大阵营:HTTP和RPC,其中SpringCloud使用的是HttpRestful方式,Dubbo是RPC框架。(二者兼容性较差)
SpringCloud应用间通信的两种实现方式:RestTemplate、Feign。(HttpClient在这里就不提了)。

RestTemplate实现服务通信

例1:RestTemplate直接请求 (例3最优)
1. 在服务端创建 一个简单的接口,客户端服务需要调用它。

@RestController
public class ServerController {
    @GetMapping("/msg")
    public String msg(){
        return "this is product's message";
    }
}

2. 客户端服务进行调用

@RestController
@Slf4j
public class ClientController {
    @GetMapping("/getProductMsg")
    public String getProductMsg(){
        //第一种方式
        RestTemplate restTemplate = new RestTemplate();
        String response = restTemplate.getForObject("http://127.0.0.1:8081/msg",String.class);
        log.info("response={}",response);
        return response;
    }
}

3. 缺点:接口的url是写死的。在多个地址(服务端负载均衡时的多个服务实例)的情况下,这种调用是不合理的。

例2使用LoadBalancerClient动态获取到接口的url地址,再采用例1方法调用。 (例3最优)

服务端(Product)不需修改,客户端调用方式修改即可。

@RestController
@Slf4j
public class ClientController {
    @Resource
    private LoadBalancerClient loadBalancerClient;
    @GetMapping("/getProductMsg")
    public String getProductMsg(){
        // 选择应用的名称,以便创建服务实例。应用名在application配置中定义
        ServiceInstance serviceInstance = loadBalancerClient.choose("PRODUCT");
        String url = String.format("http://%s:%s",serviceInstance.getHost(),serviceInstance.getPort()+"/msg");
        RestTemplate template = new RestTemplate();
        String response = template.getForObject(url,String.class);
        log.info("response={}",response);
        return response;
    }
}

例3:在RestTemplate配置类中应用注解@LoadBalanced,然后在客户端请求中直接调用服务名。最优。 

1. 服务端(Product)不需修改,在客户端项目中 新建一个RestTemplate的配置类 用于创建Bean对象。

@Configuration
public class RestTemplateConfig {
    @Bean
    @LoadBalanced
    public RestTemplate restTemplate(){
        return new RestTemplate();
    }
}

2. 客户端请求中,直接 通过 服务名进行服务调用。

@RestController
@Slf4j
public class ClientController {
    @Autowired
    private RestTemplate restTemplate;
    @GetMapping("/getProductMsg")
    public String getProductMsg(){ // 直接通过服务名进行调用
        String response = restTemplate.getForObject("http://PRODUCT/msg",String.class);
        log.info("response={}",response);
        return response;
    }
}

实际上,例2中的 LoadBalancerClient 对象 和 @LoadBalanced注解,均是Ribbon的组件。

Ribbon:客户端负载均衡器,实现应用间通信。

Ribbon实现负载均衡的核心:服务发现;服务选择原则;服务监听。

主要组件:ServerList、IRule、ServerListFilter。

应用间通信流程:通过ServerList获取所有可用服务的列表——>ServerListFilter过滤掉部分的服务地址——>IRule选择服务实例作为最终的目标结果。
注:当有多个服务实例时,可以配置application.yml进行轮询调用,例如:

PRODUCT:
  ribbon:
    NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RandomRule

Feign实现服务通信

Fegin:声明式Rest客户端(伪RPC);采用基于接口的注解;

实例:订单服务模块 调用 商品服务 模块。

1. 添加pom依赖

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>

2. 在启动类中添加注解:@EnableFeignClients。

@SpringBootApplication
@EnableDiscoveryClient
@EnableFeignClients
public class OrderApplication {
    public static void main(String[] args) {SpringApplication.run(OrderApplication.class, args);}
}

3. 在客户端 定义 需要调用的服务方的接口

@FeignClient(name = "product")
public interface ProductClient {
    @GetMapping("/msg")
    String productMsg();
}

4. 在客户端的中 调用 服务。(注入第三步定义的接口,直接调用)

@RestController
@Slf4j
public class ClientController {

    @Resource
    private ProductClient productClient;

    @GetMapping("/getProductMsg")
    public String getProductMsg(){
        // 第四种方式 使用feign调用
        String response = productClient.productMsg();
        log.info("response={}",response);
        return response;
    }
}

实例2:在订单服务中 获取商品列表

1. 在商品模块的服务中,创建根据ID查询商品的一系列dao层和service层,以及最终controller层,以便给订单服务提供接口。注意 controller层中,需要使用@RequestBody注解修饰入参。

/**
 * 专门用于 —— 订单服务调用商品列表。需要使用 @RequestBody 注解注释入参
 */
@PostMapping("/listForOrder")
public List<ProductInfo> listForOrder(@RequestBody List<String> productIdList){
    return infoService.findByProductIdIn(productIdList);
}

2. 在订单模块,引入商品模块的实体类,以及订单模块需要的接口服务。注意 方法的入参需要使用 @RequestBody 注解修饰。另:@RequestBody注解修饰参数时,必须使用POST方式请求。

@FeignClient(name = "product")
public interface ProductClient {
    @GetMapping("/msg")
    String productMsg();
    /**
     * 接收参数时,需要使用 @RequestBody
     * 由于使用了RequestBody注解,则必须是POST请求。
     * RequestBody:请求参数为JSON格式
     * @param productIdList
     * @return
     */
    @PostMapping("/product/listForOrder")
    List<ProductInfo> listForOrder(@RequestBody List<String> productIdList);
}

3. 订单模块 调用 商品模块的服务

@RestController
@Slf4j
public class ClientController {
    @Resource
    private ProductClient productClient;

    @GetMapping("/getProductInfo")
    public ResultVO getProductInfo(){
        List<ProductInfo> productInfoList =
                Optional.ofNullable(productClient.listForOrder(Arrays.asList("157875196366160022","164103465734242707","157875227953464068")))
                    .orElse(new ArrayList<>());
        log.info("response={}",productInfoList);
        productInfoList.forEach(System.out::println);
        return ResultVOUtil.success(productInfoList);
    }
}

多模块项目优化

现阶段订单服务和商品服务存在的问题

  1. 商品服务返回给订单服务的List集合是ProductInfo实体类,不建议暴露给外部。2. 商品服务和订单服务的部分对象被重复定义了,不便于后期维护。3. 在订单服务中,将商品服务的接口定义在了订单服务方法中,不便于维护。

多模块改造

1. 创建服务配置中心——https://blog.csdn.net/haoxin963/article/details/82350985https://blog.51cto.com/zero01/2171735
简述:a. 先将配置文件存储到git中。b. 配置中心服务中,进行配置并读取存储到git中的配置文件。
2. 创建多模块的Product服务和Order服务——https://blog.csdn.net/github_39577257/article/details/81842234https://blog.csdn.net/yangshangwei/article/details/88809468

 

:在本地引入其他服务的jar包时,可以先将其他服务的jar包强制安装到本地,然后在客户端 项目的依赖引入jar包。强制打包安装命令为:mvn -U clean install -Dmaven.test.skip=true
在服务器中安装服务jar包:步骤待续。

 

其他博客:https://blog.csdn.net/qq_35275233/article/details/89041647
https://juejin.im/post/5c2b7301e51d45342a256504
https://www.cnblogs.com/linlf03/p/10180005.html
feign服务:https://blog.csdn.net/github_39577257/article/details/81842234
bootstrap.yml和application.yml配置文件的区别:https://www.cnblogs.com/BlogNetSpace/p/8469033.html

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值