一、环境准备
1.1、前言
- 目前主流的两种微服务应用件通信的方式是rpc和restful;springcloud主要支持restful的应用通信方式;springcloud 中服务间主要有两种restful调用方式:RestTemplate和Feign。
1.2、样例需求
- 我们主要以电商的订单和商品来学习springcloud的应用通信机制、订单需要获取商品的数量和价格等信息;所以我们做通信实例时可以把商品模块作为服务提供者、订单模块作为服务消费者、最后还需要一个服务注册中心
1.3、创建微服务架构
1、创建父级工程:把多余的文件删掉
2、修改以后的工程目录
3、在父级目录添加如下配置
<packaging>pom</packaging>
4、创建子模块:服务注册中心
5、修改服务注册中心子模块的pom依赖
-
修改parent标签,继承父模块而不是springboot
-
注册中心完整的依赖:
<?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> <parent> <groupId>com.communication</groupId> <artifactId>cloud-communication</artifactId> <version>0.0.1-SNAPSHOT</version> </parent> <groupId>com.communication</groupId> <artifactId>eureka-server</artifactId> <version>0.0.1-SNAPSHOT</version> <name>eureka-server</name> <description>eureka server registery center</description> <properties> <java.version>1.8</java.version> <spring-cloud.version>Greenwich.SR3</spring-cloud.version> </properties> <dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-eureka-server</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </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> </dependencies> </dependencyManagement> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build> </project>
6、在父级pom文件中添加注册中心的子module配置
7、父工程mvn install:显示安装成功;此次聚合成功
8、其他两个模块的聚合:与注册中心的逻辑是一样的,详情参照上面的聚合步骤
- 项目结构如下:
9、注意
- 每新增一个子模块或者创建工程都要先mvn clean install,否则启动工程会报下列错误:
1.3、功能完善
- 首先服务注册的基本知识不再重复赘述了,请参考此博客:springcloud核心技术之服务注册与发现(二)
1、注册中心
-
配置如下:
server: port: 8761 eureka: instance: hostname: localhost # 指定注册中心主机名称 server: enable-self-preservation: true # 自我保护模式(默认为打开);生产环境不能这样子做 eviction-interval-timer-in-ms: 5000 # 续期时间,即扫描失效服务的间隔时间(缺省为60*1000ms) client: register-with-eureka: false # 关闭自我注册功能 fetch-registry: false # 由于自己就是服务器,不需要从服务器获取注册信息 service-url: defaultZone: http://localhost:8761/eureka/
2、商品模块
-
配置:
server: port: 8081 spring: application: name: product-server eureka: client: service-url: defaultZone: http://localhost:8761/eureka/ instance: hostname: localhost
注意(踩坑):服务的名称一定不能以下划线连接,只能是纯英文或者中划线连接
-
服务开放接口(需要被消费的接口):
@RestController public class ProductController { @GetMapping("/msg") public String msg(){ return "一个鸡蛋一块钱,两个鸡蛋两块钱"; } }
3、订单模块
-
订单模块的目的:通过springcloud的应用通信机制调用商品模块的msg接口
-
配置如下:
server: port: 8082 spring: application: name: order-server eureka: client: service-url: defaultZone: http://localhost:8761/eureka/ instance: hostname: localhost
注意(踩坑):服务的名称一定不能以下划线连接,只能是纯英文或者中划线连接
4、测试:两个服务都能够在注册中心注册成功
二、通信实战
2.1、RestTemplate
- 一共有三种方式使用RestTemplate
1、方式一:直接使用RestTemplate
-
order订单系统端代码:
@RestController public class ClientController { @GetMapping("/getProductMsg") public String getProductMsg(){ RestTemplate restTemplate = new RestTemplate(); String response = restTemplate.getForObject("http://localhost:8081/msg", String.class); return response; }
-
测试:
-
缺点:
1、调用url在代码里面是固定写死的,线上的时候eureka上面会有多台服务器,IP地址肯定是不知道的
2、对方有多个地址,起了多个实例,做不了负载均衡。
2、方式二:通过LoadBalancerClient这个对象获取应用名字PRODUCT,然后获取其中的任意一个url,再使用RestTemplate
@RestController
public class ClientController {
@Autowired
LoadBalancerClient loadBalancerClient;
@GetMapping("/getProductMsg/2")
public String getMsg(){
RestTemplate restTemplate = new RestTemplate();
ServiceInstance serviceInstance = loadBalancerClient.choose("PRODUCT-SERVER");
String url = String.format("http://%s:%s", serviceInstance.getHost(), serviceInstance.getPort() + "/msg");
String response = restTemplate.getForObject(url, String.class);
return response;
}
- 测试结果:
3、方式三:利用@LoadBalanced注解,可在restTemplate里面应用名字;推荐此方式
-
配置类:
@Component public class RestTemplateConfig { @Bean @LoadBalanced public RestTemplate restTemplate(){ return new RestTemplate(); } }
-
controller调用类:
@RestController public class ClientController { @Autowired RestTemplate restTemplate; @GetMapping("/getProductMsg/3") public String getProduct() { return restTemplate.getForObject("http://PRODUCT-SERVER/msg",String.class); }
-
测试结果:
2.2、Feign
1、在调用方增加Feign依赖:即在订单模块新增下列依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-feign</artifactId>
</dependency>
2、在启动类添加注解:@EnableFeignClients
@SpringBootApplication
@EnableEurekaClient
@EnableFeignClients
public class OrderServerApplication {
public static void main(String[] args) {
SpringApplication.run(OrderServerApplication.class, args);
}
}
3、定义好需要调用商品接口的Feign接口
- 订单模块调用商品服务的接口主要是通过下面**@GetMapping("/msg")进行接口关联;@FeignClient**标签中主要是存放要调用服务的名字。
@FeignClient("product-server") public interface ProductClient { @GetMapping("/msg") String productMsg(); }
4、通过Feign接口调用商品模块接口
@RestController
public class ClientController {
@Autowired
ProductClient productClient;
@GetMapping("/getProductMsg/4")
public String getMessage(){
return productClient.productMsg();
}
}