目录
如下图所示,订单服务 调用 商品服务 的 流程的变化从1-4。

存在问题:此时,如果有新的客户端分别调用 订单服务 和 商品服务,那么这个客户端需要维护新的ip和port。而新客户端调用这些微服务时候,微服务又需要解决进行认证鉴权、安全校验、跨域问题。如果微服务 订单服务 和 商品服务 可能有成百上千个。此时存在的问题是:
解决方案:使用服务网关。而服务网关也是一个微服务,也可从注册中心获取ip。因此,所谓的API网关,就是指系统的统一入口,它封装了应用程序的内部结构,为客户端提供统一服务,一些与业务本身功能无关的公共逻辑可以在这里实现,诸如认证、鉴权、监控、路由转发等等。添加上API网关之后,系统的架构图变成了如下所示:

| 名称 | 作用 | 问题 |
| Ngnix+lua | 使用nginx的反向代理和负载均衡可实现对api服务器的负载均衡及高可用。lua是一种脚本语言,可以来编写一些简单的逻辑,nginx支持lua脚本。 | 无 |
| Kong | 基于Nginx+Lua开发,性能高,稳定,有多个可用的插件(限流、鉴权等等)可以开箱即用。 | 1、只支持Http协议 2、二次开发,自由扩展困难 3、提供管理API,缺乏更易用的管控、配置方式。 |
| zuul | Netflix开源的网关,功能丰富,使用JANA开发,易于二次开发 | zuul1.0 缺乏管控,无法动态配置;依颡组件较多;处理Http请求依赖的是Web容器,性能不如Nginx |
| Spring cloud Gateway | Spring cloud Gateway Spring公司为了替换Zuul而开发的网关服务 | 无 |
| zuul | Spring cloud Gateway |
| Zuul 1.x,是基于阻塞V/O的API网关 | Spring Cloud Gateway基于异步非阻塞模型,建立在Spring Framework5、Project Reactor和Spring Boot 2之上 |
| Zuul 1.x,是基于Servlet 2.5,使用阻塞架构和传统的IO处理模型(高并发线程数量上涨,严重影响请求处理时间),它不支持任何长连接(如WebSocket) | Spring Cloud Gateway基于webplus(webplus基于Servlet 3.x异步非阻塞的模型,核心是Reactor响应式编程),支持WebSocket |
| Zuul 1.x,设计模式和Nginx较像,每次V/О操作都是从工作线程中选择一个执行,请求线程被阻塞到工作线程完成。 但是差别是Nginx用C++实现,Zuul用Java实现,故而JVM本身会有第—次加载较慢的情况,使得Zuul的性能相对较差。 | 根据官方提供的基准测试,Spring Cloud Gateway的RPS(每秒请求数)是Zuul的1.6倍。 |
| Zuul 2.x 理念更先进,想基于Netty非阻塞和支持长连接,性能较Zuul 1.x有较大提升,但SpringCloud目前还没有整合。 | 与Spring紧密集成拥有更好的开发体验 |

Spring Cloud Gateway是Spring公司基于Spring 5.0,Spring Boot 2.0和Project Reactor等技术开发的网关,它旨在为微服务架构提供一种简单有效的统一的API路由管理方式。它的目标是替代 Netilx Zuul,其不仅提供统一的路由方式,并且基于Filter链的方式提供了网关基本的功能,例如:安全,监控/指标和限流。

