一、相关环境

  • 开发工具:idea
  • springboot版本:2.5.3
  • springcloud版本:2020.0.3
  • 中间件:eureka、fegin(其他中间件比如hystrix、ribbon等后续再添加);

二、项目创建

说明:此处我们会创建一个父项目,其他子项目(生产者、消费者、注册中心)均以module的形式在展示在项目目录中,首先比较符合当前开发规范,其次也比较方便;

1. 创建父项目

首先创建空壳项目,cloud-demo项目
File->New->Module -> Spring Initializr
image.png

删除多余文件只保留pom.xml
image.png
cloud-demo的pom.xml内容如下

  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  3. xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
  4. <modelVersion>4.0.0</modelVersion>
  5. <parent>
  6. <groupId>org.springframework.boot</groupId>
  7. <artifactId>spring-boot-starter-parent</artifactId>
  8. <version>2.5.3</version>
  9. <relativePath/> <!-- lookup parent from repository -->
  10. </parent>
  11. <groupId>com.example</groupId>
  12. <artifactId>cloud-demo</artifactId>
  13. <version>0.0.1-SNAPSHOT</version>
  14. <name>cloud-demo</name>
  15. <description>Demo project for Spring Boot</description>
  16. <properties>
  17. <java.version>1.8</java.version>
  18. </properties>
  19. <dependencies>
  20. <dependency>
  21. <groupId>org.springframework.boot</groupId>
  22. <artifactId>spring-boot-starter-web</artifactId>
  23. </dependency>
  24. </dependencies>
  25. <build>
  26. <plugins>
  27. <plugin>
  28. <groupId>org.springframework.boot</groupId>
  29. <artifactId>spring-boot-maven-plugin</artifactId>
  30. </plugin>
  31. </plugins>
  32. </build>
  33. </project>

现在就有了一个空壳maven项目,用作于父级项目,后续在其基础上创建springcloud项目

2.创建子项目—entity项目

创建空壳maven项目 cloud-demo-entity
父级项目右击New->Module -> Maven 直接创建
项目结构如下:entity子项目中主要有两个entity,包名为:com.example.clouddemoentity.entity;

  1. package com.example.clouddemoentity.entity;
  2. public class Product {
  3. private String name;
  4. private int age;
  5. private String add;
  6. private String email;
  7. public Product() {
  8. this.name = "name";
  9. this.age = 12;
  10. this.add = "北京市历史互通";
  11. this.email = "6666.qq.com";
  12. }
  13. @Override
  14. public String toString() {
  15. return "Product{" +
  16. "name='" + name + '\'' +
  17. ", age=" + age +
  18. ", add='" + add + '\'' +
  19. ", email='" + email + '\'' +
  20. '}';
  21. }
  22. }

cloud-demo-entity项目结构如下
image.png

3.创建子项目—注册中心

创建cloud-demo-eureka(注册中心)项目选择相应服务-Eureka Server

  • 父级项目右击New->Module -> Spring Initializr

image.png
image.png

