springcloud高可用注册中心搭建以及feign的使用

  • 相关介绍
  • 微服务
  • 解答上面的问题
  • eureka集群搭建
  • eureka
  • provide
  • consumer


相关介绍

最近由于笔者比较闲,同时重新翻看了一下《深入理解SpringCloud与微服务架构》一书,想就我所接触过的springcloud项目具体应用搭建做一个系统性的整合。前期我们会谈到eureka集群和服务消费而后会整理出熔断器(hystrix)、zuul(网关)、Turbine和HystrixDashboard(断路监控)、springcloudconfig(配置中心)、springcloud stream(消息驱动)、sleuth和zipkin+zuul(服务链路跟踪)等技术,笔者会以个人的理解从浅入深的阐述springcloud相关应用的知识和分布式中遇到的各种坑以及解决方法,还望轻喷。

微服务

相关的字面描述我不做过多的介绍网上很多,主要需要思考一下几个方面:

  1. 传统应用和微服务有什么区别?
  2. 微服务的相关概念
  3. 微服务的落地实现有哪些方法?
  4. 想要使用微服务需要具备怎样的技术基础?

解答上面的问题

1.传统应用可以理解是一个war包打天下,这样的应用可拓展性和维护性很差。但是也不能说它一无是处,它的优点就是开发相对简单同时开发的成本很低非常适合一个急于上线的项目。微服务各个服务单独的独立出来可以部署在不同的服务器或者容器上,可拓展性和维护性较传统应用高很多同时也不是一个量级的

2.简而言之,微服务架构是一种将单应用程序作为一套小型服务开发的方法,每种应用程序都在其自己的进程中运行,并与轻量级机制(通常是HTTP资源的API)进行通信。这些服务是围绕业务功能构建的,可以通过全自动部署机制进行独立部署。这些服务的集中化管理已经是最少的,它们可以用不同的编程语言编写,并使用不同的数据存储技术。

不在同一个注册中心的微服务Feign调用_微服务


3. dubbo+zookeeper和springcloud不过最近很多新项目都会以springcloud框架来开发微服务应用,springcloud大有一统江山的趋势,毕竟spring大法好;

4. 需要掌握的技术有:spring4.x、springmvc、springboot 、springsecurity、mybatis、rabbitmq等消息中间件、linux相关技术;

eureka集群搭建

不在同一个注册中心的微服务Feign调用_spring_02


(感谢qyj19920704老哥的图)

从图中我们可以看出这里一共有3个eureka注册中心更准确的来说是3个服务注册的服务端,同时有3个服务提供者他们会将服务地址注册到eureka中,并且这些eureka是集群状态只要还存在一个服务注册服务那么服务注册的功能将永远不会失效这就是eureka的高可用简称是AP原则。下面我会给出具体的步骤:

eureka

我这里采用的maven父子工程的模式创建的项目结构,后面我会传到我的github上
github项目地址:https://github.com/zhoudunpeng/springcloud_eureka_feign/tree/master/allencloud 项目下下来后记得将自己本地的hosts虚拟域名配置一下
C:\Windows\System32\Drivers\etc
在hosts文件下面添加这些内容:
127.0.0.1 eureka1
127.0.0.1 eureka2
127.0.0.1 eureka3

父工程的pom:

<dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>enjoy</groupId>
                <artifactId>microcloud-security</artifactId>
                <version>1.0.0-SNAPSHOT</version>
            </dependency>
            <dependency> <!-- 进行SpringCloud依赖包的导入处理 -->
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-dependencies</artifactId>
                <version>Finchley.RELEASE</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
            <dependency> <!-- SpringCloud离不开SpringBoot,所以必须要配置此依赖包 -->
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-dependencies</artifactId>
                <version>2.1.2.RELEASE</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
            //cloudapi是一些需要共用的entity类具体,你们需要自行写入
            <dependency>
                <groupId>cn.zdp</groupId>
                <artifactId>cloudapi</artifactId>
                <version>1.0.0</version>
            </dependency>
            //同上,不过这里是我独立出来的接口类
            <dependency>
                <groupId>cn.zdp</groupId>
                <artifactId>security</artifactId>
                <version>1.0.0</version>
            </dependency>
            <dependency>
                <groupId>cn.zdp</groupId>
                <artifactId>serviceapi</artifactId>
                <version>1.0.0</version>
            </dependency>
            <dependency>
                <groupId>com.alibaba</groupId>
                <artifactId>druid</artifactId>
                <version>1.0.31</version>
            </dependency>
            <dependency>
                <groupId>org.mybatis.spring.boot</groupId>
                <artifactId>mybatis-spring-boot-starter</artifactId>
                <version>1.3.0</version>
            </dependency>
        </dependencies>
    </dependencyManagement>

eureka的pom依赖:

// An highlighted block
<dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</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-devtools</artifactId>
        </dependency>
        <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-security</artifactId>
        </dependency>

    </dependencies>

application.yml配置:

server:
  port: 7001
eureka:
  instance:
    hostname: eureka1  #定义eureka实例所在的主机名称
  client:
    fetch-registry: false     #是否要从注册中心获取信息
    register-with-eureka: false  #是否要注册到eureka注册中心上
    service-url:
      defaultZone: http://admin:enjoy@eureka1:7001/eureka,http://admin:enjoy@eureka2:7002/eureka,http://admin:enjoy@eureka3:7003/eureka
  server:
    eviction-interval-timer-in-ms: 1000   #设置清理的间隔时间,而后这个时间使用的是毫秒单位(默认是60秒)
    enable-self-preservation: false #设置为false表示关闭保护模式

spring:
  security:
    user:
      name: admin
      password: enjoy

这里我采用的springsecurity做的一个访问控制,因为实际生产环境下各个服务之间需要有一定安全验证机制,不能说任何人都可以通过地址访问我具体的服务。
springsecurity相关的知识这里不做介绍,请大家自行查看。

另外提一点,eureka的自我保护机制,当注册到eureka中的服务因为某种原因宕掉后。在一定时间内eureka还是会保存这个服务的实例的。因为这里涉及到了服务提供方会往注册中心发送心跳的机制,心跳就是用来告诉注册中心我这服务还存在,如果一段时间注册中心接收不到服务提供方的心跳那么就会认为它是下线的状态,到达指定时间就会将提供服务的实例从注册表中删除,这里一定要关闭eureka的自我保护模式。

启动类:

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer;

@SpringBootApplication
@EnableEurekaServer
public class EurekaApp7001 {
    public static void main(String[] args) {
        SpringApplication.run(EurekaApp7001.class,args);
    }
}

provide

服务提供方pom:

<dependencies>
        <dependency>
            <groupId>cn.zdp</groupId>
            <artifactId>cloudapi</artifactId>
        </dependency>
        <dependency>
            <groupId>cn.zdp</groupId>
            <artifactId>security</artifactId>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
        </dependency>
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid</artifactId>
        </dependency>
        <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</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-devtools</artifactId>
        </dependency>
        <!--<dependency>-->
        <!--<groupId>org.springframework.boot</groupId>-->
        <!--<artifactId>spring-boot-starter-security</artifactId>-->
        <!--</dependency>-->
        <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-actuator</artifactId>
        </dependency>
    </dependencies>

我将安全组件单独起了一个项目以依赖的方式添加进去的,看你们自己的个人喜好这里我推荐采用我这种方法,让自己的项目模块看起来更清晰。

服务提供方application.yml:

server:
 port: 8080
