springcloud搭建实操

springcloud搭建

Springcloud介绍

  1. Spring Cloud和Dubbo的区别
    相同之处,它们两都具备分布式服务治理相关的功能,都能够提供服务注册、发现、路由、负载均衡等。Dubbo的功能好像也就这么多了,但是Spring Cloud是提供了一整套企业级分布式云应用的完美解决方案,能够结合Spring Boot,Docker实现快速开发的目的,所以说Dubbo只有Spring Cloud的一部分RPC功能,而且也谈不上谁好谁坏。不过,原班人马对Dubbo项目已经停止维护了,淘宝内部由hsf替代dubbo,我想这会有跟多企业倾向Spring Cloud了。
  2. Spring Cloud包含了很多组件,常用的这几个组件,比如:Eureka(注册中心)、Ribbon/Feign(消费者)、Hystrix(熔断器)、Config(统一配置中心)、Zuul(路由)、Bus(消息总线)、Gateway(网关)等 。
  3. Spring Cloud是一个相对比较新的微服务框架,它集成了众多开源的框架,利用Spring Boot的开发便利性实现了服务治理、服务注册与发现、负载均衡、数据监控,REST API发布方式等,基本囊括了分布式框架所需要的所有功能。是一套易开放、易部署、易维护的分布式开发工具包,要使用springCloud框架呢我们首先需要创建一个服务注册中心Eureka,创建Eureka呢首先需要搭建一个springCloud微服务,然后再启动类上面加上一个注解@EnableEurekaServer,然后只需要再application.yml配置文件中配置Eureka的连接信息,这样Eureka就配置好了,通常情况下为了方便,我们一般将这个Eureka服务呢是打成war包,然后配置bat启动文件,每次只需要直接使用bat文件启动就可以了,这样注册中心就已经完成了。
    然后呢我们需要配置一个服务提供者用来提供服务,服务提供者呢也是一个单独的微服务,需要在注册中心进行注册,也就是在application.yml里面配置注册中心Eureka的连接信息,然后在启动类上面加上@EnableEurekaClient注解,表明是一个eurekaclient。这样服务中心也就配置好了。
    紧接着我们需要创建一个服务消费者用来调用服务提供者提供的方法,springCloud中有两种服务调用方式,一种是ribbon,另一种是feign。ribbon是一个负载均衡客户端,可以很好的控制htt和tcp的一些行为,而Feign默认集成了ribbon,所以我们一般使用的是Feign作为服务消费者,feign需要在它的pom文件引入Feign的起步依赖spring-cloud-starter-feign、Eureka的起步依赖spring-cloud-starter-eureka、Web的起步依赖spring-boot-starter-web,需要在启动类上面加上@EnableFeignClients这个注解,开启Feign的功能,然后在application.yml配置文件中配置注册中心Eureka的地址,这样服务消费者就配置好了,然后我们需要在service的接口中调用服务提供者提供的服务,只需要在接口上面加上@FeignClient(value = “要调用的服务名”)指明要调用的服务名称,然后再方法上面使用@RequestMapping注解,value值要和那个服务提供者里面的value值保持一致,并且如果方法有参数的话需要使用@RequestParam这个注解,这样服务消费者就可以调用服务提供者的服务了。
    为了保证其高可用,单个服务通常会集群部署。由于网络原因或者自身的原因,服务并不能保证100%可用,如果单个服务出现问题,调用这个服务就会出现线程阻塞,此时若有大量的请求涌入,Servlet容器的线程资源会被消耗完毕,导致服务瘫痪。服务与服务之间的依赖性,故障会传播,会对整个微服务系统造成灾难性的严重后果,这就是服务故障的“雪崩”效应,为了解决这个问题,springCloud提供了断路器,其实就是在服务之间调用的时候发生异常的一种解决方案,当异常产生的时候呢就不再调用服务提供者的方法了,而是执行我们配置好的回调方法,一般我们是用这个方法返回一也异常的提示信息。Feign是自带断路器的,在D版本的Spring Cloud中,它没有默认打开。需要在配置文件中配置打开它,在配置文件添加feign.hystrix.enabled=true打开,然后我们需要修改service接口上的@FeignClient(value = “要调用的服务名”)后面加上fallback = 回调函数的类名.class,然后创建一个回调函数的类,并且实现这个接口,重写当前方法,方法内容一般为返回的提示信息,这样断路器也就配置好了。
    路由网关Zuul的主要功能是路由转发和过滤器,我们需要创建一个新的微服务,在pom文件里面添加spring-cloud-starter-zuul,之后再启动类上面加上@EnableZuulProxy,开启zuul的功能,然后需要在application.yml配置文件中配置Eureka注册中心和Zull的配置信息。zuul不仅只是路由,并且还能过滤,做一些安全验证。为了方便服务配置文件统一管理,实时更新,所以需要分布式配置中心组件。在Spring Cloud中,有分布式配置中心组件spring cloud config ,它支持配置服务放在配置服务的内存中(即本地),也支持放在远程Git仓库中。在spring cloud config 组件中,分两个角色,一是config server,二是config client。Server提供配置文件的存储、以接口的形式将配置文件的内容提供出去,Client通过接口获取数据、并依据此数据初始化自己的应用。
    其实就是Server端将所有的配置文件服务化,需要配置文件的服务实例去Config Server获取对应的数据。将所有的配置文件统一整理,避免了配置文件碎片化。
    如果服务运行期间改变配置文件,服务是不会得到最新的配置信息,需要解决这个问题就需要引入Refresh。它可以在服务的运行期间重新加载配置文件。
    当所有的配置文件都存储在配置中心的时候,配置中心就成为了一个非常重要的组件。
    如果配置中心出现问题将会导致灾难性的后果,因此在生产中建议对配置中心做集群,来支持配置中心高可用性。
    这时候 Spring Cloud 提出了另外一个解决方案:Spring Cloud Bus。
    Spring Cloud Bus 通过轻量消息代理连接各个分布的节点。这会用在广播状态的变化(例如配置变化)或者其它的消息指令中。
    Spring Cloud Bus 的一个核心思想是通过分布式的启动器对 Spring Boot 应用进行扩展,也可以用来建立一个或多个应用之间的通信频道。目前唯一实现的方式是用 AMQP 消息代理作为通道。
    Spring Cloud Bus 是轻量级的通讯组件。有了 Spring Cloud Bus 之后,当我们改变配置文件提交到版本库中时,会自动的触发对应实例的Refresh,实现热加载。它需要跟我们这个rabbitmq进行连用 大概就是 git仓库改变时,通过POST请求Config Server的/bus/refresh,Config Server 会从repository获取最新的信息并通过amqp传递给client .

