SpringCloud使用总结

7 篇文章 0 订阅

前言

  • 微服务是什么?
    微服务是一种架构风格,一个大型复杂软件应用由一个或多个微服务组成。系统中的各个微服务可被独立部署,各个微服务之间是松耦合的。每个微服务仅关注于完成一件任务并很好地完成该任务。在所有情况下,每个任务代表着一个小的业务能力。
    (著名的"2 pizza 团队"很好的诠释了这一解释: 2 pizza 团队最早是亚马逊 CEO Bezos提出来的,意思是说单个服务的设计,所有参与人从设计、开发、测试、运维所有人加起来 只需要2个披萨就够了 。 )
  • 微服务与单体应用的优缺点
    单体架构在规模比较小的情况下工作情况良好,但是随着系统规模的扩大,它暴露出来的问题也越来越多:
    1.可复用性差:服务被打包在应用中,功能不易复用;
    2.系统启动慢,一个进程包含了所有的业务逻辑,涉及到的启动模块过多,导致系统的启动、重启时间周期过长。
    3.维护成本高: 随着时间推移,系统逐渐臃肿,代码逐渐复杂,维护起来相当困难。
    微服务的优点:
    1.高内聚,低耦合。便于复用和维护。
    2.单独部署,独立开发;
    微服务的缺点:
    1.开发成本高。
    2.多服务,增加了系统架构的复杂度。
    建议:简单的应用系统还是用单体架构,业务复杂的应用可以使用微服务架构。

(一)服务注册,服务发现(如eureka,consul)

服务注册与发现: 服务注册是指微服务启动时,将自己的信息注册到服务发现组件上的过程.
服务发现是指查询可用微服务列表及其网络地址的机制。

  • 添加依赖:
// Eureka Server
compile('org.springframework.cloud:spring-cloud-starter-netflix-eureka-server')
  • 配置文件
server.port= 8761
##实例主机名称
eureka.instance.hostname= localhost

##实例是否在eureka服务器上注册自己的信息以供其他服务发现,默认为true
eureka.client.registerWithEureka= false

##此客户端是否获取eureka服务器注册表上的注册信息,默认为true
##此时是服务端,故不需要配置为true
eureka.client.fetchRegistry= false
##服务的URL
eureka.client.serviceUrl.defaultZone= http://${eureka.instance.hostname}:${server.port}/eureka/
#自我保护模式,当出现出现网络分区、eureka在短时间内丢失过多客户端时,会进入自我保护模式,
#即一个服务长时间没有发送心跳,eureka  也不会将其删除,默认为true
eureka.server.enable-self-preservation=false
  • SpringBoot启动类
@EnableEurekaServer
@SpringBootApplication
public class Application {
    public static void main(String[] args){
        SpringApplication.run(Application.class, args);
    }
}

(二)服务间的相互调用(如feign,ribbon)

本人在开发期间,想要让feign能够被其他模块引用,增加复用性,故采用这种项目结构:
API项目结构
API实现:
API实现
DTO:
DTO数据类型
feign:
供外部调用的feign结构
(1)实现API相关功能,用springboot搭建好环境:
在这里插入图片描述
controller:

@RestController
@RequestMapping("/cities")
public class CityDataController {
    @Autowired
    CityDataService cityDataService;
    @GetMapping("/")
    public List<City> getAll() throws Exception{
        return cityDataService.listCity();
    }
}

特别注意:本人在开发过程中,将SpringBoot启动程序放在city-data-feign中,故调用一直失败,应当放在API实现中(即此处controller实现的模块),并保证调用时,该springboot是启动的状态。

(2)feign(被调用)需要引入的依赖:

buildscript {
    ext {
        springBootVersion = '2.0.6.RELEASE'
    }
    // 使用了Maven的中央仓库及Spring自己的仓库(也可以指定其他仓库)
    repositories {
        // mavenCentral()
        maven { url "https://repo.spring.io/snapshot" }
        maven { url "https://repo.spring.io/milestone" }
        maven { url "http://maven.aliyun.com/nexus/content/groups/public/" }
    }
    // 依赖关系
    dependencies {

        // classpath 声明了在执行其余的脚本时,ClassLoader 可以使用这些依赖项
        classpath("org.springframework.boot:spring-boot-gradle-plugin:${springBootVersion}")
    }
}
apply plugin: 'java'
apply plugin: 'org.springframework.boot'
apply plugin: 'io.spring.dependency-management'

group 'com.funnee.weather.forecast'
version '1.0-SNAPSHOT'

sourceCompatibility = 1.8

