掘金 后端 ( ) • 2021-05-30 14:03
.markdown-body{word-break:break-word;line-height:1.75;font-weight:400;font-size:15px;overflow-x:hidden;color:#333}.markdown-body h1,.markdown-body h2,.markdown-body h3,.markdown-body h4,.markdown-body h5,.markdown-body h6{line-height:1.5;margin-top:35px;margin-bottom:10px;padding-bottom:5px}.markdown-body h1{font-size:30px;margin-bottom:5px}.markdown-body h2{padding-bottom:12px;font-size:24px;border-bottom:1px solid #ececec}.markdown-body h3{font-size:18px;padding-bottom:0}.markdown-body h4{font-size:16px}.markdown-body h5{font-size:15px}.markdown-body h6{margin-top:5px}.markdown-body p{line-height:inherit;margin-top:22px;margin-bottom:22px}.markdown-body img{max-width:100%}.markdown-body hr{border:none;border-top:1px solid #ddd;margin-top:32px;margin-bottom:32px}.markdown-body code{word-break:break-word;border-radius:2px;overflow-x:auto;background-color:#fff5f5;color:#ff502c;font-size:.87em;padding:.065em .4em}.markdown-body code,.markdown-body pre{font-family:Menlo,Monaco,Consolas,Courier New,monospace}.markdown-body pre{overflow:auto;position:relative;line-height:1.75}.markdown-body pre>code{font-size:12px;padding:15px 12px;margin:0;word-break:normal;display:block;overflow-x:auto;color:#333;background:#f8f8f8}.markdown-body a{text-decoration:none;color:#0269c8;border-bottom:1px solid #d1e9ff}.markdown-body a:active,.markdown-body a:hover{color:#275b8c}.markdown-body table{display:inline-block!important;font-size:12px;width:auto;max-width:100%;overflow:auto;border:1px solid #f6f6f6}.markdown-body thead{background:#f6f6f6;color:#000;text-align:left}.markdown-body tr:nth-child(2n){background-color:#fcfcfc}.markdown-body td,.markdown-body th{padding:12px 7px;line-height:24px}.markdown-body td{min-width:120px}.markdown-body blockquote{color:#666;padding:1px 23px;margin:22px 0;border-left:4px solid #cbcbcb;background-color:#f8f8f8}.markdown-body blockquote:after{display:block;content:""}.markdown-body blockquote>p{margin:10px 0}.markdown-body ol,.markdown-body ul{padding-left:28px}.markdown-body ol li,.markdown-body ul li{margin-bottom:0;list-style:inherit}.markdown-body ol li .task-list-item,.markdown-body ul li .task-list-item{list-style:none}.markdown-body ol li .task-list-item ol,.markdown-body ol li .task-list-item ul,.markdown-body ul li .task-list-item ol,.markdown-body ul li .task-list-item ul{margin-top:0}.markdown-body ol ol,.markdown-body ol ul,.markdown-body ul ol,.markdown-body ul ul{margin-top:3px}.markdown-body ol li{padding-left:6px}.markdown-body .contains-task-list{padding-left:0}.markdown-body .task-list-item{list-style:none}@media (max-width:720px){.markdown-body h1{font-size:24px}.markdown-body h2{font-size:20px}.markdown-body h3{font-size:18px}}

文章正文第一句:本文已参与周末学习计划,点击链接查看详情

通过路由代理角度、解读集中常见的路由代理方式

常见方式

nginx+lua

  • nginx是一种高性能HTTP反向代理服务器。在我们之前的项目中我们使用nginx主要有两种用途: 方向代理+静态资源服务器管理

  • 点我下载哦

  • 下载完成之后nginx的启动也很是方便,笔者这里演示的是windows版本的。你可以直接点击应用程序也可以直接通过命令行模式启动./nginx或者nginx。看具体系统

image-20210530114514358

服务代理

  • 正常情况下的代理是使用发哦HTTP下的Server来配置的。比如我们下面需要简单代理下我们某个服务http://192.168.44.131:9200 .。我们想要的效果就是通过本地localhost就可以访问到这个服务提供的接口了。
server {
        listen 9200;
        server_name localhost;
        location / {
            proxy_pass   http://192.168.44.131:9200;
            #root   html;
            #index  index.html index.htm;
        }
    }
复制代码
  • 当然配置结束后我们需要进行nginx -s reload进行重启nginx才能重新加载配置。这个时候我们访问localhost:9200/zxhtom/start实际上就是被代理到http://192.168.44.131:9200/zxhtom/start这个接口上。

静态资源代理

  • 上面我们说了除了服务代理之外。nginx还可以作为静态资源服务器使用。
server{
        listen 90;
        server_name localhost;
        location / {
            root D:\study\PageOffice_4.6.0.3_Java;
            index index.html;
        }
    }
复制代码
  • 同样是使用了HTTP下的SERVER 。 这段代码表示将本地D:\study\PageOffice_4.6.0.3_Java这个文件夹作为本地90端口的入口。我们通过localhost:90/index.html就能访问到本地的D:\study\PageOffice_4.6.0.3_Java\index.html文件了。

负载均衡

upstream redis {
            hash $remote_addr consistent;
            # $binary_remote_addr;
            server 192.168.44.131:6379 weight=5 max_fails=3 fail_timeout=30s;
server 192.168.44.132:6379 weight=1 max_fails=3 fail_timeout=30s;
        }
        server {
            listen 6379;#redis服务器监听端口
            proxy_connect_timeout 10s;
            proxy_timeout 300s;#设置客户端和代理服务之间的超时时间,如果5分钟内没操作将自动断开。
            proxy_pass redis;
        }
复制代码
  • 有的时候我们服务并不仅仅是单节点服务。如果是分布式的话我们需要对服务进行负载均衡策略。这个时候我们就无法仅仅代理服务了。需要我们添加负载策略。我们通过upstream 来定义服务,并对服务进行策略定义。最后我们在server中直接时候用upstream的服务名就可以了。其中的weight就是对集群中服务轮询的权重设置。

常用命令

重启: nginx.exe -s reload 关闭:nginx.exe -s stop 检测配置合法性:nginx.exe -t

zuul

  • zuul是netflix开源项目,关于zuul的深入浅出我在之前的文章中解析的应该算是很全面了。点我看zuul章节
  • 通过zuul我们可以实现动态路由、认证授权、动态过滤器、最终要的是可以自定义还可以结合hystix进行服务熔断
  • zuul还可以实现灰度发布这个可以解决我们的停机发布问题。想想如果你的系统是24小时不能停机的那么zuul实现灰度发布

灰度扩展

image-20210428113708374

  • 上述我们通过请求中指定的参数实现了路由的转发实现原理是借助了eureka的metadata的参数属性路由的。
  • 但是我们在平时应该遇到过有些软件对不同地区进行不同对待。
    • 比如说支付宝蚂蚁森林不同城市有不同的策略
    • 比如说某软件邀请你参与内侧版本使用
  • 上述都统称为灰度发布,原理也很简单实际上就是有多个实例,zuul根据请求的特征转发到不同的实例上。不同城市就是根据地区来路由,邀请内侧就是通过个人用户信息来路由。他们的实现都离不开我们上面描述的Ribbon.Predicate 。想要对灰度发布进行扩展我们就离不开Predicate

Predicate

  • 该类的作用就是进行断言,是google提出的思想。具体有关google轻轻点我
  • 在ribbon专题中,我们简单的通过源码阅读的方式了解了ribbon是如何进行负载均衡以及内部负载均衡的策略的。有兴趣的可以点击主页查找。
  • 今天我们来看看ribbon在负载均衡之前是如何在获取服务列表之后进行过滤的。
//根据输入返回断言  true  or  false
@GwtCompatible
public interface Predicate<T> {
  //针对输入内容进行断言,该方法有且不仅有如下要求: 1、不会造成任何数据污染 2、在T的equals中相等在apply中是相同效果
  boolean apply(@Nullable T input);
  //返回两个Predicate是否相同。一般情况Predicate实现是不需要重写equals的 。 如果实现可以根据自己需求表明predicate是否相同。什么叫做相同就是两个predicate对象apply的结果相同即为对象相同
  @Override
  boolean equals(@Nullable Object object);
}
复制代码
  • 下面我们通过Predicate来实现下简单数据过滤。当然有的人会说为什么不用java8 stream过滤呢。这里只是为了为Ribbon中服务过滤铺路。至于ribbon为什么不使用流操作呢?个人角色google的Predicate在解耦上更加的方便吧。
@Test
    public void pt() {
        List userList = new ArrayList<>();
        for (int i = 0; i < 100; i++) {
            userList.add(new User(Long.valueOf(i+1), "张三"+(i+1)));
        }

        Predicate predicate = new Predicate() {
            @Override
            public boolean apply(User user) {
                return user.getId() % 2 == 0;
            }
        };
        ArrayList users = Lists.newArrayList(Iterables.filter(userList, predicate));
        System.out.println(users);
    }
复制代码

AbstractServerPredicate

前提回要

  • 上面我们的类结构图中可以看出AbstractServerPredicatePredicate 的实现类。这个类也是Ribbon在获取服务列表的关键角色。因为后面都是基于这个类进行功能扩展的。

image-20210428114538598

jmnarloch初识

  • 这是我在RIbbon中的内容。我们可以知道Ribbon最终是在BaseLoadBalancer 中进行负载均衡的。其内部的rule默认是new RoundRobinRule() ,因为我们引入了io-jmnarloch 。先看看内部的类结构

image-20210428135009468

  • io-jmnarloch 内部不是很复杂,至少Ribbon、feign这些比起来他真的是简单到家了。内部一个四个package
package作用api提供上下文,供外部使用predicate提供获取服务列表过滤器ruleribbon中的负载均衡策略实现support对上述的辅助包

注册负载Rule

image-20210428140904078

  • 我们可以看到在support包中的RibbonDiscoveryRuleAutoConfiguration 中配置了rule包下定义好的Ribbon的负载均衡类Rule。

image-20210428151842248

  • 断电打到BaseLoadBalancer 中我们可以看到rule就是我们rule.MetadataAwareRule 这个类。这里和ribbon章节说的好像有出入,我们在ribbon章节说需要自定义rule的时候需要在@RibbonClient(name = "CLOUD-PAYMENT-SERVICE",configuration = MySelfRule.class) 这种方式。
  • 其实在配置DiscoveryEnabledRule 的时候在注册的时候有 @Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE) 表示作用域

