SpringCloud 全家桶基本介绍及使用

SpringCloud 全家桶基本介绍及使用

1.微服务生态关系:

分为:SpringCloud Netflix,SpringCloud Apache,SpringCloud Alibaba

Netflix有:Eureka(注册中心)、Feign(服务调用、远程调用)、ribbon(负载均衡及重试)、Hystrix(熔断及降级)、Zuul(网关),Config(配置中心)

SpringCloud Apache官方:Service Registry(服务注册) Service Discovery(服务发现)、OpenFeign或者Restemplate(远程服务调用)、LoadBalance(负载均衡)、Circuit Breaker(熔断降级)、Gateway(网关)

SpringCloud Alibaba: 很火,后续单独做介绍

Netflix很多组件都不在维护,SpringBoot新版本官方逐渐把Netflix的组件移除,目前仅仅保留了Eureka,但是很多老项目还使用旧的组件,而且springcloud 组件很多思想都是相通的,还是有学习的必要。

2.单体应用和微服务

单体应用:把所有的功能及模块整合到一起,打成一个jar或者war包,部署到服务器启动应用
优点:适用于简单的项目,开发,测试及部署都简单
缺点:不适用于大型项目,随着项目不断发展功能越来越多,单体应用劣势就显示出来。

  1. 复杂度高:代码量巨大,多达百万或者千万,添加一个小功能可能会造成其他隐患。
  2. 技术债务:不坏不修复,不敢修复
  3. 持续部署困难:编译时间长,部署时间长,改一个小功能,全部部署,会导致无关的功能暂停使用
  4. 可靠性差:一个小的bug,造成整个应用无法崩溃无法使用
  5. 扩展受限:不利于扩展
  6. 阻碍创新:不利于引进新的技术。传统应用springmvc 改为springboot需要大量改动。

微服务:把整个系统按照业务拆分,拆分成一个个小服务,就是微服务,所有的微服务组成一个整体。分久必合合久必分

微服务优点

  1. 独立部署。不依赖其他服务,耦合性低,不用管其他服务的部署对自己的影响。
  2. 易于开发和维护:关注特定业务,所以业务清晰,代码量少,模块变的易开发、易理解、易维护。
  3. 启动块:功能少,代码少,所以启动快,有需要停机维护的服务,不会长时间暂停服务。
  4. 局部修改容易:只需要部署 相应的服务即可,适合敏捷开发。
  5. 技术栈不受限:java,node.js等
  6. 按需伸缩:某个服务受限,可以按需增加内存,cpu等。
  7. 职责专一。专门团队负责专门业务,有利于团队分工。
  8. 代码复用。不需要重复写。底层实现通过接口方式提供。
  9. 便于团队协作:每个团队只需要提供API就行,定义好API后,可以并行开发。

微服务缺点

  1. 分布式固有的复杂性:容错(某个服务宕机),网络延时,调用关系、分布式事务等,都会带来复杂。
  2. 分布式事务的挑战:每个服务有自己的数据库,有点在于不同服务可以选择适合自身业务的数据库。订单用MySQL,评论用Mongodb等。目前最理想解决方案是:柔性事务的最终一致性。
  3. 接口调整成本高:改一个接口,调用方都要改。
  4. 测试难度提升:一个接口改变,所有调用方都得测。自动化测试就变的重要了。API文档的管理也尤为重要。推荐:yapi。
  5. 运维要求高:需要维护 几十 上百个服务。监控变的复杂。并且还要关注多个集群,不像原来单体,一个应用正常运行即可。
  6. 重复工作:比如java的工具类可以在共享common.jar中,但在多语言下行不通,C++无法直接用java的jar包。

3.Eureka
注册中心,作于服务的自动注册与发现。分为两部分:Eureka Server和Eureka Client.微服务上线会将自己的IP,端口port,服务名servername提交到注册中心,注册中心维护者一份注册列表。服务的消费者会从注册中心拉取服务注册列表,获取具体的调用路由再去调用服务提供者。为了避免每次都访问注册中心,client会定时去server拉取注册表信息到缓存到client本地。

  1. 客户需要每30秒发送一次心跳来续租,3个30s没有续租,(默认90s续租到期)eureka服务端会在注册列表将这个实例删除。
  2. 客户端会定期从eureka 拉取注册列表进行增量更新,默认30s一次。
  3. 发送心跳续租、拉取注册列表和续租到期时间都可以自己设置。
  4. eureka提供的restful url: 可以查看eureka 的信息

