SpringCloud一条龙

本文深入探讨微服务架构的优缺点,对比单体应用,详细解析Spring Cloud组件如Eureka、Ribbon、Feign、Hystrix及Zuul的配置与使用,展示如何构建高可用、可扩展的微服务系统。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

SpringCloud

1.单体应用

项目所有的资源、模块、业务等都在一个应用中,整个项目打包成一个War包,通过一个tomcat运行,这样的应用就是单体应用,多模块应用如果最终仍然打成一个war包,那么他仍然是一个单体应用。
在这里插入图片描述缺点:

​ 1.一个模块挂掉,整个项目挂掉

​ 2.一个模块升级,整个项目重启

​ 3.项目开发越久,代码越臃肿,维护困难/开发困难

​ 4.单体tomcat对高并发的支持有限,顶不住高并发(可以通过集群解决—不能局部扩展)
在这里插入图片描述5.技术选型单一

​ 6.数据库选型一般比较单一

优点:

​ 1.技术体系简单

​ 2.初期开发成本低

​ 3.初期开发效率高-项目搭建快

​ 4.项目小使用单体应用性能很好

2.微服务(分布式)架构

微服务是一种架构思想,它提倡把一个大的系统拆分成多个小的服务(独立的项目),这些微服务都有自己的容器(Tomcat)/进程,他们是独立的,他们之间通过网络协议进行数据交互,共同协作。
在这里插入图片描述优点:

​ 1.一个微服务死掉/升级,不会影响到其他服务

​ 2.单个微服务,只关心一个业务即可,代码量少,维护/开发都方便

​ 3.每个微服务有自己的数据库—按照业务分库

​ 4.技术选型多样化

​ 5.处理高并发问题比较好,局部拓展方便(单模块集群方便)

​ 6.数据库选型多样化

在这里插入图片描述缺点:

​ 1.数据通信性能不好

​ 2.技术成本高

​ 3.开发麻烦,难度大

​ 4.成本高!!!

MicroService(微服务)架构

服务注册

在这里插入图片描述项目形式
在这里插入图片描述1.parent管理

这里主要是pom.xml中

一方面管理springboot依赖

 <!--springboot-->
<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>2.0.5.RELEASE</version>
</parent>

另一方面管理springcloud依赖–控制版本等参数

<!--springcloud-->
    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-dependencies</artifactId>
                <version>Finchley.SR1</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>

还有子模块共用的配置和一些共用的依赖也可以在这里导入

 <!--所有模块都需要的配置-可以继承-->
    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <maven.compiler.source>1.8</maven.compiler.source>
        <maven.compiler.target>1.8</maven.compiler.target>
    </properties>
    <!--测试类,所有模块都会用到-->
    <dependencies>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
        </dependency>
    </dependencies>

Eureka–server 服务端–注册中心

1.导包

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

2.主配置类

@SpringBootApplication
@EnableEurekaServer//开启服务
public class Application {
    /**
     * 启动方法
     * @param args
     */
    public static void main(String[] args) {
        SpringApplication.run(Application.class);
        //new SpringApplicationBuilder(Application.class).web(true).run(args);
    }

}

3application.yml

server:
  port: 1000 #端口

eureka:
  instance:
    hostname: localhost #主机名称
  client:
    registerWithEureka: false  #禁止将自己注册到自己
    fetchRegistry: false  #禁止获取注册清单
    serviceUrl:   #获取上面的hostname 以及 port 
      defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/  #-这里配置的这个主要是客户端要连接到服务端的连接
order-server

现在把他当做消费者端 -后续会配置其他东西

配置方式类似于注册中心,一些细小的差别

1.导包—这里导包是client客户端

<dependencies>
    <!--客户端-->
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
    </dependency>
    <!--web-->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
</dependencies>

2.主配置文件ApplicationConfig

因为这里用到了控制层,所以前面导入依赖的时候导入了spring-boot-starter-web

@SpringBootApplication
@RestController
public class ApplicationConfig {

    @RequestMapping("/")
    public String home() {
        return "Hello world";
    }

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

}

3.application.yml

eureka:
  client:
    serviceUrl:
      defaultZone: http://localhost:1000/eureka/ #连接服务器端
  instance:
    instance-id: order-server #当前客户端服务的id
    prefer-ip-address: true # 使用ip进行注册
server:
  port: 2000 #当前服务端口号

spring:
  application:
    name: order-server  #当前应用名称

微服务之间的通信

1.返回Json字符串的方式

2.公共模块,添加依赖-------推荐
在这里插入图片描述

公共模块方式实现

在这里插入图片描述1.准备domain

