SpringCloud Alibaba微服务实战三 微服务之间的调用

常用的微服务间的调用方式
RPC:
              远程过程调用,像调用本地服务(方法)一样调用服务器的服务

              支持同步、异步调用

      客户端和服务器之间建立TCP连接,可以一次建立一个,也可以多个调用复用一次链接(建立连接耗时)大公司多用RPC

              PRC数据包小

                     protobuf

                     thrift

              rpc:编解码,序列化,链接,丢包,协议(成本大)

 Rest(Http):
              http请求,支持多种协议和功能

              开发方便成本低

              http数据包大

              java开发:HttpClient,URLConnection

spring cloud暂时用这种方式
 

SpringCloud中的服务调用方式

本文主要介绍SpringCloud中三种服务调用方式:

  1. Spring DiscoveryClient
  2. 支持LoaderBalancer的RestTemplate(springcloud 2020已经不再支持ribbon)
  3. OpenFeign客户端

下面上伪代码(服务调用关键代码)

1,@Component

//通过DiscoveryClient从Eureka中获取licensingservice服务的实例数组,并返回第一个实例。

public class ConsumerDiscoveryClient {

  @Autowired
  private DiscoveryClient discoveryClient;

  public ServiceInstance getServiceInstance() {
    List serviceInstances = discoveryClient.getInstances("服务名");
    if (serviceInstances.size() == 0) {
      return null;
    }
    return serviceInstances.get(0);
  }

  public String getUrl(String url) {
    ServiceInstance serviceInstance=this.getServiceInstance();
    if (serviceInstance==null)
      throw new RuntimeException("404 ,NOT FOUND");
    String urlR=String.format(url,serviceInstance.getUri().toString());
    return urlR;
  }
}
测试controller

@RestController
@RequestMapping("test")
public class TestController {
  //注意必须new,否则会拦截器拦截,改变URL行为
  private RestTemplate restTemplate=new RestTemplate();
  @Autowired
  private ConsumerDiscoveryClient consumerDiscoveryClient;
  @RequestMapping("/getAllEmp")
  public List getAllLicense(){
    String url=consumerDiscoveryClient.getUrl("%s/test/getAllEmp");
    return restTemplate.getForObject(url,List.class);
  }
}

2,

 //指明负载均衡算法
  @Bean
  public IRule iRule() {
    return new RoundRobinRule();
  }

  @Bean
  @LoadBalanced //告诉Spring创建一个支持Ribbon负载均衡的RestTemplate
  public RestTemplate restTemplate() {
    return new RestTemplate();
  }

@RestController
@RequestMapping("rest")
public class ConsumerRestController {

    @Autowired
    private RestTemplate restTemplate;
    private final static String SERVICE_URL_PREFIX = "服务名";

    @RequestMapping("/getById")
    public Emp getById(Long id) {
        MultiValueMap paramMap = new LinkedMultiValueMap();
        paramMap.add("id", id);
        return restTemplate.postForObject(SERVICE_URL_PREFIX + "/test/getById", paramMap, Emp.class);
    }
    }

3,


    org.springframework.cloud
    spring-cloud-starter-openfeign

@FeignClient(name = "account-service")
public interface AccountFeign {
    @PostMapping("/account/insert")
    ResultData insert(@RequestBody AccountDTO accountDTO);

    @PostMapping("/account/delete")
    ResultData delete(@RequestParam("accountCode") String accountCode);

    @PostMapping("/account/update")
    ResultData update(@RequestBody AccountDTO accountDTO);

    @GetMapping("/account/getByCode/{accountCode}")
    ResultData getByCode(@PathVariable(value = "accountCode") String accountCode);
}
在接口上添加注解@FeignClient(name = "account-service"),表明这是一个Feign客户端,name属性的配置表示我这个接口最终会转发到accout-service上。

启动类加@EnableFeignClients

三,实战(重点用的是第三种feign)

一,新建子工程

工程结构如下:

SpringCloud Alibaba微服务实战三 微服务之间的调用_第1张图片

工程代码:

1,Spring DiscoveryClient方式(了解服务调用原理即可,项目不使用)