Eureka集群搭建
修改host文件:
进入目录 C:\Windows\System32\drivers\etc,
在host文件上添加

127.0.0.1       euk1.com
127.0.0.1       euk2.com

新建springboot项目
添加依赖

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

在主启动类添加 @EnableEurekaServer 注解

@EnableEurekaServer
@SpringBootApplication
public class CloudEurekaApplication {

	public static void main(String[] args) {
		SpringApplication.run(CloudEurekaApplication.class, args);
	}
}

搭建集群需要两个以上服务,euk1.com 和euk2.com配置文件分别为:

#是否将自己注册到其他Eureka Server,默认为true 需要
spring.application.name=EurekaServer
#web端口,服务是由这个端口处理rest请求的
server.port=7901
eureka.client.register-with-eureka=true
#是否从eureka server获取注册信息, 需要
eureka.client.fetch-registry=true
#设置服务注册中心的URL,用于client和server端交流
#此节点应向其他节点发起请求
eureka.client.serviceUrl.defaultZone=http://euk2.com:7902/eureka/
#主机名,必填
eureka.instance.hostname=euk1.com
management.endpoint.shutdown.enabled=true
#是否将自己注册到其他Eureka Server,默认为true 需要
spring.application.name=EurekaServer
#web端口,服务是由这个端口处理rest请求的
server.port=7902
eureka.client.register-with-eureka=true
#是否从eureka server获取注册信息, 需要
eureka.client.fetch-registry=true
#设置服务注册中心的URL,用于client和server端交流
#此节点应向其他节点发起请求
eureka.client.serviceUrl.defaultZone=http://euk1.com:7901/eureka/
#主机名,必填
eureka.instance.hostname=euk2.com
management.endpoint.shutdown.enabled=true

euk1和euk2的服务名都是EurekaServer,表示他们都是同一个服务,端口分别问7901和7902,他们相互注册和拉取信息

启动项目,浏览器访问:localhost:7901
在这里插入图片描述
eureka server集群搭建成功。

eureka提供的restful url: 可以查看eureka 的信息

http://localhost:7901/eureka/apps
http://localhost:7901/eureka/status

DiscoveryClient接口:是springcloud 服务注册发现的顶级接口

自我保护机制:当Server在短时间内丢失过多客户端时,那么Server会进入自我保护模式,会保护注册表中的微服务不被注销掉。当网络故障恢复后,退出自我保护模式。

客户端每分钟续约数量小于客户端总数的85%时会触发保护机制

Eureka Client:
客户端将信息上报给服务端
引入依赖client

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

将自己的健康信息上报给server,加入健康检查监控依赖

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

配置文件:

server.port=8081
spring.application.name=consumer
#向注册中心注册url
eureka.client.serviceUrl.defaultZone=http://euk1.com:7901/eureka/
#将自己的健康信息上报给server,状态up或者down
eureka.client.healthcheck.enabled=true
management.endpoints.web.exposure.include=*

client 更改健康状态up,down,并将状态信息上报给eureka server. 如果为down则不再调用此服务

import org.springframework.boot.actuate.health.Health;
import org.springframework.boot.actuate.health.HealthIndicator;
import org.springframework.stereotype.Service;

@Service
public class HealthService implements HealthIndicator{
	
	private Boolean flag = true;
	
	public void setFlag(Boolean flag) {
		this.flag = flag;
	}
	
	public Boolean getFlag() {
		return flag;
	}

	@Override
	public Health health() {
		// TODO Auto-generated method stub
		if(flag) {
			return Health.up().build();
		}
		return Health.down().build();
		
		
	}

}

4.Actuator
健康检查,监控应用
引入依赖

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

默认只暴露health 和 info 两个端口,可以通过配置暴露所有的监控端点:

#健康检查,暴露所有的监控端点
management.endpoints.web.exposure.include=*

访问:http://localhost:7901/actuator 查看所有的监控端点
在这里插入图片描述
查看具体的监控端点:
例如:http://localhost:7901/actuator/health
在这里插入图片描述
5.RestTemplate+LoadBalance
实现远程调用和负载均衡

用于远程调用,发送http请求调用其它微服务接口
RestTemplate 整合LoadBalance

