基于SpringBoot 2.0正式版的SpringCloud的微服务实战项目搭建

Spring Cloud简介

        Spring Cloud是一个基于Spring Boot实现的云应用开发工具,它为基于JVM的云应用开发中的配置管理、服务发现、断路器、智能路由、微代理、控制总线、全局锁、决策竞选、分布式会话和集群状态管理等操作提供了一种简单的开发方式。

        Spring Cloud包含了多个子项目(针对分布式系统中涉及的多个不同开源产品),比如:Spring Cloud Config、Spring Cloud Netflix、Spring Cloud CloudFoundry、Spring Cloud AWS、Spring Cloud Security、Spring Cloud Commons、Spring Cloud Zookeeper、Spring Cloud CLI等项目。

        本文将介绍基于springBoot2.0正式版的springCloud的微服务搭建以及需要注意的细节.

 

1.服务注册与发现

        在简单介绍了Spring Cloud和微服务架构之后,下面回归本文的主旨内容,如何使用Spring Cloud搭建服务注册与发现模块。

        这里我们会用到Spring Cloud Netflix,该项目是Spring Cloud的子项目之一,主要内容是对Netflix公司一系列开源产品的包装,它为Spring Boot应用提供了自配置的Netflix OSS整合。通过一些简单的注解,开发者就可以快速的在应用中配置一下常用模块并构建庞大的分布式系统。它主要提供的模块包括:服务发现(Eureka),断路器(Hystrix),智能路有(Zuul),客户端负载均衡(Ribbon)等。

        所以,我们这里的核心内容就是服务发现模块:Eureka。下面我们动手来做一些尝试。

创建“服务注册中心”

 1.1 创建springboot项目

         http://start.spring.io/ 自定义spring boot在线maven构建工具很方便(默认2.0版本)
 

1.2 修改pom文件,添加spring cloud 依赖如下

    <dependencies><!-- springCloud 配置 --><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-config</artifactId></dependency>    <!-- springCloud注测中心服务 --><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>

 

红色标记部分和2.0之前的版本有区别一定要注意:

2.0之前的版本为:

<dependencies>  <dependency>  <groupId>org.springframework.boot</groupId>  <artifactId>spring-boot-starter-test</artifactId>  <scope>test</scope>  </dependency>  <dependency>  <groupId>org.springframework.cloud</groupId>  <artifactId>spring-cloud-starter-eureka-server</artifactId>  </dependency>  
</dependencies>  <dependencyManagement>  <dependencies>  <dependency>  <groupId>org.springframework.cloud</groupId>  <artifactId>spring-cloud-dependencies</artifactId>  <version>Dalston.RELEASE</version><type>pom</type>  <scope>import</scope>  </dependency>  </dependencies>  
</dependencyManagement> 

 

1.3 在启动类上加上注解 如下

        通过@EnableEurekaServer注解启动一个服务注册中心提供给其他应用进行对话。这一步非常的简单,只需要在一个普通的Spring Boot应用中添加这个注解就能开启此功能,比如下面的例子:

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

 

1.4 配置文件application.properties

        在默认设置下,该服务注册中心也会将自己作为客户端来尝试注册它自己,所以我们需要禁用它的客户端注册行为,只需要在application.properties中问增加如下配置:

#注册中心服务ID
spring.application.name=compute-server#端口号
server.port=1111
# eureka.client.registerWithEureka :表示是否将自己注册到Eureka Server,默认为true。
# 由于当前这个应用就是Eureka Server,故而设为false  
eureka.client.register-with-eureka=false
# eureka.client.fetchRegistry :表示是否从Eureka Server获取注册信息,默认为true。因为这是一个单点的Eureka Server,
# 不需要同步其他的Eureka Server节点的数据,故而设为false。  
eureka.client.fetch-registry=false
# eureka.client.serviceUrl.defaultZone :设置与Eureka Server交互的地址,查询服务和注册服务都需要依赖这个地址。默认是
eureka.client.serviceUrl.defaultZone=http://localhost:1111/eureka/

 