​ 1.新建模块

​ 2.新建domain对象

​ 3.需要用到的模块,依赖(先打jar包–install一下)在这里插入图片描述注意:父模块引用管理版本

2.准备Controller

​ 1.UserServerController–@GetMapping

@RestController
@RequestMapping("/userserver")
public class UserServerController {

    //get请求
    @GetMapping("/user/{id}")//这里是被Order服务调用
    public User getUserById(@PathVariable Long id){
        //模拟数据库返回假数据
        return new User(id,"蔡徐坤","唱跳rap篮球");
    }
}

​ 2.OrderServerController

@RequestMapping("/orderserver")
@RestController
public class OrderServerController {

    @Autowired
    private RestTemplate restTemplate;

    //get请求
    @GetMapping("/user/{id}")//这里是被Order服务调用
    public User getUserById(@PathVariable Long id){
        return restTemplate.getForObject("http://localhost:3000/userserver/user/"+id,User.class);
    }
}
//这里我们准备了一个配置类,将RestTemplate交给spring管理
@Configuration
public class BeanConfig {

    @Bean
    public RestTemplate restTemplate(){
        return new RestTemplate();
    }
}

​ ----调用用户服务的接口

​ RestTemplate.getForObject(url,User.class);

​ ----浏览器调用订单服务提供的接口

Eureka集群–高可用

在这里插入图片描述1.hosts文件添加peer1,peer2

127.0.0.1     peer1
127.0.0.1     peer1

2.yml配置文件

spring:
  profiles:
    active: peer1

---
spring:
  profiles: peer1
  
server:
  port: 1000 #端口
eureka:
  instance:
    hostname: peer1 #主机名称
  client:
    registerWithEureka: false  #禁止将自己注册到自己
    fetchRegistry: false  #禁止获取注册清单
    serviceUrl:   #获取上面的hostname 以及 port
      defaultZone: http://peer2:1001/eureka/  #-这里配置的这个主要是客户端要连接到服务端的连接
---
spring:
  profiles: peer2

server:
  port: 1001 #端口
eureka:
  instance:
    hostname: peer2 #主机名称
  client:
    registerWithEureka: false  #禁止将自己注册到自己
    fetchRegistry: false  #禁止获取注册清单
    serviceUrl:   #获取上面的hostname 以及 port
      defaultZone: http://peer1:1000/eureka/  #-这里配置的这个主要是客户端要连接到服务端的连接

其他微服务yml改变

eureka:
  client:
    serviceUrl:
		defaultZone: http://peer1:1000/eureka/,http://peer2:1001/eureka/ #连接服务器端

客户端负载均衡

1.Ribbon

在这里插入图片描述Ribbon通过算法调用端口

​ 1.集群user-server

​ c-v 改东西

user-server-pom.xml

    <artifactId>user-server-3001</artifactId>

    <name>user-server-3001</name>

parent-pm.xml

 <module>user-server-3001</module>

application.yml

eureka:
  client:
    serviceUrl:
      defaultZone: http://peer1:1000/eureka/,http://peer2:1001/eureka/ #连接服务器端
  instance:
    instance-id: user-server:3001 #当前客户端服务的id
    prefer-ip-address: true # 使用ip进行注册
server:
  port: 3001 #端口号  端口号不同

spring:
  application:
    name: user-server  #当前应用名称  应用名称相同

​ 2.ribbon实现

​ 导入依赖

<!--导入ribbon-->
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-netflix-ribbon</artifactId>
</dependency>
	RestTemplate上打@LoadBalanced:让RestTemplate有负载均衡的能力
@Bean
@LoadBalanced
public RestTemplate restTemplate(){
    return new RestTemplate();
}

​ 3.Ribbon底层源码分析

​ 1.@LoadBalanced赋予RestTemplate有负载均衡的能力

​ 2.RestTemplate底层通过RibbonLoadBalancerClient客户端工具类实现

​ 3.RibbonLoadBalancerClient.execute的操作

​ 1.通过服务名获取一个LoadBalancer

​ 2.通过LoadBalancer.chooseServer选中服务(会加载所有端口)

​ 3.在LoadBalancer.chooseServer方法中会调用IRule.choose方法进行服务的选择

​ 4.IRule.choose方法按照某种算法选择服务并返回

​ 5.然后使用Http调用服务

​ 4.配置负载均衡算法

配置Bean

@Bean
    public IRule randomRule(){
        return new RandomRule();
    }

2.Feign

调用更像本地调用

​ 1.导包

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

​ 2.主配置类上打注解@EnableFeignClients–开启