repositories {
    //mavenCentral()
    maven { url "https://repo.spring.io/snapshot" }
    maven { url "https://repo.spring.io/milestone" }
    maven { url "http://maven.aliyun.com/nexus/content/groups/public/" }
}
ext{
    springCloudVersion = 'Finchley.SR2'
}
dependencies {
    compile( 'org.springframework.boot:spring-boot-starter-web')
    // Eureka Client相关依赖
    compile('org.springframework.cloud:spring-cloud-starter-netflix-eureka-client')
    // Feign相关依赖
    compile('org.springframework.cloud:spring-cloud-starter-openfeign')
    //引入同项目的模块
    compile project(':city-data-common')
}
dependencyManagement {
    imports {
        mavenBom "org.springframework.cloud:spring-cloud-dependencies:${springCloudVersion}"
    }
}

(3)feign client代码实现:

@FeignClient(name="city-data-feign")
@RequestMapping("/cities")
public interface CityDataClient {
    @GetMapping("/")
    List<City> getAll();
}

(4)feign的调用代码实现:

  • 引入依赖
compile project(':city-data-feign')
  • 代码实现
@Controller
@RequestMapping("/testInvoke")
public class WeatherReportController {
	@Autowired
	CityDataClient cityDataClient;
	@Autowired
	WeatherReportService weatherReportService;
	@GetMapping("/cityId/{cityId}")
	public ModelAndView getByCityId(@PathVariable("cityId") String cityId, Model model) throws Exception {
		model.addAttribute("cityId", cityId);
		model.addAttribute("cityList", cityDataClient.getAll());
		return new ModelAndView("test", "testModel", model);
	}
}

(三)API网关(如Nginx,zuul)

能实现如认证,限流,负载均衡,动态路由等功能。
本文采用zuul,能很好的对接SpringCloud。

  • 引入依赖:
// Zuul
compile('org.springframework.cloud:spring-cloud-starter-netflix-zuul')
  • 配置文件:
server.port=8085
spring.application.name= eureka-client-zuul
eureka.client.serviceUrl.defaultZone= http://localhost:8761/eureka/

