菜鸟日常之--------手动搭建SpringCloud

这次搭建的SpringCloud是由Eureka 、Zull、feign 、ribbon、Zull+hystricx以及zullfilter过滤器和service的搭建

功能包括:
所有微服务注册到注册中心(Eureka)中;创建Zuul,使用网关路由Zuul实现统一路由访问;创建两个service,通过feign和ribbon实现负载均衡的轮询策略;使用hystricx当服务宕机后提示无效。

一。Eureka的创建
1.创建一个module,创建一个启动类。添加如下注解:
@EnableEurekaServer
标识为注册中心,使得其它服务可以注册到这个微服务(注册中心)中

@SpringBootApplication
//服务端的启动类,可以接收别人注册进来
@EnableEurekaServer
public class EurekaserverApplication {
    public static void main(String[] args) {
        SpringApplication.run(EurekaserverApplication.class, args);
    }
}

2.编写properties或者yml配置文件

yml与properties转换器网址:
https://www.toyaml.com/index.html

添加eureka注册中心 配置

#这个是关键配置信息,让其它的服务知道是从哪里注册进来
eureka.client.serviceUrl.defaultZone=http://localhost:24680/eureka/
#端口
server.port=24680
#服务注册中心实例的主机名
spring.application.name= eurekaserver

#--------------------------以下是一些其它的配置信息---------------------
#服务端实例名称
eureka.instance.hostname=localhost
eureka.instace.instaceId= ${spring.application.name}:${random.int}

#是否想eureka注册中心注册自己
eureka.client.register-with-eureka=false
#fetch-registry为false表示是注册中心
eureka.client.fetch-registry=false

pom文件参考代码:

<?xml version="1.0" encoding="UTF-8"?>


4.0.0

<groupId>com.sun</groupId>
<artifactId>springbootEureka</artifactId>
<version>1.0-SNAPSHOT</version>

<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>2.3.1.RELEASE</version>
</parent>

<properties>
    <java.version>1.8</java.version>
    <spring-cloud.version>Hoxton.SR6</spring-cloud.version>
</properties>

<dependencies>
    <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.boot</groupId>
        <artifactId>spring-boot-starter-test</artifactId>
        <scope>test</scope>
        <exclusions>
            <exclusion>
                <groupId>org.junit.vintage</groupId>
                <artifactId>junit-vintage-engine</artifactId>
            </exclusion>
        </exclusions>
    </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>

debug启动后,输入默认 127.0.0.1:24680即可访问eureka主页面,显示如下:
在这里插入图片描述

二。service的创建
先创建一个servcie注册进注册中心(Eureka)中并运行起来。

new module,正常操作。

启动类的注解:
二者选其一
@EnableEurekaClient只适用于使用Eureka作为注册中心的场景,@EnableDiscoveryClient可以适用于其他注册中心的场景比如nacos等。

@SpringBootApplication
@ServletComponentScan

//关键的注解
@EnableDiscoveryClient

@RefreshScope   //开启配置更新功能
public class ConsumerClientApplication {
    public static void main(String[] args) {
        SpringApplication.run(ConsumerClientApplication.class, args);
    }
}

2.编写properties或者yml配置文件

#前三个必备
server.port=28888
spring.application.name=springbootService
eureka.client.serviceUrl.defaultZone=http://127.0.0.1:24680/eureka/


#---------------------------其它的配置----------------------------------
eureka.instance.prefer-ip-address=true
eureka.instance.ip-address=127.0.0.1
eureka.client.registerWithEureka=true
eureka.client.fetchRegistry=true

eureka.instance.instance-id=${eureka.instance.ip-address}:${server.port}@${random.int}

Controller:
普通的写一个方法即可,由于没有页面,因此使用RestController返回String.
(传不传参都可以)

最后成功启动service后,再Eureka的页面中显示到service已经成功注册到Eureka注册中心中。
如下图所示:
在这里插入图片描述

之后所有的服务均要与service中配置Eureka的内容一直,并且可能要增加,启动后的结果也是一样,都会再Eureka中呈现出来

当然service服务的请求也是可以访问的12
浏览器中输入
http://127.0.0.1:28888/service/hello
在这里插入图片描述

三。Zuul的创建
1.启动类
除了需要这个注解可注册到注册中心之外
@EnableDiscoveryClient

还需要这个注解
@EnableZuulProxy
启用Zuul的代理

@SpringBootApplication
@EnableDiscoveryClient

@EnableZuulProxy
@RefreshScope
public class ZuulserverApplication {
    public static void main(String[] args) {
        SpringApplication.run(ZuulserverApplication.class, args);
    }
}