springcloud搭建步骤
1、搭建springcloud注册中心eureka
在父级模块中创建一个springboot项目子模块eureka
在springboot基础jar包之上 导入eureka服务端 jar包

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

配置application.yml配置文件

server:
  port: 8080
eureka:
  instance:
    hostname: localhost
  client:
    register-with-eureka: false
    fetch-registry: false
    serviceUrl:
      defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/

启动类上加

@EnableEurekaServer

2、搭建springcloud生成者provider
在父级模块中创建一个springboot项目子模块provider
在springboot基础jar包之上 导入eureka客户端 jar包

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

配置application.yml配置文件

server:
  port: 8082
eureka:
  client:
    serviceUrl:
      #eureka.client.serviceUrl.defaultZone:此属性为注册中心地址
      defaultZone: http://localhost:8080/eureka/
spring:
  application:
    #spring.application.name:当前服务提供者在注册中心中的应用名,不同应用需要使用不同的名称。
    name: springcloud-provider

启动类上加

@EnableEurekaClient

正常编写Controller+service+dao,Controller基于restful开发风格。

@RestController
@RequestMapping("order")
public class OrderController {

    @Value("${server.port}")
    private String port;

    @GetMapping("findOrderById/{orderId}")
    public Map findOrderById(@PathVariable("orderId") Integer orderId){
        HashMap result = new HashMap();
        result.put("port",port);
        result.put("msg","接收到orderId="+orderId);
        return result;
    }