cloud-demo-eureka结构如下
image.png

  • cloud-demo-eureka的pom.xml内容如下 ```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

    1. xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">

    4.0.0

    1. <groupId>org.springframework.boot</groupId>
    2. <artifactId>spring-boot-starter-parent</artifactId>
    3. <version>2.5.3</version>
    4. <relativePath/> <!-- lookup parent from repository -->

    com.example cloud-demo-eureka 0.0.1-SNAPSHOT cloud-demo-eureka Demo project for Spring Boot

    1. <java.version>1.8</java.version>
    2. <spring-cloud.version>2020.0.3</spring-cloud.version>

    1. <dependency>
    2. <groupId>org.springframework.cloud</groupId>
    3. <artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
    4. </dependency>

    org.springframework.cloud spring-cloud-dependencies ${spring-cloud.version} pom import org.springframework.boot spring-boot-maven-plugin

  1. - 修改resources下的application.propertiesapplication.yml并添加如下配置:
  2. ```java
  3. server:
  4. port: 9080
  5. servlet:
  6. context-path: /eureka-server
  7. spring:
  8. application:
  9. name: eureka-server
  10. eureka:
  11. client:
  12. #是否启用湖区注册服务信息,因为这是一个单节点的EurekaServer,
  13. #不需要同步其他的EurekaServer节点的数据,所以设置为false;
  14. fetch-registry: false
  15. #表示是否向eureka注册服务,即在自己的eureka中注册自己,默认为true,此处应该设置为false;
  16. register-with-eureka: false
  17. # Eureka服务端启动地址,也是注册地址
  18. service-url:
  19. #defaultZone 这个是不会提示的,此处需要自己写
  20. #实际上属性应该是service-url,这个属性是个map(key-value)格式;当key是defaultZone的时候才能被解析;
  21. #所以这里没有提示,但是自己还需要写一个defaultZone;
  22. defaultZone: http://localhost:${server.port}/eureka-server/eureka/
  23. instance:
  24. hostname: eureka-server
  25. server:
  26. #设为false,关闭自我保护,即Eureka server在云心光器件会去统计心跳失败比例在15分钟之内是否低于85%,如果低于85%,
  27. #EurekaServer会将这些事例保护起来,让这些事例不会过期,但是在保护器内如果刚哈这个服务提供者非正常下线了,
  28. #此时服务消费者会拿到一个无效的服务实例,此时调用会失败,对于此问题需要服务消费者端有一些容错机制,如重试、断路器等;
  29. enable-self-preservation: false
  30. #扫描失效服务的间隔时间(单位是毫秒,摩恩是60*1000),即60s
  31. eviction-interval-timer-in-ms: 10000
  • 修改启动文件,添加@EnableEurekaServer注解,表示这个是一个Eureka注册中心

image.png

image.png

4.创建子项目—生产者

创建cloud-demo-product项目选择相应服务

  • 父级项目右击New->Module -> Spring Initializr

image.png
image.png

  • 修改该子项目pom.xml的父依赖

    1. <parent>
    2. <groupId>org.springframework.boot</groupId>
    3. <artifactId>spring-boot-starter-parent</artifactId>
    4. <version>2.5.3</version>
    5. <relativePath/> <!-- lookup parent from repository -->
    6. </parent>
    7. 改为
    8. <parent>
    9. <artifactId>cloud-demo</artifactId>
    10. <groupId>com.example</groupId>
    11. <version>0.0.1-SNAPSHOT</version>
    12. <relativePath/> <!-- lookup parent from repository -->
    13. </parent>
  • 添加依赖

    1. <dependencies>
    2. <!-- entity依赖添加到product工程中 -->
    3. <dependency>
    4. <groupId>com.example</groupId>
    5. <artifactId>cloud-demo-entity</artifactId>
    6. <version>0.0.1-SNAPSHOT</version>
    7. </dependency>
    8. <dependency>
    9. <groupId>org.springframework.cloud</groupId>
    10. <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
    11. </dependency>
    12. </dependencies>
  • 修改resources下的application.properties为application.yml并添加如下配置:

    1. spring:
    2. application:
    3. name: product-server
    4. server:
    5. port: 9085
    6. servlet:
    7. context-path: /product
    8. eureka:
    9. client:
    10. service-url:
    11. defaultZone: http://localhost:9080/eureka-server/eureka
  • 创建接口Controller