@SpringBootApplication
@EnableFeignClients//开启feign
public class OrderApplicationConfig {

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

}

​ 3.写接口

/**
 * feign的接口,这样写一个接口类
 * 在控制层就像调用自己的东西一样
 * 底层和ribbon类似
 * @FeignClient("user-server")
 * 以及下面的
 * @GetMapping("/userserver/user/{id}")
 * 会进行拼接
 * http://user-server/userserver/user/999
 * 然后还是类似于ribbon
 */
@FeignClient("user-server")
@Component
public interface UserFeignClient {

    //调用根据id获取用户的接口
    @GetMapping("/userserver/user/{id}")
    User getUserById(@PathVariable("id")Long id);
}

接口要注意

  1. @GetMapping("/userserver/user/{id}")里是我们要访问的路径,要写全

  2. User getUserById(@PathVariable(“id”)Long id);返回值类型,参数列表要一致,方法名可以不同

  3. @FeignClient(“user-server”)这里指向的是我们将要调用的服务,调用的是服务名

    spring:
      application:
        name: user-server  #当前应用名称
    

​ 4.控制层调用

	//自动注入失败就去接口添加@Component
	@Autowired
    private UserFeignClient userFeignClient;//这里实际上是代理类在运行不是接口

    //浏览器调用,根据id获取用户 : 有负载均衡
    @GetMapping("/user/{id}")
    public User getUserById(@PathVariable("id")Long id){
        //调用我们写的feign的接口
        return userFeignClient.getUserById(id);
    }

注意服务器先启动被调用的服务,也就是提供者,再启动调用的服务,也就是消费者

Hystrix 断路器

在这里插入图片描述Ribbon

​ 1.导包

<!--Hystrix-->
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
</dependency>

​ 2.注解—主配置类开启注解

@EnableCircuitBreaker

​ 3.Controller回调方法

@RequestMapping("/orderserver")
@RestController
public class OrderServerController {

    @Autowired
    private RestTemplate restTemplate;


    //浏览器调用,根据id获取用户 : 有负载均衡
    @GetMapping("/user/{id}")
    @HystrixCommand(fallbackMethod = "getUserByIdFallBack")//托底方法
    public AjaxResult getUserById(@PathVariable("id")Long id){
        //RestTemplate 调用用户服务的获取user的接口
        String url = "http://user-server/userserver/user/"+id;
        //调用方法获取用户
        User user = restTemplate.getForObject(url, User.class);
        //AjaxResult对象
        AjaxResult ajaxResult = new AjaxResult();
        //设置要传递的参数对象
        ajaxResult.setData(user);
        //返回
        return ajaxResult;
    }
    //这里我们返回AjaxResult,后面都会用这种形式进行规范
    public AjaxResult getUserByIdFallBack(@PathVariable("id")Long id){
        //AjaxResult对象
        AjaxResult ajaxResult = new AjaxResult();
        ajaxResult.setSuccess(false);
        //设置错误信息
        ajaxResult.setMsg("服务器开小差了!");
        return ajaxResult;
    }
}

备注----准备AjaxResult

public class AjaxResult {
    //默认为true
    private boolean success = true;
    private String msg;
    private Object data;

    public AjaxResult() {
    }


    public boolean isSuccess() {
        return success;
    }

    public void setSuccess(boolean success) {
        this.success = success;
    }

    public String getMsg() {
        return msg;
    }

    public void setMsg(String msg) {
        this.msg = msg;
    }

    public Object getData() {
        return data;
    }

    public void setData(Object data) {
        this.data = data;
    }
}

Feign–本身就集成了Hystrix

​ 1.开启Hystrix feign.hystrix.enabled = true

feign:
  hystrix:
    enabled: true

​ 2.方法

@FeignClient( value = "user-server" , fallback = UserFeignClientFallback.class)
//fallback = UserFeignClientFallback.class   引入托底类
@Component
public interface UserFeignClient {

    //调用根据id获取用户的接口
    @GetMapping("/userserver/user/{id}")
    User getUserById(@PathVariable("id")Long id);
}

​ 1.实现本身

@Component//fallback方法类
public class UserFeignClientFallback implements UserFeignClient {

    @Override
    public User getUserById(Long id) {
        System.out.println("1111111111111");
        return null;
    }
}

​ 2.实现工厂—一般不用这种方式 这是官网的案例

@FeignClient(name = "hello", fallbackFactory = HystrixClientFallbackFactory.class)
protected interface HystrixClient {
	@RequestMapping(method = RequestMethod.GET, value = "/hello")
	Hello iFailSometimes();
}