    @PostMapping("addOrder")
    public Map addOrder(@RequestBody OrderBo orderBo){
        HashMap result = new HashMap();
        result.put("port",port);
        result.put("msg","接收到OrderBo="+orderBo);
        return result;
    }

}

3、搭建springcloud消费者ribbon
在父级模块中创建一个springboot项目子模块ribbon
在springboot基础jar包之上 导入eureka客户端 jar包和ribbon jar包

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-netflix-ribbon</artifactId>
</dependency>

配置application.yml配置文件

server:
  port: 8079
eureka:
  client:
    serviceUrl:
      #eureka.client.serviceUrl.defaultZone:此属性为注册中心地址
      defaultZone: http://localhost:8080/eureka/
spring:
  application:
    #spring.application.name:当前服务提供者在注册中心中的应用名,不同应用需要使用不同的名称。
    name: springcloud-ribbon

启动类上加

@EnableEurekaClient
@EnableDiscoveryClient

启动类中注入

@Bean//<bean id="restTemplate" class="org.springframework.web.client.RestTemplate"></bean>
    @LoadBalanced
//注入一个restTemplate 模板调用类 通过restTemplate调用springcloud接口
        //默认负载策略为轮询
    RestTemplate restTemplate() {
        return new RestTemplate();
    }

编写service类或者controller类,通过restTemplate请求生产者提供的服务。

@RestController
@RequestMapping("order")
public class OrderController {

    @Autowired
    @Qualifier("restTemplate")//把Autowired注解强制改为byName格式查找
    private RestTemplate restTemplate;

    @PostMapping("addOrder")
    public Map addOrder(){
        OrderBo orderBo = new OrderBo();
        orderBo.setOrderId(1);
        orderBo.setOrderName("王琦");
        orderBo.setOrderDesc("王琦真白");
        //第一个参数是访问接口的路径 第二个参数为接口需要传递的参数 第三个参数为返回值类型
        Map map = restTemplate.postForObject("http://springcloud-provider/order/addOrder", orderBo, Map.class);
        return map;
    }

    @GetMapping("findOrderById/{orderId}")
    public Map findOrderById(@PathVariable("orderId") Integer orderId){
        Map map = restTemplate.getForObject("http://springcloud-provider/order/findOrderById/"+orderId, Map.class);
        return map;
    }
}

4、搭建springcloud消费者feign
企业最常用的消费者,与dubbo用法类似,符合分布式开发思想(基于接口开发),feign是对ribbon的封装,使分布式开发更加便捷
在父级模块中创建一个springboot项目子模块feign
在springboot基础jar包之上 导入eureka客户端 jar包和feign jar包

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>

配置application.yml配置文件

server:
  port: 8081
eureka:
  client:
    serviceUrl:
      #eureka.client.serviceUrl.defaultZone:此属性为注册中心地址
      defaultZone: http://localhost:8080/eureka/
spring:
  application:
    #spring.application.name:当前服务提供者在注册中心中的应用名,不同应用需要使用不同的名称。
    name: springcloud-feign

启动类上加

@EnableEurekaClient
@EnableDiscoveryClient
@EnableFeignClients

编写feign消费者接口

@FeignClient("springcloud-provider")
public interface OrderService {

    @PostMapping("order/addOrder")
    Map addOrder(OrderBo orderBo);

    @GetMapping("order/findOrderById/{orderId}")
    Map findOrderById(@PathVariable("orderId") Integer orderId);
}

4、分模块
1、搭建springcloudAPI
在父级模块中创建一个springboot项目子模块api
导入springboot基础jar包
创建service接口
示例:

@RequestMapping("springcloud-provider")
public interface OrderService {

    @PostMapping("order/addOrder")
    Map addOrder(OrderBo orderBo);

    @GetMapping("order/findOrderById/{orderId}")
    Map findOrderById(@PathVariable("orderId") Integer orderId);

}

