项目微服务改造

项目微服务改造

服务划分原则

1.基于业务逻辑 : 将系统中的业务按照**职责范围**进行识别,职责相同的划分为一个单独的服务。
--------------------------------------------------------------
2.基于稳定性 : 将系统中的业务模块按照稳定性进行**排序**。稳定的、不经常修改的划分一块;将不稳定的,经常修改的划分为一个独立服务。比如日志服务、监控服务都是相对稳定的服务,可以归到一起。
--------------------------------------------------------------
3.基于可靠性 : 将系统中的业务模块按照可靠性进行排序。对可靠性要求比较高的核心模块归在一起,对可靠性要求不高的非核心模块归在一块。规避因为一颗老鼠屎坏了一锅粥的单体弊端,同时将来要做高可用方案也能很好的节省机器或带宽的成本。
-------------------------------------------------------------
4.基于高性能 : 将系统中的业务模块按照对性能的要求进行优先级排序。把对性能要求较高的模块独立成一个服务,对性能要求不高的放在一起。比如全文搜索,商品查询和分类,秒杀就属于高性能的核心模块。

分布式项目部署

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-cvedrDZw-1599298007419)(C:\Users\白米白9999\AppData\Roaming\Typora\typora-user-images\1599031877350.png)]


[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-5EeoJdXx-1599298007421)(C:\Users\白米白9999\AppData\Roaming\Typora\typora-user-images\1598848705510.png)]


新建基于springboot的父类项目

pom.xml配置

----------------------------------------子项目框架
    <modules>
        <module>eureka-server</module>
        <module>config-server</module>
        <module>zuul-server</module>
        <module>common</module>
        <module>provider-api</module>
        <module>provider-server</module>
    </modules>
---------------------------------------

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

    <properties>
        <java.version>1.8</java.version>
        <spring-cloud.version>Greenwich.SR1</spring-cloud.version>
    </properties>

    <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>
新建基于父项目的子项目common项目

pom.xml配置

<dependencies>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
    </dependencies>
常用工具类
public class AssertUtil {
    private AssertUtil(){};

    public static void hasLength(String value ,CodeMsg codeMsg){
        if(!StringUtils.hasLength(value)){
            throw new BusinessException(codeMsg);
        }
    }
    public static void isEquals(String password, String rpassword,CodeMsg codeMsg) {
        if(!password.equals(rpassword)){
            throw new BusinessException(codeMsg);
        }
    }
}
-------------------------------------------------------------
    /**
 * 系统常量
 */
public class Consts {

    //验证码有效时间
    public static final int VERIFY_CODE_VAI_TIME = 5;  //单位分

    //token有效时间
    public static final int USER_INFO_TOKEN_VAI_TIME = 30;  //单位分
}
-------------------------------------------------------------
    @Setter
@Getter
@NoArgsConstructor
public class JsonResult<T> {
    public static final int CODE_SUCCESS = 200;
    public static final String MSG_SUCCESS = "操作成功";
    public static final int CODE_NOLOGIN = 401;
    public static final String MSG_NOLOGIN = "请先登录";

    public static final int CODE_ERROR = 500;
    public static final String MSG_ERROR = "系统异常,请联系管理员";

    public static final int CODE_ERROR_PARAM = 501;  //参数异常

    private int code;  //区分不同结果, 而不再是true或者false
    private String msg;

    private T data;  //除了操作结果之后, 还行携带数据返回


    public JsonResult(int code, String msg, T data){
        this.code = code;
        this.msg = msg;
        this.data = data;
    }

    public JsonResult(CodeMsg codeMsg, T data){
        this.code = codeMsg.getCode();
        this.msg = codeMsg.getMsg();
        this.data = data;
    }

    public static <T> JsonResult success(T data){
        return new JsonResult(CODE_SUCCESS, MSG_SUCCESS, data);
    }

    public static JsonResult success(){
        return new JsonResult(CODE_SUCCESS, MSG_SUCCESS, null);
    }

    public static <T>  JsonResult error(int code, String msg, T data){
        return new JsonResult(code, msg, data);
    }

    public static JsonResult defaultError(){
        return new JsonResult(CODE_ERROR, MSG_ERROR, null);
    }


    public static JsonResult noLogin() {
        return new JsonResult(CODE_NOLOGIN, MSG_NOLOGIN, null);
    }
}
--------------------------------------------------------------
    @Getter
public enum RedisKeys {

    TODAY_VISITOR_VIEW_NUM("today_visitor_view_num",-1L),
    TOTAL_VISITOR_VIEW_NUM("total_visitor_view_num",-1L),
    //接口防刷逻辑
    BRUSH_PROOF("brush_proof",60L),
    USER_STRATEGY_THUMBUP("user_strategy_thumbup",-1L),
    USER_STRATEGY_FAVOR("user_strategy_favor",-1L),
    STRATEGY_STATIS_VO("strategy_statis_vo",-1L),
    VERIFY_CODE("verify_code", Consts.VERIFY_CODE_VAI_TIME*60L),
    USER_LOGIN_TOKEN("user_login_token" ,Consts.USER_INFO_TOKEN_VAI_TIME*60L);
    private String prefix ;
    private Long time ;

