<?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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<!--申明此项目为springboot项目-->
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.2.2.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.example</groupId>
<artifactId>springcloud</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>pom</packaging>
<name>springcloud</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
server:
port: 8761 #指定该Eureka实例的端口
eureka:
instance:
hostname: localhost #设置当前实例的主机名称
client:
registerWithEureka: false #禁止注册自身
fetchRegistry: false #因为该服务没有注册到其他注册中心,所以关闭从注册中心拉取服务列表。
serviceUrl: #服务注册中心地址
defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/
#eureka是一个高可用的组件,它没有后端缓存,每一个实例注册之后需要向注册中心发送心跳(因此可以在内存中完成)
#通过eureka.client.registerWithEureka:false和fetchRegistry:false来表明自己是一个eureka server.
#
使用父工程spring-cloud的spring boot依赖
<?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>
<groupId>pers.li</groupId>
<artifactId>eureka-server</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>jar</packaging>
<name>eureka-server</name>
<description>Demo project for Spring Boot</description>
<parent>
<groupId>pers.li</groupId>
<artifactId>boot-parent</artifactId>
<version>0.0.1-SNAPSHOT</version>
</parent>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
<spring-cloud.version>Finchley.RELEASE</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>
<repositories>
<repository>
<id>spring-milestones</id>
<name>Spring Milestones</name>
<url>https://repo.spring.io/milestone</url>
<snapshots>
<enabled>false</enabled>
</snapshots>
</repository>
</repositories>
</project>
@EnableEurekaClient
@RestController
@SpringBootApplication
@EnableEurekaClient
@RestController
public class EurekaClient01Application {
public static void main(String[] args) {
SpringApplication.run(EurekaClient01Application.class, args);
}
@Value("${server.port}")
String port;
@RequestMapping("/hi")
public String home(@RequestParam String name) {
return "hi "+name+",i am from port:" +port;
}
}
eureka:
client:
serviceUrl:
defaultZone: http://localhost:8761/eureka/
server:
port: 8762
spring:
application:
# 此处名称最好写项目名称,便于识别服务提供者
name: eureka-client-01
@SpringBootApplication
@EnableEurekaClient
@RestController
public class EurekaClient01Application {
public static void main(String[] args) {
SpringApplication.run(EurekaClient01Application.class, args);
}
@Value("${server.port}")
String port;
@RequestMapping("/hi")
public String home(@RequestParam String name) {
return "hi "+name+",i am from port:" +port;
}
}
1.在微服务架构中,业务都会被拆分成一个独立的服务,服务与服务的通讯是基于http restful的。 Spring cloud有两种服务调用方式,一种是ribbon+restTemplate,另一种是feign。
2.ribbon是一个负载均衡客户端,可以很好的控制htt和tcp的一些行为。Feign默认集成了ribbon。
spring.application.name不修改,仍为eureka-client-01:
由于服务之间是根据此名称进行相互调用,所以此时表
示
8763,8762对外提供一 个服务,服务名为eureka-client-01=====>等同于一个小的集群

