> 文档中心 > SpringCloud微服务框架一套就够(上集)

SpringCloud微服务框架一套就够(上集)


个人简介

作者是一个来自河源的大三在校生,以下笔记都是作者自学之路的一些浅薄经验,如有错误请指正,将来会不断的完善笔记,帮助更多的Java爱好者入门。

文章目录

    • 个人简介
    • Spring Cloud Netflix
      • 搭建提供者、消费者模块
      • 引入注册中心SpringCloud Eureka
          • Bug:引入Eureka后报错。
      • 搭建Eureka集群
      • 搭建提供者集群(为了负载均衡)
          • Bug:ribbon+restTemplate报错
        • 使用actuator功能
      • Feign/OpenFeign
          • Bug:OpenFeign调用失败报错405

Spring Cloud Netflix

搭建提供者、消费者模块

1:创建一个空Maven项目,删除src目录

SpringCloud微服务框架一套就够(上集)

2:父POM

     <packaging>pom</packaging>    <properties>  <spring.cloud-version>Hoxton.RELEASE</spring.cloud-version> <spring.boot-version>2.2.4.RELEASE</spring.boot-version> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding> <java.version>1.8</java.version> <maven.compiler.source>10</maven.compiler.source> <maven.compiler.target>10</maven.compiler.target> <encoding>UTF-8</encoding>    </properties>        <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>org.springframework.boot</groupId>  <artifactId>spring-boot-dependencies</artifactId>  <version>${spring.boot-version}</version>  <type>pom</type>  <scope>import</scope>     </dependency>     <dependency>  <groupId>org.mybatis.spring.boot</groupId>  <artifactId>mybatis-spring-boot-starter</artifactId>  <version>1.3.1</version>     </dependency>     <dependency>  <groupId>mysql</groupId>  <artifactId>mysql-connector-java</artifactId>  <version>5.1.47</version>     </dependency>     <dependency>  <groupId>com.alibaba</groupId>  <artifactId>druid</artifactId>  <version>1.0.9</version>     </dependency> </dependencies>    </dependencyManagement>    <build> <resources>     <resource>  <directory>src/main/java</directory>  <includes>      <include>/*.xml</include>  </includes>  <filtering>true</filtering>     </resource> </resources>    </build>

3.创建一个子模块,作微服务的提供者,端口号为8001

SpringCloud微服务框架一套就够(上集)

里面代码如下。。。

deptController

@RestControllerpublic class deptController {    private deptService deptService;    @Autowired    @Qualifier("deptServiceImpl")    public void setDeptService(com.boot.service.deptService deptService) { this.deptService = deptService;    }    @GetMapping(path = "/queryAllDept")    public List<dept> queryAllDept(){ return deptService.queryAllDept();    }}

dao层。deptMapper

@Mapper //把这个Mapper接口变成可以注入的Bean,*一定要。@Repository  //变成组件  *一定要public interface deptMapper {    List<dept> queryAllDept(); }

Mapper.xml

<mapper namespace="com.boot.dao.deptMapper">    <select id="queryAllDept" resultType="com.pojo.dept"> select deptid,deptName from dept;    </select> </mapper>

service层省略,和普通springBoot项目构建是一样的

application.yml(8001)

server:  port: 8001spring:  application:#    微服务名    name: provider_dept8001/8002  datasource:    url: jdbc:mysql://localhost:3306/ssmrl?serverTimezone=UTC    driver-class-name: com.mysql.jdbc.Driver    username: root    password: 18420163207    type: com.alibaba.druid.pool.DruidDataSource

创建一个子模块(springcloud-02-api),专门放实体类

SpringCloud微服务框架一套就够(上集)

public class dept implements Serializable {    private String deptid;    private String deptName;    public dept() {    }    public dept(String deptid, String deptName) { this.deptid = deptid; this.deptName = deptName;    }    public String getDeptid() { return deptid;    }    public void setDeptid(String deptid) { this.deptid = deptid;    }    public String getDeptName() { return deptName;    }    public void setDeptName(String deptName) { this.deptName = deptName;    }    @Override    public String toString() { return "dept{" +  "deptid='" + deptid + '\'' +  ", deptName='" + deptName + '\'' +  '}';    }}

创建另外一个子模块,springcloud-02-provider-dept8002

把springcloud-02-provider-dept8001代码全部复制上去,修改配置文件application.yml(8002)

server:  port: 8002spring:  application:#    微服务名    name: provider_dept8001/8002  datasource:    url: jdbc:mysql://localhost:3306/ssmrl?serverTimezone=UTC    driver-class-name: com.mysql.jdbc.Driver    username: root    password: 18420163207    type: com.alibaba.druid.pool.DruidDataSource

为什么我们要复制多一份微服务提供者代码?

因为考虑到后面我们要使用负载均衡ribbon或者openFeign(不过底层也是ribbon)

4.创建子模块springcloud-02-comsumer-dept80

controller层:

@RestControllerpublic class deptController80 {    @Autowired    private RestTemplate restTemplate;    private final String URL_DEPT="http://localhost:8001/";    @GetMapping("/comsumer/queryAllDept")    public List<dept> queryAllDept80(){ List<dept> res = restTemplate.getForObject(URL_DEPT + "queryAllDept", List.class); return res;    }}

因为默认的RestTemplate没有放入IOC容器中(也就是没有Bean),我们需要手动的放入IOC容器

config层

@Configurationpublic class restTemplateConfig {    @Bean    public RestTemplate restTemplate(){ return new RestTemplate();    }}

application.yml(80)

server:  port: 80#spring:  application:    name: comsumer_dept80

为什么微服务消费者层的端口是80,因为80端口可以省略不写,就比如我们打开百度,也是不需要写端口号的,因为微服务消费者层是给用户去访问的

消费者层只需要Controller去远程调用提供者的Controller方法即可,所以消费者层—不能—有dao,service层

这样提供者和消费者就搭建好了,接下来我们可以引入SpringCloud组件了。。。。。。。


引入注册中心SpringCloud Eureka

配置如下:

SpringCloud微服务框架一套就够(上集)

创建子模块springcloud-02-eureka7001(eureka注册中心服务端)

pom.xml

 <dependencies>  <dependency>     <groupId>org.springframework.boot</groupId>     <artifactId>spring-boot-starter-web</artifactId> </dependency>  <dependency>     <groupId>org.springframework.boot</groupId>     <artifactId>spring-boot-starter-actuator</artifactId> </dependency>   <dependency>     <groupId>org.springframework.cloud</groupId>     <artifactId>spring-cloud-starter-netflix-eureka-server</artifactId> </dependency>    </dependencies>

eureka模块主启动类

@SpringBootApplication@EnableEurekaServer //开启Eureka服务器public class SpringBootApplication7001 {    public static void main(String[] args) { SpringApplication.run(SpringBootApplication7001.class,args);    }}

application.yml(7001)

server:  port: 7001#  配置eurekaeureka:  instance:    hostname: eureka-server7001.com  client:    register-with-eureka: false    fetchRegistry: false    service-url:      defaultZone: http://${eureka.instance.hostname}:7001/eureka/

给提供者springcloud-02-provider-dept8001和8002修改如下

@SpringBootApplication@EnableEurekaClient //eureka客户端public class SpringBootApplication8001 {    public static void main(String[] args) { SpringApplication.run(SpringBootApplication8001.class,args);    }}
server:  port: 8001spring:  application:#    微服务名    name: provider_dept    datasource:    url: jdbc:mysql://localhost:3306/ssmrl?serverTimezone=UTC    driver-class-name: com.mysql.jdbc.Driver    username: root    password: 18420163207    type: com.alibaba.druid.pool.DruidDataSource#    eureka Clienteureka:  client:    service-url:      defaultZone: http://eureka-server7001.com:7001/eureka/    register-with-eureka: true    fetch-registry: false  instance:    instance-id: eureka-client8001
server:  port: 8002spring:  application:#    微服务名    name: provider_dept  datasource:    url: jdbc:mysql://localhost:3306/ssmrl?serverTimezone=UTC    driver-class-name: com.mysql.jdbc.Driver    username: root    password: 18420163207    type: com.alibaba.druid.pool.DruidDataSource#   eurekaeureka:  client:    fetch-registry: false    register-with-eureka: true    service-url:      defaultZone: http://eureka-server7001.com:7001/eureka/  instance:    prefer-ip-address: true    instance-id: eureka-client8002
Bug:引入Eureka后报错。

SpringCloud微服务框架一套就够(上集)

。。。取名要规范。。。

搭建Eureka集群

创建springcloud-02-eureka7002和7003两个模块

application.yml(7001)

server:  port: 7001#  配置eurekaeureka:  instance:    hostname: eureka-server7001.com  client:    register-with-eureka: false    fetchRegistry: false    service-url:      defaultZone: http://eureka-server7002.com:7002/eureka/,http://eureka-server7003.com:7003/eureka/   #集群版就修改这个。单机认自己,集群认其他     

application.yml(7002)

server:  port: 7002spring:  application:    name: eureka7002   #这里可有可无,除了微服务提供者。eureka:  instance:    hostname: eureka-server7002.com  client:    fetch-registry: false    register-with-eureka: false    service-url:      defaultZone: http://eureka-server7001.com:7001/eureka/,http://eureka-server7003.com:7003/eureka/

application.yml(7003)

server:  port: 7003spring:  application:    name: eureka7003eureka:  instance:    hostname: eureka-server7003.com  client:    register-with-eureka: false    fetch-registry: false    service-url:      defaultZone: http://eureka-server7001.com:7001/eureka/,http://eureka-server7002.com:7002/eureka/

其实Eureka集群没啥变化,也就是修改了serviceUrl的defaultZone罢了。记住一句话,单机版认自己,集群版认其他

搭建提供者集群(为了负载均衡)

在8001和8002微服务中修改defaultZone。

eureka:  client:    service-url:      defaultZone: http://eureka-server7001.com:7001/eureka/, http://eureka-server7002.com:7002/eureka/, http://eureka-server7003.com:7003/eureka/

还有8001和8002的微服务名要一致(spring.application.name)

spring:  application:#    微服务名    name: provider-dept  #8001和8002要一致

在springcloud-02-comsumer-dept80消费者层

@Configurationpublic class restTemplateConfig {    @Bean    @LoadBalanced //开启负载均衡    public RestTemplate restTemplate(){ return new RestTemplate();    }}
@SpringBootApplication@EnableEurekaClientpublic class SpringBootApplication80 {    public static void main(String[] args) { SpringApplication.run(SpringBootApplication80.class,args);    }}

用Ribbon+RestTemplate员工调用提供者的Controller方法

@RestControllerpublic class deptController80 {    @Autowired    private RestTemplate restTemplate;    private final String URL_DEPT="http://PROVIDER-DEPT/"; //实现负载均衡用提供者微服务名代替IP:Port    @GetMapping("/comsumer/queryAllDept")    public List<dept> queryAllDept80(){ List<dept> res = restTemplate.getForObject(URL_DEPT + "queryAllDept", List.class); return res;    }}

application.yml(80)

server:  port: 80#spring:  application:    name: comsumer-dept80eureka:  client:    fetch-registry: true    register-with-eureka: false    service-url:      defaultZone: http://eureka-server7001.com:7001/eureka/,http://eureka-server7002.com:7002/eureka/,http://eureka-server7003.com:7003/eureka/
Bug:ribbon+restTemplate报错

SpringCloud微服务框架一套就够(上集)

ribbon不支持微服务名有下划线(_),修改过来即可

spring:  application:#    微服务名    name: provider-dept

使用actuator功能

#暴露端点。使用actuator功能management:  endpoints:    web:      exposure: include: "*"

SpringCloud微服务框架一套就够(上集)

Feign/OpenFeign

创建子模块springcloud-02-comsumer-openFeign-dept80

所需的依赖

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

点进去看看

SpringCloud微服务框架一套就够(上集)

我们可以发现,Feign/openFeign的底层就是Ribbon,所以openFeign自带了负载均衡的功能,相较于Ribbon+restTemplate,openFeign无需手动的使用@LoadBalanced注解来开启负载均衡,而Ribbon需要在restTemplate的Bean上加这个注解才有负载均衡的能力

主启动类

@SpringBootApplication@EnableFeignClients //开启Feign的客户端支持public class springApplicationFeign80 {    public static void main(String[] args) { SpringApplication.run(springApplicationFeign80.class,args);    }}

编写微服务接口

@Service@FeignClient("PROVIDER-DEPT") //标注这个微服务接口是属于“PROVIDER-DEPT”这个微服务的public interface deptService { //下面的代码直接从微服务提供者的controller复制过来即可    @GetMapping(path = "/queryAllDept")    public List<dept> queryAllDept();}

然后便是使用

@RestControllerpublic class deptController {    @Autowired    private deptService deptService;    @RequestMapping("/feign/queryAllDept")    public List<dept> queryAllDept(){ return deptService.queryAllDept();    }}
Bug:OpenFeign调用失败报错405

错误类型:

SpringCloud微服务框架一套就够(上集)

原因是我们没有在提供者加上@PathVariable或者@RequestParam注解

解决方法一:在提供者加上@RequestParam注解(每个提供者传入的参数都要加上这个注解)

@RestControllerpublic class deptController {    private deptService deptService;    @Value("${server.port}")    private String port;    @Autowired    @Qualifier("deptServiceImpl")    public void setDeptService(com.boot.service.deptService deptService) { this.deptService = deptService;    }    @GetMapping(path = "/queryAllDept")    @HystrixCommand(fallbackMethod = "queryAllDept_Hystrix",commandProperties = {     @HystrixProperty(name = "circuitBreaker.enabled",value = "true"),     @HystrixProperty(name = "circuitBreaker.requestVolumeThreshold",value = "10"),     @HystrixProperty(name = "circuitBreaker.sleepWindowInMilliseconds",value = "20000"),//注意这是毫秒。1秒=1000毫秒     @HystrixProperty(name = "circuitBreaker.errorThresholdPercentage",value = "50")    })    public List<dept> queryAllDept(@RequestParam("id") String id){ List<dept> depts = deptService.queryAllDept(); depts.add(new dept("999",port)); if(Integer.parseInt(id)<0){     throw new RuntimeException(); } return depts;    }    /     * 服务熔断     */    public List<dept> queryAllDept_Hystrix(@RequestParam("id") String id){ List<dept> depts = deptService.queryAllDept(); depts.add(new dept("1066","Break")); return depts;    }}

修改openFeign的微服务接口

@Service@FeignClient("PROVIDER-DEPT")public interface deptService {    @GetMapping(path = "/queryAllDept")    public List<dept> queryAllDept(@RequestParam("id") String id);}

没有报错了!!!

SpringCloud微服务框架一套就够(上集)

解决方法二:在微服务提供者加上@PathVariable注解。。

省略!!!