上面一坨都是Eureka的配置 说了好多遍了 不多赘述了
中间是端口和名字
下面才是zuul的配置:
名称为sbService的路由,serviceId 是其它服务配置信息那里设置的名字,path:是当使用路由时在端口后,request路径前,需要加上的一段

例如 service的服务端口是28888 因此直接访问127.0.0.1:28888/service/hello
配置路由后,路由端口为3333,path: /sbService/**
那么就应该这么访问:
127.0.0.1:3333/sbService/service/hello

eureka:
  client:
    fetchRegistry: true
    registerWithEureka: true
    serviceUrl:
      defaultZone: http://127.0.0.1:24680/eureka/
  instance:
    instance-id: ${eureka.instance.ip-address}:${server.port}@${random.int}
    ip-address: 127.0.0.1
    prefer-ip-address: true
    
server:
  port: 33333
spring:
  application:
    name: hyfzuulserver

zuul:
  routes:
    sbService :
      path : /sbService/**
      serviceId : springbootservice

zuul配置完毕:
之前启动的service就按照刚才说的那样也可以访问,使用路由端口,统一了路径。
127.0.0.1:3333/sbService/service/hello
结果如下:
在这里插入图片描述

里面的?token=1不用管 因为我还加了过滤功能
马上介绍。

zuul不仅拥有路由功能,还可以拥有过滤安全的功能
例如我们发请求的时候需要增加一个token,没有token则直接过滤掉
那么我们在zuul项目中的启动类同级增加一个包加入zuulfilter实现类

package sun.filter;

import java.io.IOException;

import javax.servlet.http.HttpServletRequest;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;

import com.netflix.zuul.ZuulFilter;
import com.netflix.zuul.context.RequestContext;

@Component
public class ServiceFilter extends ZuulFilter {

    private static Logger log=LoggerFactory.getLogger(ServiceFilter.class);

    @Override
    public String filterType() {
        return "pre"; // 定义filter的类型,有pre、route、post、error四种
    }

    @Override
    public int filterOrder() {
        return 0; // 定义filter的顺序,数字越小表示顺序越高,越先执行
    }

    @Override
    public boolean shouldFilter() {
        return true; // 表示是否需要执行该filter,true表示执行,false表示不执行
    }

    @Override
    public Object run() {
        // filter需要执行的具体操作
        System.out.println("inner");
        RequestContext ctx = RequestContext.getCurrentContext();
        HttpServletRequest request = ctx.getRequest();
        String token = request.getParameter("token");
        System.out.println(token);
        if(token==null){
            log.warn("there is no request token");
            ctx.setSendZuulResponse(false);
            ctx.setResponseStatusCode(401);
            try {
                ctx.getResponse().getWriter().write("there is no request token");
            } catch (IOException e) {
                e.printStackTrace();
            }
            return null;
        }
        log.info("ok");
        return null;
    }
}

那么就搞定了,当你请求之前写的那个service的时候 如果不带token则会出现
在这里插入图片描述
带token
在这里插入图片描述

除此之外 zuul还可以和hystricx配合使用,还是跟filter一样,在zuul项目中启动类的同级下增加一个包,加入FallbackProvider的实现类:

package sun.fallbackProvider;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;

import org.springframework.cloud.netflix.zuul.filters.route.FallbackProvider;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.client.ClientHttpResponse;
import org.springframework.stereotype.Component;

@Component
public class ServiceFallbackProvider implements FallbackProvider {
    @Override
    // 指定熔断器功能应用于哪些路由的服务
    public String getRoute() {
        // 这里只针对"springbootService"服务进行熔断
        // 如果需要针对所有服务熔断,则return "*"
        return "*";
    }

    @Override
    public ClientHttpResponse fallbackResponse(String route, Throwable cause) {
        System.out.println("route:"+route);

        return new ClientHttpResponse() {
            @Override
            public HttpStatus getStatusCode() throws IOException {
                return HttpStatus.OK;
            }

            @Override
            public int getRawStatusCode() throws IOException {
                return 200;
            }

            @Override
            public String getStatusText() throws IOException {
                return "ok";
            }

            @Override
            public void close() {

            }

            @Override
            // 发生熔断式,返回的信息
            public InputStream getBody() throws IOException {
                return new ByteArrayInputStream("Sorry, the service is unavailable now.".getBytes());
            }

            @Override
            public HttpHeaders getHeaders() {
                HttpHeaders headers = new HttpHeaders();
                headers.setContentType(MediaType.APPLICATION_JSON);
                return headers;
            }
        };
    }
}

当你在service服务关闭后仍继续访问service,不会报404,而是
在这里插入图片描述
这就是hystricx的熔断机制

下面介绍两种轮询的方法

四。feign的创建
启动类:
基础上多加一个@EnableFeignClients注解

@SpringBootApplication
@ServletComponentScan
@EnableDiscoveryClient
@EnableFeignClients
public class ConsumerClientApplication {
    public static void main(String[] args) {
        SpringApplication.run(ConsumerClientApplication.class, args);
    }
}

启动类同级建一个包:
放入ServiceFeignClient实现类

package sun.client;

import common.entity.RestfulResult;
import org.springframework.stereotype.Component;
import sun.entity.ServiceInfo;

@Component
public class ServiceFallback implements ServiceFeignClient{

    @Override
    public RestfulResult hello( ) {
        RestfulResult result = new RestfulResult();
        result.setData("服务调用失败");
        return result;
    }
}

和interface ServiceFeignClient

package sun.client;

import common.entity.RestfulResult;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import sun.entity.ServiceInfo;

@Component
@FeignClient(value = "springbootService", fallback=ServiceFallback.class) //这里的value对应调用服务的spring.applicatoin.name
public interface ServiceFeignClient {

    @RequestMapping(value = "/service/hello")
    RestfulResult hello();

}

controller可以另外建一个包

```java
/**
 * Copyright &copy; 2012-2014 <a href="https://github.com/thinkgem/jeesite">JeeSite</a> All rights reserved.
 */