image.png

  1. package com.example.clouddemoproduct.controller;
  2. import com.example.clouddemoentity.entity.Product;
  3. import org.springframework.web.bind.annotation.RequestMapping;
  4. import org.springframework.web.bind.annotation.RestController;
  5. @RestController
  6. public class ProductController {
  7. @RequestMapping(value = "getProduct")
  8. public String getProduct(){
  9. Product p = new Product();
  10. return p.toString();
  11. }
  12. }
  • 启动类添加注解:添加@EnableEurekaClient和EnableFeginClients注解 ```java package com.example.clouddemoproduct;

import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.netflix.eureka.EnableEurekaClient;

//@EnableEurekaClient 和 @EnableDiscoveryClient 都是让eureka发现该服务并注册到eureka上的注解 //相同点:都能让注册中心Eureka发现,并将该服务注册到注册中心上; //不同点:@EnableEurekaClient只适用于Eureka作为注册中心,而@EnableDiscoveryClient可以是其他注册中心; @EnableEurekaClient @SpringBootApplication public class CloudDemoProductApplication {

  1. public static void main(String[] args) {
  2. SpringApplication.run(CloudDemoProductApplication.class, args);
  3. }

}

  1. - 启动项目,查看是否注册到eureka注册中心上:下图所示:服务已经注册到注册中心中;
  2. ![image.png](https://cdn.nlark.com/yuque/0/2021/png/1416299/1627975925200-87873db6-1c42-4223-a3fe-8a6293aca235.png#height=434&id=Hxt6r&margin=%5Bobject%20Object%5D&name=image.png&originHeight=868&originWidth=1710&originalType=binary&ratio=1&size=113668&status=done&style=none&width=855)
  3. - 访问项目路径,查看RestController接口是否能正确返回数据:http://localhost:9085/product/getProduct,如下图,接口能正确返回数据
  4. ![image.png](https://cdn.nlark.com/yuque/0/2021/png/1416299/1627976013565-c34b6e76-833a-48c8-a6e5-55ee9be06791.png#height=74&id=mU4Yt&margin=%5Bobject%20Object%5D&name=image.png&originHeight=148&originWidth=714&originalType=binary&ratio=1&size=10719&status=done&style=none&width=357)
  5. <a name="wHnFr"></a>
  6. ### 5.创建子项目—消费者
  7. cloud-demo-consumer
  8. - module的创建过程同product工程的创建过程,此处就不细写了,主要写一下相关的配置文件
  9. - 修改该子项目pom.xml的父依赖
  10. ```java
  11. <parent>
  12. <groupId>org.springframework.boot</groupId>
  13. <artifactId>spring-boot-starter-parent</artifactId>
  14. <version>2.5.3</version>
  15. <relativePath/> <!-- lookup parent from repository -->
  16. </parent>
  17. 改为
  18. <parent>
  19. <artifactId>cloud-demo</artifactId>
  20. <groupId>com.example</groupId>
  21. <version>0.0.1-SNAPSHOT</version>
  22. <relativePath/> <!-- lookup parent from repository -->
  23. </parent>
  • 添加依赖

    1. <dependencies>
    2. <!-- entity依赖添加到product工程中 -->
    3. <dependency>
    4. <groupId>com.example</groupId>
    5. <artifactId>cloud-demo-entity</artifactId>
    6. <version>0.0.1-SNAPSHOT</version>
    7. </dependency>
    8. <dependency>
    9. <groupId>org.springframework.cloud</groupId>
    10. <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
    11. </dependency>
    12. </dependencies>
  • 修改resources下的application.properties为application.yml并添加如下配置: ```java server: servlet: context-path: /consumer port: 9086 spring: application: name: consumer-server eureka: client: service-url:

    1. defaultZone: http://localhost:9080/eureka-server/eureka
  1. - 启动类添加注解:@EnableEurekaClient
  2. ```java
  3. @EnableEurekaClient
  4. @SpringBootApplication
  5. public class CloudDemoConsumerApplication {
  6. public static void main(String[] args) {
  7. SpringApplication.run(CloudDemoConsumerApplication.class, args);
  8. }
  9. }

三.整合 openfeign

  • 第一步:添加依赖openfeign

    1. <dependency>
    2. <groupId>org.springframework.cloud</groupId>
    3. <artifactId>spring-cloud-starter-openfeign</artifactId>
    4. </dependency>
  • 第二步:启动类中添加注解:@EnableFeignClients,表示开启fegin客户端(该操作我们已经在上面搞定)

  • 第三步:创建service接口,在接口的类名称上指明服务名称(application name)和服务的应用名称(contest-path),并在接口中添加方法,方法名上添加 @RequestMapping注解,注解的value就是你要访问的方法的路径;并写明返回值和参数(如果有参数的话,需要使用@RequestParam标注参数名称),具体如下:

下图为我服务的接口相关信息:

  • 第四步:创建controller,注入service,并进行调用 ```java package com.example.clouddemoconsumer.controller;

import com.example.clouddemoconsumer.service.ProductService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController;

@RestController public class ConsumerController { @Autowired private ProductService productService;

  1. @RequestMapping(value = "getConsumer")
  2. public String getConsumer(){
  3. String str = productService.getProduct();
  4. return str;
  5. }

}

  1. ```java
  2. package com.example.clouddemoconsumer.service;
  3. import org.springframework.cloud.openfeign.FeignClient;
  4. import org.springframework.stereotype.Component;
  5. import org.springframework.web.bind.annotation.RequestMapping;
  6. //name 为product项目中application.yml配置文件中的application.name;
  7. //path 为product项目中application.yml配置文件中的context.path;
  8. @FeignClient(name = "product-server",path ="/product" )
  9. @Component
  10. public interface ProductService {
  11. @RequestMapping(value = "getProduct")
  12. String getProduct();
  13. }
  • 启动项目,查看consumer项目是否被注册到注册中心,并访问getConsumer接口;

服务已经被注册到注册中心;
image.png

image.png

使用Spring Cloud Ribbon RestTemplate

通过Spring Cloud Ribbon的封装,我们在微服务架构中使用客户端负载均衡调用非常简单,只需要如下两步:

  • 服务提供者只需要启动多个服务实例并注册到一个注册中心或是多个相关联的服务注册中心。
  • 服务消费者直接通过调用被@LoadBalanced注解修饰过的RestTemplate来实现面向服务的接口调用。

这样,我们就可以将服务提供者的高可用以及服务消费者的负载均衡调用一起实现了。

生产者提供接口,支持消费者使用

  1. @RestController
  2. public class ProductController {
  3. @RequestMapping(value = "getProduct")
  4. public String getProduct(){
  5. Product p = new Product();
  6. return p.toString();
  7. }
  8. @RequestMapping(value = "getProductMap",method = RequestMethod.GET)
  9. public String getProductMap(String name){
  10. Map<String,Product> m = new HashMap<>(2);
  11. Product p1 = new Product();
  12. Product p2 = new Product("zhang",10,"北京市历史互通","6666.qq.com");
  13. m.put("p1",p1);
  14. m.put("p2",p2);
  15. return JSON.toJSONString(m);
  16. }
  17. }

消费者

  1. @RestController
  2. public class ConsumerController {
  3. @Autowired
  4. private NewProductService newProductService;
  5. @RequestMapping(value = "getProduct")
  6. public String getProduct(){
  7. return newProductService.getProduct();
  8. }
  9. @RequestMapping(value = "getProductMap")
  10. public String getProductMap(@RequestParam(value = "name") String name){
  11. return newProductService.getProductMap(name);
  12. }
  13. @RequestMapping(value = "getProductMap1")
  14. public Map getProductMap1(@RequestParam(value = "name") String name){
  15. return newProductService.getProductMap1(name);
  16. }
  17. }
  1. @Service
  2. public class NewProductService {
  3. @Bean
  4. @LoadBalanced
  5. RestTemplate restTemplate()
  6. {
  7. return new RestTemplate();
  8. }
  9. @Autowired
  10. private RestTemplate restTemplate;
  11. public String getProduct(){
  12. return restTemplate.getForObject("http://product-server/product/getProduct",String.class);
  13. }
  14. // getForEntity函数,该方法返回的是ResponseEntity,该对象是Spring对HTTP请求响应的封装
  15. //返回的ResponseEntity对象的body内容类型会根据第二个参数转换为String类型
  16. public String getProductMap(String name){
  17. String url = "http://product-server/product/getProductMap?name="+name;
  18. ResponseEntity<String> r = restTemplate.getForEntity(url,String.class);
  19. return r.getBody();
  20. }
  21. // 返回body是一个Map对象类型
  22. public Map getProductMap1(String name){
  23. String url ="http://product-server/product/getProductMap?name={1}";
  24. ResponseEntity<Map> r1 =restTemplate.getForEntity(url,Map.class,"ss");
  25. Map<String,Product> p = r1.getBody();
  26. System.out.println(p);
  27. return p;
  28. }
  29. }

image.png

image.png

image.png