服务通信
【一】前言
在springcloud微服务的实际项目开发中,多个微服务之间不仅是相对独立的,而且也是相对关联的;也就是说,微服务之间需要相互访问,多个微服务之间的接口可能会被互相多次调用,我们称之为微服务之间的通信;微服务之间的通信方式有很多。
一般使用以下两种方法:一种是RestTemplate,另一种是Feign;
【二】服务通信的两种方式
- RestTemplate,是spring中方便使用rest资源的一个对象,交互访问的资源通过URL进行识别和定位,每次调用都使用模板方法的设计模式,模板方法依赖于具体接口的调用,从而实现了资源的交互和调用; 它的交互方法有30多种,大多数都是基于HTTP的方法,比如:delete()、getForEntity()、getForObject()、put()、headForHeaders()等;
- Feign,一个声明式的伪HTTP客户端,使得编写HTTP客户端更加容易; 它只需要创建一个接口,并且使用注解的方式去配置,即可完成对服务提供方的接口绑定,大大简化了代码的开发量; 同时,它还具有可拔插的注解特性,而且支持feign自定义的注解和springMvc的注解(默认);
【三】RestTemplate进行服务通信
- 第一步(启动Eureka)
这个部分在上几章已经讲到了,如果还有不会的小伙伴请看博客:https://blog.csdn.net/wenge1477/article/details/102459111
gitHub:https://github.com/fengsri/cloud-eureka
-
第二步(构建Order项目,Product项目)
这个地方我就只写如何构建Order项目,Product项目和Order一致,只是Product需要提供一个可进行查询的接口。注意:当前构建的Product和Order两个服务都是采用的多Model的方式构建的,如果有小伙伴对多model构建项目不熟悉的话可以看博客:https://blog.csdn.net/wenge1477/article/details/101865503构建order服务:
- 创建父项目,添加多个子model
- 添加maven对应的依赖
父层的pom.xml文件
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<packaging>pom</packaging>
<modules>
<module>comment</module>
<module>client</module>
<module>dao</module>
</modules>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.2.0.M6</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.feng</groupId>
<artifactId>order</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>order</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>1.8</java.version>
<spring-cloud.version>Hoxton.M2</spring-cloud.version>
<lombok.version>1.18.8</lombok.version>
<model.version>0.0.1-SNAPSHOT</model.version>
<feign.version>1.4.4.RELEASE</feign.version>
<fastjson.version>1.2.60</fastjson.version>
</properties>
<dependencies>
<!--用于json字符串的转换-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>${fastjson.version}</version>
</dependency>
<!--消息总线用于配置的自动刷新-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-bus-amqp</artifactId>
<version>2.1.1.RELEASE</version>
</dependency>
<!--整合RabbitMQ-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-amqp</artifactId>
</dependency>
<!--整合Redis-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<!--服务注册-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<!--使用Stream流-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-stream-rabbit</artifactId>
</dependency>
<!--用于从配置中心拉取配置-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-config-client</artifactId>
</dependency>
<!--用于查询数据库-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<!--用于服务于服务之间的通信-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-feign</artifactId>
<version>${feign.version}</version>
</dependency>
<!--服务监控-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-zipkin</artifactId>
</dependency>
<!--服务熔断-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-hystrix</artifactId>
<version>1.4.7.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
</exclusion>
</exclusions>
</dependency>
<!--简化get,set-->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>${lombok.version}</version>
<scope>provided</scope>
</dependency>
<!--连接mysql-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<scope>test</scope>
</dependency>
<!--检验参数-->
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-validator</artifactId>
<version>5.4.1.Final</version>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>${spring-cloud.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>com.feng</groupId>
<artifactId>comment</artifactId>
<version>${model.version}</version>
</dependency>
<dependency>
<groupId>com.feng</groupId>
<artifactId>dao</artifactId>
<version>${model.version}</version>
</dependency>
</dependencies>
</dependencyManagement>
<repositories>
<repository>
<id>spring-milestones</id>
<name>Spring Milestones</name>
<url>https://repo.spring.io/milestone</url>
</repository>
</repositories>
<pluginRepositories>
<pluginRepository>
<id>spring-milestones</id>
<name>Spring Milestones</name>
<url>https://repo.spring.io/milestone</url>
</pluginRepository>
</pluginRepositories>
</project>
comment模块的pom.xml文件
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>order</artifactId>
<groupId>com.feng</groupId>
<version>0.0.1-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>comment</artifactId>
</project>
dao模块的pom.xml文件
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>order</artifactId>
<groupId>com.feng</groupId>
<version>0.0.1-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>dao</artifactId>
<dependencies>
<dependency>
<groupId>com.feng</groupId>
<artifactId>comment</artifactId>
</dependency>
</dependencies>
</project>
client模块的pom.xml文件
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>order</artifactId>
<groupId>com.feng</groupId>
<version>0.0.1-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>client</artifactId>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<artifactId>dao</artifactId>
<groupId>com.feng</groupId>
</dependency>
</dependencies>
<build>
<!--最后打包的名称-->
<finalName>order</finalName>
<plugins>
<!--进行打包-->
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<!--设置主入口-->
<configuration>
<mainClass>com.feng.order.OrderApplication</mainClass>
</configuration>
<executions>
<execution>
<goals>
<goal>repackage</goal><!--可以把依赖的包都打包到生成的Jar包中-->
</goals>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-war-plugin</artifactId>
<configuration>
<warName>order</warName>
</configuration>
</plugin>
<!--跳过测试-->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<configuration>
<skipTests>true</skipTests>
</configuration>
</plugin>
</plugins>
</build>
</project>
- 完成comment层
创建Product实体
@Data
@Entity
public class Product implements Serializable {
@Id
private Integer id;
private String name;
private String type;
private Double price;
}
创建Order实体
@Data
@Entity
public class Order implements Serializable {
@Id
@GeneratedValue(strategy= GenerationType.IDENTITY)
private Integer id;
private String orderNum;
private String orderDate;
private Double orderPrice;
private String orderStatu;
}
- 完成dao层使用dataJpa查询数据库
创建OrderRepository
这个地方使用的是dataJpa所以基本的增删改查已经通过继承JpaRepository已经实现有了
@Repository
public interface OrderRepository extends JpaRepository<Order,Integer> {
}
- 完成service层和Controller层
这个地方下订单的的service我就不写了,我们主要讲下服务之间的通信问题,有兴趣的小伙伴可以自己去写下相关的逻辑。
这个地方只写服务调用的Controller:
/**
* 第一种获取服务通信的方式RestTemplate直接写死url
* @return
*/
@GetMapping("getMessgae")
public String listProduct(){
RestTemplate restTemplate = new RestTemplate();
String message = restTemplate.getForObject("http://localhost:8083/test/msg", String.class);
return message;
}
@Autowired
private LoadBalancerClient balancerClient;
/**
* 第二种获取服务通信的方式LoadBalancerClient
* @return
*/
@GetMapping("getMessgae2")
public String listProduct2(){
RestTemplate restTemplate = new RestTemplate();
ServiceInstance instance = balancerClient.choose("PRODUCT");
String url = String.format("http://%s:%s",instance.getHost(),instance.getPort());
String message = restTemplate.getForObject(url, String.class);
}
//第三种方式通信
//1、注入bean
@Component
public class RestTemplateConfig {
@Bean
@LoadBalanced
public RestTemplate restTemplate(){
return new RestTemplate();
}
}
//2、在需要的地方进行注入,调用
@Autowired
private RestTemplate restTemplate;
/**
* 第三种获取服务通信的方式
* @return
*/
@GetMapping("getMessgae3")
public String listProduct3(){
String message = restTemplate.getForObject("http://PRODUCT/test/msg", String.class);
return message;
}
-
这个地方使用配置在配置中心
需要在github上的配置项目中添加Order配置文件
如果有小伙伴多统配置中心拉取配置不熟悉的,请看博客:https://blog.csdn.net/wenge1477/article/details/102505658
- 第三步(Product提供查询商品的服务)
【四】Feign进行服务通信
-
第一步(启动Eureka)
-
第二步(构建Order项目,Product项目)
-
第三步(Product提供查询商品的服务)
前三步和上面使用RestTemplate的一样,具体的查看以上的步骤 -
第四步(创建Feign调用的接口)
@FeignClient(name = "product")
public interface ProductClient {
/**
* 通过商品的id集合去查询商品列表
* @param listId
* @return
*/
@GetMapping("/product/list")
List<Product> getByIds(@RequestParam("ids")List<Integer> listId);
/**
* 通过购物列表,去调用商品服务的减库存的服务
* @param productInfoList
*/
@PostMapping("/product/reduce")
void reduceProductCount(@RequestBody List<ProductInfo> productInfoList);
}
- 第五步(调用接口进行通信)
@RestController
@RequestMapping("test")
@RefreshScope
public class TestController {
@Autowired
private ProductClient productClient;
@Autowired
private RestTemplate restTemplate;
@GetMapping("listProduct")
public List<Product> test(){
List<Product> productList = productClient.getByIds(Arrays.asList(1,2));
redisTemplate.opsForValue().set("key", JSON.toJSONString(productList));
return productList;
}
}
- 第六步(启动类上添加注解)
@SpringBootApplication
@EnableEurekaClient
@EnableFeignClients
public class OrderApplication {[添加链接描述](https://blog.csdn.net/annotation_yang/article/details/80876913)
public static void main(String[] args) {
SpringApplication.run(OrderApplication.class, args);
}
}
Order服务GitHub地址:https://github.com/fengsri/cloud-order
Product服务GitHub地址:https://github.com/fengsri/cloud-product
【五】链接
https://blog.csdn.net/alan_liuyue/article/details/82773555
https://blog.csdn.net/yeyazhishang/article/details/84344402
https://blog.csdn.net/annotation_yang/article/details/80876913
https://www.jianshu.com/p/8bca50cb11d8