package com.example.demo;

import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.client.RestTemplate;

@Configuration
public class BeanConfig {
	
	@Bean
	//开启负载均衡,整合LoadBalance,整合后RestTemplate 只能通过服务名来调用服务,不能通过ip+端口调用服务
	@LoadBalanced
	public RestTemplate restTemplate() {
		return new RestTemplate();
	}
}

实体类People

package com.example.demo;

public class People {
	
	private String name;
	
	private Integer age;
	
	private String tel;
	
	private String addr;
	
	public People() {
		
	}
	
	public People(String name,Integer age,String tel,String addr) {
		super();
		this.name = name;
		this.age = age;
		this.tel = tel;
		this.addr = addr;
	}

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public Integer getAge() {
		return age;
	}

	public void setAge(Integer age) {
		this.age = age;
	}

	public String getTel() {
		return tel;
	}

	public void setTel(String tel) {
		this.tel = tel;
	}

	public String getAddr() {
		return addr;
	}

	public void setAddr(String addr) {
		this.addr = addr;
	}

}

RestTeamplate 发送get 请求,post 表单请求 或者是post json 请求

package com.example.demo;


import java.util.HashMap;
import java.util.List;
import java.util.Map;


import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.discovery.DiscoveryClient;
import org.springframework.cloud.client.loadbalancer.LoadBalancerClient;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;

@RestController
public class ControllerDemo01 {
	private static final Logger LOGGER = LoggerFactory.getLogger(ControllerDemo01.class);

	
	@Autowired
	private RestTemplate restTemplate;
	
	@Autowired
	private DiscoveryClient discoveryClient;  //服务注册发现顶级接口
	
	@Autowired
	private LoadBalancerClient loadBalancerClient; //LoadBalance 顶级接口
	
	@RequestMapping("/test1")
	public String test1() {
		List<ServiceInstance> list = discoveryClient.getInstances("PROVIDER");
		LOGGER.info(list.toString());
		ServiceInstance serviceInstance = list.get(0);
		String url = "http://"+serviceInstance.getHost()+":"+serviceInstance.getPort()+"/getPort";
		return restTemplate.getForObject(url, String.class);
	}
	
	@RequestMapping("/test2")
	public String test2() {
		String url = "http://PROVIDER/getPort";
		return restTemplate.getForObject(url, String.class);
		
	}
	
	@RequestMapping("/test3")
	public int test3() {
		ServiceInstance instance = loadBalancerClient.choose("PROVIDER");
		return instance.getPort();
	}
	
	@RequestMapping("/v/test4")
	public String test4() {
		return "test4";
	}
	
	/**
	 * 发送get请求,通过服务名调用,从map获取请求参数
	 * @return
	 */
	@RequestMapping("/getPeopleObject1")
	public People getPeopleObject1() {
		Map<String, Object> map = new HashMap<>();
		map.put("name", "小王");
		map.put("age", 15);
		map.put("tel", "138888888");
		map.put("addr", "中国");
		
		String url = "http://PROVIDER/getPeopleObject?name={name}&age={age}&tel={tel}&addr={addr}";
		People people = restTemplate.getForObject(url, People.class, map);
		return people;
	}
	
	@RequestMapping("/getPeopleObject2")
	public People getPeopleObject2() {
		String url = "http://PROVIDER/getPeopleObject?name={1}&age={2}&tel={3}&addr={4}";
		People people = restTemplate.getForObject(url, People.class, "小红",23,"1272272927","美国");
		return people;
	}
	
	/**
	 * 发送post form 表单请求
	 * @return
	 */
	@RequestMapping("/getPeopleObject3")
	public People postPeopleObject3() {
		LinkedMultiValueMap<String, String> map = new LinkedMultiValueMap<>();
		map.add("name", "小王");
		map.add("age", "15");
		map.add("tel", "138888888");
		map.add("addr", "中国");
		String url = "http://PROVIDER/getPeopleObject2";
		HttpHeaders headers = new HttpHeaders();
		headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
		HttpEntity<MultiValueMap<String, String>> request = new HttpEntity<MultiValueMap<String, String>>(map, headers);
		People people = restTemplate.postForObject(url, request, People.class);
		return people;
	}
	
	/**
	 * 发送post json 请求
	 * @return
	 * @throws JsonProcessingException
	 */
	