Gateway核心是:route、Predicate、Filter。
流程:通过网关服务,对访问路径进行请求,如果请求符合断言,则调用订单服务模块。
完整pom如下:
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <parent> <artifactId>springcloudbase</artifactId> <groupId>com.hwadee.springcloud2022</groupId> <version>0.0.1-SNAPSHOT</version> </parent> <modelVersion>4.0.0</modelVersion> <groupId>com.hwadee.springcloud</groupId> <artifactId>routeServer7000</artifactId> <version>0.0.1-SNAPSHOT</version> <dependencies> <!-- 引入GetAway依赖 注意引GetAway网关服务模块中不能引入 starter-web, 如果引入 starter-web 会引入tomcat,与 GetAway 底层的webFlux冲突。 --> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-gateway</artifactId> </dependency> <!--Eureka Client--> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId> </dependency> </dependencies> </project>
注意:如果GetAway网关服务模块中引入GetAway依赖,则不能引入 starter-web, 如果引入 starter-web 则会引入tomcat,与 GetAway 底层的webFlux冲突,如下图:
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
@SpringBootApplication
@EnableEurekaClient// 启动 eureka 客户端
public class RouteServerApplication {
public static void main(String[] args) {
SpringApplication.run(RouteServerApplication.class, args);
}
}
添加配置文件,配置文件中配置GetAway网关服务。
server:
port: 7000
spring:
application:
name: route-service # 为当前商品服务命名
cloud:
gateway:
routes: #路由数组[路由就是指定当请求满足什么条件的时候转到哪个微服务〕
- id: order_route #当前路由的标识,要求唯一,默认uuid。自定义命名:一般是 要转发的服务名_route
uri: http://localhost:9000 #请求最终要被转发到的 订单服务 的地址
order: 1 #路由的优先级,数字越小级别越高
predicates: #断言(条件判断返回值类型是boolean,就是路由转发要满足的条件)
- Path=/order-serv/** #当请求路径满足Path指定的规则时,才进行路由转发,如/order-serv/order/select/22
filters: #过滤器,请求在传递过程中可以通过过滤器对请求进行一定的修改
- StripPrefix=1 #使用 StripPrefix= x 是指在转发前去掉几层路径。见下面属性说明案例。
- id: order_route #当前路由的标识,要求唯一
uri: http://localhost:9001 #请求最终要被转发到的 产品服务 的地址
order: 1 #路由的优先级,数字越小级别越高
predicates: #断言(条件判断返回值类型是boolean,就是路由转发要满足的条件)
- Path=/product-serv/** #当请求路径满足Path指定的规则时,才进行路由转发
filters: #过滤器,请求在传递过程中可以通过过滤器对请求进行一定的修改
- StripPrefix=1 #使用 StripPrefix= 1 在转发前去掉1层路径。
eureka:
client:
service-url: # 配置服务注册地址,与 eureka-server 中暴露地址保持一致
defaultZone: http://localhost:8000/eureka
instance:
prefer-ip-address: true # 是否使用 IP 地址注册,默认 false
# instance-id: product-service # 实例 id,服务的唯一标识
instance-id: ${spring.cloud.client.ip-address}:${server.port} # 如果想在控制页面看到服务地址与端口,可以将 instance-id 这样配置
lease-renewal-interval-in-seconds: 5 # 发送心跳的间隔,单位秒,默认 30
lease-expiration-duration-in-seconds: 10 # 续约到期时间,单位秒,默认90
属性介绍说明:
| routes | #路由数组[路由就是指定当请求满足什么条件的时候转到哪个微服务] |
| id | id: order_route # 表示当前路由的标识,要求唯一,默认是uuid。 一般是转发到哪个服务就在哪个服务名后加route |
| uri | #请求最终要被转发到的哪个服务的地址,如:http://localhost:8081 |
| order | order:1 #路由的优先级,数字越小级别越高 |
| predicates | #断言(条件判断返回值类型是boolean,就是路由转发要满足的条件) |
| Path | # - Path=/product-serv/**,当请求路径满足Path指定的规则时,才进行路由转发,如:/product-serv/order/buy/22 满足条件,会进行路由转发。 |
| filters | #过滤器,请求在传递过程中可以通过过滤器对请求进行一定的修改 |
| StripPrefix | # -StripPrefix = 1 是指转发之前去掉1层路径,例如: 我们的需求是 当浏览器访问路径是http://localhost:8081/order-serv/order/buy/22时候, |
创建 订单服务模块 及创建 商品服务模块 见第十章
删除 商品服务 模块中的 controller 内 selectHystrixBreaker方法里的超时代码,修改如下:
import com.hwadee.springcloud.entity.Product;
import com.netflix.hystrix.contrib.javanica.annotation.DefaultProperties;
import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand;
import com.netflix.hystrix.contrib.javanica.annotation.HystrixProperty;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.math.BigDecimal;
@RestController
@RequestMapping("/product")
public class ProductController {
//方便后面讲负载均衡,查看ip,此处获取配置中的端口号和ip
@Value("${server.port}")
private String port;
@Value("${spring.cloud.client.ip-address}")
private String ip;
@RequestMapping(value = "/select/{id}")
@HystrixCommand(fallbackMethod = "selectHystrixBreakerFallback", commandProperties = {
@HystrixProperty(name = "circuitBreaker.enabled", value = "true"),//是否开启断路器
@HystrixProperty(name = "circuitBreaker.requestVolumeThreshold", value = "20"),//请求次数
@HystrixProperty(name = "circuitBreaker.sleepWindowInMilliseconds", value = "10000"),//时间窗口期
@HystrixProperty(name = "circuitBreaker.errorThresholdPercentage", value = "60"),//失败率达到多少后跳闸
})
public Product selectHystrixBreaker(@PathVariable Long id) {
// 程序异常 测试异常熔断
if (id<=0) {
throw new RuntimeException("id无效");
}
// 程序正常执行
Product product = new Product();
product.setId(id);
// 后面需要测试负载均衡,所以返回 ip 地址及端口号
product.setName("当前访问服务地址:" + ip + ":" + port + " " + "从购物车删除订单,订单号:" + id);
product.setPrice(new BigDecimal(10000.0));
System.out.println(product);
return product;
}
public Product selectHystrixBreakerFallback(Long id) {
Product product = new Product();
product.setId(id);
product.setName("当前访问服务地址:" + ip + ":" + port + " " + "查询订单异常,通过注解 @HystrixCommand()指定的备选方案进行服务熔断");
product.setPrice(new BigDecimal(10000.0));
return product;
}
}
分别启动 注册中心、订单服务、商品服务、和网关服务
分别访问以下地址查看使用网关之前和使用网关之后的效果。
访问商品服务:http://localhost:9001/product/select/2
访问订单服务:http://localhost:9000/order/select/22
访问商品服务:http://localhost:7000/product-serv/product/select/22
访问订单服务:http://localhost:7000/order-serv/order/select/22




第十二章:Spring Cloud Config 统一配置中心详解
我正在尝试使用ruby和Savon来使用网络服务。测试服务为http://www.webservicex.net/WS/WSDetails.aspx?WSID=9&CATID=2require'rubygems'require'savon'client=Savon::Client.new"http://www.webservicex.net/stockquote.asmx?WSDL"client.get_quotedo|soap|soap.body={:symbol=>"AAPL"}end返回SOAP异常。检查soap信封,在我看来soap请求没有正确的命名空间。任何人都可以建议我
我想安装一个带有一些身份验证的私有(private)Rubygem服务器。我希望能够使用公共(public)Ubuntu服务器托管内部gem。我读到了http://docs.rubygems.org/read/chapter/18.但是那个没有身份验证-如我所见。然后我读到了https://github.com/cwninja/geminabox.但是当我使用基本身份验证(他们在他们的Wiki中有)时,它会提示从我的服务器获取源。所以。如何制作带有身份验证的私有(private)Rubygem服务器?这是不可能的吗?谢谢。编辑:Geminabox问题。我尝试“捆绑”以安装新的gem..
最近,当我启动我的Rails服务器时,我收到了一长串警告。虽然它不影响我的应用程序,但我想知道如何解决这些警告。我的估计是imagemagick以某种方式被调用了两次?当我在警告前后检查我的git日志时。我想知道如何解决这个问题。-bcrypt-ruby(3.1.2)-better_errors(1.0.1)+bcrypt(3.1.7)+bcrypt-ruby(3.1.5)-bcrypt(>=3.1.3)+better_errors(1.1.0)bcrypt和imagemagick有关系吗?/Users/rbchris/.rbenv/versions/2.0.0-p247/lib/ru
在Rails4.0.2中,我使用s3_direct_upload和aws-sdkgems直接为s3存储桶上传文件。在开发环境中它工作正常,但在生产环境中它会抛出如下错误,ActionView::Template::Error(noimplicitconversionofnilintoString)在View中,create_cv_url,:id=>"s3_uploader",:key=>"cv_uploads/{unique_id}/${filename}",:key_starts_with=>"cv_uploads/",:callback_param=>"cv[direct_uplo
我想在Ruby中创建一个用于开发目的的极其简单的Web服务器(不,不想使用现成的解决方案)。代码如下:#!/usr/bin/rubyrequire'socket'server=TCPServer.new('127.0.0.1',8080)whileconnection=server.acceptheaders=[]length=0whileline=connection.getsheaders想法是从命令行运行这个脚本,提供另一个脚本,它将在其标准输入上获取请求,并在其标准输出上返回完整的响应。到目前为止一切顺利,但事实证明这真的很脆弱,因为它在第二个请求上中断并出现错误:/usr/b
您如何在Rails中的实时服务器上进行有效调试,无论是在测试版/生产服务器上?我试过直接在服务器上修改文件,然后重启应用,但是修改好像没有生效,或者需要很长时间(缓存?)我也试过在本地做“脚本/服务器生产”,但是那很慢另一种选择是编码和部署,但效率很低。有人对他们如何有效地做到这一点有任何见解吗? 最佳答案 我会回答你的问题,即使我不同意这种热修补服务器代码的方式:)首先,你真的确定你已经重启了服务器吗?您可以通过跟踪日志文件来检查它。您更改的代码显示的View可能会被缓存。缓存页面位于tmp/cache文件夹下。您可以尝试手动删除
require"socket"server="irc.rizon.net"port="6667"nick="RubyIRCBot"channel="#0x40"s=TCPSocket.open(server,port)s.print("USERTesting",0)s.print("NICK#{nick}",0)s.print("JOIN#{channel}",0)这个IRC机器人没有连接到IRC服务器,我做错了什么? 最佳答案 失败并显示此消息::irc.shakeababy.net461*USER:Notenoughparame
我有一个使用PDFKit呈现网页的pdf版本的Rails应用程序。我使用Thin作为开发服务器。问题是当我处于开发模式时。当我使用“bundleexecrailss”启动我的服务器并尝试呈现任何PDF时,整个过程会陷入僵局,因为当您呈现PDF时,会向服务器请求一些额外的资源,如图像和css,看起来只有一个线程.如何配置Rails开发服务器以运行多个工作线程?非常感谢。 最佳答案 我找到的最简单的解决方案是unicorn.geminstallunicorn创建一个unicorn.conf:worker_processes3然后使用它:
关于如何使用git设置类似Dropbox的服务,您有什么建议吗?您认为git是解决此问题的合适工具吗?我在考虑使用git+rush解决方案,你觉得怎么样? 最佳答案 检查这个开源项目:https://github.com/hbons/SparkleShare来自项目的自述文件:Howdoesitwork?SparkleSharecreatesaspecialfolderonyourcomputer.Youcanaddremotelyhostedfolders(or"projects")tothisfolder.Theseprojec
我将以下代码放在一起用于一个简单的RubyTFTP服务器。它工作正常,因为它监听端口69并且我的TFTP客户端连接到它,我能够将数据包写入test.txt,但我不只是写入数据包,我希望能够从我的客户端通过TFTP传输文件到/temp目录。预先感谢您的帮助!require'socket.so'classTFTPServerdefinitialize(port)@port=portenddefstart@socket=UDPSocket.new@socket.bind('',@port)whiletruepacket=@socket.recvfrom(1024)putspacketFile