学习搭建spring cloud的一点笔记

spring cloud 入门

自己学习spring cloud的一篇笔记…

简介

Spring Cloud将现在非常流行的一些技术整合到一起,实现了诸如:配置管理,服务发现,智能路由, 负载均衡,熔断器,控制总线,集群状态等功能;协调分布式环境中各个系统,为各类服务提供模板性配置。其主要涉及的组件包括:

  • Eureka:注册中心
  • Zuul、Gateway:服务网关
  • Ribbon:负载均衡
  • Feign:服务调用
  • Hystrix或Resilience4j:熔断器

1.搭建服务提供方,消费方

1.1 准备工作

创建一个空的maven工程,在pom文件中添加如下

 <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.1.5.RELEASE</version>
        <relativePath/>
    </parent>

    <properties>
        <java.version>1.8</java.version>
        <spring-cloud.version>Greenwich.SR1</spring-cloud.version>
        <mapper.starter.version>2.1.5</mapper.starter.version>
        <mysql.version>5.1.46</mysql.version>
    </properties>

    <dependencyManagement>
        <dependencies>
            <!-- springCloud -->
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-dependencies</artifactId>
                <version>${spring-cloud.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
            <!-- 通用Mapper启动器 -->
            <dependency>
                <groupId>tk.mybatis</groupId>
                <artifactId>mapper-spring-boot-starter</artifactId>
                <version>${mapper.starter.version}</version>
            </dependency>
            <!-- mysql驱动 -->
            <dependency>
                <groupId>mysql</groupId>
                <artifactId>mysql-connector-java</artifactId>
                <version>${mysql.version}</version>
            </dependency>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-starter-config</artifactId>
            </dependency>
        </dependencies>
    </dependencyManagement>
    <dependencies>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.16.20</version>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

在这里创建spring boot脚手架,并对接下来将要用到的工具进行了版本的管理.

1.2 创建服务提供者

创建一个新的module service,继承刚才创建的父工程,用以提供服务.在pom中添加依赖

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>tk.mybatis</groupId>
            <artifactId>mapper-spring-boot-starter</artifactId>
        </dependency>
      
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.47</version>
        </dependency>
    </dependencies>

创建application.yml文件

server:
  port: 9091
spring:
  datasource:
    driver-class-name: com.mysql.jdbc.Driver
    url: jdbc:mysql:///springcloud
    username: root
    password: 123
  application:
    name: user-service
mybatis:
 type-aliases-package: com.example.pojo

创建启动类 com.example.ServiceApplication

package com.example;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import tk.mybatis.spring.annotation.MapperScan;

@SpringBootApplication
@MapperScan("com.example.mapper")//配置包扫描
public class ServiceApplication {
    public static void main(String[] args) {
        SpringApplication.run(ServiceApplication.class, args);
    }
}

编写user实体类 com.example.pojo.User

package com.example.pojo;

import lombok.Data;
import tk.mybatis.mapper.annotation.KeySql;
import javax.persistence.Id;
import javax.persistence.Table;
import java.util.Date;

@Data
@Table(name = "tb_user")
public class User {
    // id
    @Id
    //开启主键自动回填
    @KeySql(useGeneratedKeys = true)
    private Long id;
    // 用户名
    private String userName;
    // 密码
    private String password;
    // 姓名
    private String name;
    // 年龄
    private Integer age;
    // 性别,1男性,2女性
    private Integer sex;
    // 出生日期
    private Date birthday;
    // 创建时间
    private Date created;
    // 更新时间
    private Date updated;
    // 备注
    private String note;
}

编写mapper接口,继承Mapper类,通过继承该类,可以不用写sql语句调用基本的数据库操作

package com.example.mapper;

import com.example.pojo.User;
import tk.mybatis.mapper.common.Mapper;

public interface UserMapper extends Mapper<User> {   
}

编写服务类 com.example.service.UserService 提供查询服务

package com.example.service;

import com.example.mapper.UserMapper;
import com.example.pojo.User;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

@Service
public class UserService {

    @Autowired
    UserMapper mapper;

    public User findById(int id) {
        return mapper.selectByPrimaryKey(id);
    }
}

提供一个对外查询的控制器 com.example.controller.UserController

package com.example.controller;

import com.example.pojo.User;
import com.example.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/user")
public class UserController {

    @Autowired
    UserService service;

    @RequestMapping("/{id}")
    public User findById(@PathVariable int id) {
        return service.findById(id);
    }
}

启动服务,通过访问链接http://localhost:9091/user/1 ,测试服务,前提示数据库中已有id=1的数据.

1.3创建服务消费者

​ 创建新的module --> consume,同样继承父工程 编写pom.xml ,添加web启动器依赖

<dependency>
     <groupId>org.springframework.boot</groupId>
     <artifactId>spring-boot-starter-web</artifactId>
</dependency>

编写yml

server:
  port: 8081

spring:
  application:
    name: user-consumer

​ 创建启动器类,并在其中注册RestTemplate

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

Spring提供了一个RestTemplate模板工具类,对基于Http的客户端进行了封装,并且实现了对象与json的序列化和
反序列化,非常方便。RestTemplate并没有限定Http的客户端类型,而是进行了抽象,目前常用的3种都有支持:

HttpClient
OkHttp
JDK原生的URLConnection(默认的)

创建pojo实体类

package com.example.pojo;

import lombok.Data;
import java.util.Date;

@Data
public class User {
    // id
    private Long id;
    // 用户名
    private String userName;
    // 密码
    private String password;
    // 姓名
    private String name;
    // 年龄
    private Integer age;
    // 性别,1男性,2女性
    private Integer sex;
    // 出生日期
    private Date birthday;
    // 创建时间
    private Date created;
    // 更新时间
    private Date updated;
    // 备注
    private String note;
}

创建控制器 com.example.controller.UserController,通过RestTemplate远程调用服务

package com.example.controller;

import com.example.pojo.User;
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.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;

@RestController
@RequestMapping("/user")
public class UserController {
    
    @Autowired
    private RestTemplate restTemplate;
    
    @GetMapping("{id}")
    public User queryById(@PathVariable Long id){
        String url = "http://localhost:9091/user/" + id;
        return restTemplate.getForObject(url, User.class);
    }
}

运行服务提供者和服务消费者 通过访问http://localhost:8081/user/1 可以获取由服务提供者提供的查询服务.

2.搭建注册中心

2.1搭建eureka-server module

在pom.xml中添加依赖

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

编写启动器Application com.example.Application

package com.example;

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

//声明当前应用为Eureka服务
@EnableEurekaServer
@SpringBootApplication
public class Application {
    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }
}