2、将api项目作为jar包导入frign和provider
示例:

<dependency>
    <groupId>cn.jk</groupId>
    <artifactId>springcloud-api</artifactId>
    <version>0.0.1-SNAPSHOT</version>
</dependency>

3、FeignClient接口继承api中service接口
示例:

@FeignClient("springcloud-provider")
public interface OrderFeignClient extends OrderService{
}

4、provider项目controller类实现service接口
示例:

@RestController
public class OrderController implements OrderService {

    @Value("${server.port}")
    private String port;

    public Map findOrderById(@PathVariable("orderId") Integer orderId){
        HashMap result = new HashMap();
        result.put("port",port);
        result.put("msg","接收到orderId="+orderId);
        return result;
    }

    public Map addOrder(@RequestBody OrderBo orderBo){
        HashMap result = new HashMap();
        result.put("port",port);
        result.put("msg","接收到OrderBo="+orderBo);
        return result;
    }

}

5、再分common项目,和提取公共jar包
5、开启feign熔断器
配置application.yml配置文件

#开启熔断器 使服务自动降级
feign:
  hystrix:
    enabled: true

创建FeignClientError类实现FeignClient接口
示例:

@Component
@RequestMapping("error")
public class OrderFeignClientError implements OrderFeignClient{
    @Override
    public Map addOrder(OrderBo orderBo) {
        HashMap map = new HashMap();
        map.put("msg","网络异常,请重试");
        return map;
    }

    @Override
    public Map findOrderById(Integer orderId) {
        HashMap map = new HashMap();
        map.put("msg","网络异常,请重试");
        return map;
    }
}

开启FeignClient接口熔断器设置

@FeignClient(value = "springcloud-provider",fallback = OrderFeignClientError.class)
public interface OrderFeignClient extends OrderService{
}

6、开启日志记录
配置application.yml配置文件

#扫描日志记录的xml
logging:
  config: classpath:logback-spring.xml

resources下拉入logback-spring.xml

<?xml version="1.0" encoding="UTF-8"?>
<configuration debug="false">
    <!--设置存储路径变量-->
    <property name="LOG_HOME" value="./log"/>

    <!--控制台输出appender-->
    <appender name="console" class="ch.qos.logback.core.ConsoleAppender">
        <!--设置输出格式-->
        <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
            <!--格式化输出:%d表示日期,%thread表示线程名,%-5level:级别从左显示5个字符宽度%msg:日志消息,%n是换行符-->
            <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n</pattern>
            <!--设置编码-->
            <charset>UTF-8</charset>
        </encoder>
    </appender>

    <!--文件输出,时间窗口滚动-->
    <appender name="timeFileOutput" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <!--日志名,指定最新的文件名,其他文件名使用FileNamePattern -->
        <File>${LOG_HOME}/timeFile/out.log</File>
        <!--文件滚动模式-->
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <!--日志文件输出的文件名,可设置文件类型为gz,开启文件压缩-->
            <FileNamePattern>${LOG_HOME}/timeFile/info.%d{yyyy-MM-dd}.%i.log.gz</FileNamePattern>
            <!--日志文件保留天数-->
            <MaxHistory>30</MaxHistory>
            <!--按大小分割同一天的-->
            <timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
                <maxFileSize>10MB</maxFileSize>
            </timeBasedFileNamingAndTriggeringPolicy>
        </rollingPolicy>

        <!--输出格式-->
        <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
            <!--格式化输出:%d表示日期,%thread表示线程名,%-5level:级别从左显示5个字符宽度%msg:日志消息,%n是换行符-->
            <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n</pattern>
            <!--设置编码-->
            <charset>UTF-8</charset>
        </encoder>

    </appender>

    <!--指定基础的日志输出级别-->
    <root level="INFO">
        <!--appender将会添加到这个loger-->
        <appender-ref ref="console"/>
        <appender-ref ref="timeFileOutput"/>
    </root>
</configuration>

controller类中定义