##转发请求匹配/cityData/**转发给city-data-feign应用处理
##若要实现负载均衡,在不同端口起几个city-data-feign
zuul.routes.cities.path=/cityData/**
zuul.routes.cities.serviceId= city-data-feign

zuul.routes.weather.path= /weatherData/**
zuul.routes.weather.serviceId= weather-data-feign
  • 主程序:
@EnableDiscoveryClient
@SpringBootApplication
@EnableZuulProxy
public class Application {
    public static void main(String[] args){
        SpringApplication.run(Application.class, args);
    }
}

(四)配置中心

Config支持我们使用的请求的参数规则为:

  • / { application } / { profile } [ / { label } ]
  • / { application } - { profile }.yml
  • / { application } - { profile }.properties
  • / { label } / { application } - { profile }.yml
  • / { label } / { application } - { profile }.properties

<1>引入相关依赖

// Eureka Client
compile('org.springframework.cloud:spring-cloud-starter-netflix-eureka-client')
// config server
compile('org.springframework.cloud:spring-cloud-config-server')

<2>配置文件

server.port= 8888
spring.application.name= config-server

eureka.client.serviceUrl.defaultZone= http://localhost:8761/eureka/
# 配置git仓库地址
spring.cloud.config.server.git.uri=https://github.com/linhaoSmash/configServer.git
# 配置git仓库路径
spring.cloud.config.server.git.searchPaths=config-server
# 访问git仓库的用户名
spring.cloud.config.server.git.username=******
# 访问git仓库的密码
spring.cloud.config.server.git.password=*****
spring.cloud.config.server.git.default-label= master

######### 访问远程仓库时无需配置下面的配置项 ########
# 配置本地config.server 下 configs路径
spring.cloud.config.server.native.search-locations=file:C:\\Users\\74061\\Desktop\\weather-forecast\\config-server\\configs
# 设置为native 会从本地获取配置文件,设置dev会走git方式
spring.profiles.active=dev
###############################################

<3>SpringBoot启动类实现

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

<4>测试结果
在GitHub上的配置文件目录结构:
在这里插入图片描述
通过configServer端访问远程配置文件:
在这里插入图片描述
根据请求参数的规则,换一种URL访问依然成功:
在这里插入图片描述
在这里插入图片描述
<5>bootstrap.yml与application.yml
可以通过设置spring.cloud.bootstrap.enabled=false来禁用bootstrap
5.1 加载顺序
这里主要是说明application和bootstrap的加载顺序。
bootstrap.yml(bootstrap.properties)先加载
application.yml(application.properties)后加载

bootstrap.yml 用于应用程序上下文的引导阶段。Spring Cloud会创建一个Bootstrap Context,作为Spring应用的Application Context的父上下文.
5.2 典型的应用场景如下:

当使用 Spring Cloud Config Server 的时候,你应该在 bootstrap.yml 里面指定
spring.application.name 和 spring.cloud.config.server.git.uri 和一些加密/解密的信息

当使用 Spring Cloud 的时候,配置信息一般是从 config server 加载的,为了取得配置信息(比如密码等),你需要一些提早的或引导配置。因此,把 config server 信息放在 bootstrap.yml,用来加载真正需要的配置信息。

bootstrap.yml一般放在configServer外作为应用的初始配置。

(五)服务熔断

服务不可用或者访问速度慢,所有依赖该服务的服务都被影响,也被成为血崩效应。
熔断机制是应对雪崩效应的一种微服务链路保护机制。我们在各种场景下都会接触到熔断这两个字。高压电路中,如果某个地方的电压过高,熔断器就会熔断,对电路进行保护。股票交易中,如果股票指数过高,也会采用熔断机制,暂停股票的交易。同样,在微服务架构中,熔断机制也是起着类似的作用。

示例代码:

  • 引入依赖:
// Hystrix
compile('org.springframework.cloud:spring-cloud-starter-netflix-hystrix')
  • 配置文件:
#feign断路器
feign.hystrix.enabled=true
#断路器超时时间,不配置容易报timeout异常
hystrix.command.default.execution.isolation.thread.timeoutInMilliseconds=5000
  • 服务入口程序:
@SpringBootApplication
@EnableDiscoveryClient
@EnableCircuitBreaker//断路器
@EnableFeignClients(value = {"eureka-client-zuul"},basePackages = {"com.funnee.weather.forecast.weatherReport.zuul"})
@ComponentScan(basePackages = {"com.funnee.weather.forecast.weatherReport.zuul","com.funnee.weather.forecast.cityData.feign","com.funnee.weather.forecast.weatherData.feign"})
public class WeatherReportApplication {
    public static void main(String[] args){
        SpringApplication.run(WeatherReportApplication.class, args);
    }
}
  • 两种写法:
    1)回调类用法:
  • Feign:
//此处走的是API网关
@FeignClient(name="eureka-client-zuul", fallback= DataClientFallback.class)
public interface DataClient {
    @GetMapping("/cityData/cities/")
    List<City> getAll();

    @GetMapping("/weatherData/weather/cityId/{cityId}")
    WeatherResponse getWeatherByCityId(@PathVariable("cityId") String cityId);

    @GetMapping("/weatherData/weather/cityName/{cityName}")
    WeatherResponse getWeatherByCityName(@PathVariable("cityName") String cityName);

    @PostMapping("/weatherData/weather/cityId/{cityId}")
    void syncDateByCityId(@PathVariable("cityId") String cityId);
}
  • 失败回调类:
/**
 * DataClient Fallback.
 * DataClient调用失败回调类
 */
@Component
public class DataClientFallback implements DataClient {

	/**为FeignClient加调用失败或者超时后后的默认实现**/
	
    @Override
    public List<City> getAll() {
        List<City> cityList = null;
        cityList = new ArrayList<>();

        City city = new City();
        city.setCityId("101280601");
        city.setCityName("深圳");
        cityList.add(city);

        city = new City();
        city.setCityId("101280301");
        city.setCityName("惠州");
        cityList.add(city);

        return cityList;
    }

    @Override
    public WeatherResponse getWeatherByCityId(String cityId) {
        return null;
    }
    @Override
    public WeatherResponse getWeatherByCityName(String cityName) {
        return null;
    }
    @Override
    public void syncDateByCityId(String cityId) {
	//...
    }

}

2)回调方法用法:

@RestController
@RequestMapping("/cities")
public class CityDataController {
    @Autowired
    CityDataService cityDataService;
    @GetMapping("/")
    @HystrixCommand(fallbackMethod="defaultCities")
    public List<City> getAll() throws Exception{
        return cityDataService.listCity();
    }

    /**
     * 熔断回调方法
     * 如果调用Feign失败会返回这个方法的数据
     * @return
     */
    public List<City> defaultCities() {
        List<City> cityList = null;
        cityList = new ArrayList<>();

        City city = new City();
        city.setCityId("101280601");
        city.setCityName("深圳");
        cityList.add(city);

        return cityList;
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Funnee

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值