    private RedisKeys(String prefix, Long time){
            this.prefix=prefix;
            this.time=time;
    }

    public String join(String... values){
        StringBuilder sb = new StringBuilder();
        sb.append(this.prefix);
        for (String value : values) {
            sb.append(":").append(value);
        }
        return sb.toString();
    }
}
新建基于父项目的子项目eureka-server项目
//启动类 贴注解
@SpringBootApplication
@EnableEurekaServer
public class EurekaServer {

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

application.yml

server:
  port: 8761

eureka:
  instance:
    hostname: localhost
  client:
    #是否将自己注册进去eureka,false为不注册,true注册
    registerWithEureka: false
    #是否从eureka抓取注册信息,单点无所谓,集群必须设置为true才能配合ribbon使用负载均衡
    fetchRegistry: false
    serviceUrl:
      defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/
  server:
    #默认是true,现在关闭自我保护机制,保证不可用服务被删除
    enable-self-preservation: false

pom.xml

<dependencies>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
        </dependency>
    </dependencies>

新建基于父项目的子项目config-server项目

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

application.yml

server:
  port: 9100
spring:
  application:
    name: config-server
  cloud:
    config:
      server:
        git:
          uri: https://gitee.com/xxxxxxxxxxxxx
          username: hpsyj88@163.com
          password: xxxxxxx
          label: master
eureka:
  client:
    registerWithEureka: true
    fetchRegistry: true
    serviceUrl:
      defaultZone:  http://localhost:8761/eureka/
  instance:
    #eureka客户端向服务端发送心跳的时间间隔,单位为秒,默认是30
    lease-renewal-interval-in-seconds: 1
    #eureka服务端收到最后一次心跳等待的时间上限,单位为秒,默认是90,超时剔除
    lease-expiration-duration-in-seconds: 2

pom.xml

<dependencies>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-config-server</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
        </dependency>
    </dependencies>

新建基于父项目的子项目Zuul-server项目

@SpringBootApplication
@EnableEurekaClient
@EnableZuulProxy
public class ZuulServer {
    //跨域访问
    @Bean
    public WebMvcConfigurer corsConfigurer() {
        return new WebMvcConfigurer() {
            @Override
            //重写父类提供的跨域请求处理的接口
            public void addCorsMappings(CorsRegistry registry) {
                //添加映射路径
                registry.addMapping("/**")
                        //放行哪些原始域
                        .allowedOrigins("*")
                        //是否发送Cookie信息
                        .allowCredentials(true)
                        //放行哪些原始域(请求方式)
                        .allowedMethods("GET", "POST", "PUT", "DELETE","OPTIONS")
                        //放行哪些原始域(头部信息)
                        .allowedHeaders("*")
                        //暴露哪些头部信息(因为跨域访问默认不能获取全部头部信息)
                        .exposedHeaders("Header1", "Header2");
            }
        };
    }
    -------------------------------------------------------------
         @Bean
    public CorsFilter corsFilter() {
        final UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
        final CorsConfiguration config = new CorsConfiguration();
        // 允许cookies跨域
        config.setAllowCredentials(true);
        // #允许向该服务器提交请求的URI,*表示全部允许,在SpringMVC中,如果设成*,会自动转成当前请求头中的Origin
        config.addAllowedOrigin("*");
        // #允许访问的头信息,*表示全部
        config.addAllowedHeader("*");
        // 预检请求的缓存时间(秒),即在这个时间段里,对于相同的跨域请求不会再预检了
        config.setMaxAge(18000L);
        // 允许提交请求的方法,*表示全部允许
        config.addAllowedMethod("OPTIONS");
        config.addAllowedMethod("HEAD");
        // 允许Get的请求方法
        config.addAllowedMethod("GET");
        config.addAllowedMethod("PUT");
        config.addAllowedMethod("POST");
        config.addAllowedMethod("DELETE");
        config.addAllowedMethod("PATCH");
        source.registerCorsConfiguration("/**", config);
        return new CorsFilter(source);
    }

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

bootstrap.yml

spring:
  application:
    name: zuul-server
  cloud:
    config:
      label: master  #分支名称
      name: zuul    #配置文件名称
      profile: server   #读取后缀名称:
      uri: http://localhost:9100

eureka:
  client:
    registerWithEureka: true
    fetchRegistry: true
    serviceUrl:
      defaultZone:  http://localhost:8761/eureka/
  instance:
    #eureka客户端向服务端发送心跳的时间间隔,单位为秒,默认是30
    lease-renewal-interval-in-seconds: 1
    #eureka服务端收到最后一次心跳等待的时间上限,单位为秒,默认是90,超时剔除
    lease-expiration-duration-in-seconds: 2

码云上的zuul-server.yml

server:
  port: 9000
ribbon:
  ConnectTimeout: 6000
  ReadTimeout: 60000


zuul:
  sensitiveHeaders:
  #强制采用原始请求的编码格式,即不对Get请求参数做编解码
  forceOriginalQueryStringEncoding: true 
  #忽略匹配,既: order-server这种格式请求路径忽略掉
  ignoredPatterns: /*-server/**
  routes:
    website-server-route:
      path: /website/**
      service-id: website-server

pom.xml

 <dependencies>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-zuul</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-config-client</artifactId>
        </dependency>
    </dependencies>

新建基于父项目的子项目provider-api项目

(非业务逻辑,只提供接口与熔断降级操作)

pomx.xml

 <dependencies>
        <dependency>
            <groupId>cn.wolfcode.wolf2w</groupId>
            <artifactId>common</artifactId>
            <version>1.0.0</version>
        </dependency>

        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-openfeign</artifactId>
        </dependency>
    </dependencies>

新建基于父项目provider-api的member-api项目

pom.xml

<dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-mongodb</artifactId>
            <!---依赖不传递-->
            <optional>true</optional>
</dependency>

新建feign接口 如

@FeignClient(name = "member-server",fallback = UserInfoFeignHystrix.class)
public interface UserInfoFeignApi {

    @GetMapping("/users/get/")
    JsonResult get(@RequestParam("id") String id);

    @GetMapping("/users/checkPhone/")
    JsonResult checkPhone(@RequestParam("phone") String phone);

    @PostMapping("/users/regist/")
    JsonResult regist(@RequestParam(value = "phone",required = false) String phone,@RequestParam(value = "password",required = false) String password,
                      @RequestParam(value = "nickname",required = false)  String nickname, @RequestParam(value = "rpassword",required = false) String rpassword);
}

定义熔断,降级方法

@Component
public class UserInfoFeignHystrix implements UserInfoFeignApi {
    @Override
    public JsonResult get(String id) {
        System.out.println("出问题啦,找降级方法啦.....");
        return null;
    }
    @Override
    public JsonResult checkPhone(String phone) {
        System.out.println("出问题啦,找降级方法啦.....");
        return null;
    }
    @Override
    public JsonResult regist(String phone, String password, String nickname, String rpassword) {
        System.out.println("出问题啦,找降级方法啦.....regist");
        return null;
    }
}
新建基于父项目provider-api的member-api项目
@FeignClient(name = "msg-server",fallback = MsmFeignHystrix.class)
public interface MsmFeignApi {

    @GetMapping("/msms/sendVerifyCode")
    JsonResult sendVerifyCode(@RequestParam("phone") String phone);

    @GetMapping("/msms/checkVerifyCode")
    JsonResult checkVerifyCode(@RequestParam("phone") String phone, @RequestParam("verifyCode") String verifyCode);
}
@Component
public class MsmFeignHystrix implements MsmFeignApi {
    @Override
    public JsonResult sendVerifyCode(String phone) {
        System.out.println("出问题啦,找降级方法啦.....sendVerifyCode");
        return null;
    }
    @Override
    public JsonResult checkVerifyCode(String phone, String verifyCode) {
        System.out.println("出问题啦,找降级方法啦.....checkVerifyCode");
        return null;
    }
}

新建基于父项目的子项目provider-server项目

pom.xml

<dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-actuator</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-config-client</artifactId>
        </dependency>
    </dependencies>
新建基于父项目provider-api的member-server项目

pom.xml

   <dependencies>
        <dependency>
            <groupId>cn.wolfcode.wolf2w</groupId>
            <artifactId>member-api</artifactId>
            <version>1.0.0</version>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-mongodb</artifactId>
        </dependency>
    </dependencies>
@SpringBootApplication
//@EnableHystrix
public class MemberServer {

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

bootstrap.yml

spring:
  application:
    name: member-server
  cloud:
    config:
      label: master  #分支名称
      name: member    #配置文件名称
      profile: server   #读取后缀名称:
      uri: http://localhost:9100

eureka:
  client:
    registerWithEureka: true
    fetchRegistry: true
    serviceUrl:
      defaultZone:  http://localhost:8761/eureka/
  instance:
    #eureka客户端向服务端发送心跳的时间间隔,单位为秒,默认是30
    lease-renewal-interval-in-seconds: 1
    #eureka服务端收到最后一次心跳等待的时间上限,单位为秒,默认是90,超时剔除
    lease-expiration-duration-in-seconds: 2

码云上的menber-server.yml

server:
  port: 8081
spring:
  data:
    mongodb:
      uri: mongodb://localhost:27017/member
feign:
  client:
    config:
      default:
        connectTimeout: 8000
        readTimeout: 8000
  hystrix:
    enabled: true

各种业务逻辑实现

@RestController
public class UserInfoFeignApiClient implements UserInfoFeignApi {

    @Autowired
    IUserInfoService userInfoService;
    @Autowired
    private UserInfoRepository userInfoRepository;

    @Override
    public JsonResult get(String id) {
        return JsonResult.success(userInfoService.get(id));
    }
    @Override
    public JsonResult checkPhone(String phone) {
        UserInfo userInfo = userInfoRepository.findByPhone(phone);
        //int i =1/0;
        return JsonResult.success(userInfo == null);
    }
    @Override
    public JsonResult regist(String phone, String password, String nickname, String rpassword) {
        //传入参数不能为空
        AssertUtil.hasLength(phone, MemberCodeMsg.PHONE_ERROR);
        AssertUtil.hasLength(password, MemberCodeMsg.PASSWORD_ERROR);
        AssertUtil.hasLength(rpassword, MemberCodeMsg.REPASSWORD_ERROR);
        AssertUtil.hasLength(nickname, MemberCodeMsg.NICKNAME_ERROR);
        //验证二次密码是否一致
        AssertUtil.isEquals(password, rpassword, MemberCodeMsg.EQUALSPASSWORD_ERROR);
        //验证手机号码是否唯一
        if (userInfoService.checkPhone(phone)) {
            throw new BusinessException(MemberCodeMsg.HASE_REGIST_ERROR);
        }
        UserInfo userInfo = new UserInfo();
        userInfo.setNickname(nickname);
        userInfo.setPhone(phone);
        userInfo.setPassword(password);
        userInfo.setEmail("");
        userInfo.setCity("");
        userInfo.setGender(UserInfo.GENDER_SECRET);
        userInfo.setLevel(1);
        userInfo.setState(UserInfo.STATE_NORMAL);
        userInfo.setHeadImgUrl("images/default.jpg");
        userInfo.setId(null);
        userInfoService.save(userInfo);
        return JsonResult.success();
    }
}
新建基于父项目provider-api的msg-server项目

pom.xml

<dependencies>
        <dependency>
            <groupId>cn.wolfcode.wolf2w</groupId>
            <artifactId>msg-api</artifactId>
            <version>1.0.0</version>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-redis</artifactId>
        </dependency>
    </dependencies>
@SpringBootApplication
//@EnableHystrix
public class MsgServer {

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

bootstrap.yml

spring:
  application:
    name: msg-server
  cloud:
    config:
      label: master  #分支名称
      name: msg    #配置文件名称
      profile: server   #读取后缀名称:
      uri: http://localhost:9100

eureka:
  client:
    registerWithEureka: true
    fetchRegistry: true
    serviceUrl:
      defaultZone:  http://localhost:8761/eureka/
  instance:
    #eureka客户端向服务端发送心跳的时间间隔,单位为秒,默认是30
    lease-renewal-interval-in-seconds: 1
    #eureka服务端收到最后一次心跳等待的时间上限,单位为秒,默认是90,超时剔除
    lease-expiration-duration-in-seconds: 2

msg-server.yml

server:
  port: 8082
spring:
  redis:
    host: 127.0.0.1
feign:
  client:
    config:
      default:
        connectTimeout: 8000
        readTimeout: 8000
  hystrix:
    enabled: true

import java.util.concurrent.TimeUnit;
@Service
public class SmsRedisServiceImpl implements ISmsRedisService {

    @Autowired
    private StringRedisTemplate template ;

    @Override
    public void saveCode(String phone, String code) {
        template.opsForValue().set(RedisKeys.VERIFY_CODE.join(phone),code ,RedisKeys.VERIFY_CODE.getTime(),TimeUnit.SECONDS );
    }
    @Override
    public String getCode(String phone) {
        String key =RedisKeys.VERIFY_CODE.join(phone);
        String code = template.opsForValue().get(key);
        return code;
    }
}

g:
default:
connectTimeout: 8000
readTimeout: 8000
hystrix:
enabled: true


```java

import java.util.concurrent.TimeUnit;
@Service
public class SmsRedisServiceImpl implements ISmsRedisService {

    @Autowired
    private StringRedisTemplate template ;

    @Override
    public void saveCode(String phone, String code) {
        template.opsForValue().set(RedisKeys.VERIFY_CODE.join(phone),code ,RedisKeys.VERIFY_CODE.getTime(),TimeUnit.SECONDS );
    }
    @Override
    public String getCode(String phone) {
        String key =RedisKeys.VERIFY_CODE.join(phone);
        String code = template.opsForValue().get(key);
        return code;
    }
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值