@SpringBootApplication
@EnableDiscoveryClient
public class ServiceRibbonApplication {
public static void main(String[] args) {
SpringApplication.run(ServiceRibbonApplication.class, args);
}
@Bean
@LoadBalanced
RestTemplate restTemplate() {
return new RestTemplate();
}
}
service-ribbon结构
HelloRibbonService
package com.example.serviceribbon.service;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;
@Service
public class HelloRibbonService {
@Autowired
RestTemplate restTemplate;
public String hiService(String name) {
//使用注册到Eureka服务中心的客户端,由客户端分配具体调用哪个服务
return restTemplate.getForObject("http://eureka-client-01/hi?name="+name,String.class);
}
}
HelloRibbonController
package com.example.serviceribbon.controller;
import com.example.serviceribbon.service.HelloRibbonService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class HelloRibbonController {
@Autowired
HelloRibbonService helloRibbonService;
@RequestMapping(value = "/hi")
public String hi(@RequestParam String name){
return helloRibbonService.hiService(name);
}
}
场景总结:
1.一个服务注册中心,eureka-server端口为8761
2.向服务注册中心eureka-server注册两个客户端服务:名称为eureka-client-01包含服务:8762/8763
3.向服务之策中心eureka-server注册消费者服务:service-ribbon端口:8764
4.当service-ribbon通过restTemplate调用eureka-client-01的hi接口时,因为用了ribbon负载均衡,会轮流的调用eureka-client-01:8762和8763两个端口的hi接口;
1.Feign是一个声明式的伪Http客户端,它使得写Http客户端变得更简单。
使用Feign,只需要创建一个接口并注解。它具有可插拔的注解特性,可使用Feign 注解和JAX-RS注解。
Feign支持可插拔的编码器和解码器。Feign默认集成了Ribbon,并和Eureka结合,默认实现了负载均衡的效果
2.简单理解:
·Feign 采用的是基于接口的注解
·Feign 整合了ribbon
<?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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.example</groupId>
<artifactId>service-feign</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>service-feign</name>
<description>Demo project for Spring Boot</description>
<parent>
<groupId>com.example</groupId>
<artifactId>spring-cloud</artifactId>
<version>0.0.1-SNAPSHOT</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
<spring-cloud.version>Hoxton.SR1</spring-cloud.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-feign</artifactId>
</dependency>
<!--高版本feign依赖============================-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</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>
<repositories>
<repository>
<id>spring-milestones</id>
<name>Spring Milestones</name>
<url>https://repo.spring.io/milestone</url>
<snapshots>
<enabled>false</enabled>
</snapshots>
</repository>
</repositories>
</project>
package com.example.servicefeign;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.openfeign.EnableFeignClients;
@SpringBootApplication
@EnableDiscoveryClient
@EnableFeignClients
public class ServiceFeignApplication {
public static void main(String[] args) {
SpringApplication.run(ServiceFeignApplication.class, args);
}
}
和controller,feign基于接口——创建serviceTestHelloFeignService
package com.example.servicefeign.service;
import com.example.servicefeign.service.impl.HelloFeignServiceImpl;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
@FeignClient(value = "eureka-client-01", fallback = HelloFeignServiceImpl.class)
public interface HelloFeignService {
@GetMapping(value = "/hi")
String sayHiFromClientOne(@RequestParam (value = "name") String name);
}
HelloFeignServiceImpl
package com.example.servicefeign.service.impl;
import com.example.servicefeign.service.HelloFeignService;
public class HelloFeignServiceImpl implements HelloFeignService {
@Override
public String sayHiFromClientOne(String name) {
return "hello"+name;
}
}
HelloFeignController
package com.example.servicefeign.controller;
import com.example.servicefeign.service.HelloFeignService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class HelloFeignController {
@Autowired
private HelloFeignService helloFeignService;
@GetMapping(value = "hi")
public String sayHi(@RequestParam String name){
return helloFeignService.sayHiFromClientOne(name);
}
}
在微服务架构中,一个请求需要调用多个服务是非常常见的,叫底层的服务如果出现故障,会导致连锁故障。当对特定的服务的调用的不可用达到一个阈值(Hystrix是5秒20次断路器将会被打开。断路打开后,可用避免连锁故障,fallback方法可以直接返回一个固定值)。


3)改造HelloRibbonService类,在hiService方法上加上@HystrixCommand注解
注意: 该注解对该方法创建了熔断器的功能,并指定了fallbackMethod熔断方法, 熔断方法直接返回了一个字符串,字符串为“hi,”+name+“,sorry,error!”,
package com.example.serviceribbon.service;
import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;
@Service
public class HelloRibbonService {
@Autowired
RestTemplate restTemplate;
@HystrixCommand(fallbackMethod="hiError")
public String hiService(String name) {
//使用注册到Eureka服务中心的客户端,由客户端分配具体调用哪个服务
return restTemplate.getForObject("http://eureka-client-01/hi?name="+name,String.class);
}
public String hiError(String name){
return "hi" + name +"sorry,error!";
}
}
4)断路测试: ·启动ribben访问 ·关闭eureka-client-01,再访问,会得到短路由返回值 ·eureka-client-01工程不可用的时候,service-ribbon调用 service-hi的API接口时, 会执行快速失败,直接返回一组字符串,而不是等待响应超时,这很好的控制了容器的线程阻塞
4.Feign中使用断路器:
1)
Feign是自带断路器的,在D版本的Spring Cloud中,它没有默认打开。需要在配置文件中配置打开:feign.hystrix.enabled=true

2)在@FeignClient接口的注解中加上fallback的指定类HelloFeignServiceImpl

3)HelloFeignServiceImpl
需要实现HelloFeignService接口,并注入到Ioc容器中(组件注解)
4)断路测试-同上
1.
Hystrix Dashboard (断路器:Hystrix 仪表盘):
--仪表盘添加+ribbon和feign相同
--以ribbon为例:
1)添加依赖
<!--添加断路器-仪表盘依赖==========-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix-dashboard</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
</dependency>
·加入@EnableHystrixDashboard注解,开启hystrixDashboard
·在spring版本2.0以上需要注入一个servlet(启动类中)
//仪表盘注解
@EnableHystrixDashboard
·第一个文本框输入http://localhost:8764/hystrix.stream

4)点击
monitor stream,进入下一个界面
·显示加载中load....
5)
访问:http://localhost:8764/hi?name=forezp,查看监控界面