package sun.controller;

import common.entity.RestfulResult;
import common.utils.CommUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import sun.client.ServiceFeignClient;
import sun.entity.ServiceInfo;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

@RestController
public class ServiceController {

    @Autowired
    ServiceFeignClient serviceFeignClient;

    // 调用:localhost:6004/consumerService?token=1
    @RequestMapping("/consumerService")
    public void consumerService(HttpServletRequest request, HttpServletResponse response
                                ){

        RestfulResult restfulResult =  serviceFeignClient.hello();

        CommUtils.printDataJason(response, restfulResult);
    }
}

最后是配置:

server.port=28280
feign.hystrix.enabled=true

eureka.instance.prefer-ip-address=true
eureka.instance.ip-address=127.0.0.1
eureka.client.registerWithEureka=true
eureka.client.fetchRegistry=true
eureka.client.serviceUrl.defaultZone=http://127.0.0.1:24680/eureka/
spring.application.name=hyfSpringbootServiceFeign
eureka.instance.instance-id=${eureka.instance.ip-address}:${server.port}@${random.int}

忘记说一个小问题,到这里,因为是同一个服务内的轮询,那么至少两个service,所以我们需要copy一份之前的service,改一下端口,和到时候会打印的信息。
把这两个service都启动,feign也启动,然后连续访问feign的service会出现,轮流访问不同端口的service
结果如下;
1.
在这里插入图片描述
2.在这里插入图片描述

五。ribbon的创建

启动类
底下加一个template

@SpringBootApplication
@ServletComponentScan
@EnableDiscoveryClient
@EnableHystrix
public class ConsumerClientApplication {

    public static void main(String[] args) {

        SpringApplication.run(ConsumerClientApplication.class, args);
    }

    @Bean
    @LoadBalanced
    RestTemplate restTemplate(){
        return new RestTemplate();
    }
}

配置文件正常配置

controller在一个包里,包和启动类同级

/**
 * Copyright &copy; 2012-2014 <a href="https://github.com/thinkgem/jeesite">JeeSite</a> All rights reserved.
 */
package sun.controller;

import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;
import sun.entity.ServiceInfo;

@RestController
public class ServiceController {

    @Autowired
    RestTemplate restTemplate;

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

    // 调用:localhost:6007/consumerServiceRibbon?token=1
    @RequestMapping("/consumerServiceRibbon")
    @HystrixCommand(fallbackMethod="consumerServiceRibbonFallback")
    public String consumerServiceRibbon(@RequestBody ServiceInfo serviceInfo){
        String result = this.restTemplate.postForObject("http://springbootService/service/rest?token=1", serviceInfo, String.class);
        return result;
    }

    public String consumerServiceRibbonFallback(@RequestBody ServiceInfo serviceInfo){
        return "consumerServiceRibbon异常,端口:" + port + ",Name=" + serviceInfo.getName();
    }
}

搞定
结果:
在这里插入图片描述

在这里插入图片描述

失败的同学看这里,在两个service项目那个controller那里,做两件事
1.让返回的值带上端口,这样可以清楚的显示
2.把方法的参数去掉,就留一个HttpServerlet。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值