微服务架构中,每个项目都有一个yml配置,管理起来麻烦。要使用spring cloud config来统一管理。
在Spring Cloud中,有分布式配置中心组件spring cloud config ,它支持配置服务放在配置服务的内存中(即本地),也支持放在远程Git仓库中
在spring cloud config 组件中,分两个角色,一是config server,二是config client。
项目所有的资源都在一个应用中,打包成一个war包,使用一个tomcat去运行,运行在一个进程中
微服务就是把一个大的系统,拆分成多个小的服务,每个微服务只专注一个业务 ,每个服务有各自的进程, 微服务之间使用网络通信协议进行数据交互(通常是基于HTTP的RESTful API)。
Spring cloud是一个基于Spring Boot实现的服务治理工具包,用于微服务架构中管理和协调服务的
实现服务的通信地址的注册(保存地址)与发现(获取地址)
当微服务启动就要提交注册,并且下载注册中心的服务通信地址清单
在调用访问的时候,从地址清单中根据服务名找到ip和端口,进行http调用
注册中心使用心跳机制动态实现服务的上下线,并且通知所有的微服务重新同步地址清单
springcloud-parent
springcloud-eureka-server-1000 //注册中心EurekaServer
springcloud-order-server-3000 //订单服务EurekaClient ,消费者
springcloud-user-server-2000 //用户服务EurekaClient ,提供者
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.0.5.RELEASE</version>
</parent>
<groupId>cn.itsource.springcloud</groupId>
<artifactId>springcloud-parent</artifactId>
<version>1.0-SNAPSHOT</version>
<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.SR1</spring-cloud.version>
</properties>
<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>
<dependencies>
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starter-netflix-eureka-serverartifactId>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-webartifactId>
dependency>
dependencies>
/**
* @EnableEurekaServer :开启注册中心
*/
@SpringBootApplication
@EnableEurekaServer
public class EurekaServerApplication1000 {
public static void main(String[] args) {
SpringApplication.run(EurekaServerApplication1000.class);
}
}
server:
port: 1000
eureka:
instance:
hostname: localhost #主机配置
client:
registerWithEureka: false #禁用注册中心向自己注册
fetchRegistry: false #不让注册中心获取服务的注册列表
serviceUrl:
defaultZone: http://localhost:1000/eureka/
#注册中心的注册地址 ,其他微服务需要向这个地址注册
<dependencies>
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starter-netflix-eureka-clientartifactId>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-webartifactId>
dependency>
dependencies>
//@EnableDiscoveryClient:开启注册中心客户端和 @EurekaClient一样,不一样的是EnableDiscoveryClient:功能强大一些,不仅仅支持Eureka
@SpringBootApplication
@EnableDiscoveryClient
public class UserServerApplication2000 {
public static void main(String[] args) {
SpringApplication.run(UserServerApplication2000.class);
}
}
eureka:
client:
serviceUrl:
defaultZone: http://localhost:1000/eureka/ #注册中心地址
server:
port: 2000
spring:
application:
name: user-server
步骤同上(搭建提供者服务)
1.修改用户服务和订单服务如下
eureka:
client:
serviceUrl:
defaultZone: http://localhost:1000/eureka/ #注册中心地址
instance:
prefer-ip-address: true #使用ip地址注册
instance-id: order-server #指定服务的id
1.建立domain : user
<dependency>
<groupId>cn.itsource.springcloudgroupId>
<artifactId>springcloud-common-userartifactId>
<version>...version>
dependency>
/**
* 用户提供者服务
*/
@RestController
public class UserProviderController {
/**
* 提供者方法,orderServer消费者来调用
* @param id
* @return
*/
// @RequestMapping(value = "/user/{id}",method = RequestMethod.GET)
@GetMapping("/user/{id}")
public User getUserById(@PathVariable("id")Long id){
System.out.println("UserProviderController.getUserById被调用了......");
return new User(id,"zs:"+id , "123");
}
}
<dependency>
<groupId>cn.itsource.springcloudgroupId>
<artifactId>springcloud-common-userartifactId>
<version>...version>
dependency>
/**
* SpringMvc提供的一个基于Rest风格的http调用工具
* @return
*/
@Bean
public RestTemplate restTemplate(){
return new RestTemplate();
}
/**
* 订单消费者服务
*/
@RestController
public class OrderConsumerController {
@Autowired
private RestTemplate restTemplate ;
/**
* 该方法是浏览器来调用
* String url :是要从哪里获取数据就写哪里的地址
* @param id
* @return
*/
@GetMapping("/order/user/{id}")
public User getUserById(@PathVariable("id") Long id){
System.out.println("OrderConsumerController.getUserById被调用了......");
String url = "http://localhost:2000/user/"+id;
User user = restTemplate.getForObject(url, User.class);
return user;
}
}
如果只有一个EurekaSever,如果EurekaSever挂了那么整个微服务都不可用
EurekaServer高可用集群
创建两个本地域名 C:\Windows\System32\drivers\etc\hosts
127.0.0.1 peer1
127.0.0.1 peer2
修改注册中心eureka-server-1000
server:
port: 1000
eureka:
instance:
hostname: peer1
client:
registerWithEureka: false #禁用注册中心向自己注册
fetchRegistry: false #不让注册中心获取服务的注册列表
serviceUrl:
defaultZone: http://peer2:1001/eureka/
#注册中心的注册地址 ,其他微服务需要向这个地址注册
搭建第二个注册中心eureka-server-1001 - 复制
server:
port: 1001
eureka:
instance:
hostname: peer2
client:
registerWithEureka: false #禁用注册中心向自己注册
fetchRegistry: false #不让注册中心获取服务的注册列表
serviceUrl:
defaultZone: http://peer1:1000/eureka/·
#注册中心的注册地址 ,其他微服务需要向这个地址注册
eureka:
client:
serviceUrl:
defaultZone: http://peer1:1000/eureka/,http://peer2:1001/eureka/ #注册中心地址
...
ribbon是负载均衡器,是基于RestTemplate ,它赋予了RestTemplate 负载均衡的能力
eureka:
client:
serviceUrl:
defaultZone: http://peer1:1000/eureka/,http://peer2:1001/eureka/ #注册中心地址
instance:
prefer-ip-address: true #使用ip地址注册
instance-id: user-server1 #指定服务的id
server:
port: 2000
spring:
application:
name: user-server
注意:instance-id需要修改,端口需要修改spring.application.name 不需要修改
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starter-netflix-ribbonartifactId>
dependency>
/**
* SpringMvc提供的一个基于Rest风格的http调用工具
* @LoadBalanced :ribbon的负载均衡标签,赋予RestTemplate有负债均衡的能力
*/
@Bean
@LoadBalanced
public RestTemplate restTemplate(){
return new RestTemplate();
}
@LoadBalanced :ribbon的负载均衡标签,赋予RestTemplate有负债均衡的能力
@GetMapping("/order/user/{id}")
public User getUserById(@PathVariable("id") Long id){
System.out.println("OrderConsumerController.getUserById被调用了......");
//String url = "http://localhost:2000/user/"+id;
String url = "http://user-server/user/"+id;
User user = restTemplate.getForObject(url, User.class);
return user;
}
修改效果: String url = “http://user-server/user/”+id;
基于Ribbon封装的客户端负载均衡器
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starter-openfeignartifactId>
dependency>
@SpringBootApplication
@EnableFeignClients("cn.itsource.client")
public class DeptServerApplication4000
...
/**
* @FeignClient(value="user-server") : 针对于用户服务调用的 Fiegn的客户端接口
* value属性就是该接口要调用的目标服务的名字
* Feign是如何实现服务调用的:
* 1.通过@FeignClient(value="user-server")标签上的服务名字能够找到要调用的目标服务
* 2.通过接口中的方法的 @GetMapping的url路径找到目标服务的controller的方法 ,
* 所以要保证Feign客户端接口的方法和目标服务的对应的方法要完全一致。
*
*/
@FeignClient(value="user-server")
public interface UserFeignClient {
@GetMapping("/user/{id}")
User getUserById(@PathVariable("id")Long id);
}
Feign客户端接口的方法和目标服务的对应的方法要完全一致
@RestController
public class DeptConsumerController {
@Autowired
private UserFeignClient userFeignClient ;
/**
* 该方法是浏览器来调用
* @param id
* @return
*/
@GetMapping("/dept/user/{id}")
public User getUserById(@PathVariable("id") Long id){
User user = userFeignClient.getUserById(id); //使用Feign的接口调用
return user;
}
}
一个微服务的故障导致整个微服务调用链全部瘫痪
解决服务器故障(雪崩)的一个组件 ,它可以实现:隔离 ,熔断 ,降级,缓存
隔离 :包括线程池隔离和信号量隔离,限制调用分布式服务的资源使用,某一个调用的服务出现问题不会影响其他服务调用。
熔断 :当请求次数达到规定的阀值都出现服务故障(超时),Hystrix就把服务标记为短路状态.
正常情况下,断路器处于关闭状态(Closed),如果调用持续出错或者超时,电路被打开进入熔断状态(Open),
后续一段时间内的所有调用都会被拒绝(Fail Fast),一段时间以后,保护器会尝试进入半熔断状态(Half-Open),
允许少量请求进来尝试,
如果调用仍然失败,则回到熔断状态
如果调用成功,则回到电路闭合状态;
降级 :高并发情况下 ,为了保证一些主要的服务有足够的资源不出问题 ,会认为的关掉一些无关紧要的服务,然后返回一些托底的数据,给用户一个友好的提示。
缓存 :Hystrix内部会把请求做缓存
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starter-netflix-hystrixartifactId>
dependency>
/**
* 该方法是浏览器来调用
* @HystrixCommand :开启方法的短路功能 , 如果方法出现异常,会调用 fallbackMethod
* 指向的方法 ,然后返回托底数据
*/
@HystrixCommand(fallbackMethod = "getUserByIdFallback")
@GetMapping("/order/user/{id}")
public JsonResult getUserById(@PathVariable("id") Long id){
System.out.println("OrderConsumerController.getUserById被调用了......");
String url = "http://user-server/user/"+id;
User user = restTemplate.getForObject(url, User.class);
return JsonResult.me().setData(user);
}
public JsonResult getUserByIdFallback(@PathVariable("id") Long id){
return JsonResult.me().setSuccess(false)
.setMessage("服务展示不可用,请骚后重试[服务降级]");
}
注意:托底方法的参数和返回结果要和原方法一致
feign:
hystrix:
enabled: true #开启熔断支持
client:
config:
remote-service: #服务名,填写default为所有服务
connectTimeout: 30000
readTimeout: 30000
hystrix:
command:
default:
execution:
isolation:
thread:
timeoutInMilliseconds: 30000
@FeignClient(value="user-server" ,fallback = UserFeignClientFallback.class)
public interface UserFeignClient {
@GetMapping("/user/{id}")
User getUserById(@PathVariable("id")Long id);
}
fallback = UserFeignClientFallback.class : 该类是当前接口的实现类 ,也是托底数据所在的处理类
@Component
public class UserFeignClientFallback implements UserFeignClient {
@Override
public User getUserById(Long id) {
System.out.println("托底数据执行....");
return new User(-1L,"托底数据","");
}
}
Zuul是一个SpringCloud的网关组件 , 它是微服务的入口,网络关卡 ,通过Zuul我们可以实现请求的分发(负载均衡),鉴权,限流等等操作。
<dependencies>
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starter-netflix-eureka-clientartifactId>
dependency>
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starter-netflix-zuulartifactId>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-webartifactId>
dependency>
<dependency>
<groupId>com.alibabagroupId>
<artifactId>fastjsonartifactId>
<version>1.2.55version>
dependency>
dependencies>
/**
* @EnableZuulProxy :开启 网关
* @EnableZuulServer
*/
@SpringBootApplication
@EnableZuulProxy
@EnableDiscoveryClient
public class ZuulApplication5000
...
eureka:
client:
serviceUrl:
defaultZone: http://peer1:1000/eureka/,http://peer2:1001/eureka/ #注册中心地址
instance:
prefer-ip-address: true #使用ip地址注册
instance-id: zuul-server #指定服务的id
server:
port: 5000
spring:
application:
name: zuul-server
zuul:
ignored-services: "*" #禁止使用服务名字进行访问
prefix: "/servers" #统一的前缀
routes: #配置路由,指定服务的访问路径
dept-server: "/dept/**"
user-server: "/user/**"
order-server: "/order/**"
retryable: true #开启重试
ribbon:
ConnectTimeout: 2000 # 连接超时时间(ms)
ReadTimeout: 2000 # 通信超时时间(ms)
OkToRetryOnAllOperations: true # 是否对所有操作重试
MaxAutoRetriesNextServer: 2 # 同一服务不同实例的重试次数
MaxAutoRetries: 1 # 同一实例的重试次数
hystrix:
command:
default:
execution:
isolation:
thread:
timeoutInMillisecond: 3000 # 熔断超时时长:3000ms
http://localhost:1000/
@Component
public class LoginCheckFilter extends ZuulFilter {
public static final int LOGIN_CHECK_FILTER_ORDER = 1;
//过滤器的类型 :
@Override
public String filterType() {
return FilterConstants.PRE_TYPE; //pre 前置
}
@Override
public int filterOrder() {
return LOGIN_CHECK_FILTER_ORDER;
}
/**
* 返回true执行run,否则不执行run
* @return
*/
@Override
public boolean shouldFilter() {
// login 路径要放行,不需要检查登录
//请求对象
HttpServletRequest request = RequestContext.getCurrentContext().getRequest();
//访问路径
if(request.getRequestURI().contains("login")){
return false;
}
return true;
}
@Override
public Object run() throws ZuulException {
//做登录的判断
RequestContext currentContext = RequestContext.getCurrentContext();
//请求对象
HttpServletRequest request = currentContext.getRequest();
String token = request.getHeader("access-token");
if(!StringUtils.hasLength(token)){
//没有登录
HttpServletResponse response = currentContext.getResponse();
response.setContentType("text/json;charset=utf-8");
//响应码:没有授权
response.setStatus(HttpStatus.SC_UNAUTHORIZED);
//设置错误响应信息
Map<String,Object> map = new HashMap<>();
map.put("success",false );
map.put("message","没有登录,请先登录");
String jsonString = JSON.toJSONString(map);
try {
response.getWriter().write(jsonString);
} catch (IOException e) {
e.printStackTrace();
}
//阻止放行
currentContext.setSendZuulResponse(false);
}
//判断token是否正确,来确定是否已经
return null;
}
}
集中管理微服务的配置: SpringCloud-config 是分布式配置中心组件
码云上创建配置文件
<dependencies>
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starter-netflix-eureka-clientartifactId>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-webartifactId>
dependency>
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-config-serverartifactId>
dependency>
dependencies>
/**
* @EnableConfigServer:开启配置中心
*/
@SpringBootApplication
@EnableDiscoveryClient
@EnableConfigServer
public class ConfigServerApplication6000
...
eureka:
client:
serviceUrl:
defaultZone: http://peer1:1000/eureka/,http://peer2:1001/eureka/ #注册中心地址
instance:
prefer-ip-address: true #使用ip地址注册
instance-id: config-server #指定服务的id
server:
port: 6001
spring:
application:
name: config-server
cloud:
config:
server:
git:
uri: https://gitee.com/baidu11.com/springcloud-config.git #配置远程仓库地址
username: [email protected]
password: wodemima123
5.测试
http://localhost:6001/application-dept-test.yml
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-config-clientartifactId>
dependency>
spring:
cloud:
config:
uri: http://localhost:6001
name: application-dept #-test.yml
profile: test #环境 组成完整的文件名:application-dept-test.yml
label: master
1.说一下你对SpringBoot的理解
2.说一下SpringBoot自动配置原理 / 为什么SpringBoot用来很简单 / 你们项目为什么要用SpringBoot 不用ssm
3.说一下你的SpringCloud的理解
4.你们项目中用zuul做什么?
5.如果不用dubbo,SpringCloud等微服务框架 ,你怎么让你的项目实现微服务
6.说一dubbo和SpringCloud的区别
全天代码
全天代码