	@RequestMapping("/getPeopleObject4")
	public People postPeopleObject4() throws JsonProcessingException {
		ObjectMapper objectMapper = new ObjectMapper();
		People people = new People("json", 20, "272292", "北京");
		String jsonContent = objectMapper.writeValueAsString(people);
		String url = "http://PROVIDER/getPeopleObject3";
		HttpHeaders headers = new HttpHeaders();
		headers.setContentType(MediaType.APPLICATION_JSON);
		
		HttpEntity<String> request = new HttpEntity<String>(jsonContent, headers);
		People people1 = restTemplate.postForObject(url, request, People.class);
		return people1;
	}

}

6.OpenFeign
OpenFeign 默认实现了ribbon,可以实现远程调用及负载均衡
引入依赖

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

主启动类加上@EnableFeignClients注解
在主启动类加@EnableFeignClients注解开启对@FeignClient注解类扫描加载处理,生成代理对象注入到ioc容器。扫描所有@FeignClient注解的类,并将这些信息注入Spring IoC容器中。当定义的Feign接口中的方法被调用时,通过JDK的代理方式,来生成具体的RequestTemplate来远程调用。

@FeignClient(服务名)+@RequestMapping(url路径) http://服务名/url 来远程访问。如果有参数一定要加上@RequestParam,如果发送的是post json 请求,加上@RequestBody

FeignController

package com.example.demo;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import com.example.demo.service.FeignService;

@RestController
public class FeignController {
	
	@Autowired 
	private FeignService feignService;
	
	@RequestMapping("/feignTest")
	public String feignTest() {
		return feignService.feignTest();
	}
	
	@RequestMapping("/feignTest2")
	public String feignTest2() {
		return feignService.feignTest2("王五","20");
	}
	
	@RequestMapping("/feignTest3")
	public String feignTest3() {
		People people = new People("赵六",22,"29742","美国");
		return feignService.feignTest3(people);
	}
	
	@RequestMapping("/feignTest4")
	public String feignTest4() {
		return feignService.feignTest4("天天",30,"12494242","台湾");
	}
	
	@RequestMapping("/feignTest5")
	public String feignTest5() {
		System.out.println("发起请求");
		return feignService.feignTest5();
	}
	

}

FeignService

package com.example.demo.service;

import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;

import com.example.demo.People;

@FeignClient(name="PROVIDER",fallback = FeignServiceFallback.class)
public interface FeignService {
	
	@RequestMapping("/feignTest")
	public String feignTest();

	@RequestMapping("/feignTest2")
	public String feignTest2(@RequestParam String name, @RequestParam String age);
	
	@RequestMapping("/feignTest3")
	public String feignTest3(@RequestBody People people);
	
	@RequestMapping("/feignTest4")
	public String feignTest4(@RequestParam String name,@RequestParam Integer age,@RequestParam String tel,@RequestParam String addr);

	@RequestMapping("/feignTest5")
	public String feignTest5();
	
	

}

Feign 连接超时、请求超时重试配置

package com.example.demo;

import java.util.concurrent.TimeUnit;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import feign.Request;
import feign.Retryer;

@Configuration
public class FeignConfig {
	//连接超时
	private static int connectTimeoutMillis = 1000;
	//请求超时
	private static int readTimeoutMillis = 2000;
	
	@Bean
	public Request.Options options(){
		return new Request.Options(connectTimeoutMillis, TimeUnit.MILLISECONDS, readTimeoutMillis, TimeUnit.MILLISECONDS, true);
	}
	
	//重试
	@Bean
	public Retryer feignRetryer() {
		return new Retryer.Default(100, 1000, 4);
	}

}

7.Hystrix

熔断降级。比喻:保险丝过载熔断。熔断请求不再处理,执行降级方法
默认情况:

触发条件:最少20个请求,失败达到50%

每当20个请求中,有50%失败时,熔断器就会打开,此时在调用此服务,将会直接返回失败,不再调远程服务。直到5s钟之后,重新检测该触发条件,判断是否吧熔断器关闭,或者继续打开。达到熔断之后,那么后面它就直接不去调该微服务