private static Logger logger = LoggerFactory.getLogger(OrderController.class);

try catch中可以

logger.info("新增成功");
logger.error("新增失败");

7、搭建网关路由
在父级模块中创建一个springboot项目子模块zuul
在springboot基础jar包之上 导入eureka客户端 jar包和zuul jar包

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

配置application.yml配置文件

server:
  port: 8888
spring:
  application:
    name: springcloud-zuul
eureka:
  client:
    serviceUrl:
      defaultZone: http://localhost:8080/eureka/
zuul:
  routes:
    service-a:
      path: /server-a/*
      #有几个/*非常重要
      serviceId: springcloud-feign
    service-b:
      path: /server-b/*
      serviceId: springcloud-ribbon

启动类上加

@EnableEurekaClient
@EnableZuulProxy//开启路由代理

网关路由配置完毕
使用示例:

http://127.0.0.1:8888/server-a/findShopPageList

可开启网关路由过滤器
创建zuul包结构
创建MyZuul类

import com.netflix.zuul.ZuulFilter;
import com.netflix.zuul.context.RequestContext;
import com.netflix.zuul.exception.ZuulException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;

import javax.servlet.http.HttpServletRequest;

@Component
public class MyZuul extends ZuulFilter {

    private static Logger logger = LoggerFactory.getLogger(ZuulFilter.class);

    /**
     * filterType 路由类型
     * pre:路由之前
     * routing:路由之时
     * post: 路由之后
     * error:发生错误调用
     * @return
     */
    @Override
    public String filterType() {
        return "pre";
    }

    @Override
    public int filterOrder() {
        return 0;
    }

    /**
     * 开启网关路由过滤
     * @return  true 开启 false 关闭
     */
    @Override
    public boolean shouldFilter() {
        return false;
    }

    @Override
    public Object run() throws ZuulException {
        //获取当前项目上下文环境
        RequestContext ctx = RequestContext.getCurrentContext();
        //获取到http request对象
        HttpServletRequest request = ctx.getRequest();
        //token 身份验证 用户的基本信息 用户名密码等
        String token = request.getParameter("token");
        if(StringUtils.isEmpty(token)){
            logger.warn("token 值为空");
            //是否继续请求跳转 false代表停止转发
            ctx.setSendZuulResponse(false);
            //http 401 无访问权限
            ctx.setResponseStatusCode(401);
        }
        logger.info("ok");
        return null;
    }
}

8、搭建config配置中心
在父级模块中创建一个springboot项目子模块config
在springboot基础jar包之上 导入eureka客户端 jar包和config服务端jar包

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

在父级项目根目录下创建存放配置文件的文件夹 结构如下:
config-manager(配置文件根目录)
springcloud-feign(模块配置文件文件夹)
springcloud-feign-dev.yml(配置文件:开发环境下)
springcloud-feign-prod.yml(配置文件:上线环境下)
springcloud-feign-test.yml(配置文件:测试环境下)
将配置文件上传至git
配置application.yml配置文件

server:
  port: 8761
spring:
  application:
    name: server-config
  cloud:
    config:
      server:
        #连接git上已上传的配置文件目录
        git:
          uri: https://gitee.com/bright_moon_skyline/springcloud.git
          username: 17737275442
          password: wq980818
          search-paths: config-manager/*
eureka:
  client:
    serviceUrl:
      defaultZone: http://localhost:8080/eureka/

启动类上加

@EnableEurekaClient
@EnableConfigServer//开启统一配置中心服务

//配置中心完毕 可以在浏览器测试配置中心能否读取git上配置文件的信息
在feign项目中加入config客户端的依赖jar包

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

在feign模块将application.yml的项目配置文件修改为bootstrap.yml的连接配置中心的配置文件

spring:
  application:
    name: springcloud-feign
  cloud:
    config:
      label: master
      discovery:
        service-id: server-config
        enabled: true
      profile: dev

eureka:
  client:
    serviceUrl:
      defaultZone: http://localhost:8080/eureka/

springcloud基本搭建完成

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值