1.在Spring Cloud微服务系统中,一种常见的负载均衡方式是:
1)客户端请求-->负载均衡(zuul、Ngnix)-->
2)服务网关(zuul集群)-->具体的服务器 -->
3)服务统一注册到高可用的服务注册中心集群
4)服务的所有的配置文件由配置服务管理, 配置服务的配置文件放在git仓库,方便开发人员随时改配置
2.Zuul的主要功能是路由转发和过滤器。路由功能是微服务的一部分, 比如/api/user转发到到user服务,/api/shop转发到到shop服务。 zuul默认和Ribbon结合实现了负载均衡的功能
3.创建service-zuul工程:
1)pom依赖:spring-cloud-starter-netflix-zuul
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-zuul</artifactId>
</dependency>
2)applicaton类
·加上注解@EnableZuulProxy,开启zuul的功能
·加上注解@EnableEurekaClient,注册进eureka服务
3)yml配置文件
eureka:
client:
serviceUrl:
defaultZone: http://localhost:8761/eureka/
server:
port: 8769
spring:
application:
name: service-zuul
zuul:
routes:
api-a:
path: /api-a/**
serviceId: service-ribbon
api-b:
path: /api-b/**
serviceId: service-feign
#指定服务注册中心的地址为http://localhost:8761/eureka/,服务的端口为8769,服务名为service-zuul;
#以/api-a/ 开头的请求都转发给service-ribbon服务
#以/api-b/ 开头的请求都转发给service-feign 服务
4)测试:
http://localhost:8769/api-a/hi?name=forezp 浏览器显示:hi forezp,i am from port:8762
http://localhost:8769/api-b/hi?name=forezp 浏览器显示:hi forezp,i am from port:8762
4.zuul不仅只是路由,并且还能过滤,做一些安全验证,详见filter
package com.example.servicezuul.filter;
import com.netflix.zuul.ZuulFilter;
import com.netflix.zuul.context.RequestContext;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import javax.servlet.http.HttpServletRequest;
@Component
public class Filter extends ZuulFilter {
private static Logger log = LoggerFactory.getLogger(Filter.class);
/**
* filterType:返回一个字符串代表过滤器的类型:
* 在zuul中定义了四种不同生命周期的过滤器类型-
* pre:路由之前
* routing:路由之时
* post: 路由之后
* error:发送错误调用
* @return
*/
@Override
public String filterType() {
return "pre";
}
/**
* filterOrder:过滤的顺序
* @return
*/
@Override
public int filterOrder() {
return 0;
}
/**
* shouldFilter:这里可以写逻辑判断,是否要过滤,本文true,永远过滤
* @return
*/
@Override
public boolean shouldFilter() {
return true;
}
/**
* run:过滤器的具体逻辑。可用很复杂,包括查sql,nosql去判断该请求到底有没有权限访问
* @return
*/
@Override
public Object run() {
RequestContext ctx = RequestContext.getCurrentContext();
HttpServletRequest request = ctx.getRequest();
log.info(String.format("%s >>> %s", request.getMethod(), request.getRequestURL().toString()));
Object accessToken = request.getParameter("token");
if(accessToken == null) {
log.warn("token is empty");
ctx.setSendZuulResponse(false);
ctx.setResponseStatusCode(401);
try {
ctx.getResponse().getWriter().write("token is empty");
}catch (Exception e){}
return null;
}
log.info("ok");
return null;
}
}
5.测试filter:
http://localhost:8769/api-a/hi?name=forezp 网页显示: token is empty
http://localhost:8769/api-a/hi?name=forezp&token=22 网页显示: hi forezp,i am from port:8762
A上@EnableConfigServer注解开启配置服务器的功能
config-client-dev.properties
访问http://localhost:8888/config-client-dev.properties
展示其内容:foo: foo version 2
b)具体的映射关系测试请查看其配置文件
http请求地址和资源文件映射如下:
/{application}/{profile}[/{label}]
/{application}-{profile}.yml
/{label}/{application}-{profile}.yml
/{application}-{profile}.properties
/{label}/{application}-{profile}.properties
#{application}映射客户端的"spring.application.name"
#{profile}映射客户端的"spring.profiles.active"(逗号分隔列表)
spring-cloud-starter-config
bootstrap.properties
3)程序的入口类,写一个API接口“/hi”,返回从配置中心读取的foo变量的值
测试
打开网址访问:http://localhost:8881/hi,网页显示:
dev|å¼?å??è??模å¼?é??ç½® 这就说明,config-client从config-server获取了foo的属性,而config-server是从git仓库读取的
eureka依赖
2)yml上,指定服务端口为8889,加上作为服务注册中心的基本配置
3)入口类: @EnableEurekaServer
EurekaClient的起步依赖
2)配置文件application.yml,指定服务注册地址为http://localhost:8889/eureka/
3)最后需要在程序的启动类Application加上@EnableEurekaClient的注解
加上服务注册地址为http://localhost:8889/eureka
spring.cloud.config.discovery.enabled是从配置中心读取文件。
spring.cloud.config.discovery.serviceId配置中心的serviceId,即服务名。
这时发现,在读取配置文件不再写ip地址,而是服务名,这时如果配置服务部署多份,通过负载均衡,从而高可用。
dev|å¼?å??è??模å¼?é??ç½®
原文:https://www.cnblogs.com/maoxy/p/12167791.html