1.5配置文件application.properties

 

        为了与后续要进行注册的服务区分,这里将服务注册中心的端口通过server.port属性设置为1111

启动工程后,访问:http://localhost:1111/

可以看到下面的页面,其中还没有发现任何服务:

 

2.搭建服务端

    2.1 创建springboot项目同上

    2.2 修改pom.xml文件,添加spring cloud 依赖如下(红色标记一定要添加)

        <dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter</artifactId></dependency><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-config</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-test</artifactId><scope>test</scope></dependency><dependency>    <groupId>org.springframework.boot</groupId>    <artifactId>spring-boot-starter-web</artifactId></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>

 

2.3 在启动类上加上注解 如下

        最后在主类中通过加上@EnableEurekaClient 注解,该注解能激活Eureka中的对注册中心注册服务实现,才能实现Controller中对服务信息的输出。

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

 

2.4 配置文件application.properties

#服务名称  
spring.application.name=compute-service1  
#端口号  
server.port=2222  
#在注册中心中进行注册
eureka.client.serviceUrl.defaultZone=http://localhost:1111/eureka/ 
#启动服务发现的功能,开启了才能调用其它服务 
spring.cloud.config.discovery.enabled=true
#发现的服务的名字--对应注测中心的服务名字
spring.cloud.config.discovery.serviceId=compute-server 

 

        通过spring.application.name属性,我们可以指定微服务的名称后续在调用的时候只需要使用该名称就可以进行服务的访问。

    eureka.client.serviceUrl.defaultZone属性对应服务注册中心的配置内容,指定服务注册中心的位置。

          为了在本机上测试区分服务提供方和服务注册中心,使用server.port属性设置不同的端口。

2.5 启动该项目

        再次访问:http://localhost:1111/

        可以看到,我们定义的服务被注册了。如下图所示:

 

 

3 spring cloud 路由网关服务---Zuul

        在使用Zuul之前,我们先构建一个服务注册中心、以及两个简单的服务,比如:我构建了一个service-A,一个service-B。然后启动eureka-server和这两个服务。通过访问eureka-server,我们可以看到service-A和service-B已经注册到了服务中心。

服务service-A和service-B的配置都一样并实现在注册中心注册,只有端口号不一样而已如下所示:

service-A:

并在service-A:下构建一个controller如下:

package com.example.demo.controller;import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;@RestController
public class MyController {@RequestMapping(value = "/info" ,method = RequestMethod.GET)public String info() {  return "hello I am is spring-serviceA"; //测试代码直接返回一个字符串,不再调用service层等等。  
    }
}

 

service-B:

并在service-B下构建一个controller如下:

package com.example.demo.controller;import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;@RestController
public class MyController {@RequestMapping(value = "/info" ,method = RequestMethod.GET)public String info() {  return "hello I am is spring-service"; //测试代码直接返回一个字符串,不再调用service层等等。  
    }
}

 

    3.1 创建 spring boot项目 同上

    3.2 修改pom.xml文件,添加spring cloud 依赖如下

    <dependencies><!-- springBoot 核心 --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter</artifactId></dependency><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-config</artifactId></dependency><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-netflix-zuul</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope></dependency></dependencies>

 

3.3 在启动类上加上注解 如下

  • 应用主类使用@EnableZuulProxy注解开启Zuul
  • 这里用了@SpringCloudApplication注解,之前没有提过,通过源码我们看到,它整合了@SpringBootApplication、@EnableEurekaClient、@EnableCircuitBreaker,主要目的还是简化配置。这几个注解的具体作用这里就不做详细介绍了,之前的文章已经都介绍过。
    @EnableZuulProxy
    @SpringCloudApplication    
    public class Demo4Application {public static void main(String[] args) {SpringApplication.run(Demo4Application.class, args);}
    }

     

3.4 配置文件application.properties

application.properties中配置eureka服务注册中心