@Component
static class HystrixClientFallbackFactory implements FallbackFactory<HystrixClient> {
	@Override
	public HystrixClient create(Throwable cause) {
		return new HystrixClient() {
			@Override
			public Hello iFailSometimes() {
				return new Hello("fallback; reason was: " + cause.getMessage());
			}
		};
	}
}

网关(Zuul)

在这里插入图片描述1.导包

<dependency>
     <groupId>org.springframework.cloud</groupId>
     <artifactId>spring-cloud-starter-netflix-zuul</artifactId>
</dependency>
<!--客户端-->
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>

2.开启Zuul代理 ----- 主配置类注解

@EnableZuulProxy//开启网关代理

3.配置文件application.yml

server:
  port: 4000
spring:
  application:
    name: zuul

eureka:
  client:
    service-url:
      defaultZone: http://peer1:1000/eureka/,http://peer2:1001/eureka/ #连接服务器端
  instance:
    instance-id: zuul
    prefer-ip-address: true
zuul:
  ignored-services: "*"    #禁止所有服务使用默认的服务名进行访问
  routes:
    order-server: "/order/**"   #order-server服务使用 "/order/**"路径访问
  # prefix: "/dsq"   #前缀前要加  /

执行流程
在这里插入图片描述在这里插入图片描述loginFilter

@Component
public class LoginFilter extends ZuulFilter {
    /**
     * filter类型--前置后置等
     * @return
     */
    @Override
    public String filterType() {
        return PRE_TYPE;//"pre"
    }

    /**
     * 执行顺序
     * @return
     */
    @Override
    public int filterOrder() {
        return 1;
    }

    /**
     * 可以理解为放行资源的方法
     * 返回true是表示要拦截
     * 返回false表示放行
     * @return
     */
    @Override
    public boolean shouldFilter() {
        //获取上下文对象
        RequestContext currentContext = RequestContext.getCurrentContext();
        //获取请求对象
        HttpServletRequest request = currentContext.getRequest();
        //获取uri
        String requestURI = request.getRequestURI();
        //判断请求--后续可以放一个配置文件中然后遍历进行放行
        if(requestURI.toUpperCase().contains("LOGIN")){
            return false;
        }
        return true;//执行下面的run拦截
    }

    /**
     * 拦截器的主要逻辑代码
     * @return
     * @throws ZuulException
     */
    @Override
    public Object run() throws ZuulException {
        //获取上下文对象
        RequestContext currentContext = RequestContext.getCurrentContext();
        //获取请求对象
        HttpServletRequest request = currentContext.getRequest();
        //获取响应对象
        HttpServletResponse response = currentContext.getResponse();
        //设置响应的数据的格式
        response.setContentType("application/json;charset=utf-8");
        //获取请求头信息
        String token = request.getHeader("x-token");
        //为空表示没有登陆
        if (StringUtils.isBlank(token)){
            //准备map
            Map<String,Object> map =new HashMap<>();
            //放信息进去
            map.put("success",false);
            map.put("msg","没有登陆");
            //fastjson转为json字符串
            String s = JSON.toJSONString(map);
            PrintWriter writer = null;
            try {
                //获取writer对象
                writer = response.getWriter();
                //将前面的json字符串打印出去
                writer.print(s);
            } catch (IOException e) {
                e.printStackTrace();
            }
            //阻止放行
            currentContext.setSendZuulResponse(false);
        }
        return null;
    }
}

SpringCloud Config

概念
在这里插入图片描述架构
在这里插入图片描述1.码云-新建配置文件application-zuul-dev.yml

2.新建模块config-server-5000

​ 1.注册到Eureka注册中心

​ 2.集成Config

​ 1.导包

<!--config-->
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-config-server</artifactId>
</dependency>

​ 2.配置类打标签@EnableConfigServer

@EnableConfigServer

​ 3.yml

server:
  port: 5000
spring:
  application:
    name: config-server
  cloud:
    config:
      server:
        git:
          uri: https://gitee.com/dsqzzz/pro4.git  #码云仓库
          username: 2219726571@qq.com #用户名
          password: zzz12345 #密码
eureka:
  client:
    service-url:
      defaultZone: http://peer1:1000/eureka/,http://peer2:1001/eureka/ #连接服务器端
  instance:
    instance-id: config-server:5000
    prefer-ip-address: true #使用ip注册

客户端

1.导包

 <!--config-client-->
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-config-client</artifactId>
</dependency>

2.yml文件改名 —bootstrap.yml 优先级提高

#执行配置中心
spring:
  cloud:
    config:
      uri: http://localhost:5000   #配置中心地址
      name: application-order
      profile: dev

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值