##项目概要html
项目环境信息
IDEA ultimate 2018.3.2 springboot 2.1.7.RELEASE springCloud Greenwich.SR2 前端
###Eureka 介绍java
基于netflix eureka作了二次封装 两个组件组成:mysql
- Eureka Server 注册中心
- Eureka Client 服务注册
搭建Eureka Server
一、配置Eureka 的application.ymlgit
eureka: client: serviceUrl: defaultZone: http://localhost:8761/eureka/ #默认为true,是否向本身注册本身 register-with-eureka: false #关闭Eureka自我保护(生产环境要设置为true,测试环境设置为false) server: enable-self-preservation: false #配置服务实例名 spring: application: name: spring-cloud-eureka #配置服务实例端口 server: port: 8761
注意:若是报错读取application.yml文件错误,检查Settings->File Encodings,所有设置为UTF-8 二、配置Eureka pom.xmlgithub
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.1.7.RELEASE</version> <relativePath/> <!-- lookup parent from repository --> </parent> <groupId>com.practice</groupId> <artifactId>eureka</artifactId> <version>0.0.1-SNAPSHOT</version> <name>eureka</name> <description>Demo project for Spring Boot</description> <properties> <java.version>1.8</java.version> <spring-cloud.version>Greenwich.SR2</spring-cloud.version> </properties> <dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-eureka-server</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> </dependencies> <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> </dependencies> </dependencyManagement> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build> </project>
三、Eureka 入口类web
package com.practice.eureka; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer; @SpringBootApplication @EnableEurekaServer public class EurekaApplication { public static void main(String[] args) { SpringApplication.run(EurekaApplication.class, args); } }
四、Eureka的高可用算法
设置启动端口在VM options设置启动端口 -Dserver.port=8761
启动多个能够以下配置,相互注册
spring
Eureka Server有心跳检测、健康检查、负载均衡等功能。sql
能够启动多个Eureka Server,互相注册(经过修改端口启动多个Eureka Server,修改注册中心地址为对方实现互相注册) 好比,启动了Eureka Server1 和 Eureka Server2 这样Eureka Client在注册到其中一台Eureka Server后(如Server1),也会同步到另外一个Eureka Server。 若是Eureka Server1挂掉了,另外一台Eureka Server2仍然可用,并能够发现客户端。
可是若是从新启动Eureka Client,则会发现因为Server1已经停掉了,则没法正常注册,也就没法同步到Server2
为了确保高可用,能够将Client同时注册到全部的Eureka上
-注册中心地址配置 eureka: client: service-url: defaultZone: http://localhost:8761/eureka,http://localhost:8762/eureka
若是超过两台以上的Eureka Server,为保证高可用,则能够设置其两两注册,同时将Client同时注册到全部Eureka Server上。
注册发现
一、客户端入口类 添加EnableDiscoveryClient,提供eureka客户端
package com.practice.client; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.client.discovery.EnableDiscoveryClient; @SpringBootApplication @EnableDiscoveryClient public class ClientApplication { public static void main(String[] args) { SpringApplication.run(ClientApplication.class, args); } }
二、Client新建application.yml
eureka: client: serviceUrl: defaultZone: http://localhost:8761/eureka/ #页面跳转后台端口前名称 # instance: # hostname: product #客户端实例在注册中心的名称 spring: application: name: product # mysql数据库链接信息 datasource: driver-class-name: com.mysql.jdbc.Driver username: root password: 123456 url: jdbc:mysql://127.0.0.1:3306/springcloud_sell?characterEncoding=utf-8&useSSL=false # 打开jpa日志,在控制台打印 jpa: show-sql: true env: dev
三、Client新建pom.xml
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.1.7.RELEASE</version> <relativePath/> <!-- lookup parent from repository --> </parent> <groupId>com.practice</groupId> <artifactId>client</artifactId> <version>0.0.1-SNAPSHOT</version> <name>client</name> <description>Demo project for Spring Boot</description> <properties> <java.version>1.8</java.version> <spring-cloud.version>Greenwich.SR2</spring-cloud.version> </properties> <dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> </dependencies> <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> </dependencies> </dependencyManagement> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build> </project>
四、客户端书写中间实体(*) 下面的JsonProperty中的value是序列化后的名称,定义的名称是为了更好的理解属性
@Data public class ProductVO { /** * 此处须要返回给前端name,可是表示的是类目名称,因此添加JsonProperty进行标识 */ @JsonProperty("name") private String categoryName; @JsonProperty("type") private Integer categoryType; @JsonProperty("foods") List<ProductInfoVO> productInfoVOList; }
Ribbon实现负载均衡
一、了解Ribbon
Eureka属于客户端发现,客户端会向服务器拉取可用的服务器信息,根据负载均衡服务,命中那台服务器发送请求,整个过程都是在客户端完成,并不须要服务器的参与。
Ribbon是netflix ribbon实现的,经过springcloud的封装,轻松的面向服务的rest的模板请求,自动的转换为客户端服务调用。
RestTemplate/zuul/Feign都使用到了Ribbon
SpringCloud在结合Ribbon的负载均衡实践中,封装增长了HTTPClient和OKHttpClient 两种请求端实现,默认使用了Ribbon对Eureka的客户端发现的负载均衡client
二、Ribbon的工做原理
轮寻RoundRobinRule、随机链接RandomRule
Ribbon实现软负载均衡核心有三点
(1)服务发现:依据实例名称,把该实例下全部的实例都找出来
(2)服务选择规则:依据规则从多个规则中选择有效的服务
(3)服务监听:检测失效的服务,作到高效剔除
Ribbon主要组件
serverList (获取全部的可用服务列表)----> ServerListFilter(过滤掉一部分地址) -----> IRule从剩下的地址中选择一个实例,做为目标结果。
HTTP服务调用方式RestTemplate 操做和Feign简单对比
RestTemplate使用的三种方式
详解restTemplate操做 https://blog.csdn.net/itguangit/article/details/78825505
方式一:直接使用restTemplate,路径固定了很差修改 restTemplate对于路径的访问比较固定,若是线上部署,很差进行修改;或者是启动了多个实例,很差进行捕获和调用
RestTemplate restTemplate = new RestTemplate(); String response = restTemplate.getForObject("http://localhost:9001/msg", String.class); log.info("response{}",response); return response;
方式二:利用LoadBalancerClient经过应用名获取url,而后在使用restTemplate 注入LoaderBalancerClient,经过服务名称进行获取服务的ip和端口号
@Autowired private LoadBalancerClient loadBalancerClient; @GetMapping("/getProductMsg") public String getProductMsg(){ // 方式二:(利用LoadBalancerClient经过应用名获取url,而后在使用restTemplate) RestTemplate restTemplate = new RestTemplate(); ServiceInstance serviceInstance = loadBalancerClient.choose("PRODUCT"); String url = String.format("http://%s:%s", serviceInstance.getHost(),serviceInstance.getPort() + "/msg"); String response = restTemplate.getForObject(url, String.class); log.info("response{}",response); return response; }
方式三:经过LoadBalanced实例化restTemplate
@Component public class RestTemplateConfig { @Bean @LoadBalanced public RestTemplate restTemplate() { return new RestTemplate(); } }
经过实例名称进行访问,随机分配一个服务
String response = restTemplate.getForObject("http://PRODUCT/msg", String.class);
Feign进行通讯
- 声明式Rest客户端(伪RPC)
- 采用了基于接口的注解
第一步:引入pom.xml依赖
<!--应用间通讯--> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-feign</artifactId> </dependency>
第二步:添加EnableFeignClient注解
第三步:经过@FeignClient声明被调用的服务信息
@FeignClient(value = "product", fallback = ProductClientFallbackImpl.class ) public interface ProductClient { /** * 获取产品端的信息 * * @return */ @GetMapping("/msg") String getProductMsg(); }
经过fallback处理异常等信息
@Component @Slf4j public class ProductClientFallbackImpl implements ProductClient { /** * 获取产品端的信息 * * @return */ @Override public String getProductMsg() { log.info("返回消息接口出现问题"); return null; } }
Config配置中心
为何须要统一配置中心? 不方便维护 配置内容安全与权限 更新配置项目须要重启
Config Server服务端配置
一、Config服务引入pom.xml
<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-config-server</artifactId> </dependency>
二、Config服务配置yml文件
spring: application: name: micro-weather-config-server cloud: config: server: git: ## 统一配置的仓库 uri: https://github.com/MarkGao11520/spring-cloud-repo/ username: xxxx password: xxxx # 拉取下来的文件防止位置 basedir: /Users/gaowenfeng/project/idea/springcloud_sell/config/basedir ## 仓库路径 search-paths: config-repo eureka: # 服务的url service-url: defaultZone: http://localhost:8761/eureka/ server: port: 8888
配置中心须要配置远程git仓库存储配置 配置中心获取配置文件的两种方式: /{name}-{profiles}.yml /{label}/{name}-{profiles}.yml
name 服务名 profiles 环境 label 分支(branch)
第一种默认使用的是master分支
三、Config启动类上引入ConfigServer注解
@SpringBootApplication @EnableEurekaClient @EnableConfigServer public class ConfigApplication { public static void main(String[] args) { SpringApplication.run(ConfigApplication.class, args); } }
Config Client客户端配置
一、order客户端pom.xml
<!--config配置中心--> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-config-client</artifactId> </dependency>
1.这里若是配置文件名称为application.xxx的话,会搞乱顺序,先找配置中心再找注册中心,这样会找不到,会致使去加载默认的8888 2. eureka.service-url的配置须要在本地,否则会找不到注册中心 bootstrap.yml中配置
# 配置规则 #因此应该叫bootstrap.yml # /{application}/{profile}[/{label}] # /{application}-{profile}.yml # /{application}-{profile}.properties # /{label}/{application}-{profile}.yml # /{label}/{application}-{profile}.properties spring: application: # 配置规则里的{application} name: micro-weather-config-client cloud: config: # 找到配置的方式一 # uri: http://localhost:8888 # 配置规则里的${profile} # profile: bus-dev # 配置规则里的${label},label 是分支,不是路径! # label: test # 找到配置的方式二 discovery: enabled: true service-id: CONFIG profile: test eureka: # 服务的url service-url: defaultZone: http://localhost:8761/eureka/
测试
@RunWith(SpringRunner.class) @SpringBootTest public class MicroWeatherConfigClientApplicationTests { @Value("${version}") private String version; @Test public void contextLoads() { Assert.assertEquals("1.0",version); } }
配置完成以后经过
localhost:8989/order-dev.yml
进行访问
经过localhost:8989/bus/refresh
进行动态刷新本地的配置信息
配置中心出现的问题:
a、springcloud 消息总线读取配置文件的两种方式
https://blog.csdn.net/qq_35275233/article/details/89074886
b、springcloud监控配置中心健康状态
https://blog.csdn.net/qq_35275233/article/details/89074380
c、SpringCloud 配置中心执行bus/refresh 出现 Full authentication is required to access this resource
Spring Cloud Bus 自动刷新配置
一、添加依赖
rabbitmq 的bus依赖添加 config server 和client的版本应该对应起来才能够 <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-bus-amqp</artifactId> </dependency>
二、将bus-refresh接口暴露出去
## 变量名可能会变,利用自动提示能够找到对应的正确配置变量 (输入management.endpoints 找到默认包含一个[health,info]数组的变量,修改成"*") management: endpoints: web: 暴露全部接口 expose: "*"
三、修改使用配置的客户端的地方
@RestController @RequestMapping("/env") @RefreshScope // 加入这个注解自动刷新配置 public class EnvController { @Value("${env}") private String env; @GetMapping("/print") public String print() { return env; } } } @Data @Component @ConfigurationProperties("girl") @RefreshScope // 加入这个注解自动刷新配置 public class GirlConfig { private String name; private Integer age; }
四、使用
修改git的配置之后,POST 请求访问 "http://192.161.4:8080/actuator/bus-refresh" 便可(这个路径可能会变,具体请查阅启动日志)
五、集成WebHooks实现动态更新
在GitHub或者其余git工具上配置webhook,而后就能够实现,在git更新的时候,发送请求给咱们的config服务器让其刷新配置
经过RabbitMQ给各服务发送消息
一、发送消息
/** * 测试给家电和水果服务发送消息 */ @Test public void sendOrderMqMsg() { amqpTemplate.convertAndSend("myOrder","computer","now:" + new Date()); }
二、设置消息监听
/** * 1/测试给家电服务发送消息 * @param message */ @RabbitListener(bindings = @QueueBinding( exchange = @Exchange("myOrder"), key = "computer", value = @Queue("computerOrder") )) public void processComputer (String message) { log.info("ComputerOrder:{}",message); } /** * 2/测试给水果服务发送消息 * @param message */ @RabbitListener(bindings = @QueueBinding( exchange = @Exchange("myOrder"), key = "fruit", value = @Queue("fruitOrder") )) public void processFruit (String message) { log.info("FruitOrder:{}",message); }
Spring Cloud Stream
官方定义stream为微服务应用构建消息驱动能力的框架,为springcloud的另外一个组件
目前springcloud支持的消息中间件是RabbitMQ和Kafka 一、引入pom.xml
<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-stream-rabbit</artifactId> </dependency>
服务网关和Zuul
核心依赖
<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-zuul</artifactId> </dependency>
启动类
/** * Eureka 客户端启动类 * 启用Eureka客户端 */ @SpringBootApplication @EnableDiscoveryClient @EnableZuulProxy public class MicroWeatherEurekaClientApplication { public static void main(String[] args) { SpringApplication.run(MicroWeatherEurekaClientApplication.class, args); } }
配置
spring: application: name: micro-weather-eureka-client eureka: # 服务的url service-url: defaultZone: http://localhost:8761/eureka/ zuul: routes: # 将city的请求转发到应用msa-weather-city-server city: path: /city/** serviceId: msa-weather-city-server # 将data的请求转发到应用msa-weather-data-server data: path: /data/** serviceId: msa-weather-data-server
路由 + 过滤器 = zuul
除了spring bus外,zuul也能够实现自动刷新配置
/** * 实现配置的动态注入 */ @Component public class ZuulConfig { @ConfigurationProperties("zuul") @RefreshScope public ZuulProperties zuulProperties() { return new ZuulProperties(); } }
Zuul的高可用
- 多个Zuul节点注册到Eureka Server
- Nginx和Zuul的“混搭”
pre和post过滤器
一、访问前进行参数校验
pre-filter:请求到目标结果以前,对返回的结果进行加工,访问链接中须要添加token,进行接口加密校验
@Component public class TokenFilter extends ZuulFilter { @Override public String filterType() { return PRE_TYPE; } @Override public int filterOrder() { return PRE_DECORATION_FILTER_ORDER - 1; } @Override public boolean shouldFilter() { return true; } @Override public Object run() { RequestContext requestContext = RequestContext.getCurrentContext(); HttpServletRequest request = requestContext.getRequest(); String token = request.getParameter("token"); if (StringUtils.isEmpty(token)) { requestContext.setSendZuulResponse(false); requestContext.setResponseStatusCode(HttpStatus.SC_UNAUTHORIZED); } return null; } }
二、post-fileter : 请求到目标结果以后,对返回的结果进行加工
@Component public class AddResponseHeaderFilter extends ZuulFilter { @Override public String filterType() { return POST_TYPE; } @Override public int filterOrder() { return SEND_RESPONSE_FILTER_ORDER - 1; } @Override public boolean shouldFilter() { return true; } @Override public Object run() { /** * 往返回的结果添加x-foo */ RequestContext requestContext = RequestContext.getCurrentContext(); HttpServletResponse response = requestContext.getResponse(); response.setHeader("X-FOO", UUID.randomUUID().toString()); return null; } }
Zuul 限流
时机:请求被转发以前调用
令牌桶:拿到令牌的放行
限流过滤器
/** * 限流过滤器 */ @Component public class RateLimitFilter extends ZuulFilter { /** * guava的令牌桶算法:放入100个令牌 */ private static final RateLimiter RATE_LIMITER = RateLimiter.create(100); @Override public String filterType() { return PRE_TYPE; } @Override public int filterOrder() { return SERVLET_DETECTION_FILTER_ORDER - 1; } @Override public boolean shouldFilter() { return true; } @Override public Object run() { /** * tryAcquire(1, 0, TimeUnit.MICROSECONDS); * 取到一个令牌 */ if (!RATE_LIMITER.tryAcquire()) { throw new RateLimitException(); } return null; } }
参考资料: 微信点餐系统 springcloud学习笔记 dependencies与dependencyManagement的区别 springcloud(一)-集成Eureka 服务注册与发现 Springcloud(二)-拆分微服务 SpringCloud(三)-应用间通讯 springcloud(四)-zuul网关与config统一配置 Springcloud-hystrix断路器实现