eureka.client.serviceUrl.defaultZone=http://localhost:1111/eureka/  
server.port=3333  
spring.application.name=service-zuul  
#表示只要访问以/api-a/开头的多层目录都可以路由到 id为compute-service的服务上  
zuul.routes.compute-service=/api-a/**  

 

 

#表示只要访问以/api-a/开头的多层目录都可以路由到 id为compute-service1的服务上
zuul.routes.compute-service1=/api-a/**
上面的一行等同于下面的两行
zuul.routes.api-a.path=/api-a/**
zuul.routes.api-a.serviceId=compute-service1

通配符含义举例解释
?匹配任意单个字符/feign-consumer/?匹配/feign-consumer/a,/feign-consumer/b,/feign-consumer/c等
*匹配任意数量的字符/feign-consumer/*匹配/feign-consumer/aaa,feign-consumer/bbb,/feign-consumer/ccc等,无法匹配/feign-consumer/a/b/c
**匹配任意数量的字符/feign-consumer/*匹配/feign-consumer/aaa,feign-consumer/bbb,/feign-consumer/ccc等,也可以匹配/feign-consumer/a/b/c

  3.4 启动项目 测试

输入地址:http://localhost:4444/api-a/info

每次刷新访问都会在下面结果轮询显示:

 

这就简单实现了springCloud的负载均衡.

 

  3.5 服务过滤

        在完成了服务路由之后,我们对外开放服务还需要一些安全措施来保护客户端只能访问它应该访问到的资源。所以我们需要利用Zuul的过滤器来实现我们对外服务的安全控制。

        在服务网关中定义过滤器只需要继承ZuulFilter抽象类实现其定义的四个抽象函数就可对请求进行拦截与过滤。

        比如下面的例子,定义了一个Zuul过滤器,实现了在请求被路由之前检查请求中是否有accessToken参数,若有就进行路由,若没有就拒绝访问,返回401 Unauthorized错误。

package com.example.demo.filter;import javax.servlet.http.HttpServletRequest;import org.springframework.util.StringUtils;import com.netflix.zuul.ZuulFilter;
import com.netflix.zuul.context.RequestContext;public class AccessFilter  extends ZuulFilter {@Overridepublic Object run() {RequestContext ctx = RequestContext.getCurrentContext();  HttpServletRequest request = ctx.getRequest();System.out.println(String.format("%s demoFilter request to %s", request.getMethod(), request.getRequestURL().toString()));String username = request.getParameter("username");// 获取请求的参数 if(!StringUtils.isEmpty(username)&&username.equals("lilei")){//通过ctx.setSendZuulResponse(true);// 对该请求进行路由  ctx.setResponseStatusCode(200);  ctx.set("isSuccess", true);// 设值,让下一个Filter看到上一个Filter的状态  return null;  }else{ctx.setSendZuulResponse(false);// 过滤该请求,不对其进行路由  ctx.setResponseStatusCode(401);// 返回错误码  ctx.setResponseBody("{\"result\":\"username is not correct!\"}");// 返回错误内容  ctx.set("isSuccess", false);  return null;}}@Overridepublic boolean shouldFilter() {return true;// 是否执行该过滤器,此处为true,说明需要过滤
    }@Overridepublic int filterOrder() {return 0;// 优先级为0,数字越大,优先级越低
    }/*pre:可以在请求被路由之前调用route:在路由请求时候被调用post:在route和error过滤器之后被调用error:处理请求时发生错误时被调用*/@Overridepublic String filterType() {return "pre";// 前置过滤器   
    }}

 

自定义过滤器的实现,需要继承ZuulFilter,需要重写实现下面四个方法:

  • filterType:返回一个字符串代表过滤器的类型,在zuul中定义了四种不同生命周期的过滤器类型,具体如下:
    • pre:可以在请求被路由之前调用
    • routing:在路由请求时候被调用
    • post:在routing和error过滤器之后被调用
    • error:处理请求时发生错误时被调用
  • filterOrder:通过int值来定义过滤器的执行顺序
  • shouldFilter:返回一个boolean类型来判断该过滤器是否要执行,所以通过此函数可实现过滤器的开关。在上例中,我们直接返回true,所以该过滤器总是生效。
  • run:过滤器的具体逻辑。需要注意,这里我们通过ctx.setSendZuulResponse(false)令zuul过滤该请求,不对其进行路由,然后通过ctx.setResponseStatusCode(401)设置了其返回的错误码,当然我们也可以进一步优化我们的返回,比如,通过ctx.setResponseBody(body)对返回body内容进行编辑等。