mybatis:
 mapper-locations: # 所有的mapper映射文件
 - classpath:mapping/*.xml
spring:
 application:
   name: microcloud-provider-product
 datasource:
   type: com.alibaba.druid.pool.DruidDataSource # 配置当前要使用的数据源的操作类型
   driver-class-name: com.mysql.jdbc.Driver # 配置MySQL的驱动程序类
   url: jdbc:mysql://localhost:3306/springcloud?serverTimezone=GMT%2B8 # 数据库连接地址
   username: root # 数据库用户名
   password: root # 数据库连接密码
 #security:
 #  user:
 #    name: admin  #用户名
 #    password: enjoy #密码
 #    roles: USER  #授权角色
logging:
  level:
    cn.enjoy.mapper: debug

eureka:  #将服务注册进eure
  client:
    service-url:
      #defaultZone: http://admin:enjoy@eureka1:7001/eureka
      defaultZone: http://admin:enjoy@eureka1:7001/eureka,http://admin:enjoy@eureka2:7002/eureka,http://admin:enjoy@eureka3:7003/eureka
  instance:
    instance-id: microcloud-provider-product
    prefer-ip-address: true
    lease-renewal-interval-in-seconds: 2 # 设置心跳的时间间隔(默认是30秒)
    lease-expiration-duration-in-seconds: 5  # 超过了5秒的间隔下线(默认是90秒)

info:
  app.name: microcloud-provider-product  #服务名
  company.name: enjoy         #公司名
  build.artifactId: $project.artifactId$
  build.modelVersion: $project.modelVersion$

这里采用的springboot整合mybatis的项目,在我本地用了3个数据库为了测试负载。具体的请参考我的github。

服务提供方启动类:

@SpringBootApplication
@MapperScan("cn.enjoy.mapper")
@EnableEurekaClient
@EnableDiscoveryClient
public class ProductApp{
    public static void main(String[] args) {
        SpringApplication.run(ProductApp.class,args);
    }
}

记住一点服务方一定要将自己的服务注册到eureka中,不然服务将无法被消费方调用。springcloud的调用方式是基于http的采用rest的方式进行服务访问的。如果将服务注册到eureka中即可采用serverId的形式进行调用。

consumer

service接口模块:

import cn.zdp.vo.Product;
import feign.Feign;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;

import java.util.List;

@FeignClient(name = "MICROCLOUD-PROVIDER-PRODUCT",configuration = FeignConfig.class)
public interface IProductClientService {

    @RequestMapping("/prodcut/get/{id}")
    public Product getProduct(@PathVariable("id")long id);

    @RequestMapping("/prodcut/list")
    public List<Product> listProduct() ;

    @RequestMapping("/prodcut/add")
    public boolean addPorduct(Product product) ;
}

这里我直接是使用的是feign的方式来访问服务,为什么我不说ribbon呢,因为feign在这一块做的比ribbon要好太多,你可以这么认为feign是集ribbon,httphead,eurekaclient,hystrix与一身的框架。它的底层负载还是采用ribbon做的实现默认为轮询方式,同时feign更注重接口的方式进行开发,你只要保证接口层的方法与服务提供者的controller方法相同即可,feign的service接口采用的也是mvc的编写模式。如果有人问你为什么你要用feign你可以这么告诉他,你是想写实现还是想调接口?是个正常点的程序员都会选择调接口吧,哈哈。

feignconfig设置请求头登录安全组件:

@Configuration
public class FeignConfig {

    @Bean
    public BasicAuthRequestInterceptor getBasicAuthRequestInterceptor() {
        return new BasicAuthRequestInterceptor("admin", "enjoy");
    }
}

这一步也很重要哦,不然就无法访问服务提供方,因为服务提供方也引入的安全组件进行拦截。

消费方的主启动类:

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
import org.springframework.cloud.openfeign.EnableFeignClients;

@SpringBootApplication
@EnableEurekaClient
@EnableFeignClients("cn.zdp.service")
public class FeignConsumerApp_80 {

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

前面我已经提到了我将service模块单独独立出来,采用依赖的方式添加consumer的pom中,这里在启动是就会加载feign相关的配置信息到spring的容器中去你只需要设置好包的位置即可。

到这里eureka集群和feign的使用已整理完成,具体的项目结构请去我的github浏览。下一期我会整合一下zuul和hystrix相关的Turbine- hystrixDashborad对多个服务进行监控。