作用:

  1. 为系统提供保护机制。在依赖的服务出现高延迟或失败时,为系统提供保护和控制。
  2. 防止雪崩。
  3. 快速失败:Fail Fast。同时能快速恢复。侧重点是:(不去真正的请求服务,发生异常再返回),而是直接失败。
  4. 监控:Hystrix可以实时监控运行指标和配置的变化,提供近实时的监控、报警、运维控制。
  5. 回退机制:fallback,当请求失败、超时、被拒绝,或当断路器被打开时,执行回退逻辑。回退逻辑我们自定义,提供优雅的服务降级。
  6. 自我修复:断路器打开一段时间后,会自动进入“半开”状态,可以进行打开,关闭,半开状态的转换。

添加依赖

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

主启动类添加@EnableCircuitBreaker,旧版本是@EnableHystrixBreaker

RestTemplate + Hystrix

package com.example.demo;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;

import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand;


@RestController
public class HystrixController {
	
	@Autowired
	private RestTemplate restTemplate;
	
	
	@RequestMapping("/hystrixTest1")
	@HystrixCommand(fallbackMethod = "back")
	public String hystrixTest1() {
		String url = "http://PROVIDER/getPort";
		return restTemplate.getForObject(url, String.class);
	}
	
	private String back() {
		System.out.println("降级方法!!!");
		return "降级方法";
	}
	

}

OpenFeign+hystrix
配置文件:

#Feign整合Hystrix
feign.circuitbreaker.enabled=true
#旧版
#feign.hystrix.enabled=true
package com.example.demo.service;

import org.springframework.stereotype.Component;

import com.example.demo.People;

@Component
public class FeignServiceFallback implements FeignService {
	
	public String feignTest() {
		return "feignTest 降级";
	}

	@Override
	public String feignTest2(String name, String age) {
		// TODO Auto-generated method stub
		return null;
	}

	@Override
	public String feignTest3(People people) {
		// TODO Auto-generated method stub
		return null;
	}

	@Override
	public String feignTest4(String name, Integer age, String tel, String addr) {
		// TODO Auto-generated method stub
		return null;
	}

	@Override
	public String feignTest5() {
		// TODO Auto-generated method stub
		return null;
	}

}

8.HystrixDashboard

添加依赖

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

主启动类
@EnableHystrixDashboard

暴露所有监控端点 management.endpoints.web.exposure.include=*

允许被hystrix-dashboard 监控 hystrix.dashboard.proxy-stream-allow-list=*

访问: http://localhost:8081/hystrix 图形化界面

页面输入: localhost:8081/actuator/hystrix.stream,健康上报
在这里插入图片描述
访问方法,图像化界面监控
在这里插入图片描述

9.Sleuth + zipkin

Sleuth 链路追踪
如果能跟踪每个请求,中间请求经过哪些微服务,请求耗时,网络延迟,业务逻辑耗时等。我们就能更好地分析系统瓶颈、解决系统问题。因此链路跟踪很重要。
span(跨度),基本工作单元。一次链路调用,创建一个span
trace(跟踪),一组共享“root span”的span组成的树状结构 称为 trace,trace也有一个64位ID,trace中所有span共享一个trace id。类似于一颗 span 树
zipkin:图形化界面,Sleuth将日志发送到zipkin,zipkin收集日志以图形化的方式将信息展示出来

添加依赖

		<dependency>
			<groupId>org.springframework.cloud</groupId>
			<artifactId>spring-cloud-starter-zipkin</artifactId>
			<version>2.2.6.RELEASE</version>
		</dependency>

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

配置文件
#sleuth+zipkin
#设置采样率
spring.sleuth.sampler.rate=1
spring.zipkin.base-url=http://127.0.0.1:9411/
访问localhost:9411
在这里插入图片描述

10.SpringCloud Admin

admin serve和admin client
新建项目admin server
添加依赖

<dependency>
			<groupId>de.codecentric</groupId>
			<artifactId>spring-boot-admin-starter-server</artifactId>
</dependency>

主启动类 @EnableAdminServer
启动admin server

admin client:
添加依赖:

	<!-- Admin 服务 -->
      <dependency>
        <groupId>de.codecentric</groupId>
        <artifactId>spring-boot-admin-starter-client</artifactId>
        <version>2.2.1</version>
      </dependency>

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

配置文件

#spring cloud admin
management.endpoints.web.exposure.include=*
management.endpoint.health.show-details=always
spring.boot.admin.client.url=http://localhost:8080

浏览器访问admin
http://localhost:8080/
在这里插入图片描述

11.SpringCloud gateway
网关

待更新。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值