MetadataAwareRule如何过滤服务

image-20210428152925935

  • 通过MetadataAwareRule 结合代码我们可以了解到最终是PredicateBaseRule#choose 在选择服务列表

image-20210428153232467

image-20210428153646707

  • 这个predicate 就是我们choose中getPredicate()方法获取的。所以在ribbon进行选择服务之前会通过MetadataAwarePredicate 进行过滤服务。

  • 获取到过滤器对象后,我们就会执行chooseRoundRibbinAfterFiltering .

回到AbstractServerPredicate

image-20210428154144876

  • 上文说到最终会通过Predicate去执行chooseRoundRobinAfterFiltering。 还记得一开始predicate的结构图了吗。MetadataAwarePredicate最终继承AbstractServerPredicate 。 而AbstractServerPredicate # chooseRoundRobinAfterFiltering 是依赖getEligibleServers`来获取合适的服务列表的。
  • AbstractServerPredicate 实现了好多chooseXXX的方法。因为ribbon默认是轮询方式所以在BaseLoadBalance中是选择Round对应的方法。这些我们都可以自己去修改方式。这里不赘述
  • Eligible 译为合适的。getEligibleServers 翻译过来是获取合适的服务列表。

image-20210428155022152

  • 我们很明显的可以看到最终过滤的逻辑落在了apply方法上。

image-20210428155158141

  • 这就是我们上述通过metadata-map:lancher 配置我们的服务信息。

  • 下面是AbstractServerPredicate 精简后样子。主要就是getEligibleServers 这个方法。

image-20210429092945792

子类

  • 在上面AbstractServerPredicate 结构图中我们可以看到除了DiscoveryEnabledPredicate 这个子类外,还有四个子类。
子类作用AvailabilityPredicate过滤不可用服务器CompositePredicate组合模式,保证服务数量一定数量。换句话说就是服务太少则会一个一个fallback知道服务数量达到要求ZoneAffinityPredicate选取指定zone区域内的ServerZoneAvoidancePredicate避免使用符合条件的server . 和ZoneAffinityPredicate功能相反