pom:



    
        cloud_alibaba_learn
        com.liu.learn
        1.0-SNAPSHOT
    
    4.0.0

    cloud_order

    
        
            org.springframework.boot
            spring-boot-starter-web
        
        
        
            com.alibaba.cloud
            spring-cloud-starter-alibaba-nacos-discovery
        
        
        
            com.alibaba.cloud
            spring-cloud-starter-alibaba-nacos-config
        
        
        
            org.springframework.cloud
            spring-cloud-starter-openfeign
        


    


 配置文件bootstrap.xml

server:
  port: 8090
spring:
  profiles:
    active: dev
  application:
    main:
      allow-bean-definition-overriding: true
    name: order-service #服务名
    cloud:
      nacos:
        discovery:
          server-addr: 127.0.0.1:8848

控制器类:

package com.liu.controller;

import com.liu.config.ConsumerDiscoveryClient;
import com.liu.entities.CommonResult;
import com.liu.entities.Payment;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;

@RestController
@Slf4j
public class OrderFirstController {

    private RestTemplate restTemplate = new RestTemplate();

    @Autowired
    private ConsumerDiscoveryClient consumerDiscoveryClient;

  
    @GetMapping("/consumer/payment/get/{id}")
    public CommonResult getPaymentById(@PathVariable("id") Long id) {
       
       String url = consumerDiscoveryClient.getUrl("%s/payment/get/"+id);
        System.out.println("url:"+url);
        return restTemplate.getForObject(url,CommonResult.class);
    }

  
}

 控制器依赖类

package com.liu.config;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.discovery.DiscoveryClient;
import org.springframework.stereotype.Component;

import java.util.List;

@Component
public class ConsumerDiscoveryClient {
    @Autowired
 private DiscoveryClient discoveryClient;

    public ServiceInstance getServiceInstance() {
         List serviceInstances = discoveryClient.getInstances("payment-service");
        if (serviceInstances.size() == 0) {
         return null;
        }
        for (ServiceInstance serviceInstance : serviceInstances) {
            System.out.println("url:"+serviceInstance.getUri());
        }
    return serviceInstances.get(0);
}

 public String getUrl(String url) {
     ServiceInstance serviceInstance=this.getServiceInstance();
     if (serviceInstance==null)
        throw new RuntimeException("404 ,NOT FOUND");
     String urlR=String.format(url,serviceInstance.getUri().toString());
     return urlR;
 }
}

其他

package com.liu.entities;


import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

@Data //lombok 自动生成getset
@AllArgsConstructor  // 有参构造函数
@NoArgsConstructor //无参函数
public class CommonResult {
    private Integer code;
    private String message;
    private T     data;

    public CommonResult(Integer code,String message){
        this(code,message,null);
    }
}

package com.liu.entities;


import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

import java.io.Serializable;

@Data
@AllArgsConstructor  // 有参构造函数
@NoArgsConstructor //无参函数
public class Payment implements Serializable { //为对象提供标准的序列化与反序列化操作
    private Long id;

    /**
     * 订单号
     */
    private String serial;

}

 启动类:

package com.liu;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;

@SpringBootApplication
@EnableDiscoveryClient
public class OrderMain {

    public static void main(String[] args) {

        SpringApplication.run(OrderMain.class, args);
    }

}

2,第二种方式(常用) 

与第一种方式不同的地方

1,

RestTemplate创建方式

代码只贴出与第一种不同的地方

package com.liu.config;

import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.client.RestTemplate;

@Configuration
@ComponentScan("org.springframework.boot.actuate.autoconfigure.metrics")
public class ApplicationContextConfig {

    @Bean
    @LoadBalanced
    public RestTemplate getRestTemplate() {
        return new RestTemplate();
    }
}
package com.liu.controller;

import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;

@RestController
@Slf4j
public class OrderSecondController {

//实现负载均衡必须为注册中心服务名
    //String PAYMENT_URL = "http://localhost:8070/";
    String PAYMENT_URL = "http://payment-service/";

    @Autowired
    private RestTemplate restTemplate ;