在实现了自定义过滤器之后,还需要实例化该过滤器才能生效,我们只需要在应用主类中增加如下内容:

再次访问链接:http://localhost:4444/api-a/info

提示如下错误:被过滤器给拦截了

访问新地址:http://localhost:4444/api-a/info?username=lilei

则可以正常访问

项目源码:https://download.csdn.net/download/guokezhongdeyuzhou/10303861

由于时间原因暂时更新到此,后面会陆续更新相关的其他特性.

转载于:https://www.cnblogs.com/powerwu/articles/9767878.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/570330.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

StringBuffer的用法

有一次在做项目的时候&#xff0c;发现一处平时都是用String的地方&#xff0c;用上了StringBuffer。不禁疑惑&#xff0c;在网上查了一下&#xff0c;看看他们之间的区别&#xff1a; 初始化&#xff1a; StringBuffer s new StringBuffer(); 此时初始化的对象是一个空对象…

【Docker】安装tomcat并部署应用

安装tomcat 1.拉取tomcat镜像查看镜像部署应用[rootizwz99yhul3o6y7emidjqlz ~]# docker exec -it app bashrootcb526f04d30b:/usr/local/tomcat# lsBUILDING.txt CONTRIBUTING.md LICENSE NOTICE README.md RELEASE-NOTES RUNNING.txt bin conf include lib …

prompt set feedback set define的详解

prompt 输出后面的信息&#xff0c;类似操作系统的echo命令 SQL>prompt hello world--输出 hello world set feedback 客户端执行SQL时&#xff0c;数据库服务器都会返回反馈信息。如&#xff1a; 创建表时&#xff0c;成功的反馈信息为“Table Created”等 feedback属…

python 学习第四十七天shelve模块

shelve模块是一个简单的k&#xff0c;v将内存数据通过文件持久化的模块&#xff0c;可以持久化任何pickle可支持的python数据格式。 1&#xff0c;序列化 import shelve fshelve.open(shelve_test) names[www.96net.com.cn,"www.dc3688.com","www.baidu.com&quo…

怎么在oracle中使用for循环

测试打印语句&#xff1a; BEGIN sys.dbms_output.put_line(‘hello world’); END; DECLAREa number(20) :0; BEGINfor i in 1 .. 10 loop--insert into user (name) values(ss i); --写相关sqlsys.dbms_output.put_line(ss || i);end loop;commit; END; 详解&a…

structc 开源框架介绍

引言 - 一切才刚刚开始 structc 是 C 结构基础库. 简单可复用. structc - https://github.com/wangzhione/structc 之前也描述过几次 structc, 文字多代码风格少. 最近加班不多, 准备详细解说哈其思考初衷. 0.0 整体结构 structc ├── extern ├── LICENSE ├── Makefil…

charCodeAt()和charAt()的用法

语法&#xff1a; stringObject.charCodeAt(index) 参数index 必需&#xff0c;第一个字符的下标是0。如果index是负数&#xff0c;或者大于等于字符串的长度&#xff0c;则返回NaN。 作用&#xff1a;返回指定位置的字符的Unicode编码。这个返回值在0~65535之间的整数。 va…

“not a single-group group function”

在使用group by进行分组的时候&#xff0c;select 后面要查询出来的表字段 只用有两种情况&#xff1a; 1、在group by 子句中出现 2、或者写在聚合函数里面&#xff0c;像count&#xff08;id&#xff09;等等。 若是没有&#xff0c;select出来的表字段就会报错&#xff0…

jmeter笔记

Jmeter性能测试 入门Jmeter 录制脚本&#xff1a;使用一个叫badbody的工具录制脚步供jmeter使用&#xff0c;http://www.badboy.com.au/&#xff1b;也可以用jmeter来录制Jmeter教程 简单的压力测试linux环境jmeter测试probe监控tomcat转载于:https://www.cnblogs.com/liqipeng…