编写配置文件

server:
  port: 10086

spring:
  application:
    name: eureka-server # 应用名称,会在Eureka中作为服务的id标识(serviceId)
eureka:
  client:
    service-url: # EurekaServer的地址,现在是自己的地址,如果是集群,需要写其它Server的地址。
      defaultZone: http://127.0.0.1:10086/eureka
    register-with-eureka: false # 不注册自己
    fetch-registry: false #不拉取

这时候可以通过访问 http://127.0.0.1:10086 查看eureka 界面

2.2 注册服务

在service pom中添加eureka依赖

<!-- Eureka客户端 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>

通过在启动类上添加 @EnableDiscoveryClient 注解来开启Eureka客户端功能

@EnableDiscoveryClient
@SpringBootApplication
@MapperScan("com.example.mapper")
public class ServiceApplication {
    public static void main(String[] args) {
        SpringApplication.run(ServiceApplication.class, args);
    }
}

编写yml文件 ,添加eureka 配置

eureka:
  client:
    service-url:
      defaultZone: http://localhost:10086/eureka

yml中 spring application name 来指定应用名称,将来会作为服务的id使用

2.3 服务发现

在消费方pom中添加依赖,与服务提供方一样

<!-- Eureka客户端 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>

一样的,在启动类上添加 @EnableDiscoveryClient 注解来开启Eureka客户端功能 ,在yml中添加注册中心地址;

代码同上>>>>注册服务代码;

修改控制器,用DiscoveryClient类的方法,根据服务名称,获取服务实例

@RestController
@RequestMapping("/user")
public class UserController {
    @Autowired
    DiscoveryClient discoveryClient;

    @Autowired
    private RestTemplate restTemplate;

    @GetMapping("{id}")
    public User queryById(@PathVariable Long id){
        //String url = "http://localhost:9091/user/" + id;
        List<ServiceInstance> serviceInstanceList = discoveryClient.getInstances("user-service");
        ServiceInstance serviceInstance = serviceInstanceList.get(0);
        String url = "http://" + serviceInstance.getHost() + ":" + serviceInstance.getPort()
                + "/user/" + id;
        return restTemplate.getForObject(url, User.class);
    }
}

