目录
1、概念
1.1、什么是微服务
1.2、微服务优点
1.3、springboot和springcloud关系
2、构建一个简单的微服务项目
2.1、目录结构
2.2、springcloud_parent
2.3、增加eureka注册中心
2.4、把一个服务注册到eureka中去
2.5、添加eureka集群
2.6、Zookeeper
2.7、Eureka和Zookeeper区别
2.7、Ribbon
2.8、Feign负载均衡
2.9、Hystrix服务熔断
2.10、dashboard流监控
2.11、Zuul路由网关
2.12、Springcloud config分布式配置
3、总结
4、参考资料
通常而言,微服务架构是一种架构模式/风格。它提倡将单一的应用程序划分成一组组小的服务,每个服务运行在独立的进程内,服务之间采用轻量级的通信机制相互沟通,每个服务都围绕着具体的业务进行构建,并且能够被独立的部署到生产环境中。另外,应尽量避免统一的,集中式的服务管理机制,对具体一个服务而言,应根据业务上下文,选择合适的语言,工具对其进行构建。可以有一个非常轻量级的集中式管理来协助这些服务,可以使用不同的语言来编写服务,也可以使用不同的数据库存储。
简单来说就是把业务拆分成一个一个的服务,每一个微服务提供单个业务功能,一个服务做一件事,从技术角度看就是一种小而独立的处理过程,类似进程的概念,能够自行单独启动或者销毁,拥有自己独立的数据库。
单一职责原则
每个服务足够内聚,足够小,每个服务负责聚焦一个指定的功能业务
微服务是松耦合的,支持不同语言开发,每个服务可以有自己的数据库,也可以有统一的数据库。
微服务是纯后端业务逻辑层代码。
springboot专注于开发单个微服务
springcloud是关注全局的微服务协调治理框架,它将springboot开发的各个单体微服务整合并管理起来,为各个微服务之间提供:配置管理、服务发现、断路器、路由、微代理、事件总线、全局锁、分布式会话等集成服务。
一个完整的大型网站架构
项目git地址:Atlantide/微服务
pom.xml
4.0.0
org.springframework.boot
spring-boot-starter-parent
1.5.4.RELEASE
com.springcloud
demo
0.0.1-SNAPSHOT
demo
Demo project for Spring Boot
1.8
4.12
1.2.17
1.16.18
org.springframework.cloud
spring-cloud-dependencies
Greenwich.SR1
pom
runtime
org.springframework.boot
spring-boot-starter
org.springframework.boot
spring-boot-starter-web
org.springframework.boot
spring-boot-devtools
runtime
true
org.projectlombok
lombok
true
org.springframework.boot
spring-boot-starter-test
test
com.alibaba
druid
1.2.6
junit
junit
${junit.version}
org.projectlombok
lombok
${lombok.version}
provided
log4j
log4j
1.2.17
com.baomidou
mybatis-plus-boot-starter
3.4.0
com.baomidou
mybatis-plus-generator
3.4.0
com.alibaba
fastjson
1.2.78
io.springfox
springfox-swagger2
2.9.2
io.springfox
springfox-swagger-ui
2.9.2
com.microsoft.sqlserver
mssql-jdbc
9.2.1.jre8
org.mybatis.spring.boot
mybatis-spring-boot-starter
1.3.2
com.fasterxml.jackson.core
jackson-databind
2.12.5
org.springframework.boot
spring-boot-starter-actuator
1.5.4.RELEASE
springcloud_parent作为整个微服务项目的父工程。其配置的dependencies可以为所有子工程沿用。对于绝大多数依赖,如果在父工程中有配置版本号,那么在子工程中沿用可不设置版本号。
springcloud_api该服务只负责提供一个实体类。
pom.xml
4.0.0
org.springframework.boot
spring-boot-starter-parent
1.5.4.RELEASE
com.springcloud_api
springcloud_api
0.0.1-SNAPSHOT
springcloud_api
Demo project for Spring Boot
1.8
org.springframework.boot
spring-boot-starter
org.projectlombok
lombok
true
org.springframework.boot
spring-boot-starter-test
test
org.mybatis.spring.boot
mybatis-spring-boot-starter
2.2.0
com.baomidou
mybatis-plus-boot-starter
3.4.0
com.baomidou
mybatis-plus-generator
3.4.0
无需写application.properties配置
springcloud_provider为服务提供者,这里具有的功能是提供接口供消费者consumer消费。
pom.xml
4.0.0
org.springframework.boot
spring-boot-starter-parent
1.5.4.RELEASE
com.springcloud_provider
springcloud_provider
0.0.1-SNAPSHOT
springcloud_provider
Demo project for Spring Boot
1.8
com.springcloud_api
springcloud_api
0.0.1-SNAPSHOT
org.springframework.boot
spring-boot-starter
org.springframework.boot
spring-boot-starter-test
test
com.microsoft.sqlserver
mssql-jdbc
9.2.1.jre8
com.alibaba
druid
1.2.6
junit
junit
org.projectlombok
lombok
provided
log4j
log4j
1.2.17
org.mybatis.spring.boot
mybatis-spring-boot-starter
2.2.0
com.baomidou
mybatis-plus-boot-starter
3.4.0
com.baomidou
mybatis-plus-generator
3.4.0
io.springfox
springfox-swagger2
2.9.2
io.springfox
springfox-swagger-ui
2.9.2
com.alibaba
fastjson
1.2.78
com.fasterxml.jackson.core
jackson-databind
org.springframework.boot
spring-boot-starter-web
application.yml
server:
port: 8082
spring:
datasource:
name: druidDataSource
url: jdbc:sqlserver://127.0.0.1:1433;database=Practise
username: root
password: 123456
driver-class-name: com.microsoft.sqlserver.jdbc.SQLServerDriver
mybatis:
mapper-locations: classpath:mapper/*.xml #注意:一定要对应mapper映射xml文件的所在路径
type-aliases-package: com.springcloud_api.entity # 注意:对应实体类的路径jetbrains://idea/navigate/reference?project=DataBase_conn&fqn=com.example.demo.Entity
控制层
package com.springcloud_provider.controller;
import com.springcloud_api.entity.StudentPO;
import com.springcloud_provider.service.impl.StudentServiceImpl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;
import java.util.List;
/**
* @author: Wu Linchun
* @date: 2021/09/16/15:10
* @Description:
**/
@EnableAutoConfiguration
@Controller
@RequestMapping("/index")
public class MyController {
@Autowired
private StudentServiceImpl studentService;
@GetMapping("/getAll")
@ResponseBody
public List getAll() {
return studentService.getAll();
}
}
mapper接口
@Mapper
@Component
public interface IStudentMapper extends BaseMapper {
}
服务层接口
package com.springcloud_provider.service;
import com.springcloud_api.entity.StudentPO;
import org.springframework.stereotype.Component;
import java.util.List;
/**
* @author: Wu Linchun
* @date: 2021/06/04/14:02
* @Description:
**/
@Component
public interface IStudentService {
/**
* 查询全部
*
* @return
*/
List getAll();
}
服务层接口实现
package com.springcloud_provider.service.impl;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.springcloud_api.entity.StudentPO;
import com.springcloud_provider.mapper.IStudentMapper;
import com.springcloud_provider.service.IStudentService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* @author: Wu Linchun
* @date: 2021/06/04/14:55
* @Description:
**/
@Service
public class StudentServiceImpl implements IStudentService {
@Autowired
private IStudentMapper iStudentMapper;
@Override
public List getAll() {
return iStudentMapper.selectList(null);
}
}
启动类
package com.springcloud_provider;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@MapperScan("com.springcloud_provider.mapper")
@SpringBootApplication
public class SpringcloudProviderApplication {
public static void main(String[] args) {
SpringApplication.run(SpringcloudProviderApplication.class, args);
}
}
springcloud_consumer为服务消费者,主要作用是消费服务提供者提供的接口。
pom.xml
4.0.0
org.springframework.boot
spring-boot-starter-parent
1.5.4.RELEASE
com.springcloud_consumer
springcloud_consumer
0.0.1-SNAPSHOT
springcloud_consumer
Demo project for Spring Boot
1.8
org.springframework.boot
spring-boot-starter
org.springframework.boot
spring-boot-starter-test
test
org.springframework.boot
spring-boot-starter-web
RELEASE
compile
org.springframework.cloud
spring-cloud-starter-eureka
1.3.5.RELEASE
org.springframework.boot
spring-boot-starter-actuator
application.yml
server:
port: 8083
spring:
application:
name: Myspringcloud-consumer_8083
#eureka配置,服务注册到哪?
eureka:
client:
service-url:
defaultZone: http://localhost:8001/eureka/
instance:
instance-id: springcloud-consumer_8083 #修改eureka上默认描述信息
#info配置
info:
app.name: testSpringCloud
company.name: xxx
Config配置类:因为掉服务接口会用到restTemplate,因此在启动的时候就要把restTemplate实例化成一个bean。
package com.springcloud_consumer.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.client.RestTemplate;
/**
* @author: wu linchun
* @time: 2021/9/16 17:36
* @description:
*/
@Configuration
public class ConfigBean {
@Bean
public RestTemplate getRestTemplate() {
return new RestTemplate();
}
}
消费者接口
package com.springcloud_consumer.controller;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpMethod;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.client.RestTemplate;
import java.util.List;
/**
* @author: wu linchun
* @time: 2021/9/16 17:32
* @description:
*/
@Controller
@RequestMapping("consumer")
public class ConsumerController {
//RestTemplate已经在配置类中变成了一个bean
@Autowired //RestTemplate提供多种便捷访问远程http服务的方法,简单的restful服务模板
private RestTemplate restTemplate;
@GetMapping("c1")
public void PrintAll() {
ResponseEntity responseEntity = restTemplate.exchange("http://127.0.0.1:8082/index/getAll",
HttpMethod.GET, null, List.class);
System.out.println("返回的请求体为:" + responseEntity.getBody());
}
}
启动类
package com.springcloud_consumer;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
@SpringBootApplication
public class SpringcloudConsumerApplication {
public static void main(String[] args) {
SpringApplication.run(SpringcloudConsumerApplication.class, args);
}
}
调消费者接口
eureka是服务注册中心,是Netflix的一个子模块,也是核心模块之一。微服务原则上是应该有多个服务提供者的实例的,在通常情况下服务提供者的数量和分布往往是动态变化的,这样在传统的单体应用中的那种硬编码服务url进行远程调用的方式就不足取。服务注册中心就是为了解决服务之间的注册与发现而产生的。
服务注册中心本质上是为了解耦服务提供者和服务消费者。
新建一个eureka
pom.xml
4.0.0
org.springframework.boot
spring-boot-starter-parent
1.5.4.RELEASE
com.springcloud_eureka
springcloud_eureka
0.0.1-SNAPSHOT
springcloud_eureka
Demo project for Spring Boot
1.8
org.springframework.cloud
spring-cloud-starter-eureka-server
1.3.5.RELEASE
org.springframework.boot
spring-boot-starter
org.springframework.boot
spring-boot-starter-test
test
application.yml
server:
port: 8001
#Eureka配置
eureka:
instance:
hostname: localhost #Eureka服务端的实例名称
client:
register-with-eureka: false #是否向eureka注册中心注册自己,因为这里本身就是eureka服务端,所以无需向eureka注册自己
fetch-registry: false #fetch-registry为false,则表示自己为注册中心
service-url: #监控页面
defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/
启动类
package com.springcloud_eureka;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer;
@SpringBootApplication
@EnableEurekaServer //使eureka服务端可以工作
public class SpringcloudEurekaApplication {
public static void main(String[] args) {
SpringApplication.run(SpringcloudEurekaApplication.class, args);
}
}
启动eureka
这里是把consumer注册到eureka中去。
首先要在pom.xml中添加eureka相关依赖
org.springframework.cloud
spring-cloud-starter-eureka
1.3.5.RELEASE
在application.yml添加要注册到哪
#eureka配置,服务注册到哪?
eureka:
client:
service-url:
defaultZone: http://localhost:8001/eureka/
instance:
instance-id: springcloud-consumer_8083 #修改eureka上默认描述信息
启动类
package com.springcloud_consumer;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
@SpringBootApplication
@EnableEurekaClient //服务启动后,自动注册到eureka中
public class SpringcloudConsumerApplication {
public static void main(String[] args) {
SpringApplication.run(SpringcloudConsumerApplication.class, args);
}
}
如果某个服务宕机了,eureka就会开启“自我保护机制”,虽然该服务宕机了,但在eureka中该服务还是在,这是为了避免与该服务相关联的其他服务被影响到。
新建两个module作为eureka,三个eureka形成一个eureka集群。
先配置一下localhost(127.0.0.1)的别名
依次启动三个eureka
#eureka配置,服务注册到哪?
eureka:
client:
service-url:
defaultZone: http://eureka8001:8001/eureka/,http://eureka8002:8002/eureka/,http://eureka8003:8003/eureka/
instance:
instance-id: springcloud-consumer_8083 #修改eureka上默认描述信息
分别启动三个eureka,以及相应的服务。服务将同时注册到三个eureka中去。
CAP:
CAP的三进二:CA、AP、CP,任何注册中心只能满足其中两个。
CAP理论核心:一个分布式系统不可能同时很好的满足一致性,可用性和分区容错性这三个需求。
Eureka保证的是AP、Zookeeper保证的是AP。
因此,Eureka可以很好的应对因网络故障导致部分节点失去联系的情况,而不会像Zookeeper那样使整个注册服务瘫痪。
Spring Cloud Ribbon是基于Netflix Ribbon实现的一套客户端负载均衡的工具。
简单来说,Ribbon是Netflix发布的开源项目,主要功能是提供客户端的软件负载均衡算法,将NetFlix的中间层服务连接在一起。Ribbon的客户端组件提供一系列完整的配置项,如:连接超时、重试等等。简单来说就是在配置文件中列出LoadBalancer(LB:负载均衡)后面的所有的机器,Ribbon会自动的帮助你基于某种规则(如简单轮询,随机连接等等)去连接这些机器。当然也可以自己去实现Ribbon的负载均衡算法。
负载均衡简单分类可分为“集中式LB”和“进程式LB”:
建立多个服务提供者
pom.xml
org.springframework.cloud
spring-cloud-starter-ribbon
1.3.5.RELEASE
org.springframework.cloud
spring-cloud-starter-eureka
1.3.5.RELEASE
application.yml
server:
port: 8084
spring:
application:
name: MYSPRINGCLOUD-PROVIDER #3个提供者服务名字一样
#eureka配置,服务注册到哪?
eureka:
client:
service-url:
defaultZone: http://eureka8001:8001/eureka/,http://eureka8002:8002/eureka/,http://eureka8003:8003/eureka/
instance:
instance-id: springcloud-provider-8084 #修改eureka上默认描述信息
controller
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;
import java.util.List;
/**
* @author: Wu Linchun
* @date: 2021/09/16/15:10
* @Description:
**/
@EnableAutoConfiguration
@Controller
@RequestMapping("/index")
public class MyController {
@GetMapping("/testGroup")
@ResponseBody
public String testGroup() {
return "springcloud-provider-8084";
}
}
启动类
package com.springcloud_provider_01;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
@SpringBootApplication
@EnableEurekaClient
@EnableDiscoveryClient //服务发现
public class SpringcloudProvider01Application {
public static void main(String[] args) {
SpringApplication.run(SpringcloudProvider01Application.class, args);
}
}
其余两个服务提供者也按照此配置。spring.application.name多个服务提供者要相同,但eureka.instance.instance-id多个服务提供者要不同。
创建服务消费者
pom.xml
org.springframework.cloud
spring-cloud-starter-ribbon
1.3.5.RELEASE
org.springframework.cloud
spring-cloud-starter-eureka
1.3.5.RELEASE
config
package com.springcloud_consumer.config;
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;
/**
* @author: wu linchun
* @time: 2021/9/16 17:36
* @description:
*/
@Configuration
public class ConfigBean {
@Bean
@LoadBalanced //Ribbon 配置负载均衡实现restTemplate
public RestTemplate getRestTemplate() {
return new RestTemplate();
}
}
controller
package com.springcloud_consumer.controller;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.discovery.DiscoveryClient;
import org.springframework.http.HttpMethod;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.client.RestTemplate;
import java.util.List;
/**
* @author: wu linchun
* @time: 2021/9/16 17:32
* @description:
*/
@Controller
@RequestMapping("consumer")
public class ConsumerController {
//RestTemplate已经在配置类中变成了一个bean
@Autowired //RestTemplate提供多种便捷访问远程http服务的方法,简单的restful服务模板
private RestTemplate restTemplate;
//ribbon和eureka整合后,客户端可以直接调用,而不需要去知道ip地址和端口号
private static final String REST_URL_PREFIX = "http://MYSPRINGCLOUD-PROVIDER";
@GetMapping("/testGroup")
@ResponseBody
public Object testGroup() {
ResponseEntity responseEntity = restTemplate.exchange(REST_URL_PREFIX + "/index/testGroup",
HttpMethod.GET, null, String.class);
return responseEntity.getBody();
}
}
启动类
package com.springcloud_consumer;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
@SpringBootApplication
@EnableEurekaClient //服务启动后,自动注册到eureka中
@EnableDiscoveryClient //服务发现
public class SpringcloudConsumerApplication {
public static void main(String[] args) {
SpringApplication.run(SpringcloudConsumerApplication.class, args);
}
}
同时启动多个eureka注册中心以及多个服务提供者,并启动消费者。
可以看到当访问消费者时,会在多个服务提供者之间进行轮询切换到其中一个服务提供者。
1、使用netflix.loadbalancer包下定义好的负载均衡算法
即每次会随机切换一个服务提供者。
在消费者服务中的相应配置类中添加:
package com.springcloud_consumer.config;
import com.netflix.loadbalancer.*;
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;
/**
* @author: wu linchun
* @time: 2021/9/16 17:36
* @description:
*/
@Configuration
public class ConfigBean {
//配置负载均衡实现RestTemplate
@Bean
@LoadBalanced //Ribbon 配置负载均衡实现restTemplate
public RestTemplate getRestTemplate() {
return new RestTemplate();
}
//IRule
//RoundRobinRule:轮询
//RandomRule:随机
//AvailabilityFilteringRule:会过滤掉跳闸、访问故障的服务~,对剩下的进行轮询~
//RetryRule:会先按照轮询获取服务,如果服务获取失败会在指定的时间内进行重试
@Bean
public IRule myRule() {
//随机切换服务
return new RandomRule();
// return new RoundRobinRule();
// return new AvailabilityFilteringRule();
// return new RetryRule();
}
}
2、 自定义负载均衡算法
可以参照RandomRule、RoundRobinRule、AvailabilityFilteringRule、RetryRule等netflix.loadbalancer包中常用的负载均衡策略,继承AbstractLoadBalancerRule类,并实现相应的方法写一个自己的负载均衡策略。
package com.myrules;
import com.netflix.client.config.IClientConfig;
import com.netflix.loadbalancer.AbstractLoadBalancerRule;
import com.netflix.loadbalancer.ILoadBalancer;
import com.netflix.loadbalancer.Server;
import java.util.List;
/**
* @author: wu linchun
* @time: 2021/10/6 16:07
* @description: 自定义负载均衡策略,调用某个服务超过5次,自动切换到下一个
*/
public class MyRibbonRule extends AbstractLoadBalancerRule {
//每次调用则total+1,当调用5次,切换到下一个服务
private int total = 0;
//当前是谁在提供服务
private int currentIndex = 0;
@Override
public void initWithNiwsConfig(IClientConfig iClientConfig) {
}
// @edu.umd.cs.findbugs.annotations.SuppressWarnings(value = "RCN_REDUNDANT_NULLCHECK_OF_NULL_VALUE")
public Server choose(ILoadBalancer lb, Object o) {
if (lb == null) {
return null;
}
Server server = null;
while (server == null) {
if (Thread.interrupted()) {
return null;
}
List upList = lb.getReachableServers();
List allList = lb.getAllServers();
int serverCount = allList.size();
if (serverCount == 0) {
/*
* No servers. End regardless of pass, because subsequent passes
* only get more restrictive.
*/
return null;
}
// int index = rand.nextInt(serverCount);
if (total < 5) {
server = upList.get(currentIndex);
total++;
} else {
total = 0;
currentIndex++;
if (currentIndex > upList.size()) {
currentIndex = 0;
}
server = upList.get(currentIndex);
}
if (server == null) {
/*
* The only time this should happen is if the server list were
* somehow trimmed. This is a transient condition. Retry after
* yielding.
*/
Thread.yield();
continue;
}
if (server.isAlive()) {
return (server);
}
// Shouldn't actually happen.. but must be transient or a bug.
server = null;
Thread.yield();
}
return server;
}
@Override
public Server choose(Object o) {
return this.choose(this.getLoadBalancer(), o);
}
// public Server choose(Object key) {
// return this.choose(this.getLoadBalancer(), key);
// }
}
WRule
package com.myrules;
import com.netflix.loadbalancer.IRule;
import com.netflix.loadbalancer.RandomRule;
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;
/**
* @author: wu linchun
* @time: 2021/10/6 17:13
* @description:
*/
@Configuration
public class WRule {
@Bean
public IRule myWRule() {
return new MyRibbonRule();
// return new RandomRule();
}
}
在启动类中要加上配置类的信息
package com.springcloud_consumer;
import com.myrules.MyRibbonRule;
import com.myrules.WRule;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
import org.springframework.cloud.netflix.ribbon.RibbonClient;
@SpringBootApplication
@EnableEurekaClient //服务启动后,自动注册到eureka中
//@EnableDiscoveryClient //服务发现
//在微服务启动的时候就能去加载自定义的Ribbon配置类
@RibbonClient(value = "MYSPRINGCLOUD-PROVIDER", configuration = WRule.class)
public class SpringcloudConsumerApplication {
public static void main(String[] args) {
SpringApplication.run(SpringcloudConsumerApplication.class, args);
}
}
feign是声明式的web service客户端,它可以让微服务之间的调用变得更加简单,类似于controller调用service,而不需要像之前那样使用restTemplate调用不同的服务了。SpringCloud集成了Ribbon和Eureka,可以在使用Feign时提供负载均衡的http客户端。
调用微服务的两种方式
1、服务提供者
@GetMapping("/getAllFeignByHttpAPI")
@ResponseBody
public List getAllFeignByHttpAPI() {
return studentService.getAllFeignByHttpAPI();
}
2、服务消费者
因为是消费者通过feign去调用其他接口,所以要在消费者服务中引入feign依赖。
org.springframework.cloud
spring-cloud-starter-feign
1.3.5.RELEASE
package com.springcloud_consumer.feign;
import com.springcloud_api.entity.StudentPO;
import org.springframework.cloud.netflix.feign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import java.util.List;
/**
* @description: feign调用接口
* @author: wu linchun
* @time: 2021/11/11 20:57
*/
@FeignClient(name = "test", url = "http://127.0.0.1:8082/index") //url为服务提供者url
public interface FeignTestAPI {
@GetMapping("/getAllFeignByHttpAPI")
List getAllFeignByHttpAPI();
}
启动类
package com.springcloud_consumer;
import com.myrules.MyRibbonRule;
import com.myrules.WRule;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
import org.springframework.cloud.netflix.feign.EnableFeignClients;
import org.springframework.cloud.netflix.ribbon.RibbonClient;
@SpringBootApplication
@EnableEurekaClient //服务启动后,自动注册到eureka中
//@EnableDiscoveryClient //服务发现
//在微服务启动的时候就能去加载自定义的Ribbon配置类
@EnableAutoConfiguration(exclude = {DataSourceAutoConfiguration.class})
@EnableFeignClients(basePackages = "com.springcloud_consumer.feign")
@RibbonClient(value = "MYSPRINGCLOUD-PROVIDER", configuration = WRule.class)
public class SpringcloudConsumerApplication {
public static void main(String[] args) {
SpringApplication.run(SpringcloudConsumerApplication.class, args);
}
}
测试
1、服务提供者
@EnableAutoConfiguration
@Controller
@RequestMapping("/index")
public class MyController {
@Autowired
private StudentServiceImpl studentService;
@GetMapping("/getAllFeignByApplicationName")
@ResponseBody
public List getAllFeignByApplicationName() {
return studentService.getAllFeignByApplicationName();
}
2、服务消费者
package com.springcloud_consumer.feign;
import com.springcloud_api.entity.StudentPO;
import org.springframework.cloud.netflix.feign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import java.util.List;
/**
* @description: feign调用服务名
* @author: wu linchun
* @time: 2021/11/11 21:09
*/
@FeignClient(value = "MYSPRINGCLOUD-PROVIDER") //MYSPRINGCLOUD-PROVIDER是服务提供者在eureka注册的服务名称
public interface FeignTestApplicationName {
@GetMapping("/index/getAllFeignByApplicationName")
List getAllFeignByApplicationName();
}
在进行服务间调用的话,必须确保“调用者”和“被调用者”都在eureka上注册了。
消费者启动类同2.8.1
测试
复杂的分布式体现结构中的应用程序有数十个依赖关系,每个依赖关系在某些时候将不可避免的失败。对于调用失败的服务,为了不产生级联影响,要对该服务及时熔断。
服务雪崩
Hystrix
Hystix是一个用于处理分布式系统的延迟和容错的开源库,在分布式系统里,许多依赖不可避免的会调用失败,比如超时,异常等,Hystrix能够保证在一个依赖出问题的情况下,不会导致整体服务失败,避免级联故障,以提高分布式系统的弹性。
熔断器/断路器
hystrix实现服务熔断其实就是有点类似于java里面的异常处理机制,一旦程序发生异常则直接将当前服务熔断,并把发生异常的服务降级(前提是要有次级服务)。一级降二级,二级降三级......依次类推。
其实就是类似于java的异常机制。
hystrix服务熔点降级与java异常比较:
例:
package com.springcloud_provider_hystrix_8082.controller;
import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand;
import com.netflix.ribbon.proxy.annotation.Hystrix;
import com.springcloud_api.entity.StudentPO;
import com.springcloud_provider_hystrix_8082.service.impl.StudentServiceImpl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;
import java.util.List;
/**
* @author: Wu Linchun
* @date: 2021/09/16/15:10
* @Description:
**/
@EnableAutoConfiguration
@Controller
@RequestMapping("/index")
public class MyController {
/**
* @author: wu linchun
* @create: 2022/1/4 21:27
* @desc: 除法一级服务(使用hystrix处理异常)
**/
@GetMapping("/division")
@ResponseBody
@HystrixCommand(fallbackMethod = "divisionGet")
public float division(@RequestParam("num1") int num1, @RequestParam("num2") int num2) {
return num1 / num2;
}
/**
* @author: wu linchun
* @create: 2022/1/4 21:27
* @desc: 除法二级服务
**/
@HystrixCommand(fallbackMethod = "divisionGet1")
public float divisionGet(@RequestParam("num1") int num1, @RequestParam("num2") int num2) {
return num2 / num1;
}
/**
* @author: wu linchun
* @create: 2022/1/4 21:29
* @desc: 除法三级服务
**/
public float divisionGet1(@RequestParam("num1") int num1, @RequestParam("num2") int num2) {
return -999999f;
}
@GetMapping("/divisionEX")
@ResponseBody
@HystrixCommand(fallbackMethod = "divisionGet")
public float divisionEX(@RequestParam("num1") int num1, @RequestParam("num2") int num2) {
try {
return num1 / num2;
} catch (Exception e) {
//e.printStackTrace();
try {
return num2 / num1;
} catch (Exception e1) {
return -999999f;
}
}
}
}
其实hystrix实现服务降级也是利用了java的异常机制。
hystrix相关源码解析可以参考:Hystrix - @HystrixCommand 源码解析 - frank_cui - 博客园 (cnblogs.com)
hystrix源码解析——FallbackMethod是如何接收异常的_frcoder的博客-CSDN博客
Hystrix Dashboard 是 Hystrix 的仪表盘组件,提供了数据监控,可以实时监控 Hystrix 的各个指标,然后通过图形化界面展示出来。
实例:
新建一个dashboard服务
引入hystrix dashboard以及监控相关依赖
org.springframework.cloud
spring-cloud-starter-hystrix-dashboard
1.4.6.RELEASE
org.springframework.cloud
spring-cloud-starter-hystrix
1.4.6.RELEASE
org.springframework.boot
spring-boot-starter-actuator
启动类
package com.springcloud;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
import org.springframework.boot.autoconfigure.jdbc.DataSourceTransactionManagerAutoConfiguration;
import org.springframework.cloud.netflix.hystrix.dashboard.EnableHystrixDashboard;
/**
* @ClassName SpringcloudHystrixDashboard
* @Version 1.0
* @Author Wulc
* @Date 2022-01-05 14:22
* @Description
*/
@SpringBootApplication(exclude = { DataSourceAutoConfiguration.class, DataSourceTransactionManagerAutoConfiguration.class })
@EnableHystrixDashboard
public class SpringcloudHystrixDashboard {
public static void main(String[] args) {
SpringApplication.run(SpringcloudHystrixDashboard.class, args);
}
}
http://localhost:9001/hystrix进入dashboard monitor界面
dashboard的UI界面
把一个服务添加到dashboard监控中
Step1、该服务需先导入hystrix和actuator相关依赖
org.springframework.cloud
spring-cloud-starter-hystrix
1.4.6.RELEASE
org.springframework.boot
spring-boot-starter-actuator
Step2、在启动类中添加相关的dashboard的bean
package com.springcloud_provider_hystrix_8082;
import com.netflix.hystrix.contrib.metrics.eventstream.HystrixMetricsStreamServlet;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.web.servlet.ServletRegistrationBean;
import org.springframework.cloud.client.circuitbreaker.EnableCircuitBreaker;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
import org.springframework.context.annotation.Bean;
@SpringBootApplication
@EnableEurekaClient //在服务启动后自动注册到eureka中去
@EnableDiscoveryClient //服务发现
@EnableCircuitBreaker //添加对熔断的支持
public class SpringcloudProviderHystrix8082Application {
public static void main(String[] args) {
SpringApplication.run(SpringcloudProviderHystrix8082Application.class, args);
}
@Bean
public ServletRegistrationBean hystrixMetricsStreamServlet() {
//HystrixMetricsStreamServlet:hystrix的流监控
ServletRegistrationBean registrationBean = new ServletRegistrationBean(new HystrixMetricsStreamServlet());
registrationBean.addUrlMappings("/actuator/hystrix.stream");
return registrationBean;
}
}
Zuul包含了路由请求和过滤两个主要的功能:
其中路由功能负责将外部请求转发到具体的微服务实例上,是实现外部访问统一入口的基础,而过滤功能负责对请求的处理过程进行干预,是实现请求校验,服务聚合等功能的基础,Zuul和Eureka进行整合,将Zuul自身注册为Eureka服务治理下的应用,同时从Eureka中获得其他微服务的消息,即以后的访问微服务都是通过Zuul跳转后获得的。(注意:Zuul服务最终还是会注册进eureka,Zuul提供代理+路由+过滤三大功能!
一个简单的Zuul实现网关的例子
Step1、新建一个网关服务
Step2、引入Zuul依赖
org.springframework.cloud
spring-cloud-starter-zuul
1.4.7.RELEASE
Step3、启动类上加上Zuul代理 @EnableZuulProxy
package com.springcloud;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
import org.springframework.cloud.netflix.zuul.EnableZuulProxy;
/**
* @author: wu linchun
* @time: 2022/1/6 21:41
* @description:
*/
@SpringBootApplication
@EnableZuulProxy
@EnableEurekaClient //在服务启动后自动注册到eureka中去
@EnableDiscoveryClient //服务发现
public class ZuulApplication9527 {
public static void main(String[] args) {
SpringApplication.run(ZuulApplication9527.class, args);
}
}
Step4、application.yml
server:
port: 9527
spring:
application:
name: SPRINGCLOUD-ZUUL
eureka:
client:
service-url:
defaultZone: http://localhost:8001/eureka/
instance:
instance-id: springcloud-zuul-8082 #修改eureka上默认描述信息
#prefer-ip-address: true
zuul:
routes:
myprovider.serviceId: myspringcloud-provider-hystrix
myprovider.path: /myprovider/**
#添加访问前缀,使访问路径变成:http://localhost:9527/wlc/myprovider/index/division?num1=0&num2=0
#prefix: /wlc
# ignored-services: "*" 表示隐藏全部路径
#表示不能再使用这个路径访问了(myspringcloud-provider-hystrix)即不能使用http://localhost:9527/myspringcloud-provider-hystrix/index/division?num1=0&num2=0访问
#ignored-services: myspringcloud-provider-hystrix
由于微服务需要将单体应用中的业务拆分成一个个子服务,每个服务的粒度相对较小,因此系统中会出现大量的服务,由于每个服务都需要必要的配置信息(yml)才能运行,所以需要一套集中、动态的配置管理设施来管理每个服务的配置信息。SpringCloud提供了ConfigServer来解决这个问题。可以统一修改每个微服务的application.yml信息。
SpringCloud Config的配置可参考文档:Spring Cloud Config 中文文档 参考手册 中文版
实现一个简单的例子:
Step1、首先在git上面建一个配置中心
Step2、编写一个服务端,用于与git上面的配置中心交互
引入依赖:
pom.xml
org.springframework.cloud
spring-cloud-config-server
1.4.7.RELEASE
注意:引入依赖要注意版本 !!!以免启动失败!!!
配置文件:
application.yml
server:
port: 4399
spring:
application:
name: springcloud-config-server
#连接远程git仓库
cloud:
config:
server:
git:
uri: https://gitee.com/wulinchun/springcloud-config.git
启动类:
package com.springcloud;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.config.server.EnableConfigServer;
/**
* @author: wu linchun
* @time: 2022/1/8 11:33
* @description:
*/
@SpringBootApplication
@EnableConfigServer //开启服务配置中心
public class ConfigServerApplication {
public static void main(String[] args) {
SpringApplication.run(ConfigServerApplication.class, args);
}
}
启动成功后就可以在配置中心看到git上的配置信息了
Step3、编写客户端
pom.xml
org.springframework.cloud
spring-cloud-starter-config
1.4.7.RELEASE
org.springframework.boot
spring-boot-starter-web
org.springframework.boot
spring-boot-starter
org.springframework.boot
spring-boot-starter-test
test
org.springframework
spring-web
bootstrap.yml
#系统级别的配置
spring:
cloud:
config:
name: config-client #需要从git上读取资源名称(不加.yml后缀)
profile: dev
label: master #配置在master分支上
uri: http://localhost:4399 #通过4399这个服务再连接到git上面1获取配置
application.yml
#用户级别的配置
spring:
application:
name: springcloud-config-client-2266
注:boostrap系统级的优先级要高于application用户级!!!
在使用springcloud config中配置中心的信息一定要写在bootstrap.yml中,这是因为在springcloud项目客户端启动以后会先访问bootstrap.yml,绑定config server,然后获取application.yml配置。
如果仅仅在application.yml中去绑定config server,application.yml会默认从8888获取配置信息,那么所获得的端口是默认的8080,无法从config server中获取任何配置信息。
编写一个测试接口:用于输出配置信息
package com.springcloud.controller;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
/**
* @author: wu linchun
* @time: 2022/1/8 14:20
* @description:
*/
@RestController
public class ConfigClientController {
@Value("${spring.application.name}")
private String applicationName;
@Value("${eureka.client.service-url.defaultZone}")
private String eurekaServer;
@Value("${server.port}")
private String port;
@RequestMapping("/getConfig")
public String getConfig() {
return "applicationName:" + applicationName + "eurekaServer:" + eurekaServer + "port:" + port;
}
}
启动类:
package com.springcloud;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
/**
* @author: wu linchun
* @time: 2022/1/8 14:18
* @description:
*/
@SpringBootApplication
public class ConfigClientApplication {
public static void main(String[] args) {
SpringApplication.run(ConfigClientApplication.class, args);
}
}
测试一下:
这是我在b站看“狂神”的教程做的一些笔记,从Spring——>SpringMVC——>SpringBoot——>SpringCloud,也算是把一套Spring全家桶都大致学习完了吧。从我最早大三开始学的java EE上接触到的jsp+servlet的网站开发模式,再到大四上javaEE实训课上学到的Spring、MVC这些,大四做毕业设计时学了一下Springboot、然后在工作中又慢慢接触到了SpringCloud。从开发效果来看,其实这些技术都差不多的(当然在业务量较小的情况下)。
jsp是个很厉害的东西,既能写前端又能写后端,甚至一个网站的前后端都只用jsp就行了,但jsp的缺点就在于它可以支持后端代码!我大三时做过一个课程设计就是用jsp+servlet实现的一个高校课程资源管理系统:Atlantide/高校课程资源管理系统
其实可以看到jsp里面嵌套了许多后端代码,然后servlet里面不乏有类似这种
直接out.print在后端输出前端内容的写法。包括我第一份工作是做保险公司IT系统的生产运维,其中他们的一些老系统也会有这种写法。这种前后的代码混在一起的,一方面可读性差,另一方面对于前端渲染以及后端数据返回的实时性性能会降低。因为目前哪怕是单体应用,只要不是特别老,近10年左右开发的系统,都是采用前后端分离,接口通信的模式。前后端分别部署在不同的服务器上,视实际业务量设计相应的负载均衡策略以及持久层数据库集群等。
其实可以看出,随着技术的发展,应用系统其实一直在做减法,去中心化。最早的应用其实就是前后端一起打成一个包,然后丢服务器上去跑,因此早期的开发人员基本都算是全栈。然后随着前端框架的日益发展,以及后端业务量,处理量日益增加,因此前后端开始分开,然后就会专门区分前端工程师&后端工程师。但是即便这样还满足不了日益增长的业务量。因此去中心化,分布式开始成为主流,各大公司的信息系统都在向原子化转型。把原来庞大冗余的单体应用根据功能拆分成一个个不同的服务,每个服务可能就提供一到两个接口。当然微服务的盛行也离不开自动化技术的发展,没有自动化部署&测试,要想管理由原来单体应用拆分出来的几十个上百个微服务应用显然是很费人力的。
其实从目前学习以及工作的经历来看,微服务有点那种“换汤不换药”的感觉,并不是什么很“高大上”的技术吧,感觉就是把以前就有的技术做了一个大融合,进行了一个封装,开箱即用,降低门槛!
简单说一下,像服务间通信,feign也好,restTemplate也好,不就都是http请求吗?不就是原来的单体应用通过接口去掉一些第三方服务这样的模式吗?包括服务熔断,这个不就是异常处理吗?以及路由网关,ip隐藏,虽然我不太懂网络层的东西,但凭借我以前学计算机网络的印象,感觉也就是相当于ip——>域名的样子。
反正总而言之,SpringCloud的出现对于大多数初中级开发没有多大影响,crud,ctrl c+v要的还是这些东西,可能对于运维那块具有革命性的意义。目前其实我感觉已经进入了一个后互联网时代,前几年把该开发的东西都开发好了,架子已经搭好,应用已经上线。所以从我之前求职的经历来看,绝大多数都是走的二次敏捷开发路线。或者就是系统转型,有.net转java、单体转微服务、老系统用新技术重构。反正没有多少新的东西吧。我现在的工作就是不断的做需求,然后改老系统,用java逐渐重构老系统的一些接口,sql拆分+重组!!!
其实技术的发展只是在拉大两级分化,上手容易但专研难啊!!!
【狂神说Java】SpringCloud最新教程IDEA版_哔哩哔哩_bilibili
springcloud学习(一)之Eureka - 易水寒的博客 - 博客园
Spring Boot 项目启动报 driver class for database type NONE 的原因及解决方法_CG国斌的博客-CSDN博客Spring Boot 项目启动报 driver class for database type NONE 的原因及解决方法_CG国斌的博客-CSDN博客
springcloud hystrix dashboard 界面loading显示不出来问题_向往一点点的博客-CSDN博客
SpringCloud 之 Netflix Hystrix 服务监控 - 路仁甲 - 博客园
springcloud学习(一)之Eureka - 易水寒的博客 - 博客园
springboot与springcloud版本不对应导致报错java.lang.NoSuchMethodError: org.springframework.boot.builder.SpringApplicationBuilder.
SpringCloud-Config-Client配置文件为什么一定要是bootstrap.yml或者bootstrap.properties_wenyixicodedog的博客-CSDN博客
Hystrix - @HystrixCommand 源码解析 - frank_cui - 博客园
hystrix源码解析——FallbackMethod是如何接收异常的_frcoder的博客-CSDN博客