   @GetMapping("/consumer2/payment/get/{id}")
    public com.liu.entities.CommonResult getPaymentById(@PathVariable("id") Long id) {
        return restTemplate.getForObject(PAYMENT_URL+"/payment/get/"+id, com.liu.entities.CommonResult.class);
    }

}

 为了直观展示负载均衡,修改payment-service服务 添加

@Value("${server.port}")
private String serverPort;
package com.liu.controller;


import com.liu.entitis.CommonResult;
import com.liu.entitis.Payment;
import com.liu.service.PaymentService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.web.bind.annotation.*;
import org.springframework.beans.factory.annotation.Value;

import javax.annotation.Resource;

@Slf4j
@RestController
@RequestMapping("/payment")
/**
 * 支付controller类
 */
@RefreshScope
public class PaymentController {

    @Value("${server.port}")
    private String serverPort;

    @Resource
    private PaymentService paymentService;

    @PostMapping(value = "/create")
	//添加  前
    /**
     * 添加 后
     */
    public CommonResult create(@RequestBody com.liu.entitis.Payment payment){
        boolean  result=paymentService.save(payment);
        log.info("****插入结果:"+result);
        if (result){
            return new CommonResult(444,"插入数据库成功",null);
        }else {
            return new CommonResult(200,"插入数据库失败",result);
        }
    }

    //通过id进行查询
    @GetMapping("/get/{id}")
    /**
     * 通过id进行查询
     */
	//通过id查询
    public CommonResult getPaymentById(@PathVariable("id") Long id ){
       Payment payment = paymentService.getById(id);
        log.info("****查询结果:"+payment);
        if (payment != null){
            return new CommonResult(200,"查询成功"+serverPort,payment);
        }else {
            return new CommonResult(444,"没有对应记录,查询ID:"+id,null);
        }
    }

}

导出order-service jar包,以8071端口号运行

package com.liu.controller;

import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;

@RestController
@Slf4j
public class OrderSecondController {

   // String PAYMENT_URL = "http://localhost:8071/";
//服务名
   String PAYMENT_URL = "http://payment-service";

    @Autowired
    private RestTemplate restTemplate ;



   @GetMapping("/consumer2/payment/get/{id}")
    public com.liu.entities.CommonResult getPaymentById(@PathVariable("id") Long id) {
        return restTemplate.getForObject(PAYMENT_URL+"/payment/get/"+id, com.liu.entities.CommonResult.class);
    }

}
package com.liu.config;

import org.springframework.cloud.client.loadbalancer.LoadBalanced;

import org.springframework.cloud.loadbalancer.annotation.LoadBalancerClient;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.client.RestTemplate;

@Configuration
@LoadBalancerClient(name = "payment-service")
public class ApplicationContextConfig {

    @Bean
    @LoadBalanced
    public RestTemplate getRestTemplate() {
        return new RestTemplate();
    }
}

访问浏览器

两个服务之间进行切换

负载均衡服务切换算法下文讲解

3,服务调用方式(常用)

新建feifn调用服务接口

package com.liu.servcie;

import com.liu.entities.CommonResult;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;

@Component
@FeignClient(value = "payment-service")
public interface PaymentFeignService {

    @GetMapping(value = "/payment/get/{id}")
    public CommonResult getPaymentById(@PathVariable("id") Long id);
}

新建控制器类

package com.liu.controller;

import com.liu.entities.CommonResult;
import com.liu.servcie.PaymentFeignService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;

import javax.annotation.Resource;

@RestController
@Slf4j
public class OrderThirdController {

    @Resource
    private PaymentFeignService paymentFeignService;

    @GetMapping(value = "/consumer3/payment/get/{id}")
    public CommonResult getPaymentById(@PathVariable("id") Long id){
        return paymentFeignService.getPaymentById(id);
    }
}

修改启动类

package com.liu;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.openfeign.EnableFeignClients;

@SpringBootApplication
@EnableDiscoveryClient
@EnableFeignClients
public class OrderMain {

    public static void main(String[] args) {

        SpringApplication.run(OrderMain.class, args);
    }

}

启动服务进行测试

openfeign已经集成了负载均衡

 PaymentFeignService  服务接口类似提供服务类的controller层,不同的是feign客户端为接口类

你可能感兴趣的:(SpringCloud,Alibaba微服务实战)