此时,将服务提供方,消费方,注册中心打开,消费方就可以通过服务名从注册中心获取服务提供方url了;

当有多个eureka注册中兴需要搭建集群时,我们将注册中心yml中配置做修改即可

eureka:
  client:
    service-url:
		# eureka服务地址;如果是集群则是其它服务器地址,后面要加/eureka
      defaultZone: http://127.0.0.1:10086/eureka			
# 是否注册自己,自身不提供服务所以不注册
	 #register-with-eureka: false
# 是否拉取服务
	 #fetch-registry: false

注意把register-with-eureka和fetch-registry修改为true或者注释掉

EurekaServer不止一个,因此 user-service 项目注册服务或者 consumer-demo 获取服务的时候,service-url参
数需要修改为如下 :

eureka:
  client:
    service-url: # EurekaServer地址,多个地址以','隔开
      defaultZone: http://127.0.0.1:10086/eureka,http://127.0.0.1:10087/eureka

3.负载均衡

3.1 开启负载均衡

直接在服务消费方启动器中修改–>在RestTemplate的配置方法上添加 @LoadBalanced 注解

@SpringBootApplication
@EnableDiscoveryClient
public class ComsumeApplication {
    public static void main(String[] args) {
        SpringApplication.run(ComsumeApplication.class, args);
    }

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

修改 消费方控制器 调用方式,不再手动获取ip和端口,而是直接通过服务名称调用;

@GetMapping("{id}")
    public User queryById(@PathVariable Long id){
        //String url = "http://localhost:9091/user/" + id;
//        List<ServiceInstance> serviceInstanceList = discoveryClient.getInstances("user-service");
//        ServiceInstance serviceInstance = serviceInstanceList.get(0);
//        String url = "http://" + serviceInstance.getHost() + ":" + serviceInstance.getPort()
//                + "/user/" + id;
        String url = "http://user-service/user/" + id;
        return restTemplate.getForObject(url, User.class);
    }

4.熔断器Hystrix

Hystrix解决雪崩问题的手段主要是服务降级,包括:
线程隔离
服务熔断

添加熔断器 ,首先在消费端引入依赖

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

在启动类 ConsumerApplication 上添加注解:@EnableCircuitBreaker

//@SpringBootApplication
//@EnableDiscoveryClient
//@EnableCircuitBreaker//熔断器
@SpringCloudApplication//组合了上面的三个注解
public class ComsumeApplication {
    public static void main(String[] args) {
        SpringApplication.run(ComsumeApplication.class, args);
    }

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

在控制器中编写降级逻辑

 @GetMapping("{id}")
    @HystrixCommand(fallbackMethod = "queryByIdFallback")//添加注解
    public String queryById(@PathVariable Long id){
   
        String url = "http://user-service/user/" + id;
        return restTemplate.getForObject(url, String.class);
    }

    public String queryByIdFallback(Long id){
        return "请求失败!网络正忙";
    }
}

@DefaultProperties(defaultFallback = “defaultFallBack”):在类上指明统一的失败降级方法;该类中所有方法
返回类型要与处理失败的方法的返回类型一致

当方法较多时可以采用此方法

@RestController
@RequestMapping("/user")
@DefaultProperties(defaultFallback = "defaultFallback")
public class UserController {
    @Autowired
    DiscoveryClient discoveryClient;

    @Autowired
    private RestTemplate restTemplate;

    @GetMapping("{id}")
    //@HystrixCommand(fallbackMethod = "queryByIdFallback")
    @HystrixCommand
    public String queryById(@PathVariable Long id){
      
        String url = "http://user-service/user/" + id;
        return restTemplate.getForObject(url, String.class);
    }

    public String queryByIdFallback(Long id){
        return "请求失败!网络正忙";
    }

    public String defaultFallback(){
        return "默认回复!";
    }
}

Hystrix的默认超时时长为1,我们可以通过配置修改这个值;修改 yml 添加如下配置:

hystrix:
  command:
    default:
      execution:
        isolation:
          thread:
            timeoutInMilliseconds: 2000 #熔断器等待时间

当访问时间超过2秒时会开启熔断

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值