在oracle中的日期类型与String类型

to_date()作用是将字符类转化为日期类型。 具体用法&#xff1a;to_date(”2004-11-27”,”yyyy-mm-dd”),前者为字符串&#xff0c;后者为转换日期格式。 to_date(”2004-11-27 13:34:43”, ”yyyy-mm-dd hh24:mi:ss”) 将得到具体的时间 to_char()作用将日期转按一定格式换…

CSS颜色

CSS的颜色可以通过以下方法指定&#xff1a; 十六进制颜色RGB颜色RGBA颜色HSL色彩HSLA颜色 十六进制颜色 指定一个十六进制的颜色其组成部分是&#xff1a;#RRGGBB&#xff0c;其中RR&#xff08;红色&#xff09;&#xff0c;GG&#xff08;绿色&#xff09;和BB&#xff08;蓝…

URI 和 URL 的区别

URI : 统一资源标识符&#xff08;Uniform Resource Identifier)是一个用于标识&#xff0c;某一互联网资源名称的字符串。 Web上可用的每种资源 -HTML文档、图像、视频片段、程序等 - 由一个通用资源标识符&#xff08;Uniform Resource Identifier, 简称”URI”&#xff09;进…

CAN总线(1)--初探(更新中)

前言&#xff1a; CAN总线可以控制可以使用Xilinx中IP核来直接实现&#xff0c;也可以使用专用的CAN芯片&#xff08;例如&#xff1a;SJA1000&#xff09;通过单片机和FPGA驱动控制来实现&#xff1b; 目前是使用控制器SJA1000来进行实现&#xff1b; CAN总线控制器-SJA1000 结…

使用encodeURl()进行编解码

encodeURI() 函数可把字符串作为 URI 进行编码。 在js中要使用两次编码&#xff1a; var name encodeURI(encodeURI(name)); 在java中解码&#xff1a; String name URLDecoder.decode(request.getParameter("name"), "UTF-8"); decodeURI() 函数 :…

CodeForces 1065E. Side Transmutations 计数

昨天不该早点走的.... 首先操作限制实际上是一个回文限制 每个$b[i] - b[i - 1]$互不干扰&#xff0c;不妨设这个串关于中心点对称的这么一对区间的串分别为$(S_1, S_2)$ 题目的限制相当与存在$(T_1, T_2)$使得$T_1 inv(S_2) \;and\;T_2 inv(S_1)$ 考虑一对串$(S_1, S_2)$被计…

ORA-00911: invalid character

sql语句中最后多了分号的原因&#xff0c;去掉分号就可以。

Luogu1979 NOIP2013D2T3 华容道 搜索、最短路

题目传送门 题意&#xff1a;给出一个$N \times M$的棋盘&#xff0c;棋盘上有一些块可以移动&#xff0c;有一些块无法移动。$Q$次询问&#xff0c;每一次询问给出三个块$a,b,c$&#xff0c;将$a$块变为空格&#xff0c;空格旁边可移动的块可以与空格交换位置。问每一次询问中…

user_tab_columns是什么

user_tab_columns是保存了当前用户的表、视图和Clusters中的列等信息&#xff0c;用于oracle获取表结构。 user_tab_cols不同的是比user_tab_columns多几列&#xff1a; HIDDEN_COLUMN VARCHAR2(3) VIRTUAL_COLUMN VARCHAR2(3) SEGMENT_COLUMN_ID NUMBER INTERNAL_COLUMN_…

英语初级学习系列-00-Name-介绍自己

1. 询问名字 常用句子 1. Hi, may I have your name, please? 2. Could you please tell me your name? 3. Will it be convenient for you if I have your name? 4. your name? 5. What is your name? 使用场景 你想知道别人的叫啥的时候你想和别人搭话&#xff08;搭讪&…

Oracle 怎么删除重复数据

1、根据rowid来查询重复数据 select * from table1 a where rowid !(select max(rowid) from table1 b where a.name1b.name1 ) 2、根据rowid来删除重复数据 delete from table1 a where rowid !(select max(rowid) from table1 b where a.name1b.name1 ) 3、根据group …